自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

徹底搞懂 Select / Poll / Epoll,就這篇了!

開發(fā) 前端
假設(shè)此時客戶端發(fā)送了數(shù)據(jù),網(wǎng)卡接收到的數(shù)據(jù)塞到對應(yīng)的 socket 的接收隊列中,此時 socket 知道來數(shù)據(jù)了,那如何喚醒 select 呢?

之前已經(jīng)把網(wǎng)絡(luò) I/O 相關(guān)要點都盤了,還剩 select/poll/epoll 這幾個區(qū)別沒說,這篇就來搞搞它們,并且是從完全理解原理的角度來區(qū)分它們。

本來是要上源碼的,但是感覺沒啥必要,身為應(yīng)用開發(fā)我覺得理解原理就行了,源碼反正看了就忘了,理解才是最重要!所以我就盡量避免代碼且用大白話來盤一盤這三個玩意。

話不多說,發(fā)車。

小思考

首先,我們知道 select/poll/epoll 是用來實現(xiàn)多路復(fù)用的,即一個線程利用它們即可 hold 住多個 socket。

按照這個思路,線程不可被任何一個被管理的 Socket 阻塞,且任一個 Socket 來數(shù)據(jù)之后都得告知 select/poll/epoll 線程。

想想看,這應(yīng)該如何實現(xiàn)呢?

我們拿 select 的邏輯來分析下

按照我們的理解,select 管理多個 Socket 的模型如下圖所示:

這里要注意一下內(nèi)核態(tài)和用戶態(tài)的交互,用戶程序訪問不了內(nèi)核空間。

所以,我們調(diào)用 select 會把所有要管理的 socket 的 fd (文件描述符,Linux下皆為文件,簡單理解就是通過 fd 能找到這個 socket)傳到內(nèi)核中。

此時,要遍歷所有 socket,看看是否有感興趣的事件發(fā)生。如果沒有一個 socket 有事件發(fā)生,那么 select 的線程就需要讓出 cpu 阻塞等待,這個等待可以是不設(shè)置超時時間的死等,也可以是設(shè)置 timeout 的有超時時間的等待。

假設(shè)此時客戶端發(fā)送了數(shù)據(jù),網(wǎng)卡接收到的數(shù)據(jù)塞到對應(yīng)的 socket 的接收隊列中,此時 socket 知道來數(shù)據(jù)了,那如何喚醒 select 呢?

其實每個 socket 有個屬于自己的睡眠隊列,select 會安排一個內(nèi)應(yīng),即在被管理的 socket 的睡眠隊列里面塞入一個 entry。

當(dāng) socket 接收到網(wǎng)卡的數(shù)據(jù)后,就會去它的睡眠隊列里遍歷 entry,調(diào)用 entry 設(shè)置的 callback 方法,這個 callback 方法里就能喚醒 select !

所以 select 在每個被它管理的 socket 的睡眠隊列里都塞入一個與它相關(guān)的 entry,這樣不論哪個 socket 來數(shù)據(jù)了,它立馬就能被喚醒然后干活!

但是,select 的實現(xiàn)不太好,因為喚醒的 select 此時只知道來活了,并不知道具體是哪個 socket 來數(shù)據(jù)了,所以只能傻傻地遍歷所有 socket ,看看到底是哪個 scoket 來活了,然后把所有來活的 socket 封裝成事件返回。

這樣用戶程序就能獲得發(fā)生的事件,然后進行 I/O 和業(yè)務(wù)處理了。

這就是 select 的實現(xiàn)邏輯,理解起來應(yīng)該不難。

這里再提一嘴 select 的限制,因為被管理的 socket fd 需要從用戶空間拷貝到內(nèi)核空間,為了控制拷貝的大小而做了限制,即每個 select 能拷貝的 fds 集合大小只有1024。

然后要改的話只能修改宏..再重新編譯內(nèi)核。網(wǎng)上很多文章都是這樣說的,但是(沒錯有個但是)。

我看了一篇文章,確實有這個宏,值也是 1024,但內(nèi)核根本沒有限制 fds 集合的大小。然后托人問了個內(nèi)核大佬,大佬說內(nèi)核確實沒做限制,glibc那層做了。

所以..重新編譯內(nèi)核?那篇文章放文末。

poll

poll 這玩意相比于 select 主要就是優(yōu)化了 fds 的結(jié)構(gòu),不再是 bit 數(shù)組了,而是一個叫 pollfd 的玩意,反正就是不用管啥 1024 的限制了。

不過現(xiàn)在也沒人用 poll,我就不多說了。

epoll

這個就是重點了。

相信看了 select 的實現(xiàn),我們稍微思考下,就能想出幾個可以優(yōu)化的點。

比如,為什么每次 select 需要把監(jiān)控的 fds 傳輸?shù)絻?nèi)核里?不能在內(nèi)核里維護個?

為什么 socket 只喚醒 select,不能告訴它是哪個 socket 來數(shù)據(jù)了?

epoll 主要就是基于上面兩點做了優(yōu)化。

首先,搞了個叫 epoll_ctl 的方法,這方法就是用來管理維護 epoll 所監(jiān)控的哪些 socket。

如果你的 epoll 要新加一個 socket 來管理,那就調(diào)用 epoll_ctl,要刪除一個 socket 也調(diào)用 epoll_ctl,通過不同的入?yún)砜刂圃鰟h改。

