linux網(wǎng)絡(luò)之?dāng)?shù)據(jù)包的接受過(guò)程

網(wǎng)卡收包從整體上是網(wǎng)線中的高低電平轉(zhuǎn)換到網(wǎng)卡FIFO存儲(chǔ)再拷貝到系統(tǒng)主內(nèi)存(DDR3)的過(guò)程,其中涉及到網(wǎng)卡控制器,CPU,DMA,驅(qū)動(dòng)程序,在OSI模型中屬于物理層和鏈路層,如下圖所示。

image

網(wǎng)卡工作在物理層和數(shù)據(jù)鏈路層,主要由PHY/MAC芯片、Tx/Rx FIFO、DMA等組成,其中網(wǎng)線通過(guò)變壓器接PHY芯片、PHY芯片通過(guò)MII接MAC芯片、MAC芯片接PCI總線

PHY芯片主要負(fù)責(zé):CSMA/CD、模數(shù)轉(zhuǎn)換、編解碼、串并轉(zhuǎn)換

MAC芯片主要負(fù)責(zé):

比特流和幀的轉(zhuǎn)換:7字節(jié)的前導(dǎo)碼Preamble和1字節(jié)的幀首定界符SFD
CRC校驗(yàn)
Packet Filtering:L2 Filtering、VLAN Filtering、Manageability / Host Filtering
Intel的千兆網(wǎng)卡以82575/82576為代表、萬(wàn)兆網(wǎng)卡以82598/82599為代表

image

接收數(shù)據(jù)包是一個(gè)復(fù)雜的過(guò)程,涉及很多底層的技術(shù)細(xì)節(jié),但大致需要以下幾個(gè)步驟:

  1. 網(wǎng)卡收到數(shù)據(jù)包。
  2. 將數(shù)據(jù)包從網(wǎng)卡硬件緩存轉(zhuǎn)移到服務(wù)器內(nèi)存中。
  3. 通知內(nèi)核處理。
  4. 經(jīng)過(guò)TCP/IP協(xié)議逐層處理。
  5. 應(yīng)用程序通過(guò)read()從socket buffer讀取數(shù)據(jù)。
image

物理網(wǎng)卡收到數(shù)據(jù)包的處理流程如上圖所示,詳細(xì)步驟如下:

  1. 網(wǎng)卡收到數(shù)據(jù)包,先將高低電平轉(zhuǎn)換到網(wǎng)卡fifo存儲(chǔ),網(wǎng)卡申請(qǐng)ring buffer的描述,根據(jù)描述找到具體的物理地址,從fifo隊(duì)列物理網(wǎng)卡會(huì)使用DMA將數(shù)據(jù)包寫(xiě)到了該物理地址,,其實(shí)就是skb_buffer中.
  2. 這個(gè)時(shí)候數(shù)據(jù)包已經(jīng)被轉(zhuǎn)移到skb_buffer中,因?yàn)槭荄MA寫(xiě)入,內(nèi)核并沒(méi)有監(jiān)控?cái)?shù)據(jù)包寫(xiě)入情況,這時(shí)候NIC觸發(fā)一個(gè)硬中斷,每一個(gè)硬件中斷會(huì)對(duì)應(yīng)一個(gè)中斷號(hào),且指定一個(gè)vCPU來(lái)處理,如上圖vcpu2收到了該硬件中斷.
  3. 硬件中斷的中斷處理程序,調(diào)用驅(qū)動(dòng)程序完成,a.啟動(dòng)軟中斷
  4. 硬中斷觸發(fā)的驅(qū)動(dòng)程序會(huì)禁用網(wǎng)卡硬中斷,其實(shí)這時(shí)候意思是告訴NIC,再來(lái)數(shù)據(jù)不用觸發(fā)硬中斷了,把數(shù)據(jù)DMA拷入系統(tǒng)內(nèi)存即可
  5. 硬中斷觸發(fā)的驅(qū)動(dòng)程序會(huì)啟動(dòng)軟中斷,啟用軟中斷目的是將數(shù)據(jù)包后續(xù)處理流程交給軟中斷慢慢處理,這個(gè)時(shí)候退出硬件中斷了,但是注意和網(wǎng)絡(luò)有關(guān)的硬中斷,要等到后續(xù)開(kāi)啟硬中斷后,才有機(jī)會(huì)再次被觸發(fā)
  6. NAPI觸發(fā)軟中斷,觸發(fā)napi系統(tǒng)
  7. 消耗ringbuffer指向的skb_buffer
  8. NAPI循環(huán)處理ringbuffer數(shù)據(jù),處理完成
  9. 啟動(dòng)網(wǎng)絡(luò)硬件中斷,有數(shù)據(jù)來(lái)時(shí)候就可以繼續(xù)觸發(fā)硬件中斷,繼續(xù)通知CPU來(lái)消耗數(shù)據(jù)包.

