当你运行程序时到底会发生什么
Source: Dev.to
从按键到执行
当你输入命令并按下 Enter 键时,程序会在毫秒级别启动。操作系统协调了一系列步骤,涉及 shell、内核、CPU 调度器和内存管理。
Shell → 内核交互
- Shell 解析命令 – Shell 作为普通用户进程运行。
- Shell 请求内核 启动一个新程序。
内核随后:
- 在内存中分配进程结构体
- 分配唯一的 PID(进程标识符)
- 从磁盘加载可执行文件
- 将代码和数据映射到虚拟内存
- 将进程放入就绪队列
可执行文件包含机器指令和元数据。加载程序将这些段映射后,进程已存在,但必须等待 CPU 时间。
特权模式
- 用户模式 – 运行应用程序代码;硬件访问受限。
- 内核模式 – 拥有完整特权,控制硬件、内存和设备。
当程序需要系统资源时,它会发出 系统调用,这会触发受控的切换到内核模式。内核验证请求,执行操作,然后将控制权返回用户模式,从而保护系统免受错误程序的影响。
Unix‑Style Program Launch (fork‑exec)
- fork – 创建一个子进程,子进程获得父进程内存空间和文件描述符的副本。父子进程从同一点继续执行。
- exec – 子进程用新的程序映像替换其内存。PID 保持不变,只有代码和数据被更换。
Shell 按如下模式工作:
- Shell 调用
fork - 子进程调用
exec并传入你的命令 - 父 Shell 根据作业规则等待或继续执行
这种模型使进程管理保持简单且可预测。
进程生命周期
| 状态 | 描述 |
|---|---|
| 新建 | 进程已创建 |
| 就绪 | 进程等待 CPU 时间 |
| 运行 | 进程在核心上执行 |
| 等待 | 进程因 I/O 或其他事件而暂停 |
| 已终止 | 进程退出 |
调度
调度程序使用诸如 FCFS、SJF、SRTF、Round‑Robin 等算法来选择下一个要运行的就绪进程。大多数调度程序使用优先级和 时间片——即进程在发生上下文切换前可以运行的时长限制。
在一次上下文切换期间,内核会:
- 保存当前进程的 CPU 寄存器
- 加载下一个进程的寄存器
- 更新调度数据
快速切换会产生并行执行的幻觉。
进程控制块 (PCB)
内核使用 PCB 来跟踪每个进程,PCB 存储:
- 调度信息
- 内存映射
- 打开的文件表
- 会计数据
虚拟内存
虚拟内存为每个进程提供了独立的地址空间。页表将虚拟地址映射到物理内存,内核通过这些映射执行保护规则。
文件描述符
文件描述符将进程与资源关联。每个描述符指向内核表项,该表项引用文件、设备或管道。内核管理权限和引用计数。
Source: …
观察进程
你可以通过常用命令看到这些概念的实际运行情况。
ps aux
显示当前活动进程的快照,每一行代表一个由内核跟踪的进程,并列出 CPU 和内存使用情况。
top
提供实时、持续更新的视图。你可以观察进程在调度器轮转任务时状态的变化。
尝试一下:
- 在一个终端中启动一个长时间运行的程序。
- 在另一个终端中使用
ps或top观察它。 - 停止该程序,观察它的条目消失。
每个你运行的程序都会触发进程创建、调度和资源跟踪,这些步骤在后台不断循环进行,决定了系统的行为方式。
下次在终端中按 Enter 时,记住你正在启动一个完整的进程生命周期,且全程由你控制。