sigwait 处理 SIGPIPE

Posted

技术标签:

【中文标题】sigwait 处理 SIGPIPE【英文标题】:SIGPIPE handling by sigwait 【发布时间】:2021-02-22 13:03:15 【问题描述】:

当一个进程的输出通过管道传输到另一个进程时,我正在尝试实现一个进程的正常关闭。我正在通过管道输出来测试下面的代码:./a.out | less 并在出现提示时按q。我看到的是信号处理程序的调用,而不是sigwait() 的预期完成(这里添加它只是为了显示正在发生的事情)。

#include <csignal>
#include <chrono>
#include <iostream>
#include <thread>

#include <signal.h>

int handlerSig 0;

void signalHandler(int s)

    handlerSig = s;
    std::cerr << "handlerSig: "  << handlerSig << std::endl;


int main()

    for (int i = 1; i < 32; ++i)
    
        std::signal(i, signalHandler);
    

    bool run true;
    std::thread thread [&]
        
            while (run)
            
                std::cout << "ping" << std::endl;
                std::this_thread::sleep_for(std::chrono::milliseconds 500);        
            
        ;

    sigset_t waitSet;
    sigemptyset(&waitSet);
    sigaddset(&waitSet, SIGINT);
    sigaddset(&waitSet, SIGPIPE);
    sigaddset(&waitSet, SIGTERM);

    pthread_sigmask(SIG_BLOCK, &waitSet, nullptr);

    int waitSig 0;
    sigwait(&waitSet, &waitSig);
    run = false;
    thread.join();

    std::cerr << "waitSig: "  << waitSig << std::endl;


我在 WSL2 和 CentOS 机器上得到了一致的结果,我更愿意专注于在那里解决这个问题。在 WSL1 下运行时,除非我删除 pthread_sigmask(SIG_BLOCK...),否则 SIGINT 和 SIGTERM 都不会导致 sigwait() 完成,但这似乎与我对应该如何使用 sigwait() 的理解相矛盾。

【问题讨论】:

std::cerr &lt;&lt; ... 在信号处理程序中是不安全的。您只能从信号处理程序中安全地进行异步信号安全调用,并且使用 std::cerr 和类似的方法不是异步信号安全的。 【参考方案1】:

您需要设计其他方法来发现写入失败,例如,忽略 SIGPIPE 但设置 std::cout.exceptions(ios::badbit),或在写入线程中处理信号。

重要的是,将始终为您的写作线程生成 SIGPIPE,尽管您的 sigwait()ing 线程也如此。线程活动产生的某些信号专门为该线程生成,这意味着它们将仅被传递给该线程或由该线程接受。 (POSIX.1-2008 System Interfaces 2.4.1) 通常,“自然发生”的 SIGPIPE、SIGFPE 和 SIGSEGV 是这样工作的。

【讨论】:

谢谢 - 第二段解释了我的结果。出于我的目的,我可能只是将该信号转发到主线程。我在另一个答案中添加示例。【参考方案2】:

这是将 SIGPIPE 转发到主线程的示例 - 在我的情况下可能就足够了:

#include <csignal>
#include <chrono>
#include <iostream>
#include <thread>

#include <signal.h>

pthread_t mainThread pthread_self();

void forwardSig(int sig)

    if (not pthread_equal(pthread_self(), mainThread))
    
        pthread_kill(mainThread, sig);
    


int main()

    struct sigaction newAction ;
    sigemptyset(&newAction.sa_mask);
    newAction.sa_handler = forwardSig;
    sigaction(SIGPIPE, &newAction, nullptr);

    bool run true;
    std::thread thread [&]
        
            while (run)
            
                std::cout << "ping" << std::endl;
                std::this_thread::sleep_for(std::chrono::milliseconds 500);        
            
        ;

    sigset_t waitSet;
    sigemptyset(&waitSet);
    sigaddset(&waitSet, SIGINT);
    sigaddset(&waitSet, SIGPIPE);
    sigaddset(&waitSet, SIGTERM);

    pthread_sigmask(SIG_BLOCK, &waitSet, nullptr);

    int waitSig 0;
    sigwait(&waitSet, &waitSig);
    run = false;
    thread.join();

    std::cerr << "waitSig: "  << waitSig << std::endl;

【讨论】:

我会谨慎对待:你想要 sigaction 而不是 signal(在此处搜索为什么),你想要 pthread_equal 而不是相等运算符,而且你的信号中仍然有未阻塞的信号您应该考虑的工作线程。 (规范没有将 block+sigwait 优先于正常处理——如果终端的 Ctrl-C SIGINT 被传递给工作线程而不是主线程怎么办?) @pilcrow 按照建议 - 我已将 sigactionpthread_equal 合并到我上面的代码中。什么会导致终端信号被发送到子线程而不是主线程 - 是在任何地方定义的吗?在这个例子中,我没有考虑故意向子线程发送信号。

以上是关于sigwait 处理 SIGPIPE的主要内容,如果未能解决你的问题,请参考以下文章

Linux (Fedora 13) 与 OS X 中的 sigwait

如果我单步调试阻塞的线程会发生啥?

Unix系统编程 信号部分学习笔记

linux 平台上 C# 中的自定义 posix 信号不起作用

xenomai鍐呮牳瑙f瀽涔嬩俊鍙穝ignal(浜?---xenomai淇″彿澶勭悊鏈哄埗

ARM协处理器