欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

TCP/IP的底層隊(duì)列是如何實(shí)現(xiàn)的?-創(chuàng)新互聯(lián)

自從上次學(xué)習(xí)了TCP/IP的擁塞控制算法后,我越發(fā)想要更加深入的了解TCP/IP的一些底層原理,搜索了很多網(wǎng)絡(luò)上的資料,收益頗多。今天就總結(jié)一下。

我們提供的服務(wù)有:成都網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)、微信公眾號(hào)開(kāi)發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、瀘溪ssl等。為1000+企事業(yè)單位解決了網(wǎng)站和推廣的問(wèn)題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的瀘溪網(wǎng)站制作公司

我自己比較了解Java語(yǔ)言,對(duì)Java網(wǎng)絡(luò)編程的理解就止于Netty框架的使用。Netty的源碼貢獻(xiàn)者Norman Maurer對(duì)于Netty網(wǎng)絡(luò)開(kāi)發(fā)有過(guò)一句建議,"Never block the event loop, reduce context-swtiching"。也就是盡量不要阻塞IO線程,也盡量減少線程切換。我們今天只關(guān)注前半句。

為什么不能阻塞讀取網(wǎng)絡(luò)信息的IO線程呢?這里就要從經(jīng)典的網(wǎng)絡(luò)C10K開(kāi)始理解,服務(wù)器如何支持并發(fā)1萬(wàn)請(qǐng)求。C10K的根源在于網(wǎng)絡(luò)的IO模型。Linux 中網(wǎng)絡(luò)處理都用同步阻塞的方式,也就是每個(gè)請(qǐng)求都分配一個(gè)進(jìn)程或者線程,那么要支持1萬(wàn)并發(fā),難道就要使用1萬(wàn)個(gè)線程處理請(qǐng)求嘛?這1萬(wàn)個(gè)線程的調(diào)度、上下文切換乃至它們占用的內(nèi)存,都會(huì)成為瓶頸。解決C10K的通用辦法就是使用I/O 多路復(fù)用,Netty就是這樣。

TCP/IP的底層隊(duì)列是如何實(shí)現(xiàn)的?

Netty有負(fù)責(zé)服務(wù)端監(jiān)聽(tīng)建立連接的線程組(mainReactor)和負(fù)責(zé)連接讀寫(xiě)操作的IO線程組(subReactor),還可以有專門(mén)處理業(yè)務(wù)邏輯的Worker線程組(ThreadPool)。

三者相互獨(dú)立,這樣有很多好處。一是有專門(mén)的線程組負(fù)責(zé)監(jiān)聽(tīng)和處理網(wǎng)絡(luò)連接的建立,可以防止TCP/IP的半連接隊(duì)列(sync)和全連接隊(duì)列(acceptable)被占滿。二是IO線程組和Worker線程分開(kāi),雙方并行處理網(wǎng)絡(luò)I/O和業(yè)務(wù)邏輯,可以避免IO線程被阻塞,防止TCP/IP的接收?qǐng)?bào)文的隊(duì)列被占滿。當(dāng)然,如果業(yè)務(wù)邏輯較少,也就是IO 密集型的輕計(jì)算業(yè)務(wù),可以將業(yè)務(wù)邏輯放在IO線程中處理,避免線程切換,這也就是Norman Maurer話的后半部分。

TCP/IP怎么就這么多隊(duì)列啊?今天我們就來(lái)細(xì)看一下TCP/IP的幾個(gè)隊(duì)列,包括建立連接時(shí)的半連接隊(duì)列(sync),全連接隊(duì)列(accept)和接收?qǐng)?bào)文時(shí)的receive、outoforder、prequeue以及backlog隊(duì)列。

建立連接時(shí)的隊(duì)列

TCP/IP的底層隊(duì)列是如何實(shí)現(xiàn)的?

