0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

深度剖析Linux的epoll機(jī)制

Linux愛(ài)好者 ? 來(lái)源:奇伢云存儲(chǔ) ? 作者:奇伢 ? 2021-07-29 10:52 ? 次閱讀

Linux 系統(tǒng)之中有一個(gè)核心武器:epoll 池,在高并發(fā)的,高吞吐的 IO 系統(tǒng)中常常見(jiàn)到 epoll 的身影。

IO 多路復(fù)用

在 Go 里最核心的是 Goroutine ,也就是所謂的協(xié)程,協(xié)程最妙的一個(gè)實(shí)現(xiàn)就是異步的代碼長(zhǎng)的跟同步代碼一樣。比如在 Go 中,網(wǎng)絡(luò) IO 的 read,write 看似都是同步代碼,其實(shí)底下都是異步調(diào)用,一般流程是:

write ( /* IO 參數(shù) */ )

請(qǐng)求入隊(duì)

等待完成

后臺(tái) loop 程序

發(fā)送網(wǎng)絡(luò)請(qǐng)求

喚醒業(yè)務(wù)方

Go 配合協(xié)程在網(wǎng)絡(luò) IO 上實(shí)現(xiàn)了異步流程的代碼同步化。核心就是用 epoll 池來(lái)管理網(wǎng)絡(luò) fd 。

實(shí)現(xiàn)形式上,后臺(tái)的程序只需要 1 個(gè)就可以負(fù)責(zé)管理多個(gè) fd 句柄,負(fù)責(zé)應(yīng)對(duì)所有的業(yè)務(wù)方的 IO 請(qǐng)求。這種一對(duì)多的 IO 模式我們就叫做 IO 多路復(fù)用。

多路是指?多個(gè)業(yè)務(wù)方(句柄)并發(fā)下來(lái)的 IO 。

復(fù)用是指?復(fù)用這一個(gè)后臺(tái)處理程序。

站在 IO 系統(tǒng)設(shè)計(jì)人員的角度,業(yè)務(wù)方咱們沒(méi)辦法提要求,因?yàn)闃I(yè)務(wù)是上帝,只有你服從的份,他們要?jiǎng)?chuàng)建多個(gè) fd,那么你就需要負(fù)責(zé)這些 fd 的處理,并且最好還要并發(fā)起來(lái)。

業(yè)務(wù)方?jīng)]法提要求,那么只能要求后臺(tái) loop 程序了!

要求什么呢?快!快!快!這就是最核心的要求,處理一定要快,要給每一個(gè) fd 通道最快的感受,要讓每一個(gè) fd 覺(jué)得,你只在給他一個(gè)人跑腿。

那有人又問(wèn)了,那我一個(gè) IO 請(qǐng)求(比如 write )對(duì)應(yīng)一個(gè)線程來(lái)處理,這樣所有的 IO 不都并發(fā)了嗎?是可以,但是有瓶頸,線程數(shù)一旦多了,性能是反倒會(huì)差的。

這里不再對(duì)比多線程和 IO 多路復(fù)用實(shí)現(xiàn)高并發(fā)之間的區(qū)別,詳細(xì)的可以去了解下 nginx 和 redis 高并發(fā)的秘密。

1 最樸實(shí)的實(shí)現(xiàn)方式?

我不用任何其他系統(tǒng)調(diào)用,能否實(shí)現(xiàn) IO 多路復(fù)用?

可以的。那么寫個(gè) for 循環(huán),每次都嘗試 IO 一下,讀/寫到了就處理,讀/寫不到就 sleep 下。這樣我們不就實(shí)現(xiàn)了 1 對(duì)多的 IO 多路復(fù)用嘛。

while True:

for each 句柄數(shù)組 {

read/write(fd, /* 參數(shù) */)

}

sleep(1s)

慢著,有個(gè)問(wèn)題,上面的程序可能會(huì)被卡死在第三行,使得整個(gè)系統(tǒng)不得運(yùn)行,為什么?

默認(rèn)情況下,我們 create 出的句柄是阻塞類型的。我們讀數(shù)據(jù)的時(shí)候,如果數(shù)據(jù)還沒(méi)準(zhǔn)備好,是會(huì)需要等待的,當(dāng)我們寫數(shù)據(jù)的時(shí)候,如果還沒(méi)準(zhǔn)備好,默認(rèn)也會(huì)卡住等待。所以,在上面?zhèn)未a第三行是可能被直接卡死,而導(dǎo)致整個(gè)線程都得到不到運(yùn)行。

