今日主题: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 常见调度算法

  1. 时间片轮转算法
  2. 先来先服务、后来后服务算法
  3. 高优先级优先执行算法
  4. 短作业优先算法

4. 进程三态基础模型

  • 就绪态:一切准备完成,只差 CPU 时间片
  • 运行态:正在被 CPU 执行
  • 阻塞态:等待 IO 资源,例如getchar()fgets()等待用户输入

二、Linux 真实进程 7 种状态

Linux 内核把进程细化成 7 种状态:

状态标识

状态名称

说明

R

运行态

用户态 / 内核态,正在被 CPU 调度执行

S

就绪态

具备运行条件,等待 CPU 分配时间片

S

可中断睡眠态

也叫睡眠态,阻塞等待资源,可以被信号唤醒

D

不可唤醒等待态

不会被 CPU 信号打断,等待硬件 IO 完成

T

暂停态

进程被暂停执行

Z

僵尸态

进程已经执行结束,但资源没有被父进程回收

X

结束态

进程结束,资源完全回收

思维导图简单理解:
中断、系统调用会让进程状态切换;睡眠可以被唤醒;暂停需要手动恢复;僵尸进程是学习重点。

三、进程的消亡

进程消亡分为两步:

  1. 进程退出:程序执行完毕,进程结束
  2. 进程回收:父进程回收子进程占用的内存资源,没有回收就变成僵尸进程

四、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

返回值规则

  1. pid > 0:父进程返回,返回值是子进程 PID
  2. pid = 0:子进程内部返回 0
  3. -1:创建失败

配套函数:

  • getpid() 获取自己进程 ID
  • getppid() 获取父进程 ID

⚠️重要注意点

  1. 父子进程用户空间互相独立,变量互不干扰
  2. 进程之间不能直接通信,要通信必须借助内核 IPC 机制

2. 进程退出函数

进程结束三种途径:

  1. main 函数return返回
  2. exit()/_Exit()系统函数主动退出
  3. 收到 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,执行成功当前程序直接被替换,后续代码不再运行。

今日学习小结

  1. 程序静态存硬盘,进程动态跑内存;一个程序可以生成多个进程。
  2. 进程基础三态:就绪‑运行‑阻塞;Linux 细化 7 种状态,重点区分僵尸、孤儿。
  3. fork()拷贝生成子进程,父子空间隔离;exec替换进程运行别的程序。
  4. 命令ps/top/pstree/kill日常调试进程必用。

僵尸进程根源:子进程退出父进程没有回收;孤儿进程父进程先退出,系统帮忙善后。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