如上圖所示,這里有兩個(gè)隊(duì)列:syns queue(半連接隊(duì)列)和accept queue(全連接隊(duì)列)。三次握手中,服務(wù)端接收到客戶端的SYN報(bào)文后,把相關(guān)信息放到半連接隊(duì)列中,同時(shí)回復(fù)SYN+ACK給客戶端。?第三步的時(shí)候服務(wù)端收到客戶端的ACK,如果這時(shí)全連接隊(duì)列沒(méi)滿,那么從半連接隊(duì)列拿出相關(guān)信息放入到全連接隊(duì)列中,否則按tcp_abort_on_overflow的值來(lái)執(zhí)行相關(guān)操作,直接拋棄或者過(guò)一段時(shí)間在重試。

接收?qǐng)?bào)文時(shí)的隊(duì)列

相比于建立連接,TCP在接收?qǐng)?bào)文時(shí)的處理邏輯更為復(fù)雜,相關(guān)的隊(duì)列和涉及的配置參數(shù)更多。

應(yīng)用程序接收TCP報(bào)文和程序所在服務(wù)器系統(tǒng)接收網(wǎng)絡(luò)里發(fā)來(lái)的TCP報(bào)文是兩個(gè)獨(dú)立流程。二者都會(huì)操控socket實(shí)例,但是會(huì)通過(guò)鎖競(jìng)爭(zhēng)來(lái)決定某一時(shí)刻由誰(shuí)來(lái)操控,由此產(chǎn)生很多不同的場(chǎng)景。例如,應(yīng)用程序正在接收?qǐng)?bào)文時(shí),操作系統(tǒng)通過(guò)網(wǎng)卡又接收到報(bào)文,這時(shí)該如何處理?若應(yīng)用程序沒(méi)有調(diào)用read或者recv讀取報(bào)文時(shí),操作系統(tǒng)收到報(bào)文又會(huì)如何處理?

我們接下來(lái)就以三張圖為主,介紹TCP接收?qǐng)?bào)文時(shí)的三種場(chǎng)景,并在其中介紹四個(gè)接收相關(guān)的隊(duì)列。

接收?qǐng)?bào)文場(chǎng)景一

TCP/IP的底層隊(duì)列是如何實(shí)現(xiàn)的?

上圖是TCP接收?qǐng)?bào)文場(chǎng)景一的示意圖。操作系統(tǒng)首先接收?qǐng)?bào)文,存儲(chǔ)到socket的receive隊(duì)列,然后用戶進(jìn)程再調(diào)用recv進(jìn)行讀取。

1) 當(dāng)網(wǎng)卡接收?qǐng)?bào)文并且判斷為T(mén)CP協(xié)議時(shí),經(jīng)過(guò)層層調(diào)用,最終會(huì)調(diào)用到內(nèi)核的tcp_v4_rcv方法。由于當(dāng)前TCP要接收的下一個(gè)報(bào)文正是S1,所以tcp_v4_rcv函數(shù)將其直接加入到receive隊(duì)列中。receive隊(duì)列是將已經(jīng)接收到的TCP報(bào)文,去除了TCP頭部、排好序放入的、用戶進(jìn)程可以直接按序讀取的隊(duì)列。由于socket不在用戶進(jìn)程上下文中(也就是沒(méi)有用戶進(jìn)程在讀socket),并且我們需要S1序號(hào)的報(bào)文,而恰好收到了S1報(bào)文,因此,它進(jìn)入了receive隊(duì)列。

2) 接收到S3報(bào)文,由于TCP要接收的下一個(gè)報(bào)文序號(hào)是S2,所以加入到out_of_order隊(duì)列,所有亂序的報(bào)文會(huì)放在這里。

3) 接著,收到了TCP期望的S2報(bào)文,直接進(jìn)入recevie隊(duì)列。由于此時(shí)out_of_order隊(duì)列不為空,需要檢查一下。

4) 每次向receive隊(duì)列插入報(bào)文時(shí)都會(huì)檢查out_of_order隊(duì)列,由于接收到S2報(bào)文后,期望的的序號(hào)為S3,所以out_of_order隊(duì)列中的S3報(bào)文會(huì)被移到receive隊(duì)列。

