色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

一文讀懂I/O復用

jf_78858299 ? 來源:阿Q正磚 ? 作者:阿Q正磚 ? 2023-02-15 11:17 ? 次閱讀

今天給大家聊聊I/O復用,對于大部分公司面試來說,這塊肯定是必問內容,它不僅能側面反映面試這對基礎掌握的是否扎實,還能反映出求職者的知識廣度。

1 從阻塞 I/O 到 I/O 多路復用

阻塞 I/O,是指進程發起調用后,會被掛起(阻塞),直到收到數據再返回。如果調用一直不返回,進程就會一直被掛起。因此,當使用阻塞 I/O 時,需要使用多線程來處理多個文件描述符。

多線程切換有一定的開銷,因此引入非阻塞 I/O。非阻塞 I/O 不會將進程掛起,調用時會立即返回成功或錯誤,因此可以在一個線程輪詢多個文件描述符是否就緒。

但是非阻塞 I/O 的缺點是:每次發起系統調用,只能檢查一個文件描述符是否就緒。當文件描述符很多時,系統調用的成本很高。

因此引入了 I/O 多路復用,可以 通過一次系統調用,檢查多個文件描述符的狀態 。這是 I/O 多路復用的主要優點,相比于非阻塞 I/O,在文件描述符較多的場景下,避免了頻繁的用戶態和內核態的切換,減少了系統調用的開銷。

I/O 多路復用相當于將「遍歷所有文件描述符、通過非阻塞 I/O 查看其是否就緒」的過程從用戶線程移到了內核中,由內核來負責輪詢。

進程可以通過 select、poll、epoll 發起 I/O 多路復用的系統調用,這些系統調用都是同步阻塞的: 如果傳入的多個文件描述符中,有描述符就緒,則返回就緒的描述符;否則如果所有文件描述符都未就緒,就阻塞調用進程,直到某個描述符就緒,或者阻塞時長超過設置的 timeout 后,再返回 。I/O 多路復用內部使用非阻塞 I/O 檢查每個描述符的就緒狀態。

如果 timeout參數設為 NULL,會無限阻塞直到某個描述符就緒;如果timeout參數設為 0,會立即返回,不阻塞。

I/O 多路復用引入了一些額外的操作和開銷,性能更差。但是好處是用戶可以在一個線程內同時處理多個 I/O 請求。如果不采用 I/O 多路復用,則必須通過多線程的方式,每個線程處理一個 I/O 請求。后者線程切換也是有一定的開銷的。

2 為什么 I/O 多路復用內部需要使用非阻塞 I/O?

I/O 多路復用內部會遍歷集合中的每個文件描述符,判斷其是否就緒:

for fd in read_set if (readable(fd)) // 判斷fd是否就緒 count++; FDSET(fd, &res_rset) // 將fd添加到就緒隊列中 break;return count;

這里的 readable(fd) 就是一個非阻塞 I/O 調用。試想,如果這里使用阻塞 I/O,那么fd未就緒時,select會阻塞在這個文件描述符上,無法檢查下個文件描述符。

注意:這里說的是 I/O 多路復用的內部實現,而不是說,使用 I/O 多路復用就必須使用非阻塞 I/O。

3 select

函數簽名與參數

int select(int nfds,            fd_set *restrict readfds,            fd_set *restrict writefds,            fd_set *restrict errorfds.            struct timeval *restrict timeout);

readfds、writefds、errorfds 是三個文件描述符集合。select 會遍歷每個集合的前 nfds個描述符,分別找到可以讀取、可以寫入、發生錯誤的描述符,統稱為“就緒”的描述符。然后用找到的子集替換參數中的對應集合,返回所有就緒描述符的總數。

timeout 參數表示調用 select 時的阻塞時長。如果所有文件描述符都未就緒,就阻塞調用進程,直到某個描述符就緒,或者阻塞超過設置的 timeout 后,返回。如果 timeout 參數設為 NULL,會無限阻塞直到某個描述符就緒;如果 timeout 參數設為 0,會立即返回,不阻塞。

3.1 什么是文件描述符 fd

文件描述符(file descriptor)是一個非負整數,從 0 開始。進程使用文件描述符來標識一個打開的文件。

系統為每一個進程維護了一個文件描述符表,表示該進程打開文件的記錄表,而 文件描述符實際上就是這張表的索引 。當進程打開(open)或者新建(create)文件時,內核會在該進程的文件列表中新增一個表項,同時返回一個文件描述符 —— 也就是新增表項的下標。

