【问题标题】:boost::asio::signal_set does not restore previous signal handlersboost::asio::signal_set 不恢复以前的信号处理程序
【发布时间】:2014-08-27 09:59:51
【问题描述】:

所以我有一个可以使用SIGQUIT 优雅关闭的守护进程。 这个守护进程正在运行boost::asio::io_service。我使用boost::asio::signal_set 来捕捉这个信号。

我遇到了我认为完全错误的行为。当我销毁 boost::asio::signal_set 对象时,它不会恢复该信号的先前处理程序。 SIGQUIT 的先前处理程序是无操作的。因此,在boost::asio::signal_set 被销毁后收到此信号后,我的守护程序将终止。我的猜测是这是因为boost::asio::signal_set 在销毁时设置了默认处理程序,即终止程序,而不是之前的处理程序。

我认为这是非常不合适的。我要问的是我错了吗?也许我错过了什么?

【问题讨论】:

    标签: c++ signals boost-asio


    【解决方案1】:

    Boost.Asio 没有为已添加到boost::asio::signal_set 然后通过signal_set::remove()signal_set::clear() 或销毁signal_set 删除的信号指定结果处理程序状态。特别是,没有为Signal Set Service requirements 中的任何关联操作指定后置条件。

    快速浏览signal_set_service::add()implementation

    ::sigaction(signal_number, &sa, 0)
    

    还有signal_set_service::clear()implementation

    struct sigaction sa;
    memset(&sa, 0, sizeof(sa));
    sa.sa_handler = SIG_DFL;
    ::sigaction(reg->signal_number_, &sa, 0)
    

    表明对sigaction() 的调用未处理以前安装的处理程序,并导致在通过signal_set_service 删除信号时注册默认处理程序操作。


    由于信号可能在 Boost.Asio 将信号操作设置为默认值之后传递,但在应用程序代码能够分配自己的处理程序之前,请考虑使用 pthread_sigmask() 来阻止 io_service 内的所有信号。从signal_set 中删除信号后,通过sigaction() 分配所需的处理程序,然后解除对信号的阻塞。

    下面是一个完整的例子来演示这种方法:

    #include <iostream>
    #include <boost/asio.hpp>
    
    void signal_handler(int signal_number)
    {
      std::cout << "signal_handler(): " << signal_number << std::endl;
    }
    
    int main()
    {
      // Force scope to control io_service lifetime.
      {
        boost::asio::io_service io_service;
    
        // Boost.Asio will register an internal handler for SIGQUIT.
        boost::asio::signal_set signal_set(io_service, SIGQUIT);
        signal_set.async_wait(
          [](const boost::system::error_code& error,
             int signal_number)
          {
            std::cout << "siganl_set.async_wait handler: " 
                      << signal_number << std::endl;
    
            // Block SIGQUIT.
            sigset_t signal_mask;
            sigemptyset(&signal_mask);
            sigaddset(&signal_mask, SIGQUIT);
            assert(pthread_sigmask(SIG_BLOCK, &signal_mask, NULL) == 0);
          });
    
        // Send SIGQUIT to this process.
        raise(SIGQUIT);
        // By the time raise() returns, Boost.Asio has handled SIGQUIT with its
        // own internal handler, queuing it internally.  At this point, Boost.Asio
        // is ready to dispatch this notification to a user signal handler 
        // (i.e. those provided to signal_set.async_wait()) within the
        // io_service event loop.
    
        // Prior to calling the io_service, SIGQUIT is not blocked.
        io_service.run();
        // The user provided handler was invoked and has blocked SIGQUIT.
      }
    
      // Send SIGQUIT to this process.
      raise(SIGQUIT);
      // Even though Boost.Asio has set the default handler for SIGQUIT, the
      // signal is blocked, so the signal has been placed into a pending state.
    
      // Register a custom handler for SIGQUIT.
      struct sigaction sa;
      sigemptyset(&sa.sa_mask);
      sa.sa_handler = &signal_handler;
      assert(sigaction(SIGQUIT, &sa, 0) == 0);
    
      // Unblock SIGQUIT.
      sigset_t signal_mask;
      sigemptyset(&signal_mask);
      sigaddset(&signal_mask, SIGQUIT);
      assert(pthread_sigmask(SIG_UNBLOCK, &signal_mask, NULL) == 0);
      // Upon unblocking, the pending SIGQUIT signal is delivered and handled
      // by the handler registered via sigaction.
    
      std::cout << "Fin" << std::endl;
    }
    

    及其输出:

    $ ./a.out 
    siganl_set.async_wait handler: 3
    signal_handler(): 3
    Fin
    

    【讨论】:

    • 我认为这是不恢复旧 sigaction 对象的奇怪行为。非常非常奇怪。谢谢你的帮助! with 信号阻塞可能是唯一可靠的方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-13
    • 1970-01-01
    • 1970-01-01
    • 2016-12-22
    • 2010-12-19
    • 1970-01-01
    • 2012-11-14
    相关资源
    最近更新 更多