MST

星途 面试题库

面试题:C语言信号屏蔽与信号处理函数的协同工作

在一个多线程的C程序中,主线程负责屏蔽SIGTERM信号,子线程注册一个SIGTERM的信号处理函数。请详细说明可能会遇到的问题以及如何解决,同时给出代码示例展示正确的实现方式。
10.2万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

可能遇到的问题

  1. 信号处理函数执行上下文:信号处理函数在一个异步的上下文中执行,可能会打断子线程的正常执行流程,导致共享数据的访问冲突。
  2. 线程安全性:由于主线程屏蔽了信号,子线程注册处理函数,不同线程对信号处理的交互可能导致线程安全问题,例如信号在主线程屏蔽期间到达,当主线程解除屏蔽时,信号处理可能在子线程中意外触发,访问未保护的共享资源。
  3. 可重入性:信号处理函数必须是可重入的,即它可以被中断并再次进入而不会导致数据损坏或其他未定义行为。如果处理函数中调用了不可重入的函数,会引发问题。

解决方法

  1. 使用线程特定数据(TSD):通过设置线程特定数据来保护共享资源,确保信号处理函数中对共享资源的访问是安全的。
  2. 互斥锁(Mutex):在访问共享资源前后加锁,防止多个线程同时访问共享资源,避免数据竞争。
  3. 选择可重入函数:在信号处理函数中只调用可重入的函数,例如 _exitwrite 等。

代码示例

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

// 定义互斥锁
pthread_mutex_t mutex;
// 共享数据
int shared_data = 0;

// 信号处理函数
void sigterm_handler(int signum) {
    // 加锁
    pthread_mutex_lock(&mutex);
    printf("SIGTERM received in child thread. Shared data: %d\n", shared_data);
    // 解锁
    pthread_mutex_unlock(&mutex);
    // 使用可重入函数退出程序
    _exit(EXIT_SUCCESS);
}

void* child_thread(void* arg) {
    // 注册SIGTERM信号处理函数
    struct sigaction sa;
    sa.sa_handler = sigterm_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if (sigaction(SIGTERM, &sa, NULL) == -1) {
        perror("sigaction");
        pthread_exit(NULL);
    }

    while (1) {
        // 模拟子线程工作
        pthread_mutex_lock(&mutex);
        shared_data++;
        printf("Child thread incremented shared data: %d\n", shared_data);
        pthread_mutex_unlock(&mutex);
        sleep(1);
    }
    pthread_exit(NULL);
}

int main() {
    pthread_t tid;
    // 初始化互斥锁
    pthread_mutex_init(&mutex, NULL);

    // 屏蔽SIGTERM信号
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGTERM);
    if (pthread_sigmask(SIG_BLOCK, &set, NULL) == -1) {
        perror("pthread_sigmask");
        return EXIT_FAILURE;
    }

    // 创建子线程
    if (pthread_create(&tid, NULL, child_thread, NULL) != 0) {
        perror("pthread_create");
        return EXIT_FAILURE;
    }

    // 主线程等待子线程结束
    if (pthread_join(tid, NULL) != 0) {
        perror("pthread_join");
        return EXIT_FAILURE;
    }

    // 主线程清理
    pthread_mutex_destroy(&mutex);
    return EXIT_SUCCESS;
}

此代码通过以下方式解决问题:

  1. 使用互斥锁 pthread_mutex_t 保护共享数据 shared_data,确保在信号处理函数和子线程正常执行时,对共享数据的访问是线程安全的。
  2. 信号处理函数 sigterm_handler 中使用可重入函数 _exit 安全地退出程序,避免在处理函数中调用不可重入函数导致的问题。
  3. 主线程屏蔽 SIGTERM 信号,子线程注册信号处理函数,同时处理函数中通过互斥锁保证共享数据访问的安全性。