libev學習(一)


一.libev簡介

  Libev是一個事件循環:你注冊感興趣的特定事件(比如一個文件可以讀取時或者發生超時時),它將管理這些事件源,將這些事件反饋給你的程序。為了實現這些,至少要在你的進程(或線程)中執行事件循環句柄控制,然后就能通過回調機制進行事件通信。你通過所謂的watchers注冊感興趣的特定事件,這些watchers都是相對較小的C語言結構體,它們通過初始化具體的事件得到,然后交由libev啟動那個watcher

     Libev支持 select,poll,Linux特有的epoll,BSD特有的kqueue以及Solaris特有的文件描述符事件端口機制(ev_io),Linux信息通知接口(ev_stat),Linux事件文件/信號文件(為了更快更完整的喚醒沉睡線程(ev_async/信號捕捉(ev_signal))相對定時器(ev_timer),用戶自定義的絕對定時器(ev_periodic),同步信號(ev_signal),進程狀態改變事件(ev_child),和通過事件循環機制實現的事件觀察者管理本身(ev_idle,ev_embed,ev_prepareev_check監控)也和文件監控(ev_stat)和有限支持的派生子進程事件(ev_fork)一樣。

 

二.libev特點

  1.關於時間:時間類型為ev_tstampLibev描述時間采用一個浮點數,它來自於距離(POSIX)時期的一個(帶小數)的秒數)。

  2.錯誤處理Libev 知道三類錯誤:操作系統錯誤、用法錯誤和內部錯誤(bugs) 

當 libev 捕捉到無法處理的操作系統錯誤時(如一個系統調用返回了libev不能識別的返回值),它通過調用 ev_set_syserr_cb設定的回調函數,它假設這個回調函數可修正這個問題或者是退出。默認行為是打印一個摘要信息並調用 abort()

當 libev 檢測到用法錯誤(如負的時間),它會打印一條摘要信息並退出(使用 assert 機制,因此宏 NDEBUG 會關閉這種檢測); 表明這里有libev調用端的編程錯誤需要修正。

Libev也有一些內部錯誤檢查,也有大多數的代碼錯誤檢查。這些在正常情況下不會觸發,他們表明在libev存在一個bug或者更大的錯誤。

 

