圖解 IO 多路複用之 Poll 的實現原理

    IO 多路複用在 Linux 上還有一種 poll 的實現方案,其實 poll 和 select 一樣,它們都是函數,但是 poll 針對 select 的底層使用的 bit 數組的文件描述符列表做了優化,下面分析 poll 的原理。

1、認識 poll 函數

poll 的原型函數如下所示:

struct pollfd myfd[500];   
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

(1)pollfd 結構體

函數的結構形式如下所示:

struct pollfd {
    # 需要內核檢測的文件描述符
    int fd;
    # 文件描述符的事件
    short events;     
    # 返回就緒的事件
    short revents;
};

    在 pollfd 結構體中 events 和 revents 的事件主要是讀事件、寫事件和讀寫異常的事件,事件的整理如下表所示:

CRj15Y

    events 可以單獨的傳讀事件或寫事件(如 events=POLLIN),也可以同時傳入讀事件和寫事件(如 events=POLLIN | POLLOUT)

    revents 是內核寫出的值,其值是根據 events 傳入的事件決定的,如果 events 傳入的是讀事件,那麼 revents 傳出的就是就緒的讀文件描述符。

    poll 相比於 select 方式最大的改進是增加了 pollfd 結構體,正是由於 pollfd 結構體的出現,那麼如 32 位系統就不會出現 1024 個文件描述符的限制,相對於 select 來說可以承受更多客戶端連接並且 pollfd 結構體也是可以重複使用的。

(2)nfds

    本參數用來表示在 struct pollfd myfd[500] 中有效的文件描述符的最大值加 1(如在數組中有效的文件描述是 0-300,那麼 nfds = 300 + 1 = 301),設置此參數的目的是給用戶態的 fds 遍歷就緒文件描述符的時候指定一個循環的上限範圍值。

(3)timeout

    設置 poll 函數的阻塞時長,單位是毫秒,設置不同的值會有不同的效果,如下表所示:

Z7PIH5

(4)poll 函數的返回值

    poll 返回一個 int 類型的數據,我們可以根據返回的不同結果來分析是否有就緒的文件描述符,其返回值表如下所示:

Ny8YkE

2、poll 的原理

    以客戶端讀取數據爲案例介紹 poll 的工作原理,下圖展示了客戶端讀取數據的流程圖:

(1)客戶端設置的文件描述符信息,如下圖設置的讀事件:

(2)通過 poll 函數將設置的文件描述符信息加載到內核中,此時內核中的文件描述符信息如下所示:

(3)內核開始監聽文件描述符對應的事件,此時有數據通過網卡發送過的的時候,如下所示:

    數據在內核的環形緩衝區中使用 DMA 複製技術將數據複製到對應的讀緩衝區中,然後修改文件描述符信息(更新 revents 上對應的數據),poll 經過一輪監聽之後將就緒的文件描述符數據返回到用戶態。

(4)poll 返回數據到用戶態,用戶態根據 poll 函數的返回判斷是否有數據達到(返回值大於 0 表示有數據到達),此時用戶態開始遍歷文件描述符數組來分析是哪個文件描述符就緒並做後續的相關的業務,如下圖所示:

    假設 poll 遍歷出來是第 1 個文件描述符就緒了(也就是 fd[0]),那麼它開始接收數據,數據接收完畢之後還會將第 1 個文件描述符的 revents 位置的 1 修改成 0,這樣也是爲什麼 pollfd 可以重複的原因。

    通過以上的步驟就完成了讀數據的讀取過程。寫數據的過程也是類似的,這裏就不展開描述了。

總結:

(1)poll 相對於 select 方式,增加了 pollfd 的結構體,其工作原理的 seclet 基本類似。

(2)select 具有跨平臺的特點,但是 poll 只能在 Linux 上使用。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Vv4mP4h4nRbR8gKB2m64LA