进程属性深入了解(上篇):核心标识、状态与内存属性
进程是操作系统进行资源分配和独立调度的基本单位,其所有运行状态、资源信息都被内核记录在专属的数据结构中。上篇我们从标识身份、生命周期状态、内存空间三个核心维度,结合图示拆解进程的基础属性。
一、进程的 “身份档案”:标识类属性
操作系统通过一组唯一的标识来管理每一个进程,这些标识全部存储在 ** 进程控制块(PCB,Process Control Block)** 中。PCB 是进程存在的唯一标志,相当于进程的 “档案袋”。
1. 进程控制块(PCB):所有属性的载体
PCB 是操作系统内核维护的结构体,每创建一个进程就会生成对应的 PCB,进程销毁时 PCB 也会被回收。它集中保存了进程运行所需的全部信息,大到内存权限,小到寄存器快照,都在 PCB 中有对应字段。
经典的 PCB 结构分为三大类信息:进程标识、处理器状态、进程控制信息,具体字段如下:
<div align="center"> <img src="..." ref="ref_4_0" alt="经典PCB结构示意图"> <p>图1:经典PCB结构字段示意图</p> </div>
以 Linux 系统为例,PCB 的具体实现是task_struct结构体,里面包含了上百个字段,完整描述了一个进程的所有属性。
2. 核心标识属性:PID、PPID、UID/GID
最基础的进程标识有四个,对应进程的 “身份信息”:
- PID(进程 ID):系统全局唯一的非负整数,是进程最核心的标识。比如 Linux 中 PID=1 的进程是
systemd,是所有用户进程的 “始祖”。 - PPID(父进程 ID):创建当前进程的进程的 PID。进程不能凭空产生,都是由父进程通过
fork创建,因此每个进程都有父进程,形成树形的进程关系。 - UID/GID(用户 ID / 组 ID):标识进程属于哪个用户、哪个用户组,决定了进程的文件访问权限。普通用户创建的进程默认继承当前用户的 UID。
举例理解: 在 Linux 终端执行ps -l,可以看到当前 shell 进程的 PID、PPID、UID 等信息:
plaintext
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 0 S 1000 2345 2301 0 80 0 - 5623 wait pts/0 00:00:00 bash这里 UID=1000 是当前普通用户,PID=2345 是 bash 进程自身,PPID=2301 是启动它的终端进程。
二、进程的 “生命周期”:状态属性
进程从创建到销毁的整个生命周期中,会因为资源等待、调度切换等原因,在不同状态之间动态切换。状态属性描述了进程当前 “正在做什么”、“能不能运行”。
1. 经典五态模型
操作系统理论中最基础的是三态模型(就绪、运行、阻塞),扩展后形成五态模型,完整覆盖进程的全生命周期:
<div align="center"> <img src="..." ref="ref_8_0" alt="进程五态转换图"> <p>图2:进程五态模型与转换逻辑</p> </div>
五个状态的含义:
- 新建态:进程正在被创建,PCB 已生成但资源未分配完成,还没进入就绪队列。
- 就绪态:进程已经具备所有运行条件,只等 CPU 调度。系统中所有就绪进程会排成 “就绪队列”。
- 运行态:进程正在 CPU 上执行指令。单核 CPU 同一时刻只有一个进程处于运行态。
- 阻塞态(等待态):进程因为等待某个事件(比如读磁盘、等网络数据)而暂停运行,即使 CPU 空闲也无法执行。
- 终止态:进程运行结束或异常退出,等待系统回收资源。
2. Linux 系统的实际进程状态
在 Linux 中,进程状态在五态基础上做了更细的划分,用单个字母标识:
- R(Running/Runnable):运行或就绪态,对应理论中的运行 + 就绪,都在 CPU 的运行队列里。
- S(Sleeping):可中断睡眠,对应阻塞态,等待事件时可以被信号唤醒。比如执行
sleep 10的进程就是 S 态。 - D(Disk Sleep):不可中断睡眠,一般是在等待硬件 IO 完成,不能被信号打断。比如进程正在读写磁盘时可能进入 D 态。
- T(Stopped):停止态,进程被暂停(比如调试时按下 Ctrl+Z),收到恢复信号才会继续运行。
- Z(Zombie):僵尸态,进程已经结束,但父进程还没调用
wait回收它的 PCB 资源。
举例理解: 执行ps aux | grep sleep,可以看到睡眠中的进程状态为 S:
plaintext
user 3456 0.0 0.0 4356 720 pts/0 S+ 10:00 0:00 sleep 100三、进程的 “内存地盘”:虚拟地址空间属性
每个进程都拥有独立的虚拟地址空间,这是操作系统给进程的 “专属内存视野”。进程以为自己独占整个内存,实际上内核通过页表把虚拟地址映射到真实的物理内存。
1. 虚拟内存的分段布局
一个典型进程的虚拟地址空间从低地址到高地址,被划分为多个功能明确的段:
<div align="center"> <img src="..." ref="ref_17_0" alt="进程虚拟内存布局图"> <p>图3:Linux进程虚拟地址空间分段布局</p> </div>
各段的作用:
- 代码段(Text 段):存储程序的机器指令,只读不可修改,防止程序被意外篡改。
- 数据段(Data 段):存储已初始化的全局变量、静态变量,可读可写。
- BSS 段:存储未初始化的全局变量、静态变量,程序启动时会被自动初始化为 0。
- 堆(Heap):动态内存区域,由程序员手动申请和释放(比如 C 语言的
malloc/free),地址从低向高增长。 - 栈(Stack):存储函数调用的局部变量、参数、返回地址,由系统自动分配回收,地址从高向低增长。
- 内核空间:高地址的一部分是内核专属区域,所有进程共享,用于执行系统调用、内核代码。
2. 直观举例:变量存在哪里?
看一段简单的 C 代码,不同的变量对应不同的内存段:
c
运行
int a = 10; // 已初始化全局变量 → Data段 int b; // 未初始化全局变量 → BSS段 void main() { int c = 20; // 局部变量 → 栈 int *p = malloc(100); // malloc申请的内存 → 堆 static int d = 30; // 静态局部变量 → Data段 }通过/proc/[PID]/maps文件,可以直接查看一个进程真实的内存分段地址,验证上述布局。
下篇我们将继续讲解进程的调度属性、IO 资源属性、权限与家族属性,进一步深入理解进程的运行机制。
