成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

從網(wǎng)絡(luò)I/O模型到Netty,先深入了解下I/O多路復(fù)用

網(wǎng)絡(luò) 網(wǎng)絡(luò)管理
上一篇文章我們了解了Unix標(biāo)準(zhǔn)的5種網(wǎng)絡(luò)I/O模型,知道了它們的核心區(qū)別與各自的優(yōu)缺點(diǎn)。尤其是I/O多路復(fù)用模型,在高并發(fā)場(chǎng)景下,有著非常好的優(yōu)勢(shì)。而Netty也采用了I/O多路復(fù)用模型。

 上一篇文章我們了解了Unix標(biāo)準(zhǔn)的5種網(wǎng)絡(luò)I/O模型,知道了它們的核心區(qū)別與各自的優(yōu)缺點(diǎn)。尤其是I/O多路復(fù)用模型,在高并發(fā)場(chǎng)景下,有著非常好的優(yōu)勢(shì)。而Netty也采用了I/O多路復(fù)用模型。

那Netty是如何實(shí)現(xiàn)I/O多路復(fù)用的呢?

Netty實(shí)際上也是一個(gè)封裝好的框架,它的本質(zhì)上還是使用了Java的NIO包(New IO,不是網(wǎng)絡(luò)I/O模型的NIO,Nonblocking IO)包,Java NIO包里面使用了I/O多路復(fù)用。

所以,本文作為一個(gè) 前置知識(shí) + 高頻面試題 章節(jié)(手動(dòng)狗頭),一起來(lái)深入了解下I/O多路復(fù)用模型吧。

[[381555]]

本文預(yù)計(jì)閱讀時(shí)間 5分鐘,將重點(diǎn)回答以下兩個(gè)問(wèn)題:

  • I/O多路復(fù)用模式有哪些實(shí)現(xiàn)?select/poll/epoll
  • select/poll/epoll有什么區(qū)別

1.I/O多路復(fù)用模式的實(shí)現(xiàn)

這是我們上一篇講I/O多路復(fù)用使用的圖,可以再回顧一下I/O多路復(fù)用模型。


多個(gè)的進(jìn)程的IO可以注冊(cè)到一個(gè)復(fù)用器(selector)上,然后用一個(gè)進(jìn)程調(diào)用select,select會(huì)監(jiān)聽(tīng)所有注冊(cè)進(jìn)來(lái)的IO。

舉個(gè)例子。

在BIO模式中,一個(gè)老師(應(yīng)用進(jìn)程/線程)只能同時(shí)處理一個(gè)同學(xué)(IO流)的問(wèn)題。如果有10個(gè)同學(xué),就需要配置10個(gè)老師來(lái)做一對(duì)一的講解。

在IO多路復(fù)用模型中。我們給 老師 配置了一個(gè) 班長(zhǎng)(復(fù)用器Selector)。班長(zhǎng) 負(fù)責(zé)觀察班級(jí)里的10個(gè)同學(xué)誰(shuí)要提問(wèn),一旦有同學(xué)舉手,班長(zhǎng)就反饋老師去處理這個(gè)舉手同學(xué)的問(wèn)題。

這樣一來(lái),只需要1個(gè)老師,老師 只需要注意 班長(zhǎng) 的反饋,就能及時(shí)處理對(duì)應(yīng)的 同學(xué) 的問(wèn)題了。

下面我們具體來(lái)看看I/O多路復(fù)用的三種實(shí)現(xiàn):select、poll、epoll。

  • 需要注意的是,select,poll,epoll都是IO多路復(fù)用的實(shí)現(xiàn)方式,而且本質(zhì)上都是同步I/O,因?yàn)樗鼈兌夹枰谧x寫(xiě)事件就緒后自己負(fù)責(zé)進(jìn)行讀寫(xiě),也就是說(shuō)這個(gè)讀寫(xiě)過(guò)程是阻塞的。
  • 而異步I/O則無(wú)需自己負(fù)責(zé)進(jìn)行讀寫(xiě),異步I/O的實(shí)現(xiàn)會(huì)負(fù)責(zé)把數(shù)據(jù)從內(nèi)核拷貝到用戶空間。

2. select

Linux系統(tǒng)提供了一個(gè)函數(shù)select來(lái)供開(kāi)發(fā)者使用select多路復(fù)用機(jī)制。


該函數(shù)的作用是:

通過(guò)輪詢,可以同時(shí)監(jiān)視多個(gè)文件描述符是否發(fā)生了讀、寫(xiě)、異常這三類IO事件。

