为什么可以在可重入函数中使用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) 将通过一条指令完成,因此该函数的可重入能力中断该值操作的完成被消除。
我已经在 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) 将通过一条指令完成,因此该函数的可重入能力中断该值操作的完成被消除。