当前位置: 首页 > news >正文

进程备忘录

目录

一、概念

1. 僵尸进程(Zombie)

2. 孤儿进程(Orphan)

二、wait 系列函数(回收子进程)

1. pid_t wait(int *status);

2. pid_t waitpid(pid_t pid, int *status, int options);

3. pid_t waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

三、代码示例

一、概念

进程调用fork()创建子进程时,进程 ID 的变化遵循以下核心规则:

  • 父进程PID(进程ID)保持不变fork()返回子进程的 PID 给父进程。

  • 子进程获得一个新的唯一 PIDfork()返回 0 给子进程自身。

此外,还有一个容易被忽略的关键点:

  • PPID(父进程ID):子进程的 PPID 会被设置为父进程的 PID。这就像是子进程一出生就知道“谁是我的爸爸”。


关键特例:当“爸爸”先走一步时

如果父进程在子进程之前结束,那么子进程就变成了“孤儿进程”。此时,子进程会被PID 为 1 的init进程(或systemd“收养”,它的PPID 会变成 1


验证方法(代码示例)

下面这段 C 代码直观地验证:

c #include <stdio.h> #include <unistd.h> int main() { pid_t pid = fork(); if (pid == 0) { // 子进程 printf("子进程: PID=%d, PPID=%d\n", getpid(), getppid()); } else if (pid > 0) { // 父进程 printf("父进程: PID=%d, 子进程PID=%d\n", getpid(), pid); } return 0; }

输出示例

父进程: PID=1234, 子进程PID=1235 子进程: PID=1235, PPID=1234

1. 僵尸进程(Zombie)

  • 定义:子进程已终止,但其父进程尚未调用wait()/waitpid()来回收其退出状态,导致子进程的进程描述符仍保留在内核中。

  • 状态ps aux中显示为Z(Defunct)。

  • 危害:少量僵尸无大碍,大量会耗尽 PID 和内存资源。

  • 产生原因:父进程未处理SIGCHLD信号或未主动回收。

  • 解决方法

    • 父进程调用wait()/waitpid()

    • 父进程忽略SIGCHLDsignal(SIGCHLD, SIG_IGN)),内核会自动回收。

    • 父进程终止,僵尸子进程会被 init(PID=1)收养并回收。

2. 孤儿进程(Orphan)

  • 定义:父进程先于子进程终止,此时子进程变为孤儿。

  • 处理:孤儿进程会被 init 进程(PID=1)自动收养,并负责回收(调用wait),因此不会变成僵尸。

  • 应用:常用于守护进程(daemon)——通过 fork 让父进程退出,子进程成为孤儿,被 init 收养,脱离终端控制。

二、wait 系列函数(回收子进程)

1.pid_t wait(int *status);

  • 阻塞等待任意子进程终止。

  • 返回终止子进程 PID,status存储退出状态。

  • 使用宏解析:

    • WIFEXITED(status)—— 正常退出 →WEXITSTATUS(status)取返回值。

    • WIFSIGNALED(status)—— 被信号杀死 →WTERMSIG(status)取信号编号。

2.pid_t waitpid(pid_t pid, int *status, int options);

  • 更灵活:

    • pid == -1:等待任意子进程(同wait)。

    • pid > 0:等待指定 PID 的子进程。

    • options =WNOHANG:非阻塞,立即返回 0(无子进程终止)。

  • 常用于循环 +WNOHANG实现非阻塞轮询。

3.pid_t waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

  • 更高级的等待接口,支持实时信号信息。

  • idtype

    • P_PID:等待指定 PID。

    • P_PGID:等待指定进程组。

    • P_ALL:任意子进程。

  • optionsWNOHANGWEXITEDWSTOPPEDWCONTINUED

  • infop返回子进程状态详细信息(如 si_signo, si_code, si_pid 等)。

/****************************************************************************************************/

wait(&state)返回的是一个pid_t类型的值,但它返回的是“已终止的子进程的 PID”。

具体来说,这个返回值有三种情况:

1. 正常情况(返回 > 0)
返回的是刚刚终止的那个子进程的进程 ID

  • 因为一个父进程可能有多个子进程,通过这个返回值,你可以知道到底是哪一个子进程退出了。

2. 错误情况(返回 -1)

  • 表示调用失败。最常见的错误是ECHILD(没有子进程存在,或者子进程没有被等待)。

  • 此时可以通过errno查看具体错误原因。

3. 特殊信号中断(返回 0)
(仅在设置了WNOHANG选项时发生)

  • 如果你调用waitpid(-1, &state, WNOHANG),并且当前没有任何子进程终止,返回值是0

  • 但标准的wait(&state)默认没有WNOHANG标志,如果子进程没结束它会一直阻塞,所以标准的wait不会返回 0


补充一个重要细节:
虽然返回值是子进程 PID,但如果你想获取父进程自己的 PID,应该使用getpid()
wait返回的 PID 配合state参数(退出状态码)一起使用,可以精确处理每个子进程的退出结果

