【问题标题】:Calling fflush inside multithreaded signal handler?在多线程信号处理程序中调用 fflush?
【发布时间】:2016-04-30 19:14:57
【问题描述】:

我有一个实现多线程网络代理的基本草图:

FILE *proxy_log_file;

static void
SIGUSR1_handler(int sig)
{
    (void)sig;
    fflush(proxy_log_file);
}

int
main(int argc, char **argv)
{
    proxy_log_file = fopen("proxy.log", "a");
    Signal(SIGUSR1, SIGUSR1_handler);
}

这个想法是网络管理员可以通过使用kill 命令向网络代理发送SIGUSR1 信号将缓冲的日志条目刷新到日志文件。但是,我不确定在信号处理程序中调用 fflush 是否是个好主意。我知道fflush 是线程安全的,但不认为它是异步信号安全的。在多线程的信号处理程序中调用fflush 会出现什么并发问题?

【问题讨论】:

    标签: c multithreading fflush


    【解决方案1】:

    假设您的线程调用一个标准 IO 函数,该函数锁定了一个保护流数据结构的互斥体。在解锁该互斥体之前,会传递一个信号,并调用您的信号处理程序。您的信号处理程序调用fflush() 并尝试锁定互斥锁。您的线程和您的标准 IO 流现在将永远死锁,因为您的信号处理程序将在互斥体上等待,但它永远不会变得可用,因为您的线程将阻塞,直到信号处理程序返回。这是一个典型的僵局。

    这就是线程和信号处理程序之间的区别。如果一个线程试图锁定一个互斥体并且发现它已经被锁定,它就会进入休眠状态,其他线程将运行,并且迟早持有互斥体的线程将解锁它。但是您的信号处理程序不是线程,因此它不会进入睡眠状态并让被中断的线程运行 - 该线程将简单地阻塞,直到信号处理程序返回,在上面的示例中,它永远不会。

    【讨论】:

    • 所以我需要阻止异步传递并创建一个专用线程来同步接收信号,例如调用sigwait?
    • 不,您只需不要从信号处理程序中调用非异步安全的函数。这样做是不安全的。例如,您的信号处理程序可以设置一个您已声明为 volatile sig_atomic_t 的标志,然后返回,如果它检测到该标志已设置,您的一个线程可以刷新流。或者,为此使用信号以外的机制。
    • 您所写的内容仅适用于受互斥体保护的流。后者自 C11 起已标准化。在 C11 标准之前,代码可能不会死锁,但仍可能表现出其他一些奇怪的行为,例如刷新另一个线程的半填充缓冲区。
    • @alk:是的,尽管只要有 POSIX 线程,POSIX 就要求标准 IO 函数是线程安全的,所以在问题处理的类 UNIX 平台上,流可能受到互斥保护的时间比这更长。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多