linux入门(5)
进程和程序
一、进程和程序:静态文件与动态执行的核心区别
程序和进程是操作系统中最基础的两个概念,二者的核心差异在于 “静态存在” 与 “动态执行”,具体对比和延伸如下:
1. 核心定义与本质
对比维度 | 程序(Program) | 进程(Process) |
---|---|---|
存在形式 | 静态文件(如 .exe 、Linux 下的可执行文件) | 动态执行过程(程序加载到内存后的运行实例) |
核心载体 | 存储介质(硬盘、U 盘等) | 计算机内存(运行时占用的内存空间) |
状态属性 | 永久存在(不删除则一直保留) | 临时存在(从创建到消亡,生命周期有限) |
资源占用 | 仅占用存储资源(大小固定) | 占用系统资源(CPU、内存、I/O 设备、环境变量等) |
2. 经典类比:帮你快速理解
- 程序 = 剧本:剧本是写在纸上的静态文本,包含角色、台词、流程,不依赖 “舞台” 就能存在,且一份剧本可以复印多份。
- 进程 = 话剧演出:演出是剧本的 “动态执行”,需要舞台(内存)、演员(CPU)、灯光(I/O 设备)、道具(数据),且同一份剧本可以在多个剧场同时演出(同一个程序可创建多个进程)。
例如:你双击打开 2 个 “记事本”,此时 “记事本.exe” 是程序(1 个),而运行的 2 个记事本窗口就是进程(2 个),二者独立运行(关闭一个不影响另一个)。
3. Linux 系统中的进程特性
在 Linux 中,进程是操作系统 “干活” 的基本单元,每个进程都有以下独立属性,确保彼此不干扰:
- 独立的处理环境:包括当前用户(如
root
或普通用户)、运行目录(如/home/user
)、环境变量(如PATH
、HOME
)。 - 独立的系统资源:操作系统会为每个进程分配唯一的
PID
(进程 ID),并管理其 CPU 时间片、内存地址空间、打开的文件描述符等。
- 程序与进程:“文件” 和 “运行实例” 的区别
- 程序是静态的可执行文件(存放在硬盘),像 “未演出的剧本”,只占存储空间,不消耗内存和 CPU,也没有生命周期。
- 进程是程序的动态运行实例(加载到内存),像 “正在演出的话剧”,必须占用内存(存储代码、数据、状态),运行时消耗 CPU(执行指令),有明确的创建、运行、消亡过程。同一个程序可以启动多个进程(如同时打开 3 个浏览器窗口),这些进程独立占用资源,互不干扰。
单道、多道程序设计
单道程序设计
所有进程一个一个排队执行。若A阻塞,B只能等待,即使CPU处于空闲状态。而在人机交互时阻塞的出现是必然的。所有这种模型在系统资源利用上及其不合理,在计算机发展历史上存在不久,大部分便被淘汰了。
多道程序设计
在计算机内存中同时存放几道相互独立的程序,它们在管理程序控制之下,相互穿插的运行。多道程序设计必须有硬件基础作为保证。
在计算机中时钟中断即为多道程序设计模型的理论基础。并发时,任意进程在执行期间都不希望放弃cpu。因此系统需要一种强制让进程让出cpu资源的手段。时钟中断有硬件基础作为保障,对进程而言不可抗拒。 操作系统中的中断处理函数,来负责调度程序执行。
在多道程序设计模型中,多个进程轮流使用CPU (分时复用CPU资源)。而当下常见CPU为纳秒级,1秒可以执行大约10亿条指令。由于人眼的反应速度是毫秒级,所以看似同时在运行。
1s = 1000ms
1ms = 1000us
1us = 1000ns
1s = 1000000000ns
多道程序设计就像 “超市收银台”:
- 每个顾客(进程)有一个 “最长等待时间”(时间片),比如 5 分钟;
- 5 分钟到了还没处理完(比如物品太多),就先让下一个顾客(进程)上前(切换),这个顾客排到队尾等待下一次轮到自己;
- 如果某个顾客突然需要打电话问价格(I/O 操作),会主动走到一边等待(阻塞),让后面的顾客先处理。
这样既保证了所有顾客(进程)都能被处理,又不会让收银台(CPU)闲着,效率远高于 “一个顾客处理完再换下一个”(单道)。
并行和并发
一、并行(Parallelism):物理上的 “真同时”
- 核心定义:同一时刻,多个任务在多个硬件执行单元上(多核 CPU、多处理器)真正同时执行。
- 图解对应:第一张图中,进程 A、B、C 的黑色执行块是连续且同时推进的 —— 因为有多个物理 “执行核心”(如多核 CPU,每个核心独立处理一个任务),所以它们能在同一时间点各自执行指令。
- 例子类比:“两个队列同时使用两台咖啡机”。每个队列的人都能在同一时刻用自己的咖啡机做咖啡,真正 “并行不干扰”,依赖多硬件资源(多台咖啡机)。
二、并发(Concurrency):逻辑上的 “假同时”
- 核心定义:同一时刻只有一个任务在执行,但通过快速切换时间片,让多个任务宏观上看起来同时执行,微观上是 “交替串行” 的。
- 图解对应:第二张图中,进程 A、B、C 的黑色执行块是交替出现的 —— 因为只有一个 CPU,所以 CPU 会给每个任务分配 “时间片”(比如 10ms),时间片到了就切换下一个任务。虽然微观上每个时刻只有一个任务在跑,但切换速度极快(远超人类感知),宏观上感觉 “多个任务同时在推进”。
- 例子类比:“两个队列交替使用一台咖啡机”。咖啡机先给队列 1 做一点,再切换给队列 2 做一点,快速轮换。虽然咖啡机同一时刻只服务一个队列,但因为切换快,两个队列的人会觉得 “咖啡机同时在为我们服务”,不依赖多硬件,靠调度策略实现 “宏观同时”。
三、关键区别总结
对比维度 | 并行(Parallel) | 并发(Concurrency) |
---|---|---|
硬件依赖 | 必须有多核 CPU、多处理器等多硬件执行单元 | 可在单核 CPU上实现(靠时间分片调度) |
执行本质 | 同一时刻,多个任务物理上真正同时执行 | 同一时刻,只有一个任务执行;宏观 “同时”,微观 “交替串行” |
典型场景 | 大数据计算、图形渲染等 “算力密集型” 任务 | 多用户请求、I/O 密集型任务(如网页服务器同时响应多个请求) |
简单说:
- 并行是 “真・同时做多个事”,靠多硬件实现;
- 并发是 “装・同时做多个事”,靠调度策略在单硬件上模拟 “同时”,核心是提高资源利用率(避免单个任务阻塞导致整体停滞)。
MMU
MMU(内存管理单元,Memory Management Unit)是 CPU 内部的关键硬件组件,是多用户、多进程操作系统实现内存虚拟化与安全隔离的核心基础,主要作用可拆解为以下两点:
1. 虚拟地址 → 物理地址的 “翻译官”
进程运行时,CPU 生成的是虚拟地址(进程 “误以为自己独占” 的地址空间,无需关心物理内存的实际布局)。MMU 会将这些虚拟地址映射转换为物理地址(主存「如内存条」的真实地址),让主存能准确定位并读写数据。
举个例子:
- 进程 A 的虚拟地址
0x1000
,可能被 MMU 映射为物理地址0x8000
; - 进程 B 的虚拟地址
0x1000
,可能被映射为物理地址0x9000
。这样,不同进程的 “相同虚拟地址” 会指向物理内存的不同区域,既让进程获得 “连续独立” 的地址空间,又实现了多进程对物理内存的共享。
2. 内存访问的 “安检员”
MMU 通过硬件机制,对内存访问进行权限控制与隔离保护:
- 可以限制访问权限(如某段内存仅允许 “读”、禁止 “写”);
- 可以拦截非法访问(如进程试图访问不属于自己的内存区域时,MMU 会触发异常,防止破坏其他进程或系统内存)。
结合图示的流程
CPU 生成虚拟地址 → MMU 将虚拟地址翻译为物理地址 → 主存根据物理地址读写数据 → 数据返回给 CPU。
这种设计让进程无需感知物理内存细节,同时操作系统能灵活管理内存(如内存交换、共享),还能保障多进程间的隔离性与安全性。
进程控制块PCB
进程控制块(PCB,Process Control Block)是操作系统管理进程的核心数据结构,每个进程运行时,内核都会为其分配一个 PCB,用于记录进程的所有关键信息,支撑进程的创建、调度、资源管理等全生命周期操作。在 Linux 内核中,PCB 由 task_struct
结构体实现(定义在 linux/sched.h
中)。
一、task_struct
的核心作用
可以把 task_struct
理解为进程的 **“全景档案”**:操作系统通过它掌握每个进程的 “身份、状态、资源、调度策略” 等细节,从而实现对多进程的高效管理(比如决定哪个进程先运行、给进程分配内存 / 文件资源、处理进程切换等)。
二、task_struct
关键成员的意义(结合需求与代码)
从功能维度,task_struct
包含以下核心类别信息:
1. 进程标识与状态
- 进程 ID(隐含,实际由内核维护唯一
pid
):系统中每个进程的唯一编号,类似 “身份证号”,用于区分不同进程。 state
字段:表示进程状态(如代码中volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
),常见状态有:- 可运行(
runnable
):进程就绪,等待 CPU 调度; - 运行中(
running
):进程正在 CPU 上执行; - 阻塞 / 不可运行(
unrunnable
):进程因等待资源(如 I/O、锁)暂时无法执行; - 停止(
stopped
):进程被信号暂停(如调试时的暂停)。
- 可运行(
2. 调度与优先级
- 优先级相关字段:
prio
(动态优先级)、static_prio
(静态优先级)、rt_priority
(实时进程优先级)等,决定进程被 CPU 调度的 “优先程度”。 - 调度类与实体:
sched_class
(调度类,如普通进程、实时进程的调度策略)、sched_entity
(调度实体,封装调度所需的队列、运行时间等信息),是 Linux 进程调度器的核心关联结构。
3. 内存与栈
stack
指针:指向进程的内核栈(每个进程在 kernel 态运行时,有独立的栈空间)