日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

libevent源码学习-----统一事件源及信号绑定函数

發布時間:2024/4/19 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 libevent源码学习-----统一事件源及信号绑定函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

libevent在對文件描述符,套接字進行監控時直接放到event,這些event通過io多路復用函數進行監控,然而對應信號來說io復用函數卻無能為力,為了解決問題,libevent采用統一事件源的方式,即將信號也表現成event的形式,用到了socketpair套接字對

socketpair套接字對
套接字對也是通信方式的一種,在進程間通信時相比于管道和命名管道而言更簡單,也更安全
linux下使用socketpair函數創建一對套接字,函數原型

/**@param d: 協議族,通常為AF_UNIX*@param type: 套接字類型,如SOCK_STREAM*@param protocol: 協議,0即可*@param sv: 存放創建的兩個套接字*/ int socketpair(int d, int type, int protocol, int sv[2]);

創建的套接字對的每一端都可以進行讀寫,也就是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源码学习-----统一事件源及信号绑定函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。