Linxu进程概念
进程是 Linux 系统最核心的运行抽象,本文从零搭建 Linux 进程知识框架:先夯实冯诺依曼硬件约束与操作系统 “先描述、再组织” 的管理逻辑,再深入讲解进程本质、task_struct 结构体、fork 系统调用机制;逐一拆解 R/S/D/T/Z 等 7 种内核进程状态,厘清僵尸进程、孤儿进程的成因、危害与处理方式;同时讲解进程优先级、调度特性、环境变量用法,揭秘虚拟地址与物理地址映射原理,
一、底层:冯诺依曼体系与操作系统
1.冯诺依曼体系结构
我们日常使用的电脑、服务器,几乎都遵循冯诺依曼体系,核心规则只有一句话:所有设备只能直接和内存打交道。

核心组件五大部件:输入设备:键盘、鼠标、扫描仪等 。
-
输出设备:显示器、打印机等 。
-
存储器:指的就是内存 。
-
运算器 + 控制器:共同构成了 中央处理器(CPU)
至关重要的硬件规律(木桶原理体系):不考虑缓存情况下,CPU 能且只能对内存进行读写,绝对不能直接访问外设 ;外设的数据要输入或输出,也只能先写入内存或从内存读取 。
数据流向实例(以发送 QQ 消息为例):键盘输入信息(外设) -> 内存 -> CPU处理加密和打包 -> 内存 -> 网卡(外设输出)
2.操作系统(OS)
OS 的概念:包含内核(进程、内存、文件、驱动管理)及其他程序(如库函数、Shell 外壳)的集合 。
核心作用:
- 操作系统帮助用户管理好下面的软硬件资源
- 为用户提供一个良好(稳定、高效、安全)的运行环境

