Linux kernel 成功的两个原因:
架构设计支持大量的志愿开发者加入到开发过程中;
每个子系统,尤其是那些需要改进的,都支持很好的扩展性。
正是这两个原因使得 Linux kernel 可以不断进化。
一、Linux内核在整个计算机系统中的位置Fig 1 - 计算机系统分层结构
分层结构的原则:
the dependencies between subsystems are from the top down: layers pictured near the top depend on lower layers, but subsystems nearer the bottom do not depend on higher layers.
这种子系统之间的依赖性只能是从上到下,也就是图中顶部的子系统依赖底部的子系统,反之则不行。
二、内核的作用虚拟化(抽象),将计算机硬件抽象为一台虚拟机,供用户进程(process)使用;进程运行时完全不需要知道硬件是如何工作的,只要调用 Linux kernel 提供的虚拟接口(virtual interface)即可。
多任务处理,实际上是多个任务在并行使用计算机硬件资源,内核的任务是仲裁对资源的使用,制造每个进程都以为自己是独占系统的错觉。
PS:进程上下文切换就是要换掉程序状态字、换掉页表基地址寄存器的内容、换掉 current 指向的 task_struct 实例、换掉 PC ——>也就换掉了进程打开的文件(通过 task_struct 的 files 可以找到)、换掉了进程内存的执行空间(通过 task_struct 的 mem 可以找到);
三、Linux内核的整体架构Linux内核的整体架构
中心系统是进程调度器(Process Scheduler,SCHED):所有其余的子系统都依赖于进程调度器,因为其余子系统都需要阻塞和恢复进程。当一个进程需要等待一个硬件动作完成时,相应子系统会阻塞这个进程;当这个硬件动作完成时,子系统会将这个进程恢复:这个阻塞和恢复动作都要依赖于进程调度器完成。
上图中的每一个依赖箭头都有原因:
进程调度器依赖内存管理器(Memory manager):进程恢复执行时,需要依靠内存管理器分配供它运行的内存。
IPC 子系统依赖于内存管理器:共享内存机制是进程间通信的一种方法,运行两个进程利用同一块共享的内存空间进行信息传递。
VFS 依赖于网络接口(Network Interface):支持 NFS 网络文件系统;
VFS 依赖于内存管理器:支持 ramdisk 设备
内存管理器依赖于 VFS,因为要支持交换(swapping),可以将暂时不运行的进程换出到磁盘上的交换分区(swap),进入挂起状态。
四、高度模块化设计的系统,利于分工合作。只有极少数的程序员需要横跨多个模块开展工作,这种情况确实会发生,仅发生在当前系统需要依赖另一个子系统时;
硬件设备驱动(hardware device drivers)、文件系统模块(logical filesystem modules)、网络设备驱动(network device drivers)和网络协议模块(network protocol modules)这四个模块的可扩展性最高。
五、系统中的数据结构任务列表(Task List)
进程调度器针对每个进程维护一个数据结构 task_struct;所有的进程用链表管理,形成 task list;进程调度器还维护一个 current 指针指向当前正在占用 CPU 的进程。
内存映射(Memory Map)
内存管理器存储每个进程的虚拟地址到物理地址的映射;并且也提供了如何换出特定的页,或者是如何进行缺页处理。这些信息存放在数据结构 mm_struct 中。每个进程都有一个 mm_struct 结构,在进程的 task_struct 结构中有一个指针 mm 指向次进程的 mm_struct 结构。
在 mm_struct 中有一个指针 pgd,指向该进程的页目录表(即存放页目录首地址)——>当该进程被调度时,此指针被换成物理地址,写入控制寄存器 CR3(x86体系结构下的页基址寄存器)
I-nodes
VFS 通过 inodes 节点表示磁盘上的文件镜像,inodes 用于记录文件的物理属性。每个进程都有一个 files_struct 结构,用于表示该进程打开的文件,在 task_struct 中有个 files 指针。使用 inodes 节点可以实现文件共享。文件共享有两种方式:(1)通过同一个系统打开文件 file 指向同一个 inodes 节点,这种情况发生于父子进程间;(2)通过不同系统打开文件指向同一个 inode 节点,举例有硬链接;或者是两个不相关的指针打开同一个文件。