一般來說,每個進程最多可以打開 64 個文件,fd ∈ 0~63。在不同系統上,最多允許打開的文件個數不同,Linux 2.4.22 強制規定最多不能超過 1,048,576。

每個進程默認都有 3 個文件描述符:0 (stdin)、1 (stdout)、2 (stderr)。

3.2 socket 與 fd 的關系

socket 是 Unix 中的術語。socket 可以用于同一臺主機的不同進程間的通信,也可以用于不同主機間的通信。一個 socket 包含地址、類型和通信協議等信息,通過 **socket() **函數創建:

int socket(int domain, int type, int protocol)

返回的就是這個 socket 對應的文件描述符 fd操作系統將 socket 映射到進程的一個文件描述符上,進程就可以通過讀寫這個文件描述符來和遠程主機通信。

可以這樣理解:socket 是進程間通信規則的高層抽象,而 fd 提供的是底層的具體實現。socket 與 fd 是一一對應的。通過 socket 通信,實際上就是通過文件描述符 fd 讀寫文件。這也符合 Unix“一切皆文件”的哲學。

3.3 fd_set 文件描述符集合

參數中的 **fd_set **類型表示文件描述符的集合。

由于文件描述符 fd 是一個從 0 開始的無符號整數,所以可以使用 fd_set二進制每一位來表示一個文件描述符。某一位為 1,表示對應的文件描述符已就緒。比如比如設 fd_set 長度為 1 字節,則一個 fd_set 變量最大可以表示 8 個文件描述符。當 **select **返回 **fd_set = 00010011 **時,表示文件描述符 **125 **已經就緒。

3.4 select 使用示例

下圖的代碼說明:

(1)先聲明一個 fd_set 類型的變量 readFDs

(2)調用 FD_ZERO,將 readFDs 所有位 置 0

(3)調用 FD_SET,將 readFDs 感興趣的位置 1,表示要監聽這幾個文件描述符

(4)將 readFDs 傳給 select,調用 select

(5)select會將 readFDs 中就緒的位置 1,未就緒的位置 0,返回就緒的文件描述符的數量

(6)當 select 返回后,調用 FD_ISSET 檢測給定位是否為 1,表示對應文件描述符是否就緒

比如進程想監聽 1、2、5 這三個文件描述符,就將 readFDs 設置為 00010011,然后調用 select

如果 fd=1fd=2 就緒,而 fd=5 未就緒,select 會將 readFDs 設置為 00000011 并返回 2。

如果每個文件描述符都未就緒,select 會阻塞 timeout 時長,再返回。這期間,如果 readFDs 監聽的某個文件描述符上發生可讀事件,則 select 會將對應位置 1,并立即返回。

圖片

**3.5 **select 的缺點

  1. 性能開銷大
    1. 調用 select 時會陷入內核,這時需要將參數中的 fd_set 從用戶空間拷貝到內核空間
    2. 內核需要遍歷傳遞進來的所有 fd_set 的每一位,不管它們是否就緒
  2. 同時能夠監聽的文件描述符數量太少。受限于 sizeof(fd_set) 的大小,在編譯內核時就確定了且無法更改。一般是 1024,不同的操作系統不相同。

4 poll

poll 和 select 幾乎沒有區別。poll 在用戶態通過數組方式傳遞文件描述符,在內核會轉為鏈表方式 存儲 ,沒有最大數量的限制 。

poll 的函數簽名如下:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

其中 fds 是一個 pollfd 結構體類型的數組,調用 poll() 時必須通過 nfds 指出數組 fds 的大小,即文件描述符的數量。

從性能開銷上看,poll 和 select 的差別不大。

5 epoll

epoll 是對 select 和 poll 的改進,避免了“性能開銷大”和“文件描述符數量少”兩個缺點。

簡而言之,epoll 有以下幾個特點:

  • 使用紅黑樹存儲文件描述符集合
  • 使用隊列存儲就緒的文件描述符
  • 每個文件描述符只需在添加時傳入一次;通過事件更改文件描述符狀態

select、poll 模型都只使用一個函數,而 epoll 模型使用三個函數:epoll_createepoll_ctlepoll_wait

5.1 epoll_create

int epoll_create(int size);

epoll_create 會創建一個 epoll 實例,同時返回一個引用該實例的文件描述符。

