1。我个人认为作者MAX对Linux的了解不像他对Solaris那样深入,我不知道也没法知道他的下列关于Linux的内容来自自己的代码阅读分析还是只是来自第三方的文档资料而未经自己实地验证;2。我已经尽量符合原意地翻译了,当然中间实在忍不住的地方也插两句自己的话;3。无论是只阅读这一篇文章,还是看其他东西,我都觉得,保持自己头脑清醒很重要
Max Bruning 是一名教师/资讯专家,他的教授内容包括Solaris内部组织,设备驱动,内核和应用的crash分析及调试,网络组织和其他一些特定科目(他的blog在blogspot,不费点劲可能访问不了,所以也可以看看www.bruningsystems.com)。
在解释这些子系统在Solaris中是如何实现的时候,他的学生们总会问“Linux里它是怎么工作的?”或者“FreeBSD里是这样,Solaris里呢?”这种经历最终让Max在OpenSolaris网站写了这篇A Comparison of Solaris, Linux, and FreeBSD Kernels。
文章里讨论了调度,内存管理和文件系统架构--这3个子系统在任何操作系统中都有普遍应用,而且他们是最well-understood 的组件。
目前很多分析或对比文章所引用的材料及代码都比较老,与现实脱节,Max推荐如下几个多少比较up to date的网站:
Solaris Vs. Linux
Comparing MySQL Performance
Fast Track to Solaris 10 Adoption
Solaris 10 Heads for Linux Territory
其实抛开3个系统之间的差别,他们也有很多相似之处。除了那些不同的命名习惯,这些OS在实现不同概念的时候采用了非常相似的方法。他们都支持线程的分时调度,支持最近未使用页面替换算法实现请求调页,支持虚拟文件系统层允许不同文件系统架构。这个系统里的一个好概念在另一个系统里也会采用。比如Linux也接受并实现了 Solaris slab 内存分配算法的概念。FreeBSD 代码里的很多术语在Solaris里也出现了(快去看看代码。。。)。考虑到这3个系统的源代码都能得到了, fxr.watson.org提供了系统源码的交叉阅读浏览,可能会发现很多有趣的地方。
好了,温情默默的套近乎结束,进入正题。
调度和调度器
Solaris的调度单位是kthread_t,FreeBSd是thread,Linux是task_struct。抬高一级,Solaris的进程是proc_t,当然每个进程里的线程就是kthread_t;Linux的进程和线程都由task_struct 表示,单线程的进程在Linux里是一个task_struct。单线程的进程在Solaris
里有一个proc_t,一个kthread_t,还有一个klwp_t表示。klwp_t提供了用户和内核模式线程切换的存储区。FreeBSD里的单线程进程有一个proc ,一个thread 和一个ksegrp 。ksegrp 是“内核调度的实体组kernel scheduling entity group”。三个系统的线程表示结构不同,不过都支持调度线程。
和大家熟悉的基本一样,调度是基于优先级的。小小的数学问题是,在Linux和FreeBSD里,数字越小,优先级越高;而SUN的宝贝却喜欢数字越大,优先级越高。参考下表
![](https://i-blog.csdnimg.cn/blog_migrate/7abd18e5bd918ddcb9ee3a1ec85996c9.png)
三个系统都更推崇interactive 线程/进程(下面会提到interactive怎么回事)。Interactive 线程比compute-bound 线程优先级要高,不过得到的时间片要少一些。Solaris,FreeBSD和Linux都使用每CPU的“运行队列 runqueue”。FreeBSD和Linux有一个active队列和一个expired队列。名字说得很清楚了--系统从active上按照优先级选择线程进行调度。用完自己时间片的线程就从active搬到expired上(或者为了避免“饿死”的其他情况),active空以后,内核交换active和expired。FreeBSD还多一个idle 队列--其他两个queue都空的时候才轮到这个。Solaris的概念是每CPU“调度队列 dispatch queue”。线程用完时间片后,内核给其一个新优先级然后放回调度队列。所有3个系统的runqueue,对不同优先级的可运行线程都分别有链表。FreeBSD四个优先级共享一个链表,Solaris和Linux则每个优先级一个链表
Linux和FreeBSD结合运行时间和睡眠时间计算线程的interactive-ness,Solaris查表。他们都不支持“gang scheduling”(有兴趣查Google即知,并行计算上的调度算法,大白话说就是一组任务一把disptach到各个CPU上。劳伦斯.利弗莫尔那帮造原子弹的家伙最喜欢了,他们有世界上最昂贵的玩具,可以理解)每个OS都调度下一个线程而不是N个线程开始运行。这3个OS都有利用CACHE(warm affinity)和负载均衡的机制。对超线程CPU,FreeBSD能尽量将多个线程保持在一个CPU节点上(当然可能是不同的CPU超线程上)。Solaris也有类似机制,不过是在用户和应用的控制下,而且并不限于CPU的超线程,他们的术语是processor sets,FreeBSD的叫法是processor groups
和其他2个OS最大的不同是,Solaris同时支持多个“scheduling classes”。3个OS都支持POSIX的SCHED_FIFO,SCHED_RR和SCHED_OTHER (或者SCHED_NORMAL)。SCHED_FIFO 和SCHED_RR通常支持实时线程(我不同意。。。但是照翻。。。)。Solaris和Linux为支持实时线程都支持了内核抢占。Solaris支持fixed priority类,system class的是系统线程(比如换页线程),interactive的是在X控制下运行窗口环境的线程,还有一个Fair Share Scheduler 用于资源管理。具体可以参考Solaris资料。FreeBSD的调度器是在编译时决定的,Linux的调度?--要看版本了。
支持在系统中加入新的调度类是要付出代价的。内核中每个可能决定调度的地方都得有一个间接得函数调用去call调度类相关的代码。比如,当一个线程将要sleep时,内核调用调度类相关代码,完成该类中线程sleep需要完成工作。在Linux和FreeBSD上,调度已经完成了所有工作。不需要再来一个间接调用。额外的层次,就意味着Solaris的调度要占用稍微多一点的系统开销--不过提供了更多的功能。
内存管理和分页
Solaris的进程地址空间由逻辑段segment组成。进程地址中的这些段可以通过pmap访问。Solaris将其内存管理代码和数据结构分为平台无关和平台相关部分(这不跟没说一样嘛。。。)。平台相关部分位于HAT(hardware address translation)层。FreeBSD用vmspace描述进程地址空间,将其划分为逻辑块region。硬件相关部分在pmap(physical map)模块,而vmap 例程处理硬件无关部分和数据结构。Linux使用内存描述符划分进程地址空间,逻辑单位是memory areas。Linux也由pmap来examine 进程地址空间。
Linux将机器相关层从更高层次的机器无关层中划分出来。Solaris和FreeBSD中大多数类似代码比如page fault处理是机器无关的,而Linux处理page fault的代码则非常机器相关--从fault处理开始就是这样了。由此下来的结果是,Linux能很快地完成大多数分页相关代码--因为数据抽象更少。不过,代价是,下层硬件的改变需要大量修改代码--Solaris和FreeBSD则分别把这样的工作堵截在HAT和pmap层搞定。
Segment,region和meory area的分割是:区域的虚拟地址segmetn/region/memory area映射的object/文件的位置权限map的大小
例如,程序的text(text段,即代码)在一个segmetn/region/memory area中,OS管理地址空间的机制是类似的,不过数据结构名字完全不同。
分页3个系统都使用了最近最少使用least recently used算法的变种完成页替换。他们都有一个守护daemon进程/线程完成页替换。FreeBSD的是vm_pageout daemon,它周期性地,或者当free的内存不多时,被唤醒。当可用内存低于某个限制时,vm_pageout 运行例程vm_pageout_scan扫描内存并释放一些页面。vm_pageout_scan例程可能需要异步地将更改过的页面写回到磁盘,在释放他们之前。不论由多少颗CPU,只有一个这样的daemon。Solaris的是pageout daemon,它也周期性地运行,处理空闲内存不多的情况。Solaris中的分页限制值在系统启动时自动校准,这样可以避免该守护进程过渡占用CPU或者向磁盘发出洪水般的换页请求(嗯,flood这么翻正好 ;P )。FreeBSD的daemon在大多数情况下使用的值是固定的--不过也可以调整。Linux的LRU算法可以在运行时动态调整,而且可以有多个kswapd daemon,每CPU最多一个。这3个系统都使用global working set策略,而不是per process working set。
FreeBSD有多个页面链表来追踪最近使用页。包括active,inactive,cached和feee页。根据使用情况,页面在这些链表间走来走去。经常访问的页面会在active上。退出的进程的数据页面将被马上放到free上。如果因为负载原因vm_pageout_scan 来不及扫描全部内存的话,FreeBSD内核可能将整个进程全部换出。如果内存短缺十分严重,vm_pageout_scan 可能会kill系统中最大的进程。Linux也使用不同的页面链表。物理内存被分为(多个)3重zone:一个DMA页面,一个普通页面,一个动态分配内存页面。zone的实现很像由于x86架构限制而很产生的。页面在hot,cold和free链表间移动--机制和FreeBSD的类似。经常用的页面在hot上。可用页面则在cold或者free上。
SUN的大佬使用free链,哈希链,vnode页面链支持自己的LRU实现。后两者大致相当于FreeBSD和Linux的active/hot链--也是FreeBSD和Linux要扫描的链。Solaris要扫描的不是这两个对象,它用two-handed clock算法扫描全部页面(见Solaris Internals 或其他什么地方随你便)。大致方法是,两只手相隔固定举例,前面的手将page的引用位清空以作为标识,如果自此开始没有进程引用这个页,后面的手就释放这个页面(当然如果需要就写回磁盘)。
3个系统在分页时都考虑了NUMA本地性。他们都把IO buffer cache和虚拟内存页面的cache合并到一个系统页cache中。系统页cache用于读写文件已经被mmap了文件,还有应用的text段和data段。
文件系统
3个系统都使用数据抽象层向应用隐藏文件系统实现细节。就是用大家熟悉的open,close,read,write,stat,等等系统调用访问文件,无论下层的文件数据的实现和组织如何。Solaris和FreeBSD把这种机制称为VFS(virtual file system),基本数据结构是vnode(virtual node)。Solaris和FreeBSD里每个被访问的文件都有一个赋给他们的vnode。除了generic 的文件信息外,vnode还包含到file-system-specific 信息的指针。Linux采用了详细的机制,也叫VFS(virtual file switch),文件系统无关的数据结构是inode。这个机构和vnode类似(小心:Solaris和FreeBSD也另有自己的inode--是UFS文件系统里file-system-dependent 的数据)。Linux还有两个不同的结构,一个用于文件操作,另一个用于inode操作。Solaris和FreeBSD将他们合并为vnode操作。
VFS允许在系统里实现多种文件系统。这意味着他们相互访问对方的文件系统没问题。只要相关的文件系统例程和数据结构已经被移植到VFS上。所有这3个系统都允许文件系统堆叠stacking。下表列出了每个OS实现的文件系统类型,不是全部哈。
![](https://i-blog.csdnimg.cn/blog_migrate/b362fe089cf59e429ae69cdc0fc421ef.png)
结论
Solaris,FreeBSD和Linux显然都在从对方身上获益。随着Solaris的开源,这种相互促进有望更快。Max个人已经感觉到Linux的变化是最快的。新技术被快速地集成进系统,只是文档和健壮性可能有点落后。Linux有很多--或者有时是看上去有很多--开发者。FreeBSD则大概是(从某种意义上)3个系统中历史最长的。Solaris来自BSD Unix和AT&T Bell实验室Unix的结合,使用了更多数据抽象层,因而一般说来能更简便地支持更多功能。不过,内核中大多数这样的分层都没有文档描述。可能随着代码的开放这一点会有所改善。
至于他们的差别,最大的地方之一是page fault处理了。在Solaris中,发生page fault时,代码是从平台相关的trap handler开始执行的(以大家的智商,这好像不用说了吧。。。),然后会调用generic的as_fault例程,这个例程判断发生page fault的segment,然后调用segment driver处理page fault。segment driver调用文件系统代码,后者再调用进驱动程序,换入页面。换入完成后,segment driver 调用HAT层来更新页表项。在Linux上,发生page fault后,内核调用的代码在会马上进入平台相关部分,这些处理可能更快,不过可能不太容易扩展和移植(后半段说得太省,不知道作者有没有真的研究过Linux下对应的处理过程)。
内核观察和调试工具对正确理解系统行为有关键意义。在这方面,Solaris有kmdb,mdb和DTrace 。在开源之前,Max就对Solaris做过多年“反向工程”--他发现解决问题的时候使用工具总比阅读代码来得快--我也知道,不过得看什么场合,大家可不要被他误导。Linux嘛,我看作者Max不太熟,所以认为没有太多工具。对FreeBSD,他也认为只是可以用GDB调试内核的dump--Liux也可以。
最好作为可参考的总结性资料,读的时候要保持清醒头脑哟。
可参考资料/网站
Solaris Internals: Core Kernel Architecture by McDougall and Mauro Solaris Internals
The Design and Implementation of the FreeBSD Operating System by McKusick and Neville-Neil The Design and Implementation of the FreeBSD Operating System
Linux Kernel Development by Love Linux Kernel Development, 2nd Edition
Understanding the Linux Kernel by Bovet and Cesati Understanding the Linux Kernel, 2nd Edition --这本在国内大家应该比较熟悉的
原文来自: http://blog.csdn.net/lovemy/archive/2006/01/23/586849.aspx