個人上一篇文章研究了一下如何在程序的正文(而不是信號處理函數)中捕獲和處理信號。當時用的方案是 sigprocmask()
。但那個方法理論上是可能漏掉一些信號的。html
真正安全的作法,是使用進程 / 線程間通訊手段,在信號處理函數中向外發送信號,而後在程序正文中監聽(epoll
, select
等等)這些數據。git
這其中是須要使用全局變量的,我目前尚未不使用全局變量的方案。github
本文地址:http://www.javashuo.com/article/p-elsiypvy-en.html編程
《UNIX 環境高級編程》
libevent 源碼深度剖析
使用 sigprocmask 和 sigpending 在程序正文中捕獲和處理信號segmentfault
在設置捕獲信號以前(signal()
),首先建立一個通訊通道。在中斷處理函數中,將捕獲到的信號數往這個通道內寫入。而在程序正文中,則對這個通道進行讀取,這樣就能夠實如今程序正文中捕獲信號了。安全
這實際上是參考了 「libevent 源碼深度剖析」 裏面所說的 libevent 實現 evsignal
的方案。你們都知道,在信號處理函數中,咱們通常不要調用 printf
等 stdio 庫裏的函數。緣由請參照《UNIX 環境高級編程》中 「信號」 章的 「可重入函數」 小節。架構
而實現這個功能中最重要的 read / write
函數,是能夠在信號處理函數中調用的!這就是本方案的原理基礎。異步
write()
而且寫入極少的數據便可。read()
。而且每次獲取數據的長度是固定的:signum
的類型是 int
,獲取 sizeof(int)
字節的數據便可。read / write
API 操做,能夠方便地對接異步 I/O 庫。
「libevent 源碼深度剖析」 中提到 libevent 使用的是 UNIX域socket
(AF_UNIX)。這裏我不使用這個方案,而用了 pipe,緣由以下:socket
通常而言 pipe
是用在父子進程間通訊用的,甚至在《UNIX 環境高級編程》中還原文提到 「單個進程中的管道幾乎沒有任何用處」 。我就哈哈大笑啦——在本文的應用場景下,實際上就是 pipe 爲數很少的在單個進程以內的使用。函數
我正在本身設計一個基於 epoll 的異步 I/O 庫(GitHub 連接),目前已經實現了相似於 libevent 的普通 event 和 evsignal。若是對這個實現感興趣的話,能夠直接到個人工程裏看代碼。本文內容主要是 epEventSignal.c 文件裏的實現。
主要的實際上也就是 epEventSignal_AddToBase()
函數啦。在這個函數的操做流程以下:
pipe()
建立管道pipe[0]
賦值到全局變量中sigaction()
函數捕獲信號。信號處理函數中,將信號值寫入 pipe[0]
pipe[1]
註冊入 epoll
中,捕獲讀事件我在本身的簡單測試程序 test_server.c 中捕獲了兩個信號,分別是 SIGQUIT
(忽略,僅輸出)和 SIGINT
(觸發 event loop 安全退出)。讀者能夠 checkout 出來試試看。
有什麼問題,歡迎告訴我~~~~