久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

Linux網(wǎng)絡(luò)中數(shù)據(jù)包的接收過程是怎樣的

共計(jì) 5091 個(gè)字符,預(yù)計(jì)需要花費(fèi) 13 分鐘才能閱讀完成。

本篇文章為大家展示了 Linux 網(wǎng)絡(luò)中數(shù)據(jù)包的接收過程是怎樣的,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。

下面將介紹在 Linux 系統(tǒng)中,數(shù)據(jù)包是如何一步一步從網(wǎng)卡傳到進(jìn)程手中的。

如果英文沒有問題,強(qiáng)烈建議閱讀后面參考里的兩篇文章,里面介紹的更詳細(xì)。

丸趣 TV 小編只討論以太網(wǎng)的物理網(wǎng)卡,不涉及虛擬設(shè)備,并且以一個(gè) UDP 包的接收過程作為示例.

示例里列出的函數(shù)調(diào)用關(guān)系來自于 kernel  3.13.0,如果你的內(nèi)核不是這個(gè)版本,函數(shù)名稱和相關(guān)路徑可能不一樣,但背后的原理應(yīng)該是一樣的(或者有細(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ò)模塊開始處理:

 +-----+ | | Memroy +--------+ 1 | | 2 DMA +--------+--------+--------+--------+ | Packet |-------- | NIC |------------ | Packet | Packet | Packet | ...... | +--------+ | | +--------+--------+--------+--------+ | | --------+ +-----+ | | +---------------+ | | 3 | Raise IRQ | Disable IRQ | 5 | | |  darr; | +-----+ +------------+ | | Run IRQ handler | | | CPU |------------------ | NIC Driver | | | 4 | | +-----+ +------------+ | 6 | Raise soft IRQ |  darr;

1:數(shù)據(jù)包從外面的網(wǎng)絡(luò)進(jìn)入物理網(wǎng)卡。如果目的地址不是該網(wǎng)卡,且該網(wǎng)卡沒有開啟混雜模式,該包會(huì)被網(wǎng)卡丟棄。

2:網(wǎng)卡將數(shù)據(jù)包通過 DMA 的方式寫入到指定的內(nèi)存地址,該地址由網(wǎng)卡驅(qū)動(dòng)分配并初始化。注:老的網(wǎng)卡可能不支持 DMA,不過新的網(wǎng)卡一般都支持。

3:網(wǎng)卡通過硬件中斷 (IRQ) 通知 CPU,告訴它有數(shù)據(jù)來了

4:CPU 根據(jù)中斷表,調(diào)用已經(jīng)注冊(cè)的中斷函數(shù),這個(gè)中斷函數(shù)會(huì)調(diào)到驅(qū)動(dòng)程序 (NIC Driver) 中相應(yīng)的函數(shù)

5:  驅(qū)動(dòng)先禁用網(wǎng)卡的中斷,表示驅(qū)動(dòng)程序已經(jīng)知道內(nèi)存中有數(shù)據(jù)了,告訴網(wǎng)卡下次再收到數(shù)據(jù)包直接寫內(nèi)存就可以了,不要再通知 CPU 了,這樣可以提高效率,避免 CPU 不停的被中斷。

6:  啟動(dòng)軟中斷。這步結(jié)束后,硬件中斷處理函數(shù)就結(jié)束返回了。由于硬中斷處理程序執(zhí)行的過程中不能被中斷,所以如果它執(zhí)行時(shí)間過長,會(huì)導(dǎo)致 CPU 沒法響應(yīng)其它硬件的中斷,于是內(nèi)核引入軟中斷,這樣可以將硬中斷處理函數(shù)中耗時(shí)的部分移到軟中斷處理函數(shù)里面來慢慢處理。

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

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

 +-----+ 14 | | +----------- | NIC | | | | |Enable IRQ +-----+ | | +------------+ Memroy | | Read +--------+--------+--------+--------+ +--------------- | NIC Driver | --------------------- | Packet | Packet | Packet | ...... | | | | 9 +--------+--------+--------+--------+ | +------------+ | | | skb Poll | 8 Raise softIRQ | 6 +-----------------+ | | 10 | |  darr;  darr; +---------------+ Call +-----------+ +------------------+ | net_rx_action | -------| ksoftirqd | | napi_gro_receive | +---------------+ 7 +-----------+ +------------------+ | | 11  darr; +--------------------------+ 12 +------------------------+ | __netif_receive_skb_core |----------- | packet taps(AF_PACKET) | +--------------------------+ +------------------------+ | | 13  darr; +-----------------+ | protocol layers | +-----------------+

7:  內(nèi)核中的 ksoftirqd 進(jìn)程專門負(fù)責(zé)軟中斷的處理,當(dāng)它收到軟中斷后,就會(huì)調(diào)用相應(yīng)軟中斷所對(duì)應(yīng)的處理函數(shù),對(duì)于上面第 6 步中是網(wǎng)卡驅(qū)動(dòng)模塊拋出的軟中斷,ksoftirqd 會(huì)調(diào)用網(wǎng)絡(luò)模塊的 net_rx_action 函數(shù)

8:net_rx_action 調(diào)用網(wǎng)卡驅(qū)動(dòng)里的 poll 函數(shù)來一個(gè)一個(gè)的處理數(shù)據(jù)包

9:在 pool 函數(shù)中,驅(qū)動(dòng)會(huì)一個(gè)接一個(gè)的讀取網(wǎng)卡寫到內(nèi)存中的數(shù)據(jù)包,內(nèi)存中數(shù)據(jù)包的格式只有驅(qū)動(dòng)知道

10:驅(qū)動(dòng)程序?qū)?nèi)存中的數(shù)據(jù)包轉(zhuǎn)換成內(nèi)核網(wǎng)絡(luò)模塊能識(shí)別的 skb 格式,然后調(diào)用 napi_gro_receive 函數(shù)

