uloop是提供事件驅動機制接口,類似libevent事件框架,基於epoll接口來實現的。
uloop三大功能:事件管理(uloop_fd)、超時管理(uloop_timeout)和進程管理(uloop_process),定義在uloop.h中。
1. 整體框架
1: /** 2: * 初始化事件循環 3: *主要工作是poll_fd = epoll_create(32);/* 創建一個epoll的文件描述符監控句柄。最多監控32個文件描述符 4: **/ 5: int uloop_init(void) 6: { 7: if (poll_fd >= 0) 8: return 0; 9: 10: poll_fd = epoll_create(32);/* 創建一個epoll的句柄。最多監控32個文件描述符 */ 11: if (poll_fd < 0) 12: return -1; 13: 14: fcntl(poll_fd, F_SETFD, fcntl(poll_fd, F_GETFD) | FD_CLOEXEC); /* fd_cloexecs */ 15: return 0; 16: } 17: 18: 19: /** 20: * 事件循環主處理入口 21: *1.當某一個進程第一次調用uloop_run時,注冊sigchld和sigint信號 22: *2.循環獲取當前時間,把超時的timeout處理掉,有一條timeout鏈表在維護 23: *3.循環檢測是否收到一個sigchld信號,如果收到,刪除對應的子進程,有一條process子進程鏈表在維護 24: *4.循環調用epoll_wait 監相應的觸發事件文件描述符fd 25: **/ 26: void uloop_run(void) 27: { 28: static int recursive_calls = 0; /* static value */ 29: struct timeval tv; 30: 31: /* 32: * Handlers are only updated for the first call to uloop_run() (and restored 33: * when this call is done). 34: */ 35: if (!recursive_calls++) /* 第一次運行uloop_run時調用, 注冊信號處理函數 */ 36: uloop_setup_signals(true); 37: 38: uloop_cancelled = false; 39: while(!uloop_cancelled) 40: { 41: uloop_gettime(&tv); /* 獲取當前時間 */ 42: uloop_process_timeouts(&tv); /* 把超時的timeout清理掉 */ 43: if (uloop_cancelled) 44: break; 45: 46: if (do_sigchld) /* 收到一個sigchld的信號 */ 47: uloop_handle_processes(); /* 銷毀該進程的uloop_process */ 48: uloop_gettime(&tv); 49: uloop_run_events(uloop_get_next_timeout(&tv));/* 處理相應的觸發事件fd */ 50: } 51: 52: if (!--recursive_calls) 53: uloop_setup_signals(false); 54: } 55: 56: 57: /** 58: * 銷毀事件循環 59: * 關閉epoll描述符 60: * 銷毀子進程鏈表 61: * 銷毀timeout鏈表 62: **/ 63: void uloop_done(void) 64: { 65: if (poll_fd < 0) 66: return; 67: 68: close(poll_fd); 69: poll_fd = -1; 70: 71: uloop_clear_timeouts(); 72: uloop_clear_processes(); 73: }
// 設置uloop內部結束循環標志 static inline void uloop_end(void) { uloop_cancelled = true; }
int uloop_init(void);
void uloop_run(void);
void uloop_done(void);
2. uloop_fd
uloop是一個I/O循環調度,將不同文件描述符添加到輪詢中。
文件描述符fd的管理由uloop_fd結構來設置。僅需設置fd和事件發生時的回調函數,數據結構的其他部分供內部使用。
默認采用非阻塞和水平觸發。
#define ULOOP_READ (1 << 0) #define ULOOP_WRITE (1 << 1) #define ULOOP_EDGE_TRIGGER (1 << 2) #define ULOOP_BLOCKING (1 << 3) #define ULOOP_EVENT_MASK (ULOOP_READ | ULOOP_WRITE) /* internal flags */ #define ULOOP_EVENT_BUFFERED (1 << 4) #ifdef USE_KQUEUE #define ULOOP_EDGE_DEFER (1 << 5) #endif #define ULOOP_ERROR_CB (1 << 6) struct uloop_fd { uloop_fd_handler cb; int fd; bool eof; bool error; bool registered; uint8_t flags; }; int uloop_fd_add(struct uloop_fd *sock, unsigned int flags); int uloop_fd_delete(struct uloop_fd *sock);
typedef void (*uloop_fd_handler)(struct uloop_fd *u, unsigned int events);
3. uloop_timeout
超時管理部分由uloop_timeout結構來管理,在定時時間到了之后調用回調函數,定時時間單位為毫秒。
uloop定時器是一次性定時器,超時后會自動刪除。
libubox使用一個全局排序鏈表(按照超時時間升序排列)存儲定時器節點。
注:uloop將定時器節點按照絕對時間升序排隊,每次uloop循環先處理已超時的定時器,然后取定時器隊列首節點(即最近一個將要超時的定時器節點),減去當前時間得到下次將要超時的相對時間;
然后用這個相對時間作為超時時間調用epoll_wait。
struct uloop_timeout { struct list_head list; bool pending; //是否已經加入超時鏈表等待調度
uloop_timeout_handler cb;
struct timeval time;
};
int uloop_timeout_add(struct uloop_timeout *timeout);
int uloop_timeout_set(struct uloop_timeout *timeout, int msecs);
int uloop_timeout_cancel(struct uloop_timeout *timeout);
int uloop_timeout_remaining(struct uloop_timeout *timeout);
typedef void (*uloop_timeout_handler)(struct uloop_timeout *t)
uloop_timeout_add()添加定時器,要求已初始化timeout結構,應避免直接使用uloop_timeout_add()。
uloop_timeout_set()設定定時器超時事件為當前時間+指定超時時間(msecs)。內部封裝了uloop_timeout_add(),應調用本函數添加定時器。
4. uloop_process
當前進程的子進程管理。建立一個鏈表,按進程號升序方式管理所有進程id。
uloop進程管理是一次性任務,觸發后會自動刪除。
struct uloop_process { struct list_head list; bool pending; //是否已經加入任務鏈表等待調度 uloop_process_handler cb; pid_t pid; }; int uloop_process_add(struct uloop_process *p); int uloop_process_delete(struct uloop_process *p);
typedef void (*uloop_process_handler)(struct uloop_process *c, int ret)