其實(shí)上述過(guò)程過(guò)程簡(jiǎn)單描述為:網(wǎng)卡收到數(shù)據(jù)包,DMA到內(nèi)核內(nèi)存,中斷通知內(nèi)核數(shù)據(jù)有了,內(nèi)核按輪次處理消耗數(shù)據(jù)包,一輪處理完成后,開(kāi)啟硬中斷。其核心就是網(wǎng)卡和內(nèi)核其實(shí)是生產(chǎn)和消費(fèi)模型,網(wǎng)卡生產(chǎn),內(nèi)核負(fù)責(zé)消費(fèi),生產(chǎn)者需要通知消費(fèi)者消費(fèi);如果生產(chǎn)過(guò)快會(huì)產(chǎn)生丟包,如果消費(fèi)過(guò)慢也會(huì)產(chǎn)生問(wèn)題。也就說(shuō)在高流量壓力情況下,只有生產(chǎn)消費(fèi)優(yōu)化后,消費(fèi)能力夠快,此生產(chǎn)消費(fèi)關(guān)系才可以正常維持,所以如果物理接口有丟包計(jì)數(shù)時(shí)候,未必是網(wǎng)卡存在問(wèn)題,也可能是內(nèi)核消費(fèi)的太慢。

如何將網(wǎng)卡收到的數(shù)據(jù)寫(xiě)入到內(nèi)核內(nèi)存?

NIC在接收到數(shù)據(jù)包之后,首先需要將數(shù)據(jù)同步到內(nèi)核中,這中間的橋梁是rx ring buffer。它是由NIC和驅(qū)動(dòng)程序共享的一片區(qū)域,事實(shí)上,rx ring buffer存儲(chǔ)的并不是實(shí)際的packet數(shù)據(jù),而是一個(gè)描述符,這個(gè)描述符指向了它真正的存儲(chǔ)地址,具體流程如下:

  1. 驅(qū)動(dòng)在內(nèi)存中分配一片緩沖區(qū)用來(lái)接收數(shù)據(jù)包,叫做sk_buffer;
  2. 將上述緩沖區(qū)的地址和大?。唇邮彰枋龇?,加入到rx ring buffer。描述符中的緩沖區(qū)地址是DMA使用的物理地址;
  3. 驅(qū)動(dòng)通知網(wǎng)卡有一個(gè)新的描述符;
  4. 網(wǎng)卡從rx ring buffer中取出描述符,從而獲知緩沖區(qū)的地址和大小;
  5. 網(wǎng)卡收到新的數(shù)據(jù)包;
  6. 網(wǎng)卡將新數(shù)據(jù)包通過(guò)DMA直接寫(xiě)到sk_buffer中。
image

當(dāng)驅(qū)動(dòng)處理速度跟不上網(wǎng)卡收包速度時(shí),驅(qū)動(dòng)來(lái)不及分配緩沖區(qū),NIC接收到的數(shù)據(jù)包無(wú)法及時(shí)寫(xiě)到sk_buffer,就會(huì)產(chǎn)生堆積,當(dāng)NIC內(nèi)部緩沖區(qū)寫(xiě)滿后,就會(huì)丟棄部分?jǐn)?shù)據(jù),引起丟包。這部分丟包為rx_fifo_errors,在 /proc/net/dev中體現(xiàn)為fifo字段增長(zhǎng),在ifconfig中體現(xiàn)為overruns指標(biāo)增長(zhǎng)。