5) 用戶進(jìn)程開(kāi)始讀取socket,先在進(jìn)程中分配一塊內(nèi)存,然后調(diào)用read或者recv方法。socket有一系列的具有默認(rèn)值的配置屬性,比如socket默認(rèn)是阻塞式的,它的SO_RCVLOWAT屬性值默認(rèn)為1。當(dāng)然,recv這樣的方法還會(huì)接收一個(gè)flag參數(shù),它可以設(shè)置為MSG_WAITALL、MSG_PEEK、MSG_TRUNK等等,這里我們假定為最常用的0。進(jìn)程調(diào)用了recv方法。

6) 調(diào)用tcp_recvmsg方法

7)tcp_recvmsg方法會(huì)首先鎖住socket。socket是可以被多線程使用的,而且操作系統(tǒng)也會(huì)使用,所以必須處理并發(fā)問(wèn)題。要操控socket,就先獲取鎖。

8) 此時(shí),receive隊(duì)列已經(jīng)有3個(gè)報(bào)文了,將第一個(gè)報(bào)文拷貝到用戶態(tài)內(nèi)存中,由于第五步中socket的參數(shù)并沒(méi)有帶MSG_PEEK,所以將第一個(gè)報(bào)文從隊(duì)列中移除,從內(nèi)核態(tài)釋放掉。反之,MSG_PEEK標(biāo)志位會(huì)導(dǎo)致receive隊(duì)列不會(huì)刪除報(bào)文。所以,MSG_PEEK主要用于多進(jìn)程讀取同一套接字的情形。

9) 拷貝第二個(gè)報(bào)文,當(dāng)然,執(zhí)行拷貝前都會(huì)檢查用戶態(tài)內(nèi)存的剩余空間是否足以放下當(dāng)前這個(gè)報(bào)文,不夠時(shí)會(huì)直接返回已經(jīng)拷貝的字節(jié)數(shù)。

10) 拷貝第三個(gè)報(bào)文。

11)receive隊(duì)列已經(jīng)為空,此時(shí)會(huì)檢查SO_RCVLOWAT這個(gè)最小閾值。如果已經(jīng)拷貝字節(jié)數(shù)小于它,進(jìn)程會(huì)休眠,等待更多報(bào)文。默認(rèn)的SO_RCVLOWAT值為1,也就是讀取到報(bào)文就可以返回。

12) 檢查backlog隊(duì)列,backlog隊(duì)列是用戶進(jìn)程正在拷貝數(shù)據(jù)時(shí),網(wǎng)卡收到的報(bào)文會(huì)進(jìn)這個(gè)隊(duì)列。如果此時(shí)backlog隊(duì)列有數(shù)據(jù),就順帶處理下。backlog隊(duì)列是沒(méi)有數(shù)據(jù)的,因此釋放鎖,準(zhǔn)備返回用戶態(tài)。

13) 用戶進(jìn)程代碼開(kāi)始執(zhí)行,此時(shí)recv等方法返回的就是從內(nèi)核拷貝的字節(jié)數(shù)。

接收?qǐng)?bào)文場(chǎng)景二

第二張圖給出了第二個(gè)場(chǎng)景,這里涉及了prequeue隊(duì)列。用戶進(jìn)程調(diào)用recv方法時(shí),socket隊(duì)列中沒(méi)有任何報(bào)文,而socket是阻塞的,所以進(jìn)程睡眠了。然后操作系統(tǒng)收到了報(bào)文,此時(shí)prequeue隊(duì)列開(kāi)始產(chǎn)生作用。該場(chǎng)景中,tcp_low_latency為默認(rèn)的0,套接字socket的SO_RCVLOWAT是默認(rèn)的1,仍然是阻塞socket,如下圖。

TCP/IP的底層隊(duì)列是如何實(shí)現(xiàn)的?

其中1,2,3步驟的處理和之前一樣。我們直接從第四步開(kāi)始。