操作系统的管理逻辑:先描述(用结构体),再组织(用链表 / 数据结构),进程管理也遵循这个核心思路。
二、进程本质:程序的执行实例
1.什么是进程?
- 课本定义:程序的一个执行实例、正在运行的程序
- 内核视角:系统分配 CPU 、内存资源的基本实体
程序和进程核心区别:
- 程序:只是一堆死代码,作为文件静静地存储在磁盘中
- 进程:程序被加载到内存中,并由操作系统(OS)为其创建了对应的管理结构。 "进程 = 内核数据结构(PCB) + 该程序的代码和数据"
2.进程的灵魂:PCB(进程控制块)
操作系统管理进程,靠的是PCB(进程控制块),它是进程的属性集合。
PCB:进程信息被放在一个叫做进程控制块(Process Control Block) 的数据结构中,它是进程属性的集合
在 Linux 中,PCB 的具体实现是task_struct结构体,会被加载到内存中,包含进程所有关键信息:
- 标识符:PID(进程唯一 ID)、PPID(父进程 ID)
- 状态:运行、睡眠、停止、僵尸等
- 优先级:CPU 资源分配的先后顺序
- 程序计数器:下一条待执行指令的地址
- 内存指针:指向进程代码、数据的内存地址
- 上下文数据:CPU 寄存器中的进程执行现场
- I/O 状态、记账信息等
Linux 内核通过链表组织所有 task_struct,实现进程的统一管理。
当操作系统需要调度进程、杀掉进程胡哦这更改优先级时,本质上是对这个双向链表进行增、删、查、改的操作。
3. 进程的创建:fork 系统调用
fork是 Linux 创建子进程的核心系统调用,核心特性:
- 调用一次,返回两个值:子进程返回
0,父进程返回子进程 PID - 父子进程代码共享,数据私有(写时拷贝机制)
代码控制流(分流):fork() 之后通常需要使用 if - else 对其返回值进行分支处理
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
pid_t ret = fork();
if (ret < 0) {
perror("fork");
return 1;
} else if (ret == 0) {
// 子进程逻辑
printf("子进程 PID:%d\n", getpid());
} else {
// 父进程逻辑
printf("父进程 PID:%d,子进程 PID:%d\n", getpid(), ret);
}
return 0;
}
三、进程状态
1.在内核源码(task_state_arry)中定义了一下状态:
- R(运行):进程在运行队列中,不一定正在执行
- S(睡眠):可中断睡眠,等待事件完成,能被信号唤醒
- D(磁盘睡眠):不可中断睡眠,等待 I/O 结束,不能被信号杀死
- T(停止):收到 SIGSTOP 信号暂停,SIGCONT 可恢复
- Z(僵尸):进程退出但父进程未回收,残留 PCB
- t(追踪停止):被调试工具暂停
- X(死亡):瞬时状态,无法查看
2.僵尸进程(Z):系统的[内存垃圾]
- 产生原因:子进程退出,父进程未调用 wait () 读取退出状态,子进程 PCB 残留内存
- 危害:PCB 占用内存,大量僵尸进程会导致内存泄漏
- 避免方式:父进程主动回收子进程资源
3.孤儿进程
- 产生原因:父进程先退出,子进程后执行
- 处理机制:被1 号 init 进程领养,由 init 进程负责回收
4.查看进程的两种常用方式
- 文件系统级查看:通过系统自带的 /proc 动态目录。 例如,通过查看 /proc/进程的PID 文件夹,就能实时看到该进程的内存映像、可执行文件软链接等信息。
- 用户级工具查看:使用 ps aux 或 ps axj 命令。
四、进程优先级与调度基础
1.优先级核心概念
基本概念:CPU资源分配的先后顺序。优先级(数值小)的有优先执行权。
查看优先级指标 (ps -l)
- PRI(Priority):进程可悲执行的优先级,值越小优先级越高。
- NI(Nice):nice值,是PRI的修正数值。
计算公式:PRI(new) = PRI(old) + nice
- nice的取值范围是 -20 ~ 19 (共40个级别)。
- nice值为负,PRI变小,优先级变高;调整优先级在Linux下实质就是调整nice值
- nice值不是优先级,它只是影响PRI的修正数据
2.进程的特性
- 竞争性:进程多、CPU少,因此进程间具有竞争属性,需要依靠优先级更合理地竞争资源。
- 独立性:多进程运行期间互不干扰,独享资源。
- 并行(Parallelism):多个进程在多个CPU下,在同一时刻进行运行。
- 并发(Concurrency):多个进程在单个CPU下采用进程间切换(时间片轮转)的方式,在一段时间内让多个进程都得以推送。
五、环境配置:环境变量
基本概念:操作系统中用来指定运行环境的一些参数(通常具有全局特性,可被子进程继承)。
1.常见的环境变量
- PATH:指定命令的搜索路径。
- HOME:指定用户的主工作目录(即登录时的默认路径)。
- SHELL:当前使用的Shell终端壳程序(通常是 /bin/bash)。
2.操作指令
- echo $NAME:查看某个环境变量。
- export:设置一个新的环境变量(具备全局属性)。
- env:显示所有的环境变量。
3.代码获取环境变量
- 通过 main 函数的第三个参数
- 通过第三方全局全局变量:extern char** environ。
- 通过系统/库调用函数:getenv("ENV_NAME") (最常用)
// getenv获取指定环境变量
#include <stdio.h>
#include <stdlib.h>
int main() {
printf("PATH:%s\n", getenv("PATH"));
return 0;
}
六、进程地址空间
语言层面(C/C++)我们能看到的地址,全部绝对不是物理地址,而是虚拟地址(线性地址) !物理地址由 OS 统一管理,对用户不可见 。
1.Linux虚拟地址经典演练代码
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
// 定义全局变量
int g_val = 10;
int main()
{
pid_t id = fork(); // 创建子进程
if (id < 0)
{
perror("fork failed");
return 1;
}
// 子进程
else if (id == 0)
{
// 子进程修改全局变量
g_val = 100;
printf("子进程: g_val = %d, &g_val = %p\n", g_val, &g_val);
}
// 父进程
else
{
// 父进程休眠1秒,保证子进程先执行修改
sleep(1);
printf("父进程: g_val = %d, &g_val = %p\n", g_val, &g_val);
}
return 0;
}
运行结果: 父子进程输出的 g_val 变量地址完全一样,但是打印出的内容却不同
- 子进程: g_val = 100, &g_val = 0x404048
- 父进程: g_val = 10, &g_val = 0x404048
2.进程地址空间模型
-
每一个进程在被创建时,OS 都会为其分配一个虚拟的“进程地址空间”(在 32 位平台下大小为 4GB)。
-
映射机制:OS 必须负责将虚拟地址通过页表(Page Table)转化映射成实际的物理地址 。
-
对现象的解释:父子进程修改变量时发生了写时拷贝。它们的虚拟地址相同,但经页表映射后,被重新分配到了不同的物理地址上,从而实现了数据的解耦与独立性 。
Linux 2.6 内核 O(1) 调度算法架构
Linux 2.6 内核引入的 O (1) 调度算法,是经典的进程调度方案,核心亮点:无论系统有多少进程,调度选程的时间复杂度恒为常数 O (1),不会因进程变多而变慢。
- 双队列分工每个 CPU 对应一个运行队列
runqueue,内部有两个优先级数组:
- 活跃队列(active):存放时间片未用完的就绪进程
- 过期队列(expired):存放时间片耗尽的进程
-
优先级分组共 140 个优先级队列(0–99 实时进程,100–139 普通进程),同优先级进程按 FIFO 排队。
-
位图快速查找用
bitmap标记哪些优先级队列非空,不用遍历全队列,一步找到最高优先级进程,这是 O (1) 的关键。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)