亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 操作系統 > 正文

libev 中IO事件循環解析

2024-06-28 13:23:25
字體:
來源:轉載
供稿:網友
libev 中IO事件循環解析

1、IO事件基本數據結構ev_io

struct ev_io這個結構體是IO監視器。libev中所有的事件均有自己的一個結構體來表示,如時間事件是ev_time、ev_io等。

基類ev_watcher定義如下:

typedef struct ev_watcher{    int active;     int pending;    int PRiority;    void *data;     void (*cb)(struct ev_loop *loop, struct ev_watcher *w, int revents);} 

基類中 “active"表示是否激活該watcher,“pending”該監控器是否處于pending狀態,“priority"其優先級以及觸發后執行的動作的回調函數。

與基類配套的還有個裝監控器的List:

typedef struct ev_watcher_list{    int active;     int pending;    int priority;    void *data;     void (*cb)(struct ev_loop *loop, struct ev_watcher_list *w, int revents);    struct ev_watcher_list *next;} ev_watcher_list;

ev_io是對一個IO事件監視的基礎結構體。定義如下:

typedef struct ev_io{    int active;     int pending;    int priority;    void *data;     void (*cb)(struct ev_loop *loop, struct ev_io *w, int revents);    struct ev_watcher_list *next;     int fd;     /* 這里的fd,events就是派生類的私有成員,分別表示監聽的文件fd和觸發的事件(可讀還是可寫) */    int events; } ev_io;

源代碼里ev_io定義在ev.h中。原文定義中嵌套了一些基類和其他一些宏定義,這里直接寫出來,方便理解??梢钥吹綄⑴缮惖乃接凶兞糠旁诹斯灿胁糠值暮竺妗_@樣,當使用C的指針強制轉換后,一個指向 struct ev_io對象的基類 ev_watcher 的指針p就可以通過 p->active 訪問到派生類中同樣表示active的成員了。

2、IO事件的初始化和設置

初始化和設置比較簡單,如下:

#define ev_io_init(ev,cb,fd,events)          do { ev_init ((ev), (cb)); ev_io_set ((ev),(fd),(events)); } while (0)#define ev_io_set(ev,fd_,events_)            do { (ev)->fd = (fd_); (ev)->events = (events_) | EV__IOFDSET; } while (0)

初始化一個IO事件,只需要調用ev_io_init()函數,參數ev表示ev_io指針,cb表示觸發事件的回調函數,fd表示要監視的文件描述符,events表示監視的事件。

3、IO事件的注冊

先了解 struct ANFD,ANFD表示事件循環中對一個文件描述符fd的監視的基本信息結構體,定義如下:

