什么是内核?
内核是操作系统最基本的部分。它是为众多应用程序提供对计算机硬件的安全访问的一部分软件,这种访问是有限的,并且内核决定一个程序在什么时候对某部分硬件操作多长时间。直接对硬件操作是非常复杂的,所以内核通常提供一种硬件抽象的方法来完成这些操作。硬件抽象隐藏了复杂性,为应用软件和硬件提供了一套简洁,统一的接口,使程序设计更为简单。
严格地说,内核并不是计算机系统中必要的组成部分。程序可以直接地被调入计算机中执行,这样的设计说明了设计者不希望提供任何硬件抽象和操作系统的支持,它常见于早期计算机系统的设计中。最终,一些辅助性程序,例如程序加载器和调试器,被设计到机器核心当中,或者固化在只读存储器里。这些变化发生时,操作系统内核的概念就渐渐明晰起来了。
内核的任务:在技术方面说,内核是硬件与软件之间的一个中间层。其作用是将应用程序的请求传递给硬件,并充当底层驱动程序,对系统中的各种设备和组件进行寻址。
内核的组成部分
1 进程,进程切换,调度
传统上,UNIX操作系统下运行的应用程序,服务器以及其他程序都称为进程。由于Linux是多任务系统,它支持并发执行的若干进程。系统中同时真正在运行的进程数目最多不超过CPU数目,因此内核会按照短的时间间隔在不同的进程之间切换,这样就造成了同时处理多进程的假想。
进程之间的切换称之为进程切换。
确定哪个进程运行多长时间的过程称为调度。
2 UNIX进程
Linux对进程采用了一种层次系统,每个进程都依赖于一个父进程。内核启动init程序作为第一个进程,此进程作为进程树的根,pstree可以看到,init程序是一个树型结构的顶点。
UNIX操作系统中有两种创建新进程的机制,fork,exec。
fork可以创建当前进程的一个副本,父进程和子进程只有PID不同。
exec将一个新程序加载到当前进程的内存中并执行。
1)线程
一个进程可以由若干线程组成,这些线程共享同样的数据和资源,但可能执行程序中不同的代码路径。Linux用clone方法创建线程。其工作方式类似于fork,但启动了精确的检查,用来确认哪些资源与父进程共享,哪些资源线程独立创建。
2)命名空间
在某个命名空间中挂载的卷不会传播到其他命名空间中。命名空间可以看作是容器,从容器内部看这是一个完整的Linux系统,而且与其他容器没有交互。
3 地址空间与特权级别
地址空间的最大长度与时间可用的物理内存数量无关,因此被称为虚拟地址空间。Linux将虚拟地址空间划分为两个部分,内核空间和用户空间。
1)特权级别
所有的现代CPU都提供了几种特权级别,进程可以驻留在某一特权级别。Linux只使用两种不同的状态:核心态和用户态。用户态禁止访问内核空间。从用户态到核心态的切换通过系统调用的特定转换手段完成。在ps命令的输出中名称置于方括号内的为内核线程。ps fax
2)虚拟和物理地址空间
大多数情况下,单个虚拟地址空间就比系统中可用的物理内存要大。
物理内存页经常称作页桢。相比之下,页则专指虚拟地址空间中的页。
4 页表
用来将虚拟地址空间映射到物理地址空间的数据结构称为页表。内存一般使用多极分页方法建立模型。Linux采用了四级页表。该方法有一个缺点,每次必须逐级访问多个数组才能将虚拟地址转换为物理地址。CPU试图用下面两种方法加速该过程。
第一,MMU,该单元优化了内存访问操作。
第二,地址转换中出现最频繁的那些地址,保存到称为地址转换后备缓冲器的CPU告诉缓存中。
1)内存映射
内存映射是一种重要的抽象手段。在内核中大量使用,也可以用于用户应用程序。映射方法可以将任意来源的数据传输到进程的虚拟地址空间中。作为映射目标的地址空间区域,可以像普通内存那样用通常的方法访问。
5 物理内存的分配
在内核分配内存时,必须记录页桢的已分配或空闲状态,以免两个进程使用同样的内存区域。
1 伙伴系统
内核中很多时候要求分配连续页。为快速检测内存中的连续区域,内核采用了一种古老而历经检验的技术:伙伴系统。系统中的空闲内存块总是两两分组,每组中的两个内存块称为伙伴。
举个例子,假如系统需要8个页桢,则将16个页桢的块拆分为两个伙伴。每个8页,1个系统用,1个空下来放回到8页大小内存块列表中。如果下一个请求需要2个页桢,则从8页里面分2个4页,4页分2个2页,一个用于系统,剩下放回内存块中。
这样存在一个问题,在系统长期开机状态下,会发生称为碎片内存管理问题。频繁的分配和释放页桢可能导致这种情况。
2)slab缓存
内核本身经常需要比完整页桢小的多的内存块,由于内核无法使用标准库的函数,因而必须在伙伴系统基础上自行定义额外的内存管理层,将伙伴系统提供的页划分为更小的部分。该方法不仅可以分配内存,还为频繁使用的小对象实现了一个一般性的缓存--slab缓存。
3)页面交换和页面回收
页面交换通过利用磁盘空间作为扩展内存,从而增大了可用的内存。
6 计时
内核必须能够测量时间以及不同时间点的时差,进程调度就会用到该功能。jiffies是一个合适的时间坐标。
7 系统调用
系统调用是用户进程与内核交互的经典方法。PISIX标准定义了许多系统调用,以及这些系统调用在所有遵从PISIX的系统包括Linux上的语义。传统的系统调用按不同类别分组为,进程管理,信号,文件,目录和文件系统,保护机制,定时器函数。所有这些函数都对内核提出了要求。
对所有处理器来说,一个共同特点是:用户进程要从用户状态切换到核心态,并且将系统关键任务委派给内核执行,系统调用是必由之路。
8 设备驱动程序,块设备和字符设备
设备驱动程序用于与系统连接的输入/输出装置通信。按照经典的UNIX的说法,万物皆文件。对外设的访问可利用/dev目录下的设备文件来完成。
外设分为一下两类
1)字符设备:提供连续的数据流,应用程序可以顺序读取,通常不支持随机存取。
2)块设备:应用程序可以随机访问设备数据,程序可自行确定读取数据的位置。
9 网络
网卡也可以通过设备驱动程序控制,但在内核中属于特殊状况,因为网卡不能利用设备文件访问。
原因在于网络通信期间,数据打包到各种协议层中,内核接受数据时必须先区分数据从哪层来,然后解析,才能传递给应用。
10 文件系统
文件系统使用目录结构组织存储的数据,并将其他元信息(所有者,权限等)与实际数据关联起来。
内核必须提供一个额外的软件层,将各种底层文件系统的具体特性与应用层隔离开来。
11 模块和热插拔
模块用于在运行时动态的向内核添加功能,实际上内核的任何子系统几乎都可以模块化。
模块可以在运行时从内核卸载。
对支持热插拔而言,模块在本质上是必须的,某些总线允许在系统运行时连接设备,而无需系统重启。
模块特性使得内核可以支持种类繁多的设备,而内核自身的大小却不会发生膨胀。
12 缓存
内核使用缓存来改进系统性能。由于内核是通过基于页的内存映射来实现访问块设备的,因此缓存也按页组织,也就是整页都缓存下来,称为页缓存。
13 链表处理
C程序中重复出现的一项任务是对双链表的处理。内核也需要处理这样的链表。内核提供的标准链表可用于将任何类型的数据结构彼此链接起来。
14 对象管理和引用计数
1)一般性的内核对象
kobject.h kobject不是通过指针与其他数据结构连接起来,而必须直接嵌入。这样做,通过管理kobject即达到了对包含kobject对象的管理。
1)对象集合
在很多情况下必须将不同的内核对象归类到集合中,例如,所有字符设备,所有基于PCI的设备。
3)引用计数
引用计数用于检测内核在多少地方使用了某个对象。
15 数据类型
1)类型定义
内核使用typedef定义各种数据类型
2)字节序
大小端法
3)per-cpu变量
在单CPU上这与常规的变量声明没有区别。
好处在于:所需数据很可能存在处理器的缓存中,因此可以更快速地访问。在多处理器CPU中运行,可能被所有CPU访问,引发一些通信问题,采用这个概念鞥绕过这些问题。
4)访问用户空间
阅读(795) | 评论(0) | 转发(1) |