舉個(gè)例子,現(xiàn)在有 11,12,13 這 3 個(gè)句柄,現(xiàn)在 11 讀寫都沒(méi)有準(zhǔn)備好,只要 read/write(11, /*參數(shù)*/) 就會(huì)被卡住,但 12,13 這兩個(gè)句柄都準(zhǔn)備好了,那遍歷句柄數(shù)組 11,12,13 的時(shí)候就會(huì)卡死在前面,后面 12,13 則得不到運(yùn)行。這不符合我們的預(yù)期,因?yàn)槲覀?IO 多路復(fù)用的 loop 線程是公共服務(wù),不能因?yàn)橐粋€(gè) fd 就直接癱瘓。

那這個(gè)問(wèn)題怎么解決?

只需要把 fd 都設(shè)置成非阻塞模式。這樣 read/write 的時(shí)候,如果數(shù)據(jù)沒(méi)準(zhǔn)備好,返回 EAGIN 的錯(cuò)誤即可,不會(huì)卡住線程,從而整個(gè)系統(tǒng)就運(yùn)轉(zhuǎn)起來(lái)了。比如上面句柄 11 還未就緒,那么 read/write(11, /*參數(shù)*/) 不會(huì)阻塞,只會(huì)報(bào)個(gè) EAGIN 的錯(cuò)誤,這種錯(cuò)誤需要特殊處理,然后 loop 線程可以繼續(xù)執(zhí)行 12,13 的讀寫。

以上就是最樸實(shí)的 IO 多路復(fù)用的實(shí)現(xiàn)了。但好像在生產(chǎn)環(huán)境沒(méi)見(jiàn)過(guò)這種 IO 多路復(fù)用的實(shí)現(xiàn)?為什么?

因?yàn)檫€不夠高級(jí)。for 循環(huán)每次要定期 sleep 1s,這個(gè)會(huì)導(dǎo)致吞吐能力極差,因?yàn)楹芸赡茉趧偤靡?sleep 的時(shí)候,所有的 fd 都準(zhǔn)備好 IO 數(shù)據(jù),而這個(gè)時(shí)候卻要硬生生的等待 1s,可想而知。。。

那有同學(xué)又要質(zhì)疑了,那 for 循環(huán)里面就不 sleep 嘛,這樣不就能及時(shí)處理了嗎?

及時(shí)是及時(shí)了,但是 CPU 估計(jì)要跑飛了。不加 sleep ,那在沒(méi)有 fd 需要處理的時(shí)候,估計(jì) CPU 都要跑到 100% 了。這個(gè)也是無(wú)法接受的。

糾結(jié)了,那 sleep 吞吐不行,不 sleep 浪費(fèi) cpu,怎么辦?

這種情況用戶態(tài)很難有所作為,只能求助內(nèi)核來(lái)提供機(jī)制協(xié)助來(lái)。因?yàn)閮?nèi)核才能及時(shí)的管理這些事件的通知和調(diào)度。

我們?cè)偈崂硐?IO 多路復(fù)用的需求和原理。IO 多路復(fù)用就是 1 個(gè)線程處理 多個(gè) fd 的模式。我們的要求是:這個(gè) “1” 就要盡可能的快,避免一切無(wú)效工作,要把所有的時(shí)間都用在處理句柄的 IO 上,不能有任何空轉(zhuǎn),sleep 的時(shí)間浪費(fèi)。

有沒(méi)有一種工具,我們把一籮筐的 fd 放到里面,只要有一個(gè) fd 能夠讀寫數(shù)據(jù),后臺(tái) loop 線程就要立馬喚醒,全部馬力跑起來(lái)。其他時(shí)間要把 cpu 讓出去。

能做到嗎?能,但這種需求只能內(nèi)核提供機(jī)制滿足你。

2 這事 Linux 內(nèi)核必須要給個(gè)說(shuō)法?

是的,想要不用 sleep 這種辣眼睛的實(shí)現(xiàn),Linux 內(nèi)核必須出手了,畢竟 IO 的處理都是內(nèi)核之中,數(shù)據(jù)好沒(méi)好內(nèi)核最清楚。

內(nèi)核一口氣提供了 3 種工具 select,poll,epoll 。

為什么有 3 種?

歷史不斷改進(jìn),矬 -》 較矬 -》 臥槽、高效 的演變而已。

Linux 還有其他方式可以實(shí)現(xiàn) IO 多路復(fù)用嗎?

好像沒(méi)有了!

這 3 種到底是做啥的?

這 3 種都能夠管理 fd 的可讀可寫事件,在所有 fd 不可讀不可寫無(wú)所事事的時(shí)候,可以阻塞線程,切走 cpu 。fd 有情況的時(shí)候,都要線程能夠要能被喚醒。