通知系統(tǒng)內(nèi)核處理(驅(qū)動(dòng)與Linux內(nèi)核交互)

這個(gè)時(shí)候,數(shù)據(jù)包已經(jīng)被轉(zhuǎn)移到了sk_buffer中。前文提到,這是驅(qū)動(dòng)程序在內(nèi)存中分配的一片緩沖區(qū),并且是通過(guò)DMA寫(xiě)入的,這種方式不依賴(lài)CPU直接將數(shù)據(jù)寫(xiě)到了內(nèi)存中,意味著對(duì)內(nèi)核來(lái)說(shuō),其實(shí)并不知道已經(jīng)有新數(shù)據(jù)到了內(nèi)存中。那么如何讓內(nèi)核知道有新數(shù)據(jù)進(jìn)來(lái)了呢?答案就是中斷,通過(guò)中斷告訴內(nèi)核有新數(shù)據(jù)進(jìn)來(lái)了,并需要進(jìn)行后續(xù)處理。

提到中斷,就涉及到硬中斷和軟中斷,首先需要簡(jiǎn)單了解一下它們的區(qū)別:

硬中斷: 由硬件自己生成,具有隨機(jī)性,硬中斷被CPU接收后,觸發(fā)執(zhí)行中斷處理程序。中斷處理程序只會(huì)處理關(guān)鍵性的、短時(shí)間內(nèi)可以處理完的工作,剩余耗時(shí)較長(zhǎng)工作,會(huì)放到中斷之后,由軟中斷來(lái)完成。硬中斷也被稱(chēng)為上半部分。
軟中斷: 由硬中斷對(duì)應(yīng)的中斷處理程序生成,往往是預(yù)先在代碼里實(shí)現(xiàn)好的,不具有隨機(jī)性。(除此之外,也有應(yīng)用程序觸發(fā)的軟中斷,與本文討論的網(wǎng)卡收包無(wú)關(guān)。)也被稱(chēng)為下半部分。

當(dāng)NIC把數(shù)據(jù)包通過(guò)DMA復(fù)制到內(nèi)核緩沖區(qū)sk_buffer后,NIC立即發(fā)起一個(gè)硬件中斷。CPU接收后,首先進(jìn)入上半部分,網(wǎng)卡中斷對(duì)應(yīng)的中斷處理程序是網(wǎng)卡驅(qū)動(dòng)程序的一部分,之后由它發(fā)起軟中斷,進(jìn)入下半部分,開(kāi)始消費(fèi)sk_buffer中的數(shù)據(jù),交給內(nèi)核協(xié)議棧處理。

image

通過(guò)中斷,能夠快速及時(shí)地響應(yīng)網(wǎng)卡數(shù)據(jù)請(qǐng)求,但如果數(shù)據(jù)量大,那么會(huì)產(chǎn)生大量中斷請(qǐng)求,CPU大部分時(shí)間都忙于處理中斷,效率很低。為了解決這個(gè)問(wèn)題,現(xiàn)在的內(nèi)核及驅(qū)動(dòng)都采用一種叫NAPI(new API)的方式進(jìn)行數(shù)據(jù)處理,其原理可以簡(jiǎn)單理解為 中斷+輪詢(xún),在數(shù)據(jù)量大時(shí),一次中斷后通過(guò)輪詢(xún)接收一定數(shù)量包再返回,避免產(chǎn)生多次中斷。

image

網(wǎng)卡的基礎(chǔ)知識(shí)

