面試官:談?wù)勀銓O多路復(fù)用的理解?
“IO 多路復(fù)用”是編程中常見的技術(shù)詞匯,使用這種技術(shù)的框架有很多,如,Redis、Kafka、Netty、Nginx 中都用到了此技術(shù)。那問題來了,什么是 IO 多路復(fù)用?它的具體實(shí)現(xiàn)技術(shù)有哪些?這些技術(shù)之間有什么區(qū)別?今天我們就來簡單的探討一下。
1.什么是IO多路復(fù)用?
IO 多路復(fù)用技術(shù)是一種允許單個(gè)線程管理多個(gè)網(wǎng)絡(luò)連接的技術(shù),它使得服務(wù)器能夠高效地處理大量的并發(fā)連接而不需要為每個(gè)連接創(chuàng)建一個(gè)獨(dú)立的線程或進(jìn)程。
圖片
想象如果客戶端有成千上萬個(gè)的情況下,那么非 IO 多路復(fù)用就會有成千上萬個(gè)線程,那么就會發(fā)生 IO 過度爭搶和多線程切換的問題,因?yàn)?CPU 資源只有幾個(gè),而要執(zhí)行的線程卻有成千上萬個(gè)。
2.IO多路復(fù)用技術(shù)實(shí)現(xiàn)
常用的 IO 多路復(fù)用實(shí)現(xiàn)技術(shù)有:select、poll、epoll 和 kqueue 等,它們的具體介紹如下。
2.1 select
- 特點(diǎn):select 是最早出現(xiàn)的一種多路復(fù)用 I/O 模型,幾乎在所有平臺上都有支持。它通過一個(gè)調(diào)用來監(jiān)視多個(gè)文件描述符,等待其中任何一個(gè)變?yōu)榭勺x或可寫狀態(tài)。
- 局限性:
文件描述符數(shù)量受限:通常限制為 1024 個(gè),可以通過修改系統(tǒng)參數(shù)來增加這個(gè)限制,但這樣做會消耗更多的系統(tǒng)資源。
效率低下:每次調(diào)用 select 都需要將文件描述符列表復(fù)制到內(nèi)核,檢查完后又需要復(fù)制回用戶空間,這對于大量文件描述符來說效率很低。
不支持邊緣觸發(fā)模式:只支持水平觸發(fā)模式。
邊緣觸發(fā)模式 VS 水平觸發(fā)模式
- 邊緣觸發(fā)模式:當(dāng)一個(gè)文件描述符從不可讀(或不可寫)變?yōu)榭勺x(或可寫)時(shí),內(nèi)核僅通知應(yīng)用程序一次。如果應(yīng)用程序未能立即處理完所有可用的數(shù)據(jù)(例如,緩沖區(qū)中的數(shù)據(jù)未完全讀取),那么即使文件描述符仍然是可讀的,內(nèi)核也不會再次通知應(yīng)用程序,直到該文件描述符的狀態(tài)再次發(fā)生變化(例如,從可讀變?yōu)椴豢勺x,再變回可讀)。
優(yōu)點(diǎn):減少了系統(tǒng)調(diào)用次數(shù),提高了效率,特別適合于大數(shù)據(jù)量傳輸?shù)膱鼍啊?/p>
缺點(diǎn):要求應(yīng)用程序必須在接收到事件后盡可能多地讀取或?qū)懭霐?shù)據(jù),否則可能會錯(cuò)過后續(xù)的數(shù)據(jù)。因此,邊緣觸發(fā)模式對編程的要求較高,需要更加小心地處理。
- 水平觸發(fā)模式:在水平觸發(fā)模式下,只要文件描述符處于可讀(或可寫)狀態(tài),無論之前是否已經(jīng)通知過,內(nèi)核都會持續(xù)通知應(yīng)用程序。這意味著,如果應(yīng)用程序未能一次性處理完所有數(shù)據(jù),只要文件描述符仍然處于可讀或可寫狀態(tài),內(nèi)核就會繼續(xù)發(fā)送通知。
- 優(yōu)點(diǎn):編程較為簡單,因?yàn)榧词瑰e(cuò)過了某個(gè)事件的通知,只要文件描述符的狀態(tài)沒有改變,應(yīng)用程序仍然有機(jī)會在下一次輪詢時(shí)接收到同樣的事件。
- 缺點(diǎn):可能造成更多的系統(tǒng)調(diào)用,因?yàn)榧词箶?shù)據(jù)已經(jīng)被部分處理,內(nèi)核仍然會不斷地通知應(yīng)用程序,這可能導(dǎo)致效率降低。
2.2 poll
- 特點(diǎn):poll 在功能上與 select 非常相似,但沒有文件描述符數(shù)量的限制。poll 使用一個(gè) pollfd 結(jié)構(gòu)體數(shù)組來表示要監(jiān)聽的文件描述符集合。
- 局限性:雖然解決了 select 的文件描述符數(shù)量限制問題,但在性能上仍然存在類似 select 的問題,即每次調(diào)用都需要復(fù)制文件描述符列表到內(nèi)核,并且返回時(shí)也需要復(fù)制回用戶空間。
2.3 epoll
- 特點(diǎn):epoll 是 Linux 特有的高效 IO 多路復(fù)用技術(shù),它克服了 select 和 poll 的所有缺點(diǎn)。epoll 使用三個(gè)系統(tǒng)調(diào)用來管理文件描述符:epoll_create 創(chuàng)建一個(gè) epoll 實(shí)例,epoll_ctl 添加/刪除需要監(jiān)聽的文件描述符,epoll_wait 等待事件的發(fā)生。
- 優(yōu)勢:
無數(shù)量限制:沒有文件描述符數(shù)量限制。
高效:只有活躍的文件描述符才會被傳遞給用戶空間,減少了不必要的復(fù)制操作。
功能強(qiáng)大:支持邊緣觸發(fā)和水平觸發(fā)兩種工作模式。
2.4 kqueue
- 特點(diǎn):kqueue 是 FreeBSD 操作系統(tǒng)引入的一種 IO 多路復(fù)用技術(shù),后來也被 Mac OS X 和其他基于 BSD 的操作系統(tǒng)采用。kqueue 可以同時(shí)處理多種類型的事件,包括但不限于文件描述符事件、信號事件等。
- 優(yōu)勢:
功能更強(qiáng)大:不僅支持文件描述符的事件通知,還能處理其他類型的事件。
性能優(yōu)秀:與 epoll 類似,只有活躍的文件描述符才會被處理,從而提高了效率。
3.區(qū)別對比
select、poll、epoll 和 kqueue 之間的區(qū)別如下:
技術(shù)名稱 | 支持平臺 | 連接數(shù)限制 | IO效率 | 數(shù)據(jù)拷貝方式 |
select | 跨平臺 | 默認(rèn)1024 | O(N) | 每次調(diào)用都拷貝 |
poll | 跨平臺 | 無 | O(N) | 每次調(diào)用都拷貝 |
epoll | Linux 特有 | 無 | O(1) | 僅在 epoll_ctl 時(shí)拷貝 |
kqueue | MacOS、FreeBSD 等 | 無 | O(1) | 具體實(shí)現(xiàn)方式可能因系統(tǒng)而異,但通常也是高效的。 |
課后思考
什么叫做“文件描述符”?IO 多路復(fù)用為什么要進(jìn)行“數(shù)據(jù)拷貝”?