libev 中 ev_loop 結構體中的成員變數,libevev_loop
1、ev_loop是libev用來描述事件迴圈的結構體。在libev中的定義比較繞,這裡把它摘抄出來,做下注釋,方便學習。libev的定義如下
struct ev_loop{ ev_tstamp ev_rt_now; #define ev_rt_now ((loop)->ev_rt_now) #define VAR(name,decl) decl; #include "ev_vars.h" #undef VAR};#include "ev_wrap.h"static struct ev_loop default_loop_struct;EV_API_DECL struct ev_loop *ev_default_loop_ptr = 0;
ev_tstamp 是double類型,是一個時間事件的計數類型,這裡使用define來定義ev_rt_now為((loop)->ev_rt_now),程式裡可以直接使用ev_rt_now,後邊結構體的定義都是這樣,這可能是作者的一些編程技巧。
#define VAR(name,decl) decl; #define VARx(type,name) VAR(name, type name)
這兩行表示了一個意思,用type來定義name,就是等於 type name;如 int backend;相當於在結構體中定義了一個變數。具體變數在ev_vars.h標頭檔中定義。
ev_warp.h檔案的作用和#define ev_rt_now ((loop)->ev_rt_now) 相同。具體兩個檔案代碼如下:
#define VARx(type,name) VAR(name, type name)VARx(ev_tstamp, now_floor) /* last time we refreshed rt_time */VARx(ev_tstamp, mn_now) /* monotonic clock "now" */VARx(ev_tstamp, rtmn_diff) /* difference realtime - monotonic time *//* for reverse feeding of events */VARx(W *, rfeeds)VARx(int, rfeedmax)VARx(int, rfeedcnt)VAR (pendings, ANPENDING *pendings [NUMPRI])VAR (pendingmax, int pendingmax [NUMPRI])VAR (pendingcnt, int pendingcnt [NUMPRI])VARx(int, pendingpri) /* highest priority currently pending */VARx(ev_prepare, pending_w) /* dummy pending watcher */VARx(ev_tstamp, io_blocktime)VARx(ev_tstamp, timeout_blocktime)VARx(int, backend)VARx(int, activecnt) /* total number of active events ("refcount") */VARx(EV_ATOMIC_T, loop_done) /* signal by ev_break */VARx(int, backend_fd)VARx(ev_tstamp, backend_mintime) /* assumed typical timer resolution */VAR (backend_modify, void (*backend_modify)(EV_P_ int fd, int oev, int nev))VAR (backend_poll , void (*backend_poll)(EV_P_ ev_tstamp timeout))VARx(ANFD *, anfds)VARx(int, anfdmax)VAR (evpipe, int evpipe [2])VARx(ev_io, pipe_w)VARx(EV_ATOMIC_T, pipe_write_wanted)VARx(EV_ATOMIC_T, pipe_write_skipped)#if !defined(_WIN32) || EV_GENWRAPVARx(pid_t, curpid)#endifVARx(char, postfork) /* true if we need to recreate kernel state after fork */#if EV_USE_SELECT || EV_GENWRAPVARx(void *, vec_ri)VARx(void *, vec_ro)VARx(void *, vec_wi)VARx(void *, vec_wo)#if defined(_WIN32) || EV_GENWRAPVARx(void *, vec_eo)#endifVARx(int, vec_max)#endif#if EV_USE_POLL || EV_GENWRAPVARx(struct pollfd *, polls)VARx(int, pollmax)VARx(int, pollcnt)VARx(int *, pollidxs) /* maps fds into structure indices */VARx(int, pollidxmax)#endif#if EV_USE_EPOLL || EV_GENWRAPVARx(struct epoll_event *, epoll_events)VARx(int, epoll_eventmax)VARx(int *, epoll_eperms)VARx(int, epoll_epermcnt)VARx(int, epoll_epermmax)#endif#if EV_USE_KQUEUE || EV_GENWRAPVARx(pid_t, kqueue_fd_pid)VARx(struct kevent *, kqueue_changes)VARx(int, kqueue_changemax)VARx(int, kqueue_changecnt)VARx(struct kevent *, kqueue_events)VARx(int, kqueue_eventmax)#endif#if EV_USE_PORT || EV_GENWRAPVARx(struct port_event *, port_events)VARx(int, port_eventmax)#endif#if EV_USE_IOCP || EV_GENWRAPVARx(HANDLE, iocp)#endifVARx(int *, fdchanges)VARx(int, fdchangemax)VARx(int, fdchangecnt)VARx(ANHE *, timers)VARx(int, timermax)VARx(int, timercnt)#if EV_PERIODIC_ENABLE || EV_GENWRAPVARx(ANHE *, periodics)VARx(int, periodicmax)VARx(int, periodiccnt)#endif#if EV_IDLE_ENABLE || EV_GENWRAPVAR (idles, ev_idle **idles [NUMPRI])VAR (idlemax, int idlemax [NUMPRI])VAR (idlecnt, int idlecnt [NUMPRI])#endifVARx(int, idleall) /* total number */VARx(struct ev_prepare **, prepares)VARx(int, preparemax)VARx(int, preparecnt)VARx(struct ev_check **, checks)VARx(int, checkmax)VARx(int, checkcnt)#if EV_FORK_ENABLE || EV_GENWRAPVARx(struct ev_fork **, forks)VARx(int, forkmax)VARx(int, forkcnt)#endif#if EV_CLEANUP_ENABLE || EV_GENWRAPVARx(struct ev_cleanup **, cleanups)VARx(int, cleanupmax)VARx(int, cleanupcnt)#endif#if EV_ASYNC_ENABLE || EV_GENWRAPVARx(EV_ATOMIC_T, async_pending)VARx(struct ev_async **, asyncs)VARx(int, asyncmax)VARx(int, asynccnt)#endif#if EV_USE_INOTIFY || EV_GENWRAPVARx(int, fs_fd)VARx(ev_io, fs_w)VARx(char, fs_2625) /* whether we are running in linux 2.6.25 or newer */VAR (fs_hash, ANFS fs_hash [EV_INOTIFY_HASHSIZE])#endifVARx(EV_ATOMIC_T, sig_pending)#if EV_USE_SIGNALFD || EV_GENWRAPVARx(int, sigfd)VARx(ev_io, sigfd_w)VARx(sigset_t, sigfd_set)#endifVARx(unsigned int, origflags) /* original loop flags */#if EV_FEATURE_API || EV_GENWRAPVARx(unsigned int, loop_count) /* total number of loop iterations/blocks */VARx(unsigned int, loop_depth) /* #ev_run enters - #ev_run leaves */VARx(void *, userdata)/* C++ doesn't support the ev_loop_callback typedef here. stinks. */VAR (release_cb, void (*release_cb)(EV_P) EV_THROW)VAR (acquire_cb, void (*acquire_cb)(EV_P) EV_THROW)VAR (invoke_cb , ev_loop_callback invoke_cb)#endif#undef VARx
#define acquire_cb ((loop)->acquire_cb)#define activecnt ((loop)->activecnt)#define anfdmax ((loop)->anfdmax)#define anfds ((loop)->anfds)#define async_pending ((loop)->async_pending)#define asynccnt ((loop)->asynccnt)#define asyncmax ((loop)->asyncmax)#define asyncs ((loop)->asyncs)#define backend ((loop)->backend)#define backend_fd ((loop)->backend_fd)#define backend_mintime ((loop)->backend_mintime)#define backend_modify ((loop)->backend_modify)#define backend_poll ((loop)->backend_poll)#define checkcnt ((loop)->checkcnt)#define checkmax ((loop)->checkmax)#define checks ((loop)->checks)#define cleanupcnt ((loop)->cleanupcnt)#define cleanupmax ((loop)->cleanupmax)#define cleanups ((loop)->cleanups)#define curpid ((loop)->curpid)#define epoll_epermcnt ((loop)->epoll_epermcnt)#define epoll_epermmax ((loop)->epoll_epermmax)#define epoll_eperms ((loop)->epoll_eperms)#define epoll_eventmax ((loop)->epoll_eventmax)#define epoll_events ((loop)->epoll_events)#define evpipe ((loop)->evpipe)#define fdchangecnt ((loop)->fdchangecnt)#define fdchangemax ((loop)->fdchangemax)#define fdchanges ((loop)->fdchanges)#define forkcnt ((loop)->forkcnt)#define forkmax ((loop)->forkmax)#define forks ((loop)->forks)#define fs_2625 ((loop)->fs_2625)#define fs_fd ((loop)->fs_fd)#define fs_hash ((loop)->fs_hash)#define fs_w ((loop)->fs_w)#define idleall ((loop)->idleall)#define idlecnt ((loop)->idlecnt)#define idlemax ((loop)->idlemax)#define idles ((loop)->idles)#define invoke_cb ((loop)->invoke_cb)#define io_blocktime ((loop)->io_blocktime)#define iocp ((loop)->iocp)#define kqueue_changecnt ((loop)->kqueue_changecnt)#define kqueue_changemax ((loop)->kqueue_changemax)#define kqueue_changes ((loop)->kqueue_changes)#define kqueue_eventmax ((loop)->kqueue_eventmax)#define kqueue_events ((loop)->kqueue_events)#define kqueue_fd_pid ((loop)->kqueue_fd_pid)#define loop_count ((loop)->loop_count)#define loop_depth ((loop)->loop_depth)#define loop_done ((loop)->loop_done)#define mn_now ((loop)->mn_now)#define now_floor ((loop)->now_floor)#define origflags ((loop)->origflags)#define pending_w ((loop)->pending_w)#define pendingcnt ((loop)->pendingcnt)#define pendingmax ((loop)->pendingmax)#define pendingpri ((loop)->pendingpri)#define pendings ((loop)->pendings)#define periodiccnt ((loop)->periodiccnt)#define periodicmax ((loop)->periodicmax)#define periodics ((loop)->periodics)#define pipe_w ((loop)->pipe_w)#define pipe_write_skipped ((loop)->pipe_write_skipped)#define pipe_write_wanted ((loop)->pipe_write_wanted)#define pollcnt ((loop)->pollcnt)#define pollidxmax ((loop)->pollidxmax)#define pollidxs ((loop)->pollidxs)#define pollmax ((loop)->pollmax)#define polls ((loop)->polls)#define port_eventmax ((loop)->port_eventmax)#define port_events ((loop)->port_events)#define postfork ((loop)->postfork)#define preparecnt ((loop)->preparecnt)#define preparemax ((loop)->preparemax)#define prepares ((loop)->prepares)#define release_cb ((loop)->release_cb)#define rfeedcnt ((loop)->rfeedcnt)#define rfeedmax ((loop)->rfeedmax)#define rfeeds ((loop)->rfeeds)#define rtmn_diff ((loop)->rtmn_diff)#define sig_pending ((loop)->sig_pending)#define sigfd ((loop)->sigfd)#define sigfd_set ((loop)->sigfd_set)#define sigfd_w ((loop)->sigfd_w)#define timeout_blocktime ((loop)->timeout_blocktime)#define timercnt ((loop)->timercnt)#define timermax ((loop)->timermax)#define timers ((loop)->timers)#define userdata ((loop)->userdata)#define vec_eo ((loop)->vec_eo)#define vec_max ((loop)->vec_max)#define vec_ri ((loop)->vec_ri)#define vec_ro ((loop)->vec_ro)#define vec_wi ((loop)->vec_wi)#define vec_wo ((loop)->vec_wo)
幾種經典的網路伺服器架構模型的分析與比較
相比於傳統的網路編程方式,事件驅動能夠極大的降低資源佔用,增大服務接待能力,並提高網路傳輸效率。 關於本文提及的伺服器模型,搜尋網路可以查閱到很多的實現代碼,所以,本文將不拘泥於原始碼的陳列與分析,而側重模型的介紹和比較。使用 libev 事件驅動庫的伺服器模型將給出實現代碼。 本文涉及到線程/時間圖例,只為表明線程在各個 IO 上確實存在阻塞時延,但並不保證時延比例的正確性和 IO 執行先後的正確性;另外,本文所提及到的介面也只是筆者熟悉的 Unix/Linux 介面,並未推薦 Windows 介面,讀者可以自行查閱對應的 Windows 介面。 阻塞型的網路編程介面 幾乎所有的程式員第一次接觸到的網路編程都是從 listen()、send()、recv() 等介面開始的。使用這些介面可以很方便的構建伺服器/客戶機的模型。 我們假設希望建立一個簡單的伺服器程式,實現向單個客戶機提供類似於“一問一答”的內容服務。 圖1. 簡單的一問一答的伺服器/客戶機模型 我們注意到,大部分的 socket 介面都是阻塞型的。所謂阻塞型介面是指系統調用(一般是 IO 介面)不返回調用結果並讓當前線程一直阻塞,只有當該系統調用獲得結果或者逾時出錯時才返回。 實際上,除非特別指定,幾乎所有的 IO 介面(包括 socket 介面)都是阻塞型的。這給網路編程帶來了一個很大的問題,如在調用 send() 的同時,線程將被阻塞,在此期間,線程將無法執行任何運算或響應任何的網路請求。這給多客戶機、多商務邏輯的網路編程帶來了挑戰。這時,很多程式員可能會 選擇多線程的方式來解決這個問題。 多線程伺服器程式 應對多客戶機的網路應用,最簡單的解決方式是在伺服器端使用多線程(或多進程)。多線程(或多進程)的目的是讓每個串連都擁有獨立的線程(或進程),這樣任何一個串連的阻塞都不會影響其他的串連。 具體使用多進程還是多線程,並沒有一個特定的模式。傳統意義上,進程的開銷要遠遠大於線程,所以,如果需要同時為較多的客戶機提供服務,則不推 薦使用多進程;如果單個服務執行體需要消耗較多的 CPU 資源,譬如需要進行大規模或長時間的資料運算或檔案訪問,則進程較為安全。通常,使用 pthread_create () 建立新線程,fork() 建立新進程。 我們假設對上述的伺服器/客戶機模型,提出更高的要求,即讓伺服器同時為多個客戶機提供一問一答的服務。於是有了如下的模型。 圖2. 多線程伺服器模型 在上述的線程 / 時間圖例中,主線程持續等待用戶端的串連請求,如果有串連,則建立新線程,並在新線程中提供為前例同樣的問答服務。 很多初學者可能不明白為何一個 socket 可以 accept 多次。實際上,socket 的設計者可能特意為多客戶機的情況留下了伏筆,讓 accept() 能夠返回一個新的 socket。下面是 accept 介面的原型: int accept(int s, struct sockaddr *addr, socklen_t *addrlen); 輸入參數 s 是從 socket(),bind() 和 listen() 中沿用下來的 socket 控制代碼值。執行完 bind() 和 listen() 後,作業系統已經開始在指定的連接埠處監聽所有的串連請求,如果有請求,則將該串連請求加入請求隊列。調用 accept() 介面正是從 socket s 的請求隊列抽取第一個串連資訊,建立一個與 s 同類的新的 socket 返回控制代碼。新的 so......餘下全文>>
幾種經典的網路伺服器架構模型的分析與比較
相比於傳統的網路編程方式,事件驅動能夠極大的降低資源佔用,增大服務接待能力,並提高網路傳輸效率。關於本文提及的伺服器模型,搜尋網路可以查閱到很多的實現代碼,所以,本文將不拘泥於原始碼的陳列與分析,而側重模型的介紹和比較。使用 libev 事件驅動庫的伺服器模型將給出實現代碼。本文涉及到線程 / 時間圖例,只為表明線程在各個 IO 上確實存在阻塞時延,但並不保證時延比例的正確性和 IO 執行先後的正確性;另外,本文所提及到的介面也只是筆者熟悉的 Unix/Linux 介面,並未推薦 Windows 介面,讀者可以自行查閱對應的 Windows 介面。阻塞型的網路編程介面幾乎所有的程式員第一次接觸到的網路編程都是從 listen()、send()、recv()等介面開始的。使用這些介面可以很方便的構建伺服器 /客戶機的模型。我們假設希望建立一個簡單的伺服器程式,實現向單個客戶機提供類似於“一問一答”的內容服務。圖1. 簡單的一問一答的伺服器 /客戶機模型我們注意到,大部分的 socket介面都是阻塞型的。所謂阻塞型介面是指系統調用(一般是 IO介面)不返回調用結果並讓當前線程一直阻塞,只有當該系統調用獲得結果或者逾時出錯時才返回。實際上,除非特別指定,幾乎所有的 IO介面 (包括 socket 介面 )都是阻塞型的。這給網路編程帶來了一個很大的問題,如在調用 send()的同時,線程將被阻塞,在此期間,線程將無法執行任何運算或響應任何的網路請求。這給多客戶機、多商務邏輯的網路編程帶來了挑戰。這時,很多程式員可能會選擇多線程的方式來解決這個問題。多線程伺服器程式應對多客戶機的網路應用,最簡單的解決方式是在伺服器端使用多線程(或多進程)。多線程(或多進程)的目的是讓每個串連都擁有獨立的線程(或進程),這樣任何一個串連的阻塞都不會影響其他的串連。具體使用多進程還是多線程,並沒有一個特定的模式。傳統意義上,進程的開銷要遠遠大於線程,所以,如果需要同時為較多的客戶機提供服務,則不推薦使用多進 程;如果單個服務執行體需要消耗較多的 CPU 資源,譬如需要進行大規模或長時間的資料運算或檔案訪問,則進程較為安全。通常,使用 pthread_create () 建立新線程,fork() 建立新進程。我們假設對上述的伺服器 / 客戶機模型,提出更高的要求,即讓伺服器同時為多個客戶機提供一問一答的服務。於是有了如下的模型。圖2. 多線程伺服器模型 在上述的線程 / 時間圖例中,主線程持續等待用戶端的串連請求,如果有串連,則建立新線程,並在新線程中提供為前例同樣的問答服務。很多初學者可能不明白為何一個 socket 可以 accept 多次。實際上,socket 的設計者可能特意為多客戶機的情況留下了伏筆,讓 accept() 能夠返回一個新的 socket。下面是 accept 介面的原型: int accept(int s, struct sockaddr *addr, socklen_t *addrlen); 輸入參數 s 是從 socket(),bind() 和 listen() 中沿用下來的 socket 控制代碼值。執行完 bind() 和 listen() 後,作業系統已經開始在指定的連接埠處監聽所有的串連請求,如果有請求,則將該串連請求加入請求隊列。調用 accept() 介面正是從 socket s 的請求隊列抽取第一個串連資訊,建立一個與 s 同類的新的 socket 返回控制代碼。新的 socket 控制代碼即是後續......餘下全文>>