50. exit - 程序正常退出 链接到标题
函数介绍 链接到标题
exit
函数用于正常终止调用进程,执行清理操作并返回指定的退出状态码给父进程。它是C标准库函数,不是系统调用。
函数原型 链接到标题
#include <stdlib.h>
void exit(int status);
功能 链接到标题
正常终止当前进程,执行清理处理程序,关闭流,并返回退出状态。
参数 链接到标题
int status
: 退出状态码0
或EXIT_SUCCESS
: 成功退出- 非0值 或
EXIT_FAILURE
: 失败退出 - 状态码只有低8位有效
返回值 链接到标题
- 无返回值(函数不返回)
特殊限制 链接到标题
- 会调用注册的清理函数(atexit注册的函数)
- 会刷新并关闭所有打开的流
- 调用_exit系统调用终止进程
相似函数 链接到标题
_exit()
: 立即终止进程,不执行清理_Exit()
: C99标准的立即退出函数return
: 从main函数返回
示例代码 链接到标题
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
// 清理函数1
void cleanup_function1(void) {
printf("清理函数1被执行\n");
}
// 清理函数2
void cleanup_function2(void) {
printf("清理函数2被执行\n");
}
// 清理函数3
void cleanup_function3(void) {
printf("清理函数3被执行\n");
}
int main() {
printf("=== Exit 函数示例 ===\n");
// 示例1: 基本使用
printf("\n示例1: 基本使用\n");
// 注册清理函数(后注册的先执行)
if (atexit(cleanup_function1) != 0) {
printf("注册清理函数1失败\n");
}
if (atexit(cleanup_function2) != 0) {
printf("注册清理函数2失败\n");
}
if (atexit(cleanup_function3) != 0) {
printf("注册清理函数3失败\n");
}
printf("程序即将退出,注册的清理函数将被执行...\n");
printf("观察清理函数的执行顺序\n");
// 正常退出
exit(EXIT_SUCCESS);
// 下面的代码不会执行
printf("这行代码不会被执行\n");
}
// 单独的测试函数
void test_exit_behavior() {
pid_t pid;
int status;
printf("\n=== Exit 行为测试 ===\n");
// 创建子进程测试exit行为
pid = fork();
if (pid == -1) {
perror("fork失败");
return;
}
if (pid == 0) {
// 子进程
printf("子进程PID: %d\n", getpid());
// 注册清理函数
atexit(cleanup_function1);
// 打开文件并写入数据(测试缓冲区刷新)
FILE *fp = fopen("test_exit.txt", "w");
if (fp != NULL) {
fprintf(fp, "这是测试数据,应该被刷新\n");
// 注意:不调用fclose,exit会自动刷新
printf("文件已写入数据但未关闭\n");
}
printf("子进程即将调用exit(42)\n");
exit(42); // 退出状态42
} else {
// 父进程等待子进程
pid_t result = waitpid(pid, &status, 0);
if (result != -1) {
if (WIFEXITED(status)) {
int exit_code = WEXITSTATUS(status);
printf("子进程正常退出,退出码: %d\n", exit_code);
}
}
// 检查文件是否被正确刷新
FILE *fp = fopen("test_exit.txt", "r");
if (fp != NULL) {
char buffer[100];
if (fgets(buffer, sizeof(buffer), fp) != NULL) {
printf("文件内容被正确刷新: %s", buffer);
}
fclose(fp);
unlink("test_exit.txt");
}
}
}
// 测试不同的退出方式
void test_exit_methods() {
printf("\n=== 不同退出方式对比 ===\n");
// 方法1: exit()
printf("1. exit() - 执行清理,刷新缓冲区\n");
// 方法2: return from main
printf("2. return from main() - 等同于exit()\n");
// 方法3: _exit() - 立即退出
printf("3. _exit() - 立即退出,不执行清理\n");
printf("\n退出方式选择建议:\n");
printf("- 正常程序退出: 使用exit()或return\n");
printf("- 异常情况快速退出: 使用_exit()\n");
printf("- main函数结束: 可以使用return\n");
}
// 测试退出状态码
void test_exit_codes() {
printf("\n=== 退出状态码测试 ===\n");
printf("标准退出状态:\n");
printf("EXIT_SUCCESS: %d (成功)\n", EXIT_SUCCESS);
printf("EXIT_FAILURE: %d (失败)\n", EXIT_FAILURE);
printf("\n自定义退出状态:\n");
printf("0: 成功\n");
printf("1: 通用错误\n");
printf("2: 用法错误\n");
printf("126: 命令无法执行\n");
printf("127: 命令未找到\n");
printf("128+n: 信号n导致的终止\n");
// 演示不同退出码
pid_t pid = fork();
if (pid == 0) {
// 子进程测试不同退出码
int test_codes[] = {0, 1, 2, 126, 127, 255};
int num_codes = sizeof(test_codes) / sizeof(test_codes[0]);
for (int i = 0; i < num_codes; i++) {
pid_t child = fork();
if (child == 0) {
printf("测试退出码 %d\n", test_codes[i]);
exit(test_codes[i]);
} else if (child > 0) {
int status;
waitpid(child, &status, 0);
if (WIFEXITED(status)) {
int code = WEXITSTATUS(status);
printf("子进程退出码: %d (0x%02x)\n", code, code);
}
}
}
exit(0);
} else if (pid > 0) {
wait(NULL);
}
}
int main_test_all() {
printf("=== Exit 函数完整测试 ===\n");
// 运行各个测试
test_exit_behavior();
test_exit_methods();
test_exit_codes();
printf("\n=== 总结 ===\n");
printf("exit()函数的特点:\n");
printf("1. 执行注册的清理函数(atexit)\n");
printf("2. 刷新并关闭所有流\n");
printf("3. 返回退出状态给父进程\n");
printf("4. 不返回到调用者\n");
printf("5. 是标准C库函数\n\n");
printf("清理函数执行顺序:\n");
printf("- 后注册的先执行\n");
printf("- LIFO(后进先出)顺序\n\n");
printf("缓冲区处理:\n");
printf("- stdout/stderr等流会被刷新\n");
printf("- 文件缓冲区会被写入磁盘\n");
printf("- 未关闭的文件会被自动关闭\n\n");
printf("退出状态:\n");
printf("- 只有低8位有效\n");
printf("- 0表示成功,非0表示失败\n");
printf("- 父进程可以通过wait获取状态\n\n");
printf("使用建议:\n");
printf("1. 正常退出使用exit()或return\n");
printf("2. 需要立即退出使用_exit()\n");
printf("3. 合理使用退出状态码\n");
printf("4. 注册必要的清理函数\n");
printf("5. 注意资源的正确释放\n");
return 0; // 等同于 exit(0)
}
51. exit_group - 终止线程组 链接到标题
函数介绍 链接到标题
exit_group
是一个Linux系统调用,用于终止整个线程组(进程组),而不仅仅是当前线程。它是多线程程序中用于整体退出的重要机制。
函数原型 链接到标题
#include <sys/syscall.h>
#include <unistd.h>
void exit_group(int status);
功能 链接到标题
终止调用线程所属的整个线程组,所有线程都会退出。
参数 链接到标题
int status
: 退出状态码,传递给父进程
返回值 链接到标题
- 无返回值(函数不返回)
特殊限制 链接到标题
- 是Linux特有的系统调用
- 需要通过syscall调用
- 终止整个线程组,不只是当前线程
相似函数 链接到标题
exit()
: 终止当前进程(单线程环境)_exit()
: 立即终止当前进程pthread_exit()
: 终止当前线程
示例代码 链接到标题
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <pthread.h>
#include <sys/wait.h>
#include <errno.h>
#include <string.h>
// 系统调用包装
static void exit_group_wrapper(int status) {
syscall(__NR_exit_group, status);
}
// 线程函数
void* thread_function(void* arg) {
int thread_id = *(int*)arg;
printf("线程 %d 启动\n", thread_id);
// 模拟工作
sleep(1);
if (thread_id == 2) {
printf("线程 %d 调用exit_group,整个线程组将退出\n", thread_id);
exit_group_wrapper(42); // 整个进程组退出
// 下面的代码不会执行
printf("这行代码不会被执行\n");
}
// 其他线程继续工作
sleep(2);
printf("线程 %d 完成工作\n", thread_id);
return NULL;
}
int main() {
printf("=== Exit_group 函数示例 ===\n");
printf("当前进程PID: %d\n", getpid());
// 示例1: 多线程环境中的exit_group
printf("\n示例1: 多线程环境中的exit_group\n");
pthread_t threads[3];
int thread_ids[3] = {1, 2, 3};
// 创建多个线程
for (int i = 0; i < 3; i++) {
if (pthread_create(&threads[i], NULL, thread_function, &thread_ids[i]) != 0) {
perror("创建线程失败");
exit(EXIT_FAILURE);
}
printf("创建线程 %d\n", thread_ids[i]);
}
// 等待线程(实际上不会等到,因为线程2会调用exit_group)
printf("主线程等待子线程...\n");
// 这里程序会因为exit_group而终止,不会执行到下面
for (int i = 0; i < 3; i++) {
pthread_join(threads[i], NULL);
}
printf("所有线程完成\n"); // 这行不会执行
return 0; // 这行也不会执行
}
// 单独的测试函数
void test_exit_group_behavior() {
printf("\n=== Exit_group 行为测试 ===\n");
pid_t pid = fork();
if (pid == -1) {
perror("fork失败");
return;
}
if (pid == 0) {
// 子进程
printf("子进程PID: %d\n", getpid());
// 创建多个线程
pthread_t threads[2];
// 线程1
pthread_create(&threads[0], NULL, [](void* arg) -> void* {
printf("线程1开始工作\n");
sleep(3); // 比主线程工作时间长
printf("线程1完成工作\n"); // 这行可能不会执行
return NULL;
}, NULL);
// 线程2(主线程模拟)
printf("主线程工作1秒后调用exit_group\n");
sleep(1);
printf("调用exit_group(100)\n");
exit_group_wrapper(100);
// 这些代码不会执行
printf("这行不会被执行\n");
pthread_join(threads[0], NULL);
} else {
// 父进程等待子进程
int status;
pid_t result = waitpid(pid, &status, 0);
if (result != -1) {
if (WIFEXITED(status)) {
int exit_code = WEXITSTATUS(status);
printf("子进程通过exit_group退出,退出码: %d\n", exit_code);
} else if (WIFSIGNALED(status)) {
int signal = WTERMSIG(status);
printf("子进程被信号 %d 终止\n", signal);
}
}
}
}
// exit vs exit_group 对比
void compare_exit_functions() {
printf("\n=== Exit vs Exit_group 对比 ===\n");
printf("exit() 行为:\n");
printf(" - 单线程: 终止整个进程\n");
printf(" - 多线程: 终止调用线程,其他线程继续运行\n");
printf(" - 执行清理函数\n");
printf(" - 刷新缓冲区\n\n");
printf("exit_group() 行为:\n");
printf(" - 单线程: 终止整个进程\n");
printf(" - 多线程: 终止整个线程组(所有线程)\n");
printf(" - 不执行清理函数\n");
printf(" - 不刷新缓冲区\n");
printf(" - 立即终止\n\n");
printf("pthread_exit() 行为:\n");
printf(" - 终止调用线程\n");
printf(" - 其他线程继续运行\n");
printf(" - 如果是最后一个线程,终止进程\n\n");
}
// 实际应用场景演示
void demonstrate_real_world_usage() {
printf("\n=== 实际应用场景 ===\n");
printf("exit_group的典型使用场景:\n");
printf("1. 多线程程序的整体错误处理\n");
printf("2. 资源严重不足时的紧急退出\n");
printf("3. 接收到致命信号时\n");
printf("4. 线程检测到不可恢复的错误\n\n");
// 模拟错误处理场景
printf("错误处理示例:\n");
printf("void handle_critical_error(int error_code) {\n");
printf(" log_error(\"严重错误: %%d\\n\", error_code);\n");
printf(" // 通知所有线程立即退出\n");
printf(" exit_group(error_code);\n");
printf("}\n\n");
printf("信号处理示例:\n");
printf("void signal_handler(int sig) {\n");
printf(" if (sig == SIGSEGV || sig == SIGBUS) {\n");
printf(" // 段错误,立即退出整个进程\n");
printf(" exit_group(128 + sig);\n");
printf(" }\n");
printf("}\n\n");
}
// 测试exit_group与信号的关系
void test_exit_group_with_signals() {
printf("\n=== Exit_group 与信号 ===\n");
printf("exit_group与信号的关系:\n");
printf("1. exit_group不被信号中断\n");
printf("2. 调用后立即终止,不处理待处理信号\n");
printf("3. 退出状态通过wait机制传递\n");
printf("4. 不执行信号处理程序\n\n");
// 演示信号处理
printf("信号处理中的使用:\n");
printf("在信号处理程序中使用exit_group:\n");
printf("void sigsegv_handler(int sig) {\n");
printf(" write(STDERR_FILENO, \"段错误!\\n\", 8);\n");
printf(" exit_group(128 + SIGSEGV);\n");
printf("}\n\n");
}
int main_comprehensive_test() {
printf("=== Exit_group 完整测试 ===\n");
// 运行各个测试
test_exit_group_behavior();
compare_exit_functions();
demonstrate_real_world_usage();
test_exit_group_with_signals();
printf("\n=== 总结 ===\n");
printf("exit_group的特点:\n");
printf("1. Linux特有系统调用\n");
printf("2. 终止整个线程组\n");
printf("3. 立即终止,不执行清理\n");
printf("4. 不刷新缓冲区\n");
printf("5. 适用于紧急退出场景\n\n");
printf("使用场景:\n");
printf("1. 多线程程序整体退出\n");
printf("2. 严重错误的紧急处理\n");
printf("3. 信号处理中的快速退出\n");
printf("4. 资源不足时的优雅退出\n\n");
printf("注意事项:\n");
printf("1. 需要通过syscall调用\n");
printf("2. 不执行atexit注册的函数\n");
printf("3. 不刷新标准I/O缓冲区\n");
printf("4. 所有线程立即终止\n");
printf("5. 慎重使用,可能导致数据丢失\n\n");
printf("与相关函数的区别:\n");
printf("- exit(): 执行清理,适用于正常退出\n");
printf("- _exit(): 立即退出,但只影响当前线程\n");
printf("- exit_group(): 立即退出整个线程组\n");
printf("- pthread_exit(): 只退出当前线程\n\n");
return 0;
}