50. exit - 程序正常退出 链接到标题

函数介绍 链接到标题

exit函数用于正常终止调用进程,执行清理操作并返回指定的退出状态码给父进程。它是C标准库函数,不是系统调用。

函数原型 链接到标题

#include <stdlib.h>

void exit(int status);

功能 链接到标题

正常终止当前进程,执行清理处理程序,关闭流,并返回退出状态。

参数 链接到标题

  • int status: 退出状态码
    • 0EXIT_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;
}