網(wǎng)卡本身是有內(nèi)存的,每個(gè)網(wǎng)卡一般都有4k以上的內(nèi)存,用來(lái)發(fā)送、接受數(shù)據(jù)。數(shù)據(jù)從主內(nèi)存搬到網(wǎng)卡之后,不是立即就能被發(fā)送出去的,而是要先在網(wǎng)卡自身的內(nèi)存中排隊(duì),再按先后順序發(fā)送,同樣的,數(shù)據(jù)從以太網(wǎng)傳遞到網(wǎng)卡時(shí),網(wǎng)卡也是先把數(shù)據(jù)存儲(chǔ)到自身的內(nèi)存中,等到收到一幀數(shù)據(jù)了,再經(jīng)過(guò)中斷的方式,告訴CPU把網(wǎng)卡內(nèi)存的數(shù)據(jù)讀走(現(xiàn)在網(wǎng)卡大都支持DMA方式直接從網(wǎng)卡內(nèi)存拷貝被內(nèi)核內(nèi)存),而讀走后的內(nèi)存,又被清空,再次被用來(lái)接收新的數(shù)據(jù)。


image.gif

藍(lán)色部分為發(fā)送數(shù)據(jù)用的頁(yè)面總和,總共只有6個(gè)頁(yè)面用于發(fā)送數(shù)據(jù)(40h-45h);剩余的46h-80h都是接收數(shù)據(jù)用的,而在接收數(shù)據(jù)內(nèi)存中,只有紅色部分是有數(shù)據(jù)的,當(dāng)接收新的數(shù)據(jù)時(shí),是向紅色部分前面的綠色中的256字節(jié)寫(xiě)入數(shù)據(jù),同時(shí)“把當(dāng)前指針”移動(dòng)到+256字節(jié)的后面(網(wǎng)卡自動(dòng)完成),而現(xiàn)在要讀的數(shù)據(jù),是在“邊界指針”那里開(kāi)始的256字節(jié)(紫色部分),下一個(gè)要讀的數(shù)據(jù),是在“下一包指針”的位置開(kāi)始的256字節(jié),當(dāng)256字節(jié)被讀出來(lái)了,就變成了重新可以使用的內(nèi)存,即綠色所表示,而接收數(shù)據(jù),就是把可用的內(nèi)存拿來(lái)用,即變成了紅色,當(dāng)數(shù)據(jù)寫(xiě)到了0x80h后,又從0x46h開(kāi)始寫(xiě)數(shù)據(jù),這樣循環(huán),如果數(shù)據(jù)滿了,則網(wǎng)卡就不能再接收數(shù)據(jù),必須等待數(shù)據(jù)被讀出去了,才能再繼續(xù)接收。

網(wǎng)卡到內(nèi)存

網(wǎng)卡需要有驅(qū)動(dòng)才能工作,驅(qū)動(dòng)是加載到內(nèi)核中的模塊,負(fù)責(zé)銜接網(wǎng)卡和內(nèi)核的網(wǎng)絡(luò)模塊,驅(qū)動(dòng)在加載的時(shí)候?qū)⒆约鹤?cè)進(jìn)網(wǎng)絡(luò)模塊,當(dāng)相應(yīng)的網(wǎng)卡收到數(shù)據(jù)包時(shí),網(wǎng)絡(luò)模塊會(huì)調(diào)用相應(yīng)的驅(qū)動(dòng)程序處理數(shù)據(jù)。
下圖展示了數(shù)據(jù)包(packet)如何進(jìn)入內(nèi)存,并被內(nèi)核的網(wǎng)絡(luò)模塊開(kāi)始處理:


