1299 字
3 分钟
OS笔记(三):进程控制
OS笔记(三):进程控制
本文是操作系统笔记系列的第三篇,详细讲解操作系统如何通过原语来控制进程的创建、终止、阻塞、唤醒和切换。
📚 目录
一、什么是原语?
1.1 原语的定义
原语(Primitive)是操作系统内核中由若干指令组成的原子操作,执行过程一气呵成,不可中断。
1.2 如何保证原子性?
使用 关中断指令 和 开中断指令 这两个特权指令:
关中断指令 ← 关闭中断响应 操作1 操作2 ...开中断指令 ← 恢复中断响应原理:CPU执行关中断指令后,不再检查中断信号,直到执行开中断指令后才恢复。这样中间的指令序列就不可被中断了。
1.3 为什么需要原语?
进程控制涉及修改PCB、队列等关键数据结构,如果不原子执行,可能出现数据不一致的错误。
二、进程创建
2.1 创建原语的执行步骤
| 步骤 | 操作 |
|---|---|
| 1 | 申请空白PCB |
| 2 | 为新进程分配所需资源(内存、文件等) |
| 3 | 初始化PCB(PID、程序计数器、寄存器等) |
| 4 | 将PCB插入就绪队列 |
2.2 引起进程创建的事件
| 事件 | 说明 | 示例 |
|---|---|---|
| 用户登录 | 用户登录系统时创建Shell进程 | SSH登录 |
| 作业调度 | 从外存调入作业执行 | 批处理系统 |
| 提供服务 | OS为用户请求创建服务进程 | 打印请求 |
| 应用请求 | 进程主动创建子进程 | fork()系统调用 |
三、进程终止
3.1 撤销原语的执行步骤
| 步骤 | 操作 |
|---|---|
| 1 | 从PCB集合中找到终止进程的PCB |
| 2 | 若进程正在运行,立即剥夺CPU |
| 3 | 终止其所有子进程(级联终止) |
| 4 | 将该进程拥有的所有资源归还给父进程或OS |
| 5 | 删除PCB |
3.2 引起进程终止的事件
| 事件 | 说明 | 示例 |
|---|---|---|
| 正常结束 | 进程执行完毕 | 程序运行结束 |
| 异常结束 | 遇到致命错误 | 段错误、除零错误 |
| 外界干预 | 被其他进程或管理员终止 | kill命令 |
四、进程阻塞与唤醒
4.1 阻塞原语
| 步骤 | 操作 |
|---|---|
| 1 | 找到要阻塞的进程对应的PCB |
| 2 | 保护进程运行现场,将PCB状态设为阻塞态 |
| 3 | 将PCB插入相应事件的等待队列 |
4.2 唤醒原语
| 步骤 | 操作 |
|---|---|
| 1 | 在事件等待队列中找到PCB |
| 2 | 将PCB从等待队列中移除,设为就绪态 |
| 3 | 将PCB插入就绪队列 |
注意:阻塞是进程主动调用的(如等待I/O),唤醒是由其他进程或OS在事件完成时调用的。
五、进程切换
5.1 切换原语的执行步骤
| 步骤 | 操作 |
|---|---|
| 1 | 将运行环境信息(寄存器值等)存入当前进程的PCB |
| 2 | 将当前PCB移入相应队列(就绪/阻塞) |
| 3 | 选择另一个进程执行,更新其PCB |
| 4 | 根据新进程的PCB恢复运行环境 |
5.2 引起进程切换的事件
| 事件 | 说明 |
|---|---|
| 时间片用完 | 当前进程时间片到,切换到下一个就绪进程 |
| 进程阻塞 | 当前进程主动阻塞,切换到其他就绪进程 |
| 更高优先级进程就绪 | 抢占式调度下,高优先级进程抢占CPU |

六、Linux实战:进程控制
6.1 创建进程:fork()
# 在C语言中使用fork()创建子进程cat > fork_demo.c << 'EOF'#include <stdio.h>#include <unistd.h>
int main() { pid_t pid = fork();
if (pid > 0) { // 父进程 printf("父进程 PID=%d, 子进程 PID=%d\n", getpid(), pid); } else if (pid == 0) { // 子进程 printf("子进程 PID=%d, 父进程 PID=%d\n", getpid(), getppid()); }
return 0;}EOF
gcc fork_demo.c -o fork_demo./fork_demo6.2 终止进程
# 正常终止kill <PID>
# 发送SIGTERM信号(可被捕获)kill -15 <PID>
# 强制终止(不可捕获)kill -9 <PID>
# 按名称终止pkill -f "python3 my_script.py"
# 终止所有同名进程killall python36.3 进程状态管理
# 暂停进程(发送SIGSTOP)kill -STOP <PID>
# 恢复进程(发送SIGCONT)kill -CONT <PID>
# 实际操作sleep 100 &PID=$!echo "进程 PID=$PID 已启动"
sleep 2kill -STOP $PIDecho "进程已暂停"
sleep 2kill -CONT $PIDecho "进程已恢复"6.4 查看进程关系
# 查看进程树pstree -p
# 查看特定进程的父进程ps -p <PID> -o pid,ppid,cmd
# 查看子进程ps --ppid <PID>6.5 Shell中的进程控制
# 前台运行./my_program
# 后台运行./my_program &
# 查看后台任务jobs
# 切换到前台fg %1
# 继续后台运行bg %1
# nohup保持运行(退出终端后继续)nohup ./my_program &七、本章小结
核心概念
| 概念 | 要点 |
|---|---|
| 原语 | 原子操作,用关中断/开中断保证 |
| 创建原语 | 申请PCB→分配资源→初始化→插入就绪队列 |
| 终止原语 | 找PCB→剥夺CPU→终止子进程→回收资源→删除PCB |
| 阻塞原语 | 保护现场→设阻塞态→插入等待队列 |
| 唤醒原语 | 从等待队列移除→设就绪态→插入就绪队列 |
| 切换原语 | 保存环境→移队列→选新进程→恢复环境 |
考研/期末常见考点
- 原语的原子性如何保证(关中断/开中断)
- 五种原语的执行步骤
- 阻塞和唤醒的关系(阻塞主动,唤醒被动)
- fork()的工作原理(子进程复制父进程)
思考题
- 为什么原语要用关中断/开中断来保证原子性?
- 进程被阻塞后,是谁把它唤醒的?
- fork()之后,父进程和子进程的执行顺序是怎样的?
上一篇:OS笔记(二):进程的状态与转换 下一篇:OS笔记(四):进程通信
分享
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
相关文章 智能推荐
1
OS笔记(一):进程与线程简介
操作系统 深入理解操作系统中进程与线程的概念、组成、特征,以及它们在Linux系统中的实际表现
2
OS笔记(二):进程的状态与转换
操作系统 深入理解操作系统中进程的五种状态、状态转换条件,以及在Linux中如何观察进程状态
3
OS笔记(六):调度的概念、层次
操作系统 深入理解操作系统中调度的三个层次、七状态模型,以及Linux进程调度机制
4
OS笔记(四):进程通信
操作系统 深入理解操作系统中进程间通信的三种方式:共享存储、消息传递、管道通信,以及Linux IPC实战
5
OS笔记(五):线程
操作系统 深入理解操作系统中线程的概念、实现方式、多线程模型,以及Linux线程管理实践








