libuv事件迴圈

sherlock_lin發表於2021-02-03

1、說明

事件迴圈是 libuv 的核心功能,負責 IO 的輪詢和事件回撥的排程。

2、資料型別

2.1、uv_loop_t

事件迴圈資料型別,結構體

uv_loop_t.data 用於傳遞使用者資料,libuv 不會觸碰

2.2、uv_walk_cb

傳遞給 uv_walk() 方法的回撥函式型別

void (*uv_walk_cb)(uv_handle_t* handle, void* arg);

3、API

3.1、uv_loop_init

int uv_loop_init(uv_loop_t* loop);

初始化 uv_loop_t 結構體

要注意,呼叫之前需要先給 uv_loop_t 分配記憶體資源,否則會崩潰

3.2、uv_loop_configure

int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...);

設定事件迴圈配置,一般應該在第一次呼叫 uv_run() 之前執行

返回值: 0表示成功,錯誤則返回一個 UV_E 錯誤碼,UV_ENOSYS 表示平臺不支援該事件迴圈配置

支援選項:

  • UV_LOOP_BLOCK_SIGNAL :輪詢新事件時阻塞指定事件,uv_loop_configure 的第二個引數是訊號編號;
  • UV_METRICS_IDLE_TIME :在事件的提供者的事件迴圈中收集空閒時間,使用 uv_metrics_idle_time() 方法需要使用這個選項

3.3、uv_loop_close

int uv_loop_close(uv_loop_t* loop);

釋放所有內部迴圈資源

僅當所有迴圈完成並且所有開啟的控制程式碼和請求都已經關閉的時候才可呼叫此函式,否則將返回 UV_EBUSY

此函式返回後,使用者可以釋放為迴圈申請的記憶體

3.4、uv_default_loop

uv_loop_t* uv_default_loop(void);

返回 libuv 的預設事件迴圈,如果分配失敗了,可能返回NULL

這個方法是在整個應用程式中進行全域性迴圈的一個便捷的方式,和自定義的事件迴圈相同

3.5、uv_run

int uv_run(uv_loop_t* loop, uv_run_mode mode);

執行事件迴圈,mode 指定執行模式,有如下幾種:

  • UV_RUN_DEFAULT :執行事件迴圈,知道沒有活動的和被引用的控制程式碼和請求。如果使用 uv_stop() 方法終止還未停止的迴圈(仍然有活動的和被引用的控制程式碼和請求),則返回值非0,其他情況下返回0;
  • UV_RUN_ONCE :只進行一次 IO 口輪詢,如果沒有需要執行的回撥函式,這個方法會阻塞,返回0表示完成(沒有活動的和被引用的控制程式碼和請求),返回非0表示需要進行更多的回撥(此時,需要再次進行事件迴圈);
  • UV_RUN_NOWAIT :和 UV_RUN_ONCE 的區別是,不會阻塞;

uv_run() 方法是不可重入的,它不能作為回撥函式被呼叫

3.6、uv_loop_alive

int uv_loop_alive(const uv_loop_t* loop);

判斷事件迴圈是否還在活動,返回非0表示還在活動

有以下幾種情況之一表示還在活動:

  • 有被引用的活動控制程式碼或者活動請求;
  • 正在關閉的控制程式碼;

3.7、uv_stop

void uv_stop(uv_loop_t* loop);

停止事件迴圈,會讓 uv_run() 方法儘快結束

該方法呼叫之後,下次事件迴圈迭代之前結束迴圈,正在進行的事件迴圈仍然繼續

如果該函式在 IO 阻塞之前執行,則在當前的事件迭代中, IO 不會被阻塞

3.8、uv_loop_size

size_t uv_loop_size(void);

返回 uv_loop_t 結構體 size

3.9、uv_backend_fd

int uv_backend_fd(const uv_loop_t* loop);

獲取後端檔案描述符,僅使用於 kqueue、epoll 和 事件埠

該方法可以和 run(loop, UV_RUN_NOWAIT) 一起聯合使用,在一個執行緒中輪詢 IO 和處理回撥

3.10、uv_backend_timeout

int uv_backend_timeout(const uv_loop_t* loop);

獲取 IO 輪詢超時時間,單位為毫秒,沒有超時時返回 -1

3.11、uv_now

uint64_t uv_now(const uv_loop_t* loop);

返回當前的時間戳,單位毫秒

時間戳在事件迴圈開始時快取

3.12、uv_update_time

void uv_update_time(uv_loop_t* loop);

更新事件迴圈的時間戳,會影響 uv_now() 的返回值,libuv 會在事件迴圈開始時快取當前時間,以減少系統的時間相關的方法的呼叫

通常情況下不需要呼叫此方法,除了事件迴圈中的某個回撥會阻塞相當長的時間,這個所謂的相當長的時間是有些主觀的,可能是一毫秒或者更長

3.13、uv_walk

void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg);

遍歷控制程式碼列表,執行回撥

arg 引數會傳遞給回撥函式

void (*uv_walk_cb)(uv_handle_t* handle, void* arg);

handle 為遍歷中的某個控制程式碼

3.14、uv_loop_fork

int uv_loop_fork(uv_loop_t* loop);

在呼叫 fork(2) 之後,如果有必要,在子程式中重新初始化核心狀態

在子程式中以觀察者的身份繼續事件迴圈

如果你想繼續在子程式中使用事件迴圈,包括預設的事件迴圈(儘管不想在父程式中使用它),那麼,在每個父程式中建立的事件迴圈中顯示地呼叫此方法是很有必要的

該方法必須在 uv_run() ,或者其他想要在子程式中呼叫其他API之前呼叫。如果不這樣做,可將將導致一些未知的錯誤,比如事件被重複交給父程式和子程式,或者子程式異常退出

可以的話,最好在子程式中建立一個新的迴圈,而不是重複使用父程式建立的迴圈。在 fork 子程式之後,並在子程式中建立的新的迴圈不應該使用此方法

該方法不適用於windows作業系統,他會返回 UV_ENOSYS

需要注意的是,該方法可能存在BUG

3.15、uv_loop_get_data

void* uv_loop_get_data(const uv_loop_t* loop);

返回 loop->data

3.16、uv_loop_set_data

void* uv_loop_set_data(uv_loop_t* loop, void* data);

設定 loop->data 的值

相關文章