三、代码示例

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <signal.h> void daemonize() { pid_t pid; // 1. fork 并让父进程退出(成为孤儿,被 init 收养) pid = fork(); if (pid < 0) exit(EXIT_FAILURE); if (pid > 0) exit(EXIT_SUCCESS); // 父进程退出 /*exit(0); exit(EXIT_SUCCESS);通常是0 exit(EXIT_FAILURE);通常是1 exit(2); 自定义错误码 */ // 2. 创建新会话,脱离控制终端 if (setsid() < 0) exit(EXIT_FAILURE); // 3. 再次 fork(防止无意中重新获得终端) pid = fork(); if (pid < 0) exit(EXIT_FAILURE); if (pid > 0) exit(EXIT_SUCCESS); // 4. 修改工作目录为根目录(避免占用可卸载文件系统) chdir("/"); // 5. 重设文件权限掩码(更宽松) umask(0); // 6. 关闭所有打开的文件描述符(0,1,2) close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); // 7. 将 stdin/out/err 重定向到 /dev/null(可选) open("/dev/null", O_RDWR); // fd 0 dup(0); // fd 1 dup(0); // fd 2 //dup 是什么?复制一个“已存在的 fd” 规则:返回 最小可用 fd // 8. 忽略 SIGCHLD,避免僵尸(或使用 signal 处理) signal(SIGCHLD, SIG_IGN); } int main() { daemonize(); // 现在进程是守护进程,可以写日志、监听端口等 while (1) { // 守护进程主循环 sleep(10); } return 0; }

在 Linux 内核机制中,会话组长有权申请打开一个终端。如果这个守护进程以后运气不好(比如它去打开了一个串口设备,或者某个库函数试图打开/dev/tty),它就会重新获得一个控制终端

  • 一旦重新获得终端,用户的键盘信号(比如Ctrl+C)就有可能意外地发送给这个守护进程,导致它被杀死。

  • 这违背了守护进程“默默在后台运行,不受终端干扰”的初衷。

  • 此时的孙子进程

    • 它继承了会话 ID,但它不是会话组长(因为它的 PID 不等于会话组的 SID)。

    • 在 Linux 规则中,只有会话组长才能获取控制终端。既然孙子进程不是组长,它就永远、绝对、丧失了重新获得终端的资格。

    • 它变成了一个纯粹的、不受任何终端信号干扰的后台进程。

WIFEXITED(state)检查是否正常退出
WEXITSTATUS(state)提取退出码
WIFSIGNALED(state)检查是否被信号终止
WTERMSIG(state)提取终止信号编号
WIFSTOPPED(state)检查是否被暂停
WSTOPSIG(state)提取暂停信号编号
http://www.jsqmd.com/news/1094133/

相关文章:

  • 从实战到预防:NBU证书生命周期管理与Error 8506深度解析
  • 路由器里有个你看不到的队列
  • 模具全流程数字化验证三方案横评:CMM、激光扫描、蓝光3D扫描谁更香?
  • 一分钟学会 C++ 标准模板库智能指针
  • 独立开发者用MonkeyCode一个月:我的真实收入变化
  • 做了一个月 Skills,我才理解 Agent 可靠性的本质
  • 钉钉ONE项目用10个月证明了一件事:资源多不等于做得好
  • PHP无字母数字RCE:位运算与临时文件上传的绕过艺术
  • 逆向工程实战:VMP 3.x x64壳导入表修复与VMPDump工具应用
  • 热场分布一目了然!安科瑞光纤测温系统,让数据说话
  • 从滤波器到匹配滤波:幅频与相频特性如何塑造信号处理
  • 2026耳夹耳机哪个品牌好?耳夹耳机排行榜前十名多维度参数测评
  • ESP32 入门教程(一):使用 GPIO 控制 LED 亮灭
  • 轻量化算力方案:某科技公司的AI研发算力服务器案例
  • LSLIDAR激光雷达C16-V4 改频率
  • NPDP培训机构怎么选?盯着这4个问题问就对了!
  • LangChain基础实践——论文阅读助手
  • 华大九天加大投资并购力度,韬定律驱动EDA全流程加速布局
  • 计科八股20260629——离散数学复习(数理逻辑、集合论、代数结构,图论明天补)
  • AutoCAD Architecture 2027下载安装教程【超详细】保姆级图文教程(附安装包)
  • 兰大一篇顶刊插图翻车,全网科研人慌了:AI绘图彻底禁用?
  • Java毕业设计-基于 SpringBoot 框架的项目审批管理系统设计与开发 面向企业的项目评审流程管理系统(源码+LW+部署文档+全bao+远程调试+代码讲解等)
  • 写 Dioxus Demo 不难,难的是把它写成项目
  • 淘宝电商课程哪个更适合新手
  • Java单例模式
  • 2026年企业采购AI外呼系统:怎么选性价比更高?
  • 终极免费跨平台KVM软件指南:如何用Barrier一套键鼠控制多台电脑
  • 一次 GitHub Actions 翻车实录:E2E 测试把我的后端项目按在地上做体检
  • pg空值管理
  • 深入解析无列名SQL注入:原理、实战与防御