如何在信号处理程序和普通函数中对全局变量进行互斥读写操作

How to do mutually exclusive read or write operation on a global variable in signal handler and normal function

本文关键字:全局变量 操作 读写 函数 信号处理 程序      更新时间:2023-10-16

应用程序是多线程的。在main()中,我注册SIGUSR1:的信号处理程序

// Global variable to indicate whether directory's 
// content needs to be reloaded
bool reload_dir = 0;
int main (int argc, char *argv[])
{
...
signal(SIGUSR1, sigusr1_handler);
...

RunServer(arg1, ...);
return 0;
}

信号处理器:

static void
sigusr1_handler (int signo __unused)
{
reload_dir = 1;
return;
}

以下函数(从main调用(仅由主线程执行:

void
RunServer (arg1, ...)
{
// do some stuffs
...
while (cond != true) {
sleep(1);
}
server_exit();
}

现在,当SIGUSR1被捕获(被包括主线程在内的任何线程捕获(时,我将reload_dir变量设置为1。在RunServer()中,我将根据该值重新加载目录。但是,我还将全局变量reload_dir重置为0,以避免无限期地重复加载目录。并且将reload_dir设置为0将引入竞赛。

既然我们不应该在信号处理程序中使用锁或互斥,那么我如何在不大量更改现有应用程序设计的情况下实现这一点呢。

void
RunServer (arg1, ...)
{
// do some stuffs
...
while (cond != true) {
if (reload_dir) {
// reset reload_dir to avoid loading repeatedly indefinitely 
reload_dir = 0;   // Race condition?
dir_reinit();
}
sleep(1);
}
server_exit();
}

在派生任何线程之前用pthread_sigmask阻止SIGUSR1,以便所有线程都继承该掩码。然后,在main()中,在主循环中使用sigtimedwait而不是sleep来检查USR1是否已交付。

int main(...) {
...
sigset_t ss_usr1;
sigemptyset(&ss_usr1);
sigaddset(&ss_usr1, SIGUSR1);
// block SIGUSR1
pthread_sigmask(SIG_BLOCK, &ss_usr1, NULL);
... spawn threads ...
// RunServer
struct timespec patience = { .tv_sec = 1 };
while (! some_condition) {
int s = sigtimedwait(&ss_usr1, NULL, &patience);
if (s == SIGUSR1) dir_reinit();
// handle errors other than timeout appropriately
}
server_exit();
...
}

优点:没有信号处理程序的复杂性(正确地说,你不会因为使用signal而不是高级sigaction而受到惩罚;不需要原子标志或sig_atomic_t;更容易对行为进行推理。