而這三種方式以 epoll 池的效率最高。為什么效率最高?

其實(shí)很簡(jiǎn)單,這里不詳說(shuō),其實(shí)無(wú)非就是 epoll 做的無(wú)用功最少,select 和 poll 或多或少都要多余的拷貝,盲猜(遍歷才知道)fd ,所以效率自然就低了。

舉個(gè)例子,以 select 和 epoll 來(lái)對(duì)比舉例,池子里管理了 1024 個(gè)句柄,loop 線程被喚醒的時(shí)候,select 都是蒙的,都不知道這 1024 個(gè) fd 里誰(shuí) IO 準(zhǔn)備好了。這種情況怎么辦?只能遍歷這 1024 個(gè) fd ,一個(gè)個(gè)測(cè)試。假如只有一個(gè)句柄準(zhǔn)備好了,那相當(dāng)于做了 1 千多倍的無(wú)效功。

epoll 則不同,從 epoll_wait 醒來(lái)的時(shí)候就能精確的拿到就緒的 fd 數(shù)組,不需要任何測(cè)試,拿到的就是要處理的。

epoll 池原理

下面我們看一下 epoll 池的使用和原理。

1 epoll 涉及的系統(tǒng)調(diào)用

epoll 的使用非常簡(jiǎn)單,只有下面 3 個(gè)系統(tǒng)調(diào)用。

epoll_create

epollctl

epollwait

就這?是的,就這么簡(jiǎn)單。

epollcreate 負(fù)責(zé)創(chuàng)建一個(gè)池子,一個(gè)監(jiān)控和管理句柄 fd 的池子;

epollctl 負(fù)責(zé)管理這個(gè)池子里的 fd 增、刪、改;

epollwait 就是負(fù)責(zé)打盹的,讓出 CPU 調(diào)度,但是只要有“事”,立馬會(huì)從這里喚醒;

2 epoll 高效的原理

Linux 下,epoll 一直被吹爆,作為高并發(fā) IO 實(shí)現(xiàn)的秘密武器。其中原理其實(shí)非常樸實(shí):epoll 的實(shí)現(xiàn)幾乎沒(méi)有做任何無(wú)效功。 我們從使用的角度切入來(lái)一步步分析下。

首先,epoll 的第一步是創(chuàng)建一個(gè)池子。這個(gè)使用 epoll_create 來(lái)做:

原型:

int epoll_create(int size);

示例:

epollfd = epoll_create(1024);

if (epollfd == -1) {

perror(“epoll_create”);

exit(EXIT_FAILURE);

}

這個(gè)池子對(duì)我們來(lái)說(shuō)是黑盒,這個(gè)黑盒是用來(lái)裝 fd 的,我們暫不糾結(jié)其中細(xì)節(jié)。我們拿到了一個(gè) epollfd ,這個(gè) epollfd 就能唯一代表這個(gè) epoll 池。注意,這里又有一個(gè)細(xì)節(jié):用戶可以創(chuàng)建多個(gè) epoll 池。

然后,我們就要往這個(gè) epoll 池里放 fd 了,這就要用到 epoll_ctl 了

原型:

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

示例:

if (epoll_ctl(epollfd, EPOLL_CTL_ADD, 11, &ev) == -1) {

perror(“epoll_ctl: listen_sock”);

exit(EXIT_FAILURE);

}

上面,我們就把句柄 11 放到這個(gè)池子里了,op(EPOLL_CTL_ADD)表明操作是增加、修改、刪除,event 結(jié)構(gòu)體可以指定監(jiān)聽(tīng)事件類型,可讀、可寫。

第一個(gè)跟高效相關(guān)的問(wèn)題來(lái)了,添加 fd 進(jìn)池子也就算了,如果是修改、刪除呢?怎么做到快速?

這里就涉及到你怎么管理 fd 的數(shù)據(jù)結(jié)構(gòu)了。

最常見(jiàn)的思路:用 list ,可以嗎?功能上可以,但是性能上拉垮。list 的結(jié)構(gòu)來(lái)管理元素,時(shí)間復(fù)雜度都太高 O(n),每次要一次次遍歷鏈表才能找到位置。池子越大,性能會(huì)越慢。

那有簡(jiǎn)單高效的數(shù)據(jù)結(jié)構(gòu)嗎?

有,紅黑樹(shù)。Linux 內(nèi)核對(duì)于 epoll 池的內(nèi)部實(shí)現(xiàn)就是用紅黑樹(shù)的結(jié)構(gòu)體來(lái)管理這些注冊(cè)進(jìn)程來(lái)的句柄 fd。紅黑樹(shù)是一種平衡二叉樹(shù),時(shí)間復(fù)雜度為 O(log n),就算這個(gè)池子就算不斷的增刪改,也能保持非常穩(wěn)定的查找性能。