返回的文件描述符僅僅指向對應的 epoll 實例,并不表示真實的磁盤文件節點。其他 APIepoll_ctlepoll_wait 會使用這個文件描述符來操作相應的 epoll 實例。

當創建好 epoll 句柄后,它會占用一個 fd 值,在 linux 下查看 /proc/進程id/fd/,就能夠看到這個 fd。所以在使用完 epoll 后,必須調用 close(epfd) 關閉對應的文件描述符,否則可能導致 fd 被耗盡。當指向同一個 epoll 實例的所有文件描述符都被關閉后,操作系統會銷毀這個 epoll 實例。

epoll 實例內部存儲:

  • 監聽列表:所有要監聽的文件描述符,使用紅黑樹
  • 就緒列表:所有就緒的文件描述符,使用鏈表

5.2 epoll_ctl

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epoll_ctl 會監聽文件描述符 fd 上發生的 event 事件。

參數說明:

  • epfdepoll_create 返回的文件描述符,指向一個 epoll 實例
  • fd 表示要監聽的目標文件描述符
  • event 表示要監聽的事件(可讀、可寫、發送錯誤…)
  • op 表示要對 fd 執行的操作,有以下幾種:
    • EPOLL_CTL_ADD:為 fd 添加一個監聽事件 event
    • EPOLL_CTL_MOD:Change the event event associated with the target file descriptor fd(event 是一個結構體變量,這相當于變量 event 本身沒變,但是更改了其內部字段的值)
    • EPOLL_CTL_DEL:刪除 fd 的所有監聽事件,這種情況下 event 參數沒用

返回值 0 或 -1,表示上述操作成功與否。

epoll_ctl 會將文件描述符 fd 添加到 epoll 實例的監聽列表里,同時為 fd 設置一個回調函數,并監聽事件 event。當 fd 上發生相應事件時,會調用回調函數,將 fd 添加到 epoll 實例的就緒隊列上。

5.3 epoll_wait

int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);

這是 epoll 模型的主要函數,功能相當于 select

參數說明:

  • epfdepoll_create 返回的文件描述符,指向一個 epoll 實例
  • events 是一個數組,保存就緒狀態的文件描述符,其空間由調用者負責申請
  • maxevents 指定 events 的大小
  • timeout 類似于 select 中的 timeout。如果沒有文件描述符就緒,即就緒隊列為空,則 epoll_wait 會阻塞 timeout 毫秒。如果 timeout 設為 -1,則 epoll_wait 會一直阻塞,直到有文件描述符就緒;如果 timeout 設為 0,則 epoll_wait 會立即返回

返回值表示 events 中存儲的就緒描述符個數,最大不超過 maxevents

5.4 epoll 的優點

一開始說,epoll 是對 select 和 poll 的改進,避免了“性能開銷大”和“文件描述符數量少”兩個缺點。

對于“文件描述符數量少”,select 使用整型數組存儲文件描述符集合,而 epoll 使用紅黑樹存儲,數量較大。

對于“性能開銷大”,epoll_ctl 中為每個文件描述符指定了回調函數,并在就緒時將其加入到就緒列表,因此 epoll 不需要像 select 那樣遍歷檢測每個文件描述符,只需要判斷就緒列表是否為空即可。這樣,在沒有描述符就緒時,epoll 能更早地讓出系統資源。

相當于時間復雜度從 O(n) 降為 O(1)

此外,每次調用 select 時都需要向內核拷貝所有要監聽的描述符集合,而 epoll 對于每個描述符,只需要在 epoll_ctl 傳遞一次,之后 epoll_wait 不需要再次傳遞。這也大大提高了效率。

5.5 水平觸發、邊緣觸發

select 只支持水平觸發,epoll 支持水平觸發和邊緣觸發。

水平觸發 (LT,Level Trigger):當文件描述符就緒時,會觸發通知,如果用戶程序沒有一次性把數據讀/寫完,下次還會發出可讀/可寫信號進行通知。

邊緣觸發 (ET,Edge Trigger):僅當描述符從未就緒變為就緒時,通知一次,之后不會再通知。

區別:邊緣觸發效率更高, 減少了事件被重復觸發的次數 ,函數不會返回大量用戶程序可能不需要的文件描述符。

水平觸發、邊緣觸發的名稱來源:數字電路當中的電位水平,高低電平切換瞬間的觸發動作叫邊緣觸發,而處于高電平的觸發動作叫做水平觸發。

5.6 為什么邊緣觸發必須使用非阻塞 I/O?