image.png
  1. 數(shù)據(jù)包從外面的網(wǎng)絡(luò)進(jìn)入物理網(wǎng)卡。如果目的地址不是該網(wǎng)卡,并且該網(wǎng)卡沒(méi)有開(kāi)啟混雜模式,該包會(huì)被網(wǎng)卡丟棄。
  2. 網(wǎng)卡將數(shù)據(jù)包通過(guò)DMA的方式寫(xiě)入到指定的內(nèi)存地址,該地址由網(wǎng)卡驅(qū)動(dòng)分配。
  3. 網(wǎng)卡通過(guò)硬件中斷(IRQ)告知cpu有數(shù)據(jù)來(lái)了。
  4. cpu根據(jù)中斷表,調(diào)用已經(jīng)注冊(cè)的中斷函數(shù),這個(gè)中斷函數(shù)會(huì)調(diào)動(dòng)驅(qū)動(dòng)程序中相應(yīng)的函數(shù)
  5. 驅(qū)動(dòng)先禁用網(wǎng)卡的中斷,表示驅(qū)動(dòng)程序已經(jīng)知道內(nèi)存中有數(shù)據(jù)了,告訴網(wǎng)卡下次再收到數(shù)據(jù)包直接寫(xiě)內(nèi)存就可以了,不要再通知cpu了,這樣可以提高效率,避免cpu不停的被中斷
  6. 啟動(dòng)軟中斷。這步結(jié)束后,硬件中斷處理函數(shù)就結(jié)束返回了,由于硬中斷處理程序執(zhí)行的過(guò)程中不能被中斷,所以如果它執(zhí)行時(shí)間過(guò)長(zhǎng),會(huì)導(dǎo)致cpu無(wú)法響應(yīng)其他硬件的中斷,于是內(nèi)核引入軟中斷,這樣可以將硬中斷處理函數(shù)中耗時(shí)的部分移到軟中斷處理函數(shù)里面來(lái)慢慢處理。

內(nèi)核的網(wǎng)絡(luò)模塊

軟中斷會(huì)觸發(fā)內(nèi)核網(wǎng)絡(luò)模塊中的軟中斷處理函數(shù),后續(xù)流程如下:

                                                     +-----+
                                             17      |     |
                                        +----------->| NIC |
                                        |            |     |
                                        |Enable IRQ  +-----+
                                        |
                                        |
                                  +------------+                                      Memroy
                                  |            |        Read           +--------+--------+--------+--------+
                 +--------------->| NIC Driver |<--------------------- | Packet | Packet | Packet | ...... |
                 |                |            |          9            +--------+--------+--------+--------+
                 |                +------------+
                 |                      |    |        skb
            Poll | 8      Raise softIRQ | 6  +-----------------+
                 |                      |             10       |
                 |                      ↓                      ↓
         +---------------+  Call  +-----------+        +------------------+        +--------------------+  12  +---------------------+
         | net_rx_action |<-------| ksoftirqd |        | napi_gro_receive |------->| enqueue_to_backlog |----->| CPU input_pkt_queue |
         +---------------+   7    +-----------+        +------------------+   11   +--------------------+      +---------------------+
                                                               |                                                      | 13
                                                            14 |        + - - - - - - - - - - - - - - - - - - - - - - +
                                                               ↓        ↓
                                                    +--------------------------+    15      +------------------------+
                                                    | __netif_receive_skb_core |----------->| packet taps(AF_PACKET) |
                                                    +--------------------------+            +------------------------+
                                                               |
                                                               | 16
                                                               ↓
                                                      +-----------------+
                                                      | protocol layers |
                                                      +-----------------+

  1. 內(nèi)核中的ksoftirqd進(jìn)程專(zhuān)門(mén)負(fù)責(zé)軟中斷的處理,當(dāng)它收到軟中斷后,就會(huì)調(diào)用相應(yīng)軟中斷所對(duì)應(yīng)的處理函數(shù),對(duì)于上面第六步中網(wǎng)卡驅(qū)動(dòng)模塊拋出的軟中斷,ksoftirqd會(huì)調(diào)用網(wǎng)絡(luò)模塊的net_rx_action函數(shù)
  2. net_rx_action調(diào)用網(wǎng)卡驅(qū)動(dòng)里的poll函數(shù)來(lái)一個(gè)一個(gè)的處理數(shù)據(jù)包
  3. 在pool函數(shù)中,驅(qū)動(dòng)會(huì)一個(gè)接一個(gè)的讀取網(wǎng)卡寫(xiě)到內(nèi)存中的數(shù)據(jù)包,內(nèi)存中數(shù)據(jù)包的格式只有驅(qū)動(dòng)知道
  4. 驅(qū)動(dòng)程序?qū)?nèi)存中的數(shù)據(jù)包轉(zhuǎn)換成內(nèi)核網(wǎng)絡(luò)模塊能識(shí)別的skb格式,然后調(diào)用napi_gro_receive函數(shù)
  5. napi_gro_receive會(huì)處理GRO相關(guān)的內(nèi)容,也就是將可以合并的數(shù)據(jù)包進(jìn)行合并,這樣就只需要調(diào)用一次協(xié)議棧。然后判斷是否開(kāi)啟了RPS,如果開(kāi)啟了,將會(huì)調(diào)用enqueue_to_backlog
  6. 在enqueue_to_backlog函數(shù)中,會(huì)將數(shù)據(jù)包放入CPU的softnet_data結(jié)構(gòu)體的input_pkt_queue中,然后返回,如果input_pkt_queue滿了的話,該數(shù)據(jù)包將會(huì)被丟棄,queue的大小可以通過(guò)net.core.netdev_max_backlog來(lái)配置
  7. CPU會(huì)接著在自己的軟中斷上下文中處理自己input_pkt_queue里的網(wǎng)絡(luò)數(shù)據(jù)(調(diào)用__netif_receive_skb_core)
  8. 如果沒(méi)開(kāi)啟RPS,napi_gro_receive會(huì)直接調(diào)用__netif_receive_skb_core
  9. 看是不是有AF_PACKET類(lèi)型的socket(也就是我們常說(shuō)的原始套接字),如果有的話,拷貝一份數(shù)據(jù)給它。tcpdump抓包就是抓的這里的包。
  10. 調(diào)用協(xié)議棧相應(yīng)的函數(shù),將數(shù)據(jù)包交給協(xié)議棧處理。
  11. 待內(nèi)存中的所有數(shù)據(jù)包被處理完成后(即poll函數(shù)執(zhí)行完成),啟用網(wǎng)卡的硬中斷,這樣下次網(wǎng)卡再收到數(shù)據(jù)的時(shí)候就會(huì)通知CPU

