包括进程地址空间管理、线程与进程通信、临界区与同步互斥问题及解决方案等,内容如下:

  • 进程地址空间管理

  • 多进程地址空间隔离

  • 段机制与分页机制:宫晓利指出,在使用段机制和分页的虚拟地址空间机制时,可通过硬件帮助实现多个进程地址空间的隔离。在现代化虚拟地址管理中,依靠页表劫持进程对内存的访问,避免两个进程的内存访问导入到同一物理内存上。此前,还使用硬件长度寄存器确保进程只能访问分配给它的地址空间。

  • 内核与用户空间连接的问题:操作系统设计者将内核部分和用户部分地址空间连接在一起,虽在进程上下文切换和系统调用时有省事之处,但会产生严重的安全漏洞,如 Spectre 和 MELTDOWN 漏洞。这种连接方式使内核空间的数据对用户具有吸引力,需要强有力的手段保护内核空间不被用户访问。

  • 内存权限标识与访问控制

  • 内存权限标识:硬件将内存空间分为用户态和特权态,通过页表项上的 privilege 位标识物理页的权限。CPU 上有寄存器标识当前所处的特权状态,如 X86 中的 ring 0 或 ring 3。

  • MMU 访问控制:每一次访存操作都委托给 MMU,MMU 会检查指令的运行状态和页表项上的权限状态是否匹配。若用户态 CPU 想访问特权态页,MMU 会阻止;特权态 CPU 可访问用户态页,但在 RISC five 处理器中,有模式可限制特权态处理器对用户态页面的读取。

  • 线程与进程通信

  • 线程的概念与应用

  • 线程的产生:当两个进程需要高频次、大数据量的内存共享时,产生了线程的概念。线程在游戏开发中应用广泛,每个 NPC 和技能都可看作一个线程。

  • 线程上下文切换:宫晓利用测试程序说明,内核态线程上下文切换耗时,如在鲲鹏服务器上约需 1900 纳秒,主要包括权限管理和寄存器切换。而用户态上下文切换可减少权限管理部分的时间,如纤程、协程等,约需 900 纳秒。

  • 进程间通信机制

  • 共享内核空间通信:由于内核空间和用户空间连接以及所有进程内核空间重叠,进程间可通过共享的内核空间进行通信,如管道、消息队列和共享内存。

  • 共享内存的问题与挑战:共享内存会导致一个物理地址有多个虚拟地址,引发缓存命中问题。处理器设计需考虑使用虚拟地址还是物理地址做索引,最新的 CACHE 技术 VIPT 结合虚拟地址索引和物理地址 tag 解决此问题。此外,共享内存还需要巨大的 TLB 来完成地址翻译。

  • 临界区与同步互斥

  • 临界区问题与解决方案

  • 临界区的定义与问题:宫晓利以宿舍买面包为例,引出临界区的概念,即一段希望只有一个人执行的代码。为保证临界区只有一个进程执行,需要解决进入区、退出区和非临界区的代码逻辑问题。

  • 软件解决方案:通过分析 Peterson 算法和 Deker 算法,说明软件解决临界区问题的复杂性。Peterson 算法虽能解决问题,但代码晦涩,在现实操作系统设计中几乎无人使用。

  • 硬件解决方案:软件解决问题复杂时,可借助硬件。如使用 test and set 指令,该指令能在一条指令内完成检测和修改操作,避免调度中断。使用该指令可实现锁,确保临界区代码的正确性,但会导致盲等待问题,浪费 CPU 资源。在单核系统中,可采用阻塞和唤醒机制解决盲等待问题。

  • 同步与互斥问题

  • 同步与互斥的概念:同步是指进程之间有顺序要求,如挖坑、种树和填土的进程,需要保证坑挖好后才能种树,树种好后才能填土。互斥是指同一时刻只有一个进程能执行某段代码,如多个进程争抢同一个资源。

  • 信号量机制:迪杰斯特拉提出信号量的概念,包括 p 操作和 v 操作。p 操作将信号量减一,若结果小于 0 则进程进入休眠状态;v 操作将信号量加一,若结果小于等于 0 则唤醒等待队列中的进程。通过信号量机制可解决同步和互斥问题。

  • 管程机制:管程是一种更新的编程模型,本质与信号量相同,但使用接口更友好。管程通过 wait 和 signal 函数封装锁的释放和获取操作,避免程序员手动管理锁的顺序,提高程序的可靠性。

  • 经典 IPC 问题

  • 生产者 - 消费者问题

  • 问题描述与分析:生产者负责往仓库放东西,消费者负责从仓库取东西。同一时刻只能有一个生产者或消费者进入仓库,且生产者需等待消费者制造空位,消费者需等待生产者生产商品。

  • 解决方案与注意事项:使用三个信号量,一个用于互斥,两个用于同步。在实现过程中,需注意加互斥锁时尽量少引入代码逻辑,避免持有锁时休眠,否则会导致系统死锁。

  • 哲学家就餐问题

  • 问题描述与挑战:哲学家主要进行思考和吃饭,吃饭需要拿起两根筷子。若所有哲学家都拿起左手边的筷子,会导致所有人都无法拿到右手边的筷子,从而饿死。

  • 解决方案:可通过规定部分人先拿左边筷子,部分人先拿右边筷子,或设置允许拿筷子的人数信号量等方法解决问题。

  • 读者 - 写者问题

  • 问题描述与特点:读者往共享缓冲区读取数据,写者往缓冲区写入数据。同一时刻只能有一个人写,但可以有多个人读。写操作时不能读,读操作时不能写。

  • 解决方案与优化:可通过信号量机制实现读者和写者的互斥和同步。但简单的实现可能导致读者或写者饥饿问题,需要设计更合理的机制,如写者优先或读者写者公平得到权利的机制。

  • 沉睡的理发师问题

  • 问题描述与分析:理发师若没有顾客就会睡觉,有顾客到来时,若椅子未满,顾客会等待;若椅子满了,顾客会离开。第一个到来的顾客若发现理发师在睡觉,会唤醒他。

  • 解决方案:该问题包含互斥和同步问题,可使用信号量机制解决。互斥锁用于控制顾客争抢椅子,同步信号量用于控制理发师的睡眠和唤醒。

  • 锁、信号量与条件变量

  • 锁、信号量与条件变量的概念

  • 锁:锁是通过循环等待,直到可以运行的机制,可通过原子指令实现,如 test and set 指令。

  • 信号量:信号量是指若能得到条件执行就执行,否则进入休眠状态,可通过关中断和系统调用实现。

  • 条件变量:条件变量是管程中的关键变量,管程和条件变量组合是用面向对象技术封装的新的锁和信号量组合,可在有条件时拿到锁执行,无条件时释放锁并休眠。

  • 锁和信号量的效率比较:宫晓利认为,在绝大部分情况下,若指令在接下来的执行中马上可以拿到锁,高并发程序员偏爱于锁,因为锁不用发生系统调用,没有用户态到内核态的切换和进程上下文切换,效率更高。