进程和线程是 Linux 系统编程的核心概念,也是理解操作系统资源调度、并发执行的基础。本文将从进程的基本概念入手,系统讲解进程的生命周期、调度策略、状态转换,以及核心编程接口,并通过两个实战案例帮助你掌握进程编程的核心技巧。
一、进程核心概念解析
1. 程序 vs 进程
- 程序:存放在硬盘中的静态数据集合(代码、数据、配置等),是 "死" 的,不占用系统资源;
- 进程:程序动态执行的过程,是操作系统资源分配的基本单位,包含创建、调度、运行、消亡的完整生命周期,占用 CPU、内存、文件描述符等系统资源。
简单来说:程序是文件,进程是运行中的程序实例。
2. 进程的虚拟地址空间
为了保证进程间的隔离性,Linux 为每个进程分配独立的虚拟地址空间(0~4G),通过 MMU(内存管理单元)映射到物理内存,核心分布如下:
表格
| 地址区域 | 内容 | 特性 |
|---|---|---|
| 内核空间(3G~4G) | 内核代码 / 数据 | 用户进程不可直接访问 |
| 堆区(heap) | 动态分配的内存(malloc/mmap) | 手动申请 / 释放,自低地址向高地址增长 |
| 栈区(stack) | 局部变量、函数调用栈 | 自动回收,自高地址向低地址增长(默认 8M) |
| 数据段 | .data(已初始化全局 / 静态变量).bss(未初始化全局 / 静态变量).rodata(字符串常量) | 编译时分配,.bss 默认初始化为 0 |
| 文本段(.text) | 程序代码、指令 | 只读,所有进程实例共享(如果代码相同) |
关键特性:多个进程的虚拟地址空间看似相同,但 MMU 会映射到不同的物理内存,保证进程间的隔离性。
3. 进程的核心标识
- PID:进程 ID,每个进程唯一,值大于 0;
- PPID:父进程 ID,所有进程(除 init 进程)都有父进程;
- PGID:进程组 ID,用于批量管理进程;
- UID/EUID:用户 ID / 有效用户 ID,标识进程的权限。
4. 进程的调度算法
操作系统通过调度算法决定 CPU 分配给哪个进程,核心算法包括:
- 先来先服务(FCFS):按进程到达顺序调度,简单但不利于短作业;
- 短作业优先(SJF):优先调度运行时间短的进程,提升整体效率;
- 高优先级调度:优先级范围 - 20(最高)~20(最低),优先调度高优先级进程;
- 时间片轮转:将 CPU 时间划分为固定时间片(5~10ms),进程轮流执行,实现 "宏观并行、微观串行";
- 多级队列反馈调度:结合优先级和时间片,动态调整进程优先级;
- 抢占式调度:高优先级进程可抢占低优先级进程的 CPU 资源。
5. 进程的状态(重点)
通过ps -aux可查看进程状态,核心状态如下:
表格
| 状态标识 | 名称 | 说明 |
|---|---|---|
| R | 运行态 / 就绪态 | 正在执行(运行态)或等待 CPU 调度(就绪态) |
| S | 可唤醒等待态(浅睡眠) | 等待资源(如 IO、信号),可被信号唤醒 |
| D | 不可唤醒等待态(深睡眠) | 等待磁盘 IO 等关键资源,不能被打断(kill -9 也无效) |
| T | 停止态 | 被SIGSTOP信号暂停,可被SIGCONT恢复 |
| Z | 僵尸态 | 进程执行结束,但父进程未回收其资源(PID 仍存在) |
| X | 结束态 | 进程资源已被完全回收,不可见 |
僵尸进程是重点问题 :需通过父进程调用wait/waitpid或子进程主动退出避免。
二、进程相关常用命令
日常开发中,我们常用以下命令管理和查看进程:
表格
| 命令 | 功能 | 示例 | |
|---|---|---|---|
| top | 动态查看进程资源占用(CPU / 内存) | 直接输入top,按q退出 |
|
| ps -ef | 查看所有进程的完整信息(PID/PPID/ 用户) | `ps -ef | grep nginx`(过滤 nginx 进程) |
| ps -aux | 查看进程状态、CPU / 内存占用率 | `ps -aux | sort -k 3 -r`(按 CPU 占用降序) |
| kill | 向进程发送信号(默认 SIGTERM) | kill -9 1234(强制杀死 PID=1234 的进程) |
|
| killall | 按进程名杀死进程 | killall -9 firefox |
|
| & | 后台执行进程 | ./test &(test 程序后台运行) |
|
| jobs | 查看当前终端的后台任务 | jobs -l(显示任务编号和 PID) |
|
| fg 编号 | 后台任务调至前台 | fg 1(将编号 1 的后台任务调前台) |
|
| nice | 指定进程优先级启动 | nice -n 10 ./test(优先级 10,默认 0) |
|
| renice | 修改运行中进程的优先级 | renice -n 5 1234(将 PID=1234 的进程优先级改为 5) |
三、进程核心编程接口
1. fork:创建子进程
fork是 Linux 创建进程的核心接口,通过 "复制父进程" 创建子进程:
c
运行
#include <unistd.h>
pid_t fork(void);
核心特性
- 返回值 :
- 父进程:返回子进程的 PID(大于 0);
- 子进程:返回 0;
- 失败:返回 - 1(如进程数达到上限)。
- 内存拷贝:子进程会拷贝父进程的文本段、数据段、堆区、栈区(写时复制 COW,仅修改时才真正拷贝);
- 文件描述符:子进程共享父进程打开的文件描述符(指向同一文件表项);
- 执行流程:fork 后,父子进程从 fork 的下一行代码开始并行执行。
2. getpid/getppid:获取进程 ID
c
运行
#include <unistd.h>
// 获取当前进程的PID
pid_t getpid(void);
// 获取当前进程的父进程PID
pid_t getppid(void);
四、实战案例 1:创建 2 个子进程并打印 PID
需求分析
创建一个父进程,通过两次 fork 创建 2 个子进程,要求:
- 子进程打印自己的 PID 和父进程 PID;
- 父进程打印自己的 PID 和两个子进程的 PID;
- 避免 "进程树" 混乱(如子进程再创建子进程)。
完整代码

