为什么可以在可重入函数中使用sig_atomic_t

Why can sig_atomic_t be used in a reentrant function

我已经在 Linux 上开发 C++ 很长时间了。当我开发一些处理message/task队列的独立模块时,我总是处理SIGINT信号以避免message/task丢失。这是我的代码示例:

volatile sig_atomic_t sig = 0;

void sig_handler(int signal)
{
    sig = 1;
}

int main()
{
    signal(SIGINT,sig_handler);

    msg_queue = init_msg_queue();
    init_receiving_msg_thread();    // start a thread to receive msgs and push them into msg_queue
    while(!sig) {
        process_msg(msg_queue.top());    // process the first msg in the queue
        msg_queue.pop();    // remove the first msg
    }
    stop_receiving_msg_thread();
    process_all_msgs(msg_queue);
    return 0;
}

嗯,这段代码很简单:如果捕获到信号SIGINT,则停止接收消息,处理队列中剩余的所有消息和return。否则代码会一直停留在无限while中。

我认为 sig_atomic_t 是某种黑魔法。因为据我理解,函数 sig_handler 必须是可重入函数,这意味着它不能保存任何静态或全局非常量数据: What exactly is a reentrant function?

所以我一直认为sig_atomic_t是一些棘手的东西,而不是一个全局变量。

但是今天我读到了这个 link: How does sig_atomic_t actually work?,它告诉我 sig_atomic_t 只不过是一个 typedef,例如 int。所以看起来 sig_atomic_t sig 只是一个全局变量。

现在我很困惑。

我上面的代码是否正确使用了 sig_atomic_t?如果没有,你能给我一个正确的例子吗?如果我的代码是正确的,我误解了什么? sig_atomic_t 不是全局变量?或者可以在可重入函数中使用全局变量?或者函数sig_handler可以是不可重入函数?

您对 volatile sig_atomic_t 的使用是正确的。

signal(3p) 的联机帮助页说明如下:

   "the behavior is undefined if the signal handler refers to any
   object other than errno with static storage duration other than
   by assigning a value to an object declared as volatile
   sig_atomic_t," ...

那么为什么volatile sig_atomic_t有这个能力而其他变量是不安全的呢?

link you provided 实际上回答了这个问题。 “保证一次读取和写入。”但要详细说明,sig_atomic_t 实际上 不是原子类型 ,但它本质上就像一个用于信号中断目的的类型。但它通常不适合多线程用途,所以不要将它用于线程间通信。

真正的原子类型实际上是由 CPU 专门处理的,它将确保只有一个核心 (cpu) 可以执行整个操作(有些操作需要多条指令才能完成)在另一个 CPU (或进程)可以操作该数据之前。另一方面,sig_atomic_t 不提供硬件保证,而是确保对其执行的任何操作 (read/write) 将通过一条指令完成,因此该函数的可重入能力中断该值操作的完成被消除。