這樣,在內(nèi)核里面就維護了此 epoll 管理的 socket 集合,這樣就不用每次調(diào)用的時候都得把所有管理的 fds 拷貝到內(nèi)核了。

對了,這個 socket 集合是用紅黑樹實現(xiàn)的。

然后和 select 類似,每個 socket 的睡眠隊列里都會加個 entry,當(dāng)每個 socket 來數(shù)據(jù)之后,同樣也會調(diào)用 entry 對應(yīng)的 callback。

與 select 不同的是,引入了一個 ready_list 雙向鏈表,callback 里面會把當(dāng)前的 socket 加入到 ready_list 然后喚醒 epoll。

這樣被喚醒的 epoll 只需要遍歷 ready_list 即可,這個鏈表里一定是有數(shù)據(jù)可讀的 socket,相比于 select 就不會做無用的遍歷了。

同時收集到的可讀的 fd 按理是要拷貝到用戶空間的,這里又做了個優(yōu)化,利用了 mmp,讓用戶空間和內(nèi)核空間映射到同一塊內(nèi)存中,這樣就避免了拷貝。

完美啊~

這就是 epoll 基于 select 所作的優(yōu)化,還有一些差別沒細說,比如 epoll 是阻塞睡眠在一個 single_epoll_wait_list 而不是 socket 的睡眠隊列等等,我就不提了,理解上面的這些已經(jīng)夠了。

ET&LT

都談到 epoll 了,避免不了要扯扯 ET 和 LT 兩個模式。

ET,邊沿觸發(fā)。

按照上面的邏輯就是 epoll 遍歷 ready_list 的時候,會把 socket 從 ready_list 里面移除,然后讀取這個 scoket 的事件。

而 LT,水平觸發(fā),有點不一樣。

在這個模式下 epoll 遍歷 ready_list 的時候,會把 socket 從 ready_list 里面移除,然后讀取這個 scoket 的事件,如果這個 socket 返回了感興趣的事件,那么當(dāng)前這個 socket 會再被加入到 ready_list 中,這樣下次調(diào)用 epoll_wait 的時候,還能拿到這個 socket。

這就是這兩者最本質(zhì)的區(qū)別了。

看到這有人會問,這兩種模式的使用會造成哪種不一樣的結(jié)果?

如果此時一個客戶端同時發(fā)來了 5 個數(shù)據(jù)包,按正常的邏輯,只需要喚醒一次 epoll ,把當(dāng)前 socket 加一次到 ready_list 就行了,不需要加 5 次。然后用戶程序可以把 socket 接收隊列的所有數(shù)據(jù)包都讀完。

但假設(shè)用戶程序就讀了一個包,然后處理報錯了,后面不讀了,那后面的 4 個包咋辦?

如果是 ET 模式,就讀不了了,因為沒有把 socket 加入到 ready_list 的觸發(fā)條件了。除非這個客戶端發(fā)了新的數(shù)據(jù)包過來,這樣才會再把當(dāng)前 socket 加入到 ready_list,在新包過來之前,這 4 個數(shù)據(jù)包都不會被讀到。

而 LT 模式不一樣,因為每次讀完有感興趣的事件發(fā)生之后,會把當(dāng)前 socket 再加入到 ready_list,所以下次肯定能讀到這個 socket,所以后面的 4 個數(shù)據(jù)包會被訪問到,不論客戶端是否發(fā)送新包。

至此,我想你應(yīng)該理解什么是 ET ,什么是 LT 了,而不用對著一些什么狀態(tài)變更觸發(fā)這些不易理解的名詞而發(fā)暈。

最后

好了,今天的分析到此完畢,我個人覺得對 select/poll/epoll 的理解到這個程度就差不多了,當(dāng)然還有很多細節(jié),需要自行去看源碼探究,問我我也不懂,這些都是閱讀網(wǎng)上的源碼分析文章得出的結(jié)論。

我也不建議讀的那么深,畢竟人的精力有限對吧,有涉及到相關(guān)底層優(yōu)化的時候,再去研究也不遲。

我是yes,從一點點到億點點,我們下篇見。

參考:

https://blog.csdn.net/dog250/article/details/105896693(select真的受1024限制嗎?)

https://blog.csdn.net/dog250/article/details/50528373


責(zé)任編輯:武曉燕 來源: yes的練級攻略
相關(guān)推薦

2020-11-04 07:49:04

Select

2021-05-31 06:50:47

SelectPoll系統(tǒng)

2025-01-07 00:07:17

2024-07-05 11:01:13

2019-07-31 15:56:57

Jvm虛擬機Content

2022-04-07 13:02:53

前端緩存

2020-09-09 12:55:28

Nginx高并發(fā)性能

2020-09-10 09:31:34

Nginx HTTP代理服務(wù)器

2022-06-03 10:52:55

selectpolepoll

2018-10-12 09:42:00

分布式鎖 Java多線

2024-09-27 13:09:30

2020-07-20 10:20:30

this前端代碼

2025-04-21 04:00:00

2022-09-19 18:49:01

偵聽器異步組件

2022-07-01 13:38:48

霧計算邊緣計算

2024-01-03 13:39:00

JS,Javascrip算法

2023-10-18 10:55:55

HashMap

2025-01-13 16:00:00

服務(wù)網(wǎng)關(guān)分布式系統(tǒng)架構(gòu)

2025-04-11 05:55:00

2017-12-05 17:44:31

機器學(xué)習(xí)CNN卷積層
點贊
收藏

51CTO技術(shù)棧公眾號