現(xiàn)在思考第二個(gè)高效的秘密:怎么才能保證數(shù)據(jù)準(zhǔn)備好之后,立馬感知呢?

epoll_ctl 這里會(huì)涉及到一點(diǎn)。秘密就是:回調(diào)的設(shè)置。在 epoll_ctl 的內(nèi)部實(shí)現(xiàn)中,除了把句柄結(jié)構(gòu)用紅黑樹(shù)管理,另一個(gè)核心步驟就是設(shè)置 poll 回調(diào)。

思考來(lái)了:poll 回調(diào)是什么?怎么設(shè)置?

先說(shuō)說(shuō) file_operations-》poll 是什么?

在 文件描述符 fd 究竟是什么 說(shuō)過(guò),Linux 設(shè)計(jì)成一切皆是文件的架構(gòu),這個(gè)不是說(shuō)說(shuō)而已,而是隨處可見(jiàn)。實(shí)現(xiàn)一個(gè)文件系統(tǒng)的時(shí)候,就要實(shí)現(xiàn)這個(gè)文件調(diào)用,這個(gè)結(jié)構(gòu)體用 struct file_operations 來(lái)表示。這個(gè)結(jié)構(gòu)體有非常多的函數(shù),精簡(jiǎn)了一些,如下:

struct file_operations {

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

__poll_t (*poll) (struct file *, struct poll_table_struct *);

int (*open) (struct inode *, struct file *);

int (*fsync) (struct file *, loff_t, loff_t, int datasync);

// 。。。。

};

你看到了 read,write,open,fsync,poll 等等,這些都是對(duì)文件的定制處理操作,對(duì)于文件的操作其實(shí)都是在這個(gè)框架內(nèi)實(shí)現(xiàn)邏輯而已,比如 ext2 如果有對(duì) read/write 做定制化,那么就會(huì)是 ext2_read,ext2_write,ext4 就會(huì)是 ext4_read,ext4_write。在 open 具體“文件”的時(shí)候會(huì)賦值對(duì)應(yīng)文件系統(tǒng)的 file_operations 給到 file 結(jié)構(gòu)體。

那我們很容易知道 read 是文件系統(tǒng)定制 fd 讀的行為調(diào)用,write 是文件系統(tǒng)定制 fd 寫的行為調(diào)用,file_operations-》poll 呢?

這個(gè)是定制監(jiān)聽(tīng)事件的機(jī)制實(shí)現(xiàn)。通過(guò) poll 機(jī)制讓上層能直接告訴底層,我這個(gè) fd 一旦讀寫就緒了,請(qǐng)底層硬件(比如網(wǎng)卡)回調(diào)的時(shí)候自動(dòng)把這個(gè) fd 相關(guān)的結(jié)構(gòu)體放到指定隊(duì)列中,并且喚醒操作系統(tǒng)

舉個(gè)例子:網(wǎng)卡收發(fā)包其實(shí)走的異步流程,操作系統(tǒng)把數(shù)據(jù)丟到一個(gè)指定地點(diǎn),網(wǎng)卡不斷的從這個(gè)指定地點(diǎn)掏數(shù)據(jù)處理。請(qǐng)求響應(yīng)通過(guò)中斷回調(diào)來(lái)處理,中斷一般拆分成兩部分:硬中斷和軟中斷。poll 函數(shù)就是把這個(gè)軟中斷回來(lái)的路上再加點(diǎn)料,只要讀寫事件觸發(fā)的時(shí)候,就會(huì)立馬通知到上層,采用這種事件通知的形式就能把浪費(fèi)的時(shí)間窗就完全消失了。

劃重點(diǎn):這個(gè) poll 事件回調(diào)機(jī)制則是 epoll 池高效最核心原理。

劃重點(diǎn):epoll 池管理的句柄只能是支持了 file_operations-》poll 的文件 fd。換句話說(shuō),如果一個(gè)“文件”所在的文件系統(tǒng)沒(méi)有實(shí)現(xiàn) poll 接口,那么就用不了 epoll 機(jī)制。

第二個(gè)問(wèn)題:poll 怎么設(shè)置?

在 epoll_ctl 下來(lái)的實(shí)現(xiàn)中,有一步是調(diào)用 vfs_poll 這個(gè)里面就會(huì)有個(gè)判斷,如果 fd 所在的文件系統(tǒng)的 file_operations 實(shí)現(xiàn)了 poll ,那么就會(huì)直接調(diào)用,如果沒(méi)有,那么就會(huì)報(bào)告響應(yīng)的錯(cuò)誤碼。

