1、进程相关的函数接口
1.1 exit
- 函数原型
cpp
#include <stdlib.h> // 头文件补充
void exit(int status);
-
功能:
exit()是 C 标准库(<stdlib.h>)中的函数(用户层),作用是立即终止当前运行的程序 ,并将一个 "退出状态码" 返回给操作系统(比如 Windows、Linux),程序一旦执行到exit()就会停止,后续代码不会再运行。- 在主函数(main)中调用
exit(n)和return n效果基本等价(都会终止进程并返回退出码);但在非主函数中,return仅返回函数调用处,而exit会直接终止整个进程。
-
参数 :
status是一个整型参数,称为退出状态码,核心作用是告诉操作系统 "程序是正常结束还是异常结束"。 -
退出流程:
- 执行通过
atexit()/on_exit()注册的清理函数; - 刷新并关闭所有打开的标准 I/O 流(缓存区写入磁盘);
- 删除通过
tmpfile()创建的临时文件; - 最终调用
_exit()完成内核层的进程终止。
- 执行通过
1.2 _exit
- 函数原型
cpp
#include <unistd.h> // 头文件补充
void _exit(int status);
-
功能:
- 直接终止进程,属于系统调用(内核层),无任何用户层清理操作。
- 不会刷新标准 I/O 缓存区、不会执行注册的清理函数、不会删除临时文件,直接释放进程资源并终止。
-
对比示例:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
printf("hello"); // 无换行,内容存于缓存区
// exit(0); // 执行此句:会刷新缓存,终端输出 "hello"
_exit(0); // 执行此句:不刷新缓存,终端无输出
return 0;
}
1.3 wait
- 函数原型
cpp
#include <sys/wait.h> // 头文件补充
pid_t wait(int *wstatus);
-
功能:
- 阻塞等待当前进程的任意一个子进程终止,并回收其资源(避免僵尸进程)。
- 若当前进程无未回收的子进程,直接返回 - 1;若有子进程但未终止,父进程阻塞直到子进程结束。
-
参数:
wstatus:指向整型变量的指针,用于存储子进程的退出状态(可传 NULL,表示不关心退出状态)。 -
返回值:
- 成功:返回被回收子进程的 PID;
- 失败:返回 - 1(如无可用子进程、被信号中断等),并设置
errno。
-
退出状态宏(基于wststus):
| 宏 | 功能 |
|---|---|
| WIFEXITED(wstatus) | 判断子进程是否正常退出(exit/_exit) |
| WEXITSTATUS(wstatus) | 若 WIFEXITED 为真,获取子进程退出码(仅低 8 位) |
| WIFSIGNALED(wstatus) | 判断子进程是否被信号终止 |
| WTERMSIG(wstatus) | 若 WIFSIGNALED 为真,获取终止子进程的信号编号 |
| WCOREDUMP(wstatus) | 判断子进程终止时是否产生核心转储文件(core dump)(是否段错误退出) |
| WIFSTOPPED(wstatus) | 判断子进程是否被信号暂停(仅 waitpid 配合 WUNTRACED 时有效) |
| WSTOPSIG(wstatus) | 若 WIFSTOPPED 为真,获取暂停子进程的信号编号 |
| WIFCONTINUED(wstatus) | 判断子进程是否被 SIGCONT 信号恢复运行(仅 waitpid 配合 WCONTINUED 时有效) |
1.4 waitpid
- 函数原型
cpp
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *wstatus, int options);
-
功能:
- 回收指定的子进程,相比 wait 更灵活,支持阻塞 / 非阻塞模式。
-
参数:
-
pid:指定回收的子进程范围pid > 0:回收 PID 等于该值的子进程;pid = -1:回收任意子进程(等价于 wait);pid = 0:回收与当前进程同进程组的所有子进程;pid < -1:回收进程组 ID 等于pid绝对值的所有子进程。
-
wstatus:同 wait,存储子进程退出状态(可传 NULL); -
options:选项(可组合,用 | 分隔)0:默认,阻塞模式(子进程未终止则父进程阻塞);WNOHANG:非阻塞模式,若没有子进程终止,直接返回 0;WUNTRACED:返回被暂停的子进程状态;WCONTINUED:返回被 SIGCONT 恢复运行的子进程状态。
-
返回值:
- 成功:返回被回收 / 状态改变的子进程 PID;
- 失败:返回 - 1(无可用子进程、信号中断等);
- 非阻塞模式(WNOHANG)且无子进程终止:返回 0。
-
等价关系:
wait(NULL)≡waitpid(-1, NULL, 0)
2、进程的消亡
僵尸态进程如何产生:
- 子进程终止后,内核会保留该进程的少量信息(PID、退出状态、资源使用统计等),等待父进程通过
wait/waitpid回收;若父进程未调用wait/waitpid,且父进程未终止,则子进程会一直处于 "僵尸态"(Z 状态),占用 PID 资源。(进程代码执行结束,但是空间没有被回收)
如何避免产生僵尸进程:
- 父进程先终止:父进程先于子进程结束,子进程成为孤儿进程,孤儿进程会被init(PID=1)进程收养,子进程结束,init会自动回收子进程空间,避免产生僵尸进程
- 父进程主动回收 :子进程结束,父进程通过
wait/waitpid(阻塞 / 非阻塞)及时回收子进程空间,避免产生僵尸进程
孤儿进程:
- 父进程终止,子进程仍在运行,此时子进程的父进程变为 init 进程(PID=1),由 init 进程负责管理和回收,不会成为僵尸进程。
3、exec函数族
cpp
#include <unistd.h> // 所有exec函数的头文件
int execl(const char *path, const char *arg, ... /* (char *) NULL */);
int execlp(const char *file, const char *arg, ... /* (char *) NULL */);
int execle(const char *path, const char *arg, ... /*, (char *) NULL, char *const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
核心功能:
- 替换当前进程的代码段、数据段、堆栈等,执行新的程序(内核复用当前进程的 PID,仅替换进程内容);
- 若 exec 函数调用成功,不会返回 (原进程后续代码被覆盖);只有调用失败时,才会返回 - 1,并设置
errno。
参数命名规则:
| 后缀 | 含义 |
|---|---|
| l | list:参数以可变参数列表形式传递,最后必须以 NULL 结尾 |
| v | vector:参数以字符串指针数组形式传递,数组最后一个元素必须为 NULL |
| p | path:自动在环境变量PATH指定的目录中查找可执行文件(无需写绝对路径) |
| e | environment:自定义环境变量,传入新的环境变量数组(替代原进程的 environ) |
常用示例:
cpp
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
// 1. execl:绝对路径 + 列表参数
// 执行/bin/ls,参数为ls -l -a
execl("/bin/ls", "ls", "-l", "-a", NULL);
// 2. execlp:PATH查找 + 列表参数(无需写路径)
// execlp("ls", "ls", "-l", "-a", NULL);
// 3. execv:绝对路径 + 数组参数
// char *argv[] = {"ls", "-l", "-a", NULL};
// execv("/bin/ls", argv);
// 4. execvp:PATH查找 + 数组参数
// char *argv[] = {"ls", "-l", "-a", NULL};
// execvp("ls", argv);
// 5. execle:自定义环境变量
// char *envp[] = {"PATH=/bin:/usr/bin", "USER=test", NULL};
// execle("/bin/ls", "ls", "-l", NULL, envp);
// 若执行到此处,说明exec调用失败
perror("exec error");
exit(1);
}
exec + fork 经典用法:
父进程 fork 创建子进程,子进程调用 exec 执行新程序,父进程用 wait 回收子进程:
cpp
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
pid_t pid = fork();
if (pid == 0) { // 子进程执行新程序
execlp("ls", "ls", "-l", NULL);
perror("execlp error"); // exec失败才会执行
exit(1);
} else if (pid > 0) { // 父进程回收子进程
wait(NULL);
printf("子进程执行完毕\n");
}
return 0;
}