Contents

OS learning

开篇

最近在学MIT 6.S081 Operating System,受益匪浅。开篇文章记录一下对一些所思、所想,或是总结。预计长期缓慢更新。

OS Organization

Why user/kernel mode?

现今操作系统普遍拥有内核态与用户态两层,这么做在根本上是为了权限隔离。

操作系统的权限隔离依赖于硬件的权限隔离。如Intel CPU 把指令权限划分为ring0-ring3四个等级,操作系统内核跑在ring0,拥有最高权限。用户态跑在ring3,权限最低。

权限隔离来源于操作系统对用户程序的零信任。要想设计一个尽可能安全的操作系统,就必须假设用户程序是恶意的。用户程序想要进行一些涉及特权指令的敏感操作(操作进程、更改页表、使用文件系统等等)都必须经过内核把关,即通过syscall把控制权交给内核。内核确认操作合法后,运行特权指令,最后把控制权还给用户程序。如果内核认为操作非法,多半会直接终止程序运行。

权限隔离促成了进程间隔离。一个进程无法访问其他进程的内存空间,得益于页表机制。但也正因为权限隔离,致使一个进程无法访问或更改页表,从而给页表加了把锁。每个进程都认为自己独占整个物理内存,使得每个进程都运行在一个隔离的环境中,没有互相间的影响。

使用用户态、内核态两层设计也提高了整个系统的稳定性。当一个用户程序运行中出现异常或崩溃时,内核不受影响,可以继续维持整个机器的正常运转。但这也警示我们,内核本身必须要足够健壮,一旦内核出现panic,那就真是回天乏术了。

宏内核?微内核?

传统的操作系统都是基于宏内核的。所谓宏内核,首先给人的印象就是大。从代码量上看,宏内核的代码量达到微内核的几十倍不成问题。

宏内核意味着在内核中实现了丰富的功能,包括但不限于:

  • 为了管理硬盘数据,内核需要实现一个文件系统。

  • 为了合理分配使用内存,内核普遍采用页表机制实现虚拟地址到物理地址的映射。

  • 为了处理硬件消息,统筹各种硬件运作,内核需要实现一整套中断机制。

  • 为了实现多核CPU的并行运算,内核需要建立一个线程切换、管理系统。

  • 为了保证多线程下数据的安全访问,内核还需要可靠的锁机制。

  • 为了封装底层,给应用开发提供系统API,内核需要规划、设计各种系统调用。

这也会是这篇文章后续讨论的一些主题。

随着操作系统的不断发展,人们发现内核中被塞进的东西越来越多,越来越难以维护。终于有人不堪忍受,提出了微内核的概念。

微内核致力于尽可能地减小内核的体量。微内核内部主要做两件事:

  • 实现一个基于服务(service)的进程、内存空间管理系统

  • 实现高效的进程间通信(IPC)

至于宏内核中文件系统、页表机制等等,被从内核中剥离了出来,放在用户空间变成了插件,作为一个进程运行。这在实际上让代码模块化更好,更易于维护。并且由于内核十分精简,出bug、漏洞的可能性大大降低,使整个系统更加健壮。

当用户应用需要使用文件系统、页表机制等时,就相当于要和另一个进程通信,即IPC,所以IPC的效率决定了整个系统工作的效率。整个过程中内核仅参与调度、管理,而不执行实际的功能代码。

在一些嵌入式场景中,由于文件系统或其他插件根本用不到,微内核的精简就显示出了其优势,被广受欢迎。

但是,在如今主机/服务器领域,还是宏内核、以及一些披着微内核名字的混合内核的天下。原因有三:

  • 性能问题。微内核由于在IPC过程中需要更频繁地进出内核,运行效率比宏内核更低。不过在著名的L4微内核中,通过设计的优化能将效率提升到非常接近原生linux内核的地步,未来微内核是否能更进一步也未可知。

  • 插件问题。想要真正开发出一个微内核操作系统,最难的点不在于内核,而在于插件。那些被从宏内核中拿出来的东西必须全部重新设计、开发,这并不是一件简单的事。

  • 应用生态问题。没有足够的成果表明微内核优于宏内核,并且由于上一点的原因,插件体系一直都不够完善,放弃主流的宏内核,为微内核开发应用是件费力不讨好的事。