協(xié)議棧

IP層

          |
          |
          ↓         promiscuous mode &&
      +--------+    PACKET_OTHERHOST (set by driver)   +-----------------+
      | ip_rcv |-------------------------------------->| drop this packet|
      +--------+                                       +-----------------+
          |
          |
          ↓
+---------------------+
| NF_INET_PRE_ROUTING |
+---------------------+
          |
          |
          ↓
      +---------+
      |         | enabled ip forword  +------------+        +----------------+
      | routing |-------------------->| ip_forward |------->| NF_INET_FORWARD |
      |         |                     +------------+        +----------------+
      +---------+                                                   |
          |                                                         |
          | destination IP is local                                 ↓
          ↓                                                 +---------------+
 +------------------+                                       | dst_output_sk |
 | ip_local_deliver |                                       +---------------+
 +------------------+
          |
          |
          ↓
 +------------------+
 | NF_INET_LOCAL_IN |
 +------------------+
          |
          |
          ↓
    +-----------+
    | UDP layer |
    +-----------+
  • ip_rcv: ip_rcv函數(shù)是IP模塊的入口函數(shù),在該函數(shù)里面,第一件事就是將垃圾數(shù)據(jù)包(目的mac地址不是當(dāng)前網(wǎng)卡,但由于網(wǎng)卡設(shè)置了混雜模式而被接收進(jìn)來(lái))直接丟掉,然后調(diào)用注冊(cè)在NF_INET_PRE_ROUTING上的函數(shù)
  • NF_INET_PRE_ROUTING: netfilter放在協(xié)議棧中的鉤子,可以通過(guò)iptables來(lái)注入一些數(shù)據(jù)包處理函數(shù),用來(lái)修改或者丟棄數(shù)據(jù)包,如果數(shù)據(jù)包沒(méi)被丟棄,將繼續(xù)往下走
  • routing: 進(jìn)行路由,如果是目的IP不是本地IP,且沒(méi)有開(kāi)啟ip forward功能,那么數(shù)據(jù)包將被丟棄,如果開(kāi)啟了ip forward功能,那將進(jìn)入ip_forward函數(shù)
  • ip_forward: ip_forward會(huì)先調(diào)用netfilter注冊(cè)的NF_INET_FORWARD相關(guān)函數(shù),如果數(shù)據(jù)包沒(méi)有被丟棄,那么將繼續(xù)往后調(diào)用dst_output_sk函數(shù)
  • 該函數(shù)會(huì)調(diào)用IP層的相應(yīng)函數(shù)將該數(shù)據(jù)包發(fā)送出去
  • ip_local_deliver:如果上面routing的時(shí)候發(fā)現(xiàn)目的IP是本地IP,那么將會(huì)調(diào)用該函數(shù),在該函數(shù)中,會(huì)先調(diào)用NF_INET_LOCAL_IN相關(guān)的鉤子程序,如果通過(guò),數(shù)據(jù)包將會(huì)向下發(fā)送到UDP層

