Linux内核

一、进程调度子系统(Process Scheduler)

1. 核心职责

进程调度子系统是内核的 “CPU 资源分配器”,负责决定哪个进程 / 线程哪个 CPU 核心上**何时运行 **,核心目标是:

  • 公平性:所有进程都能获得合理的 CPU 时间片;
  • 高效性:最大化 CPU 利用率,减少进程切换开销;
  • 响应性:保证交互式进程(如终端、GUI 程序)的快速响应。

2. 核心概念与机制

(1)进程状态

内核通过 task_struct 结构体的 state 字段描述进程状态,关键状态包括:

状态 含义
TASK_RUNNING 运行态 / 就绪态:进程正在 CPU 上运行,或在就绪队列等待调度
TASK_INTERRUPTIBLE 可中断睡眠态:进程等待某事件(如 IO 完成、信号),可被信号唤醒
TASK_UNINTERRUPTIBLE 不可中断睡眠态:进程等待硬件事件(如磁盘 IO),不能被信号唤醒,避免硬件操作被打断
TASK_ZOMBIE 僵尸态:进程已终止,但父进程未调用 wait 回收其资源,task_struct 仍存在
TASK_STOPPED 停止态:进程被信号(如 SIGSTOP)暂停执行

(2)调度策略

Linux 内核支持多种调度策略,以适配不同类型的进程:

  1. SCHED_NORMAL(默认)

    • 面向普通进程(如后台服务、命令行程序),采用 CFS(完全公平调度器) 实现;
    • CFS 的核心思想:给每个进程分配比例公平的 CPU 时间,通过 “虚拟运行时间” 计算进程优先级,虚拟运行时间越短的进程,越优先被调度。
  2. SCHED_FIFO

    • 实时先进先出调度策略,适用于硬实时进程;
    • 高优先级进程一旦就绪,立即抢占低优先级进程;同优先级进程按 “先到先服务” 顺序执行,直到主动放弃 CPU。
  3. SCHED_RR

    • 实时时间片轮转调度策略,适用于软实时进程;
    • 同优先级进程轮流使用 CPU 时间片(默认 100ms),时间片耗尽后被放回就绪队列尾部。

(3)关键数据结构

  • **task_struct**:进程控制块,存储进程的调度信息(优先级、调度策略、虚拟运行时间等);
  • **runqueue**:每个 CPU 核心对应一个就绪队列,存储该核心上的就绪进程,避免多核心竞争;
  • **sched_entity**:调度实体,嵌入在 task_struct 中,描述进程的调度属性(如虚拟运行时间、权重)。

3. 调度流程

  1. 触发时机:进程时间片耗尽、进程主动放弃 CPU(如 sleep)、高优先级进程就绪;
  2. 选择下一个进程:CFS 遍历就绪队列,选择虚拟运行时间最短的进程;
  3. 上下文切换:保存当前进程的 CPU 寄存器状态(到 task_struct),恢复目标进程的寄存器状态,完成进程切换。

二、内存管理子系统(Memory Management, MM)

1. 核心职责

内存管理子系统是内核的 “内存资源管理器”,负责物理内存和**虚拟内存 ** 的分配、回收与映射,核心目标是:

  • 为每个进程提供独立的虚拟地址空间,避免进程间内存冲突;
  • 高效利用物理内存(如通过分页、交换、缓存机制);
  • 支持内存共享(如动态库、进程间共享内存)。

2. 核心概念与机制

(1)虚拟地址空间

在 64 位 Linux 系统中,每个进程拥有 128TB 的虚拟地址空间,内核将其划分为两个部分:

  • 用户空间(0 ~ 0x7fffffffffff):进程私有,包含代码段、数据段、堆、栈等;
  • 内核空间(0xffff880000000000 ~ 末尾):所有进程共享,存放内核代码和数据,用户态进程需通过系统调用访问。

(2)分页机制(核心)

