如何在使用boost::asio时防止SIGPIPE

How do I prevent SIGPIPE when using boost::asio?

本文关键字:asio SIGPIPE boost      更新时间:2023-10-16

我使用管道在Gnu/Linux上的两个进程之间进行通信。当发送端仍在尝试发送数据时,接收端关闭管道。下面是一些模拟这种情况的代码。

#include <unistd.h>                                                              
#include <boost/asio.hpp>                                                        
int main()                                                                       
{                                                                                
    int pipe_fds[2];                                             
    if( ::pipe(pipe_fds) != 0 ) return 1;                                        
    // close the receiving end 
    ::close( pipe_fds[0] );
    boost::asio::io_service io;                                             
    boost::asio::posix::stream_descriptor sd( io, pipe_fds[1] );     
    boost::system::error_code ec;                                                
    sd.write_some( boost::asio::buffer("blah"), ec );
    return 0;                                                                    
}

当我运行它时,我得到一个SIGPIPE;这是典型的情况,我知道。然而,我看到boost::asio::error::basic_errors有一个broken_pipe值。我希望在error_code中返回它,而不引发信号。

这可以不创建SIGPIPE处理程序为我的进程?例如,是否缺少boost::asio的配置选项?也许在实现中启用MSG_NOSIGNAL的东西?

如果您希望看到适当的error_code,请安装信号处理程序以忽略SIGPIPE

代码和编译

#include <unistd.h>
#include <iostream>
#include <boost/asio.hpp>

int main( int argc, const char** argv )
{
    bool ignore = false;
    if ( argc > 1 && !strcmp(argv[1], "ignore") ) {
        ignore = true;
    }
    std::cout << (ignore ? "" : "not ") << "ignoring SIGPIPE" << std::endl;
    if ( ignore ) {
        struct sigaction sa;
        std::memset( &sa, 0, sizeof(sa) );
        sa.sa_handler = SIG_IGN;
        int res = sigaction( SIGPIPE, &sa, NULL);
        assert( res == 0 );
    }
    int pipe_fds[2];
    if( ::pipe(pipe_fds) != 0 ) return 1;
    // close the receiving end 
    ::close( pipe_fds[0] );
    boost::asio::io_service io;
    boost::asio::posix::stream_descriptor sd( io, pipe_fds[1] );
    boost::system::error_code ec;
    sd.write_some( boost::asio::buffer("blah"), ec );
    if ( ec ) {
        std::cerr << boost::system::system_error(ec).what() << std::endl;
    } else {
        std::cout << "success" << std::endl;
    }
    return 0;
}
samjmill@bgqfen7 ~> g++ pipe.cc -lboost_system -lboost_thread-mt
samjmill@bgqfen7 ~> 

运行
samm@macmini ~> ./a.out 
not ignoring SIGPIPE
samm@macmini ~> echo $?
141
samm@macmini ~> ./a.out ignore
ignoring SIGPIPE
Broken pipe
samm@macmini ~> 
这个行为的基本原理在write(2)手册页

EPIPE

fd连接读端关闭的管道或插座。当在这种情况下,写入进程也将接收到SIGPIPE信号。(因此,写返回值只在程序捕获,阻塞或忽略此信号)

SIGPIPE是在管道的一端未连接时由操作系统生成的-您无法使用boost::asio来真正阻止它。但是,您可以简单地忽略该信号,其余部分应该自行处理。

signal_init已经在文件boost/asio/detail/signal_init.hpp中做过了