linux内核学习
内存管理
页
页表项(PTE)

拆解后分两部分:
(1) PFN(物理页号)
就是哪个物理页,存放在 PTE 的高 52 位(细节依赖 CPU)。
物理地址 = PFN << 12
(因为每页 4KB)
(2) flag bits(权限/状态位)
常见的标志位包括:
| 位 | 名称 | 含义 |
|---|---|---|
| P | Present | 是否在内存中 |
| R/W | 可写 | 是否允许写 |
| U/S | 用户态/内核态 | 是否允许用户态访问 |
| NX | Non-Execute | 是否禁止执行 |
| A | Accessed | 是否被访问 |
| D | Dirty | 是否被写过 |
| PAT | caching mode | 缓存策略 |
| Global | 全局页 | TLB 不会在切换 CR3 时刷新 |
| COW | Linux软件层标志 | 写时复制,内核组织在更高层 meta 信息 |
这些位让 CPU 知道如何处理对该页的内存访问。
例:
PTE 中 R/W=0 → 页只读
PTE 中 NX=1 → 页不可执行
PTE 中 Present=0 → 页不在内存,引发 page fault,从 swap/load-in 再填充页表
反向映射(RMAP)技术
基本概念
-
正向映射
当进程分配内存并发生写操作时,会分配虚拟地址并产生缺页,进而分配物理内存并建立虚拟地址到物理地址的映射关系, 这个叫正向映射。
-
反向映射
反过来, 通过物理页面找到映射它的所有虚拟页面叫反向映射(reverse-mapping, RMAP)。
为什么需要反向映射? 主要是为了内存回收。在操作系统中,多个进程的虚拟地址可能映射到同一个物理页(例如共享内存、写时复制等)。当内核需要回收一个物理页时,它必须修改所有映射了该页的页表项,使其无效或指向其他位置。如果没有反向映射,内核将不得不遍历所有进程的页表来寻找映射,这是极其低效的。
Linux2.4没有反向映射
在linux2.4内核中没有反向映射的这个概念,当时为了找到一个物理页面对应的页表项就需要遍历系统中所有的mm组成的链表,然后再遍历每一个mm的每一个vma看这个vma是否映射了这页,这个过程相当低效,最坏情况不得不遍历完所有的mm然后才能找映射到这个页的所有pte。
伙伴系统和slab分配器
在现代操作系统中,内存管理是支撑系统运行的核心支柱之一。它不仅决定了资源利用的效率,还直接影响性能、稳定性和应用程序的体验。在 Linux 内核中,内存管理通过分层设计实现了高效与灵活的平衡,其中 伙伴系统(Buddy System) 是物理内存分配的基础机制。无论是用户态程序通过 malloc() 请求内存,还是内核态创建进程描述符、分配文件缓冲区,背后都离不开伙伴系统的支持。
伙伴系统
Linux内核中使用伙伴系统(buddy system)来分配以页为单位的内存。
首先我们来说一下使用伙伴系统的原因,也就是解释一下内存分配时的内存碎片问题。内存碎片即“碎片的内存”,它分为外碎片和内碎片,内存碎片描述一个系统中所有不可用的空闲内存,这些碎片之所以不能被使用,是因为负责动态分配内存的分配算法使得这些空闲的内存无法使用,这一问题的发生,原因在于这些空闲内存小且以不连续方式出现在不同的位置。因此这个问题的或大或小取决于内存管理算法的实现上。
外碎片
外部碎片指的是还没有被分配出去(不属于任何进程),但由于太小了无法分配给申请内存空间的新进程的内存空闲区域。

内碎片
内部碎片就是已经被分配出去(能明确指出属于哪个进程)却不能被进程利用的内存空间。也就是分配给进程的内存空间过大,进程原本不需要这么多内存。

