嵌入式学习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 的区别
-
标准库函数
exit
- 会调用通过
atexit
注册的函数。 - 刷新标准 I/O 缓冲区(如
printf
的输出)。全面的回收工作,文件关闭、堆释放,缓冲区清理。 - 最终调用
_exit
系统调用。
- 会调用通过
-
系统调用
_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。
典型应用场景
- 关闭文件描述符或释放动态内存。
- 持久化程序状态或日志收尾。
- 临时文件清理。
wait函数概述
wait
函数在Linux中用于父进程等待子进程的状态变化(如终止或停止)。它是进程间同步的重要工具,属于系统调用的一部分,定义在<sys/wait.h>
头文件中。
基本语法
#include <sys/wait.h>
pid_t wait(int *status);
- 参数:
status
是一个指针,用于存储子进程的退出状态信息。若不需要状态信息,可传入NULL
。 - 返回值:成功时返回终止的子进程PID,失败时返回
-1
。
功能详解
- 阻塞等待:调用
wait
时,父进程会阻塞直到任一子进程终止。(一个wait回收一个子进程) - 状态信息:通过
status
可获取子进程退出原因(如正常退出、信号终止等)。需使用宏(如WIFEXITED
、WEXITSTATUS
)解析状态值。
注意事项
- 若父进程无子进程,
wait
会立即返回-1
,并设置errno
为ECHILD
。 - 多个子进程时,
wait
按任意顺序返回一个终止的子进程状态。需循环调用以等待所有子进程。
waitpid 函数概述
waitpid
是 Linux/Unix 系统中用于等待子进程状态变化的系统调用。它允许父进程暂停执行,直到指定的子进程终止或收到信号。相比于 wait
,waitpid
提供了更精确的控制,例如可以指定等待的子进程 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
。