linux网络协议栈之数据包处理过程,Linux网络协议栈之数据包处理过程
這篇文檔是基于 x86 體系結(jié)構(gòu)和轉(zhuǎn)發(fā) IP 分組的。
數(shù)據(jù)包在 Linux 內(nèi)核鏈路層路徑?接收分組
1 接收中斷
如果網(wǎng)卡收到一個(gè)和自己 MAC 地址匹配或鏈路層廣播的以太網(wǎng)幀,它就會(huì)產(chǎn)生一個(gè)中斷。此網(wǎng)卡的驅(qū)動(dòng)程序會(huì)處理此中斷:
從 DMA/PIO 或其他得到分組數(shù)據(jù),寫到內(nèi)存里去;
接著,會(huì)分配一個(gè)新的套接字緩沖區(qū) skb ,并調(diào)用與協(xié)議無關(guān)的、網(wǎng)絡(luò)設(shè)備均支持的通用網(wǎng)絡(luò)接收處理函數(shù) netif_rx(skb)。 netif_rx() 函數(shù)讓內(nèi)核準(zhǔn)備進(jìn)一步處理 skb 。
然后, skb 會(huì)進(jìn)入到達(dá)隊(duì)列以便 CPU 處理(對(duì)于多核 CPU 而言,每個(gè) CPU 維護(hù)一個(gè)隊(duì)列)。如果 FIFO 隊(duì)列已滿,就會(huì)丟棄此分組。在 skb 排隊(duì)后,調(diào)用 __cpu_raise_softirq() 標(biāo)記 NET_RX_SOFTIRQ 軟中斷,等待 CPU 執(zhí)行。
至此, netif_rx() 函數(shù)調(diào)用結(jié)束,返回調(diào)用者狀況信息(成功還是失敗等)。此時(shí),中斷上下文進(jìn)程完成任務(wù),數(shù)據(jù)分組繼續(xù)被上層協(xié)議棧處理。
2 softirq 和 bottom half
內(nèi)核 2.4 以后,整個(gè)協(xié)議棧不再使用 bottom half (下半文,沒找到好的翻譯),而是被軟中斷 softirq 取代。軟中斷softirq 優(yōu)勢(shì)明顯,可以同時(shí)在多個(gè) CPU 上執(zhí)行;而 bottom half 一次只能在一個(gè) CPU 上執(zhí)行,即在多個(gè) CPU 執(zhí)行時(shí)嚴(yán)格保持串行。
中斷服務(wù)程序往往都是在 CPU 關(guān)中斷的條件下執(zhí)行的,以避免中斷嵌套而使控制復(fù)雜化。但是 CPU 關(guān)中斷的時(shí)間不能太長(zhǎng),否則容易丟失中斷信號(hào)。為此, Linux 將中斷服務(wù)程序一分為二,各稱作“ Top Half ”和“ Bottom Half ”。前者通常對(duì)時(shí)間要求較為嚴(yán)格,必須在中斷請(qǐng)求發(fā)生后立即或至少在一定的時(shí)間限制內(nèi)完成。因此為了保證這種處理能原子地完成, Top Half 通常是在 CPU 關(guān)中斷的條件下執(zhí)行的。具體地說, Top Half 的范圍包括:從在 IDT 中登記的中斷入口函數(shù)一直到驅(qū)動(dòng)程序注冊(cè)在中斷服務(wù)隊(duì)列中的 ISR 。而 Bottom Half 則是 Top Half 根據(jù)需要來調(diào)度執(zhí)行的,這些操作允許延遲到稍后執(zhí)行,它的時(shí)間要求并不嚴(yán)格,因此它通常是在 CPU 開中斷的條件下執(zhí)行的,比如網(wǎng)絡(luò)底層操作就是這樣,由于某些原因,中斷并沒有立刻響應(yīng),而是先記錄下來,等到可以處理這些中斷的時(shí)候就一塊處理了。但是, Linux 的這種 Bottom Half (以下簡(jiǎn)稱 BH )機(jī)制有兩個(gè)缺點(diǎn),也即:
( 1 )在任意一時(shí)刻,系統(tǒng)只能有一個(gè) CPU 可以執(zhí)行 Bottom Half 代碼,以防止兩個(gè)或多個(gè) CPU 同時(shí)來執(zhí)行 Bottom Half 函數(shù)而相互干擾。因此 BH 代碼的執(zhí)行是嚴(yán)格“串行化”的。
( 2 ) BH 函數(shù)不允許嵌套。
這兩個(gè)缺點(diǎn)在單 CPU 系統(tǒng)中是無關(guān)緊要的,但在 SMP 系統(tǒng)中卻是非常致命的。因?yàn)?BH 機(jī)制的嚴(yán)格串行化執(zhí)行顯然沒有充分利用 SMP 系統(tǒng)的多 CPU 特點(diǎn)。為此, Linux2.4 內(nèi)核在 BH 機(jī)制的基礎(chǔ)上進(jìn)行了擴(kuò)展,這就是所謂的“軟中斷請(qǐng)求”( softirq )機(jī)制。 Linux 的 softirq 機(jī)制是與 SMP 緊密不可分的。為此,整個(gè) softirq 機(jī)制的設(shè)計(jì)與實(shí)現(xiàn)中自始自終都貫徹了一個(gè)思想:“誰觸發(fā),誰執(zhí)行 ”( Who marks , Who runs ),也即觸發(fā)軟中斷的那個(gè) CPU 負(fù)責(zé)執(zhí)行它所觸發(fā)的軟中斷,而且每個(gè) CPU 都由它自己的軟中斷觸發(fā)與控制機(jī)制。這個(gè)設(shè)計(jì)思想也使得 softirq 機(jī)制充分利用了 SMP 系統(tǒng)的性能和特點(diǎn)。
3 NET_RX_SOFTIRQ 網(wǎng)絡(luò)接收軟中斷
這一階段會(huì)根據(jù)協(xié)議的不同來處理數(shù)據(jù)分組。 CPU 開始處理軟中斷 do_softirq() ,,接著 net_rx_action() 處理前面標(biāo)記的 NET_RX_SOFTIRQ ,把出對(duì)列的 skb 送入相應(yīng)列表處理(根據(jù)協(xié)議不同到不同的列表)。比如, IP 分組交給 ip_rcv()處理, ARP 分組交給 arp_rcv() 處理等。
4 處理 IPv4 分組
下面以 IPv4 為例,講解 IPv4 分組在高層的處理。
linux 內(nèi)核協(xié)議棧之網(wǎng)絡(luò)層
ip_rcv() 函數(shù)驗(yàn)證 IP 分組,比如目的地址是否本機(jī)地址,校驗(yàn)和是否正確等。若正確,則交給 netfilter 的NF_IP_PRE_ROUTING 鉤子(關(guān)于netfilter細(xì)節(jié)可以參考 Hacking the Linux Kernel Network Stack );否則,丟棄。到了 ip_rcv_finish() 函數(shù),數(shù)據(jù)包就要根據(jù) skb 結(jié)構(gòu)的目的或路由信息各奔東西了。
ip_local_deliver() 處理到本機(jī)的數(shù)據(jù)分組;
ip_forward() 處理需要轉(zhuǎn)發(fā)的數(shù)據(jù)分組;
ip_mr_input() 轉(zhuǎn)發(fā)組播數(shù)據(jù)包
至此,數(shù)據(jù)分組的接受和處理工作就告一段落了,至于于此相對(duì)的數(shù)據(jù)分組的發(fā)送,我就貼個(gè)圖吧,具體細(xì)節(jié)可參考 The Linux? Networking Architecture: Design and Implementation of Network Protocols in the Linux Kernel Prentice Hall August 01, 2004
dev_queue_xmit() 處理發(fā)送分組
附一張 Linux 2.4 核的 netfilter 框架下分組的走向圖:
總結(jié)下:
1.中斷處理函數(shù)中:
網(wǎng)卡收到一幀------------------------〉
引發(fā)中斷-------------------〉
cpu調(diào)用相應(yīng)的中斷處理函數(shù)(指向此網(wǎng)卡驅(qū)動(dòng)中的相應(yīng)的處理函數(shù))(把此packet讀到ram中)--------------------〉
呼叫netif_rx函數(shù)來打上timestamp,并把此skb放入到cpu設(shè)置的隊(duì)列中-----------------〉
標(biāo)記軟中斷(__cpu_raise_softirq)---------------------〉中斷完成。
2.當(dāng)軟中斷被調(diào)用時(shí)(一共在三個(gè)地方調(diào)用),呼叫NET_RX_SOFTIRQ(其實(shí)就是net_rx_action()函數(shù))來處理網(wǎng)絡(luò)方面的軟中斷。-----------------〉
net_rx_action()根據(jù)數(shù)據(jù)包的協(xié)議類型在數(shù)組ptype_base[16]里找到相應(yīng)的協(xié)議,并從中知道了接收的處理函數(shù),然后把數(shù)據(jù)包交給處理函數(shù),這樣就交給了上層處理,實(shí)際調(diào)用處理函數(shù)是通過net_rx_action()里的pt_prev- func()這一句。例如如果數(shù)據(jù)包是IP協(xié)議的話,ptype_base[ETH_P_IP]- func()(ip_rcv()),這樣就把數(shù)據(jù)包交給了IP協(xié)議。根據(jù)包的類型,查找系統(tǒng)中注冊(cè)了的相應(yīng)的包處理函數(shù),對(duì)于ipv4的ip包,呼叫ip_rcv-------------〉
NF_IP_PRE_ROUTING--------------〉
ip_rcv_finish(進(jìn)行對(duì)此包的路由操作)---------------------〉
根據(jù)路由的結(jié)果,呼叫ip_local_deliver(給本機(jī)的包)/ip_forwardd(要轉(zhuǎn)發(fā)的包)/ip_error()(出現(xiàn)錯(cuò)誤的包)/ip_mr_input()(多播包的處理)ip_forward:要進(jìn)行轉(zhuǎn)發(fā)的包,check ttl, mtu, call NF_IP_FORWARDS,--------------〉
ip_forwmard_finish(check other ip options for forwardd!)-------------------〉
ip_send(如果需要分片,則調(diào)用ip_fragment,否則調(diào)用ip_finish_output()(call NF_IP_POST_ROUTING,然后時(shí)ip_finish_output2(填充鏈路層頭部到skb結(jié)構(gòu)中)--------------〉
hh- hh_output/dsr- neighbour- output
總結(jié)
以上是生活随笔為你收集整理的linux网络协议栈之数据包处理过程,Linux网络协议栈之数据包处理过程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux 命令提示符 时间,在LINU
- 下一篇: lua读取linux文件内容,使用lua