typedef struct{  WL head;//watch_list結構體  unsigned char events; /* 所監視的事件 */  unsigned char reify;  /* 標志位,用來標記ANFD需要被重新實例化(EV_ANFD_REIFY, EV__IOFDSET) */  unsigned char emask;  /* the epoll backend stores the actual kernel mask in here */  unsigned char unused;  unsigned int egen;    /* generation counter to counter epoll bugs */} ANFD;  /* 這里去掉了對epoll的判斷和windows的IOCP*/

首先是WL head 這個基類監視器鏈表,這里首先只用關注一個 “head” ,他是之前說過的wather的基類鏈表。這里一個ANFD就表示對一個文件描述符的監控,那么對該文件描述的可讀還是可寫監控,監控的動作是如何定義的,就是通過這個鏈表,(這個鏈表的長度一般不會超過3,文件的監控條件無非是可讀、可寫等)把對該文件描述法的監控器都掛上去,這樣就可以通過文件描述符找到了。而前面的說的anfds就是這個對象的數組,下標通過文件描述符fd進行索引。anfds是一個ANFD型動態數組。這樣anfds數組就是全部的IO監控,最后可以通過epoll_wait()來監測事件。

每當有新的IO監視器fd加入,調用wlist_add()添加到anfds[fd]的鏈表head中。如果一個anfds的元素監控條件發生改變,如何修改這個元素的監控條件呢。anfds的下標可以用fd來表示,這里有一個新的數組,數組元素內容是新添加的要監視的IO事件的fd或者修改監視內容的fd,數組名是fdchanges,也是動態數組。這個數組記錄了新加入fd或者修改的fd的值,具體實現函數為“fd_change”

inline_size voidfd_change (EV_P_ int fd, int flags){  unsigned char reify = anfds [fd].reify;  anfds [fd].reify |= flags;//標志,表示fd監視條件被修改了  if (expect_true (!reify))//如果fd最初的監視條件為空,表示新加入的fd    {      ++fdchangecnt;//fd計數器加一      array_needsize (int, fdchanges, fdchangemax, fdchangecnt, EMPTY2);//添加到fdchanges數組中      fdchanges [fdchangecnt - 1] = fd;    }  //如果不是新加入的fd,則fdchanges數組中已經有fd了。表示以前添加過對fd的IO監視}

這時所有的要被監視的fd都存放在fdchanges數組中,當我們運行ev_run時,會調用“fd_reify”,它遍歷fdchanges數組,如果發現fd的監視條件發生變化了,就會調用epoll_ctl()函數來改變fd的監視狀態。這個fdchanges數組的作用就在于此,他記錄了anfds數組中的watcher監控條件可能被修改的文件描述符,并在適當的時候將調用系統的epoll_ctl或則其他文件復用機制修改系統監控的條件。注意,假如我們在某個fd 上已經有個 watch 注冊 了 read 事件,這時我們又再添加一個watch,還是read 事件,但是不同的回調函數,在此種情況下,我們不應該調用epoll_ctrl 之類的系統調用(減少系統開銷),因為我們的events 集合是沒有改變的(表示監視的事件沒有發生改變),所以為了達到這個目,anfd[fd] 結構體中還有一個events事件,它是原先的所有watcher 的事件的 ”|“ 操作,向系統的epoll 重新添加描述符的操作 是在下次事件迭代開始前進行的,當我們依次掃描fdchangs,找到對應的anfd 結構,如果發現先前的events 與 當前所有的watcher 的”|“ 操作結果不等,則表示我們需要調用epoll_ctrl 之類的函數來進行更改,反之不做操作即,作為一條原則,在調用系統調用前,我們已經做了充分的檢查,確保不進行多余的系統調用!fd_reify()中定義如下:

inline_size voidfd_reify (EV_P){  int i;  for (i = 0; i < fdchangecnt; ++i)    {      int fd = fdchanges [i];//取出可能改變監控條件的fd      ANFD *anfd = anfds + fd;//得到anfds中下標      ev_io *w;//頂一個ev_io指針      unsigned char o_events = anfd->events;      unsigned char o_reify  = anfd->reify;      anfd->reify  = 0;      /*if (expect_true (o_reify & EV_ANFD_REIFY)) probably a deoptimisation */        {          anfd->events = 0;          for (w = (ev_io *)anfd->head; w; w = (ev_io *)((WL)w)->next)//這里用到了強制轉換,for循環的作用就是          //獲得fd全部的新的監控事件集合,存放在events成員變量中            anfd->events |= (unsigned char)w->events;          if (o_events != anfd->events)//如果新監控事件和舊監控事件不同,            o_reify = EV__IOFDSET; /* actually |= *///修改標志位,表示fd監控條件改變        }      if (o_reify & EV__IOFDSET)//fd監控條件改變,調用backend_modify也就是epoll_ctl()修改fd的監控條件        backend_modify (EV_A_ fd, o_events, anfd->events);    }  fdchangecnt = 0;//一次遍歷完成,fdchanges數組個數清零}

所以,總結一下注冊過程就是通過之前設置了監控條件IO watcher (ev_io的一個實例)獲得監控的文件描述符fd,找到其在anfds中對應的ANFD結構anfds[fd],將該watcher掛到該結構的head鏈上wlist_add()。由于對應該fd的監控條件有改動了,因此在fdchanges數組中記錄下該fd,在后續的步驟中調用系統的接口修改對該fd監控的條件。整個注冊示意圖如下:

111

4、啟動IO事件驅動器

啟動IO事件驅動器,ev_run中主要調用了fd_reify()后,做了一些時間計算后,進入了backend_poll也就是epoll_poll()中,執行了wait操作

eventcnt = epoll_wait (backend_fd, epoll_events, epoll_eventmax, timeout * 1e3);

成功的話,返回了響應事件的個數,然后執行了fd_event()

inline_speed voidfd_event (EV_P_ int fd, int revents){
/* do not submit kernel events for fds that have reify set */  
/* because that means they changed while we were polling for new events */
ANFD *anfd = anfds + fd; if (expect_true (!anfd->reify))//reify是0
    /*如果reify不是0,則表示我們添加了新的事件在fd上,不是很懂*/    fd_event_nocheck (EV_A_ fd, revents);}fd_event_nocheck 如下
inline_speed voidfd_event_nocheck (EV_P_ int fd, int revents){  ANFD *anfd = anfds + fd;  ev_io *w;  for (w = (ev_io *)anfd->head; w; w = (ev_io *)((WL)w)->next)//對fd上的監視器依次做檢測,    {      int ev = w->events & revents;//相應的事件被觸發了      if (ev)//pending條件滿足,監控器加入到pendings數組中pendings[pri]上的pendings[pri][old_lenght+1]的位置上
          ev_feed_event (EV_A_ (W)w, ev);    }}void noinlineev_feed_event (EV_P_ void *w, int revents) EV_THROW{  W w_ = (W)w;  int pri = ABSPRI (w_);  if (expect_false (w_->pending))    pendings [pri][w_->pending - 1].events |= revents;  else    {      w_->pending = ++pendingcnt [pri];      array_needsize (ANPENDING, pendings [pri], pendingmax [pri], w_->pending, EMPTY2);      pendings [pri][w_->pending - 1].w      = w_;      pendings [pri][w_->pending - 1].events = revents;    }  pendingpri = NUMPRI - 1;}

以epoll 為例,當epoll_wait 返回一個fd_event 時 ,我們就可以直接定位到對應fd 的 watch list ,這個watch list 的長度一般不會超過3 ,fd_event 會有一個導致觸發的事件 ,我們用這個事件依次和各個watch 注冊的 event 做 “&” 操作, 如果不為0 ,則把對應的watch 加入到 待處理隊列pendings中(當我們啟用watcher 優先級模式時,pendings 是個2維數組,此時僅考慮普通模式)

這里要介紹一個新的數據結構,他表示pending中的wather也就是監控條件滿足了,但是還沒有觸發動作的狀態。

typedef struct{  W w;  int events; /* the pending event set for the given watcher */} ANPENDING;

這里 W w應該知道是之前說的基類指針。pendings就是這個類型的一個二維數組數組。其以watcher的優先級(libev可以對watcher優先級進行設置,這里用一維數組下標來表示)為一級下標。再以該優先級上pengding的監控器數目為二級下標(例如在這個fd上的監控數目,加入有讀和寫,則二維數組的下標就是0和1),對應的監控器中的pending值就是該下標加一的結果。其定義為ANPENDING *pendings [NUMPRI]。同anfds一樣,二維數組的第二維 ANPENDING *是一個動態調整大小的數組。這樣操作之后。這個一系列的操作可以認為是fd_feed的后續操作,xxx_reify目的最后都是將pending的watcher加入到這個pengdings二維數組中。后續的幾個xxx_reify也是一樣,等分析到那個類型的監控器類型時在作展開。這里用個圖梳理下結構。

215034_LAfF_917596

最后在循環中執行宏EV_INVOKE_PENDING,其實是調用loop->invoke_cb,如果沒有自定義修改的話(一般不會修改)就是調用ev_invoke_pending。該函數會依次遍歷二維數組pendings,執行pending的每一個watcher上的觸發動作回調函數。

至此一次IO觸發過程就完成了。

5、總結下

在Libev中watcher要算最關鍵的數據結構了,整個邏輯都是圍繞著watcher做操作。Libev內部維護一個基類ev_wathcer和若干個特定監控器的派生類ev_xxx。在使用的時候首先生成一個特定watcher的實例。并通過該派生對象私有的成員設置其觸發條件。然后用anfds或者最小堆管理這些watchers。然后Libev通過backend_poll以及時間堆管理運算出pending的watcher。然后將他們加入到一個以優先級為一維下標的二維數組。在合適的時間依次調用這些pengding的watcher上注冊的觸發動作回調函數,這樣便可以按優先級先后順序實現“only-for-ordering”的優先級模型。

215211_W3Cy_917596

寫這篇博客主要是為了做一個學習記錄,里邊肯定會有很多錯誤。學習IO事件時,查閱了不少博文,這幾篇的幫組很大,多向大牛學習,文中也大量引用了他們博文中的圖片和例子,如有不妥,請告之

http://my.oschina.net/u/917596/blog/177030

https://cnodejs.org/topic/4f16442ccae1f4aa270010a3


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日日噜噜噜夜夜爽亚洲精品| 亚洲成人a级网| 国产欧美一区二区三区在线看| 欧洲成人在线视频| 日韩视频精品在线| 日本乱人伦a精品| 亚洲国产女人aaa毛片在线| 日韩大陆欧美高清视频区| 91国内产香蕉| 好吊成人免视频| 欧美在线不卡区| 国产成人亚洲综合91| 日本老师69xxx| 国产精品久久99久久| 欧美亚洲国产精品| 欧美激情视频免费观看| 久久久久久久久久久免费精品| 欧美日韩一区二区免费在线观看| 精品久久久国产| 欧美日韩xxxxx| 亚洲精品久久久久中文字幕欢迎你| 亚洲色图欧美制服丝袜另类第一页| 26uuu亚洲伊人春色| 国产精品成人v| 91精品久久久久久久久| 日韩av在线网址| 久久久久久久久久久免费| 欧美激情啊啊啊| 国产欧美日韩高清| 成人av资源在线播放| 久久视频在线视频| 欧美理论在线观看| 久久精品国产亚洲精品| 国产精品福利无圣光在线一区| 欧美成人合集magnet| 亚洲成人激情在线| 国产香蕉精品视频一区二区三区| 欧美国产日韩一区二区在线观看| 日韩精品在线视频美女| 亚洲一区二区三区四区视频| 欧美亚洲午夜视频在线观看| 国产精品h片在线播放| 91精品国产自产在线| 欧美日韩国产麻豆| 国产91色在线|| 91黑丝高跟在线| 欧美超级乱淫片喷水| 丝袜情趣国产精品| 国产亚洲欧洲在线| 综合网中文字幕| 国产视频在线一区二区| 国产精品久久久av| 国产一区二区日韩精品欧美精品| 成人在线视频网站| 亚洲男人天堂2023| 久久6精品影院| 社区色欧美激情 | 欧美日本在线视频中文字字幕| 尤物yw午夜国产精品视频明星| 亚洲乱码国产乱码精品精天堂| 国产丝袜一区视频在线观看| 亚洲深夜福利在线| 国产精品香蕉在线观看| 91精品国产777在线观看| 亚洲精品wwwww| 欧日韩在线观看| 国产精品xxxxx| 91精品在线影院| 在线播放国产一区中文字幕剧情欧美| 欧美孕妇与黑人孕交| 国产亚洲欧洲在线| 亚洲日本成人女熟在线观看| 欧美多人乱p欧美4p久久| 亚洲午夜未满十八勿入免费观看全集| 欧美在线视频一区| 国产日韩欧美91| 亚洲欧美日韩国产中文| 在线观看精品国产视频| 亚洲激情 国产| 亚洲www视频| 国产精品黄色av| 欧美丰满老妇厨房牲生活| 国产精品久久久亚洲| 欧美美女18p| 国产精品美女在线| 欧美丰满少妇xxxxx| 中文字幕日韩综合av| 国产亚洲欧美视频| 国产精品欧美日韩| 国产视频一区在线| 亚洲男人的天堂在线播放| 欧美国产视频一区二区| 国产精品美女www爽爽爽视频| 国产精品精品视频一区二区三区| 欧美激情视频一区二区| 欧美成人性生活| 国产成人中文字幕| 91亚洲va在线va天堂va国| 91在线免费网站| 欧美精品日韩三级| 国产精品美女www爽爽爽视频| 国产精品自拍偷拍| 日韩激情av在线免费观看| 欧美高清性猛交| yellow中文字幕久久| 成人黄色网免费| 欧美夫妻性视频| 久久国产精品偷| 国产成人精品综合久久久| 久久久久中文字幕| 久久影视电视剧免费网站| 久久伊人精品一区二区三区| 欧美体内谢she精2性欧美| 亚洲人成网站777色婷婷| 亚洲日韩中文字幕| 91网站在线免费观看| 色先锋久久影院av| 亚洲小视频在线观看| 亚洲免费高清视频| 欧美精品福利视频| 中文字幕亚洲欧美日韩在线不卡| 欧美日韩国产中文字幕| 日韩av中文字幕在线免费观看| 亚洲美腿欧美激情另类| 久久久久中文字幕| 国产成人精品久久二区二区| 久久久精品日本| 国产精品成人一区二区三区吃奶| 国产精品久久久| 欧美激情第6页| 国产脚交av在线一区二区| 日韩在线欧美在线国产在线| 热久久视久久精品18亚洲精品| 国产精品久久久久久久久久| 成人免费观看49www在线观看| 国产精品激情av在线播放| 国产精品久久久久久久电影| 精品国模在线视频| 日韩专区中文字幕| 国产综合久久久久| 不卡av在线播放| 国产精选久久久久久| 97av在线视频免费播放| www.欧美精品一二三区| 热草久综合在线| 国产精品夜间视频香蕉| 日韩av影院在线观看| 亚洲香蕉av在线一区二区三区| 日韩国产高清视频在线| 欧美激情中文字幕乱码免费| 精品久久久久久久中文字幕| 国产精品igao视频| 成人精品视频在线| 久久精品国产成人精品| 隔壁老王国产在线精品| 亚洲mm色国产网站| 欧美久久精品午夜青青大伊人| 国模视频一区二区三区| 色综合天天狠天天透天天伊人| 欧美视频专区一二在线观看| 国产v综合v亚洲欧美久久| 青草成人免费视频| 91中文字幕在线观看| 国产精品1区2区在线观看|