slab分配器
内存回收
内存回收的方式
需要注意的是,我们这里提到的内存回收指的物理内存管理的逻辑。
1.直接内存回收:在编程语言或操作系统级别手动回收不再使用的内存资源。在编程语言中,就像用户程序通过free函数释放曾经通过malloc函数分配的内存一样。在操作系统层面,直接内存回收通常由垃圾回收器(Garbage Collector)来完成。垃圾回收器会自动检测并回收不再使用的对象所占用的内存空间,以减少内存泄漏和资源浪费。
2.定期扫描回收(kswapd):定期扫描回收(kswapd)是Linux内核中的一个守护进程,用于管理系统内存和交换空间。它负责检查当前内存使用情况,并在需要时进行页面回收或交换页面到磁盘上的交换分区。
3.OOM机制(out of memory):OOM机制是在内存不足时,内核使用了kswapd回收内存后仍然不够分配内存,就会触发OOM机制强制杀死占用内存最多的程序
定期扫描回收(kswapd)
kswapd 是一个内核线程,在内存不足时负责在后台进行内存回收,这个过程发生在后台,因此是异步发生,不会阻塞进程。
内核对内存的容量设置了三个阈值:
- 页最小阈值(pages_min)
- 页低阈值(pages_low)
- 页高阈值(pages_high)
当内存大于 pages_high 时,表示此时系统内存足够,不会进行内存回收。
当内存小于 pages_low 时,表示此时内存存在压力,会触发 kswapd0 进行后台内存回收,直到 pages_high 为止。
当内存小于 pages_min 时,表示此时用户内存耗尽,会触发直接内存回收,进程被阻塞。
直接内存回收
OOM
CPU调度
CPU的上下文切换
上下文切换的概念:如今的OS几乎都支持"同时"运行远大于CPU数量的任务,OS会将CPU轮流分配给它们使用。这就要求OS必须知道从哪里加载任务,以及加载后从哪里开始运行,而这些信息都保存在CPU的寄存器中,其中即将执行的下一条指令的地址被保存在程序计数器(PC)这一特殊寄存器上。我们将寄存器的这些信息称为CPU的上下文,也叫硬件上下文。
OS在切换运行任务时,将上一任务的上下文保存下来,并将即将运行的任务的上下文加载到CPU寄存器上的这一动作,被称为CPU上下文切换。
CPU上下文属于进程上下文的一部分,我们常说的进程上下文由如下两部分组成:
- 用户级上下文:包含进程的运行时堆栈、数据块、代码块等信息。
- 系统级上下文:包含进程标识信息、进程现场信息(CPU上下文)、进程控制信息等信息。
那具体的上下文切换过程是怎么样的呢?上下文信息保存在哪?
操作系统为每个进程分配虚拟内存时,会预留一部分仅内核可见的地址空间。当调度发生时,内核会将当前 CPU 的通用寄存器、程序计数器等现场信息写入该进程内核空间的特定数据结构中(如进程控制块 PCB 关联的内核栈)。
上下文切换的触发机制
- 协作式策略:用户程序通过执行系统调用或产生运算异常(如除零)将 CPU 控制权交还给内核。
- 抢占式策略:操作系统在初始化时向硬件注册中断处理回调,每隔固定时间周期,硬件产生的时钟中断会强制夺回 CPU 处理权并跳转至内核代码。
CPU调度:操作系统从就绪队列中挑选一个进程或者线程作为CPU将要运行的下一个进程或者线程。调度的程序是进程或者线程的内核函数(通过一些调度策略实现)
进行调度的时机:即操作系统什么时候执行进程或者线程的内核函数(调度程度),内核运行调度程序的的条件为一个进程从运行状态切换到等待状态或者当前进程被终结
抢占式和非抢占式调度策略
抢占式:内核可以在进程运行过程中,根据优先级或时间片等策略强制中断当前进程的执行,并将其拥有的 CPU 资源分配给另一个进程的机制。
非抢占式:一旦进程获得处理机便一直运行直至执行完毕或主动阻塞,操作系统内核在此期间不会强制剥夺其 CPU 控制权的调度方式。
注意这里的抢占式和非抢占式同前文的协作式和抢占式不同。此处指的是操作系统的调度策略,前文指的是上下文切换的触发机制,但是从逻辑上来说总体逻辑非常相似
调度算法评判标准
CPU使用率:指的是CPU处于忙状态的时间百分比
吞吐量:单位时间内完成的进程数量
周转时间:指从任务到达至任务完成之间的时间,计算方式为
等待时间:进程在就绪队列中等待的总时间
响应时间:指从任务到达至任务首次被调度的时间
算法目标:
响应时间目标:响应时间是操作系统计算延迟的指标,一个好的调度策略应该做到以下两点
- 减少响应时间,即使处理用户的输入请求,尽快将输出反馈给用户
- 减少平均响应时间的波动
吞吐量目标:吞吐量是操作系统计算带宽的重要指标,一个好的调度策略应该做到以下两点
- 增加吞吐量,减少开销(操作系统进行上下文切换的开销),保证系统的资源得到高效利用(CPU、I/O设备)
- 减少每个进程的等待时间
公平性目标:保证每个进程占用相同的CPU时间,保证每个进程的等待时间相同(即每一个进程都有可能得到操作系统的服务)
批处理调度
FCFS(First Come,First Served):先来先服务
依据进程进入就绪状态队列的先后顺序排序,运行态进程进入等待或结束状态时,就绪队列中的下一个进程就会占用CPU执行,下面举一个先来先服务的示例,三个进程的处理时间分别为12,3,3,分两种进程到达顺序讨论(它们在接近同一时刻到达,但是也有先后顺序)
先来先服务是cpu调度算法最简单的实现方式,但是其缺点是,当一个长任务先到达时,后续的好几个短任务都必须要先等待长任务处理完之后再处理,平均等待时间过长,I/O资源和CPU资源利用率较低,CPU密集型会导致I/O设备闲置,I/O密集型会导致CPU闲置。
SJF(Shortest Job First): 短作业优先算法
选择就绪队列中执行时间最短的进程占用CPU进行入运行状态,就绪队列按照预期的执行时间来排序(准确的进程运行时间在未执行结束前是不知道的,只能通过预判断来获取大致运行时间,然后排序)
HRRN(Highest Response Ratio Next):最高响应比优先算法
RR():时间片轮转算法
MLFQ(Multilevel Feedback Queues):多级反馈队列算法
实时调度
多处理器调度
优先级反转现象
操作系统I/O基础
在计算机操作系统中,I/O就是输入(Input)和输出(Output),也可以理解为读(Read)和写(Write),针对不同的对象,I/O模式可以划分为磁盘IO模型和网络IO模型。
- 读操作:操作系统会先从内核缓冲区中查找是否有用户态所需要的数据,如果有就直接从内核缓冲区copy到用户缓冲区,供用户的应用程序使用。如果没有,对于磁盘I/O来说直接从磁盘中读取到内核缓冲区。而对于网络I/O,应用程序需要等待客户端发送数据,如果客户端还没有发送数据,对应的应用程序将会被阻塞,直到客户端发送了数据,该应用程序才会被唤醒,从Socket协议找中读取客户端发送的数据到内核空间,然后把内核空间的数据copy到用户空间,供应用程序使用。
- 写操作:用户的应用程序将数据从用户空间copy到内核空间的缓冲区中(如果用户空间没有相应的数据,则需要从磁盘—>内核缓冲区—>用户缓冲区依次读取),这时对用户程序来说写操作就已经完成,至于什么时候再写到磁盘或通过网络发送出去,由操作系统决定。除非应用程序显示地调用了sync 命令,立即把数据写入磁盘,或执行flush()方法,通过网络把数据发送出去。