1.服務(wù)端處理網(wǎng)絡(luò)請(qǐng)求過程
IO分為:
網(wǎng)絡(luò)IO:本質(zhì)是socket文件讀取;上圖中紅線部分
磁盤IO:上圖中藍(lán)線部分
由上圖可知:IO都分為了兩個(gè)階段
1.將數(shù)據(jù)從文件先加載至內(nèi)核內(nèi)存空間(緩沖區(qū))--時(shí)間長(zhǎng)
2.將數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到用戶空間的進(jìn)程的內(nèi)存中--時(shí)間短
服務(wù)端處理網(wǎng)絡(luò)請(qǐng)求的過程
1.客戶端發(fā)起請(qǐng)求,數(shù)據(jù)發(fā)送到網(wǎng)卡
2.將網(wǎng)卡中數(shù)據(jù)通過DMA機(jī)制復(fù)制到內(nèi)核緩沖區(qū)
3.內(nèi)核緩沖區(qū)復(fù)制到用戶空間(比如要:GET index.html)
-------------------
4.web服務(wù)器收到請(qǐng)求數(shù)據(jù),因?yàn)閼?yīng)用程序無(wú)法與硬件打交道,所以向內(nèi)核空間發(fā)起系統(tǒng)調(diào)用
5.內(nèi)核空間收到信息,cpu發(fā)出指令,讓DMA設(shè)備去磁盤獲取index.html,并拷貝到內(nèi)核緩沖區(qū)
6.內(nèi)核緩沖區(qū)再?gòu)?fù)制到用戶空間
-------------------
7.web服務(wù)器收到數(shù)據(jù),然后構(gòu)建響應(yīng)報(bào)文,再發(fā)送給內(nèi)核空間的socket buffer
8.socket buffer 在發(fā)送給網(wǎng)卡
9.通過網(wǎng)卡把響應(yīng)報(bào)文發(fā)送給客戶端
2.什么是DMA
DMA:直接內(nèi)存訪問
PIO 程序的輸入輸出模型,--所有操作都需要CPU參與
DMA 直接內(nèi)存訪問;只需要cpu發(fā)送指令,讓DMA設(shè)備去把磁盤數(shù)據(jù)拷貝到內(nèi)存,降低CPU的使用率
3.IO模型
同步,異步,阻塞,非阻塞的理解
比如上圖:A是領(lǐng)導(dǎo) B是員工
現(xiàn)在A讓B去做一件事情
同步就是:領(lǐng)導(dǎo)自己去詢問事情是否做完
異步就是:員工主動(dòng)告知領(lǐng)導(dǎo)事情的進(jìn)度
阻塞就是:事情非常緊急需要馬上解決,領(lǐng)導(dǎo)必須盯著員工把事情做完,不能干其他的
非阻塞:領(lǐng)導(dǎo)在員工做事情時(shí),可以去喝喝茶,打打高爾夫...等等
A對(duì)應(yīng):應(yīng)用程序
B對(duì)應(yīng):內(nèi)核
只有讓內(nèi)核多做事情,應(yīng)用程序少做,這樣才能實(shí)現(xiàn)高并發(fā)
衍生出
同步阻塞,同步非阻塞,異步阻塞,異步非阻塞
場(chǎng)景:用燒水壺?zé)?br/>
同步阻塞就是:買的燒水壺是無(wú)聲的,為了把水燒開,并且不能讓水噴出來(lái),就只能守著燒
同步非阻塞:買的燒水壺也是無(wú)聲的,只需要把水燒開就行,不管開水會(huì)不會(huì)噴出,所以我就可以在燒水時(shí),去看看電視
偶爾去看看水是否燒開就行
異步阻塞:這次買的燒水壺可以語(yǔ)音提示,燒開了會(huì)提醒,如果我此時(shí)還守著,不是有病嘛,所以這沒意義
異步非阻塞:水燒開自動(dòng)提醒我,在燒水時(shí)我可以開心看電視
4.五中IO模型
阻塞型、非阻塞型、復(fù)用型、信號(hào)驅(qū)動(dòng)型、異步
4.1.阻塞型
理解場(chǎng)景:釣魚
去釣魚,那人必須守著
數(shù)據(jù)準(zhǔn)備好:就好比魚兒上鉤,這個(gè)過程是很漫長(zhǎng)的
復(fù)制完成:好比當(dāng)魚兒上鉤,把魚兒放到桶里,這個(gè)過程很快
在釣魚的過程中,為了不讓魚兒跑掉,你必須一直守著,不能干其他的;所以一直是阻塞的
4.2.非阻塞型
理解場(chǎng)景: 釣魚,釣魚旁邊有個(gè)麻將館
釣魚時(shí),把魚竿搞好,就去麻將館打麻將了...
打一局,我就去看看魚兒上鉤沒有...
當(dāng)魚兒上鉤了,這個(gè)時(shí)候我必須在場(chǎng)把魚兒放在桶里
在魚兒上鉤過程中,我都跑去打麻將了,只是偶爾去看看魚兒是否上鉤,也只有把魚放在桶里這階段我在場(chǎng),所以
只有這段時(shí)間是阻塞的
4.3.復(fù)用型
理解場(chǎng)景:我雇了一只貓去守著釣魚,而且這次拿了很多釣竿
讓貓?jiān)诤舆吺刂?,但是呢,我又怕貓偷吃,所以我也只能盯著貓不能去打麻將了,這階段我是阻塞的
當(dāng)有釣竿魚上鉤了,貓就叫我,然后我就過去把魚放在桶里,所以我從頭到尾一直都是阻塞的....
4.4.信號(hào)驅(qū)動(dòng)型
理解場(chǎng)景:我買了一個(gè)帶語(yǔ)音提示的魚竿
這次,我把魚竿搞好,我也依然去旁邊的麻將館打麻將了,
當(dāng)魚兒上鉤了,就語(yǔ)音提示我,主人,主人,魚上鉤了,然后我就去把魚兒放在桶里
只有把魚放在桶里,我需要去河邊,這段時(shí)間我是阻塞的
4.5.異步
理解場(chǎng)景:由于前幾次都是空軍,這次我就聘請(qǐng)了一個(gè)釣魚達(dá)人去釣
釣魚達(dá)人在釣魚,我就去打麻將..
我麻將打完了,釣魚達(dá)人,就把魚兒給我了
5.五種I/O模型比較
阻塞型:一直都阻塞
非阻塞:只有魚兒放桶里是阻塞
復(fù)用型:也是一直阻塞
信號(hào)驅(qū)動(dòng):只有魚兒放桶里是阻塞
異步:完全不阻塞
6.模型實(shí)現(xiàn)的方式
Select: #Linux實(shí)現(xiàn) 對(duì)應(yīng)I/O復(fù)用模型 BSD4.2最早實(shí)現(xiàn),POSIX標(biāo)準(zhǔn),一般操作系統(tǒng)均有實(shí)現(xiàn)
Poll: #Linux實(shí)現(xiàn), 對(duì)應(yīng)I/O復(fù)用模型 System V unix最早實(shí)現(xiàn)
Epoll: #Linux特有, 對(duì)應(yīng)I/O復(fù)用模型 具有信號(hào)驅(qū)動(dòng)I/O模型的某些特性
Kqueue: #FreeBSD實(shí)現(xiàn), 對(duì)應(yīng)I/O復(fù)用模型 具有信號(hào)驅(qū)動(dòng)I/O模型某些特性
/dev/poll:#SUN的Solaris實(shí)現(xiàn) 對(duì)應(yīng)I/O復(fù)用模型 具有信號(hào)驅(qū)動(dòng)I/O模型的某些特性
Iocp #Windows實(shí)現(xiàn), 對(duì)應(yīng)第5種(異步I/O)模型
7.select/poll/epoll
7.1.select
Select:POSIX所規(guī)定,目前幾乎在所有的平臺(tái)上支持,其良好跨平臺(tái)支持也是它的一個(gè)優(yōu)點(diǎn),本質(zhì)上是通過設(shè)置或
者檢查存放fd標(biāo)志位的數(shù)據(jù)結(jié)構(gòu)來(lái)進(jìn)行下一步處理
#缺點(diǎn)
1)單個(gè)進(jìn)程能夠監(jiān)視的文件描述符的數(shù)量存在最大限制,在Linux上一般為1024,可以通過修改宏定義FD_SETSIZE,
再重新編譯內(nèi)核實(shí)現(xiàn),但是這樣也會(huì)造成效率的降低
2)單個(gè)進(jìn)程可監(jiān)視的fd數(shù)量被限制,默認(rèn)是1024,修改此值需要重新編譯內(nèi)核
3)對(duì)socket是線性掃描,即采用輪詢的方法,效率較低
4)select 采取了內(nèi)存拷貝方法來(lái)實(shí)現(xiàn)內(nèi)核將 FD 消息通知給用戶空間,這樣一個(gè)用來(lái)存放大量fd的數(shù)據(jù)結(jié)構(gòu),
這樣會(huì)使得用戶空間和內(nèi)核空間在傳遞該結(jié)構(gòu)時(shí)復(fù)制開銷大
7.2.poll
1)本質(zhì)上和select沒有區(qū)別,它將用戶傳入的數(shù)組拷貝到內(nèi)核空間,然后查詢每個(gè)fd對(duì)應(yīng)的設(shè)備狀態(tài)
2)其沒有最大連接數(shù)的限制,原因是它是基于鏈表來(lái)存儲(chǔ)的
3)大量的fd的數(shù)組被整體復(fù)制于用戶態(tài)和內(nèi)核地址空間之間,而不管這樣的復(fù)制是不是有意義
4)poll特點(diǎn)是"水平觸發(fā)",如果報(bào)告了fd后,沒有被處理,那么下次poll時(shí)會(huì)再次報(bào)告該fd
7.3.epoll
epoll:在Linux 2.6內(nèi)核中提出的select和poll的增強(qiáng)版本
支持水平觸發(fā)LT和邊緣觸發(fā)ET,最大的特點(diǎn)在于邊緣觸發(fā),它只告訴進(jìn)程哪些fd剛剛變?yōu)榫托钁B(tài),并且只會(huì)通知一次
使用"事件"的就緒通知方式,通過epoll_ctl注冊(cè)fd,一旦該fd就緒,內(nèi)核就會(huì)采用類似callback的回調(diào)機(jī)制來(lái)激
活該fd,epoll_wait便可以收到通知
#優(yōu)點(diǎn):
1)沒有最大并發(fā)連接的限制:能打開的FD的上限遠(yuǎn)大于1024(1G的內(nèi)存能監(jiān)聽約10萬(wàn)個(gè)端口),具體查看
/proc/sys/fs/file-max,此值和系統(tǒng)內(nèi)存大小相關(guān)
2)效率提升:非輪詢的方式,不會(huì)隨著FD數(shù)目的增加而效率下降;只有活躍可用的FD才會(huì)調(diào)用callback函數(shù),
即epoll最大的優(yōu)點(diǎn)就在于它只管理"活躍"的連接,而跟連接總數(shù)無(wú)關(guān)
3)內(nèi)存拷貝,利用mmap(Memory Mapping)加速與內(nèi)核空間的消息傳遞;即epoll使用mmap減少?gòu)?fù)制開銷
邊緣觸發(fā):只通知一次
8.什么是零拷貝
傳統(tǒng)Linux中 I/O 的問題
傳統(tǒng)的Linux系統(tǒng)的標(biāo)準(zhǔn) I/O 接口(read、write)是基于數(shù)據(jù)拷貝的,也就是數(shù)據(jù)都是 copy_to_user 或者
copy_from_user,這樣做的好處是,通過中間緩存的機(jī)制,減少磁盤 I/O 的操作,
但是壞處也很明顯,大量數(shù)據(jù)的拷貝,用戶態(tài)和內(nèi)核態(tài)的頻繁切換,會(huì)消耗大量的 CPU 資源,嚴(yán)重影響數(shù)據(jù)傳輸?shù)?br/>性能
統(tǒng)計(jì)表明,在Linux協(xié)議棧中,數(shù)據(jù)包在內(nèi)核態(tài)和用戶態(tài)之間的拷貝所用的時(shí)間甚至占到了數(shù)據(jù)包整個(gè)處理流
程時(shí)間的57.1%
零拷貝就是上述問題的一個(gè)解決方案,盡量避免拷貝操作來(lái)緩解 CPU 的壓力。
零拷貝并沒有真正做到"0"拷貝,它更多是一種思想,很多的零拷貝技術(shù)都是基于這個(gè)思想去做的優(yōu)化
8.1.原始數(shù)據(jù)拷貝操作
8.2.MMAP:Memory Mapping
8.3.SENDFILE
8.4.DMA 輔助的 SENDFILE
9.Httpd MPM
HTTP中的三種請(qǐng)求處理模式(MPM)的區(qū)別
(1)prefork(預(yù)派生模式):多進(jìn)程I/o模型,每個(gè)進(jìn)程相應(yīng)一個(gè)請(qǐng)求 #-->>進(jìn)程模型,兩級(jí)結(jié)構(gòu)
主進(jìn)程--子進(jìn)程--線程--請(qǐng)求
|--子進(jìn)程--線程--請(qǐng)求
|--子進(jìn)程--線程--請(qǐng)求
優(yōu)點(diǎn):穩(wěn)定
缺點(diǎn):慢,占用資源(內(nèi)存),不適用于高并發(fā)場(chǎng)景
使用select 模型,最大并發(fā)1024
(2)worker:復(fù)用的多進(jìn)程I/O模型,多進(jìn)程多線程 #-->>線程模型,三級(jí)結(jié)構(gòu)
主進(jìn)程--子進(jìn)程--線程--請(qǐng)求
| --線程--請(qǐng)求
| --線程--請(qǐng)求
|--子進(jìn)程--線程--請(qǐng)求
| --線程--請(qǐng)求
| --線程--請(qǐng)求
優(yōu)點(diǎn):相比prefork,占用內(nèi)存較少,可以同時(shí)處理更多的請(qǐng)求
缺點(diǎn):使用keep-alive的長(zhǎng)連接方式,某個(gè)線程會(huì)一直被占據(jù),即使沒有傳輸數(shù)據(jù),也需要一直等待到超時(shí)才會(huì)被釋放。
(3)event 事件驅(qū)動(dòng)模型(epoll),增加了一個(gè)監(jiān)聽線程 #-->>線程模型,三級(jí)結(jié)構(gòu)
監(jiān)聽線程:用于向工作線程分配任務(wù)并和客戶端保持會(huì)話連接,超時(shí)之后監(jiān)聽線程會(huì)刪除socket,工作線程只處理用戶請(qǐng)求,處理完之后將會(huì)話保持交于監(jiān)聽線程,自己去處理新的請(qǐng)求,不再負(fù)責(zé)會(huì)話保持
主進(jìn)程--子進(jìn)程--監(jiān)聽線程--請(qǐng)求1 空請(qǐng)求不被分配 請(qǐng)求3
| |--工作線程--請(qǐng)求1
| |--工作線程--請(qǐng)求3
| |--工作線程 處理完成請(qǐng)求,將keep-alived交給監(jiān)聽線程,自己開始等待處理新請(qǐng)求
與worker進(jìn)程很像,最大的區(qū)別在于,它解決了keep-alive場(chǎng)景下,長(zhǎng)期被占用的線程的資源浪費(fèi)問題
優(yōu)點(diǎn):單線程響應(yīng)多請(qǐng)求,占據(jù)更少的內(nèi)存,高并發(fā)下表現(xiàn)更優(yōu)秀
缺點(diǎn):沒有線程安全控制
本文摘自 :https://blog.51cto.com/t