📘 目录

  1. 什么是进程控制?
  2. 进程的创建:fork 与 exec
  3. 父子进程与进程关系
  4. 进程终止与退出码
  5. 僵尸进程与孤儿进程
  6. 等待与回收子进程(wait/waitpid)
  7. 进程组与会话(Process Group & Session)
  8. 守护进程(Daemon)的实现
  9. 信号机制:控制进程的利器
  10. 实用案例与命令实践
  11. 小结与思考

1️⃣ 什么是进程控制?

进程控制是指操作系统对**进程生命周期(创建→运行→阻塞→退出)**进行管理与调度的所有操作行为,开发者也可以通过系统调用来创建、控制、终结进程。


2️⃣ 进程的创建:fork() 与 exec()

Linux 创建新进程最经典的方法就是使用 fork()

pid_t pid = fork();
if (pid == 0) {
    // 子进程逻辑
} else if (pid > 0) {
    // 父进程逻辑
} else {
    // 创建失败
}

fork() 复制父进程的内存空间,但采用**写时复制(Copy-on-Write)**机制。

要在子进程中运行另一个程序,应配合 exec() 使用:

execl("/bin/ls", "ls", "-l", NULL);

常见的 exec 家族函数包括:

  • execlexecpexecvexecve 等

3️⃣ 父子进程与进程关系

  • 每个进程有一个父进程(通过 getppid() 获取)
  • 父进程通过 wait() 系列函数来收尸(回收子进程)
  • 父进程终止后,子进程会被 init 或 systemd 收养,成为孤儿进程

4️⃣ 进程终止与退出码

子进程可以通过 exit() 正常终止,返回退出码,供父进程获取:

exit(0); // 表示正常退出
exit(1); // 非正常退出

父进程使用如下方式获取退出状态:

int status;
wait(&status);
if (WIFEXITED(status)) {
    printf("Exit code: %d\n", WEXITSTATUS(status));
}

5️⃣ 僵尸进程与孤儿进程

  • 僵尸进程(Zombie):子进程已退出,但父进程未回收,系统保留其 PID 和状态信息
  • 孤儿进程(Orphan):父进程提前终止,子进程由 init 或 systemd 接管

解决僵尸进程的方式:

  • 使用 wait() 或 waitpid() 回收
  • 使用信号 SIGCHLD 通知父进程回收子进程

6️⃣ 等待与回收子进程:wait() 与 waitpid()

  • wait():等待任意一个子进程结束
  • waitpid(pid, &status, options):指定等待某个子进程,更灵活
waitpid(child_pid, &status, 0); // 阻塞等待指定子进程结束

可选项包括:

  • WNOHANG:非阻塞模式
  • WUNTRACED:监听停止的子进程

7️⃣ 进程组与会话(pgid/session)

Linux 中进程有组织结构:

概念说明
进程组(PGID)一组相关进程共享控制机制,常用于 Shell 中控制前台/后台
会话(SID)会话是一组进程组,可表示一个登录会话或终端控制会话

创建新会话的函数:

setsid();  // 创建新会话并脱离终端控制

8️⃣ 守护进程的实现(Daemon)

守护进程是后台运行的独立服务程序(如 sshdcronnginx):

基本实现步骤:

  1. fork() 创建子进程,父进程退出
  2. setsid() 创建新会话,脱离终端
  3. 修改工作目录、umask,关闭标准输入输出
  4. 运行服务主循环
daemon(0, 0);  // 快速实现守护进程化(glibc 提供)

9️⃣ 信号机制:控制进程的利器

信号是进程之间或内核向进程发送的异步通知:

信号意义
SIGINTCtrl+C 终止程序
SIGTERM优雅终止
SIGKILL强制终止,不能捕获
SIGCHLD子进程结束通知父进程
SIGHUP终端挂断

信号处理函数:

void handler(int sig) {
    printf("Received signal: %d\n", sig);
}
signal(SIGINT, handler);

🔨 10️⃣ 实用案例与命令实践

查看所有进程:

ps -ef

创建后台进程:

./myprogram &

查找特定进程 PID:

pidof nginx

杀死进程:

kill -9 <PID>

使用 C 创建子进程:

if (fork() == 0) {
    execlp("ls", "ls", "-la", NULL);
    exit(1);
}

🧠 11️⃣ 小结与思考

Linux 中的“进程控制”是操作系统最核心的模块之一。掌握它,意味着你可以:

  • 高效调度任务
  • 构建后台服务与守护进程
  • 避免僵尸进程和资源泄露
  • 编写稳定的服务端程序

📚 推荐阅读

  • 《APUE:UNIX 环境高级编程》Ch8:进程控制
  • 《Linux高性能服务器编程》Ch3:进程模型
  • man 手册:man 2 forkman 2 execman 2 waitman 7 signal