【问题标题】:How to make a function async-signal-safe?如何使函数异步信号安全?
【发布时间】:2024-01-14 07:18:01
【问题描述】:

我有以下sigaction处理函数

void signal_term_handler(int sig)
{
    printf("EXIT :TERM signal Received!\n");
    int rc = flock(pid_file, LOCK_UN | LOCK_NB);
    if(rc) {
        char *piderr = "PID file unlock failed!";
        fprintf(stderr, "%s\n", piderr);
        printf(piderr);
    }
    abort();
}

有人告诉我flockprintf 不是异步信号安全的。而且我在这个list 中找不到flock 的备用异步信号安全功能。

根据上面的链接:

当信号中断不安全的功能和信号捕获时 函数调用不安全的函数,行为未定义

有没有办法让flock异步信号安全?或者当我收到TERM 信号时是否有另一种解决方案来执行flock

【问题讨论】:

标签: c++ c linux asynchronous signals


【解决方案1】:

您可以使用fcntl() 作为flock() 的替代方法。

【讨论】:

  • 请注意fcntl() 锁的语义与flock() 的语义不同,而且用处不大。
  • 事实上我可以使用 fcntl 来开发一个新的属性flock() 函数,如topic 所示。谢谢你的回答
【解决方案2】:

flock() 通常是异步信号安全的,因为它是一个系统调用。它的语义使得很难以不同的方式实现它。它不在 POSIX 的异步信号安全函数列表中,因为它根本不在 POSIX 中。

您可能不需要显式解锁,因为当所有引用打开文件描述的文件描述符都关闭时,flock() 锁会自动释放。

printf()fprintf() 调用应替换为适当的 write() 调用。 stdio 函数不在异步信号安全函数列表中,并且通常是强异步信号不安全的。

abort() 调用可能最好通过将信号设置为默认操作并将其重新发送给自身来代替;这样,shell 就知道您的程序因信号而退出,并且可以在适当的时候中止命令序列。

【讨论】:

  • 我可以在 sigaction 处理程序中使用flock() 而不会出现问题吗?
  • 是的,但正如我所说,您可能不需要。
【解决方案3】:

您可以在信号处理程序中使用exit() 而不是abort(),然后使用atexit() 将您的不安全函数放入退出处理程序中。

更新: 通常,您不应在信号处理程序中放置任何可能阻塞的系统调用。避免这种情况的一种方法是在信号处理程序中设置一个标志,然后在主循环的上下文中执行您想要的功能。

volatile int shutdown = 0;

void signal_term_handler(int sig)
{
    shutdown = 1;
}

void cleanup() {
    printf("EXIT :TERM signal Received!\n");
    int rc = flock(pid_file, LOCK_UN | LOCK_NB);
    if(rc) {
        char *piderr = "PID file unlock failed!";
        fprintf(stderr, "%s\n", piderr);
        printf(piderr);
    }
}

void *workerFunction(void *arg) {
    while (!shutdown) {
       ... do stuff ...
    }
    return NULL;
}

int main(int argc, char *argv[]) {
    //...
    pthread_create(...,workerFunction,...);
    pthread_join(...);
    cleanup();
    return 0;
}

【讨论】:

  • 退出处理程序可以同步调用。从异步调用的信号处理程序中这样做会使它们如何运行?
  • 对,我没有仔细阅读这个问题。我想我正在回答一个更基本的问题,即如何避免信号处理程序中的不安全函数。在这种情况下,您可能希望将 exit() 替换为设置关闭标志之类的内容。