一、进程概念
在 Linux 系统中,进程(Process) 是程序执行的动态实例,是操作系统进行资源分配和调度的基本单位。
1. 程序 vs 进程
程序(Program):是静态的代码集合(如二进制可执行文件 /bin/ls),存储在磁盘上,不占用系统资源(如 CPU、内存)。
进程(Process):是程序被加载到内存后动态运行的状态,是操作系统管理的“活”的实体。同一程序可被多次执行,生成多个独立的进程(如同时打开多个 bash 终端)。
二、进程的核心特性
1. 动态性:进程是程序执行的动态过程,有生命周期(创建→运行→终止)。
2. 并发性:多个进程可在同一时间段内交替运行(宏观并行,微观串行),由操作系统调度实现。
3. 独立性:每个进程拥有独立的资源空间(如内存、文件句柄),通过内核隔离,一个进程崩溃不影响其他进程(除非涉及共享资源)、一个进程也无法直接访问其他进程内存的资源。
4. 异步性:进程的执行顺序由操作系统调度决定,具有不确定性(受优先级、资源竞争等因素影响)。
三、进程状态与切换
Linux 进程的状态反映了其当前的活动情况,常见状态包括:
状态转换示例:
- 新进程通过 fork() 创建 → 进入 R(就绪)或直接运行(若 CPU 空闲)。
- 进程因 I/O 请求进入 S(可中断睡眠),I/O 完成后被唤醒回 R。
- 进程收到 SIGKILL 信号 → 强制终止,释放资源(避免变为僵尸)。
- 父进程未调用 wait() 回收子进程 → 子进程终止后变为 Z(僵尸)。
四、进程的描述:进程控制块(PCB)
Linux 内核通过 task_struct 结构体(进程控制块,PCB)描述进程的所有信息,存储于内核内存中。关键字段包括:
1. 标识信息:进程 ID(PID,进程的唯一标识)、父进程 ID(PPID)、用户 ID(UID)、组 ID(GID)。
2. 状态信息:当前状态(如 R/S/Z)、退出状态码(终止原因)。
3. 资源信息:虚拟内存映射(mm_struct)、打开的文件描述符表(files_struct)、信号处理方式(signal_struct)。
4. 调度信息:优先级(nice 值)、CPU 占用时间(utime/stime)、调度策略(如 CFS 公平调度)。
5. 上下文信息:寄存器值(如程序计数器 PC、栈指针 SP)、浮点运算状态(用于进程切换时保存现场)。
五、进程的创建与终止
1. 进程创建
Linux 中进程通过 fork() 系统调用创建,遵循“写时复制(Copy-On-Write)”原则:
父进程调用 fork() 后,内核复制父进程的 PCB 和内存页表(初始时共享物理内存),生成子进程。
子进程从 fork() 的返回值开始往后执行(fork()系统调用,在父进程的代码是返回子进程 PID,在子进程的代码中则是返回 0)。
实际内存复制仅在子进程修改内存时发生(提高效率)。
明明是父进程调用了fork,为什么fork在子进程中也返回了一直值?而且值是0,和父进程的不一样?
解释: 从内核源码(如 Linux 的 fork() 实现)来看,fork() 的返回值是通过修改调用者的寄存器状态实现的:
当父进程调用 fork() 时,内核会复制父进程的上下文到子进程,并分别设置两个进程的“返回寄存器”:
父进程的返回寄存器被设置为子进程的 PID(正数)。子进程的返回寄存器被设置为 0(表示无错误且是子进程)。因此,父进程和子进程在 fork() 后从同一条指令继续执行,但由于寄存器中的返回值不同,它们的后续逻辑会分道扬镳。