MST

星途 面试题库

面试题:C语言信号处理中的竞争条件

在C语言信号处理过程中,可能会出现竞争条件。请阐述什么情况下会出现竞争条件,例如在信号处理函数与主程序共享某些资源时。假设我们有一个全局变量count,主程序会对其进行累加操作,而信号处理函数也可能读取该变量,如何避免因竞争条件导致的数据不一致问题?
20.5万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试
  1. 竞争条件出现的情况
    • 当信号处理函数与主程序共享某些资源(如全局变量),且它们对这些共享资源的访问顺序不可预测时,就会出现竞争条件。例如,主程序正在对共享资源进行写操作,还未完成时信号发生,信号处理函数开始执行,也对该共享资源进行读写操作,这就可能导致数据不一致。
  2. 避免竞争条件的方法
    • 使用互斥锁(Mutex)
      • 在C语言中,可以使用POSIX线程库(pthread)中的互斥锁来保护共享资源。
      • 首先,需要包含<pthread.h>头文件。
      • 定义一个互斥锁变量,例如pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
      • 在主程序对count进行累加操作前,调用pthread_mutex_lock(&mutex);获取互斥锁,操作完成后调用pthread_mutex_unlock(&mutex);释放互斥锁。
      • 在信号处理函数中读取count前,同样调用pthread_mutex_lock(&mutex);获取互斥锁,读取完成后调用pthread_mutex_unlock(&mutex);释放互斥锁。
    • 使用信号掩码(Signal Masking)
      • 在主程序对count进行累加操作前,使用sigprocmask函数阻塞信号,防止信号处理函数在此时被调用。操作完成后,再使用sigprocmask函数解除对信号的阻塞。
      • 示例代码如下:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

volatile sig_atomic_t count = 0;

void signal_handler(int signum) {
    // 信号处理函数读取count
    printf("Signal handler: count = %d\n", count);
}

int main() {
    struct sigaction sa;
    sa.sa_handler = signal_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIGUSR1, &sa, NULL);

    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGUSR1);

    for (int i = 0; i < 5; i++) {
        // 阻塞信号
        sigprocmask(SIG_BLOCK, &mask, NULL);
        count++;
        printf("Main program: count = %d\n", count);
        // 解除信号阻塞
        sigprocmask(SIG_UNBLOCK, &mask, NULL);
        sleep(1);
    }

    return 0;
}
  • 使用原子操作(如果支持)
    • 如果编译器支持原子操作,例如在C11标准中,可以使用<stdatomic.h>头文件中的原子类型。
    • count定义为_Atomic(int) count = 0;
    • 主程序中对count的累加操作可以使用atomic_fetch_add(&count, 1);
    • 信号处理函数中读取count可以使用int value = atomic_load(&count);
  • 选择合适的同步机制
    • 互斥锁适用于多线程环境下的同步,但在信号处理函数与主程序的场景中,需要注意信号处理函数的异步特性,可能需要额外的处理。
    • 信号掩码相对简单,但它可能影响程序的响应性,因为在阻塞信号期间,信号不能被及时处理。
    • 原子操作提供了一种轻量级的同步方式,但依赖于编译器和硬件的支持,并且功能相对有限,例如复杂的操作可能还是需要其他同步机制。