4) 由于此時(shí)receive,prequeuebacklog隊(duì)列都為空,所以沒(méi)有拷貝一個(gè)字節(jié)到用戶內(nèi)存中。而socket的配置要求至少拷貝SO_RCVLOWAT也就是1字節(jié)的報(bào)文,因此進(jìn)入阻塞式套接字的等待流程。最長(zhǎng)等待時(shí)間為SO_RCVTIMEO指定的時(shí)間。socket在進(jìn)入等待前會(huì)釋放socket鎖,會(huì)使第五步中,新來(lái)的報(bào)文不再只能進(jìn)入backlog隊(duì)列。

5) 接到S1報(bào)文,將其加入prequeue隊(duì)列中。

6) 插入到prequeue隊(duì)列后,會(huì)喚醒在socket上休眠的進(jìn)程。

7) 用戶進(jìn)程被喚醒后,重新獲取socket鎖,此后再接收到的報(bào)文只能進(jìn)入backlog隊(duì)列。

8) 進(jìn)程先檢查receive隊(duì)列,當(dāng)然仍然是空的;再去檢查prequeue隊(duì)列,發(fā)現(xiàn)有報(bào)文S1,正好是正在等待序號(hào)的報(bào)文,于是直接從prequeue隊(duì)列中拷貝到用戶內(nèi)存,再釋放內(nèi)核中的這個(gè)報(bào)文。

9) 目前已經(jīng)拷貝了一個(gè)字節(jié)的報(bào)文到用戶內(nèi)存,檢查這個(gè)長(zhǎng)度是否超過(guò)了最低閾值,也就是len和SO_RCVLOWAT的最小值。

10) 由于SO_RCVLOWAT使用了默認(rèn)值1,拷貝字節(jié)數(shù)大于最低閾值,準(zhǔn)備返回用戶態(tài),順便會(huì)查看一下backlog隊(duì)列中是否有數(shù)據(jù),此時(shí)沒(méi)有,所以準(zhǔn)備放回,釋放socket鎖。

11) 返回用戶已經(jīng)拷貝的字節(jié)數(shù)。

接收?qǐng)?bào)文場(chǎng)景三

在第三個(gè)場(chǎng)景中,系統(tǒng)參數(shù)tcp_low_latency為1,socket上設(shè)置了SO_RCVLOWAT屬性值。服務(wù)器先收到報(bào)文S1,但是其長(zhǎng)度小于SO_RCVLOWAT。用戶進(jìn)程調(diào)用recv方法讀取,雖然讀取到了一部分,但是沒(méi)有到達(dá)最小閾值,所以進(jìn)程睡眠了。與此同時(shí),在睡眠前接收的亂序的報(bào)文S3直接進(jìn)入backlog隊(duì)列。然后,報(bào)文S2到達(dá),由于沒(méi)有使用prequeue隊(duì)列(因?yàn)樵O(shè)置了tcplowlatency),而它起始序號(hào)正是下一個(gè)待拷貝的值,所以直接拷貝到用戶內(nèi)存中,總共拷貝字節(jié)數(shù)已滿足SO_RCVLOWAT的要求!最后在返回用戶前把backlog隊(duì)列中S3報(bào)文也拷貝給用戶。

TCP/IP的底層隊(duì)列是如何實(shí)現(xiàn)的?

1) 接收到報(bào)文S1,正是準(zhǔn)備接收的報(bào)文序號(hào),因此,將它直接加入到有序的receive隊(duì)列中。

2) 將系統(tǒng)屬性tcp_low_latency設(shè)置為1,表明服務(wù)器希望程序能夠及時(shí)的接收到TCP報(bào)文。用戶調(diào)用的recv接收阻塞socket上的報(bào)文,該socket的SO_RCVLOWAT值大于第一個(gè)報(bào)文的大小,并且用戶分配了足夠大的長(zhǎng)度為len的內(nèi)存。

3) 調(diào)用tcp_recvmsg方法來(lái)完成接收工作,先鎖住socket。

4) 準(zhǔn)備處理內(nèi)核各個(gè)接收隊(duì)列中的報(bào)文。

5)receive隊(duì)列中有報(bào)文可以直接拷貝,其大小小于len,直接拷貝到用戶內(nèi)存。