最后返回發(fā)生IO事件的文件描述符數(shù)量,以及讀事件、寫(xiě)事件、異常事件這三種事件分別發(fā)生在哪些文件描述符中(readfds、writefds、errorfds三個(gè)參數(shù))。

  • 文件描述符(File descriptor)是計(jì)算機(jī)中的一個(gè)術(shù)語(yǔ),用于表述指向文件的引用的抽象化概念。
  • Linux下一切皆文件,包括IO設(shè)備也是。因此要對(duì)某個(gè)設(shè)備進(jìn)行操作,就需要打開(kāi)此設(shè)備文件,打開(kāi)文件就會(huì)獲得該文件的文件描述符fd( file discriptor),它就是一個(gè)很小的整數(shù)。

我們結(jié)合 老師-班長(zhǎng)-同學(xué) 的模型來(lái)理解下這個(gè)過(guò)程。

  • 老師把學(xué)生名單(xxxxfds)給班長(zhǎng),讓班長(zhǎng)關(guān)注班級(jí)里的所有同學(xué)。
  • 班長(zhǎng)時(shí)刻輪訓(xùn)班級(jí)里每個(gè)同學(xué)的狀態(tài)(輪訓(xùn)所有fd_set),直到 超時(shí) 或者 有同學(xué)舉手。
  • 一旦有同學(xué)舉手,班長(zhǎng)就會(huì)把學(xué)生名單上有變化的學(xué)生名字做標(biāo)記,并把一共多少個(gè)學(xué)生有變化返回給 老師。
  • 老師可以獲得舉手同學(xué)的數(shù)量,并在學(xué)生名單(xxxxfds)上看的有哪幾個(gè)同學(xué)發(fā)生了事件(讀、寫(xiě)、異常)。
  • 老師拿到學(xué)生名單后,輪訓(xùn)班級(jí)里面的每個(gè)同學(xué)狀態(tài),根據(jù)具體的 讀、寫(xiě)、異常事件 來(lái)進(jìn)行IO處理。

特別注意,在select函數(shù)下,老師僅僅知道有學(xué)生發(fā)生變化了,但到底是哪些學(xué)生發(fā)生變化,他需要 輪詢 一遍同學(xué)名單(xxxfds),找出舉手的同學(xué),然后和他進(jìn)行交流。

select的缺點(diǎn)比較明顯:

  • 具有O(n)的無(wú)差別輪詢時(shí)間復(fù)雜度,每次調(diào)用需要輪詢fd_set,同時(shí)處理得越多,輪詢時(shí)間就越長(zhǎng)。
  • 每次調(diào)用select函數(shù),都需要把 所有 fd_set從 用戶態(tài) 拷貝到 內(nèi)核態(tài) 進(jìn)行輪訓(xùn),如果fd_set比較大,對(duì)性能影響就非常大。

3. poll

poll的實(shí)現(xiàn)和select非常相似,我們就不重復(fù)說(shuō)明了,直接介紹一下區(qū)別。poll函數(shù)如下:


主要是描述fd集合的方式不同,poll使用pollfd結(jié)構(gòu)而不是fd_set結(jié)構(gòu),pollfd結(jié)構(gòu)使用鏈表而非數(shù)組,這導(dǎo)致pollfd的長(zhǎng)度沒(méi)有限制。但是如果pollfd長(zhǎng)度過(guò)大,會(huì)導(dǎo)致性能下降。

除此之外,二者的原理基本一致,即對(duì)多個(gè)描述符也是進(jìn)行輪詢,根據(jù)描述符的狀態(tài)進(jìn)行處理。

因此,二者的缺陷也基本一致。

4. epoll

epoll的全稱是eventpoll,它是基于event事件進(jìn)行實(shí)現(xiàn)的,是linux特有的I/O復(fù)用函數(shù)。

它在實(shí)現(xiàn)和使用上和select\poll有很大差別:

  • epoll通過(guò) 一組函數(shù) 來(lái)完成任務(wù),而不是單個(gè)函數(shù)。
  • epoll把用戶關(guān)心的文件描述符fd放在一個(gè) 事件表 中,而不是像select/poll那樣把所有文件描述符集合(fds)傳來(lái)傳去。
  • epoll需要一個(gè)額外的文件描述符fd來(lái)表示這個(gè) 事件表。

