網(wǎng)卡收包從整體上是網(wǎng)線中的高低電平轉(zhuǎn)換到網(wǎng)卡FIFO存儲(chǔ)再拷貝到系統(tǒng)主內(nèi)存(DDR3)的過程氧吐,其中涉及到網(wǎng)卡控制器博个,CPU怀樟,DMA,驅(qū)動(dòng)程序盆佣,在OSI模型中屬于物理層和鏈路層往堡,如下圖所示械荷。
網(wǎng)卡工作在物理層和數(shù)據(jù)鏈路層,主要由PHY/MAC芯片虑灰、Tx/Rx FIFO吨瞎、DMA等組成,其中網(wǎng)線通過變壓器接PHY芯片穆咐、PHY芯片通過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為代表
接收數(shù)據(jù)包是一個(gè)復(fù)雜的過程,涉及很多底層的技術(shù)細(xì)節(jié)拆讯,但大致需要以下幾個(gè)步驟:
- 網(wǎng)卡收到數(shù)據(jù)包脂男。
- 將數(shù)據(jù)包從網(wǎng)卡硬件緩存轉(zhuǎn)移到服務(wù)器內(nèi)存中。
- 通知內(nèi)核處理种呐。
- 經(jīng)過TCP/IP協(xié)議逐層處理宰翅。
- 應(yīng)用程序通過read()從socket buffer讀取數(shù)據(jù)。
物理網(wǎng)卡收到數(shù)據(jù)包的處理流程如上圖所示爽室,詳細(xì)步驟如下:
- 網(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ù)包寫到了該物理地址,卜录,其實(shí)就是skb_buffer中.
- 這個(gè)時(shí)候數(shù)據(jù)包已經(jīng)被轉(zhuǎn)移到skb_buffer中戈擒,因?yàn)槭荄MA寫入,內(nèi)核并沒有監(jiān)控?cái)?shù)據(jù)包寫入情況艰毒,這時(shí)候NIC觸發(fā)一個(gè)硬中斷筐高,每一個(gè)硬件中斷會(huì)對(duì)應(yīng)一個(gè)中斷號(hào),且指定一個(gè)vCPU來(lái)處理丑瞧,如上圖vcpu2收到了該硬件中斷.
- 硬件中斷的中斷處理程序柑土,調(diào)用驅(qū)動(dòng)程序完成,a.啟動(dòng)軟中斷
- 硬中斷觸發(fā)的驅(qū)動(dòng)程序會(huì)禁用網(wǎng)卡硬中斷绊汹,其實(shí)這時(shí)候意思是告訴NIC稽屏,再來(lái)數(shù)據(jù)不用觸發(fā)硬中斷了,把數(shù)據(jù)DMA拷入系統(tǒng)內(nèi)存即可
- 硬中斷觸發(fā)的驅(qū)動(dòng)程序會(huì)啟動(dòng)軟中斷西乖,啟用軟中斷目的是將數(shù)據(jù)包后續(xù)處理流程交給軟中斷慢慢處理狐榔,這個(gè)時(shí)候退出硬件中斷了坛增,但是注意和網(wǎng)絡(luò)有關(guān)的硬中斷,要等到后續(xù)開啟硬中斷后薄腻,才有機(jī)會(huì)再次被觸發(fā)
- NAPI觸發(fā)軟中斷收捣,觸發(fā)napi系統(tǒng)
- 消耗ringbuffer指向的skb_buffer
- NAPI循環(huán)處理ringbuffer數(shù)據(jù),處理完成
- 啟動(dòng)網(wǎng)絡(luò)硬件中斷庵楷,有數(shù)據(jù)來(lái)時(shí)候就可以繼續(xù)觸發(fā)硬件中斷罢艾,繼續(xù)通知CPU來(lái)消耗數(shù)據(jù)包.
其實(shí)上述過程過程簡(jiǎn)單描述為:網(wǎng)卡收到數(shù)據(jù)包,DMA到內(nèi)核內(nèi)存尽纽,中斷通知內(nèi)核數(shù)據(jù)有了咐蚯,內(nèi)核按輪次處理消耗數(shù)據(jù)包,一輪處理完成后弄贿,開啟硬中斷仓蛆。其核心就是網(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)過快會(huì)產(chǎn)生丟包直奋,如果消費(fèi)過慢也會(huì)產(chǎn)生問題能庆。也就說(shuō)在高流量壓力情況下,只有生產(chǎn)消費(fèi)優(yōu)化后脚线,消費(fèi)能力夠快搁胆,此生產(chǎn)消費(fèi)關(guān)系才可以正常維持,所以如果物理接口有丟包計(jì)數(shù)時(shí)候邮绿,未必是網(wǎng)卡存在問題渠旁,也可能是內(nèi)核消費(fèi)的太慢。
如何將網(wǎng)卡收到的數(shù)據(jù)寫入到內(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ǔ)地址,具體流程如下:
- 驅(qū)動(dòng)在內(nèi)存中分配一片緩沖區(qū)用來(lái)接收數(shù)據(jù)包凹髓,叫做sk_buffer;
- 將上述緩沖區(qū)的地址和大兴傅恰(即接收描述符),加入到rx ring buffer蔚舀。描述符中的緩沖區(qū)地址是DMA使用的物理地址;
- 驅(qū)動(dòng)通知網(wǎng)卡有一個(gè)新的描述符;
- 網(wǎng)卡從rx ring buffer中取出描述符饵沧,從而獲知緩沖區(qū)的地址和大小;
- 網(wǎng)卡收到新的數(shù)據(jù)包;
- 網(wǎng)卡將新數(shù)據(jù)包通過DMA直接寫到sk_buffer中蚀之。
當(dāng)驅(qū)動(dòng)處理速度跟不上網(wǎng)卡收包速度時(shí),驅(qū)動(dòng)來(lái)不及分配緩沖區(qū)捷泞,NIC接收到的數(shù)據(jù)包無(wú)法及時(shí)寫到sk_buffer足删,就會(huì)產(chǎn)生堆積,當(dāng)NIC內(nèi)部緩沖區(qū)寫滿后锁右,就會(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ū)余寥,并且是通過DMA寫入的领铐,這種方式不依賴CPU直接將數(shù)據(jù)寫到了內(nèi)存中,意味著對(duì)內(nèi)核來(lái)說(shuō)宋舷,其實(shí)并不知道已經(jīng)有新數(shù)據(jù)到了內(nèi)存中绪撵。那么如何讓內(nèi)核知道有新數(shù)據(jù)進(jìn)來(lái)了呢?答案就是中斷祝蝠,通過中斷告訴內(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)完成。硬中斷也被稱為上半部分兄世。
軟中斷: 由硬中斷對(duì)應(yīng)的中斷處理程序生成啼辣,往往是預(yù)先在代碼里實(shí)現(xiàn)好的,不具有隨機(jī)性御滩。(除此之外鸥拧,也有應(yīng)用程序觸發(fā)的軟中斷党远,與本文討論的網(wǎng)卡收包無(wú)關(guān)。)也被稱為下半部分富弦。
當(dāng)NIC把數(shù)據(jù)包通過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)入下半部分砰蠢,開始消費(fèi)sk_buffer中的數(shù)據(jù),交給內(nèi)核協(xié)議棧處理唉铜。
通過中斷台舱,能夠快速及時(shí)地響應(yīng)網(wǎng)卡數(shù)據(jù)請(qǐng)求,但如果數(shù)據(jù)量大潭流,那么會(huì)產(chǎn)生大量中斷請(qǐng)求竞惋,CPU大部分時(shí)間都忙于處理中斷,效率很低灰嫉。為了解決這個(gè)問題碰声,現(xiàn)在的內(nèi)核及驅(qū)動(dòng)都采用一種叫NAPI(new API)的方式進(jìn)行數(shù)據(jù)處理,其原理可以簡(jiǎn)單理解為 中斷+輪詢熬甫,在數(shù)據(jù)量大時(shí)胰挑,一次中斷后通過輪詢接收一定數(shù)量包再返回,避免產(chǎn)生多次中斷椿肩。
網(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)過中斷的方式湃望,告訴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ù)瞳浦。
藍(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é)寫入數(shù)據(jù),同時(shí)“把當(dāng)前指針”移動(dòng)到+256字節(jié)的后面(網(wǎng)卡自動(dòng)完成)泛源,而現(xiàn)在要讀的數(shù)據(jù)拔妥,是在“邊界指針”那里開始的256字節(jié)(紫色部分),下一個(gè)要讀的數(shù)據(jù)达箍,是在“下一包指針”的位置開始的256字節(jié)没龙,當(dāng)256字節(jié)被讀出來(lái)了,就變成了重新可以使用的內(nèi)存缎玫,即綠色所表示硬纤,而接收數(shù)據(jù),就是把可用的內(nèi)存拿來(lái)用赃磨,即變成了紅色筝家,當(dāng)數(shù)據(jù)寫到了0x80h后,又從0x46h開始寫數(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ò)模塊開始處理:
- 數(shù)據(jù)包從外面的網(wǎng)絡(luò)進(jìn)入物理網(wǎng)卡拘鞋。如果目的地址不是該網(wǎng)卡,并且該網(wǎng)卡沒有開啟混雜模式兄朋,該包會(huì)被網(wǎng)卡丟棄掐禁。
- 網(wǎng)卡將數(shù)據(jù)包通過DMA的方式寫入到指定的內(nèi)存地址怜械,該地址由網(wǎng)卡驅(qū)動(dòng)分配颅和。
- 網(wǎng)卡通過硬件中斷(IRQ)告知cpu有數(shù)據(jù)來(lái)了傅事。
- cpu根據(jù)中斷表,調(diào)用已經(jīng)注冊(cè)的中斷函數(shù)峡扩,這個(gè)中斷函數(shù)會(huì)調(diào)動(dòng)驅(qū)動(dòng)程序中相應(yīng)的函數(shù)
- 驅(qū)動(dòng)先禁用網(wǎng)卡的中斷蹭越,表示驅(qū)動(dòng)程序已經(jīng)知道內(nèi)存中有數(shù)據(jù)了,告訴網(wǎng)卡下次再收到數(shù)據(jù)包直接寫內(nèi)存就可以了教届,不要再通知cpu了响鹃,這樣可以提高效率,避免cpu不停的被中斷
- 啟動(dòng)軟中斷案训。這步結(jié)束后买置,硬件中斷處理函數(shù)就結(jié)束返回了,由于硬中斷處理程序執(zhí)行的過程中不能被中斷强霎,所以如果它執(zhí)行時(shí)間過長(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 |
+-----------------+
- 內(nèi)核中的ksoftirqd進(jì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ù)
- net_rx_action調(diào)用網(wǎng)卡驅(qū)動(dòng)里的poll函數(shù)來(lái)一個(gè)一個(gè)的處理數(shù)據(jù)包
- 在pool函數(shù)中榨为,驅(qū)動(dòng)會(huì)一個(gè)接一個(gè)的讀取網(wǎng)卡寫到內(nèi)存中的數(shù)據(jù)包,內(nèi)存中數(shù)據(jù)包的格式只有驅(qū)動(dòng)知道
- 驅(qū)動(dòng)程序?qū)?nèi)存中的數(shù)據(jù)包轉(zhuǎn)換成內(nèi)核網(wǎng)絡(luò)模塊能識(shí)別的skb格式煌茴,然后調(diào)用napi_gro_receive函數(shù)
- napi_gro_receive會(huì)處理GRO相關(guān)的內(nèi)容柠逞,也就是將可以合并的數(shù)據(jù)包進(jìn)行合并,這樣就只需要調(diào)用一次協(xié)議棧景馁。然后判斷是否開啟了RPS板壮,如果開啟了,將會(huì)調(diào)用enqueue_to_backlog
- 在enqueue_to_backlog函數(shù)中合住,會(huì)將數(shù)據(jù)包放入CPU的softnet_data結(jié)構(gòu)體的input_pkt_queue中绰精,然后返回,如果input_pkt_queue滿了的話透葛,該數(shù)據(jù)包將會(huì)被丟棄笨使,queue的大小可以通過net.core.netdev_max_backlog來(lái)配置
- CPU會(huì)接著在自己的軟中斷上下文中處理自己input_pkt_queue里的網(wǎng)絡(luò)數(shù)據(jù)(調(diào)用__netif_receive_skb_core)
- 如果沒開啟RPS,napi_gro_receive會(huì)直接調(diào)用__netif_receive_skb_core
- 看是不是有AF_PACKET類型的socket(也就是我們常說(shuō)的原始套接字)僚害,如果有的話硫椰,拷貝一份數(shù)據(jù)給它。tcpdump抓包就是抓的這里的包。
- 調(diào)用協(xié)議棧相應(yīng)的函數(shù)靶草,將數(shù)據(jù)包交給協(xié)議棧處理蹄胰。
- 待內(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é)議棧中的鉤子,可以通過iptables來(lái)注入一些數(shù)據(jù)包處理函數(shù)驾窟,用來(lái)修改或者丟棄數(shù)據(jù)包庆猫,如果數(shù)據(jù)包沒被丟棄,將繼續(xù)往下走
- routing: 進(jìn)行路由绅络,如果是目的IP不是本地IP月培,且沒有開啟ip forward功能,那么數(shù)據(jù)包將被丟棄昨稼,如果開啟了ip forward功能节视,那將進(jìn)入ip_forward函數(shù)
- ip_forward: ip_forward會(huì)先調(diào)用netfilter注冊(cè)的NF_INET_FORWARD相關(guān)函數(shù),如果數(shù)據(jù)包沒有被丟棄假栓,那么將繼續(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)的鉤子程序拌蜘,如果通過,數(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举娩,如果沒有找到相應(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í)行過程都在軟中斷的上下文中陪蜻。
socket
應(yīng)用層一般有兩種方式接收數(shù)據(jù)邦马,一種是recvfrom函數(shù)阻塞在那里等著數(shù)據(jù)來(lái)贱鼻,這種情況下當(dāng)socket收到通知后宴卖,recvfrom就會(huì)被喚醒,然后讀取接收隊(duì)列的數(shù)據(jù)邻悬;另一種是通過epoll或者select監(jiān)聽相應(yīng)的socket症昏,當(dāng)收到通知后,再調(diào)用recvfrom函數(shù)去讀取接收隊(duì)列的數(shù)據(jù)父丰。兩種情況都能正常的接收到相應(yīng)的數(shù)據(jù)包肝谭。