一、一切的基础:冯・诺依曼体系结构

1. 五大硬件模块
- 输入设备:键盘、鼠标、扫描仪等
- 输出设备:显示器、打印机
- 存储器(特指内存):数据中转核心
- 运算器:负责算术、逻辑运算
- 控制器:协调硬件执行指令 运算器 + 控制器合并为 CPU。
2. 核心铁律
- CPU只能直接读写内存,无法直接和外设交互;
- 外设输入 / 输出数据,必须经过内存中转;
- 所有硬件交互,内存是唯一中间介质。
3. 存储分层金字塔

速度从快到慢、容量从小到大、成本从高到低: 寄存器 > L1/L2/L3 高速缓存 > 内存 (主存) > 本地磁盘 > 远程网络存储
4. 真实场景:QQ 聊天数据流(理解硬件数据流)
场景 1:发送文字消息
- 键盘输入文字(输入设备)→ 数据写入内存;
- QQ 进程从内存读取文字数据,CPU 完成编码;
- 数据放回内存,网卡驱动从内存读取数据,通过网卡发送到服务器;
- 好友电脑网卡接收数据,写入对方内存;
- 对方 QQ 进程读取内存数据,CPU 解析文字,输出到显示器。
场景 2:发送文件
- 文件存放在本地磁盘,磁盘先把文件数据加载到内存;
- CPU 读取内存文件数据编码,放回内存;
- 网卡读取内存数据上传服务器;
- 好友设备网卡接收数据写入内存,再持久化写入本地磁盘。
二、操作系统:计算机的 "大管家"
1. OS 定义
- 狭义 OS(内核 Kernel):核心四大模块 ------ 进程管理、内存管理、文件管理、驱动管理;
- 广义 OS:内核 + shell 外壳、C 标准库、系统工具等应用软件。

2. OS 两大核心职责
- 向下管理硬件:封装硬件操作,统一调度所有软硬件资源;
- 向上服务应用:给程序提供稳定、安全的运行环境。

3. OS 管理万物的核心思想:先描述,再组织
类比学校管理:校长(OS)不直接管学生(硬件 / 进程),
- 描述:用结构体记录学生信息(struct 保存进程 / 硬件属性);
- 组织:用链表、红黑树把结构体串起来,统一管理。
4. 系统调用 & 库函数
- 系统调用:内核对外暴露的底层接口,直接操作内核,功能原始、使用复杂;
- 库函数:第三方 / 标准库对系统调用的封装(如 printf 封装 write),降低开发成本; 用户程序 → 库函数 → 系统调用 → 内核 → 硬件。
三、进程:操作系统调度的最小单位
3.1 进程到底是什么?
- 课本定义:程序的一次运行实例;
- 内核定义:承担 CPU、内存、IO 资源分配的实体;
- 最简公式:
进程 = PCB(task_struct) + 代码 + 数据程序是磁盘上静态文件,进程是内存中动态运行的实体。
3.2 PCB:进程控制块(Linux 下 task_struct)
内核用task_struct结构体描述每一个进程,全部存放在内存,通过双向链表串联所有进程。

