1 Linux内核模式
学习的是Linux 0.11内核,采用的是单内核模式。单内核模式的主要优点是内核代码结构紧凑、执行速度快,但是层次结构性不强。
操作系统如何提供的服务流程?
- 应用主程序使用指定的参数值执行系统调用指令(int x80),使CPU从用户态(User Mode)切换至核心态(Kernel Model)。——调用服务的主程序层
- 操作系统根据具体的参数值调用特定的系统调用服务程序。这些服务程序则根据需要再调用底层的一些支持函数以完成特定的功能。——执行系统调用的服务层
- 在完成了应用程序所要求的服务之后,操作系统又使CPU从核心态切换回用户态,从而返回应用程序中继续执行后面的指令。——支持系统调用的底层函数
2 Linux内核系统体系结构
Linux内核主要由5个模块构成:
- 进程调度模块:
- 负责控制进程对CPU的资源使用。
- 采取的调度策略:各个进程能公平合理地访问CPU,同时保证内核能够及时地执行硬件操作。
- 内存管理模块:
- 用于确保所有进程能够安全地共享机器主内存区。
- 支持虚拟内存管理方式,使得Linux支持进程使用比实际内存空间更多的内存容量。
- 可以利用文件系统把暂时不用的内存数据块交换到外部存储设备上,需要的时候再交换回来。
- 文件系统模块
- 用于支持对外部设备的驱动和存储
- 虚拟文件系统模块通过向所有的外部存储设备提供一个通用的文件接口,提供并支持与其他操作系统兼容的多种文件系统格式。
- 进程间通信模块:支持多种进程间的信息交换方式
- 网络接口模块:提供对多种网络通信标准的访问并且支持许多网络硬件。
所有的模块都和进程调度模块存在依赖关系,因为它们都需要依靠进程调度程序来挂起(暂停)或者重新运行他们的进程,一般来说,一个模块会在等待硬件操作期间被挂起,而操作完成之后才可以继续运行。
可以根据源码结构来观察内核结构:
3 Linux内核对内存的管理和使用
3.1 物理内存
- Liunx内核程序占据在物理内存的开始内存。
- 接着是高速缓冲区部分(显存和ROM BIOS占用640K~1MB区域)
- 数据写入块设备上,系统先将数据放到高速缓冲区进行存储,之后由块设备驱动程序写到相应的设备上。
- 内存的最后部分供所有程序可以随时申请和使用的主内存区。
- 使用需要向内核内存管理模块提出申请,申请成功之后才可以进行使用,
- 含有RAM的虚拟盘系统,主内存区头部需要给一部分空间。
Linux系统同时采用内存分段和内存分页的管理机制。
3.2 内存地址空间概念
三种内存地址空间:
a)程序(进程)的虚拟和逻辑地址
- 虚拟地址(Virtual Address)是通过程序产生的由段选择符和段内偏移地址两个部分组成的地址。
- VA空间由GDT映射的全局地址空间和由LDT映射的局部地址空间组成。索引部分由13个比特位表示,以及区分GDT和LDT的1个比特位。
- 逻辑地址是指由程序产生的与段相关的偏移地址部分。在Intel保护模式下即是指程序执行代码段限长内的偏移地址。
b)CPU的线性地址
- 虚拟地址到物理地址变换之间的中间层,是处理器可寻址的内存空间中的地址。
- 程序代码会产生逻辑地址,或者说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。
- 如果启用了分页机制,那么线性地址可以再经变化以产生一个物理地址。如果没有启用,那么线性地址直接就是物理地址。
c)实际物理内存地址
- CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终地址。
虚拟存储
计算机呈现出比实际拥有的内存大的多的内存量,它允许程序员编制并运行比实际系统拥有的内存大的多的程序。
3.3 内存分段机制
虚拟地址到物理地址的变换过程
虚拟内存空间的含义是指一种利用二级或者外部存储空间,使程序能不受实际物理内存量限制而使用内存的一种方法。
管理方式
- 当一个程序需要使用一块不存在的内存时 ,CPU此时通过80386的页错误异常中断来实现。
- 当一个进程引用一个不存在页面中的内存地址时,就会触发CPU产生页出错异常中断,并把引起中断的线性地址放到CR2控制寄存器中。
- 此时处理中断就可以知道发生页异常的确切地址,从而可以把进程要求的页面从二级存储空间加载到物理内存当中。
- 如果此时物理内存全都被占用,可以借助二级存储空间的一部分作为交换缓冲器(Swapper)把内存中暂时不使用的页面交换到二级缓冲区当中,然后把要求的页面调入内存当中。
32位保护模式运行机制下内存寻址主要特点
- 此时段寄存器存放的是一个段描述符表(Segment Descriptor Table)中某一描述符项在表中的索引值。(包含需要寻址的内存段的基地址、段的长度值和段的访问特权级别等信息)。
- 寻址的内存位置是由该段描述符项中指定的段基地址值与一个段内偏移值组合而成。
- 段的长度可以改变,由描述符中的内容指定。
- 本质上是比实模式要多一个段选择符(Segment Selector)
每个描述符占8个字节,包含所描述段在线性地址空间中的起始地址、段的长度、段的类型、段的特权级别和其他信息。
描述符表
- 全局描述符表GDT(Global Descriptor Table),被用于所有程序来引用访问一个内存段。需设置GDTR寄存器
- 中断描述表IDT(Interrupt Descriptor Table),保存有定义中断或者是异常处理过程的段描述符。需设置IDTR寄存器
- 局部描述符表LDT(Local Descriptor Table),应用与多任务系统当中,通常每个任务使用一个LDT表,一般作为GDT的扩充。需设置LDTR寄存器。
80X86CPU中,段寄存器中的值右移3位即是描述符表中的一个描述符的索引值。13位可以定位8192个描述符项,选择符中位2(TI)指定表。
中断描述符表idt保存在内核代码段中,任务状态段TSS用于在任务切换时CPU自动保存或者是恢复相关任务的当前执行上下文(CPU当前状态)。
3.4 内存分页管理
基本原理是将CPU整个线性内存区域划分成4096字节为1页的内存页面。程序申请使用内存时,系统就以内存页为单位进行分配。为了在80X86保护模式下使用分页机制,需要把控制寄存器CR0的最高位置位。
80386使用了页目录和页表,页目录表项占用4个字节,每个页目录表或者页表必须只能包含1024个页表项。因此一个页目录表或者一个页表分别共占用1页内存。一个页目录表最多可以映射4GB的内存。
0.11内核中人工定义的最大任务数NR_TASKS是64个,每个任务逻辑地址范围是64M,并且各个任务在线性地址空间中的起始位置是(任务号)*64MB。0.11中所有任务的指令空间I和数据空间D都合用一块内存,即一个进程的所有代码、数据和堆栈部分都处于同一内存段中。
进程逻辑地址空间与CPU分段机制中代码段和数据段的区别
(1)CPU中主要是确定线性地址空间中段的用途以及相关约束与限制,每个段可以在4GB线性地址空间中的任何地方,可以独立、重叠。
(2)进程中的是指编译器加载程序时规定的在进程逻辑空间中顺序排列的代码区域、初始化和未初始化的数据区域以及堆栈区域。
3.5 CPU多任务和保护方式
0.11使用CPU0和3两个保护级,而8086分四个保护级。每个任务的代码和数据区保存于局部地址空间,不可被访问。内核代码和数据是由所有任务共享,保存于全局地址空间当中。
内核运行态:一个任务执行系统调用而陷入内核代码中执行。此时处理器在0级执行,并且执行的内核代码会使用当前进程的内核栈。
用户运行态:处理器此时在特权级最低的用户代码中运行,当被中断则进入内核态。
3.6 虚拟地址、线性地址和物理地址之间的关系
内核代码和数据的地址
- 内核代码段和数据段都是长度为16MB的段。
- 两个段在线性空间中范围重叠,都是从线性地址0开始到地址0xFFFFFF共16MB的段。
2.1. 该范围包含内核所有的代码、内核段表、页目录表和内核的二级页表、内核局部数据以及内核临时堆栈
2.2 页目录表和二级页表已经设置成0-16MB的线性地址空间①①对应到物理地址上,占用了4个目录项,即4个二级页表。
发现一下三个特点:
①内核代码段和数据段区域在线性地址空间和物理地址空间中一致。
②GDT和IDT在内核数据段中,同样满足特点①;
③除任务0以外,所有其他任务所需要的物理内存页面与线性地址中的不同。
3.6.1 任务0、任务1、其他任务地址内存分配
- 任务0
- 代码段和数据段长度设置为640KB,在线性地址空间中重叠。
- 该任务的代码和数据直接包含在内核代码和数据当中。
- 从线性地址0开始的640KB内容,可以直接使用已设置好的页目录和页表进行分页地址变换。
- 任务1
- 在线性地址空间当中,系统在使用fork()创建任务1(init进程)时:在主内存区申请了一页用于存放任务1的二级页表 ,并复制任务0的页目录和二级页表项
- 其他任务