關于這個問題的解答,強烈建議閱讀這篇文章。下面是一些關鍵摘要:

  • 每次通過 read 系統調用讀取數據時,最多只能讀取緩沖區大小的字節數;如果某個文件描述符一次性收到的數據超過了緩沖區的大小,那么需要對其 read 多次才能全部讀取完畢
  • select 可以使用阻塞 I/O 。通過 select 獲取到所有可讀的文件描述符后,遍歷每個文件描述符,read 一次數據(見上文 select 示例)
    • 這些文件描述符都是可讀的,因此即使 read 是阻塞 I/O,也一定可以讀到數據,不會一直阻塞下去
    • select 采用水平觸發模式,因此如果第一次 read 沒有讀取完全部數據,那么下次調用 select 時依然會返回這個文件描述符,可以再次 read
    • select 也可以使用非阻塞 I/O 。當遍歷某個可讀文件描述符時,使用 for 循環調用 read 多次 ,直到讀取完所有數據為止(返回 EWOULDBLOCK)。這樣做會多一次 read 調用,但可以減少調用 select 的次數
  • epoll 的邊緣觸發模式下,只會在文件描述符的可讀/可寫狀態發生切換時,才會收到操作系統的通知
    • 因此,如果使用 epoll邊緣觸發模式 ,在收到通知時,**必須使用非阻塞 I/O,并且必須循環調用 ** readwrite 多次,直到返回 EWOULDBLOCK 為止 ,然后再調用 epoll_wait 等待操作系統的下一次通知
    • 如果沒有一次性讀/寫完所有數據,那么在操作系統看來這個文件描述符的狀態沒有發生改變,將不會再發起通知,調用 epoll_wait 會使得該文件描述符一直等待下去,服務端也會一直等待客戶端的響應,業務流程無法走完
    • 這樣做的好處是每次調用 epoll_wait 都是有效的——保證數據全部讀寫完畢了,等待下次通知。在水平觸發模式下,如果調用 epoll_wait 時數據沒有讀/寫完畢,會直接返回,再次通知。因此邊緣觸發能顯著減少事件被觸發的次數
    • 為什么 epoll邊緣觸發模式不能使用阻塞 I/O ?很顯然,邊緣觸發模式需要循環讀/寫一個文件描述符的所有數據。如果使用阻塞 I/O,那么一定會在最后一次調用(沒有數據可讀/寫)時阻塞,導致無法正常結束

6 三者對比

  • select:調用開銷大(需要復制集合);集合大小有限制;需要遍歷整個集合找到就緒的描述符
  • poll:poll 采用數組的方式存儲文件描述符,沒有最大存儲數量的限制,其他方面和 select 沒有區別
  • epoll:調用開銷小(不需要復制);集合大小無限制;采用回調機制,不需要遍歷整個集合

selectpoll 都是在用戶態維護文件描述符集合,因此每次需要將完整集合傳給內核;epoll 由操作系統在內核中維護文件描述符集合,因此只需要在創建的時候傳入文件描述符。

此外 select 只支持水平觸發,epoll 支持邊緣觸發。

7 適用場景

當連接數較多并且有很多的不活躍連接時,epoll 的效率比其它兩者高很多。當連接數較少并且都十分活躍的情況下,由于 epoll 需要很多回調,因此性能可能低于其它兩者。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 編程
    +關注

    關注

    88

    文章

    3614

    瀏覽量

    93686
  • i/o
    i/o
    +關注

    關注

    0

    文章

    33

    瀏覽量

    4589