UDP層

          |
          |
          ↓
      +---------+            +-----------------------+
      | udp_rcv |----------->| __udp4_lib_lookup_skb |
      +---------+            +-----------------------+
          |
          |
          ↓
 +--------------------+      +-----------+
 | sock_queue_rcv_skb |----->| sk_filter |
 +--------------------+      +-----------+
          |
          |
          ↓
 +------------------+
 | __skb_queue_tail |
 +------------------+
          |
          |
          ↓
  +---------------+
  | sk_data_ready |
  +---------------+
  • udp_rcv: udp_rcv函數(shù)是UDP模塊的入口函數(shù),它里面會(huì)調(diào)用其它的函數(shù),主要是做一些必要的檢查,其中一個(gè)重要的調(diào)用是__udp4_lib_lookup_skb,該函數(shù)會(huì)根據(jù)目的IP和端口找對(duì)應(yīng)的socket,如果沒(méi)有找到相應(yīng)的socket,那么該數(shù)據(jù)包將會(huì)被丟棄,否則繼續(xù)
  • sock_queue_rcv_skb: 主要干了兩件事,一是檢查這個(gè)socket的receive buffer是不是滿了,如果滿了的話,丟棄該數(shù)據(jù)包,然后就是調(diào)用sk_filter看這個(gè)包是否是滿足條件的包,如果當(dāng)前socket上設(shè)置了filter,且該包不滿足條件的話,這個(gè)數(shù)據(jù)包也將被丟棄(在Linux里面,每個(gè)socket上都可以像tcpdump里面一樣定義filter,不滿足條件的數(shù)據(jù)包將會(huì)被丟棄)
  • __skb_queue_tail: 將數(shù)據(jù)包放入socket接收隊(duì)列的末尾
  • sk_data_ready: 通知socket數(shù)據(jù)包已經(jīng)準(zhǔn)備好
    調(diào)用完sk_data_ready之后,一個(gè)數(shù)據(jù)包處理完成,等待應(yīng)用層程序來(lái)讀取,上面所有函數(shù)的執(zhí)行過(guò)程都在軟中斷的上下文中。

socket

應(yīng)用層一般有兩種方式接收數(shù)據(jù),一種是recvfrom函數(shù)阻塞在那里等著數(shù)據(jù)來(lái),這種情況下當(dāng)socket收到通知后,recvfrom就會(huì)被喚醒,然后讀取接收隊(duì)列的數(shù)據(jù);另一種是通過(guò)epoll或者select監(jiān)聽(tīng)相應(yīng)的socket,當(dāng)收到通知后,再調(diào)用recvfrom函數(shù)去讀取接收隊(duì)列的數(shù)據(jù)。兩種情況都能正常的接收到相應(yīng)的數(shù)據(jù)包。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容