笔记-CSAPP 第12章 并发编程
This note is hosted on Notion: CSAPP 第12章 并发编程
基于 I/O多路复用的并发编程
I/O 多路复用(I/O multiplexing)技术的基本思想是使用 select 函数,要求内核挂起进程,只有在一个或多个 I/O 事件发生后,才将控制返回给应用程序。
select 的几个关键点
select 监控的时文件描述符集合
在 Unix 里,socket,管道,标准输入输出,本质上都能抽象成 fd
select 默认是阻塞等待
如果没有任何 fd 就绪,他就“睡着”,有事件来了才返回。
返回后要自己判断是哪几个 fd 就绪了
select 适合 fd 数量不太大时使用,因为它每次都要扫描集合,效率一般。
- 线程并发:一个连接一个线程,各等各的。
- I/O 多路复用:一个线程盯很多连接,谁就绪处理谁
多个都 ready 时,一个个处理
相比普通阻塞 read / accept,I/O 多路复用可以同时服务多个 I/O 对象。
进程的优劣
对于在父、子进程间共享状态信息,进程有一个非常清晰的模型:共享文件表,但是不共享用户地址空间。进程有独立的地址空间既是优点也是缺点。
优点:这样一来,一个进程不可能不小心覆盖另一个进程的虚拟内存(隔离性)。
缺点:另一方面,独立的地址空间使得进程共享状态信息变得更加困难。为了共享信息,它们必须使用显式的 IPC(进程间通信)机制,他们往往比较慢,因为进程控制和 IPC 的开销很高。
I/O 多路复用技术的优劣
I/O 多路复用可以做并发事件驱动程序的基础。在事件驱动程序中,某些事件会导致流向前推进。一般的思路是将逻辑流模型化(画)为状态机。

服务器使用 I/O 多路复用,借助 select 函数检测输入事件的发生。当每个已连接描述符准备好可读时,服务器就为相应的状态机执行转移,在这里就是从描述符读和写回一个文本行。
事件驱动设计的优点:
- 它比基于进程的设计给了程序员更多的对程序行为的控制
- 基于进程的设计把请求处理交给 OS 调度器处理,用户无法控制
- 基于 I/O 多路复用的事件驱动设计,程序员更直接地控制 I/O 事件处理顺序、时机和粒度
- 一个基于 I/O 多路复用的事件驱动服务器是运行在单一进程上下文中的,因此每个逻辑流都能访问该进程的全部地址空间。这使得在流之间共享数据变得容易。且可以像对顺序程序那样用调试工具调试并发服务器
- 事件驱动设计常常比基于进程的设计要高效的多。因为他们不需要进程上下文切换来调度新的流。
缺点:
- 编码复杂。随着并发粒度的减小,复杂性还会上升。这里的粒度是指每个逻辑流每个时间片执行的指令数量。
- 不能充分利用多核处理器
基于线程的并发编程——基于进程和基于I/O多路复用方法的混合
线程就是运行在进程上下文的逻辑流
线程由内核自动调度。每个线程都有自己的线程上下文,包括唯一线程ID(TID),栈,栈指针,程序计数器,通用目的寄存器和条件码
所有运行在一个进程里的线程共享该进程的整个虚拟地址空间
线程执行与进程的不同处:
线程的上下文切换要比进程的上下文切换快得多
线程不像进程那样,不是按照严格的父子层次来组织的
和一个进程相关的线程组成一个对等(线程)池,独立于其他线程创建的线程
主线程和其他线程的区别仅在于它总是进程中第一个运行的线程
对等(线程)池概念的主要影响是,一个线程可以杀死它的任何对等线程,或者等待它的任何对等线程终止。每个对等线程都能读写相同的共享数据
互斥锁加锁规则:给定所有互斥操作的一个全序,如果每个线程都是以一种顺序获得互斥锁并以相反的顺序释放,那么这个程序就是无死锁的。
在 Notion 参与讨论
本文托管在 Notion,欢迎到原文评论区留言交流