运行
编译运行
bash
运行
# 编译
gcc fork_two_child.c -o fork_two_child
# 运行
./fork_two_child
输出示例
plaintext
父进程:PID=1234,子进程1 PID=1235,子进程2 PID=1236
子进程1:PID=1235,PPID=1234
子进程2:PID=1236,PPID=1234
所有子进程已退出
关键要点
- 子进程退出 :每个子进程执行完打印后立即
exit(0),避免子进程继续执行后续的 fork 逻辑; - 等待子进程 :父进程通过
waitpid等待子进程退出,回收资源,避免僵尸进程; - 错误处理:fork 失败时需处理,且保证已创建的子进程被回收。
五、实战案例 2:遍历目录并筛选媒体文件
需求分析
遍历指定目录,筛选出后缀为.flv、.avi、.rmvb、.rm 的媒体文件,并打印文件名。
完整代码
c
运行


编译运行
bash
运行
# 编译
gcc find_media_file.c -o find_media_file
# 运行(替换为实际目录)
./find_media_file /home/user/videos
输出示例
plaintext
媒体文件:/home/user/videos/movie.avi
媒体文件:/home/user/videos/show.rmvb
媒体文件:/home/user/videos/clip.flv
关键要点
- 目录遍历 :通过
opendir/readdir/closedir接口遍历目录项; - 过滤特殊目录 :跳过
.和..,避免死循环; - 文件类型判断 :通过
d_type == DT_REG判断是否为普通文件; - 后缀匹配 :使用
strrchr找到文件名最后一个.,再对比后缀。
六、线程基础(补充)
虽然你主要关注进程,但这里补充进程与线程的核心区别,帮助你建立完整的知识体系:
表格
| 特性 | 进程 | 线程 |
|---|---|---|
| 资源分配 | 资源分配的基本单位 | 调度执行的基本单位 |
| 地址空间 | 独立的虚拟地址空间 | 共享进程的地址空间(代码 / 数据 / 文件描述符) |
| 通信方式 | 管道、消息队列、共享内存等(复杂) | 直接读写进程内存(简单,需加锁) |
| 开销 | 创建 / 销毁 / 切换开销大 | 创建 / 销毁 / 切换开销小 |
| 稳定性 | 一个进程崩溃不影响其他进程 | 一个线程崩溃会导致整个进程崩溃 |
总结
本文系统讲解了 Linux 进程的核心知识点,关键总结如下:
- 进程核心特性:进程是动态的程序实例,拥有独立的虚拟地址空间,通过 MMU 映射到物理内存,进程间相互隔离;
- fork 核心逻辑:fork 创建子进程时采用写时复制,父子进程并行执行,父进程需通过 waitpid 回收子进程资源;
- 实战关键技巧 :
- 创建多子进程时需避免子进程重复 fork;
- 遍历目录时需跳过。和..,并通过文件类型过滤目标文件;
- 始终处理系统调用的返回值,避免僵尸进程和资源泄漏。
掌握进程编程是 Linux 系统编程的基础,后续可进一步学习进程间通信(IPC)、线程同步、守护进程等进阶内容,逐步构建完整的系统编程知识体系。