Linux 系统中的进程管理与状态演变
1. 进程的基础定义
在 Linux 操作系统中,进程通常被描述为正在运行的程序实例。从不同的维度来看,进程具有以下含义:
- 用户视角: 进程是计算机为了完成特定任务而执行的一系列指令。一个二进制程序文件可以被多次启动,从而产生多个互不干扰的进程。
- 内核视角: 进程是分配系统资源(如 CPU 时间片、内存空间)的基本实体。
2. 进程控制块:task_struct
当一个可执行程序被加载到内存中时,仅有代码和数据是不足以让内核进行管理的。操作系统需要通过一个专门的结构体来记录进程的属性、优先级、内存指针等信息。这个结构体被称为 PCB (Process Control Block)。
在 Linux 内核中,PCB 具体体现为 struct task_struct。它是系统对进程进行调度的主要依据。因此,我们可以总结出一个公式:
进程 = 内核数据结构 (task_struct) + 程序代码与数据
内核对进程的管理,本质上就是对包含所有 task_struct 的双向链表进行增删查改。当我们在终端使用 ./app 运行程序时,系统会自动创建一个新的 task_struct 并将其加入到运行队列中。
3. 进程标识与查询
每个进程在系统中都有一个唯一的数字标识,称为 PID (Process ID)。同时,除了 0 号进程外,每个进程都有一个父进程 ID (PPID)。
3.1 系统调用获取 PID
通过 C 标准库提供的接口,我们可以动态获取当前进程及其父进程的 ID:
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
int main() {
while (true) {
std::cout << "Current Node PID: " << getpid()
<< " | Parent Node PID: " << getppid() << std::endl;
sleep(2);
}
return 0;
}
3.2 命令行查询与控制
利用 ps 工具可以实时查看系统进程状态:
ps ajx:查看详细的进程列表。ps ajx | grep '进程名':过滤特定进程信息。
若需强制终止某个进程,可以使用 kill 命令:
kill -9 [PID]:强制杀死进程。kill -19 [PID]:暂停进程。kill -18 [PID]:恢复进程运行。
4. 使用 fork() 创建子进程
fork() 是 Linux 系统中创建新进程的核心函数。它会为当前进程创建一个副本(子进程)。
- 返回值:
fork()会返回两次。在父进程中返回子进程的 PID,在子进程中返回 0。 - 资源分配: 子进程会继承父进程的代码段,但数据段采用写时拷贝 (Copy-On-Write) 机制,即只有在修改数据时才真正分配物理内存。
#include <iostream>
#include <unistd.h>
int main() {
pid_t result_id = fork();
if (result_id < 0) {
std::cerr << "Fork execution failed!" << std::endl;
return 1;
} else if (result_id == 0) {
// 子进程逻辑
std::cout << "Child node active. PID: " << getpid() << std::endl;
} else {
// 父进程逻辑
std::cout << "Parent node active. Target Child PID: " << result_id << std::endl;
}
return 0;
}
5. 进程状态详解
内核通过修改 task_struct 内部的状态字段来标记进程当前的运行情况:
- R (Running): 运行态,进程正在 CPU 上运行或在运行队列中等待调度。
- S (Sleeping): 可中断睡眠,进程正在等待某种资源(如 IO 输入),可以被信号唤醒。
- D (Disk Sleep): 不可中断睡眠,通常涉及磁盘 IO 核心操作,系统无法强制杀掉该状态的进程。
- T (Stopped): 停止态,通过发送
SIGSTOP信号进入该状态。 - Z (Zombie): 僵尸态,子进程退出但父进程尚未读取其退出码,导致资源未完全释放。
6. 僵尸进程与孤儿进程
6.1 僵尸进程 (Zombie Process)
当子进程退出,而父进程没有调用 wait() 或 waitpid() 获取其退出信息时,该进程会变成僵尸进程。僵尸进程会保留其 task_struct 在进程表中,长期存在会导致内存泄漏。由于进程已经死亡,kill -9 也无法将其清除。
6.2 孤儿进程 (Orphan Process)
如果父进程先于子进程退出,子进程将成为"孤儿"。为了防止孤儿进程在退出后变成无人回收的僵尸进程,系统规定1 号进程 (init/systemd) 将领养该子进程,并在其退出时负责回收资源。
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
int main() {
pid_t id = fork();
if (id > 0) {
// 父进程快速退出,使子进程成为孤儿
std::cout << "Parent process exiting..." << std::endl;
sleep(2);
exit(0);
} else {
// 子进程持续运行
while (true) {
std::cout << "Child PID: " << getpid()
<< " | Parent PID: " << getppid() << std::endl;
sleep(1);
}
}
return 0;
}