為什么要有信號處理
程序在執行過程中可能會被系統關閉,譬如在命令行執行時用戶按下CTRL+C終止執行,或者在進程管理時直接終止,為了保證程序能夠正常退出,來釋放所有占用的資源,做一些必要的退出前處理,都需要能夠捕獲到這些信號并進行處理。
C++標準的處理方法
C++提供了signal方法用來安裝一個信號處理handler來替換默認的處理handler:
//Visual Studio 2015中的定義
typedef void (__CRTDECL* _crt_signal_t)(int); //處理handler定義
_crt_signal_t __cdecl signal(_In_ int _Signal, _In_opt_ _crt_signal_t _Function);
Boost.Asio
中的signal_set
Boost.Asio
中定義了signal_set
來進行系統信號的響應,其使用io_service
和要相應的信號構造,提供了如下方法:
- add:添加一個信號
- remove:移除一個信號
- clear:移除其所有信號
- async_wait:啟動異步操作等待接收信號
信號的通知
信號發生后會被排隊到隊列,在有async_wait
的情況下會發出通知,當多個信號發生,也會一個一個地通知,如果信號被從signal_set
中移除則對應的通知會被拋棄。
使用方法
示例如下:
void handler( const boost::system::error_code& error, int signal_number)
{
if (!error)
{
// 信號發生了
}
}
...
// 構造signal set 來接收進程終止消息.
boost::asio::signal_set signals(io_service, SIGINT, SIGTERM);
// 啟動異步操作等待信號發生
signals.async_wait(handler);
注意事項
相同的信號可以被注冊到不同的signal_set
,當信號發生時所有signal_set
的處理handler均會被調用;多次注冊只是在Boost.Asio
中能夠使用;如果使用signal_set
就不要再使用其它方式注冊信號處理handler了。
在Boost.Asio
中如何使用
當接收到對應信號后就需要進行清理流程了,如果啟動了io_service.run
,可以在清理流程中調用io_service.stop
,這樣io_service.run
會盡可能快地退出,所有的異步操作等都會被拋棄,示例如下:
class server
{
public:
server(unsigned short port)
:signals_(io_,SIGINT,SIGTERM),
acceptor_(io_,tcp::endpoint(tcp::v4(),port)),
socket_(io_)
{
}
~server() { io_.stop(); };
void listen()
{
//當接收到中斷信號就停止服務執行退出流程
signals_.async_wait([this](const boost::system::error_code& ec,int signal_number){
if (!ec)
{
io_.stop();
std::cout<<"server stopping\n";
}
});
do_accept();
io_.run();
}
void do_accept()
{
acceptor_.async_accept(socket_, [this](boost::system::error_code ec) {
if (!ec)
{
......
}
else
{
std::cerr << ec.message() << std::endl;
}
do_accept();//繼續發起accept流程阻止run退出
});
}
private:
boost::asio::io_service io_;
boost::asio::signal_set signals_;
tcp::acceptor acceptor_;
tcp::socket socket_;
};