news 2026/2/28 18:49:43

【Linux内核设计与实现读书笔记】(三)进程管理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【Linux内核设计与实现读书笔记】(三)进程管理

Linux内核设计与实现读书笔记—(三)进程管理

(1)进程

①啥是进程

进程是处于执行期的程序。不仅是代码。 进程不仅仅包含可执行的代码(Unix称其为代码段 text section),它还是所有相关资源的集合。这包括:

  • 打开的文件
  • 挂起的信号
  • 内核内部数据
  • 处理器状态
  • 内存地址空间(包含内存映射)
  • 一个或多个执行线程
  • 数据段(存放全局变量)

程序本身不是进程,程序是静态的目标码。进程是程序执行时的实时结果。完全可能有两个不同的进程在执行同一个程序,它们并存且可以共享资源(如打开的文件、地址空间)。

②啥是线程

基本概念: 线程是进程中活动的对象。每个线程拥有独立的程序计数器、进程栈和一组进程寄存器。

调度单位: 内核调度的对象实际上是线程,而不是进程。

Linux 的特殊性(重点):

  • Linux 内核不区分线程和进程。
  • 在 Linux 内核眼中,线程只不过是一种特殊的进程(通常被称为轻量级进程),它们与其他进程共享某些资源(如地址空间)。这与其他操作系统专门设计线程机制有很大不同。

③进程的虚拟机制

虚拟处理器:让进程觉得自己在独享 CPU(实际上是多进程共享,由调度器管理)。

虚拟内存 :让进程在分配和管理内存时觉得拥有整个系统的所有内存资源。

同一进程内的线程之间共享虚拟内存,但每个线程拥有各自的虚拟处理器。

④进程的生命周期

Linux 进程的生命周期主要由几个核心系统调用控制:

  • 创建 (Creation) - fork():
    • Linux 通过复制一个现有进程(父进程)来创建一个全新的进程(子进程)。
    • fork() 从内核返回两次:一次回到父进程,一次回到新产生的子进程。
    • 在现代 Linux 内核中,fork()实际上是由 clone()系统调用实现的。
  • 加载 (Loading) - exec():
    • 创建进程后,通常接着调用 exec()这组函数。它会创建新的地址空间,并将新的程序载入其中执行。
  • 终结 (Termination) - exit():
    • 程序通过 exit()系统调用退出执行,终结进程并释放占用的资源。
  • 清理与等待 - wait4():
    • 父进程通过 wait4()查询子进程是否终结。这使得父进程可以等待特定子进程执行完毕。
    • 僵死状态 (Zombie):进程退出执行后,直到它的父进程调用 wait()为止,它会被设置为僵死状态。

⑤Linux内核通常把进程也叫做任务

(2)进程描述符及任务结构

①进程描述符

内核把进程的列表存放在叫做任务队列的双向循环链表中。链表中的每一项都是类型为task_struct,称为进程描述符的结构,进程描述符包含一个具体进程的所有信息(打开的文件,进程的地址空间,挂起的信号,进程的状态还有其他更多的信息)。

②分配进程描述符

Linux 通过 slab 分配器动态生成 task_struct,以实现对象复用和缓存着色。

③进程描述符的存放

内核通过唯一的进程标识符(PID)来标识每一个进程,这个PID也在进程各自的进程描述符中。

访问任务需要获取指向task_struct的指针,但不同的硬件结构实现方式不同有些硬件结构体系专门拿出一个专门的寄存器来存放当前进程的task_struct的指针,用于加快访问速度。像x86这种寄存器不富裕的体系结构,只能在内核栈的尾端创建thread_info结构,同步内部各个计算偏移间接地查找task_struct结构。对于ARM架构优先采用寄存器存储 task_struct 指针。

④进程状态