static inline __poll_t vfs_poll(struct file *file, struct poll_table_struct *pt)

{

if (unlikely(!file-》f_op-》poll))

return DEFAULT_POLLMASK;

return file-》f_op-》poll(file, pt);

}

你肯定好奇 poll 調(diào)用里面究竟是實(shí)現(xiàn)了什么?

總結(jié)概括來(lái)說(shuō):掛了個(gè)鉤子,設(shè)置了喚醒的回調(diào)路徑。epoll 跟底層對(duì)接的回調(diào)函數(shù)是:ep_poll_callback,這個(gè)函數(shù)其實(shí)很簡(jiǎn)單,做兩件事情:

把事件就緒的 fd 對(duì)應(yīng)的結(jié)構(gòu)體放到一個(gè)特定的隊(duì)列(就緒隊(duì)列,ready list);

喚醒 epoll ,活來(lái)啦!

當(dāng) fd 滿足可讀可寫的時(shí)候就會(huì)經(jīng)過(guò)層層回調(diào),最終調(diào)用到這個(gè)回調(diào)函數(shù),把對(duì)應(yīng) fd 的結(jié)構(gòu)體放入就緒隊(duì)列中,從而把 epoll 從 epoll_wait 出喚醒。

這個(gè)對(duì)應(yīng)結(jié)構(gòu)體是什么?

結(jié)構(gòu)體叫做 epitem ,每個(gè)注冊(cè)到 epoll 池的 fd 都會(huì)對(duì)應(yīng)一個(gè)。

就緒隊(duì)列需要用很高級(jí)的數(shù)據(jù)結(jié)構(gòu)嗎?

就緒隊(duì)列就簡(jiǎn)單了,因?yàn)闆](méi)有查找的需求了呀,只要是在就緒隊(duì)列中的 epitem ,都是事件就緒的,必須處理的。所以就緒隊(duì)列就是一個(gè)最簡(jiǎn)單的雙指針鏈表。

小結(jié)下:epoll 之所以做到了高效,最關(guān)鍵的兩點(diǎn):

內(nèi)部管理 fd 使用了高效的紅黑樹(shù)結(jié)構(gòu)管理,做到了增刪改之后性能的優(yōu)化和平衡;

epoll 池添加 fd 的時(shí)候,調(diào)用 file_operations-》poll ,把這個(gè) fd 就緒之后的回調(diào)路徑安排好。通過(guò)事件通知的形式,做到最高效的運(yùn)行;

epoll 池核心的兩個(gè)數(shù)據(jù)結(jié)構(gòu):紅黑樹(shù)和就緒列表。紅黑樹(shù)是為了應(yīng)對(duì)用戶的增刪改需求,就緒列表是 fd 事件就緒之后放置的特殊地點(diǎn),epoll 池只需要遍歷這個(gè)就緒鏈表,就能給用戶返回所有已經(jīng)就緒的 fd 數(shù)組;

3 哪些 fd 可以用 epoll 來(lái)管理?

再來(lái)思考另外一個(gè)問(wèn)題:由于并不是所有的 fd 對(duì)應(yīng)的文件系統(tǒng)都實(shí)現(xiàn)了 poll 接口,所以自然并不是所有的 fd 都可以放進(jìn) epoll 池,那么有哪些文件系統(tǒng)的 file_operations 實(shí)現(xiàn)了 poll 接口?

首先說(shuō),類似 ext2,ext4,xfs 這種常規(guī)的文件系統(tǒng)是沒(méi)有實(shí)現(xiàn)的,換句話說(shuō),這些你最常見(jiàn)的、真的是文件的文件系統(tǒng)反倒是用不了 epoll 機(jī)制的。

那誰(shuí)支持呢?

最常見(jiàn)的就是網(wǎng)絡(luò)套接字:socket 。網(wǎng)絡(luò)也是 epoll 池最常見(jiàn)的應(yīng)用地點(diǎn)。Linux 下萬(wàn)物皆文件,socket 實(shí)現(xiàn)了一套 socket_file_operations 的邏輯( net/socket.c ):

static const struct file_operations socket_file_ops = {

.read_iter = sock_read_iter,

.write_iter = sock_write_iter,

.poll = sock_poll,

// 。。。

};

我們看到 socket 實(shí)現(xiàn)了 poll 調(diào)用,所以 socket fd 是天然可以放到 epoll 池管理的。

還有支持的嗎?

有的,很多。其實(shí) Linux 下還有兩個(gè)很典型的 fd ,常常也會(huì)放到 epoll 池里。