3.全局函數(這些函數可以在任何時候被調用,甚至可以在初始化libev庫之前調用

  ev_tstamp ev_time ()             //返回libev使用它時的當前時間。請注意,ev_now函數通常更快,也經常用來返回你想知道的時間戳。ev_now_update和ev_now組合在一起使用更好

  ev_sleep (ev_tstamp interval)   //休眠指定的時間間隔:當前線程將被阻塞直到它被中斷或者給定的時間間隔已經過去。當interval <= 0.時將立即返回.基本上這是一個精度稍低的sleep()函數.Interval的值是有限制的-libev只保證休眠時間最長為一天(interval <= 86400 

       int ev_version_major ()

       int ev_version_minor ()  //通過調用ev_version_major 和ev_version_minor函數,你可以找出主要的和次要的ABI鏈接庫版本號。如果需要,你可以比較全局字段EV_VERSION_MAJOR 和 EV_VERSION_MINOR,它們表明了你的程序編譯完成后的庫版本

  ev_set_allocator (void *(*cb)(void *ptr, long size))  //這是申請或釋放內存的,如果申請內存是返回0,當內存需要被分配(大小!= 0),庫可能中止或采取一些潛在的破壞性的行動

     例:

  static void * persistent_realloc (void *ptr, size_t size) { for (;;) { void *newptr = realloc (ptr, size); if (newptr) return newptr; sleep (60); } } ... ev_set_allocator (persistent_realloc);
View Code


     ev_set_syserr_cb (void (*cb)(const char *msg))   //設置發生回系統錯誤時的調函數

  例:    

 static void fatal_error (const char *msg) { perror (msg); abort (); } ... ev_set_syserr_cb (fatal_error);
View Code

  ev_feed_signal (int signum) //這個函數可以用來“模擬”一個信號接收。它是完全安全的調用這個函數在任何時間,從任何上下文,包括信號處理程序或隨機線程

   

  4.該庫知道兩種類型的循環,默認的循環(支持子進程事件)和動態創建事件循環

 

         struct ev_loop *ev_default_loop (unsigned int flags)

 常用:ev_default_loop (EVBACKEND_POLL | EVBACKEND_SELECT | EVFLAG_NOENV); //限制測試程序的select、poll的后端,不允許環境設置要考慮

  struct ev_loop *ev_loop_new (unsigned int flags)   //這個函數是線程安全的,常用方式:使用ev_loop_new創建一個時間循環在每個線程中,在主線程中使用默認事件循環。   The flags argumentis usually specified as 0 (or EVFLAG_AUTO).

  flags argumentis

EVFLAG_AUTO:默認值,常用

    • EVFLAG_NOENV:指定 libev 不使用LIBEV_FLAGS環境變量。常用於調試和測試

    • EVFLAG_FORKCHECK:與ev_loop_fork()相關,本文暫略

    • EVFLAG_NOINOTIFY:在ev_stat監聽中使用inotify API

    • EVFLAG_SIGNALFD:在ev_signal監聽中使用signalfd API

    • EVFLAG_NOSIGMASK:使 libev 避免修改 signal mask。這樣的話,你要使 signal 是非阻塞的。在未來的 libev 中,這個 mask 將會是默認值。

    • EVBACKEND_SELECT:通用后端

    • EVBACKEND_POLL:除了 Windows 之外的所有后端都可以用

    • EVBACKEND_EPOLL:Linux 后端

    • EVBACKEND_KQUEUE:大多數 BSD 的后端

    • EVBACKEND_DEVPOLL:Solaris 8 后端

    • EVBACKEND_PORT:Solaris 10 后端

         ev_loop_destroy (loop)    //Destroys an event loop object

     ev_loop_fork (loop)

         int  ev_is_default_loop (loop)   //判斷是否是默認事件循環,是返回ture

        unsigned int ev_iteration (loop)      //返回當前的 loop 的迭代數。等於 libev pool 新事件的數量(?)。這個值對應ev_prepareev_check調用,並在 prepare 和 check 之間增一

       unsigned int ev_depth (loop)   //返回ev_run()進入減去退出次數的差值

unsigned int ev_backend (struct ev_loop *loop);

返回EVBACKEND_*

 ev_tstamp ev_now (loop)

得到當前的“event loop time”。在 callback 調用期間,這個值是不變的。

 void ev_new_update (loop)

更新從ev_now()中返回的時間。不必要的話,不要使用,因為這個函數的開銷相對是比較大的。

 void ev_suspend (struct ev_loop *loop);  void ev_resume (struct ev_loop *loop);

暫停當前的 loop,使其刮起當前的所有工作。同時其 timeout 也會暫停。如果恢復后,timer 會從上一次暫停狀態繼續及時——這一點對於實現一些要連同時間也一起凍結的功能時,非常有用。

注意已經 resume 的loop不能再 resume,反之已經 suspend 的 loop 不能再 suspend。

 bool ev_run (struct ev_loop *loop, int flags);

初始化 loop 結束后,調用這個函數開始 loop。如果 flags == 0,直至 loop 沒有活躍的時間或者是調用了 ev_bread 之后停止。

Loop 可以是異常使能的,你可以在 callback 中調用longjmp來終端回調並且跳出 ev_run,或者通過拋出 C++ 異常。這些不會導致 ev_depth 值減少。

EVRUN_NOWAIT會檢查並且執行所有未解決的 events,但如果沒有就緒的時間,ev_run 會立刻返回。EVRUN_ONCE會檢查所有的 events,在至少每一個 event 都執行了一次事件迭代之后才返回。但有時候,使用ev_prepare/ev_check更好。

以下是ev_run的大致工作流程:

    • loop depth ++

    • 重設ev_break狀態

    • 在首次迭代之前,調用所有 pending watchers

LOOP:

    • 如果置了EVFLAG_FORKCHECK,則檢查 fork,如果檢測到 fork,則排隊並調用所有的 fork watchers

    • 排隊並且調用所有 ready 的watchers

    • 如果ev_break被調用了,則直接跳轉至 FINISH

    • 如果檢測到了 fork,則分離並且重建 kernel state

    • 使用所有未解決的變化更新 kernel state

    • 更新ev_now的值

    • 計算要 sleep 或 block 多久

    • 如果指定了的話,sleep

    • loop iteration ++

    • 阻塞以等待事件

    • 排隊所有未處理的I/O事件

    • 更新ev_now的值,執行 time jump 調整

    • 排隊所有超時事件

    • 排隊所有定期事件

    • 排隊所有優先級高於 pending 事件的 idle watchers

    • 排隊所有 check watchers

    • 按照上述順序的逆序,調用 watchers (check watchers -> idle watchers -> 定期事件 -> 計時器超時事件 -> fd事件)。信號和 child watchers 視為 fd watchers。

    • 如果ev_break被調用了,或者使用了EVRUN_ONCE或者EVRUN_NOWAIT,則如果沒有活躍的 watchers,則 FINISH,否則 continue

FINISH:

    • 如果是EVBREAK_ONE,則重設 ev_break 狀態

    • loop depth --

    • return

 void ev_break (struct ev_loop *loop, how);

中斷 loop。參數可以是 EVBREAK_ONE(執行完一個內部調用后返回)或EVBREAK_ALL(執行完所有)。

下一次調用 ev_run 的時候,相應的標志會清除

 void ev_ref (struct ev_loop *loop);  void ev_unref (struct ev_loop *loop);

類似於 Objective-C 中的引用計數,只要 reference count 不為0,ev_run 函數就不會返回。

在做 start 之后要 unref;stop 之前要 ref。

 void ev_set_io_collect_interval (struct ev_loop *loop, ev_tstamp interval);  void ev_set_timeout_collect_interval (struct ev_loop *loop, ev_tstamp interval);

兩個值均默認為0,表示盡量以最小的延遲調用 callback。但這是理想的情況,實際上,比如 select 這樣低效的系統調用,由於可以一次性讀取很多,所以可以適當地進行延時。通過使用比較高的延遲,但是增加每次處理的數據量,以提高 CPU 效率。

 void ev_invoke_pending (struct ev_loop *loop);

調用所有的 pending 的 watchers。這個除了可以在 callback 中調用(少見)之外,更多的是在重載的函數中使用。參見下一個函數

 void ev_set_invoke_pending_cb (struct ev_loop *loop, void (*invoke_pending_cb(EV_P)));

重載 ev_loop 調用 watchers 的函數。新的回調應調用 ev_invoke_pending。如果要恢復默認值,則置喙 ev_invoke_pending 即可。

 int ev_pending_count (struct ev_loop *loop);

返回當前有多少個 pending 的 watchers。

 void ev_set_loop_release_cb (struct ev_loop *loop, void (*release)(EV_P)throw(), void (*acquire)(EV_P)throw());

這是一個 lock 操作,你可以自定義 lock。其中 release 是 unlock,acquire 是 lock。release 是在 loop 掛起以等待events 之前調用,並且在開始回調之前調用 acquire。

 void ev_set_userdata (struct ev_loop *loop, void *data);  void *ev_userdata (struct ev_loop *loop);

設置 / 讀取 loop 中的用戶 data。這一點和 libevent 很不同,libevent 的參數 / 用戶數據是以 event 為單位的,而 libev 的原生用戶數據是以 loop 為單位的。

 void ev_verify (struct ev_loop *loop);

驗證當前 loop 的設置。如果發現問題,則打印 error msg 並 abort()

 

 

  ps:  來自libev手冊

https://my.oschina.net/haopengstack/blog/511585


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM