IO多路複用之select、poll、epoll學習入門

  • 看視頻學習:https://www.bilibili.com/video/BV1qJ411w7du?from=search&seid=17786017183453179660
  • 知乎 epoll講解: https://zhuanlan.zhihu.com/p/63179839

網卡接收信息

網卡接收到信息會複製到內存中,然後產生中斷,操作系統接着執行中斷程序,如下圖。關於將數據由哪一個socket接收是由端口決定的。
在這裏插入圖片描述

文件描述符

先描述一下文件描述符吧。先看一張圖(別人的)。
在這裏插入圖片描述
linux希望能夠實現一切都是類似文件操作。

在一個線程創建時,就會有一個pcb進程控制塊,記錄各種信息,其中包括files_struct*指針,指向一個struct_file結構體對象,裏面有包含關於一個線程用到的所有的文件引用地址,它們是用一個fd_array存儲,那麼這個數組的下標就是我們說的文件描述符,這也是爲什麼很多通過文件描述符是整型,利用文件描述符 可以快速的找到一個文件進行操作。

那麼你可能平時使用c操作文件時用的是fopen,返回一個File* 類型,是不是有點熟悉的感覺?fd_array中保存的就是這個類型,也就是說它和文件描述符的區別就是,一個是索引,一個是具體的數組[索引]對應File* 類型的值。而c語言中的open函數則是返回文件描述符,文件描述符和File*類型的值是可以相互轉化的。open與fopen區別 相關描述可以看:https://blog.csdn.net/t_x_l_/article/details/72123669 的介紹。

io多路複用模型之select

內存分爲用戶態和內核態,那我們平時程序是跑在用戶態的,程序發起一個監聽的accept事件時,在內存中就會產生一個socket對象,詳細的瞭解可以查看(盜圖的):https://zhuanlan.zhihu.com/p/63179839。

當進程執行到創建socket的語句時,操作系統會創建一個由文件系統管理的socket對象(如下圖),創建時返回的值就是對應的文件描述符,拿着文件描述符就相當拿着指向這個socket的指針。這個socket對象包含了發送緩衝區、接收緩衝區、等待隊列等成員。等待隊列是個非常重要的結構,它指向所有需要等待該socket事件的進程。

在這裏插入圖片描述
在這裏插入圖片描述
看上面的代碼,顯示通過accept在內存中建立了socket,我們得到每個socket的文件描述符,保存在fds數組中,我們再利用一個叫 rset 的值去標記,這個rset思想有的類似一個十進制數的二進制位,如果fds[i] 爲5,那麼就讓第4 個位爲1(從0開始).

接着,程序調用select把這個rset傳給內核,讓內核對rset中爲1的位的對應的fd_array中記錄的 指向的 socket進行輪詢(讓內核去做輪詢速度會更快),訪問看有沒有數據,如果都沒,那麼內核的selet進入阻塞,直到某個socket來了數據,這時候可以就會喚醒系統 select,select會遍歷所有的rset 指定的socke,若有數據,就會對對應的位置1,最後 返回到應用程序向下執行代碼。那麼程序中繼續往下執行,用戶程序需要對 rset 的值進行對比,比如說,第4位原來爲1,來了數據就變爲0,那麼進程到對應內核空間的接收緩衝區中去讀取數據。

最後,用戶程序再次調用select時,仍然需要去重置 rset。

IO多路複用 之 poll

在這裏插入圖片描述

poll引入了新的數據結構,pollfd,好處就是解決了原來select模型中的 bitmap大小限定最大爲1024的問題(如果要更改更大的值,可修改某些配置),然後,我想到的是,如果select修改配置,輪詢2000個socket,很耗內核的輪詢時間,即使你用poll去監聽2000個不是也很耗時。。

IO多路複用之epoll

先看大概的代碼和圖示。其實我不大明白accept部分爲啥可以創建多個socket(因爲java裏面到了這一步是會阻塞的,除非設置了不阻塞)
在這裏插入圖片描述
epoll的改進之處很明顯。首先引入了epoll_event結構體,然後event.data.fd是記錄文件描述符,ev.events是關注的事件。最重要的是epoll.ctl,這個方法將ev變量放到 共享內存中,這樣用戶程序和內核的程序就不需要進行交換數據,故效率提升,另外每次都返回對應的已經接收到數據的文件描述符O(1),代替原來的再次遍歷所有O(n)。epoll_wait就是類似select模型中的select.還有就是epoll使用通知機制,這樣的好處是不需要去輪詢遍歷,比如當某個socket中數據可讀,則觸發這樣的一個事件,通過這個事件將文件描述符添加到 rdlist(就緒隊列)中。

視頻中講epoll是將有數據的socket的文件描述符重排,再返回有數據的socket的個數,這樣直接遍歷所有的已有數據的socket接收緩衝區,emmm,不是很確定,我看https://zhuanlan.zhihu.com/p/64746509上講的,是把所有有消息的socket放到rdlist中,然後返回用戶程序直接根據rdlist就行。。 。應該是知乎專欄說的對吧,不過知乎專欄沒提到 共享內存 的使用,所以一開始看就很懵。

推薦閱讀文章:https://www.jianshu.com/p/31cdfd6f5a48

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章