通过 sigwait 处理 SIGPIPE

SIGPIPE handling by sigwait

我正在尝试在某个进程的输出通过管道传输到另一个进程时正常关闭该进程。我正在通过管道输出来测试下面的代码: ./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() 的理解相矛盾。

您需要设计一些其他方式来注意到写入失败,例如,忽略 SIGPIPE 但设置 std::cout.exceptions(ios::badbit),或者在您的写入线程中处理信号。

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

这是将 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;
}