当使用boost::asio时,如何防止出现SIGPIPE?

15

我正在使用管道在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的东西?
3个回答

8

如果您希望查看适当的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信号。(因此,只有当程序捕获、阻塞或忽略该信号时,才能看到写入返回值。)

我添加了强调。

太糟糕了,我没有意识到实现是使用write的。我希望他们使用send,并且我可以启用传递MSG_NOSIGNAL标志。感谢提供示例代码。 - kalaxy
@kalaxy,你可以在send函数中使用MSG_NOSIGNAL。 - Sam Miller

3

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


2

signal_init 在文件 boost/asio/detail/signal_init.hpp 中完成。


1
但它似乎只针对少数平台有效。不幸的是,它尚未包含在 Boost 1.55.0 中的 Linux 和 OS X 版本中。 - eregon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接