|-----------------------------------------------------------------|
| 今日主题:Linux 多任务之进程,理解进程概念、状态、命令、fork 与 exec 函数,搞懂父子进程、僵尸进程、孤儿进程。 |
前言
Linux 实现多任务依靠多进程、多线程。进程是操作系统资源分配的最小单元,嵌入式 Linux 开发中,进程是必须吃透的基础,今天完整梳理进程整套知识点。
一、进程基础概念
1. 程序与进程的区别
|------|------------------------------|-------------|
| 对比项 | 程序 | 进程 |
| 存储位置 | 存放在硬盘空间 | 加载到内存运行 |
| 属性 | 静态,一堆指令集合 | 动态,拥有完整生命周期 |
| 资源占用 | 不占用内存 CPU | 占用内存、CPU 资源 |
| 关系 | 一个程序可以产生多个进程;一个进程内部也可以执行多个程序 | 程序运行之后才生成进程 |
简单理解:gcc main.c -o a.out得到的a.out是程序 ;执行./a.out跑起来之后,就是进程。
2. 进程的创建
进程启动时,操作系统会给它分配0~4G 虚拟内存空间 。
进程是操作系统资源分配的最小单元。
3. CPU 调度:宏观并行,微观串行
单核 CPU,看上去多个任务同时跑(并发),实际 CPU 在快速切换任务。
- 并行:多个任务真正同时执行,需要多核 CPU。
- 并发:单核 CPU 时间片轮转,快速切换,看起来同时运行。
CPU 常见调度算法
- 时间片轮转算法
- 先来先服务、后来后服务算法
- 高优先级优先执行算法
- 短作业优先算法
4. 进程三态基础模型

- 就绪态:一切准备完成,只差 CPU 时间片
- 运行态:正在被 CPU 执行
- 阻塞态 :等待 IO 资源,例如getchar()、fgets()等待用户输入
二、Linux 真实进程 7 种状态
Linux 内核把进程细化成 7 种状态:
|------|---------|-------------------------|
| 状态标识 | 状态名称 | 说明 |
| R | 运行态 | 用户态 / 内核态,正在被 CPU 调度执行 |
| S | 就绪态 | 具备运行条件,等待 CPU 分配时间片 |
| S | 可中断睡眠态 | 也叫睡眠态,阻塞等待资源,可以被信号唤醒 |
| D | 不可唤醒等待态 | 不会被 CPU 信号打断,等待硬件 IO 完成 |
| T | 暂停态 | 进程被暂停执行 |
| Z | 僵尸态 | 进程已经执行结束,但资源没有被父进程回收 |
| X | 结束态 | 进程结束,资源完全回收 |
|-------------------------------------------------------|
| 思维导图简单理解: 中断、系统调用会让进程状态切换;睡眠可以被唤醒;暂停需要手动恢复;僵尸进程是学习重点。 |
三、进程的消亡
进程消亡分为两步:
- 进程退出:程序执行完毕,进程结束
- 进程回收:父进程回收子进程占用的内存资源,没有回收就变成僵尸进程
四、Linux 进程常用命令
|---------|--------------------------|---------------------------------|
| 命令 | 作用 | 常用示例 |
| ps -aux | 查看全部进程 PID、状态、CPU / 内存占用 | ps -aux | grep a.out过滤自己程序 |
| ps -ef | 查看 PID 与 PPID(父进程 ID) | PPID 就是父进程 ID |
| top | 动态实时监控进程、CPU、内存负载 | |
| pstree | 树形展示进程父子族谱关系 | pstree -sp PID查看指定进程父子链 |
| kill | 给进程发送信号 | kill -9 PID强制结束进程;kill -l查看全部信号 |
| jobs | 查看当前终端后台进程编号 | |
| fg 编号 | 把后台进程调到前台运行 | |
|---------------------------------------------------------------------|
| 进程状态字符补充: * S+:前台可中断睡眠进程 * S:后台可中断睡眠进程 * T:后台暂停进程 |
五、进程相关 C 语言系统函数
1. fork () 创建子进程
|---------------------|
| c pid_t fork(void); |
功能:创建子进程。原来进程叫父进程,新生成叫子进程。
|----------------------------------------------------------------------|
| 底层逻辑:子进程完整拷贝父进程 0‑4G 用户空间;内核 PCB 部分部分拷贝;PID 不拷贝,PPID 修改为父进程 ID 。 |
返回值规则
- pid > 0:父进程返回,返回值是子进程 PID
- pid = 0:子进程内部返回 0
- -1:创建失败
配套函数:
- getpid() 获取自己进程 ID
- getppid() 获取父进程 ID
⚠️重要注意点
- 父子进程用户空间互相独立,变量互不干扰
- 进程之间不能直接通信,要通信必须借助内核 IPC 机制
2. 进程退出函数
进程结束三种途径:
- main 函数return返回
- exit()/_Exit()系统函数主动退出
- 收到 kill 信号强制结束
|--------------------------|
| c void exit(int status); |
status:进程结束状态,0 代表正常退出,非 0 代表异常退出。
3. 僵尸进程 & 孤儿进程
- 僵尸进程 (Zombie) :子进程已经结束,父进程没有回收它资源,残留 PCB。
解决办法:
①父进程调用wait() / waitpid()回收子进程
②让子进程变成孤儿进程,由系统进程自动回收
- 孤儿进程:父进程提前结束,子进程还在运行;子进程被系统进程收养,结束后自动回收资源,不会产生僵尸。
4. exec 函数簇
|-------------------------------------------------------|
| 作用:在当前进程内部,加载并运行另外一个外部可执行程序,替换当前进程文本段,不会创建新 PID 。 |
exec 家族函数记忆口诀:
- l → list 参数列表形式传参
- v → vector 指针数组形式传参
- p → 使用 PATH 环境变量自动搜索程序路径
- e → 可以自定义传入环境变量
|--------------------------|----------------------|
| 函数原型 | 特点 |
| execl(path,arg...,NULL) | 路径 + 列表传参,NULL 做结尾标记 |
| execlp(file,arg...,NULL) | 依靠 PATH 找程序,列表传参 |
| execv(path,argv\[\]) | 路径 + 字符串指针数组传参 |
| execvp(file,argv\[\]) | PATH 查找,指针数组传参 |
| execle / execve | 额外支持自定义环境变量 |
|---------------------------------------|
| 返回值:只有出错才返回‑1,执行成功当前程序直接被替换,后续代码不再运行。 |
今日学习小结
- 程序静态存硬盘,进程动态跑内存;一个程序可以生成多个进程。
- 进程基础三态:就绪‑运行‑阻塞;Linux 细化 7 种状态,重点区分僵尸、孤儿。
- fork()拷贝生成子进程,父子空间隔离;exec替换进程运行别的程序。
- 命令ps/top/pstree/kill日常调试进程必用。
僵尸进程根源:子进程退出父进程没有回收;孤儿进程父进程先退出,系统帮忙善后。