MST

星途 面试题库

面试题:C语言中信号处理函数的设计与实现细节

在Linux C编程中,假设你需要处理SIGINT信号(通常由用户按下Ctrl+C触发),请详细描述如何编写一个可靠的信号处理函数。包括如何防止竞态条件,以及信号处理函数中不能调用哪些类型的函数,为什么?
39.8万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

1. 编写可靠的信号处理函数

在Linux C编程中,处理SIGINT信号可按以下步骤进行:

  1. 定义信号处理函数

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    
    volatile sig_atomic_t sigint_received = 0;
    
    void sigint_handler(int signum) {
        sigint_received = 1;
    }
    

    这里定义了一个全局变量sigint_received,用于标记SIGINT信号是否被接收。volatile sig_atomic_t类型用于确保对该变量的访问是原子操作,避免竞态条件。

  2. 注册信号处理函数

    int main() {
        struct sigaction sa;
        sa.sa_handler = sigint_handler;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
    
        if (sigaction(SIGINT, &sa, NULL) == -1) {
            perror("sigaction");
            return 1;
        }
    
        while (!sigint_received) {
            // 主程序执行正常任务
            printf("Working...\n");
            sleep(1);
        }
        printf("Received SIGINT, cleaning up and exiting...\n");
        // 执行清理操作
        return 0;
    }
    

    使用sigaction函数注册信号处理函数,sigemptyset用于清空信号掩码,sa_flags设置为0表示默认行为。

2. 防止竞态条件

  • 使用volatile sig_atomic_t类型:如上述代码中对sigint_received变量的定义。volatile关键字告诉编译器该变量的值可能会在程序控制之外被改变,防止编译器对其进行过度优化。sig_atomic_t类型保证对该变量的读写操作是原子的,即在多线程或信号处理环境下不会出现数据竞争。

3. 信号处理函数中不能调用的函数类型及原因

  • 不可重入函数

    • 原因:不可重入函数在被信号处理函数调用时,可能会导致数据不一致。例如,函数内部使用了静态变量,当信号处理函数中断主程序执行并调用该函数时,静态变量的状态可能会被破坏,因为主程序和信号处理函数共享这些静态变量。像printf函数就是不可重入的,因为它内部使用了静态缓冲区。
    • 示例asctime函数,它返回一个指向静态缓冲区的指针。如果在信号处理函数和主程序中同时调用asctime,可能会导致缓冲区数据冲突。
  • 标准I/O函数

    • 原因:标准I/O函数通常是基于缓冲区的,信号处理函数调用这些函数可能会破坏缓冲区的状态。例如,fprintf函数在向缓冲区写入数据时,如果被信号处理函数中断并再次调用fprintf,可能会导致缓冲区混乱,数据输出错误。
    • 示例fprintffgets等函数都不适合在信号处理函数中调用。
  • malloc和free函数

    • 原因mallocfree函数维护着堆内存的状态,信号处理函数调用它们可能会导致堆内存管理结构的不一致。例如,malloc在分配内存时,可能正在修改堆内存的元数据,如果此时信号处理函数中断并调用mallocfree,可能会破坏堆内存的一致性,导致程序崩溃。
    • 示例:在信号处理函数中调用malloc来分配内存,可能会引发未定义行为。