不同于select使用三個(gè)fd_set來(lái)對(duì)應(yīng)讀/寫(xiě)/異常的IO變化,epoll專門(mén)定義了一個(gè)epoll_event結(jié)構(gòu)體,將其作為讀/寫(xiě)/異常的IO變化的邏輯封裝,稱為事件(event)。


4.1 epoll的三個(gè)核心函數(shù)

epoll把原先的select/poll調(diào)用分成了3個(gè)函數(shù)。


  • 調(diào)用int epoll_create(int size)建立一個(gè)epoll句柄對(duì)象,返回一個(gè)文件描述符fd,指向 事件表。在linux下如果查看/proc/進(jìn)程id/fd/,是能夠看到這個(gè)fd的,所以在使用完epoll后,必須調(diào)用close()關(guān)閉,否則可能導(dǎo)致fd被耗盡。
  • 參數(shù)size并不是限制了epoll所能監(jiān)聽(tīng)的描述符最大個(gè)數(shù),只是對(duì)內(nèi)核初始分配內(nèi)部數(shù)據(jù)結(jié)構(gòu)的一個(gè)建議。

  • 調(diào)用epoll_ctl向epoll對(duì)象中添加連接的套接字。
  • epfd就是epoll_creat返回的事件表id。
  • op表示具體操作。包括添加fd的監(jiān)聽(tīng)事件EPOLL_CTL_ADD、刪除fd的監(jiān)聽(tīng)事件EPOLL_CTL_DEL、修改fd的監(jiān)聽(tīng)事件EPOLL_CTL_MOD。
  • fd是需要監(jiān)聽(tīng)的fd(文件描述符)
  • event是告訴內(nèi)核需要監(jiān)聽(tīng)哪個(gè)事件

  • 調(diào)用epoll_wait收集發(fā)生的事件的連接
  • 返回值表示已經(jīng)準(zhǔn)備繼續(xù)的文件描述符的總數(shù)。
  • epfd表示事件表id。
  • events表示 準(zhǔn)備就緒的事件數(shù)組。event_wait如果檢測(cè)到事件,就把就緒的事件從 事件表 中復(fù)制到這個(gè)數(shù)組中。(比select/poll高效的地方!!)
  • maxevents表示最多監(jiān)聽(tīng)多少事件。

4.2 epoll的實(shí)現(xiàn)原理

當(dāng)某一進(jìn)程調(diào)用 epoll_create()方法 時(shí),內(nèi)核空間會(huì)創(chuàng)建一個(gè)eventpoll結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體中有兩個(gè)成員變量與epoll的使用方式密切相關(guān),結(jié)構(gòu)體如下所示:


  • 紅黑樹(shù)根節(jié)點(diǎn)rbr:紅黑樹(shù)的根節(jié)點(diǎn),這顆樹(shù)中存儲(chǔ)著所有添加到epoll中的需要監(jiān)控的事件
  • 鏈表rdlist:鏈表中則存放著將要通過(guò)epoll_wait返回給用戶的滿足條件的事件

epoll_ctl()方法 將新添加的監(jiān)控事件event加入到 紅黑樹(shù)rbr 中。還會(huì)給內(nèi)核中斷處理程序注冊(cè)一個(gè) 回調(diào)函數(shù),告訴內(nèi)核,如果這個(gè)句柄的中斷到了,就把它放到準(zhǔn)備就緒list鏈表里。

一旦基于某個(gè)文件描述符就緒時(shí),內(nèi)核會(huì)采用類似callback的回調(diào)機(jī)制,迅速激活這個(gè)文件描述符,被觸發(fā)的事件會(huì)被 回調(diào)函數(shù) 加入eventpoll的 鏈表rdlist 中。

當(dāng)調(diào)用 epoll_wait()方法 檢查是否有事件發(fā)生時(shí),只需要檢查eventpoll對(duì)象中的rdlist鏈表中是否有元素即可。如果鏈表中有數(shù)據(jù)的話,就把對(duì)應(yīng)有修改的事件event復(fù)制到epoll_wait()方法的events數(shù)組變量中,用戶就能獲得了。

  • 對(duì)比select/poll,我們可以看到此處不需要遍歷監(jiān)聽(tīng)的文件描述符,這正是epoll的魅力所在。

