1. PCB 到底是什么?
从代码角度看,PCB 就是内核空间里定义的一个巨大的 C 语言结构体 (Struct)。
-
存放位置 :它存在于内核内存区 (Kernel Space),普通用户程序无法直接读写,只有操作系统核心才能访问。
-
生命周期 :进程创建时 (
fork) 产生,进程结束时回收。如果进程退出了但 PCB 没回收,就变成了"僵尸进程"。
2. PCB 里存了什么?(核心解剖)
PCB 里的信息非常多,但归纳起来主要包含以下 4 大类核心信息:
A. 进程标识信息 (Who am I?)
这是进程的身份证明。
-
PID (Process ID):进程的唯一编号(身份证号)。
-
PPID (Parent PID):父进程的 ID(户口本上的父亲)。
-
UID/GID:属于哪个用户、哪个组(决定了你有没有权限读写文件)。
B. 处理器状态信息 (Where did I stop?) ------ 最关键!
这是实现并发 和上下文切换的基础。当进程被 CPU 踢下去(暂停)时,必须把当前的"案发现场"保存下来,下次回来才能继续跑。
-
通用寄存器值:EAX, EBX 等寄存器里暂存的数据。
-
程序计数器 (PC / IP) :最重要。记录了下一条该执行哪行代码指令。
-
栈指针 (SP):记录当前函数调用栈顶在哪里。
-
状态字 (PSW):记录当前 CPU 的状态(如是否发生了进位、溢出等)。
通俗理解 :这就好比玩单机游戏时的"存档"。没有这个存档,下次打开游戏你就得从头开始,而不能接着上次的进度玩。
C. 进程调度与控制信息 (When can I run?)
操作系统用这些信息来决定把 CPU 分给谁。
-
进程状态:Running(运行)、Ready(就绪)、Blocked(阻塞)等。
-
优先级 (Priority):你是 VIP 还是普通用户?优先级高的先跑。
-
时间片 (Time Slice):你这次能跑多久?
-
调度队列指针:指向下一个 PCB 的指针(OS 通常把 PCB 串成链表来管理)。
D. 资源控制信息 (What do I own?)
记录进程占用的系统资源。
-
内存管理信息 :指向页表 (Page Table) 的指针。决定了该进程的虚拟地址如何映射到物理内存(即该进程的地盘在哪里)。
-
文件描述符表 (File Descriptors):你打开了哪些文件?使用了哪些 Socket?
-
信号处理句柄 :如果有信号(如
kill)发过来,该怎么处理?
3. PCB 如何工作:上下文切换 (Context Switch)
这是 PCB 发挥作用的高光时刻。
假设 CPU 正在运行进程 A,现在要切换到进程 B,OS 会做以下动作:
-
暂停 A:通过中断机制打断进程 A。
-
保存 A (Save) :把 CPU 当前所有寄存器的值(PC, SP 等)全部抄写到 进程 A 的 PCB 中。
-
调度:调度器决定运行进程 B。
-
恢复 B (Restore) :从 进程 B 的 PCB 中读取它上次保存的寄存器值,填回到 CPU 寄存器里。
-
运行 B:CPU 指针指向了 B 上次停下的地方,进程 B 继续运行。
代价 :你可以看到,读写 PCB 需要大量内存操作。这就是为什么进程切换(Context Switch)很"重",很消耗性能的原因。这也是为什么现代高并发编程(如 Go 协程)倾向于在用户态搞轻量级调度的原因------为了绕过笨重的 PCB 切换。
4. Linux 中的 PCB:task_struct
在 Linux 内核源码中,PCB 对应的具体结构体名叫 task_struct。
-
定义位置 :
<include/linux/sched.h> -
大小:这是一个非常复杂的结构体,包含数百个字段。
-
查看方式 :虽然你不能直接看内存,但 Linux 的
/proc文件系统把部分 PCB 信息映射成了文件。-
比如查看 PID 为 1234 的状态:
cat /proc/1234/status -
查看它的内存映射:
cat /proc/1234/maps
-