:从冯诺依曼到 PCB,一篇打通进程入门核心)
学习 Linux 系统编程进程是绕不开的核心基石。它是操作系统分配资源的最小单位也是理解并发、调度、内存管理的关键。这篇博客结合核心知识点补充带你从底层硬件逻辑出发一步步拆解进程本质直达内核原理。一、先搞懂底层冯诺依曼体系结构我们日常使用的电脑、服务器几乎都遵循冯诺依曼体系它决定了数据到底怎么在硬件间流动。核心组成输入单元键盘、鼠标、扫描仪等CPU中央处理器运算器 控制器输出单元显示器、打印机等存储器内存这里特指内存不是硬盘黄金规则必记CPU只能直接读写内存不能直接访问外设。外设键盘、硬盘、显示器只能和内存打交道。一句话总结所有设备只跟内存玩CPU 只跟内存说话。举个例子QQ 发消息键盘输入 → 写入内存 → CPU 读取处理 → 写入内存 → 网卡从内存取数据发送全程没有任何设备直接跟 CPU “直连” 通信。二、操作系统到底在 “管理” 什么操作系统OS本质就是一款专门搞管理的软件。1. OS 的两层定位向下管理所有硬件资源CPU、内存、磁盘、外设向上给应用程序提供稳定、安全、统一的运行环境2. 操作系统的管理逻辑操作系统管理任何东西都遵循两步描述用struct结构体把对象属性描述清楚组织用链表、红黑树等结构把对象串起来管理内核管理进程也是一样先描述再组织。3. 系统调用与库函数系统调用操作系统暴露给上层的底层接口偏底层、难用。库函数对系统调用做封装更友好、更易用。三、进程到底是什么最核心定义标准定义课本程序的执行实例内核分配系统资源CPU 时间、内存的基本实体最真实的内核公式进程 task_struct内核数据结构 程序代码 数据创建进程先建立 task_struct再加载代码数据销毁进程先释放代码数据最后释放 task_struct四、PCB进程在内核的 “身份证”PCBProcess Control Block进程控制块是进程所有属性的集合。在 Linux 中PCB 的具体实现就是task_struct 结构体。它被加载到内存里内核靠它唯一识别、管理一个进程。task_struct 核心内容PID进程唯一标识符PPID父进程 ID进程状态运行、睡眠、停止、僵尸…优先级CPU 调度先后顺序程序计数器 PC下一条要执行的指令地址内存指针指向代码、数据、共享内存上下文数据CPU 寄存器中的瞬时数据切换进程用I/O 信息打开的文件、I/O 请求记账信息CPU 使用时长、时间片退出信息进程退出码、退出原因int code等成员内核如何组织进程所有进程的task_struct在内核中以双向链表形式串起来方便遍历、调度、管理 —— 这是进程控制的核心实现方式。五、进程状态挂起 / 阻塞与核心状态详解1. 核心状态概念补充阻塞状态进程等待某事件如 I/O、信号完成无法执行对应 Linux 中的 S/D 状态挂起状态进程数据从内存换出到磁盘暂时不参与调度Linux 无单独标识是状态 内存状态的组合2. Linux 内核定义的 7 种进程状态static const char *const task_state_array[] { R (running), /* 0 运行态 */ S (sleeping), /* 1 可中断睡眠/阻塞 */ D (disk sleep), /* 2 不可中断睡眠/阻塞 */ T (stopped), /* 4 停止态 */ t (tracing stop), /* 8 追踪停止态 */ X (dead), /* 16 死亡态 */ Z (zombie), /* 32 僵尸态 */ };3. 状态细分解释R 运行态正在运行 OR 在运行队列等待 CPUS 可中断睡眠等待事件可被信号唤醒典型阻塞状态D 不可中断睡眠等 I/O不能被打断防止数据错乱磁盘阻塞T 停止态进程做了非法但不致命的操作被 OS 暂停或收到 SIGSTOP 信号暂停t 追踪停止调试时触发断点进程被追踪暂停gdb 调试时的状态X 死亡态瞬间消失看不到Z 僵尸态子进程退出父进程未读取退出信息4. 前台 / 后台进程状态标识前台进程状态后带如S可直接用Ctrl C终止后台进程启动方式./程序 或给前台进程发暂停信号后切换状态标识无如S终止方式只能用kill 进程PID终止六、进程退出核心逻辑与退出码1. 进程退出的三步核心逻辑代码停止执行 →立即释放程序代码和数据这是最先释放的资源进程退出信息退出码、退出原因→ 保存在task_struct内部的成员属性中如int code管理结构task_struct→ 必须被 OS 维护方便用户 / 父进程获取退出信息2. 退出码查看echo $?# 执行一个成功的命令 ls echo $? # 输出 0表示最近一个程序执行成功 # 执行一个失败的命令 ls /xxx echo $? # 输出非0如2表示执行失败echo $?查看最近一个执行程序的退出信息0 表示成功非 0 表示失败退出码含义自定义如 main 返回值或系统约定1-255七、僵尸进程与孤儿进程补充核心细节1. 僵尸进程Zombie形成父进程存活子进程退出但父进程未读取其退出状态核心特征维护自己的task_struct方便未来父进程读取状态危害task_struct一直占用内存默认无人主动回收大量僵尸会导致内存泄漏、系统资源耗尽本质PCB 必须保留到父进程 “签收” 退出信息为止2. 孤儿进程形成父进程先退出子进程仍在运行处理机制子进程被1 号进程systemd/initd领养结果子进程退出时由系统进程自动回收task_struct不会变成僵尸进程八、查看 / 创建进程实操代码1. 查看进程 PID 代码#include stdio.h #include sys/types.h #include unistd.h int main() { printf(pid: %d\n, getpid()); // 当前进程ID printf(ppid: %d\n, getppid()); // 父进程ID return 0; }2. 最简 fork 创建子进程代码#include stdio.h #include sys/types.h #include unistd.h int main() { pid_t id fork(); if (id 0) { perror(fork); return 1; } else if (id 0) { // 子进程 printf(I am child, pid: %d\n, getpid()); sleep(10); // 留时间观察状态 } else { // 父进程 printf(I am parent, pid: %d\n, getpid()); sleep(20); // 留时间观察子进程僵尸态 } return 0; }总结进程本质进程 task_struct 代码 数据创建先建 task_struct释放后释 task_struct内核用双向链表管理所有进程进程状态t 状态是调试断点暂停T 状态是 OS 因非法操作 / 信号暂停前台进程带可 CtrlC 终止后台无需 kill 终止进程退出先释放代码数据退出信息存在 task_struct僵尸进程是父进程未读退出信息孤儿进程被 1 号进程领养避免僵尸这些是进程学习的核心基础理解透这些概念就能轻松应对进程调度、内存管理等进阶知识点