Linux 进程知识点全面复习
1. 引言
进程是 Linux 系统中最重要的抽象概念之一,也是操作系统课程与面试中的高频考点。本文系统梳理 Linux 进程的核心概念、生命周期、控制块、调度算法、进程间通信机制以及常用管理命令,并结合实例说明,帮助读者在复习时建立完整的知识框架。
2. 进程的定义与特性
2.1 什么是进程
进程是程序的一次执行过程,是操作系统进行资源分配和调度的基本单位。程序是静态的指令集合,而进程是动态的,包含程序代码、数据、堆栈以及执行状态等信息。
进程与程序的主要区别如下:
- 动态性:进程有创建、运行、阻塞、终止等生命周期,程序是静态文件。
- 并发性:多个进程可以同时存在于内存中并交替执行。
- 独立性:每个进程拥有独立的地址空间和系统资源。
- 结构性:进程由程序段、数据段和进程控制块(PCB)三部分组成。
2.2 进程与线程的关系
线程是进程内的执行单元,一个进程可以包含多个线程。进程是资源分配的基本单位,线程是 CPU 调度的基本单位。同一进程内的线程共享地址空间和资源,而进程之间相互独立。
3. 进程的生命周期
3.1 进程状态
Linux 进程在生命周期中会经历多种状态转换,主要包括:
- 创建态(New):进程正在被创建,尚未就绪。
- 就绪态(Ready):进程已具备运行条件,等待 CPU 调度。
- 运行态(Running):进程正在 CPU 上执行。
- 阻塞态(Blocked/Wait):进程因等待某事件(如 I/O 完成)而暂停执行。
- 终止态(Terminated):进程执行完毕或被强制结束,等待系统回收资源。
3.2 状态转换过程
进程状态转换的核心路径如下:
- 创建态 → 就绪态:进程创建完成后进入就绪队列。
- 就绪态 → 运行态:调度器选中该进程,分配 CPU。
- 运行态 → 就绪态:时间片用完或被更高优先级进程抢占。
- 运行态 → 阻塞态:进程发起 I/O 请求或等待资源。
- 阻塞态 → 就绪态:等待的事件完成,进程重新进入就绪队列。
- 运行态 → 终止态:进程正常退出或被信号终止。
3.3 进程创建与终止
在 Linux 中,进程通过 fork() 系统调用创建子进程,子进程是父进程的副本。随后通常调用 exec() 系列函数加载新的程序映像。进程终止可通过 exit() 正常退出,或接收信号(如 SIGKILL)强制终止。
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
// 子进程
printf("子进程 PID: %d\n", getpid());
return 0;
} else if (pid > 0) {
// 父进程
wait(NULL);
printf("父进程等待子进程结束\n");
}
return 0;
}
4. 进程控制块(PCB)
4.1 PCB 的作用
进程控制块(Process Control Block,PCB)是操作系统用于描述和管理进程的数据结构,是进程存在的唯一标志。内核通过 PCB 感知进程的存在,并据此进行调度、资源分配和状态管理。
4.2 PCB 的核心内容
- 进程标识符(PID):唯一标识进程的数字。
- 进程状态:记录进程当前处于就绪、运行还是阻塞状态。
- 程序计数器:保存下一条要执行指令的地址。
- CPU 寄存器:保存进程切换时的现场信息。
- 调度信息:优先级、时间片、调度队列指针等。
- 内存管理信息:页表指针、代码段和数据段地址。
- I/O 状态信息:进程打开的文件描述符、I/O 设备等。
- 记账信息:CPU 使用时间、内存占用等统计信息。
4.3 Linux 中的 task_struct
在 Linux 内核中,PCB 对应 task_struct 结构体,定义在 include/linux/sched.h 中。每个进程对应一个 task_struct,内核通过双向链表管理所有进程。
5. 进程调度算法
5.1 先来先服务(FCFS)
FCFS 按进程到达就绪队列的顺序分配 CPU,实现简单但容易产生「长任务饥饿」问题,平均等待时间较长。
5.2 时间片轮转(RR)
RR 将 CPU 时间划分为固定时间片,按就绪队列顺序轮流分配。每个进程运行一个时间片后回到队尾,适合交互式系统,响应时间较短。
5.3 优先级调度
每个进程分配一个优先级,调度器优先选择优先级最高的进程运行。Linux 采用动态优先级机制,结合 nice 值和运行历史动态调整,避免低优先级进程长期得不到 CPU。
5.4 多级反馈队列
多级反馈队列将就绪队列分为多个优先级层级,新进程进入最高优先级队列,时间片用完后降入下一级。兼顾响应时间和吞吐量,是 Linux 实际采用的调度策略基础。
5.5 调度算法对比
| 算法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| FCFS | 实现简单、公平 | 平均等待时间长 | 批处理系统 |
| RR | 响应快、公平 | 时间片设置影响性能 | 交互式系统 |
| 优先级调度 | 灵活、可区分任务 | 可能产生饥饿 | 实时系统 |
| 多级反馈队列 | 兼顾响应与吞吐 | 参数调优复杂 | 通用操作系统 |
6. 进程间通信机制
6.1 管道(Pipe)
管道是最基本的 IPC 方式,分为匿名管道和命名管道(FIFO)。匿名管道用于父子进程间通信,数据单向流动;命名管道可用于无亲缘关系的进程间通信。
# 匿名管道示例:统计文件行数
cat /etc/passwd | wc -l
命名管道示例
mkfifo mypipe
echo "hello" > mypipe &
cat mypipe
6.2 信号(Signal)
信号用于通知进程发生了异步事件,是一种软件中断机制。常见信号包括 SIGINT(Ctrl+C)、SIGKILL(强制终止)、SIGTERM(正常终止)等。
# 向进程发送信号
kill -9 1234 # 强制终止 PID 为 1234 的进程
kill -TERM 1234 # 发送 SIGTERM 信号
6.3 消息队列(Message Queue)
消息队列是内核维护的消息链表,进程可以向队列写入或读取消息,支持按类型读取,适合进程间传递结构化数据。
#include <sys/msg.h>
// 创建消息队列
int msgid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
// 发送消息
msgsnd(msgid, &msg, sizeof(msg.mtext), 0);
// 接收消息
msgrcv(msgid, &msg, sizeof(msg.mtext), 0, 0);
6.4 共享内存(Shared Memory)
共享内存是最快的 IPC 方式,多个进程直接映射同一块物理内存区域,无需内核参与数据拷贝。通常需要配合信号量实现同步互斥。
#include <sys/shm.h>
// 创建共享内存
int shmid = shmget(IPC_PRIVATE, 4096, 0666 | IPC_CREAT);
// 映射到进程地址空间
void *addr = shmat(shmid, NULL, 0);
// 解除映射
shmdt(addr);
6.5 信号量(Semaphore)
信号量用于进程间同步与互斥,本质是一个计数器,支持 P(wait)和 V(signal)操作。常用于保护共享资源,防止多个进程同时访问造成数据不一致。
6.6 IPC 方式对比
| 方式 | 速度 | 适用场景 | 特点 |
|---|---|---|---|
| 管道 | 较慢 | 父子进程、简单数据流 | 单向、字节流 |
| 信号 | 快 | 异步事件通知 | 信息量小、非可靠 |
| 消息队列 | 中等 | 结构化消息传递 | 按类型读取、有界 |
| 共享内存 | 最快 | 大量数据交换 | 需同步机制配合 |
| 信号量 | 快 | 同步与互斥 | 不传数据、只做控制 |
7. 进程管理命令
7.1 ps:查看进程快照
ps 命令用于显示当前系统的进程状态快照,常用组合参数如下:
# 查看当前用户所有进程
ps -u
查看所有进程的详细信息
ps -ef
查看进程树
ps -ejH
结合 grep 查找特定进程
ps -ef | grep nginx
7.2 top:动态监控进程
top 命令实时显示系统资源占用和进程动态信息,按 CPU 或内存使用率排序,适合持续监控系统负载。
# 启动 top 交互界面
top
按内存使用率排序
top -o %MEM
指定进程 PID 监控
top -p 1234
7.3 kill:终止进程
kill 命令用于向进程发送信号,默认发送 SIGTERM,常用信号如下:
# 正常终止进程
kill 1234
强制终止进程
kill -9 1234
查看所有可用信号
kill -l
7.4 pstree:查看进程树
pstree 以树状结构展示进程间的父子关系,便于理解进程的继承结构。
# 显示进程树
pstree
显示 PID 信息
pstree -p
指定用户进程树
pstree -u username
7.5 其他常用命令
- pgrep:按名称查找进程 PID。
- nice / renice:调整进程优先级。
- jobs / bg / fg:管理后台任务。
- free:查看内存使用情况。
- uptime:查看系统负载。
8. 面试常考题
8.1 基础概念题
题目 1:进程和程序有什么区别?
参考答案:程序是静态的指令集合,存储在磁盘上;进程是程序的一次动态执行过程,包含代码、数据、堆栈和 PCB 等运行时信息。程序是持久的,进程有生命周期;一个程序可以对应多个进程实例。
题目 2:fork() 之后父子进程如何区分?
参考答案:fork() 在父进程中返回子进程的 PID,在子进程中返回 0。通过返回值判断当前处于哪个进程分支,返回值小于 0 表示创建失败。
8.2 原理分析题
题目 3:进程切换时操作系统需要保存哪些信息?
参考答案:需要保存程序计数器、CPU 寄存器、栈指针、进程状态、调度信息、内存管理信息等现场数据,这些信息保存在进程的 PCB 中,切换时恢复目标进程的现场。
题目 4:共享内存为什么需要配合信号量使用?
参考答案:共享内存允许多个进程直接访问同一块内存区域,但缺少同步机制会导致数据竞争。信号量提供互斥和同步控制,确保同一时刻只有一个进程写入,避免数据不一致。
题目 5:什么是僵尸进程?如何避免?
参考答案:子进程终止后,如果父进程未调用 wait() 回收其 PCB,子进程会进入僵尸状态。避免方法:父进程调用 wait()/waitpid() 回收子进程;或采用信号处理机制,在 SIGCHLD 信号中回收子进程。
8.3 实际操作题
题目 6:如何查看系统中占用 CPU 最高的前 5 个进程?
解题思路:使用 top 命令按 CPU 排序,或使用 ps -eo pid,comm,%cpu --sort=-%cpu | head -6 查看前 5 个进程。
题目 7:如何安全地终止一个失控进程?
解题思路:先用 ps -ef | grep 进程名 找到 PID,优先使用 kill PID 发送 SIGTERM 让进程自行清理;若无效再使用 kill -9 PID 强制终止。
题目 8:如何查看某个进程的父子关系?
解题思路:使用 pstree -p 查看完整进程树,或使用 ps -ef 查看 PPID 字段定位父进程 PID。
9. 总结
Linux 进程知识体系涵盖概念、生命周期、PCB、调度、IPC 和命令操作等多个层面。复习时建议先理解进程的本质和状态转换,再掌握 PCB 的结构与调度算法,最后通过实际操作命令加深记忆。面试中既要能讲清原理,也要能熟练演示命令操作,做到理论与实践结合。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)