task_struct 核心成员
- 标识符:PID (进程 ID)、PPID (父进程 ID);
- 进程状态、退出码、信号;
- 优先级 nice 值;
- 程序计数器:下一条要执行的指令地址;
- 内存指针:代码段、堆、栈、共享内存映射;
- 上下文数据:CPU 寄存器临时数据,进程切换时保存恢复;
- IO 信息:打开的文件、占用外设;
- 记账信息:CPU 占用时长、调度统计。
3.3 查看进程的两种方式
- 文件系统:
/proc虚拟文件系统,/proc/PID存放对应进程全部信息; - 命令工具:
ps aux、ps axj、top。
3.4 获取进程 ID(系统调用)
cpp
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("当前进程PID:%d\n", getpid());
printf("父进程PPID:%d\n", getppid());
return 0;
}
3.5 fork () 创建子进程
核心特性
- 一次调用,两个返回值:父进程返回子进程 PID,子进程返回 0,失败返回 - 1;
- 父子代码共享,数据采用写时拷贝:只读时共享物理内存,修改数据时复制独立内存;
- fork 后父子进程同时执行后续代码,配合 if 分流实现不同逻辑。
基础 demo
cpp
#include <stdio.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);
}
sleep(10);
return 0;
}
四、Linux 七大进程状态
内核源码定义 7 种任务状态:
| 标识 | 状态名 | 特点 |
|---|---|---|
| R | 运行态 | 正在 CPU 运行 / 在就绪队列等待调度 |
| S | 可中断睡眠 | 等待事件,可被信号唤醒 |
| D | 不可中断睡眠 | 等待 IO 完成,无法被信号打断 |
| T | 停止态 | SIGSTOP 暂停,SIGCONT 恢复 |
| t | 追踪停止 | gdb 调试时暂停 |
| X | 死亡态 | 进程彻底销毁,瞬时状态不可见 |
| Z | 僵尸态 | 子进程退出,父进程未回收 PCB |
4.1 僵尸进程 Z
- 产生原因:子进程先退出,父进程没有调用 wait/waitpid 读取子进程退出信息;
- 危害 :task_struct(PCB)持续占用内存,造成内存泄漏,大量僵尸耗尽系统进程资源;
- 解决方案:父进程回收子进程、父进程退出变成孤儿进程由 systemd 回收。
4.2 孤儿进程
- 产生原因:父进程提前退出,子进程失去父进程;
- 处理机制:孤儿进程会被 1 号进程 systemd (init) 领养,systemd 自动回收,无内存泄漏风险。
五、进程优先级:CPU 资源分配规则
- PRI:进程优先级,数值越小,越优先调度;
- NI (nice 值) :优先级修正值,范围
-20 ~ 19; 计算公式:新PRI = 原始PRI + nice
- nice 为负数:优先级升高;正数:优先级降低。
操作命令
top:按 r 修改运行中进程 nice 值;nice:启动程序时指定优先级;renice:修改正在运行进程的 nice; 系统调用:getpriority()/setpriority()
四个基础概念
- 竞争性:进程多、CPU 少,互相抢占资源,因此需要优先级;
- 独立性:多进程拥有独立资源,互不干扰;
- 并发:单 CPU 快速切换进程,一段时间内同时推进多个任务;
- 并行:多 CPU 核心,同时运行多个进程。
六、进程切换 & Linux2.6 O (1) 调度算法
6.1 上下文切换
CPU 寄存器只有一套,切换进程时:
- 保存当前进程寄存器数据(上下文)到 PCB;
- 加载下一个进程的上下文到寄存器;
- 恢复执行新进程。 每个进程切换都会消耗 CPU 资源,频繁切换会降低性能。

6.2 O (1) 调度(Linux2.6 经典调度器)
每个 CPU 独立一个 runqueue 运行队列,包含两个数组:
- active 活跃队列:时间片未耗尽的进程,140 个优先级队列(0-99 实时,100-139 普通);
- expired 过期队列:时间片耗尽的进程。
高效设计
- bitmap 位图标记队列是否为空,快速找到最高优先级进程,查找耗时固定;
- 活跃队列全部耗尽时,直接交换 active 和 expired 指针,无需迁移进程;
- 无论系统有多少进程,调度耗时恒定,称为 O (1) 调度。

七、环境变量:进程运行环境配置
7.1 常见环境变量
PATH:命令搜索路径,执行程序时自动查找;HOME:用户家目录;SHELL:当前使用终端。
7.2 操作指令
echo $XXX:查看环境变量;export:导出全局环境变量(可被子进程继承);env:查看全部环境变量;unset:删除环境变量;set:查看本地变量 + 环境变量。
7.3 代码获取环境变量
- main 函数第三个参数
char *env[]; - 全局外部变量
extern char **environ; - 系统调用
getenv("PATH")获取指定变量。
八、进程虚拟地址空间
8.1 32 位 Linux 进程内存布局(低地址→高地址)

- 代码段 (.text):只读,存放程序指令;
- 初始化数据段 (.data):已初始化全局 / 静态变量;
- 未初始化段 (.bss):未初始化全局变量,程序启动清零;
- 堆 (heap):malloc 动态分配,自低向高增长;
- 共享区:动态库、mmap 文件映射;
- 栈 (stack):局部变量、函数栈帧,自高向低增长;
- 命令行参数、环境变量;
- 内核空间(32 位占 1G,用户不可访问)。
8.2 虚拟地址 vs 物理地址
fork 后父子进程打印同一个全局变量,虚拟地址完全相同,但变量值可以不同。 原理:
- C 语言代码所有地址都是虚拟地址,用户无法直接操作物理内存;
- 操作系统通过页表 + MMU,将虚拟地址映射到真实物理内存;
- 写时拷贝:父子进程初始共享物理页,修改数据时分配新物理页,保证进程独立。
8.3 内核描述虚拟内存的结构体
mm_struct:每个进程独有,记录整个虚拟地址空间所有段的起止地址;vm_area_struct:描述单个虚拟内存区域(代码段 / 堆 / 栈等),链表 + 红黑树双结构管理。

8.4 虚拟地址空间四大作用
- 内存隔离安全:进程不能访问其他进程 / 内核物理内存,防止越权篡改;
- 编译地址统一:每个进程独占完整虚拟空间,编译链接地址固定,无需关心物理内存位置;
- 内存利用率提升:延迟分配(申请虚拟地址不分配物理内存,访问时才分配)、内存交换;
- 模块解耦:进程管理、内存管理完全分离,降低内核复杂度。