6) 在進(jìn)行第五步的同時(shí),內(nèi)核又接收到S3報(bào)文,此時(shí)socket被鎖,報(bào)文直接進(jìn)入backlog隊(duì)列。這個(gè)報(bào)文并不是有序的。

7) 在第五步時(shí),拷貝報(bào)文S1到用戶內(nèi)存,它的大小小于SO_RCVLOWAT的值。由于socket是阻塞型,所以用戶進(jìn)程進(jìn)入睡眠狀態(tài)。進(jìn)入睡眠前,會(huì)先處理backlog隊(duì)列的報(bào)文。因?yàn)镾3報(bào)文是失序的,所以進(jìn)入out_of_order隊(duì)列。用戶進(jìn)程進(jìn)入休眠狀態(tài)前都會(huì)先處理一下backlog隊(duì)列。

8) 進(jìn)程休眠,直到超時(shí)或者receive隊(duì)列不為空。

9) 內(nèi)核接收到報(bào)文S2。注意,此時(shí)由于打開(kāi)了tcp_low_latency標(biāo)志位,所以報(bào)文是不會(huì)進(jìn)入prequeue隊(duì)列等待進(jìn)程處理。

10) 由于報(bào)文S2正是要接收的報(bào)文,同時(shí),一個(gè)用戶進(jìn)程在休眠等待該報(bào)文,所以直接將報(bào)文S2拷貝到用戶內(nèi)存。

11) 每處理完一個(gè)有序報(bào)文后,無(wú)論是拷貝到receive隊(duì)列還是直接復(fù)制到用戶內(nèi)存,都會(huì)檢查out_of_order隊(duì)列,看看是否有報(bào)文可以處理。報(bào)文S3拷貝到用戶內(nèi)存,然后喚醒用戶進(jìn)程。

12) 喚醒用戶進(jìn)程。

13) 此時(shí)會(huì)檢查已拷貝的字節(jié)數(shù)是否大于SO_RCVLOWAT,以及backlog隊(duì)列是否為空。兩者皆滿足,準(zhǔn)備返回。

總結(jié)一下四個(gè)隊(duì)列的作用。

  • receive隊(duì)列是真正的接收隊(duì)列,操作系統(tǒng)收到的TCP數(shù)據(jù)包經(jīng)過(guò)檢查和處理后,就會(huì)保存到這個(gè)隊(duì)列中。

  • backlog是“備用隊(duì)列”。當(dāng)socket處于用戶進(jìn)程的上下文時(shí)(即用戶正在對(duì)socket進(jìn)行系統(tǒng)調(diào)用,如recv),操作系統(tǒng)收到數(shù)據(jù)包時(shí)會(huì)將數(shù)據(jù)包保存到?backlog隊(duì)列中,然后直接返回。

  • prequeue是“預(yù)存隊(duì)列”。當(dāng)socket沒(méi)有正在被用戶進(jìn)程使用時(shí),也就是用戶進(jìn)程調(diào)用了read或者recv系統(tǒng)調(diào)用,但是進(jìn)入了睡眠狀態(tài)時(shí),操作系統(tǒng)直接將收到的報(bào)文保存在?prequeue中,然后返回。

  • out_of_order是“亂序隊(duì)列”。隊(duì)列存儲(chǔ)的是亂序的報(bào)文,操作系統(tǒng)收到的報(bào)文并不是TCP準(zhǔn)備接收的下一個(gè)序號(hào)的報(bào)文,則放入?out_of_order隊(duì)列,等待后續(xù)處理。

創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國(guó)云服務(wù)器,動(dòng)態(tài)BGP最優(yōu)骨干路由自動(dòng)選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動(dòng)現(xiàn)已開(kāi)啟,新人活動(dòng)云服務(wù)器買(mǎi)多久送多久。

網(wǎng)站題目:TCP/IP的底層隊(duì)列是如何實(shí)現(xiàn)的?-創(chuàng)新互聯(lián)
轉(zhuǎn)載來(lái)源:http://chinadenli.net/article48/diccep.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)公司、網(wǎng)站內(nèi)鏈、用戶體驗(yàn)、微信小程序、定制開(kāi)發(fā)移動(dòng)網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)公司