四、进程状态:生命周期管理
4.1 进程状态的设计意义
问题背景:CPU资源有限,进程数量众多。如何高效管理进程的执行顺序和资源分配?
解决方案:引入进程状态,实现进程的有序调度。
4.2 Linux进程状态详解
状态定义(Linux内核源码):
static const char *const task_state_array[] = { "R (running)", /* 运行状态 */ "S (sleeping)", /* 睡眠状态 */ "D (disk sleep)", /* 磁盘休眠 */ "T (stopped)", /* 停止状态 */ "t (tracing stop)", /* 跟踪停止 */ "X (dead)", /* 死亡状态 */ "Z (zombie)", /* 僵尸状态 */};
状态说明:
| | |
|---|
| R (Running) | | |
| S (Sleeping) | | |
| D (Disk Sleep) | | |
| T (Stopped) | | |
| Z (Zombie) | | |
4.3 僵尸进程:危害与预防
形成原因:子进程退出,父进程未调用wait()读取退出状态。
危害: - PCB无法释放,占用内存 - 大量僵尸进程导致内存泄漏 - 系统资源浪费
示例代码:
#include#include#includeintmain(){ pid_t pid = fork(); if (pid > 0) { // 父进程:不回收子进程 printf("父进程PID: %d,睡眠30秒\n", getpid()); sleep(30); } else if (pid == 0) { // 子进程:5秒后退出 printf("子进程PID: %d,即将成为僵尸\n", getpid()); sleep(5); exit(0); } return 0;}
查看僵尸进程:
# 编译运行gcc zombie.c -o zombie./zombie# 另一个终端查看ps aux | grep zombie# 输出: 表示僵尸状态
预防方法:
// 父进程调用wait()回收子进程#includewait(NULL); // 或 waitpid()
4.4 孤儿进程:自动领养
形成原因:父进程先退出,子进程成为孤儿。
处理机制:被init进程(PID=1)领养,由init负责回收。
示例代码:
#include #include #include int main() { pid_t pid = fork(); if (pid > 0) { // 父进程:3秒后退出 printf("父进程PID: %d,即将退出\n", getpid()); sleep(3); exit(0); } else if (pid == 0) { // 子进程:继续运行 printf("子进程PID: %d,父进程PID: %d\n", getpid(), getppid()); sleep(10); printf("子进程被领养,新父进程PID: %d\n", getppid()); } return 0;}
五、进程优先级:资源竞争的仲裁
5.1 优先级的必要性
问题背景:CPU资源稀缺,进程众多。如何公平高效分配CPU时间?
解决方案:引入优先级机制,重要进程优先执行。
5.2 PRI与NI的关系
概念辨析: - PRI (Priority):进程优先级,值越小优先级越高 - NI (Nice):优先级修正值,范围-20到19
计算公式:
PRI(new) = PRI(old) + nice
实际应用:
# 查看进程优先级ps -l# 输出示例:F UID PID PPID PRI NI SZ STIME TTY TIME CMD0 1000 1234 1233 80 0 1024 10:00 pts/0 00:00:01 bash
调整优先级:
# 方法1:top命令top → 按'r' → 输入PID → 输入nice值# 方法2:renice命令renice -n 5 -p 1234 # 将PID 1234的nice值改为5# 方法3:启动时设置nice -n 10 ./my_program # 以nice=10启动程序
编程接口:
#include// 获取优先级intgetpriority(int which, int who);// 设置优先级intsetpriority(int which, int who, int prio);
5.3 进程调度相关概念
核心概念:
六、进程切换:CPU上下文管理
6.1 上下文切换的本质
设计目的:实现并发执行,让多个进程共享CPU。
切换过程:
保存当前进程上下文 → 选择下一个进程 → 加载新进程上下文 → 执行新进程
上下文内容: - CPU寄存器内容 - 程序计数器(PC) - 栈指针(SP) - 其他处理器状态
6.2 时间片轮转
机制:每个进程分配固定时间片,时间片用完强制切换。
实现:
// 时间片本质是一个计数器struct task_struct { unsigned long time_slice; // 剩余时间片};
6.3 Linux O(1)调度算法
设计目标:调度时间复杂度为O(1),不随进程数量增加而降低性能。
核心结构:
struct prio_array { unsigned int nr_active; // 活动进程数量 DECLARE_BITMAP(bitmap, MAX_PRIO+1); // 位图:快速查找 struct list_head queue[MAX_PRIO]; // 140个优先级队列};
调度流程:
1. 从位图找到最高优先级的非空队列2. 取出队列第一个进程执行3. 时间片用完,移到过期队列4. 活动队列空后,交换active和expired指针
效率保证: - 位图查找:O(1) - 队列操作:O(1) - 指针交换:O(1)
想要了解更多内容,请阅读下列书籍,也可以关注小飞连载更多内容