libevent源码学习-----统一事件源及信号绑定函数
libevent在對文件描述符,套接字進行監控時直接放到event,這些event通過io多路復用函數進行監控,然而對應信號來說io復用函數卻無能為力,為了解決問題,libevent采用統一事件源的方式,即將信號也表現成event的形式,用到了socketpair套接字對
socketpair套接字對
套接字對也是通信方式的一種,在進程間通信時相比于管道和命名管道而言更簡單,也更安全
linux下使用socketpair函數創建一對套接字,函數原型
創建的套接字對的每一端都可以進行讀寫,也就是sv[0]可以既作為讀端也可以作為寫端,sv[1]也是。
libevent中sv[1]為讀端,sv[0]為寫端。向sv[0]中寫入數據時sv[1]會變為可讀
對于信號的處理,其實用戶可以自己使用sigaction注冊信號處理函數然后自己提供回調函數,但是既然想要交給libevent處理,就說明用戶只提供回調函數,其他什么都不管。
也可以在內部只調用sigaciton/signal注冊信號處理函數,回調函數是用戶提供的那個,這樣也可以滿足需求,但是既然統一事件源,就需要把信號也當成event處理。
libevent的做法是為
- 讀端注冊到base中,提供一個內部的回調函數,該步驟僅僅調用一次
- 信號值作為fd創建event,綁定內部回調函數,注冊到base中
- 為信號綁定一個內部的信號處理函數,在信號處理函數中將發生的信號值寫入套接字對的寫端
- 此時讀端變為可讀,調用這個讀端套接字綁定的回調函數
- 讀端回調函數中讀取信號值,將信號值對應的event添加到激活隊列中
- 激活隊列處理
我覺得完全可以直接在內部使用sigaction/signal綁定信號值和用戶的信號處理函數
但是既然libevent想要把所有東西都當成event來處理,就需要將信號轉成event,把所有event都放到激活隊列中一起處理
復習一下linux下的信號處理機制
UNIX系統信號機制最簡單的接口是signal函數,函數原型為
#include <signal.h> void (*signal(int signo, void (*func)(int)))(int);- signo表示用戶關心的信號類型,如終端信號,退出信號,SIGINT,SIGQUIT等
- func可以是一個函數指針,也就是信號處理函數,當signno信號發生時,會調用這個函數。該函數有一個int類型參數,通常是信號值。也可以是常量SIG_IGN表示內核忽略該信號(SIGKILL和SIGSTOP不能被忽略),常量SIG_DFL表示執行系統默認的處理動作,通常是終止當前進程
- 返回值是指向在此之前的信號處理函數的指針
signal的使用還是比較簡單的
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <sys/types.h>void handler(int signo);int main() {signal(SIGINT,handler);for(;;)pause();return 0; }void handler(int signo) {printf("receive sig=%d\n", sig); }程序首先注冊信號處理函數,之后當中斷發生后切換到內核態,在內核的中斷處理程序執行完后返回用戶態之前檢測到有信號SIGINT發生,內核遍不恢復到main函數而是直接轉到handler函數中,在執行完handler后由內核轉到main函數中繼續執行。main和handler是兩個獨立的存在
signal并不屬于POSIX標準,在UNIX中使用sigaction函數作為signal的替代品,signaction函數的功能是檢查或修改(或檢查并修改)與制定信號相關聯的處理動作,函數原型為
#include <signal.h> int sigaction(int signo, const struct sigaction *act,struct sigaction *oact);- signno是要檢測或修改其處理動作的信號編號,如SIGINT,SIGQUIT等
- act為要修改的動作,本質上是信號處理函數
- oact為以前的信號處理動作,不關心時可以設置為NULL
- 調用成功返回0,失敗返回-1(linux的系統調用返回值大多是這樣)
結構體struct sigaction如下
struct sigaction {void (*sa_handler)(int);sigset_t sa_mask;int sa_flags;void (*sa_sigaction)(int, siginfo_t *, void *); }- sa_handler和signal的第二個參數相同,表示信號處理函數,SIG_IGN表示忽略,SIG_DFL表示默認動作,參數為信號值
- sa_mask是信號屏蔽字集,如果設置,那么在進行信號處理函數時,如果有在sa_mask中的信號到達,則不會通知進程,而是在信號處理函數執行完后才通知。默認情況下,如果程序正在執行信號處理函數,那么對于當前這個信號處理函數處理的信號不會再次送到進程。
- 內核維護著一個信號隊列,如果當前有正在處理的信號處理函數,會把其他到達進程的信號放到這個隊列中。若同一種信號多次發生,通常只會添加一次
- sa_sigaction和sa_flags配合使用,與sa_handler相對應,通常使用sa_handler就不需要使用sa_sigaction,主要用于實時信號可以帶信息,使用sa_handler時sa_flags設為0即可
通常使用sa_handler和sa_mask就可以滿足需求了
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <signal.h> #include <sys/types.h>void handler(int signo);int main() {struct sigaction act;act.sa_handler = handler;/* 清空信號屏蔽集 */sigemptyset(&act.sa_mask);/* 在信號處理函數執行期間屏蔽SIGQUIT,執行完后送達當前進程 */sigaddset(&act.sa_mask, SIGQUIT);act.sa_flags = 0;if(sigaction(SIGINT, &act, NULL) < 0){perror("sigaction error");exit(1);}for(;;)pause();return 0; }void handler(int signo) {printf("receive sig=%d\n", signo);sleep(5); }先按ctrl+c發送中斷信號,馬上按ctrl+z發送終止信號,發現程序5秒后才終止,說明sa_mask中的信號如果發生了會在當前信號處理函數完成后才送達當前進程
總結
以上是生活随笔為你收集整理的libevent源码学习-----统一事件源及信号绑定函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: libevent源码学习-----事件驱
- 下一篇: linux网络编程-----几种服务器模