2011-11-02 7 views
13

Sto usando una pipe per comunicare tra due processi su Gnu/Linux. L'estremità ricevente chiude la pipe mentre l'invio sta ancora tentando di inviare dati. Ecco un codice che emula la situazione.Come si impedisce SIGPIPE quando si utilizza boost :: asio?

#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;                  
} 

Quando lo eseguo, ricevo SIGPIPE; situazione classica, lo so. Tuttavia, vedo che boost::asio::error::basic_errors ha un valore broken_pipe. Mi aspetto che venga restituito nel codice_errore senza che venga generato un segnale.

Questo può essere fatto senza creare un gestore SIGPIPE per il mio processo? Ad esempio, esiste un'opzione di configurazione per potenziare :: asio che mi manca? Forse qualcosa che abiliterebbe MSG_NOSIGNAL nell'implementazione?

+1

+1 per la bella risposta esauriente riproduttore –

risposta

7

Installare un gestore di segnale di ignorare SIGPIPE se si desidera vedere il codice error_code

appropriata e compilare

#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; 
} 

[email protected] ~> g++ pipe.cc -lboost_system -lboost_thread-mt 
[email protected] ~> 

corsa

[email protected] ~> ./a.out 
not ignoring SIGPIPE 
[email protected] ~> echo $? 
141 
[email protected] ~> ./a.out ignore 
ignoring SIGPIPE 
Broken pipe 
[email protected] ~> 

Il razionale per questo comportamento è in write(2) man page

EPIPE

fd è collegata ad un tubo o presa cui estremità lettura è chiusa. Quando si verifica , il processo di scrittura riceverà anche un segnale SIGPIPE. (Così, il valore di ritorno scrittura è visto solo se il programma cattura, blocca o ignora questo segnale.)

enfasi aggiunta da me.

+0

+1 come sempre :) – Ralf

+0

Peccato, non mi rendevo conto l'attuazione stava usando scrittura. Speravo che usassero send e potrei abilitare il passaggio del flag MSG_NOSIGNAL. Grazie per il codice di esempio. – kalaxy

+0

@kalaxy è possibile utilizzare MSG_NOSIGNAL con invio –

3

SIGPIPE viene generato dal sistema operativo quando un'estremità di una pipe non è collegata - non è possibile impedirla con boost :: asio. Puoi comunque semplicemente ignorare il segnale e il resto dovrebbe occuparsi di se stesso.

2

signal_init ha farlo nel file boost/asio/detail/signal_init.hpp

+1

Ma sembra farlo solo per poche piattaforme. Sfortunatamente non è incluso in Boost 1.55.0 su Linux e OS X. – eregon