虚拟地址无法直接访问物理内存,内核通过分页机制实现虚拟地址到物理地址的映射:

  1. 页面大小:默认 4KB(可配置为 2MB/1GB 大页,减少页表开销);
  2. 多级页表:64 位系统使用 4 级页表(PGD → PUD → PMD → PTE),避免页表占用过多内存;
  3. MMU 硬件加速:CPU 内置的内存管理单元(MMU)会缓存页表项(TLB),加速地址转换。

(3)物理内存分配机制

内核提供两种核心物理内存分配器,适配不同场景:

  1. 伙伴系统(Buddy System)

    • 用于分配 大块连续物理内存(以 2 的幂次方为单位,如 4KB、8KB、16KB);
    • 核心思想:将物理内存划分为多个块组,分配时寻找大小匹配的块,释放时合并相邻块,减少内存碎片。
  2. Slab 分配器

    • 用于分配 小块内存(如内核数据结构 task_structfile);
    • 核心思想:为每种数据结构创建一个 “缓存池”,预分配若干个对象,分配 / 释放时直接从缓存池获取 / 归还,避免频繁调用伙伴系统,提升效率。

(4)虚拟内存扩展机制

  1. 交换分区(Swap)

    • 当物理内存不足时,内核将不常用的内存页面写入磁盘交换分区,释放物理内存;
    • 当进程再次访问该页面时,内核将其从交换分区读回物理内存(缺页中断)。
  2. 内存映射(mmap)

    • 将文件 / 设备映射到进程的虚拟地址空间,进程可通过内存操作直接读写文件,无需 read/write 系统调用;
    • 核心优势:减少数据拷贝次数,提升大文件操作效率。

3. 关键数据结构

  • **mm_struct**:进程内存描述符,存储进程的虚拟地址空间信息(如页表指针、堆 / 栈起始地址);
  • **page**:物理页面描述符,每个物理页面对应一个 page 结构体,记录页面的状态(如是否被占用、所属进程);
  • **vm_area_struct**:虚拟内存区域描述符,描述进程虚拟地址空间中的一个连续区域(如代码段、堆)。

4. 缺页中断处理流程

当进程访问未映射的虚拟地址时,触发缺页中断,内核处理流程:

  1. 检查虚拟地址是否合法(是否在进程的 vm_area_struct 范围内);
  2. 若合法:分配物理页面,建立虚拟地址到物理地址的映射,更新页表;
  3. 若非法:触发段错误(SIGSEGV),终止进程。

三、虚拟文件系统子系统(Virtual File System, VFS)

1. 核心职责

虚拟文件系统是内核的 “文件系统抽象层”,它屏蔽了不同物理文件系统(如 ext4、xfs、tmpfs)的差异,为用户态提供**统一的文件操作接口 **(open/read/write/close)。核心目标是:

  • 支持多种文件系统的无缝挂载与访问;
  • 统一管理所有文件类资源(普通文件、目录、设备文件、套接字)。

2. 核心设计思想

VFS 定义了四个核心抽象对象,所有物理文件系统都需实现这些对象的操作接口,从而接入 VFS 框架:

抽象对象 作用 对应物理文件系统实现
超级块(super_block) 描述一个已挂载的文件系统的全局信息(如文件系统类型、块大小) ext4 的 ext4_sb_info
索引节点(inode) 描述一个文件的元数据(如大小、权限、创建时间),每个文件对应唯一的 inode ext4 的 ext4_inode_info
目录项(dentry) 描述文件的路径信息(如文件名、父目录),是路径到 inode 的映射,存在于目录项缓存 VFS 统一管理,与物理文件系统无关
文件对象(file) 描述进程打开的文件的上下文信息(如当前读写位置、打开模式) 与物理文件系统无关

3. 核心机制

(1)目录项缓存(Dentry Cache)

  • 内核将常用的目录项(dentry)缓存到内存中,避免每次访问文件都从磁盘读取目录结构;
  • 缓存采用 LRU 算法淘汰不常用的目录项,平衡缓存命中率和内存占用。