eventfd:eventfd 實(shí)現(xiàn)非常簡(jiǎn)單,故名思義就是專門用來(lái)做事件通知用的。使用系統(tǒng)調(diào)用 eventfd 創(chuàng)建,這種文件 fd 無(wú)法傳輸數(shù)據(jù),只用來(lái)傳輸事件,常常用于生產(chǎn)消費(fèi)者模式的事件實(shí)現(xiàn);

timerfd:這是一種定時(shí)器 fd,使用 timerfd_create 創(chuàng)建,到時(shí)間點(diǎn)觸發(fā)可讀事件;

小結(jié)一下:

ext2,ext4,xfs 等這種真正的文件系統(tǒng)的 fd ,無(wú)法使用 epoll 管理;

socket fd,eventfd,timerfd 這些實(shí)現(xiàn)了 poll 調(diào)用的可以放到 epoll 池進(jìn)行管理;

其實(shí),在 Linux 的模塊劃分中,eventfd,timerfd,epoll 池都是文件系統(tǒng)的一種模塊實(shí)現(xiàn)。

思考

前面我們已經(jīng)思考了很多知識(shí)點(diǎn),有一些簡(jiǎn)單有趣的知識(shí)點(diǎn),提示給讀者朋友,這里只拋磚引玉。

問(wèn)題:?jiǎn)魏?CPU 能實(shí)現(xiàn)并行嗎?

不行。

問(wèn)題:?jiǎn)尉€程能實(shí)現(xiàn)高并發(fā)嗎?

可以。

問(wèn)題:那并發(fā)和并行的區(qū)別是?

一個(gè)看的是時(shí)間段內(nèi)的執(zhí)行情況,一個(gè)看的是時(shí)間時(shí)刻的執(zhí)行情況。

問(wèn)題:?jiǎn)尉€程如何做到高并發(fā)?

IO 多路復(fù)用唄,今天講的 epoll 池就是了。

問(wèn)題:?jiǎn)尉€程實(shí)現(xiàn)并發(fā)的有開(kāi)源的例子嗎?

redis,nginx 都是非常好的學(xué)習(xí)例子。當(dāng)然還有我們 Golang 的 runtime 實(shí)現(xiàn)也盡顯高并發(fā)的設(shè)計(jì)思想。

總結(jié)

IO 多路復(fù)用的原始實(shí)現(xiàn)很簡(jiǎn)單,就是一個(gè) 1 對(duì)多的服務(wù)模式,一個(gè) loop 對(duì)應(yīng)處理多個(gè) fd ;

IO 多路復(fù)用想要做到真正的高效,必須要內(nèi)核機(jī)制提供。因?yàn)?IO 的處理和完成是在內(nèi)核,如果內(nèi)核不幫忙,用戶態(tài)的程序根本無(wú)法精確的抓到處理時(shí)機(jī);

fd 記得要設(shè)置成非阻塞的哦,切記;

epoll 池通過(guò)高效的內(nèi)部管理結(jié)構(gòu),并且結(jié)合操作系統(tǒng)提供的 poll 事件注冊(cè)機(jī)制,實(shí)現(xiàn)了高效的 fd 事件管理,為高并發(fā)的 IO 處理提供了前提條件;

epoll 全名 eventpoll,在 Linux 內(nèi)核下以一個(gè)文件系統(tǒng)模塊的形式實(shí)現(xiàn),所以有人常說(shuō) epoll 其實(shí)本身就是文件系統(tǒng)也是對(duì)的;

socketfd,eventfd,timerfd 這三種”文件“fd 實(shí)現(xiàn)了 poll 接口,所以網(wǎng)絡(luò) fd,事件fd,定時(shí)器fd 都可以使用 epoll_ctl 注冊(cè)到池子里。我們最常見(jiàn)的就是網(wǎng)絡(luò)fd的多路復(fù)用;

ext2,ext4,xfs 這種真正意義的文件系統(tǒng)反倒沒(méi)有提供 poll 接口實(shí)現(xiàn),所以不能用 epoll 池來(lái)管理其句柄。那文件就無(wú)法使用 epoll 機(jī)制了嗎?不是的,有一個(gè)庫(kù)叫做 libaio ,通過(guò)這個(gè)庫(kù)我們可以間接的讓文件使用 epoll 通知事件,以后詳說(shuō),此處不表;

后記

epoll 池使用很簡(jiǎn)潔,但實(shí)現(xiàn)不簡(jiǎn)單。還是那句話,Linux 內(nèi)核幫你包圓了。今天并沒(méi)有羅列太多源碼實(shí)現(xiàn),以很小的思考點(diǎn)為題展開(kāi),簡(jiǎn)單講了一些 epoll 的思考。

