进程基础与组成
01 认识进程
1.1 进程是什么
原文定义
进程是操作系统中一次程序执行过程,是操作系统分配资源、CPU 调度的最小单位。 运行程序时,内核会创建task_struct结构体(PCB 进程控制块),这个结构体就是进程本身;进程所有信息(状态、PID、内存地址等)全部存在这里。 程序运行结束,内核回收task_struct;程序异常崩溃,内核清理资源(防止僵尸进程)。
通俗大白话
打开微信、浏览器,每一个正在运行的软件窗口,就是进程。 操作系统给每个运行的程序单独建一张 “档案卡”task_struct,卡上记录这个程序占用多少内存、CPU、现在是运行还是休眠、编号 PID 等。程序关掉,档案销毁;程序崩溃,系统自动清理档案。
运行调度原理(原文)
CPU 采用时间片轮转调度:每个进程分配固定时间片(示例 5ms),轮流占用 CPU;用完时间片就进入就绪队列排队等待下一次调度。 示例:QQ、WPS、钉钉多个进程排队,轮流使用 CPU。
进程四大核心特征(原文 + 通俗解释)
- 动态性 原文:动态产生、动态消亡。 通俗:双击软件才会出现进程,关闭软件进程消失,不是永久存在。
- 并发性 原文:多个进程可以同时一起执行。 通俗:一边放视频、一边聊微信、一边下载文件,看似同时运行。
- 独立性 原文:独立运行、独立分配资源,有专属内存空间。 通俗:微信崩溃不会让浏览器一起闪退,两者内存互不干扰。
- 异步性 原文:进程执行速度不可预测,互相不受控。 通俗:CPU 先跑微信还是先跑浏览器,由系统调度决定,顺序不固定。
02 程序与进程的关系
2.1 程序 vs 进程对比表
表格
| 对比维度 | 程序 (Program) | 进程 (Process) |
|---|---|---|
| 定义 | 磁盘上静态文件(指令集合,如 exe),不运行不占资源 | 程序动态运行实例,占用 CPU、内存、网卡等资源,有完整生命周期 |
| 存在形式 | 静态文件,永久保存在硬盘 | 动态,启动创建、结束销毁,内存临时存在 |
| 资源占用 | 只占硬盘存储空间,不占用运行内存、CPU | 占用物理内存、CPU、文件句柄、端口,独立虚拟地址空间 |
| 状态 | 无状态,只是一堆代码 | 拥有多种运行状态(运行、休眠、暂停等),状态随时变化 |
| 独立性 | 文件之间互不影响 | 进程内存完全隔离,默认不能互相读写数据,必须靠特殊通信方式 |
| 举例 | 硬盘里的 chrome.exe 文件 | 双击浏览器后打开的窗口(1 个程序可以生成 N 个进程) |
通俗总结
程序 = 硬盘里的安装包 / 软件文件(静态、死的); 进程 = 打开软件后正在跑的程序(动态、活的)。
2.2 程序与进程关联(原文 + 补充)
- 进程是程序的运行实例,一个程序可以对应多个进程 例:同时打开 3 个浏览器窗口,chrome 程序只有一份,但有 3 个独立进程。
- 程序是模板,进程是运行表现。
- 同程序多进程:只读代码段共享(节省内存),数据段、栈、堆完全独立。 补充:代码不会被修改,所有进程共用一份代码;但变量、数据每个进程单独一份。
- 关闭进程,磁盘上的程序文件还在;删除程序文件,已经打开的进程能继续运行,直到需要读取文件时报错。
03 进程组成与运行属性
3.1 进程三大组成部分
1)PCB 进程控制块(task_struct)
原文:操作系统管理进程的核心结构体,每个进程唯一,记录 PID、状态、内存、资源占用,是系统识别进程的唯一依据。 通俗:每个进程的身份证 + 档案,内核靠它管理所有进程。
2)文本段(代码段)
原文:存放程序编译后的机器指令,只读;同一程序多进程共享,运行期间大小固定。 通俗:软件的代码指令,禁止修改,多个浏览器共用同一份代码,省内存。
3)数据段(细分 4 块)
存放运行时数据,分为:
- 初始化全局数据段(定义并赋值的全局变量)
- 未初始化数据段(只声明不赋值的全局变量)
- 堆:程序动态申请的内存(malloc/new)
- 栈:局部变量、函数调用信息
3.2 虚拟内存与物理内存映射(原文补充)
Linux 3G/1G 划分:
- 用户空间:0~3G,普通程序访问
- 内核空间:3G~4G,操作系统内核使用 MMU 内存管理单元:负责把程序看到的虚拟地址,翻译成真实物理内存地址;内存不够时会把数据放到 swap 交换分区(硬盘)。
通俗:程序看到的内存是 “假地址”,MMU 做翻译,内存不够就临时存硬盘。
3.3 Linux 三种进程类型
1. 交互进程
定义:终端手动启动,和用户交互;依赖终端,前台运行。 例子:ls、vim、./a.out,关掉终端进程直接退出。
2. 批处理进程
定义:无需人工交互,后台批量执行,优先级低;脚本、定时任务。 例子:批量日志清理、数据备份脚本。
3. 守护进程(daemon)
定义:开机自启后台服务,完全脱离终端;开机启动、关机结束,持续提供服务。 例子:nginx、mysql、ssh、systemd。
3.4 Linux 六大基础进程状态(原文 + 通俗讲解)
- R 运行 / 就绪态 正在 CPU 上运行,或排队等待 CPU 调度;
ps里看到 R。 - S 可中断睡眠 进程等待事件(等待输入、等待网络、等待子进程);可以被信号唤醒(kill、Ctrl+C 能打断)。
- D 不可中断睡眠(磁盘休眠) 等待硬件 IO(读写硬盘),任何信号都杀不死,包括
kill -9;防止写入磁盘中途中断导致数据损坏。 补充:长期 D 状态代表磁盘故障 / IO 卡死。 - T 停止态 手动暂停,
Ctrl+Z、kill -19暂停;可用kill -18恢复运行。 - Z 僵尸态 程序已经执行完毕退出,但父进程没有调用
wait()回收它的退出信息;PCB 残留,占用 PID 资源。 通俗:员工离职没办手续,档案还留在公司。 - X 死亡态 进程资源全部回收,生命周期彻底结束;瞬间状态,几乎看不到。
3.5 进程附加状态标记(ps 输出里的后缀符号)
+:前台进程,绑定当前终端l:多线程进程N:低优先级(NI>0)<:高优先级(NI<0)s:会话首进程(管理一组终端进程)L:持有文件 / 资源锁
3.6 进程状态流转简图文字版
fork()创建进程 → R 运行态- 时间片用完 → 进入就绪队列等待调度
- 调用
sleep()/ 等待 IO → S 可中断睡眠;等待磁盘硬件 → D 不可中断睡眠 SIGSTOP信号 → T 停止态;SIGCONT恢复为 R- 进程执行
exit()退出 → Z 僵尸态 - 父进程调用
wait()回收 → X 死亡态
3.7 PID 进程 ID
基础规则
PID 是进程唯一数字编号,从 1 开始;系统最大进程数由kernel.pid_max控制(默认 131072)。 所有进程信息存在/proc/[PID]文件夹,/proc/1/status查看 1 号进程详情。
三大特殊 PID
- PID 0:idle 空闲进程 系统最底层进程,CPU 无事可做时运行,负责调度空闲 CPU。
- PID 1:systemd/init 初始化进程 0 号进程创建;系统初始化、启动所有服务;自动回收所有孤儿进程。
- PID 2:kthreadd 内核线程管理进程 负责创建、管理所有内核后台任务(磁盘回收、内存清理等)。
04 进程相关命令大全(原文 + 补充用法)
4.1 ps 查看进程快照(静态,只打印一次)
表格
| 命令 | 参数作用 | 使用场景 |
|---|---|---|
ps -ef |
-e 所有进程,-f 完整格式 | 查看系统全部进程、父子 PID 关系 |
ps -aux |
-a 所有终端进程,-u 用户资源,-x 无终端守护进程 | 看 CPU、内存占用百分比 |
ps -eo pid,ppid,comm |
-e 全部进程,-o 自定义输出字段 | 只看需要的信息,精简输出 |
ps -l |
-l 长格式 | 查看当前终端进程优先级、NI、状态 |
4.2 top 实时进程监控(动态刷新)
功能:每秒刷新 CPU、内存、进程负载;可按 M 内存 / P CPU 排序; 常用操作:q退出,M按内存排序,P按 CPU 排序。
4.3 htop(top 升级版)
优势:彩色界面、支持鼠标、直观进程树、操作快捷键更简单; 补充:Ubuntu 默认不预装,安装命令:
bash
运行
sudo apt update
sudo apt install htop
4.4 kill 发送信号控制进程(按 PID 操作)
表格
| 命令 | 信号编号 | 作用 |
|---|---|---|
kill -l |
无 | 列出全部系统信号 |
kill -2 PID |
SIGINT | 等同于 Ctrl+C,正常中断程序 |
kill PID |
SIGTERM(15) | 优雅终止,允许程序保存数据、释放资源(推荐优先用) |
kill -9 PID |
SIGKILL | 强制杀死,无法拦截,丢失缓存数据(万不得已使用) |
kill -19 PID |
SIGSTOP | 暂停进程运行 |
kill -18 PID |
SIGCONT | 恢复暂停的进程 |
4.5 killall 按进程名批量杀进程
killall 进程名:正常关闭所有同名进程killall -9 进程名:强制全部杀死killall -i 进程名:逐个询问确认再关闭
4.6 pstree 进程树
以树形展示父子进程关系,直观看到谁创建了谁; 示例:pstree、pstree -p(同时显示 PID)。
4.7 bg /fg 前后台任务管理
jobs
查看当前终端所有后台暂停任务(任务编号)
bg 任务号
把暂停的进程放到后台继续运行,不占用终端输入
fg 任务号
后台进程切回前台,占据终端,可交互
4.8 nice /renice 调整进程优先级
优先级范围:-20 ~ 19
- -20:最高优先级,优先分配 CPU(仅 root 可设置)
- 0:默认优先级
- 19:最低优先级
nice -n 数值 命令:启动程序时直接设置优先级 例:nice -n 10 ./testrenice 数值 -p PID:修改正在运行进程的优先级 例:renice -5 -p 1234(root 提升优先级)
05 进程的创建 fork ()
5.1 fork 函数基础信息
头文件
c
运行
#include <sys/types.h>
#include <unistd.h>
函数原型
c
运行
pid_t fork(void);
功能
复制当前父进程,生成全新独立子进程。
返回值(重点,必背)
- 返回 >0(数字):当前是父进程,返回值 = 子进程 PID
- 返回 0:当前是子进程
- 返回 -1:创建失败(内存不足、进程数达到上限)
核心特性:调用一次,返回两次
执行一行fork(),代码分成两条执行流,父子进程同时往下跑。
5.2 父子进程内存机制:写时拷贝 COW(原文深度通俗讲解)
原文描述
fork 创建子进程不会立刻完整复制内存;只读时父子共享同一块物理内存;任意一方修改数据,才会单独拷贝一份内存给修改进程。
通俗类比
父子共用一本笔记(物理内存),只翻看不修改,共用同一本; 只要其中一方要写字修改,系统单独复印一页给他,两人各自一份,互不影响。
完整流程
- fork 后,父子页表指向同一物理内存,全部标记只读;
- 读变量:直接共享,不复制;
- 写变量(修改全局 / 局部变量):触发缺页异常;
- 内核分配新内存页,复制原有数据给写操作进程;
- 后续修改只作用在自己的独立内存,互不干扰。
补充拓展
代码段(只读指令)永久共享;数据、栈、堆只有修改才拷贝,大幅节省内存、加快 fork 创建速度。
5.3 课后练习:多进程分块拷贝文件
需求
- fork 创建父子进程协同复制文件
- 父进程拷贝文件前半段,子进程拷贝后半段,无重叠、无缺失
实现思路补充(原文拓展)
- 先用
stat()获取源文件总大小,计算分割中点; - 父子进程分别打开文件(共享文件指针会导致读写错乱);
lseek()精准移动读写偏移,父从 0 开始,子从中间位置开始;- 读写循环判断剩余字节,避免多读超出文件长度;
- 父进程使用
wait()等待子进程拷贝完成,防止产生僵尸进程。
全文补充拓展知识点(原文缺失高频考点)
1. 僵尸进程产生与解决
产生原因:子进程 exit 退出,父进程未调用wait/waitpid回收。 危害:长期堆积占用 PID 资源,系统无法新建进程。 解决:父进程结尾加wait(NULL);等待子进程回收。
2. 孤儿进程
父进程提前退出,子进程失去父进程,会被 PID=1 的 systemd 收养,自动回收,不会变成僵尸。
3. fork+exec 配套使用
fork 只复制进程,exec 系列函数可以替换子进程为全新程序(shell 执行命令底层原理:bash fork 子进程,子进程 exec 加载 ls/rm 等程序)。
4. 进程与线程区别
进程有独立内存,隔离性强、开销大;线程共享同一进程内存,开销小、无隔离。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)