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

嵌入式学习Day28

进程的退出:

僵尸进程(Zombie Process)

僵尸进程是指子进程已经终止,但其父进程尚未通过wait()waitpid()系统调用获取其退出状态信息。此时,子进程的进程描述符仍保留在系统中,但不再占用CPU资源。僵尸进程会占用少量系统资源(如PID),大量僵尸进程可能导致系统无法创建新进程。

特点

  • 进程已终止,但进程表项未释放。
  • 父进程未正确处理子进程的退出状态。

解决方法

  • 父进程调用wait()waitpid()主动回收子进程资源。
  • 若父进程不处理,可通过终止父进程(僵尸进程会被init进程接管并回收)。

孤儿进程(Orphan Process)

孤儿进程是指父进程先于子进程终止,子进程被init进程(PID=1)接管。孤儿进程不会占用系统资源,因为init进程会定期调用wait()回收它们。

特点

  • 父进程已终止,子进程由init接管。
  • 不会成为僵尸进程,因为init会自动回收。

关键区别

特征僵尸进程孤儿进程
父进程状态仍在运行但未调用wait()已终止
处理方式需父进程主动回收init进程自动回收
资源占用占用PID不占用资源

如何避免

  • 避免僵尸进程:父进程应注册SIGCHLD信号处理函数或使用waitpid()非阻塞调用。
  • 孤儿进程无需处理:系统会自动管理。

exit 函数概述

在 Linux 系统中,exit 是一个用于终止进程的系统调用或库函数。它属于标准 C 库(stdlib.h),也可通过系统调用直接使用。调用 exit 会结束当前进程,并返回一个状态码给父进程。


exit 函数原型

标准 C 库中的 exit 函数原型如下:

#include <stdlib.h>
void exit(int status);
  • status:进程的退出状态码。通常 0 表示成功,非零值表示错误(具体含义由程序定义)。

系统调用层面的 exit

在 Linux 系统调用中,exit 对应的系统调用是 _exit 原型如下:

#include <unistd.h>
void _exit(int status);
  • _exit:直接终止进程,不执行标准库的清理操作(如刷新缓冲区、调用 atexit 注册的函数等)。

exit 与 _exit 的区别

  1. 标准库函数 exit

    • 会调用通过 atexit 注册的函数。
    • 刷新标准 I/O 缓冲区(如 printf 的输出)。全面的回收工作,文件关闭、堆释放,缓冲区清理。
    • 最终调用 _exit 系统调用。
  2. 系统调用 _exit

    • 立即终止进程,不执行任何清理操作。不刷新缓存区。只会关闭打开的文件。
    • 适用于子进程或需要快速退出的场景。

退出状态码的约定

  • 0:成功退出。
  • 非零值:通常表示错误,具体含义由程序定义。

atexit 函数概述

atexit 是 Linux/Unix 系统中的一个标准库函数,用于注册程序正常终止时要调用的函数。这些函数会在程序通过 exit 或从 main 函数返回时执行,通常用于资源清理、日志记录等收尾工作。

函数原型

#include <stdlib.h>
int atexit(void (*function)(void));
  • 参数function 是一个无参数、无返回值的函数指针。
  • 返回值: 成功返回 0,失败返回非零。

结果为aaaa  bbb  cccc。

关键特性

  • 执行顺序: 后注册的函数先执行(LIFO 顺序)。
  • 调用场景: 仅在程序正常终止时触发,通过 exit 或 main 返回。强制终止(如 kill 或 abort)不会触发。
  • 限制: 标准要求至少支持 32 个函数的注册,实际可通过 sysconf(_SC_ATEXIT_MAX) 查询。

注意事项

  • 避免在退出函数中调用 exit 或依赖已释放的资源。
  • 线程安全性问题需考虑,atexit 在多线程环境中的行为需结合实现细节。
  • 使用atexit时只能调用exit。

典型应用场景

  1. 关闭文件描述符或释放动态内存。
  2. 持久化程序状态或日志收尾。
  3. 临时文件清理。

wait函数概述

wait函数在Linux中用于父进程等待子进程的状态变化(如终止或停止)。它是进程间同步的重要工具,属于系统调用的一部分,定义在<sys/wait.h>头文件中。

基本语法

#include <sys/wait.h>
pid_t wait(int *status);
  • 参数status是一个指针,用于存储子进程的退出状态信息。若不需要状态信息,可传入NULL
  • 返回值:成功时返回终止的子进程PID,失败时返回-1

功能详解

  1. 阻塞等待:调用wait时,父进程会阻塞直到任一子进程终止。(一个wait回收一个子进程)
  2. 状态信息:通过status可获取子进程退出原因(如正常退出、信号终止等)。需使用宏(如WIFEXITEDWEXITSTATUS)解析状态值。

注意事项

  • 若父进程无子进程,wait会立即返回-1,并设置errnoECHILD
  • 多个子进程时,wait按任意顺序返回一个终止的子进程状态。需循环调用以等待所有子进程。