編輯:jq

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11314

    瀏覽量

    209807

原文標(biāo)題:深入理解 Linux 的 epoll 機(jī)制

文章出處:【微信號(hào):LinuxHub,微信公眾號(hào):Linux愛(ài)好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    深入探討Linux系統(tǒng)中的動(dòng)態(tài)鏈接庫(kù)機(jī)制

    本文將深入探討Linux系統(tǒng)中的動(dòng)態(tài)鏈接庫(kù)機(jī)制,這其中包括但不限于全局符號(hào)介入、延遲綁定以及地址無(wú)關(guān)代碼等內(nèi)容。 引言 在軟件開(kāi)發(fā)過(guò)程中,動(dòng)態(tài)庫(kù)鏈接問(wèn)題時(shí)常出現(xiàn),這可能導(dǎo)致符號(hào)沖突,從而引起程序運(yùn)行
    的頭像 發(fā)表于 12-18 10:06 ?132次閱讀
    深入探討<b class='flag-5'>Linux</b>系統(tǒng)中的動(dòng)態(tài)鏈接庫(kù)<b class='flag-5'>機(jī)制</b>

    Linux--IO多路復(fù)用(select,poll,epoll)

    ,常用的系統(tǒng)調(diào)用包括select()、poll()和epoll()。這些機(jī)制允許程序監(jiān)視多個(gè)描述符,一旦某個(gè)描述符就緒(通常是讀就緒或?qū)懢途w),程序就會(huì)被通知進(jìn)行相應(yīng)的讀寫操作。這個(gè)過(guò)程通常涉及兩個(gè)階段
    的頭像 發(fā)表于 11-06 16:13 ?331次閱讀

    深度解析linux HID核心

    linux內(nèi)核中,HID核心是完成HID功能的關(guān)鍵組件,如果內(nèi)核支持HID,在啟動(dòng)過(guò)程中,則會(huì)對(duì)HID進(jìn)行初始化,完成該操作的函數(shù)是hid_init(),實(shí)現(xiàn)在/drivers/hid/hid-core.c中。
    的頭像 發(fā)表于 09-29 17:04 ?506次閱讀
    <b class='flag-5'>深度</b>解析<b class='flag-5'>linux</b> HID核心

    詳解linux內(nèi)核的uevent機(jī)制

    linux內(nèi)核中,uevent機(jī)制是一種內(nèi)核和用戶空間通信的機(jī)制,用于通知用戶空間應(yīng)用程序各種硬件更改或其他事件,比如插入或移除硬件設(shè)備(如USB驅(qū)動(dòng)器或網(wǎng)絡(luò)接口)。uevent表示“用戶空間
    的頭像 發(fā)表于 09-29 17:01 ?773次閱讀

    深度解析linux時(shí)鐘子系統(tǒng)

    linux內(nèi)核中實(shí)現(xiàn)了一個(gè)CLK子系統(tǒng),用于對(duì)上層提供各模塊(例如需要時(shí)鐘信號(hào)的外設(shè),USB等)的時(shí)鐘驅(qū)動(dòng)接口,對(duì)下層提供具體SOC的時(shí)鐘操作細(xì)節(jié)。
    的頭像 發(fā)表于 09-29 16:46 ?516次閱讀
    <b class='flag-5'>深度</b>解析<b class='flag-5'>linux</b>時(shí)鐘子系統(tǒng)

    安全加速新選擇:深度剖析境外SOCKS5代理的優(yōu)勢(shì)與應(yīng)用

    在數(shù)字化時(shí)代,跨境網(wǎng)絡(luò)訪問(wèn)已成為日常生活和工作中不可或缺的一部分。然而,網(wǎng)絡(luò)限制、延遲和安全問(wèn)題常常阻礙著我們的順暢體驗(yàn)。境外SOCKS5代理作為一種高效、安全的網(wǎng)絡(luò)解決方案,正逐漸成為許多用戶的新選擇。以下是對(duì)其優(yōu)勢(shì)與應(yīng)用的深度剖析。
    的頭像 發(fā)表于 09-25 08:12 ?210次閱讀

    IPv6 Sec機(jī)制深度解析與優(yōu)勢(shì)探討

    IPv6的sec機(jī)制,主要指的是IPv6協(xié)議中內(nèi)置的安全機(jī)制,特別是通過(guò)IP Sec協(xié)議集來(lái)實(shí)現(xiàn)的。IPv6在設(shè)計(jì)之初就考慮到了安全性問(wèn)題,并內(nèi)置了對(duì)IP Sec的支持,這使得IPv6網(wǎng)絡(luò)在安全性能上相比IPv4有了顯著的提升。
    的頭像 發(fā)表于 09-07 10:12 ?342次閱讀
    IPv6 Sec<b class='flag-5'>機(jī)制</b>的<b class='flag-5'>深度</b>解析與優(yōu)勢(shì)探討

    從設(shè)計(jì)到實(shí)施:樓宇自控系統(tǒng)建設(shè)流程的深度剖析

    從設(shè)計(jì)到實(shí)施:樓宇自控系統(tǒng)建設(shè)流程的深度剖析 在探索現(xiàn)代建筑智能化的征途中,樓宇自控系統(tǒng)(BAS)無(wú)疑是引領(lǐng)變革的關(guān)鍵力量。它不僅深刻改變了建筑的管理模式,還極大地提升了建筑的運(yùn)營(yíng)效率與居住體驗(yàn)。從
    的頭像 發(fā)表于 08-27 16:37 ?317次閱讀

    MEMS 可編程振蕩器的卓越代表:SiT9121 系列(1 to 220 MHZ)深度剖析

    MEMS 可編程振蕩器的卓越代表:SiT9121 系列(1 to 220 MHZ)深度剖析
    的頭像 發(fā)表于 08-13 10:56 ?553次閱讀
    MEMS 可編程振蕩器的卓越代表:SiT9121 系列(1 to 220 MHZ)<b class='flag-5'>深度</b><b class='flag-5'>剖析</b>

    探索巔峰性能 | 迅為RK3588開(kāi)發(fā)板深度剖析

    探索巔峰性能 | 迅為RK3588開(kāi)發(fā)板深度剖析
    的頭像 發(fā)表于 08-12 14:07 ?924次閱讀
    探索巔峰性能 | 迅為RK3588開(kāi)發(fā)板<b class='flag-5'>深度</b><b class='flag-5'>剖析</b>

    Linux內(nèi)核中的頁(yè)面分配機(jī)制

    Linux內(nèi)核中是如何分配出頁(yè)面的,如果我們站在CPU的角度去看這個(gè)問(wèn)題,CPU能分配出來(lái)的頁(yè)面是以物理頁(yè)面為單位的。也就是我們計(jì)算機(jī)中常講的分頁(yè)機(jī)制。本文就看下Linux內(nèi)核是如何管理,釋放和分配這些物理頁(yè)面的。
    的頭像 發(fā)表于 08-07 15:51 ?307次閱讀
    <b class='flag-5'>Linux</b>內(nèi)核中的頁(yè)面分配<b class='flag-5'>機(jī)制</b>

    表面貼裝低相位噪音晶體振蕩器 DSO531SHH 深度剖析

    表面貼裝低相位噪音晶體振蕩器 DSO531SHH 深度剖析
    的頭像 發(fā)表于 07-26 14:12 ?403次閱讀
    表面貼裝低相位噪音晶體振蕩器 DSO531SHH <b class='flag-5'>深度</b><b class='flag-5'>剖析</b>

    深入剖析系統(tǒng)調(diào)用機(jī)制Linux 0.12源碼

    CPU指令集,就是CPU中用來(lái)計(jì)算和控制計(jì)算機(jī)系統(tǒng)的一套指令的集合,實(shí)現(xiàn)軟件指揮硬件執(zhí)行的媒介,常見(jiàn)的CPU指令集有X86、ARM、MIPS、Alpha、RISC等
    發(fā)表于 04-19 14:29 ?620次閱讀
    深入<b class='flag-5'>剖析</b>系統(tǒng)調(diào)用<b class='flag-5'>機(jī)制</b>的<b class='flag-5'>Linux</b> 0.12源碼

    深入剖析Shell命令運(yùn)行機(jī)制與權(quán)限管理

    Linux作為一個(gè)操作系統(tǒng),稱為"kernel",一般用戶不能直接使用kernel,而通過(guò)"kernel"的外殼程序"Shell"和kernel進(jìn)行溝通。
    發(fā)表于 03-08 12:36 ?645次閱讀
    深入<b class='flag-5'>剖析</b>Shell命令運(yùn)行<b class='flag-5'>機(jī)制</b>與權(quán)限管理

    Linux 5.15.71上使用CYW43455及其godzilla ,網(wǎng)絡(luò)數(shù)據(jù)未通過(guò)的原因?

    我們?cè)?Linux 5.15.71 上使用 CYW43455 及其 godzilla 版本。 有時(shí),當(dāng)設(shè)備從深度睡眠中恢復(fù)時(shí),CYW43455 顯示已連接(靠近或遠(yuǎn)離 AP 時(shí) RSSI 正在改變
    發(fā)表于 03-01 17:33