如此一來(lái),epoll_wait的效率就非常高了。因?yàn)檎{(diào)用epoll_wait時(shí),不需要向操作系統(tǒng)復(fù)制所有的連接的句柄數(shù)據(jù),內(nèi)核也不需要去遍歷全部的連接。

4.3 epoll中有使用共享內(nèi)存嗎?

很多博客提到了這點(diǎn):

  • epoll_wait返回時(shí),對(duì)于就緒的事件,epoll使用的是共享內(nèi)存的方式,即用戶態(tài)和內(nèi)核態(tài)都指向了就緒鏈表,所以就避免了內(nèi)存拷貝消耗

但是事實(shí)確實(shí)如此嗎?

源碼面前無(wú)密碼,我們直接看下源碼吧。

參考eventpoll.c的源碼。

https://github.com/torvalds/linux/blob/master/fs/eventpoll.c

具體的epoll_wait調(diào)用關(guān)系如下圖所示。


我們可以在put_user中看到具體的說(shuō)明。


因此,事件確實(shí)是從內(nèi)核空間拷貝到用戶空間的,并沒(méi)有使用共享內(nèi)存。

5.三種實(shí)現(xiàn)對(duì)比

通過(guò)上面的分析,相信大家都已經(jīng)了解了select/poll/epoll的實(shí)現(xiàn)。

下面通過(guò)一個(gè)表格來(lái)總結(jié)他們的主要區(qū)別。

從整體來(lái)看,epoll的實(shí)現(xiàn)性能是比select/poll更好的。

當(dāng)然,如果保持活躍的連接一直非常多,epoll_wait的效率就不一定高了,因?yàn)榇藭r(shí)epoll_wait的回調(diào)函數(shù)觸發(fā)過(guò)于頻繁。

因此,epoll最適合的場(chǎng)景是連接數(shù)量很多,但是活躍連接數(shù)量不多的情況。

參考書(shū)目:

《Linux高性能服務(wù)器編程》

往期熱門(mén)筆記合集推薦:

  • HBase原理與實(shí)戰(zhàn)筆記合集
  • MySQL實(shí)戰(zhàn)筆記合集
  • Canal/Otter源碼與實(shí)戰(zhàn)筆記合集
  • Java實(shí)戰(zhàn)技巧筆記合集

 

責(zé)任編輯:姜華 來(lái)源: 阿丸筆記
相關(guān)推薦

2021-03-24 08:03:38

NettyJava NIO網(wǎng)絡(luò)技術(shù)

2023-05-08 00:06:45

Go語(yǔ)言機(jī)制

2017-01-19 19:24:29

Linux重定向

2020-10-13 07:51:03

五種IO模型

2021-03-17 16:53:51

IO多路

2025-05-08 08:01:05

2019-12-23 14:53:26

IO復(fù)用

2024-12-30 00:00:05

2023-11-08 09:22:14

I/ORedis阻塞

2021-06-09 19:25:13

IODubbo

2020-12-11 11:04:07

NettyIO

2021-01-19 06:43:10

Netty框架網(wǎng)絡(luò)技術(shù)

2022-12-08 09:10:11

I/O模型Java

2020-06-03 17:30:42

LinuxIO

2023-08-07 08:52:03

Java多路復(fù)用機(jī)制

2020-12-01 07:08:23

Linux網(wǎng)絡(luò)I

2015-02-09 16:01:18

服務(wù)器

2025-06-16 09:46:06

2021-10-13 06:49:15

網(wǎng)絡(luò) IO

2013-05-28 10:08:41

IO輸出
點(diǎn)贊
收藏

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

主站蜘蛛池模板: 久国产 | 日韩久久精品视频 | 蜜臀久久99精品久久久久久宅男 | 九九在线视频 | 国产精品精品 | 91极品视频 | 亚洲国产视频一区二区 | 国产精品久久久久久久久久久久午夜片 | av香蕉 | 不卡一二区 | 中文字幕精品一区 | 一级黄色大片 | 暖暖日本在线视频 | 亚洲精品福利视频 | 午夜电影网 | 国产视频久久久 | 久草在线| 亚洲欧美在线观看 | 国产视频一视频二 | 黄色网络在线观看 | 亚洲二区精品 | 欧美黑人一级爽快片淫片高清 | 日韩精品| 国产精品久久久久久久久免费桃花 | 久久久99精品免费观看 | 欧美日高清视频 | 色网站入口 | 一区二区三区精品视频 | 天天亚洲| 中国黄色毛片视频 | 亚洲男人网| 成人免费激情视频 | 欧洲一级毛片 | 久草资源 | 精品综合久久 | 国产高清免费 | 亚洲国产一区二区三区 | 一区二区三区在线观看视频 | 国产精品久久久久久久久久尿 | 欧美日韩国产一区二区 | 免费精品|