进程是什么
简单来说就是一个 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)存储在磁盘中。操作系统的创建步骤通常如下:
- 加载代码与静态数据到内存:
- 将磁盘中的代码和静态数据放置在进程的可寻址空间中。
- 早期操作系统采用急切加载(Eager Loading),在运行前把全部代码和数据一次性读入内存;
- 现代操作系统普遍采用惰性加载(Lazy Loading / 按需分页 Demand Paging),只在程序执行需要时才按页将数据从磁盘载入内存。
- 分配与初始化运行时栈(Run-time Stack):
- 用于存放局部变量(Local Variables)、函数参数和返回地址。
- 操作系统还会把命令行参数(即
main(int argc, char *argv[])中的argc和argv数组)填充到栈中,供程序接收。
- 分配堆空间(Heap):
- 堆用于明确请求动态分配的数据(如 C 语言中的
malloc()与free()),常用于链表、哈希表、树等数据结构。堆初始较小,随着程序运行通过系统调用按需增长。
- 堆用于明确请求动态分配的数据(如 C 语言中的
- 初始化 I/O 相关工作:
- 在 UNIX / Linux 系统中,每个进程默认打开三个文件描述符:标准输入(stdin, 0)、标准输出(stdout, 1)、标准错误(stderr, 2)。这让程序能轻松从终端读取输入、向屏幕打印输出。
- 移交控制权启动执行:
- 找到程序的入口点(即
main()函数),跳转到main()例程。操作系统通过陷入返回(return-from-trap)将 CPU 控制权交予新创建的进程,程序正式开始执行。
- 找到程序的入口点(即
进程状态与流转
一个处于生命周期中的进程,在某一时刻通常处于以下三种基本状态之一:
- Running(运行):进程正在 CPU 上执行指令。
- Ready(就绪):进程已准备就绪,但某一时刻操作系统由于调度策略没有选择运行它。
- Blocked(阻塞):进程执行了某些操作(如对磁盘发起了 I/O 请求),必须等待特定事件发生才能准备运行。在此期间,处理器会去运行其他进程。
Warning (注意)
当阻塞进程等待的 I/O 完成后,它不会直接变回 Running 状态,而是转变为 Ready 状态,重新进入就绪队列等待调度器的调度。

补充:操作系统如何管理进程?
操作系统自身也是一个软件程序,它通过**进程列表(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,该进程就会成为僵尸进程。