(2)页缓存(Page Cache)

  • 内核将文件数据缓存到物理内存中(以页面为单位),进程读取文件时优先从页缓存获取,未命中时再从磁盘读取;
  • 写文件时,数据先写入页缓存,内核通过回写线程异步将脏页面写入磁盘,提升写操作效率。

(3)设备文件(/dev 目录)

VFS 将硬件设备抽象为文件,通过设备驱动实现文件操作接口:

  • 字符设备:以字节流方式访问(如串口、键盘),对应 c 类型文件;
  • 块设备:以块为单位访问(如磁盘、U 盘),对应 b 类型文件;
  • 进程通过 read/write 操作设备文件,内核会调用对应的设备驱动函数,完成硬件交互。

4. 典型操作流程(以 read 为例)

  1. 用户态调用 read(fd, buf, len),触发系统调用;
  2. 内核通过进程的 files_struct 找到 file 对象;
  3. 调用 file->f_op->read 函数(由具体文件系统实现);
  4. 检查页缓存:若数据在页缓存中,直接拷贝到用户缓冲区;若不在,从磁盘读取数据到页缓存,再拷贝到用户缓冲区;
  5. 更新 file->f_pos(文件当前读写位置),返回读取的字节数。

四、网络接口子系统(Network Interface, NET)

1. 核心职责

网络接口子系统是内核的 “网络通信管理器”,实现了 TCP/IP 协议栈的核心功能,负责**网络数据包的收发、路由、协议解析 **。核心目标是:

  • 为应用程序提供统一的网络编程接口(如 socket 套接字);
  • 支持多种网络协议(TCP、UDP、ICMP 等)和网络设备(以太网、无线网卡);
  • 实现高效的数据包转发与处理。

2. 核心架构与协议栈

Linux 网络子系统采用分层架构,与 TCP/IP 五层模型对应:

