一、进程退出
退出场景
- 正常终止:代码执行完毕且结果符合预期(退出码为
0
)。 - 异常终止:运行结果错误(退出码非
0
)或进程被信号强制终止。(如SIGINT
或SIGSEGV
)。
- 正常终止:代码执行完毕且结果符合预期(退出码为
退出方法
- 正常退出方式:
return
:从main
函数返回,隐含调用exit
函数。exit(int status)
:标准库函数,执行清理操作(如刷新缓冲区、调用atexit
注册的函数)后终止进程。_exit(int status)
:系统调用,直接终止进程,不刷新缓冲区。
- 正常退出方式:
exit函数和_exit函数的区别
_exit :立即终止进程,不执行任何清理操作
exit : 调用清理函数、刷新缓冲区后终止进程
二、进程等待
为什么要有进程等待?
父进程在忙,子进程结束了,但无人回收,这样就造成了“死亡”的子进程一直占用资源
这个时候的子进程被称为“僵尸进程”
为了解决这个问题,最初的思路是:让父进程停下,等待子进程执行完,然后回
- 回收资源:子进程退出后若未回收,将残留
task_struct
结构(僵尸进程)。 - 获取状态:父进程需通过等待机制获取子进程的退出码或异常信号。
- 回收资源:子进程退出后若未回收,将残留
1、wait
函数参数解析
pid_t wait(int *status);
参数说明
-
status
:- 类型为
int*
,是输出型参数,用于接收子进程的终止状态(如退出码或终止信号)。 - 若不关心子进程状态,可设为
NULL
(如wait(NULL)
)
- 类型为
返回值
- 成功时返回终止的子进程 pid;
- 无子进程或调用失败时返回
2、waitpid
函数参数详解
pid_t waitpid(pid_t pid, int *status, int options);
- 参数说明
-
pid
:-
> 0
:等待指定 pid 的子进程; -
-1
:等待任意子进程(等价于wait
); -
0
:等待与调用进程同进程组的所有子进程; -
< -1
:等待进程组 ID 为|pid|
的任意子进程。
-
-
status
:- 同
wait
的status
参数,存储子进程终止状态。
- 同
-
options
:-
0
:默认阻塞模式; -
WNOHANG
:非阻塞模式,若指定子进程未结束则立即返回0
; -
WUNTRACED
:支持作业控制,返回已暂停的子进程状态。
-
-
- 返回值
- 成功时返回子进程 pid;
- 若使用
WNOHANG
且无子进程终止,返回0
; - 错误时返回
-1
。
waitpid
函数第二个参数详解
waitpid
函数的第二个参数 int *status
是用于接收子进程终止状态的关键参数,需配合特定宏解析具体信息。以下是详细说明:
tatus不能简单的当作整型来看,要从二进制的角度来看,32位下,整型转化为二进制有32个bit位,但是我们仅关注低16位
正常退出(子进程调用 exit
或 _exit
):
if (WIFEXITED(status)) {
int exit_code = WEXITSTATUS(status); // 提取低8位退出码(取值范围0~255):ml-citation{ref="2,4" data="citationList"}
}
信号终止(子进程被信号杀死):
if (WIFSIGNALED(status)) {
int signal_num = WTERMSIG(status); // 提取终止信号编号(如 SIGKILL=9):ml-citation{ref="4,7" data="citationList"}
printf("Terminated by signal: %d\n", signal_num);
}
暂停状态(需配合 WUNTRACED
选项):
if (WIFSTOPPED(status)) {
int stop_signal = WSTOPSIG(status); // 提取暂停信号编号(如 SIGSTOP=19):ml-citation{ref="4,7" data="citationList"}
}
status
的二进制位分布:
WIFEXITED(status)
:判断子进程是否正常退出(返回非零值时表示正常退出)。WEXITSTATUS(status)
:若子进程正常退出,通过此宏获取子进程的退出码(如exit(5)
中的5
)。WIFSIGNALED(status)
:判断子进程是否因信号终止(返回非零值时表示被信号终止)。WTERMSIG(status)
:若子进程因信号终止,通过此宏获取信号代码(如SIGKILL
对应9
)
可忽略性
若父进程不关心子进程状态,可将参数设为 NULL
,此时仅等待子进程结束而不获取状态信息
关键细节
- 指针的必要性:必须传递地址(如
&status
),操作系统需要写入状态值到该地址。 - 状态位结构:
status
的整数值包含多个信息位,需通过宏函数按需提取。 - 与
wait
的关系:wait(&status)
等价于waitpid(-1, &status, 0)
,二者的status
解析方式一致