收藏 人收藏

    評論

    相關推薦

    讀懂i/o端口地址譯碼

    I/O端口是接口電路中能被CPU直接訪問的寄存器。訪問端口就是訪問接口電路中的寄存器。個接口電路(外設)通常擁有不止個端口,如命令口、狀態口、數據口等。端口地址編碼形式有統
    的頭像 發表于 11-16 09:40 ?1.4w次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>文</b><b class='flag-5'>讀懂</b><b class='flag-5'>i</b>/<b class='flag-5'>o</b>端口地址譯碼

    讀懂接口模塊的組合應用有哪些?

    讀懂接口模塊的組合應用有哪些?
    發表于 05-17 07:15

    讀懂如何去優化AC耦合電容?

    讀懂如何去優化AC耦合電容?
    發表于 06-08 07:04

    讀懂什么是NEC協議

    讀懂什么是NEC協議?
    發表于 10-15 09:22

    讀懂中斷方式和輪詢操作有什么區別嗎

    讀懂中斷方式和輪詢操作有什么區別嗎?
    發表于 12-10 06:00

    使用引腳作為普通的I/O定要進行引腳的功能復用

    ##學習筆記.相關表格1.PB3,PB4,PA13,PA14,PA15引腳可根據上表復用成普通IO口。在mcu復位的時候這幾個引腳被作為jtag的功能。當我們要使用這些引腳作為普通的I/O
    發表于 03-01 07:03

    數字I/O介紹

    數字I/O腳有專用和復用。數字I/O腳的功能通過9個16位控制寄存器來控制。控制寄存器分為兩類:(1)I
    發表于 09-16 12:20 ?19次下載

    Java I/O 的相關方法分析

    asynchronous I/O。 Java 是種跨平臺語言,為了支持異步 I/O,誕生了 NIO,Java1.4 引入的 NIO1.0
    發表于 09-27 13:18 ?0次下載
    Java <b class='flag-5'>I</b>/<b class='flag-5'>O</b> 的相關方法分析

    Linux I/O多路復用

    /O,非阻塞I/O,I/O多路復用,信號驅動I/
    發表于 04-02 14:31 ?310次閱讀

    Linux中如何使用信號驅動式I/O

    大圖 I/O 復用 (select、poll、epoll): 通過 I/O 復用函數向內核注冊
    的頭像 發表于 03-12 14:47 ?2423次閱讀
    Linux中如何使用信號驅動式<b class='flag-5'>I</b>/<b class='flag-5'>O</b>?

    關于STM32通用和復用I/O

    關于STM32通用和復用I/O,概述? STM32F10x系列具有豐富的端口可供使用包括26、37、51、80、112個多功能雙向5V兼容的快速
    發表于 12-03 09:51 ?9次下載
    關于STM32通用和<b class='flag-5'>復用</b><b class='flag-5'>I</b>/<b class='flag-5'>O</b>口

    讀懂MCU的特點、功能及如何編寫

    讀懂MCU的特點、功能及如何編寫
    發表于 12-05 09:51 ?24次下載
    <b class='flag-5'>一</b><b class='flag-5'>文</b><b class='flag-5'>讀懂</b>MCU的特點、功能及如何編寫

    關于STM32的 I/O 復用功能

    今天給大家分享兩點內容: 是,為什么我們要先開啟STM32外設時鐘;二是,關于STM32的 I/O 復用功能及什么時候開啟AFIO時鐘。
    的頭像 發表于 10-20 14:19 ?3639次閱讀

    讀懂,什么是BLE?

    讀懂,什么是BLE?
    的頭像 發表于 11-27 17:11 ?2280次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>文</b><b class='flag-5'>讀懂</b>,什么是BLE?

    讀懂車規級AEC-Q認證

    讀懂車規級AEC-Q認證
    的頭像 發表于 12-04 16:45 ?938次閱讀
    主站蜘蛛池模板: 男女爽爽无遮挡午夜视频在线观看| 久久99热在线观看7| 果冻传媒最新视频在线观看 | 日韩精品a在线视频| 麻豆一二三区果冻| 日本成熟bbxxxxxxxx| 亚洲成人网导航| 19十主播福利视频| 囯产精品久久久久久久久免费蜜桃| 精品成人片深夜| 欧洲精品一区二区不卡观看 | 芳草地在线观看免费观看| 国产真实强被迫伦姧女在线观看| 免费视频不卡| 亚洲 欧美 综合 高清 在线| 99热这里有精品| 国语自产一区第二页| 欧美日韩久久久精品A片| 亚洲免费观看| 草神被爆漫画羞羞漫画| 久久精品国产欧美成人| 免费国产成人手机在线观看| 无套日出白浆在线播放| 99久久99久久免费精品蜜桃| 国产综合视频在线观看一区| 欧美最猛性xxxxx亚洲精品| 夜夜躁婷婷AV蜜桃视频| 岛国精品在线观看| 国产ts在线| 男女牲交大战免费播放| 亚洲欧美日韩另类精品一区二区三区 | 蜜桃AV色欲A片精品一区| 亚欧成人毛片一区二区三区四区| 99视频在线免费看| 久久久WWW免费人成精品| 香蕉97超级碰碰碰碰碰久| qvod 韩国| 国产乱子影视频上线免费观看| 欧美91精品久久久久网免费| 夜夜精品视频一区二区| 国产午夜在线视频|