From 0da4c192b61e72f4cdc88f4453ebecad593c2659 Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Wed, 28 Oct 2026 22:51:26 +0800 Subject: [PATCH 1/7] Update HTTP.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit “HTTP/1.1 新特性”移到“HTTP/2.0”之前,按版本顺序阅读。 --- notes/HTTP.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/notes/HTTP.md b/notes/HTTP.md index ec0de170..910506c7 100644 --- a/notes/HTTP.md +++ b/notes/HTTP.md @@ -44,13 +44,13 @@ * [TLS 握手](#tls-握手) * [完整性保护](#完整性保护) * [HTTPS 的缺点](#https-的缺点) - * [七、HTTP/2.0](#七http20) + * [七、HTTP/1.1 新特性](#七http11-新特性) + * [八、HTTP/2.0](#八http20) * [HTTP/1.x 缺陷](#http1x-缺陷) * [二进制分帧层](#二进制分帧层) * [服务端推送](#服务端推送) * [首部压缩](#首部压缩) * [HTTP/3](#http3) - * [八、HTTP/1.1 新特性](#八http11-新特性) * [九、GET 和 POST 比较](#九get-和-post-比较) * [作用](#作用) * [参数](#参数) @@ -994,7 +994,18 @@ HTTPS 的报文摘要功能之所以安全,是因为计算摘要时用到了 - 因为需要进行加密解密等过程,因此速度会更慢; - 需要申请和维护证书,不过 Let's Encrypt 等机构可以免费签发证书。 -## 七、HTTP/2.0 +## 七、HTTP/1.1 新特性 + +与 HTTP/1.0 相比,HTTP/1.1 主要增加了以下特性,详细内容见上文: + +- 默认使用长连接; +- 支持流水线; +- 请求必须带 Host 首部,从而支持虚拟主机; +- 新增状态码 100 Continue; +- 支持分块传输编码; +- 新增缓存处理指令 max-age 和条件请求 If-None-Match。 + +## 八、HTTP/2.0 ### HTTP/1.x 缺陷 @@ -1050,17 +1061,6 @@ UDP 本身不保证可靠,QUIC 在它之上自己实现了确认、重传和 浏览器通常先用 HTTP/2 访问,从响应首部 Alt-Svc 得知服务器支持 HTTP/3 后再切换,例如 www.example.com 的响应中有 alt-svc: h3=":443"; ma=86400。 -## 八、HTTP/1.1 新特性 - -与 HTTP/1.0 相比,HTTP/1.1 主要增加了以下特性,详细内容见上文: - -- 默认使用长连接; -- 支持流水线; -- 请求必须带 Host 首部,从而支持虚拟主机; -- 新增状态码 100 Continue; -- 支持分块传输编码; -- 新增缓存处理指令 max-age 和条件请求 If-None-Match。 - ## 九、GET 和 POST 比较 ### 作用 From 4ca59d4aeb5c327d13f830003e410b908a452640 Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Wed, 28 Oct 2026 22:51:26 +0800 Subject: [PATCH 2/7] =?UTF-8?q?Update=20=E8=AE=A1=E7=AE=97=E6=9C=BA?= =?UTF-8?q?=E7=BD=91=E7=BB=9C=20-=20=E4=BC=A0=E8=BE=93=E5=B1=82.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 慢开始改为每收到一个确认 cwnd 加 1、每个轮次翻倍;“在 UDP 上实现可靠传输”移到“TCP 粘包与拆包”之后。 --- notes/计算机网络 - 传输层.md | 42 ++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/notes/计算机网络 - 传输层.md b/notes/计算机网络 - 传输层.md index e8f4d99c..39828fcd 100644 --- a/notes/计算机网络 - 传输层.md +++ b/notes/计算机网络 - 传输层.md @@ -3,7 +3,6 @@ * [计算机网络 - 传输层](#计算机网络---传输层) * [UDP 和 TCP 的特点](#udp-和-tcp-的特点) * [选择 TCP 还是 UDP](#选择-tcp-还是-udp) - * [在 UDP 上实现可靠传输](#在-udp-上实现可靠传输) * [UDP 首部格式](#udp-首部格式) * [TCP 首部格式](#tcp-首部格式) * [TCP 的三次握手](#tcp-的三次握手) @@ -20,6 +19,7 @@ * [1. 慢开始与拥塞避免](#1-慢开始与拥塞避免) * [2. 快重传与快恢复](#2-快重传与快恢复) * [TCP 粘包与拆包](#tcp-粘包与拆包) + * [在 UDP 上实现可靠传输](#在-udp-上实现可靠传输) * [附录:实测](#附录实测) * [A1 TCP 与 UDP 发送位置更新](#a1-tcp-与-udp-发送位置更新) * [A2 握手报文丢失](#a2-握手报文丢失) @@ -52,23 +52,7 @@ TCP 的可靠和按序是有代价的:一个报文段丢失后,后面已经 5 次实测中,TCP 的 600 条更新全部到达,但有 24~53 条的延迟超过 100 ms,最长 307~939 ms,到达时位置早已过时;UDP 丢失了 23~30 条,到达的延迟都不超过 44 ms,丢失的那一条由 50 ms 后的下一次更新补上。 -### 在 UDP 上实现可靠传输 - -游戏中的位置可以丢,购买道具、释放技能这类消息却必须送达。既要 UDP 的低延迟、又有一部分数据必须可靠时,就要在应用层按 TCP 的思路自己实现: - -- 序号:给每个数据包编号,接收方据此去重和排序; -- 确认和重传:接收方回复确认,发送方超时没有收到确认就重传,超时时间根据测得的 RTT 计算; -- 流量控制和拥塞控制:限制已发送但未确认的数据量,避免压垮接收方和网络。 - -与直接使用 TCP 相比,好处在于可以按需取舍:只对需要的消息重传,不相关的消息互不阻塞,重传和拥塞控制的参数也可以按业务调整。 - -HTTP/3 使用的 QUIC 就是在 UDP 上实现的可靠传输,并改进了 TCP 的几处设计: - -- 包号只增不减,丢失的数据放进一个新编号的包中重传。收到确认时能分清确认的是哪一次发送,RTT 测得更准;TCP 重传时沿用原来的序号,无法区分; -- 数据按流(Stream)组织,每个流按帧中的偏移量单独排序,一个流丢包不影响其它流; -- 一个确认帧可以携带很多个已收到的区间,TCP 的选择确认(SACK)受首部长度限制,通常最多携带 3 个区间。 - -QUIC 选择在 UDP 上重新实现,而不是改进 TCP,是因为 TCP 实现在操作系统内核中,改动要等操作系统升级才能普及;防火墙、NAT 等中间设备还会检查甚至改写 TCP 首部,不认识的新选项可能被丢弃。QUIC 运行在用户态,随浏览器和 App 一起更新,并且加密了大部分首部,中间设备无法干预。QUIC 的握手和连接迁移见 [HTTP/3](HTTP.md#http3)。 +有一部分数据必须送达时,可以在 UDP 之上自己实现可靠传输,见[在 UDP 上实现可靠传输](#在-udp-上实现可靠传输)。 ## UDP 首部格式 @@ -222,7 +206,7 @@ TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、 ### 1. 慢开始与拥塞避免 -发送的最初执行慢开始,令 cwnd = 1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 ... +发送的最初执行慢开始,令 cwnd = 1,发送方只能发送 1 个报文段;每收到一个对新数据的确认,cwnd 加 1,一个轮次发出的报文段都被确认后 cwnd 就翻了一倍,因此之后每个轮次能够发送的报文段数量为:2、4、8 ... 实际实现中初始拥塞窗口通常更大,RFC 6928 建议初始值为 10 个报文段。 @@ -293,6 +277,26 @@ static String receive(DataInputStream in) throws IOException { 需要注意的是,长度字段来自网络,必须限制上限,否则对方发来一个 2 GB 的长度,服务器就会为它分配 2 GB 内存。 +## 在 UDP 上实现可靠传输 + +[选择 TCP 还是 UDP](#选择-tcp-还是-udp) 中比较了游戏位置更新用 TCP 和 UDP 发送的效果。 + +游戏中的位置可以丢,购买道具、释放技能这类消息却必须送达。既要 UDP 的低延迟、又有一部分数据必须可靠时,就要在应用层按 TCP 的思路自己实现: + +- 序号:给每个数据包编号,接收方据此去重和排序; +- 确认和重传:接收方回复确认,发送方超时没有收到确认就重传,超时时间根据测得的 RTT 计算; +- 流量控制和拥塞控制:限制已发送但未确认的数据量,避免压垮接收方和网络。 + +与直接使用 TCP 相比,好处在于可以按需取舍:只对需要的消息重传,不相关的消息互不阻塞,重传和拥塞控制的参数也可以按业务调整。 + +HTTP/3 使用的 QUIC 就是在 UDP 上实现的可靠传输,并改进了 TCP 的几处设计: + +- 包号只增不减,丢失的数据放进一个新编号的包中重传。收到确认时能分清确认的是哪一次发送,RTT 测得更准;TCP 重传时沿用原来的序号,无法区分; +- 数据按流(Stream)组织,每个流按帧中的偏移量单独排序,一个流丢包不影响其它流; +- 一个确认帧可以携带很多个已收到的区间,TCP 的选择确认(SACK)受首部长度限制,通常最多携带 3 个区间。 + +QUIC 选择在 UDP 上重新实现,而不是改进 TCP,是因为 TCP 实现在操作系统内核中,改动要等操作系统升级才能普及;防火墙、NAT 等中间设备还会检查甚至改写 TCP 首部,不认识的新选项可能被丢弃。QUIC 运行在用户态,随浏览器和 App 一起更新,并且加密了大部分首部,中间设备无法干预。QUIC 的握手和连接迁移见 [HTTP/3](HTTP.md#http3)。 + ## 附录:实测 以下实验都在 Docker Desktop(Apple M1 Pro)中的 Ubuntu 24.04 容器里进行,内核为 6.10.14-linuxkit,客户端和服务器都在 127.0.0.1 上,用 iptables 丢弃指定的报文,用 tc netem 模拟时延和丢包,tcpdump 的时间换算为相对第一个报文的秒数。容器中的默认参数: From dc78f9462093ed120117cab8934a8bc5b1418dca Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Wed, 28 Oct 2026 22:51:26 +0800 Subject: [PATCH 3/7] =?UTF-8?q?Update=20=E8=AE=A1=E7=AE=97=E6=9C=BA?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E7=B3=BB=E7=BB=9F=20-=20=E8=BF=9B=E7=A8=8B?= =?UTF-8?q?=E7=AE=A1=E7=90=86.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit “上下文切换”移到“协程”之前;读者-写者问题的写者优先与公平实现移到进阶篇。 --- notes/计算机操作系统 - 进程管理.md | 143 ++++------------------------- 1 file changed, 18 insertions(+), 125 deletions(-) diff --git a/notes/计算机操作系统 - 进程管理.md b/notes/计算机操作系统 - 进程管理.md index 55fcb267..dce7888e 100644 --- a/notes/计算机操作系统 - 进程管理.md +++ b/notes/计算机操作系统 - 进程管理.md @@ -5,8 +5,8 @@ * [1. 进程](#1-进程) * [2. 线程](#2-线程) * [3. 区别](#3-区别) - * [4. 协程](#4-协程) - * [5. 上下文切换](#5-上下文切换) + * [4. 上下文切换](#4-上下文切换) + * [5. 协程](#5-协程) * [进程状态的切换](#进程状态的切换) * [进程调度算法](#进程调度算法) * [1. 批处理系统](#1-批处理系统) @@ -74,7 +74,21 @@ QQ 和浏览器是两个进程,浏览器进程里面有很多线程,例如 H - 任务之间需要隔离、一个出错不能拖垮其它任务时用多进程,例如 Chrome 把网页放在单独的渲染进程中,一个网页崩溃不影响浏览器和其它标签页;执行不受信任的代码(例如 Agent 运行模型生成的脚本)也应该放在单独的进程甚至容器中; - 任务之间要频繁共享数据、对创建和切换的开销敏感时用多线程,例如 Web 服务器用线程池处理请求,各线程直接读写同一份本地缓存。 -### 4. 协程 +### 4. 上下文切换 + +上下文切换指的是 CPU 从执行一个线程改为执行另一个线程时,把当前线程的寄存器、程序计数器、栈指针等保存起来,再恢复另一个线程上次保存的值。以下情况会发生上下文切换: + +- 时间片用完,被调度程序换下; +- 线程主动阻塞,例如等待 I/O、等待锁、调用 sleep(); +- 更高优先级的线程进入就绪状态。 + +需要注意的是,系统调用只是从用户态进入内核态,执行完后通常回到同一个线程,不一定发生上下文切换。 + +同一进程中的线程切换不需要换地址空间;切换到另一个进程时还要切换页表,TLB 和 CPU 缓存中原来的地址映射和数据对新进程没有用,要重新加载,这部分间接开销通常比保存寄存器更大。x86 的 PCID 可以给 TLB 表项标上所属的地址空间,切换页表时不必清空整个 TLB,没有 PCID 时每次切换页表都会清空 TLB。 + +例如把两个执行流固定在同一个 CPU 上,通过两根管道来回传 1 个字节,让它们交替运行,两个线程之间、两个进程之间每次切换都约 1.1 微秒,包含一次 read 或 write 系统调用(Linux 实测,见[附录 A1](#a1-上下文切换))。数据量很小时两者几乎没有差别,进程切换的额外开销主要在工作集较大时体现出来。线程过多、频繁阻塞时,CPU 会把相当一部分时间花在切换上,可以用 vmstat 的 cs 列或 pidstat -w 观察每秒的切换次数。 + +### 5. 协程 协程(Coroutine)是在用户态实现的轻量级线程,由程序自己调度,而不是由操作系统内核调度。协程在等待 I/O 时主动让出执行权,运行时再把其它协程调度到同一个线程上执行。 @@ -97,20 +111,6 @@ Java 21 正式引入的虚拟线程([JEP 444](https://openjdk.org/jeps/444)) 协程也不能无限创建:每个协程的栈和状态都要占内存,例如把最大堆设为 256 MB 时,启动 100 万个阻塞的虚拟线程会抛出 OutOfMemoryError。协程适合大量任务都在等待 I/O 的场景,例如每个请求都要调用下游接口、查询数据库;CPU 密集的任务没有等待可以让出,开再多协程也只能由少量线程轮流执行,不会更快。 -### 5. 上下文切换 - -上下文切换指的是 CPU 从执行一个线程改为执行另一个线程时,把当前线程的寄存器、程序计数器、栈指针等保存起来,再恢复另一个线程上次保存的值。以下情况会发生上下文切换: - -- 时间片用完,被调度程序换下; -- 线程主动阻塞,例如等待 I/O、等待锁、调用 sleep(); -- 更高优先级的线程进入就绪状态。 - -需要注意的是,系统调用只是从用户态进入内核态,执行完后通常回到同一个线程,不一定发生上下文切换。 - -同一进程中的线程切换不需要换地址空间;切换到另一个进程时还要切换页表,TLB 和 CPU 缓存中原来的地址映射和数据对新进程没有用,要重新加载,这部分间接开销通常比保存寄存器更大。x86 的 PCID 可以给 TLB 表项标上所属的地址空间,切换页表时不必清空整个 TLB,没有 PCID 时每次切换页表都会清空 TLB。 - -例如把两个执行流固定在同一个 CPU 上,通过两根管道来回传 1 个字节,让它们交替运行,两个线程之间、两个进程之间每次切换都约 1.1 微秒,包含一次 read 或 write 系统调用(Linux 实测,见[附录 A1](#a1-上下文切换))。数据量很小时两者几乎没有差别,进程切换的额外开销主要在工作集较大时体现出来。线程过多、频繁阻塞时,CPU 会把相当一部分时间花在切换上,可以用 vmstat 的 cs 列或 pidstat -w 观察每秒的切换次数。 - ## 进程状态的切换

@@ -468,114 +468,7 @@ void writer() { } ``` -以下内容由 [@Bandi Yugandhar](https://github.com/yugandharbandi) 提供。 - -上面的实现可能导致写者饥饿。下面的实现是写者优先的:写者进入队列之后,只需要等待在它之前进入的读者,不会被之后到达的读者一直阻塞。 - -```c -int readcount, writecount; //(initial value = 0) -semaphore rmutex, wmutex, readLock, resource; //(initial value = 1) - -//READER -void reader() { - - down(&readLock); // reader is trying to enter - down(&rmutex); // lock to increase readcount - readcount++; - if (readcount == 1) - down(&resource); //if you are the first reader then lock the resource - up(&rmutex); //release for other readers - up(&readLock); //Done with trying to access the resource - - -//reading is performed - - - down(&rmutex); //reserve exit section - avoids race condition with readers - readcount--; //indicate you're leaving - if (readcount == 0) //checks if you are last reader leaving - up(&resource); //if last, you must release the locked resource - up(&rmutex); //release exit section for other readers -} - -//WRITER -void writer() { - - down(&wmutex); //reserve entry section for writers - avoids race conditions - writecount++; //report yourself as a writer entering - if (writecount == 1) //checks if you're first writer - down(&readLock); //if you're first, then you must lock the readers out. Prevent them from trying to enter CS - up(&wmutex); //release entry section - - - down(&resource); //reserve the resource for yourself - prevents other writers from simultaneously editing the shared resource - //writing is performed - up(&resource); //release file - - - down(&wmutex); //reserve exit section - writecount--; //indicate you're leaving - if (writecount == 0) //checks if you're the last writer - up(&readLock); //if you're last writer, you must unlock the readers. Allows them to try enter CS for reading - up(&wmutex); //release exit section -} -``` - -每个读者都必须先获取 readLock,而只有第一个写者需要获取 readLock,并且直到队列中没有写者时才会释放,因此这种实现可能导致读者饥饿。 - -以上两种实现总有一方可能饥饿。下面的实现使用一个先进先出的 serviceQueue 让读者和写者按到达顺序排队,获取共享数据的锁总能在有限时间内完成,因此不会出现饥饿。 - -```c -int readCount; // init to 0; number of readers currently accessing resource - -// all semaphores initialised to 1 -Semaphore resourceAccess; // controls access (read/write) to the resource -Semaphore readCountAccess; // for syncing changes to shared variable readCount -Semaphore serviceQueue; // FAIRNESS: preserves ordering of requests (signaling must be FIFO) - -void writer() -{ - down(&serviceQueue); // wait in line to be serviced - // - down(&resourceAccess); // request exclusive access to resource - // - up(&serviceQueue); // let next in line be serviced - - // - writeResource(); // writing is performed - // - - // - up(&resourceAccess); // release resource access for next reader/writer - // -} - -void reader() -{ - down(&serviceQueue); // wait in line to be serviced - down(&readCountAccess); // request exclusive access to readCount - // - if (readCount == 0) // if there are no readers already reading: - down(&resourceAccess); // request resource access for readers (writers blocked) - readCount++; // update count of active readers - // - up(&serviceQueue); // let next in line be serviced - up(&readCountAccess); // release access to readCount - - // - readResource(); // reading is performed - // - - down(&readCountAccess); // request exclusive access to readCount - // - readCount--; // update count of active readers - if (readCount == 0) // if there are no readers left: - up(&resourceAccess); // release resource access for all - // - up(&readCountAccess); // release access to readCount -} - -``` +这种实现在读者源源不断时可能导致写者饥饿,写者优先和按到达顺序排队的公平实现见[进阶:读者-写者问题](计算机操作系统%20-%20进程管理%20-%20进阶.md#读者-写者问题)。 ## 进程通信 From 1859904950d95940215a748419390c3a830dcdb0 Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Wed, 28 Oct 2026 22:51:26 +0800 Subject: [PATCH 4/7] =?UTF-8?q?Update=20=E8=AE=A1=E7=AE=97=E6=9C=BA?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E7=B3=BB=E7=BB=9F=20-=20=E8=BF=9B=E7=A8=8B?= =?UTF-8?q?=E7=AE=A1=E7=90=86=20-=20=E8=BF=9B=E9=98=B6.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增进阶篇,收录读者-写者问题的写者优先与公平实现。 --- notes/计算机操作系统 - 进程管理 - 进阶.md | 128 ++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 notes/计算机操作系统 - 进程管理 - 进阶.md diff --git a/notes/计算机操作系统 - 进程管理 - 进阶.md b/notes/计算机操作系统 - 进程管理 - 进阶.md new file mode 100644 index 00000000..512911ca --- /dev/null +++ b/notes/计算机操作系统 - 进程管理 - 进阶.md @@ -0,0 +1,128 @@ +# 计算机操作系统 - 进程管理 - 进阶 + +* [计算机操作系统 - 进程管理 - 进阶](#计算机操作系统---进程管理---进阶) + * [经典同步问题](#经典同步问题) + * [读者-写者问题](#读者-写者问题) + * [参考资料](#参考资料) + + + +## 经典同步问题 + +### 读者-写者问题 + +主线见[计算机操作系统 - 进程管理 · 读者-写者问题](计算机操作系统%20-%20进程管理.md#2-读者-写者问题),那里用 count、count_mutex 和 data_mutex 实现的是读者优先:只要还有读者在读,后到的读者可以直接进入,写者要一直等待。 + +以下内容由 [@Bandi Yugandhar](https://github.com/yugandharbandi) 提供。 + +主线中的实现可能导致写者饥饿。下面的实现是写者优先的:写者进入队列之后,只需要等待在它之前进入的读者,不会被之后到达的读者一直阻塞。 + +```c +int readcount, writecount; //(initial value = 0) +semaphore rmutex, wmutex, readLock, resource; //(initial value = 1) + +//READER +void reader() { + + down(&readLock); // reader is trying to enter + down(&rmutex); // lock to increase readcount + readcount++; + if (readcount == 1) + down(&resource); //if you are the first reader then lock the resource + up(&rmutex); //release for other readers + up(&readLock); //Done with trying to access the resource + + +//reading is performed + + + down(&rmutex); //reserve exit section - avoids race condition with readers + readcount--; //indicate you're leaving + if (readcount == 0) //checks if you are last reader leaving + up(&resource); //if last, you must release the locked resource + up(&rmutex); //release exit section for other readers +} + +//WRITER +void writer() { + + down(&wmutex); //reserve entry section for writers - avoids race conditions + writecount++; //report yourself as a writer entering + if (writecount == 1) //checks if you're first writer + down(&readLock); //if you're first, then you must lock the readers out. Prevent them from trying to enter CS + up(&wmutex); //release entry section + + + down(&resource); //reserve the resource for yourself - prevents other writers from simultaneously editing the shared resource + //writing is performed + up(&resource); //release file + + + down(&wmutex); //reserve exit section + writecount--; //indicate you're leaving + if (writecount == 0) //checks if you're the last writer + up(&readLock); //if you're last writer, you must unlock the readers. Allows them to try enter CS for reading + up(&wmutex); //release exit section +} +``` + +每个读者都必须先获取 readLock,而只有第一个写者需要获取 readLock,并且直到队列中没有写者时才会释放,因此这种实现可能导致读者饥饿。 + +以上两种实现总有一方可能饥饿。下面的实现使用一个先进先出的 serviceQueue 让读者和写者按到达顺序排队,获取共享数据的锁总能在有限时间内完成,因此不会出现饥饿。 + +```c +int readCount; // init to 0; number of readers currently accessing resource + +// all semaphores initialised to 1 +Semaphore resourceAccess; // controls access (read/write) to the resource +Semaphore readCountAccess; // for syncing changes to shared variable readCount +Semaphore serviceQueue; // FAIRNESS: preserves ordering of requests (signaling must be FIFO) + +void writer() +{ + down(&serviceQueue); // wait in line to be serviced + // + down(&resourceAccess); // request exclusive access to resource + // + up(&serviceQueue); // let next in line be serviced + + // + writeResource(); // writing is performed + // + + // + up(&resourceAccess); // release resource access for next reader/writer + // +} + +void reader() +{ + down(&serviceQueue); // wait in line to be serviced + down(&readCountAccess); // request exclusive access to readCount + // + if (readCount == 0) // if there are no readers already reading: + down(&resourceAccess); // request resource access for readers (writers blocked) + readCount++; // update count of active readers + // + up(&serviceQueue); // let next in line be serviced + up(&readCountAccess); // release access to readCount + + // + readResource(); // reading is performed + // + + down(&readCountAccess); // request exclusive access to readCount + // + readCount--; // update count of active readers + if (readCount == 0) // if there are no readers left: + up(&resourceAccess); // release resource access for all + // + up(&readCountAccess); // release access to readCount +} + +``` + +## 参考资料 + +- Tanenbaum A S, Bos H. Modern Operating Systems [M]. Pearson, 2014. +- [Readers–writers problem - Wikipedia](https://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem) From 8b35f6750f6118d7af92c89f23f380295e4ef5df Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Wed, 28 Oct 2026 22:51:26 +0800 Subject: [PATCH 5/7] =?UTF-8?q?Update=20=E8=AE=A1=E7=AE=97=E6=9C=BA?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E7=B3=BB=E7=BB=9F=20-=20=E7=9B=AE=E5=BD=95.m?= =?UTF-8?q?d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 加入进程管理的进阶篇。 --- notes/计算机操作系统 - 目录.md | 1 + 1 file changed, 1 insertion(+) diff --git a/notes/计算机操作系统 - 目录.md b/notes/计算机操作系统 - 目录.md index 9676b283..09b73414 100644 --- a/notes/计算机操作系统 - 目录.md +++ b/notes/计算机操作系统 - 目录.md @@ -2,6 +2,7 @@ - [概述](计算机操作系统%20-%20概述.md) - [进程管理](计算机操作系统%20-%20进程管理.md) +- [进程管理 - 进阶](计算机操作系统%20-%20进程管理%20-%20进阶.md) - [死锁](计算机操作系统%20-%20死锁.md) - [内存管理](计算机操作系统%20-%20内存管理.md) - [设备管理](计算机操作系统%20-%20设备管理.md) From cff48cf9669032edd6eb55a0437013cc5a355011 Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Wed, 28 Oct 2026 22:51:26 +0800 Subject: [PATCH 6/7] =?UTF-8?q?Update=20=E8=AE=A1=E7=AE=97=E6=9C=BA?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E7=B3=BB=E7=BB=9F=20-=20=E6=A6=82=E8=BF=B0.m?= =?UTF-8?q?d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新指向进程管理的链接。 --- notes/计算机操作系统 - 概述.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/计算机操作系统 - 概述.md b/notes/计算机操作系统 - 概述.md index d602ff15..e8b1f7d6 100644 --- a/notes/计算机操作系统 - 概述.md +++ b/notes/计算机操作系统 - 概述.md @@ -120,7 +120,7 @@ Linux 的系统调用主要有以下这些: - 内核入口和出口的额外工作,例如启用页表隔离(Page Table Isolation, PTI,用于缓解 Meltdown 攻击)时,每次进入和退出内核都要写 CR3,内核文档给出的量级是百余个周期; - 内核执行的工作本身,例如参数检查、查找文件、复制数据。 -需要注意的是,陷入内核不等于切换进程:系统调用执行完通常回到同一个线程,不一定发生上下文切换,见[进程管理](计算机操作系统%20-%20进程管理.md#5-上下文切换)。 +需要注意的是,陷入内核不等于切换进程:系统调用执行完通常回到同一个线程,不一定发生上下文切换,见[进程管理](计算机操作系统%20-%20进程管理.md#4-上下文切换)。 减少系统调用开销的做法主要有以下几种: From 9dd0e7e80b65ca1378d1e0058e86954c9b0c4fc1 Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Wed, 28 Oct 2026 22:51:26 +0800 Subject: [PATCH 7/7] Update Socket.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新指向 HTTP/2.0 的链接。 --- notes/Socket.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/Socket.md b/notes/Socket.md index aef0265b..8e021dcd 100644 --- a/notes/Socket.md +++ b/notes/Socket.md @@ -103,7 +103,7 @@ ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr * I/O 复用(I/O multiplexing)指用一个系统调用同时等待多个描述符,其中任何一个就绪就返回。Linux 上的实现依次为 select、poll 和 epoll,epoll 解决了前两者在描述符数量很多时的性能问题。 -这里的多路复用指内核对描述符的事件通知,与 HTTP/2 在一个 TCP 连接上并发多个流不是同一个概念,见 [HTTP/2.0](HTTP.md#七http20)。 +这里的多路复用指内核对描述符的事件通知,与 HTTP/2 在一个 TCP 连接上并发多个流不是同一个概念,见 [HTTP/2.0](HTTP.md#八http20)。 下面用一个例子贯穿全节:服务器维护 10000 个空闲连接,任意时刻只有 1 个连接可读。