3、进程与线程基础:进程的概念与生命周期、线程的概念与调度、进程与线程的区别
好,咱们今天聊聊进程和线程。这是操作系统的核心概念,也是很多嵌入式开发者容易混淆的地方。我记得刚接触QNX那会儿,也在这上面栽过跟头。别急,咱们一个一个掰开揉碎了讲。
3.1 进程的概念与生命周期
进程是什么?说白了,就是一个正在运行的程序实例。你写了一段C代码,编译成可执行文件,放在硬盘上,那叫程序。当你双击运行它,或者用命令行启动它,系统为它分配了内存、打开了文件、创建了各种资源——这时候,它就变成了一个进程。
在QNX里,进程是资源分配的最小单位。每个进程都有自己的地址空间,互不干扰。我做过一个车载仪表盘的项目,仪表盘进程和导航进程就是两个独立的进程。一个崩了,另一个还能正常工作。这就是进程隔离的好处。
进程的四个关键要素:
- PID:进程ID,系统里唯一的标识符
- 地址空间:代码、数据、堆、栈,都在自己的地盘里
- 资源句柄:打开的文件、信号量、消息队列等
- 执行上下文:寄存器状态、程序计数器等
进程的生命周期,其实就五个状态:
| 状态 | 说明 | 我踩过的坑 |
|---|---|---|
| 创建(New) | 调用fork()或spawn(),系统分配资源 | 曾经忘记检查fork返回值,子进程没创建成功,程序直接跑飞了 |
| 就绪(Ready) | 万事俱备,只等CPU | — |
| 运行(Running) | CPU正在执行它的指令 | — |
| 阻塞(Blocked) | 等某个事件,比如等I/O完成 | 调试一个串口驱动时,进程莫名其妙卡住,查了半天发现是读操作阻塞了,没设超时 |
| 终止(Terminated) | 执行完毕或被kill,释放资源 | — |
你想想看,一个进程从创建到终止,中间可能在这几个状态之间来回切换。QNX的调度器会负责这件事,它决定哪个就绪进程能拿到CPU。
小提示:在QNX里,用 pidin 命令可以查看当前所有进程的状态。我调试时几乎离不开这个命令。
3.2 线程的概念与调度
线程又是什么?线程是进程里的一个执行流。一个进程可以有一个或多个线程,它们共享进程的地址空间和资源。
我个人习惯把进程比作一个工厂,线程就是工厂里的工人。工厂(进程)有厂房、设备、原材料(地址空间和资源),工人们(线程)在里面干活,共享这些东西。但每个工人有自己的工具包(栈和寄存器)。
在QNX里,线程是调度和执行的最小单位。调度器不关心进程,它只关心线程。嗯,这里要注意:QNX的调度策略非常灵活,支持以下几种:
- FIFO调度:先来先服务,直到线程主动放弃CPU或被更高优先级抢占
- 轮转调度(Round Robin):每个线程分一个时间片,时间到了就换下一个
- 零星调度(Sporadic):适合偶发性的实时任务,平时低优先级,有事件时提优先级
- 自适应分区调度(Adaptive Partitioning):QNX的独门绝技,给每个分区分配CPU预算,防止某个分区饿死其他分区
我曾经在一个多路视频处理的项目里,用了自适应分区调度。一个分区跑视频解码,一个分区跑UI,一个分区跑网络通信。结果视频解码分区出了bug,疯狂吃CPU,但其他分区因为有预算保护,照样流畅运行。要是没有这个机制,整个系统早就卡死了。
线程的优先级:QNX支持0-255级优先级,0最高,255最低。实时线程一般用0-63,普通线程用64-255。我建议实时任务优先级别设太高,留点余量给系统关键服务。
3.3 进程与线程的区别
好,到了最关键的部分。进程和线程到底有什么区别?我直接给你列个表,一目了然:
| 对比项 | 进程 | 线程 |
|---|---|---|
| 资源分配单位 | 是,每个进程独立拥有地址空间 | 否,共享所属进程的资源 |
| 调度单位 | 不是,调度器调度的是线程 | 是,线程是调度的基本单位 |
| 地址空间 | 独立,互不干扰 | 共享,一个线程崩了可能带崩整个进程 |
| 创建开销 | 大,需要分配独立地址空间、页表等 | 小,只需分配栈和线程控制块 |
| 上下文切换 | 慢,涉及地址空间切换(TLB刷新) | 快,不涉及地址空间切换 |
| 通信方式 | IPC(消息传递、共享内存、信号等) | 直接读写共享变量(需加锁) |
| 安全性 | 高,一个进程崩溃不影响其他进程 | 低,一个线程出问题可能影响整个进程 |
说白了,进程是 heavyweight(重量级),线程是 lightweight(轻量级)。
那什么时候用进程,什么时候用线程?我个人的经验是:
- 需要强隔离、高可靠性:用进程。比如车载系统的仪表盘和娱乐系统,绝对不能互相影响。
- 需要高性能、低延迟:用线程。比如同一个任务的不同处理阶段,用线程共享数据,省去IPC的开销。
- 混合使用:这是最常见的。一个进程里开多个线程,同时多个进程之间通过IPC协作。
避坑指南:我曾经在一个项目中,为了省事,把所有功能都塞到一个进程的多个线程里。结果一个线程的野指针把另一个线程的数据给踩了,查了整整两天才定位到问题。从那以后,关键模块我一定拆成独立进程。
最后说一句,QNX的进程和线程模型非常成熟。你只要理解了「进程管资源,线程管执行」这个核心思想,后面学消息传递、同步互斥就会轻松很多。
好,这一章就到这儿。下一章咱们聊聊QNX的进程间通信——消息传递,这可是QNX的看家本领。