11: napi_gro_receive 會(huì)處理 GRO 相關(guān)的內(nèi)容,也就是將可以合并的數(shù)據(jù)包進(jìn)行合并,這樣就只需要調(diào)用一次協(xié)議棧,接著調(diào)用__netif_receive_skb_core

12:  看是不是有 AF_PACKET 類型的 socket(也就是我們常說的原始套接字),如果有的話,拷貝一份數(shù)據(jù)給它。tcpdump 抓包就是抓的這里的包。

13:調(diào)用協(xié)議棧相應(yīng)的函數(shù),將數(shù)據(jù)包交給協(xié)議棧處理。

14:待內(nèi)存中的所有數(shù)據(jù)包被處理完成后(即 poll 函數(shù)執(zhí)行完成),啟用網(wǎng)卡的硬中斷,這樣下次網(wǎng)卡再收到數(shù)據(jù)的時(shí)候就會(huì)通知 CPU

協(xié)議棧

IP 層

由于是 UDP 包,所以 *** 步會(huì)進(jìn)入 IP 層,然后一級(jí)一級(jí)的函數(shù)往下調(diào):

| |  darr; promiscuous mode   +--------+ PACKET_OTHERHOST (set by driver) +-----------------+ | ip_rcv |-------------------------------------- | drop this packet| +--------+ +-----------------+ | |  darr; +---------------------+ | NF_INET_PRE_ROUTING | +---------------------+ | |  darr; +---------+ | | enabled ip forword +------------+ +----------------+ | routing |-------------------- | ip_forward |------- | NF_INET_FOWARD | | | +------------+ +----------------+ +---------+ | | | | destination IP is local  darr;  darr; +---------------+ +------------------+ | dst_output_sk | | ip_local_deliver | +---------------+ +------------------+ | |  darr; +------------------+ | NF_INET_LOCAL_IN | +------------------+ | |  darr; +-----------+ | UDP layer | +-----------+

ip_rcv: ip_rcv 函數(shù)是 IP 模塊的入口函數(shù),在該函數(shù)里面,*** 件事就是將垃圾數(shù)據(jù)包 (目的 mac 地址不是當(dāng)前網(wǎng)卡,但由于網(wǎng)卡設(shè)置了混雜模式而被接收進(jìn)來) 直接丟掉,然后調(diào)用注冊(cè)在 NF_INET_PRE_ROUTING 上的函數(shù)

NF_INET_PRE_ROUTING: netfilter 放在協(xié)議棧中的鉤子,可以通過 iptables 來注入一些數(shù)據(jù)包處理函數(shù),用來修改或者丟棄數(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ù)

dst_output_sk:該函數(shù)會(huì)調(diào)用 IP 層的相應(yīng)函數(shù)將該數(shù)據(jù)包發(fā)送出去,同下一篇要介紹的數(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 層

 | |  darr; +---------+ +-----------------------+ | udp_rcv |----------- | __udp4_lib_lookup_skb | +---------+ +-----------------------+ | |  darr; +--------------------+ +-----------+ | sock_queue_rcv_skb |----- | sk_filter | +--------------------+ +-----------+ | |  darr; +------------------+ | __skb_queue_tail | +------------------+ | |  darr; +---------------+ | 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)用層程序來讀取,上面所有函數(shù)的執(zhí)行過程都在軟中斷的上下文中。

socket

應(yīng)用層一般有兩種方式接收數(shù)據(jù),一種是 recvfrom 函數(shù)阻塞在那里等著數(shù)據(jù)來,這種情況下當(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ù)包。

上述內(nèi)容就是 Linux 網(wǎng)絡(luò)中數(shù)據(jù)包的接收過程是怎樣的,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注丸趣 TV 行業(yè)資訊頻道。

正文完
 
丸趣
版權(quán)聲明:本站原創(chuàng)文章,由 丸趣 2023-08-04發(fā)表,共計(jì)5091字。
轉(zhuǎn)載說明:除特殊說明外本站除技術(shù)相關(guān)以外文章皆由網(wǎng)絡(luò)搜集發(fā)布,轉(zhuǎn)載請(qǐng)注明出處。
評(píng)論(沒有評(píng)論)
主站蜘蛛池模板: 广汉市| 长治县| 杨浦区| 眉山市| 北宁市| 博乐市| 板桥市| 昌吉市| 仲巴县| 昆明市| 韶关市| 临海市| 延安市| 凤山县| 常山县| 宁强县| 钟山县| 拉孜县| 车致| 九寨沟县| 东乡县| 义马市| 武山县| 延寿县| 永顺县| 广安市| 泽普县| 建昌县| 阿城市| 泸溪县| 驻马店市| 旬邑县| 尼玛县| 长阳| 重庆市| 遵义县| 栾川县| 扶余县| 阿鲁科尔沁旗| 年辖:市辖区| 乌恰县|