本节目标
- 理解进程终止的概念
- 理解退出状态码的概念以及使用方法
- 掌握_exit与exit函数的用法以及区别
- atexit函数注册终止时执行的函数
- 相关宏
一、进程终止
进程终止(Process Termination)是指操作系统结束一个进程的执行,回收其占用的资源(如内存、文件描述符等)的过程。它是操作系统管理进程生命周期的重要环节,确保系统资源的有效利用和稳定性。
1.1 进程退出的场景
正常终止
- 程序执行完成:进程完成了其预定的任务,如计算完成、文件处理完毕等。
- 显式调用终止函数:程序通过调用系统提供的终止函数(如
exit()
、_Exit()
或return
)主动请求终止。
异常终止
- 运行错误:如除以零、访问非法内存地址等。
- 信号触发:操作系统或用户通过发送信号(如
SIGKILL
、SIGTERM
)强制终止进程。 - 资源耗尽:如内存不足、文件描述符耗尽等。
外部干预
- 用户手动终止:通过任务管理器或命令行工具(如
kill
)强制结束进程。 - 系统策略:如系统资源紧张时,操作系统可能终止低优先级进程。
1.2 退出码
退出状态码是操作系统或程序用于指示进程执行结果的一种数值标识。它通常是一个整数,用于向调用者(如父进程、脚本或终端)传递进程的执行状态或错误信息。
1.2.1 常见的退出码
退出码 | 含义 |
---|---|
0 | 成功执行,无错误。 |
1 | 通用错误,未具体分类。 |
2 | 误用 shell 内置命令(如 cd 命令在脚本中直接使用但未生效)。 |
126 | 命令找到但无法执行(如权限不足或文件不可执行)。 |
127 | 命令未找到(如拼写错误或路径未包含在 $PATH 中)。 |
128+N | 进程因信号 N 终止。例如: |
- 128+2 (130 ):SIGINT (通常是 Ctrl+C 中断)。 | |
- 128+9 (137 ):SIGKILL (被强制终止)。 | |
- 128+15 (143 ):SIGTERM (正常终止请求)。 | |
>255 | 某些系统可能将状态码截断为 8 位(即取模 256)。例如,exit(257) 返回 1 。 |
退出码为0表示命令执行无误,这是完成命令的理想状态。
退出码为1我们也可以理解为“不被允许的操作”,例如没有sodo的权限下使用yum;在例如除 以0等操作也会返回错误码1。
1.2.2 strerror函数
功能:strerror 函数是 C 标准库中的一个函数,用于将错误码转换为描述该错误的字符串。通常用于处理系统调用或库函数返回的错误码,以提供更可读的错误信息。
函数原型:
#include <string.h>
char *strerror(int errnum);
其中errnum表示一个整数及错误码,返回值是一个指向描述该错误的字符串的指针。
代码示例:
#include<stdio.h>
#include<string.h>
int main()
{
for(int i=0;i<200;i++)
{
//打印0-199所有错误码所对应的错误信息
char* err=strerror(i);
printf("%d--->%s\n",i,err);
}
return 0;
}
运行结果:
这里特别要注意的是, 当传入的错误码无法识别的时候strerror函数就会返回一个“Unknown error”的字符串。
1.3 exit与_exit
1.3.1 exit函数
exit函数是 C 和 C++ 标准库中的一个重要函数,用于终止程序的执行,并返回一个退出状态码给父进程或操作系统。操作系统或父进程可以通过退出状态码来判断程序的执行结果。
函数原型:
在C语言中exit的函数原型通常定义在<stdlib.h>中
#include <stdlib.h>
void exit(int status);
在 C++ 中,也可以使用 C 风格的 exit 函数,或者使用 C++ 标准库中的std::exit函数,定义在<cstdlib.h>头文件中:
#include <cstdlib>
void std::exit(int status);
参数解析:
status:一个整数表示程序退出的退出状态码
0 通常表示程序成功执行。
非零值表示程序执行失败或出现错误。
具体的非零值可以由开发者定义,以表示不同类型的错误。
函数行为:
调用exit函数时会立即终止程序的执行,并返回控制权给操作系统。
在退出之前exit函数会执行以下清理操作:
- 调用所有已注册的
atexit
函数(通过atexit
函数注册的清理函数)。 - 刷新所有打开的输出流/缓冲区。
代码示例:
#include <stdio.h>
#include <stdlib.h>
void cleanup() {
printf("Performing cleanup operations...\n");
}
int main() {
// 注册一个清理函数
if (atexit(cleanup) != 0) {
fprintf(stderr, "Could not register cleanup function\n");
return EXIT_FAILURE; // 使用 EXIT_FAILURE 表示失败
}
// 程序逻辑
printf("Program is running...\n");
// 模拟错误情况,退出程序
int error_code = 1; // 自定义错误码
printf("An error occurred. Exiting with code %d\n", error_code);
exit(error_code);
// 下面的代码不会被执行
return 0;
}
注意事项:
调用exit函数时程序会立即终止执行,后续的代码不会执行。如果想要在退出之前执行某些清理操作应该提前使用atexit函数将指定的函数注册到程序的退出处理列表中。
1.3.2 _exit函数
_exit 函数是一个用于立即终止程序执行的底层系统调用。它通常在需要快速终止进程而不执行正常的清理操作(如调用退出处理程序、刷新缓冲区或关闭文件描述符)时使用。
函数原型:
#include <unistd.h>
void _exit(int status);
参数解析:
status:一个整数表示程序退出的退出状态码
0 通常表示程序成功执行。
非零值表示程序执行失败或出现错误。
具体的非零值可以由开发者定义,以表示不同类型的错误。
注意事项:
_exit
不会调用通过 atexit
注册的函数,也不会刷新标准 I/O 缓冲区。这意味着任何未写入的数据可能会丢失。
使用 _exit
终止进程时,文件描述符不会自动关闭。这可能导致资源泄漏,除非父进程负责清理。
1.3.3 exit与_exit函数的区别
特性 | exit | _exit |
---|---|---|
定义 | C 标准库函数 | 底层系统调用 |
头文件 | <stdlib.h> | <unistd.h> |
清理操作 | 执行清理操作 | 不执行任何清理操作 |
缓冲区刷新 | 刷新所有标准 I/O 缓冲区 | 不刷新缓冲区 |
文件描述符 | 通常关闭所有打开的文件描述符 | 不关闭文件描述符 |
退出状态 | 传递退出状态给操作系统 | 传递退出状态给操作系统 |
使用场景 | 正常终止程序 | 异常终止程序或避免清理操作 |
缓冲区处理示例:
exit函数在终止程序之前会刷新所有缓冲区:
#include<stdio.h>
#include<stdlib.h>
int main()
{
printf("这是一个示例");
exit(0);
return 0;
}
运行结果:
_exit不会执行任何清理操作,也不会刷新缓冲区:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
printf("这是一个示例");
_exit(0);
return 0;
}
1.4 atexit函数
是 C 标准库中的一个函数,用于注册程序在正常终止时要执行的函数。它提供了一种机制,使得开发者可以在程序结束时执行清理操作,例如释放资源、关闭文件、保存状态等
函数原型:
#include <stdlib.h>
int atexit(void (*func)(void));
参数解析:
func 一个指向无参数、无返回值的函数的指针。这个函数将在程序正常终止时被调用。
返回值:
注册成功时返回0,注册失败(例如没有足够的空间来存储更多的退出函数)返回一个非0值。
代码示例:
#include <stdio.h>
#include <stdlib.h>
void cleanup() {
printf("Cleaning up resources...\n");
}
int main() {
if (atexit(cleanup) != 0) {
fprintf(stderr, "Could not set exit function\n");
return EXIT_FAILURE;
}
// 程序的其他部分
printf("Program is terminating normally.\n");
return EXIT_SUCCESS; // 或者直接 return 0;
}
注意事项:
注册的函数按照它们被注册的相反顺序调用。也就是说,最后注册的函数最先被调用。
通过atexit注册的函数只有程序通过exit或return从main函数中返回,或者调用_exit之外的终止函数时才会被调用。
同一个函数可以被多次注册,每次注册都会被调用。
1.5 程序终止相关宏
1.5.1 EXIT_SUCCESS 与 EXIT_FAILURE
EXIT_SUCCESS
宏定义,通常为0,用于表示程序终止。
#include <stdlib.h>
int main() {
// 程序成功执行
return EXIT_SUCCESS;
}
EXIT_FAILURE
宏定义,通常值为非0值用于表示程序失败终止。
#include <stdlib.h>
int main() {
// 程序执行失败
return EXIT_FAILURE;
}