常正超
摘要:在大量并發(fā)訪問(wèn)時(shí),如何選擇網(wǎng)絡(luò)IO模型將是提高服務(wù)器性能的關(guān)鍵。該文通過(guò)對(duì)Linux環(huán)境下幾種網(wǎng)絡(luò)I/O模型的詳細(xì)分析和研究,提出了在不同場(chǎng)景下選擇不同網(wǎng)絡(luò)IO模型的方案。
關(guān)鍵詞:Linux系統(tǒng);網(wǎng)絡(luò)I/O;并發(fā)
中圖分類號(hào):TP393 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):1009-3044(2016)19-0028-02
1 概述
目前我們網(wǎng)絡(luò)所面臨的依然是高并發(fā)的問(wèn)題,就像某些電商網(wǎng)站雙11時(shí)的情況,瞬間的并發(fā)量是驚人的,當(dāng)然我們會(huì)有很多種方法去解決這個(gè)問(wèn)題,本文我們談?wù)摰氖菃闻_(tái)服務(wù)器,如何提高自己對(duì)并發(fā)請(qǐng)求的處理能力。要想解決這個(gè)問(wèn)題,我們需要先理清楚Unix和類Unix系統(tǒng)的I/O模型。
IO也就是輸入輸出即讀寫操作,在操作系統(tǒng)內(nèi)部邏輯上一般會(huì)分兩個(gè)空間(實(shí)際是內(nèi)存映射):用戶空間和內(nèi)核空間。為了保證數(shù)據(jù)的安全性,只有內(nèi)核才有權(quán)限從物理存儲(chǔ)(或網(wǎng)絡(luò)數(shù)據(jù))上直接進(jìn)行讀寫操作,而我們的服務(wù)進(jìn)程都是處于用戶空間的,所以說(shuō)一次IO操作就被分為了兩個(gè)階段:1)內(nèi)核從磁盤上讀取數(shù)據(jù)到內(nèi)核空間;2)復(fù)制內(nèi)核空間中的數(shù)據(jù)到用戶空間。了解這個(gè)之后我們就可以講解下5種不同的IO模型。
2 IO模型原理詳述
2.1阻塞I/O(Blocking I/O)
阻塞IO是最早期也是原理最簡(jiǎn)單的I/O模式,應(yīng)用進(jìn)程發(fā)出一個(gè)I/O請(qǐng)求,該I/O操作不能立刻完成,它需要等待內(nèi)核將數(shù)據(jù)讀取出來(lái),再等待從內(nèi)核空間中將數(shù)據(jù)拷貝出來(lái),這兩個(gè)環(huán)節(jié)進(jìn)行完之前,應(yīng)用進(jìn)程都處于阻塞狀態(tài)。
2.2非阻塞I/O(Non-blocking I/O)
應(yīng)用進(jìn)程向內(nèi)核發(fā)送一個(gè)I/O請(qǐng)求,如果沒有可用數(shù)據(jù),內(nèi)核會(huì)向用戶返回一個(gè)錯(cuò)誤值,用戶會(huì)在將來(lái)的一個(gè)合適的時(shí)候再次進(jìn)行請(qǐng)求操作,這樣就避免了進(jìn)程的阻塞,這種往返的操作也被稱為輪詢。這里有一點(diǎn)需要注意的是,在Copy data from kernel user的時(shí)候進(jìn)程依然是被阻塞的,之所以叫做非阻塞是因?yàn)橹挥性趦?nèi)核讀寫數(shù)據(jù)的階段(上文的第一階段)才叫做IO操作,所以在需要有大量進(jìn)程并發(fā)請(qǐng)求時(shí)非阻塞IO模式并不比阻塞IO模式效率高,甚至更加浪費(fèi)資源。
2.3多路復(fù)用I/O(I/O Multiplexing)
多路復(fù)用IO在Linux系統(tǒng)中一般只用于網(wǎng)絡(luò)IO,非阻塞IO中的用戶進(jìn)程輪訓(xùn)會(huì)消耗大量的系統(tǒng)資源,多路復(fù)用的提出就是將這種輪訓(xùn)方式通過(guò)select或poll系統(tǒng)調(diào)用來(lái)完成,使用select或poll 操作,進(jìn)程可在多個(gè)套接字上等待網(wǎng)絡(luò)事件,當(dāng)其中某個(gè)套接字發(fā)生某個(gè)網(wǎng)絡(luò)事件時(shí),用戶可通過(guò)查看網(wǎng)絡(luò)事件對(duì)該套接字進(jìn)行I/O 操作。但當(dāng)所有套接字都沒有網(wǎng)絡(luò)事件發(fā)生時(shí),進(jìn)程還會(huì)阻塞起來(lái)。所以,多路復(fù)用I/O 模型本質(zhì)上還是基于阻塞I/O 的。
2.4信號(hào)驅(qū)動(dòng)式I/O(Single-driven I/O)
信號(hào)驅(qū)動(dòng)式IO也有叫事件驅(qū)動(dòng)式IO,這種IO具有以上三種IO都不具有的優(yōu)勢(shì),即當(dāng)用戶進(jìn)程發(fā)起IO請(qǐng)求后即可離開,當(dāng)內(nèi)核將數(shù)據(jù)準(zhǔn)備好后,將想用戶進(jìn)程發(fā)送一個(gè)信號(hào)以通知其來(lái)拷貝數(shù)據(jù),第一階段用戶進(jìn)程時(shí)完全的非阻塞的也不需要進(jìn)行輪訓(xùn),只有在第二階段用戶進(jìn)程才是阻塞狀態(tài)。但在多進(jìn)程或多線程服務(wù)器中,信號(hào)驅(qū)動(dòng)I/O 存在一定問(wèn)題,即信號(hào)在產(chǎn)生和傳遞到目的進(jìn)程之間,狀態(tài)標(biāo)志可能會(huì)發(fā)生變化。
2.5異步I/O(Asynchronous I/O)
用戶進(jìn)程提出IO請(qǐng)求后即可做其他的操作,當(dāng)數(shù)據(jù)準(zhǔn)備好之后,內(nèi)核會(huì)直接通知用戶進(jìn)程I/O操作的結(jié)果。這種機(jī)制容易和信號(hào)驅(qū)動(dòng)I/O混淆,兩者的不同是:異步I/O 返回時(shí),I/O 操作已經(jīng)完成,返回的是I/O 操作的結(jié)果;信號(hào)驅(qū)動(dòng)I/O 只是通知進(jìn)程可以開始進(jìn)行I/O 操作,進(jìn)程得到這個(gè)信號(hào)后才開始I/O 操作。
3 五種不同I/O模型的比較和適用場(chǎng)景
3.1 阻塞模型
客戶端連接至服務(wù)器端的每一個(gè)專用進(jìn)程,服務(wù)器端的進(jìn)程處理這些連接數(shù)據(jù)。如果有大量用戶請(qǐng)求連接,服務(wù)器端就建立大量線程或者進(jìn)程,線程或進(jìn)程間的切換會(huì)使系統(tǒng)性能大大下降。這種模型適合服務(wù)器負(fù)載不大或并發(fā)數(shù)不多的情況。
3.2 非阻塞I/O
此種模式主要應(yīng)用在單進(jìn)程服務(wù)器中。當(dāng)服務(wù)器進(jìn)程接受到請(qǐng)求后,該進(jìn)程會(huì)收到內(nèi)核返回的一個(gè)錯(cuò)誤值,此進(jìn)程可以去做其他操作,單該進(jìn)程并不知道何時(shí)再次進(jìn)行I/O 請(qǐng)求操作,所以只能通過(guò)輪詢的方法查詢連接狀態(tài),但是輪詢操作會(huì)造成CPU 的浪費(fèi)。
3.3 多路復(fù)用I/O
此模型是對(duì)上訴兩種模型的改進(jìn),進(jìn)程可在多個(gè)描述符上等待網(wǎng)絡(luò)I/O 事件,擔(dān)當(dāng)沒有任何網(wǎng)絡(luò)事件發(fā)生時(shí),進(jìn)程會(huì)進(jìn)入阻塞狀態(tài)。這種模型一般使用select/poll進(jìn)行操作,進(jìn)程在多個(gè)socket上進(jìn)行監(jiān)聽,如果有IO事件發(fā)生,CPU需要掃描所有活動(dòng)鏈接,已處理有數(shù)據(jù)傳輸?shù)逆溄?,并且單個(gè)進(jìn)程可監(jiān)視的fd數(shù)量被限制,即能監(jiān)聽端口的大小有限。因此,該模型適合多并發(fā)連接的情況,且這些并發(fā)連接大多要有IO事件發(fā)生。
3.4 信號(hào)驅(qū)動(dòng)I/O
信號(hào)驅(qū)動(dòng)I/O模型用得比較少,主要使用在UDP套接字上,TCP套接字幾乎不使用。這是因?yàn)?,在UDP編程中使用信號(hào)驅(qū)動(dòng)I/O,此時(shí)SIGIO信號(hào)產(chǎn)生于下面兩種情況:1.套接字收到一個(gè)數(shù)據(jù)報(bào)。2.套接字上發(fā)生了異步錯(cuò)誤。因此,當(dāng)應(yīng)用因?yàn)槭盏揭粋€(gè)UDP數(shù)據(jù)報(bào)而產(chǎn)生的SIGIO時(shí),要么可以調(diào)用recvfrom讀取該數(shù)據(jù)報(bào),要么得到一個(gè)異步錯(cuò)誤。而對(duì)于TCP編程,信號(hào)驅(qū)動(dòng)I/O就沒有太大意義了,因?yàn)閷?duì)于流式套接字而言,有很多情況都可以導(dǎo)致SIGIO產(chǎn)生,而應(yīng)用又無(wú)法區(qū)分是什么具體情況導(dǎo)致該信號(hào)產(chǎn)生的。例如: 1)監(jiān)聽套接字完成了一個(gè)連接請(qǐng)求。 2)收到了一個(gè)斷連請(qǐng)求。3)斷連操作完成。4)套接字收到數(shù)據(jù)。5)有數(shù)據(jù)從套接字發(fā)出。對(duì)于TCP下應(yīng)用信號(hào)驅(qū)動(dòng)I/O模型,我們應(yīng)該考慮只對(duì)“監(jiān)聽TCPsocket”使用SIGIO,因?yàn)閷?duì)于“監(jiān)聽TCPsocket”產(chǎn)生SIGIO的唯一條件是新連接完成。也就是說(shuō),只有TCP下只有用作listen的端口,才考慮使用信號(hào)驅(qū)動(dòng)I/O模型。
3.5 異步I/O
Linux下的異步I/O模型使用的非常少,通常認(rèn)為L(zhǎng)inux下沒有比較完美的異步文件I/O方案。目前比較出名的有Glibc的AIO與Kernel Native AIO,當(dāng)有大量異步IO請(qǐng)求時(shí),將會(huì)產(chǎn)生大量用戶級(jí)線程,這些線程的切換將會(huì)極大的影響系統(tǒng)性能,所以AIO適用于并發(fā)訪問(wèn)量并不大的應(yīng)用。
3.6 epoll 模型
是針對(duì)poll模型的改進(jìn)。它沒有最大并發(fā)連接的限制,能打開的FD的上限遠(yuǎn)大于1024(1G的內(nèi)存上能監(jiān)聽約10萬(wàn)個(gè)端口);不采用輪詢的方式,不會(huì)隨著FD數(shù)目的增加效率下降。只有活躍可用的FD才會(huì)調(diào)用callback函數(shù);即Epoll最大的優(yōu)點(diǎn)就在于它只管你“活躍”的連接,而跟連接總數(shù)無(wú)關(guān),因此在實(shí)際的網(wǎng)絡(luò)環(huán)境中,Epoll的效率就會(huì)遠(yuǎn)遠(yuǎn)高于select和poll。并且epoll利用mmap()文件映射內(nèi)存加速與內(nèi)核空間的消息傳遞;即epoll使用mmap減少?gòu)?fù)制開銷。
4 結(jié)束語(yǔ)
從上述的IO模型中我們發(fā)現(xiàn)在現(xiàn)如今常見的大量活躍并發(fā)的場(chǎng)景中,只有epoll模型的效率是最高的,只有在一種極其極端的條件下,即所有活動(dòng)鏈接都有數(shù)據(jù)傳輸時(shí)select/poll模型比epoll模型效率稍高。epoll 模型通過(guò)callback 方法實(shí)現(xiàn)系統(tǒng)異步通知,只返回活躍的進(jìn)程,減少了輪訓(xùn)的時(shí)間,并通過(guò)mmap節(jié)省了復(fù)制的開銷。
參考文獻(xiàn):
[1] Gary.wrigh W.Richard Stevens.TCP/IP詳解(卷1):協(xié)議[M].北京:機(jī)械工業(yè)出版社,2007.
[2] 滕昱.2.6 內(nèi)核中提高I/O 性能的新方法——epo1l[z].
(2005—03~30).http://mechgouki.spaces.1ive,com/blog/PersonalSpace.aspx.
[3] Gary.wrigh W.Richard Stevens.TCP/IP詳解(卷2):協(xié)議[M].北京:機(jī)械工業(yè)出版社,2010.