协议栈分层 内核组件 核心功能
应用层 socket 接口 为用户态提供网络编程接口(socket/bind/listen/accept
传输层 TCP/UDP 协议模块 实现端到端通信(TCP 可靠传输、UDP 无连接传输)
网络层 IP/ICMP 协议模块 实现数据包的路由选择与分片 / 重组
数据链路层 网络设备驱动 / 网桥 实现 MAC 地址解析、帧的收发
物理层 硬件网卡 负责二进制数据的电信号传输

3. 核心机制与数据结构

(1)Socket 套接字

Socket 是网络通信的端点,内核通过 socket 结构体描述一个套接字:

  • sk:指向 sock 结构体,存储套接字的核心信息(如协议类型、本地 / 远端地址、接收 / 发送缓冲区);
  • ops:指向套接字操作函数集(socket_ops),不同协议(TCP/UDP)有不同的实现;
  • file:关联到 VFS 的 file 对象,使套接字可以通过文件描述符操作。

(2)TCP 协议核心机制

  1. 三次握手与四次挥手:实现 TCP 连接的建立与关闭;
  2. 滑动窗口:实现流量控制,避免发送方发送速率过快导致接收方缓冲区溢出;
  3. 拥塞控制:通过慢启动、拥塞避免、快速重传 / 恢复等算法,适应网络拥塞状态;
  4. 收发缓冲区:内核为每个 TCP 套接字分配接收缓冲区(存储待应用程序读取的数据)和发送缓冲区(存储待网络发送的数据)。

(3)数据包收发流程(以 TCP 接收为例)

  1. 网卡收到数据包后,触发硬中断,内核中断处理函数将数据包从网卡缓冲区拷贝到内核内存;

  2. 硬中断处理完成后,触发软中断,内核协议栈开始解析数据包:

    • 数据链路层:解析 MAC 地址,判断是否为本机数据包;
    • 网络层:解析 IP 地址,判断是否为本地数据包或需要转发;
    • 传输层:解析 TCP 端口,找到对应的 socket 套接字;
  3. 内核将数据包数据写入 socket 的接收缓冲区;

  4. 唤醒等待该 socket 数据的进程(如调用 read 阻塞的进程);

  5. 应用程序调用 read 读取接收缓冲区的数据。

五、进程间通信子系统(Inter-Process Communication, IPC)

1. 核心职责

进程间通信子系统提供了进程间数据交换与同步的机制,由于进程拥有独立的虚拟地址空间,无法直接访问彼此的内存,因此需要内核提供专门的 IPC 机制。核心目标是:

  • 实现进程间的数据传输(如传递字符串、文件描述符);
  • 实现进程间的同步与互斥(如锁、信号量)。

2. Linux 核心 IPC 机制

Linux 提供了多种 IPC 机制,适用于不同的应用场景,可分为三大类:

(1)单机 IPC 机制

IPC 机制 核心特点 适用场景
管道(Pipe) 半双工通信,数据单向流动;基于文件系统,生命周期随进程 父进程与子进程间的简单通信
命名管道(FIFO) 全双工通信,可用于任意进程间通信;以文件形式存在于文件系统 无亲缘关系进程间的简单通信
信号(Signal) 异步通信,通过信号传递事件(如 SIGINTSIGTERM);只能传递有限的信号编号 进程间的事件通知(如终止进程、暂停进程)
共享内存(Shared Memory) 最快的 IPC 机制;内核分配一块物理内存,映射到多个进程的虚拟地址空间 大量数据的高频传输(如数据库、游戏引擎)
信号量(Semaphore) 用于进程间的同步与互斥;通过 PV 操作实现对临界资源的保护 多进程共享资源时的互斥访问(如共享内存的读写同步)
消息队列(Message Queue) 基于队列的消息传递;消息有类型和优先级,可实现异步通信 进程间的异步消息传递(如服务端与客户端的指令交互)

(2)网络 IPC 机制

  • Socket 套接字:支持跨主机的进程间通信,基于 TCP/UDP 协议;
  • 是网络编程的核心接口,既可以用于单机通信(127.0.0.1 回环地址),也可以用于跨网络通信。

(3)特殊 IPC 机制

  • 文件描述符传递:通过 unix domain socket(UNIX 域套接字)在进程间传递文件描述符;
  • 适用于进程间共享打开的文件、套接字等资源。

3. 核心 IPC 机制详解(以共享内存为例)

共享内存是性能最高的 IPC 机制,核心流程:

  1. 创建共享内存:进程调用 shmget 函数,内核分配一块物理内存,返回共享内存标识符 shmid
  2. 映射到进程地址空间:进程调用 shmat 函数,将共享内存映射到自己的虚拟地址空间;
  3. 进程间通信:多个进程映射同一块共享内存后,可直接读写该内存区域,无需内核拷贝数据;
  4. 解除映射与销毁:进程调用 shmdt 解除映射;最后一个进程解除映射后,调用 shmctl 销毁共享内存,释放物理内存。

核心优势:数据无需在进程间拷贝,直接访问物理内存,通信效率极高;

注意事项:需要配合信号量等同步机制,避免多进程同时读写共享内存导致数据竞争。

六、五大子系统的协同工作

Linux 内核的五大子系统并非孤立存在,而是紧密协作,共同支撑应用程序的运行。以一个 “TCP 网络通信” 场景为例,子系统的交互流程:

  1. 进程调度:用户态进程调用 socket/read 系统调用,触发内核态切换;内核调度进程进入睡眠态,直到网络数据到达。
  2. 网络接口:网卡收到数据包后,内核协议栈解析数据包,将数据写入 socket 接收缓冲区;唤醒睡眠的进程。
  3. 进程调度:进程被唤醒,重新进入就绪队列,等待 CPU 调度执行。
  4. 内存管理:进程调用 read 读取接收缓冲区数据时,内核将内核态缓冲区的数据拷贝到用户态缓冲区(涉及虚拟地址到物理地址的映射)。
  5. 虚拟文件系统:socket 作为特殊的文件对象,通过 VFS 的 file 结构体管理,进程通过文件描述符操作 socket。
  6. 进程间通信:若进程需要将接收到的数据传递给另一个进程,可通过共享内存、消息队列等 IPC 机制实现。