diff --git a/notes/pics/fifo.png b/notes/pics/fifo.png new file mode 100644 index 00000000..6422dba6 Binary files /dev/null and b/notes/pics/fifo.png differ diff --git a/notes/pics/philosophers.png b/notes/pics/philosophers.png new file mode 100644 index 00000000..cac076a6 Binary files /dev/null and b/notes/pics/philosophers.png differ diff --git a/notes/pics/pipe.png b/notes/pics/pipe.png new file mode 100644 index 00000000..5bc7d457 Binary files /dev/null and b/notes/pics/pipe.png differ diff --git a/notes/pics/process-concurrent.png b/notes/pics/process-concurrent.png new file mode 100644 index 00000000..4ce4b747 Binary files /dev/null and b/notes/pics/process-concurrent.png differ diff --git a/notes/pics/process-mlfq.png b/notes/pics/process-mlfq.png new file mode 100644 index 00000000..ca8112ef Binary files /dev/null and b/notes/pics/process-mlfq.png differ diff --git a/notes/pics/process-rr.png b/notes/pics/process-rr.png new file mode 100644 index 00000000..aba55c69 Binary files /dev/null and b/notes/pics/process-rr.png differ diff --git a/notes/pics/process-states.png b/notes/pics/process-states.png new file mode 100644 index 00000000..2e589e47 Binary files /dev/null and b/notes/pics/process-states.png differ diff --git a/notes/pics/process-threads.png b/notes/pics/process-threads.png new file mode 100644 index 00000000..262660d6 Binary files /dev/null and b/notes/pics/process-threads.png differ diff --git a/notes/计算机操作系统 - 进程管理.md b/notes/计算机操作系统 - 进程管理.md index d1cd357e..aaaf5544 100644 --- a/notes/计算机操作系统 - 进程管理.md +++ b/notes/计算机操作系统 - 进程管理.md @@ -6,6 +6,7 @@ * [2. 线程](#2-线程) * [3. 区别](#3-区别) * [4. 协程](#4-协程) + * [5. 上下文切换](#5-上下文切换) * [进程状态的切换](#进程状态的切换) * [进程调度算法](#进程调度算法) * [1. 批处理系统](#1-批处理系统) @@ -26,6 +27,7 @@ * [4. 信号量](#4-信号量) * [5. 共享存储](#5-共享存储) * [6. 套接字](#6-套接字) + * [参考资料](#参考资料) @@ -37,9 +39,9 @@ 进程控制块 (Process Control Block, PCB) 描述进程的基本信息和运行状态,所谓的创建进程和撤销进程,都是指对 PCB 的操作。 -下图显示了 4 个程序创建了 4 个进程,这 4 个进程可以并发地执行。 +下图显示了 4 个进程在一个 CPU 上并发地执行。 -

+

### 2. 线程 @@ -49,7 +51,7 @@ QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 HTTP 请求线程、事件响应线程、渲染线程等等,线程的并发执行使得在浏览器中点击一个新链接从而发起 HTTP 请求时,浏览器还可以响应用户的其它事件。 -

+

### 3. 区别 @@ -81,9 +83,23 @@ QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 H Java 21 正式引入的虚拟线程([JEP 444](https://openjdk.org/jeps/444))就是 JVM 实现的协程:虚拟线程由 JVM 调度到少量的平台线程上运行,执行阻塞 I/O 时从平台线程上卸载,让平台线程去执行其它虚拟线程。虚拟线程的创建成本很低,不需要用线程池复用,需要限制并发数时使用信号量。在 JDK 21 中,虚拟线程在 synchronized 块内阻塞时无法卸载,JDK 24 起([JEP 491](https://openjdk.org/jeps/491))基本消除了这个限制。 +### 5. 上下文切换 + +上下文切换指的是 CPU 从执行一个线程改为执行另一个线程时,把当前线程的寄存器、程序计数器、栈指针等保存起来,再恢复另一个线程上次保存的值。以下情况会发生上下文切换: + +- 时间片用完,被调度程序换下; +- 线程主动阻塞,例如等待 I/O、等待锁、调用 sleep(); +- 更高优先级的线程进入就绪状态。 + +需要注意的是,系统调用只是从用户态进入内核态,执行完后通常回到同一个线程,不一定发生上下文切换。 + +同一进程中的线程切换不需要换地址空间;切换到另一个进程时还要切换页表,TLB 和 CPU 缓存中原来的地址映射和数据对新进程没有用,要重新加载,这部分间接开销通常比保存寄存器更大。 + +在 Linux 容器中用 taskset 把两个执行流固定在同一个 CPU 上,通过两根管道来回传 1 个字节,让它们交替运行:两个线程之间、两个进程之间每次切换都约 1.1 微秒(包含一次 read 或 write 系统调用)。数据量很小时两者几乎没有差别,进程切换的额外开销主要在工作集较大时体现出来。线程过多、频繁阻塞时,CPU 会把相当一部分时间花在切换上,可以用 vmstat 的 cs 列或 pidstat -w 观察每秒的切换次数。 + ## 进程状态的切换 -

+

- 就绪状态(ready):等待被调度 - 运行状态(running) @@ -131,7 +147,7 @@ Java 21 正式引入的虚拟线程([JEP 444](https://openjdk.org/jeps/444)) - 因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。 - 而如果时间片过长,那么实时性就不能得到保证。 -

+

**2.2 优先级调度** @@ -149,7 +165,7 @@ Java 21 正式引入的虚拟线程([JEP 444](https://openjdk.org/jeps/444)) 可以将这种调度算法看成是时间片轮转调度算法和优先级调度算法的结合。 -

+

### 3. 实时系统 @@ -323,7 +339,7 @@ end; ### 1. 哲学家进餐问题 -

+

五个哲学家围着一张圆桌,每个哲学家面前放着食物。哲学家的生活有两种交替活动:吃饭以及思考。当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子。 @@ -568,7 +584,7 @@ int pipe(int fd[2]); - 只支持半双工通信(单向交替传输); - 只能在父子进程或者兄弟进程中使用。 -

+

### 2. FIFO @@ -582,7 +598,7 @@ int mkfifoat(int fd, const char *path, mode_t mode); FIFO 常用于客户-服务器应用程序中,FIFO 用作汇聚点,在客户进程和服务器进程之间传递数据。 -

+

### 3. 消息队列 @@ -607,3 +623,8 @@ FIFO 常用于客户-服务器应用程序中,FIFO 用作汇聚点,在客户 ### 6. 套接字 与其它通信机制不同的是,它可用于不同机器间的进程通信。 + +## 参考资料 + +- Tanenbaum A S, Bos H. Modern Operating Systems [M]. Pearson, 2014. +- [pipe(7) — Linux manual page](https://man7.org/linux/man-pages/man7/pipe.7.html) diff --git a/tools/diagram/src/fifo.svg b/tools/diagram/src/fifo.svg new file mode 100644 index 00000000..b5f5ad17 --- /dev/null +++ b/tools/diagram/src/fifo.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + FIFO:不相关的进程通过路径名通信 + + 客户进程 1 + + + 客户进程 2 + + + 客户进程 3 + + + + + + + /tmp/server.fifo + + + 服务器进程 + mkfifo 在文件系统中创建一个特殊文件,任何有权限的进程都可以打开它读写,数据仍然经过内核,不写入磁盘。 + diff --git a/tools/diagram/src/philosophers.svg b/tools/diagram/src/philosophers.svg new file mode 100644 index 00000000..82f5f51a --- /dev/null +++ b/tools/diagram/src/philosophers.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + 哲学家进餐:5 个人、5 根筷子 + + + P0 + + + 0 + + P1 + + + 1 + + P2 + + + 2 + + P3 + + + 3 + + P4 + + + 4 + 如果每个人都先拿编号与自己相同的筷子: + P0 拿 0、P1 拿 1……P4 拿 4,再去拿另一根: + P0 等 4、P1 等 0……P4 等 3,形成环路,死锁。 + 解决方法: + ① 只有左右两根都能拿到时才拿(下面的代码); + ② 都先拿编号小的那根:P0 改为先拿 0 再拿 4, + 与 P1 争抢 0,环路被打破; + ③ 最多允许 4 个人同时拿筷子。 + diff --git a/tools/diagram/src/pipe.svg b/tools/diagram/src/pipe.svg new file mode 100644 index 00000000..5d3e6ea2 --- /dev/null +++ b/tools/diagram/src/pipe.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + 管道:fork 之后父进程写、子进程读 + + 父进程 + + 子进程 + + fd[1] 写端 + + fd[0] 关闭 + + fd[0] 读端 + + fd[1] 关闭 + + 内核中的管道缓冲区 + Linux 默认 64 KB + + + write(fd[1], "hello") + read(fd[0], buf) + pipe 返回两个文件描述符,fork 后父子进程都持有两端,各自关闭不用的一端,数据只能单向流动。 + diff --git a/tools/diagram/src/process-concurrent.svg b/tools/diagram/src/process-concurrent.svg new file mode 100644 index 00000000..94b9eac0 --- /dev/null +++ b/tools/diagram/src/process-concurrent.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + 4 个进程在一个 CPU 上并发执行 + 进程 A + + 进程 B + + 进程 C + + 进程 D + + + + + + + + + + + + + + + 时间 + 任一时刻只有一个进程在 CPU 上运行;操作系统按时间片轮流切换,宏观上看 4 个进程在同时推进。 + diff --git a/tools/diagram/src/process-mlfq.svg b/tools/diagram/src/process-mlfq.svg new file mode 100644 index 00000000..9ba84849 --- /dev/null +++ b/tools/diagram/src/process-mlfq.svg @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + 多级反馈队列:一个需要 100 个时间片的进程 + 队列 1 + + + + + + + + + 时间片 1 + 累计运行 1 + + 队列 2 + + + + + + + + + 时间片 2 + 累计运行 3 + + 队列 3 + + + + + + + + + 时间片 4 + 累计运行 7 + + 队列 4 + + + + + + + + 时间片 8 + 累计运行 15 + + 队列 5 + + + + + + + + 时间片 16 + 累计运行 31 + + 队列 6 + + + + + + + + 时间片 32 + 累计运行 63 + + 队列 7 + + + + + + + + 时间片 64 + 累计运行 100,完成 + 没用完时间片就进入下一级。这个进程只被调度 7 次,而时间片固定为 1 时要调度 100 次;新来的短进程在上面的高优先级队列中很快完成。 + diff --git a/tools/diagram/src/process-rr.svg b/tools/diagram/src/process-rr.svg new file mode 100644 index 00000000..b476548c --- /dev/null +++ b/tools/diagram/src/process-rr.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + 时间片轮转:时间片为 2,A、B、C 分别需要运行 5、3、2 + + A + + B + + C + + A + + B + + A + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + C 在 6 结束 + B 在 9 结束 + A 在 10 结束 + 用完时间片的进程回到队尾。平均完成时间 (10 + 9 + 6) / 3 ≈ 8.3;时间片越小响应越快,但切换次数越多, + 每次切换都要保存和恢复上下文。 + diff --git a/tools/diagram/src/process-states.svg b/tools/diagram/src/process-states.svg new file mode 100644 index 00000000..852a184b --- /dev/null +++ b/tools/diagram/src/process-states.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + 进程状态的切换 + + 新建 + + 就绪 + + 运行 + + 阻塞 + + 终止 + + 创建完成 + + 被调度 + + 时间片用完 + + 结束 + + 等待 I/O 或事件 + + I/O 完成 + 只有就绪和运行可以相互转换;阻塞的进程即使 CPU 空闲也不能运行,要先回到就绪队列。 + diff --git a/tools/diagram/src/process-threads.svg b/tools/diagram/src/process-threads.svg new file mode 100644 index 00000000..6a37f0b6 --- /dev/null +++ b/tools/diagram/src/process-threads.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + 浏览器进程中的多个线程 + + 浏览器进程 + 所有线程共享: + + 代码 + + 全局数据 + + 堆 + + 打开的文件、网络连接 + + 网络请求线程 + + 自己的栈 + + 寄存器、程序计数器 + 独立调度 + + 事件响应线程 + + 自己的栈 + + 寄存器、程序计数器 + 独立调度 + + 渲染线程 + + 自己的栈 + + 寄存器、程序计数器 + 独立调度 + 线程共享进程的内存,可以直接读写同一个变量通信;每个线程只有自己的栈和寄存器,所以创建和切换的开销比进程小。 +