进程描述符中的 state`域描述了进程的当前状态,进程必处于以下五种状态之一:

  1. TASK_RUNNING (运行):进程是可执行的(正在执行或在运行队列中等待)。
  2. TASK_INTERRUPTIBLE (可中断):进程正在睡眠(被阻塞),等待条件达成。可以被信号唤醒。
  3. TASK_UNINTERRUPTIBLE (不可中断):进程正在睡眠,但不响应信号。通常用于关键的 I/O 操作,防止数据破坏。
  4. __TASK_TRACED:被其他进程跟踪(如 ptrace 调试)。
  5. __TASK_STOPPED (停止):停止执行,通常由 SIGSTOP 等信号触发。

操作状态:使用 set_task_state(task, state) 函数来调整状态,它包含内存屏障以保证 SMP 下的安全性。

⑤进程上下文

当一个程序调执行了系统调用或者触发了某个异常,它就陷入了内核空间。此时,我们称内核“代表进程执行”并处于进程上下文中。在此下文中current(指向该进程的 task_struct)宏是有效的。

⑥进程家族树

Linux系统中所有的进程都是PID为1的init进程的后代。内核在系统启动的最后阶段启动init进程。该进程读取系统的初始化脚本并执行其他的相关程序,最终完成系统启动的整个过程。系统中的每个进程必有一个父进程,相应的,每个进程也可以拥有零个或多个子进程。拥
有同一个父进程的所有进程被称为兄弟。进程间的关系存放在进程描述符中。每个task_struct都包含一个指向其父进程tast_struct、叫做 parent 的指针,还包含一个称为children的子进程链表。

(3)进程创建

①写时拷贝

传统的fork()系统调用直接把所有的资源复制给新创建的进程。这种实现过于简单并且效率低下,因为它拷贝的数据也许并不共享,更糟的情况是,如果新进程打算立即执行一个新的映像,那么所有的拷贝都将前功尽弃。Linux的fork()使用写时拷贝(copy-on-write)页实现。写时拷贝是一种可以推迟甚至免除拷贝数据的技术。内核此时并不复制整个进程地址空间,而是让父进程和子进程共享同一个拷贝。
只有在需要写入的时候,数据才会被复制,从而使各个进程拥有各自的拷贝。也就是说,资源的复制只有在需要写人的时候才进行,在此之前,只是以只读方式共享。这种技术使地址空间上的页的拷贝被推迟到实际发生写入的时候才进行。在页根本不会被写入的情况下(举例来说,fork()后立即调用exec()它们就无须复制了。

②fork()

Linux 里的 fork()其实是通过 clone() 系统调用来实现的。这后面有一系列复杂的调用链:fork()-> clone() -> do_fork()-> copy_process()。

真正干活的是 copy_process(): 生孩子的具体步骤都在这儿:

  1. 复制身份证:给孩子发一个新的 task_struct(进程描述符),但里面的信息先抄爸爸的。
  2. 检查户口:看看你这个用户是不是生的孩子太多了,有没有超标。
  3. 划清界限:把孩子身上的一些统计信息清零,不能继承爸爸的“工龄”和“计时”。
  4. 先别动:把孩子的状态设为 TASK_UNINTERRUPTIBLE(不可中断),保证它还没生好前别乱跑。
  5. 分配 PID:给孩子发一个独一无二的身份证号(PID)。
  6. 处理资源:根据参数决定是复制文件、信号等资源,还是和爸爸共享。
  7. 扫尾:返回指向孩子的指针。

内核通常会让孩子(子进程)先运行。因为孩子大概率会马上调用 exec()`变身去干别的事),这样就避免了写时拷贝的发生(如果爸爸先跑,爸爸可能会改数据,一改就会触发拷贝,浪费时间)。

③vfork()

