进程

2026年9月12日
4 min read
the_process

进程是什么

简单来说就是一个 running program(运行中的程序)。

程序本身是没有生命的,平时就呆在磁盘的某个角落,由一些机器指令和静态数据组成。是操作系统把这些字节从磁盘取出来、加载进内存并调度 CPU 跑起来,才使得程序有意义。

进程由什么组成

想要知道进程由什么组成,就要看程序跑起来时会读取和修改哪些系统资源,即它的 机器状态(Machine State):

  • 内存(Memory):指令和数据都存放在内存中。程序运行期间可寻址的地址空间(Address Space)是进程的核心组成部分。
  • 寄存器(Registers):许多指令会读取或更新寄存器。典型代表包括:
    • PC(Program Counter,程序计数器):指向程序接下来要执行的指令。
    • Stack Pointer(栈指针) 与 Frame Pointer(帧指针):管理函数调用栈帧。
  • I/O 信息(I/O Information):记录进程当前打开的文件列表(文件描述符表)等。

进程是如何创建出来的

程序最初以某种可执行格式(Executable Format,如 Linux 下的 ELF)存储在磁盘中。操作系统的创建步骤通常如下:

  1. 加载代码与静态数据到内存:
    • 将磁盘中的代码和静态数据放置在进程的可寻址空间中。
    • 早期操作系统采用急切加载(Eager Loading),在运行前把全部代码和数据一次性读入内存;
    • 现代操作系统普遍采用惰性加载(Lazy Loading / 按需分页 Demand Paging),只在程序执行需要时才按页将数据从磁盘载入内存。
  2. 分配与初始化运行时栈(Run-time Stack):
    • 用于存放局部变量(Local Variables)、函数参数和返回地址。
    • 操作系统还会把命令行参数(即 main(int argc, char *argv[]) 中的 argc 和 argv 数组)填充到栈中,供程序接收。
  3. 分配堆空间(Heap):
    • 堆用于明确请求动态分配的数据(如 C 语言中的 malloc() 与 free()),常用于链表、哈希表、树等数据结构。堆初始较小,随着程序运行通过系统调用按需增长。
  4. 初始化 I/O 相关工作:
    • 在 UNIX / Linux 系统中,每个进程默认打开三个文件描述符:标准输入(stdin, 0)、标准输出(stdout, 1)、标准错误(stderr, 2)。这让程序能轻松从终端读取输入、向屏幕打印输出。
  5. 移交控制权启动执行:
    • 找到程序的入口点(即 main() 函数),跳转到 main() 例程。操作系统通过陷入返回(return-from-trap)将 CPU 控制权交予新创建的进程,程序正式开始执行。

进程状态与流转

一个处于生命周期中的进程,在某一时刻通常处于以下三种基本状态之一:

  • Running(运行):进程正在 CPU 上执行指令。
  • Ready(就绪):进程已准备就绪,但某一时刻操作系统由于调度策略没有选择运行它。
  • Blocked(阻塞):进程执行了某些操作(如对磁盘发起了 I/O 请求),必须等待特定事件发生才能准备运行。在此期间,处理器会去运行其他进程。
Warning (注意)

当阻塞进程等待的 I/O 完成后,它不会直接变回 Running 状态,而是转变为 Ready 状态,重新进入就绪队列等待调度器的调度。

进程三态转换:Running / Ready / Blocked

补充:操作系统如何管理进程?

操作系统自身也是一个软件程序,它通过**进程列表(Process List)**跟踪所有活跃进程。每个进程的信息被封装在一个称为 PCB(Process Control Block,进程控制块) 的结构体中(例如 Linux 的 task_struct、xv6 的 struct proc)。

PCB 中不仅记录了进程的状态与 PID,还在发生**上下文切换(Context Switch)**时,用于保存和恢复该进程的寄存器上下文(Context)。

此外,进程生命周期的两端还存在两个特殊状态:

  • Initial / New(初始状态):进程正在被创建,尚未进入就绪队列。
  • Final / Zombie(僵死状态):进程已退出(调用了 exit()),但其 PCB 仍保留在内存中,等待父进程通过 wait() 读取退出码并完成最终的资源清理(Reap)。如果父进程迟迟不调用 wait,该进程就会成为僵尸进程。