同时,用户应用程序和内核之间的联系,一般是通过它和内核的中间层 —— 标准 C 库来实现,而标准 C 库函数本身,则是建立在内核提供的系统调用基础之上。通过标准 C 库,以及内核体系无关部分与体系相关部分的接口,用户应用程序和部分内核都成为可移植的。
因此更为准确的是第三张图。其中,进程管理部分实现了一个进程世界的抽象,这个进程世界类似于我们的人类世界,只不过我们的世界里的个体是人,而在进程世界里则是一个一个的进程,我们人与人之间通过书信、手机、网络等交通往来,而各个进程之间则是通过不同方式的进程间通信,我们所有人都在分享同一个地球,而所有进程都在分享一个或多个 CPU 。
在这个进程的世界里,内存是重要的资源之一,就好似我们的土地。因此,管理内存的策略与方式,也就是内存管理是决定系统性能的一个关键因素。
了解了内核的体系结构,我们再来看看内核是如何工作的。
首先,内核通过系统调用来使得运行在它上面的应用程序可用。系统调用是内核和应用程序之间的桥梁,比如图中的针对文件操作的 open() , close() , read() , write() ,针对进程操作的 fork() , wait() ,还有针对网络操作的 socket() 等等,它们提供了对硬件的抽象,所以有时也被称为 linux 虚拟机。
内核提供的最接近实际用户的明显抽象是文件系统,我们很容易能够利用 open() 等几个系统调用编写一段程序打开一个文件并将它的内容拷贝到标准输出。内核通过这些系统调用为用户提供了一个文件的 " 错觉 " ,而实际上它不过是一堆数据有了个名字,这样一来就不必去与硬件底层的堆栈、分区和指针等交涉,这也就是我们经常所说的抽象 (abstraction) ,将底层的东西以更易懂的方式表达出来。
文件系统是内核提供的比较明显的一种抽象,我们可以说它是位于台前的,而相对还有一些是位于幕后的,比如进程调度。在 linux 上,任何 一个时间,都可能有好几个进程或者程序等待着运行。内核的时间调度给每个进程分配 CPU 时间,所以就一段时间内来说,我们会有种错觉:电脑同一时间运行好几个程序。
再比如另外一个位于幕后的内存管理,幕后到应用开发者都不易察觉的地步。每个程序运行得都好像它有个自己的地址空间来调用一样,实际上它跟其他进程一样共享计算机的物理存储。内存管理的另外一个方面是防止一个进程访问其他进程的地址空间 —— 对于多进程操作系统来说这是很必要的一个防范措施。
当然位于台前幕后的还有其他的角色,比如网络等。
我们现在再来简单看下它的物理组成。早期版本的内核是整体式的,也就是说所有的部分都静态地连接成一个很大的执行文件。