MST

星途 面试题库

面试题:C语言SIGINT信号处理中的重入问题

假设在一个复杂的C语言程序中,已有多个线程在运行,并且每个线程都可能产生不同的操作。现在要为该程序添加SIGINT信号处理逻辑,使得在接收到SIGINT信号时,能够安全地处理所有线程的状态并优雅地退出。请阐述在这个过程中可能遇到的重入问题,以及如何通过合适的编程技巧(如使用可重入函数、信号掩码等)来避免这些问题。同时,给出关键代码示例来展示如何实现安全的SIGINT信号处理。
10.9万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

可能遇到的重入问题

  1. 全局变量冲突:信号处理函数可能会与线程函数同时访问和修改全局变量,导致数据不一致。例如,一个线程正在更新一个全局计数器,此时信号处理函数也尝试访问或修改该计数器,就会产生冲突。
  2. 函数重入:非可重入函数在信号处理函数和线程函数中同时被调用时,可能会出现问题。非可重入函数通常使用静态或全局数据结构,这些数据结构在多线程和信号处理的并发环境下容易被破坏。例如,mallocprintf函数都不是可重入的,在信号处理函数中调用它们可能会导致程序崩溃。

避免重入问题的编程技巧

  1. 使用可重入函数:在信号处理函数中,只调用可重入函数。可重入函数不会使用静态或全局数据结构,或者在每次调用时都会创建自己的局部数据副本。例如,strcpy不是可重入的,而strncpy是可重入的。readwrite函数也是可重入的,适合在信号处理函数中使用。
  2. 信号掩码:在进入关键代码段之前,使用sigprocmask函数阻塞SIGINT信号,防止信号在关键操作过程中被处理。在关键操作完成后,再解除信号阻塞。这样可以保证关键代码段不会被信号处理函数打断,从而避免重入问题。

关键代码示例

#include <stdio.h>
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>

// 定义全局变量表示程序是否应该退出
volatile sig_atomic_t quit = 0;

// 信号处理函数
void sigint_handler(int signum) {
    quit = 1;
}

// 线程函数
void* thread_function(void* arg) {
    while (!quit) {
        // 线程的正常操作
        printf("Thread is running...\n");
        sleep(1);
    }
    printf("Thread is exiting...\n");
    return NULL;
}

int main() {
    pthread_t thread;

    // 注册SIGINT信号处理函数
    struct sigaction sa;
    sa.sa_handler = sigint_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGINT, &sa, NULL);

    // 创建线程
    if (pthread_create(&thread, NULL, thread_function, NULL) != 0) {
        perror("pthread_create");
        return 1;
    }

    // 主线程等待线程结束
    while (!quit) {
        // 主线程的正常操作
        printf("Main thread is running...\n");
        sleep(1);
    }
    printf("Main thread is waiting for the thread to finish...\n");
    pthread_join(thread, NULL);
    printf("Program is exiting...\n");
    return 0;
}

在这个示例中:

  1. volatile sig_atomic_t quit用于表示程序是否应该退出,volatile关键字确保编译器不会对该变量进行优化,sig_atomic_t类型保证该变量可以被信号处理函数安全地访问和修改。
  2. sigint_handler函数是SIGINT信号的处理函数,它只设置quit变量,这是一个简单且可重入的操作。
  3. main函数中,通过sigaction注册了信号处理函数。线程和主线程在quit变量被设置之前会持续运行,当接收到SIGINT信号时,quit变量被设置,线程和主线程会安全地退出。