waitpid 函数概述

waitpid 是 Linux/Unix 系统中用于等待子进程状态变化的系统调用。它允许父进程暂停执行,直到指定的子进程终止或收到信号。相比于 waitwaitpid 提供了更精确的控制,例如可以指定等待的子进程 PID 或通过选项调整行为。

函数原型

#include <sys/types.h>
#include <sys/wait.h>pid_t waitpid(pid_t pid, int *status, int options);
  • pid: 指定等待的子进程 PID:
    • >0: 等待特定 PID 的子进程。
    • -1: 等待任意子进程(类似 wait)。
    • 0: 等待与调用进程同组的任意子进程。
    • <-1: 等待进程组 ID 等于 |pid| 的任意子进程。
  • status: 输出参数,存储子进程的退出状态(可通过宏如 WIFEXITED 解析)。
  • options: 控制行为,常用选项:
    • WNOHANG: 非阻塞模式,无子进程退出时立即返回。
    • WUNTRACED: 报告已停止的子进程状态(即使未终止)。

返回值

  • 成功: 返回状态变化的子进程 PID。
  • 错误: 返回 -1(如无匹配子进程或信号中断),并设置 errno

关键宏解析

  • WIFEXITED(status): 子进程正常退出时返回真。
  • WEXITSTATUS(status): 提取子进程退出码(需 WIFEXITED 为真)。
  • WIFSIGNALED(status): 子进程因信号终止时返回真。
  • WTERMSIG(status): 提取终止信号的编号(需 WIFSIGNALED 为真)。

注意事项

  • 阻塞行为: 默认情况下(options=0),waitpid 会阻塞直到目标子进程状态变化。使用 WNOHANG 可避免阻塞。
  • 僵尸进程: 未调用 waitpid 的父进程可能导致子进程成为僵尸进程(保留退出状态直到父进程读取)。
  • 信号处理: 若信号中断了 waitpid,需检查 errno 是否为 EINTR 并决定是否重试。

execl 函数

execl 用于执行指定路径的程序,参数以列表形式传递。

#include <unistd.h>
int execl(const char *path, const char *arg0, ..., (char *) NULL);
  • path:可执行文件的完整路径。
  • arg0:程序名(通常与 argv[0] 一致),后续为命令行参数,以 NULL 结尾。

execlp 函数

execlp 在 execl 基础上自动搜索 PATH 环境变量中的目录。

int execlp(const char *file, const char *arg0, ..., (char *) NULL);
  • file:程序名(如 ls),系统会从 PATH 中查找可执行文件。

execvp 函数

execvp 结合了参数数组和 PATH 搜索功能。

int execvp(const char *file, char *const argv[]);
  • file:程序名,通过 PATH 查找。
  • argv:参数数组,以 NULL 结尾。

区别总结

函数路径处理参数传递方式
execl需完整路径参数列表
execlp自动搜索 PATH参数列表
execvp自动搜索 PATH参数数组

所有函数成功时替换当前进程映像,失败返回 -1 并设置 errno

http://www.xdnf.cn/news/9138.html

相关文章:

  • FreeRTOS——事件标志组
  • Java 权威方案:彻底修复 OPTIONS 方法安全漏洞(附企业级案例与测试指南)
  • 今日行情明日机会——20250526
  • 固态硬盘不识别或掉盘如何解决?——以Kingston FURY Renegade G5为例
  • Qwen-Agent的使用示例-天气查询(function calling)
  • 电子电路原理第十七章(线性运算放大器电路的应用)
  • 【登录优化】redis删除旧token
  • AI测试进入智能体时代:AutoGen 、 Coze、CrewAI 谁主沉浮?
  • C++ STL map multimap 查找操作详解
  • 2025-5-26Vue3快速上手
  • Nginx location匹配模式详解
  • 解锁 MCP 中的 JSON-RPC:跨平台通信的奥秘
  • nfs下载镜像报错File lookup fail,TTTTTTTTTTTTTTT,内核 6.11.0降到5.15.0
  • JAVA面试复习知识点
  • 【沉浸式解决问题】基于泛型递归,Java中实体类基类开启MybatisPlus的ActiveRecord模式
  • PID控制学习(位置式,增量式,算法优化,多环串级PID)
  • LitCTF 2025 Robbie Wanna Revenge
  • 并发的产生及对应的解决方案之实例举证
  • Java 中经常犯的错误
  • 2025年5月26日第一轮
  • 【springboot项目部署】打包部署
  • 矩阵链乘法问题
  • vae 视频截图 复习 gans和vae的原理区别
  • JVM垃圾回收器详细介绍
  • 注解的使用和自定义
  • Composer 常规操作说明与问题处理
  • 【部署】读取制度类txt文件导入dify的父子分段知识库
  • Kubernetes 1.33您需要了解的和升级新功能
  • 爬虫学习-Scrape Center spa6 超简单 JS 逆向
  • 二叉树遍历