vfork() 是个老东西。除了不拷贝父进程的页表项外,vfork()系统调用和fork()的功能相同。以前是因为 fork()太慢才用它,现在 fork() 有了写时拷贝,已经很快了,所以 vfork`基本上没什么用了,除非你要极致的性能优化或者在那特定场景下。

(4)线程在Linux中的实现

Linux实现线程的机制非常独特。从内核的角度来说,它并没有线程这个概念。Linux把所有的线程都当做进程来实现。内核并没有准备特别的调度算法或是定义特别的数据结构来表征线 程。相反,线程仅仅被视为一个与其他进程共享某些资源的进程。每个线程都拥有唯一隶属于自 己的task_struct,所以在内核中,它看起来就像是一个普通的进程(只是线程和其他一些进程共 享某些资源,如地址空间)。

①创造线程

线程的创建和普通进程的创建类似,只不过在调用clone()的时候需要传递一些参数标志来指明需要共享的资源:

②内核线程

上面说的普通线程(用户能看见的),Linux 还有一种特殊的内核线程(Kernel Thread)内核线程和普通的进程间的区别在于内核线程没有独立的地址空间。它们只在内核空间运行,从来不切换到用户空间去。内核进程和普通进程一样,可以被调度,也可以被枪占。

(5)进程终结

**①进程终结 **

进程终结的主要入口是 do_exit() (定义于 kernel/exit.c),无论是主动调用 exit()还是因异常/信号被动终止,最终都会执行此函数。

核心逻辑 (do_exit 的工作):

  1. 标记状态:将 task_struct 中的 flags设置为 PF_EXITING。
  2. 资源解绑
    • 删除内核定时器 (del_timer_sync)。
    • 释放内存描述符 mm_struct(若未被共享)。
    • 退出 IPC 队列 (sem__exit)。
    • 递减文件描述符及文件系统引用计数。
  3. 保存现场:将退出代码 (exit code) 写入 task_struct,供父进程检索。
  4. 通知机制:调用 exit_notify()向父进程发送信号,并为自己的子进程寻找新父进程。
  5. 进入僵死态:将状态设置为 EXIT_ZOMBIE。
  6. 调度切换:调用 schedule()切换到新进程。注意: do_exit()永不返回。

此时进程不可运行,大部分资源已释放,但内核栈、thread_info 和 task_struct 依然保留。这是为了向父进程提供信息。

**②删除进程描述符 **

僵死进程 (Zombie) 必须等待父进程回收,才能彻底释放剩余的内核数据结构。

  • 回收触发:父进程调用 wait() 族函数。
  • 清理函数:release_task()。
  • 清理步骤
    1. 脱离组织:调用 __exit_signal() -> unhash_process(),从 PID 哈希表 (pidhash) 和任务列表 (task list) 中删除该进程。
    2. 资源彻底释放:释放目前的僵死进程所使用的剩余资源。
    3. 释放内存页:释放内核栈和 thread_info结构所占的页。
    4. 释放缓存:释放 task_struct所占的 slab 高速缓存。

**③ 孤儿进程处理 **

若父进程在子进程结束前退出,子进程成为“孤儿进程”。必须为孤儿进程找到新的父进程,否则它们将永久处于僵死状态 (Memory Leak)。

  • 寻父机制:在 do_exit()中调用 exit_notify()-> forget_original_parent()-> find_new_reaper()。
  • 寻父优先级
    1. 线程组内其他线程:若当前线程组有其他存活线程,则过继给它们。
    2. init 进程:若无其他线程,最终过继给 PID 为 1 的 init 进程。
  • init 的职责:init进程会例行调用 wait()来检查并清理其名下的僵死子进程,确保系统资源被回收。
  • Ptrace 特殊处理:被调试(ptrace)的进程若父进程退出,会先在 ptrace 子进程链表中搜索兄弟进程,最后同样由 init兜底。

程**:若无其他线程,最终过继给 PID 为 1 的 init 进程。

  • init 的职责:init进程会例行调用 wait()来检查并清理其名下的僵死子进程,确保系统资源被回收。
  • Ptrace 特殊处理:被调试(ptrace)的进程若父进程退出,会先在 ptrace 子进程链表中搜索兄弟进程,最后同样由 init兜底。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/27 21:02:55

三分钟上手DNN多输出预测(附保姆级代码)

DNN多输出回归 基于深度神经网络(DNN)的多输出回归预测(多输入多输出) 程序已经调试好,数据格式为excel(如下图),仅需根据你的输出个数修改outdim值即可 1、运行环境要求MATLAB版本为2019b及其以上 2、评价指标包括:R2、MAE、MBE、RMSE等,图很…

作者头像 李华
网站建设 2026/2/27 8:16:47

什么是苹果MFi认证,有什么优势?

MFi 认证(Made for iPhone/iPad/iPod)是苹果面向第三方配件的官方许可计划,核心是通过苹果授权芯片、严格测试与协议适配,确保配件在兼容性、安全性和性能上符合苹果标准,可合法使用 MFi 标识并接入苹果生态核心功能&a…

作者头像 李华
网站建设 2026/2/25 11:23:26

Conda与Pip双管齐下:优化PyTorch-CUDA依赖安装流程

Conda与Pip双管齐下:优化PyTorch-CUDA依赖安装流程 在深度学习项目的实际开发中,最让人头疼的往往不是模型设计或训练调参,而是环境配置——尤其是当你满怀期待地运行代码时,却弹出一行红色错误:“CUDA is not availab…

作者头像 李华
网站建设 2026/2/26 9:31:51

CKA-Agent:揭示商业LLM安全防线的“特洛伊知识“漏洞

🔓 CKA-Agent:揭示商业LLM安全防线的"特洛伊知识"漏洞 论文标题: The Trojan Knowledge: Bypassing Commercial LLM Guardrails via Harmless Prompt Weaving and Adaptive Tree Search 项目地址: https://github.com/Graph-COM/CKA-Agent 论文…

作者头像 李华
网站建设 2026/2/26 2:05:06

构筑智能心理新基建:北京朗心致远AI心理场室与设备整体解决方案

在心理健康日益受到全社会关注的当下,完善的心理服务基础设施已成为现代组织与社区不可或缺的组成部分。北京朗心致远科技有限公司,作为专注于 心理健康场室建设 与 智能心理设备 研发的专业机构,旨在为教育、企事业单位、医疗社区、司法武警…

作者头像 李华