色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Linux發送HTTP網絡包圖像 sk_buff數據結構解析

xCb1_yikoulinux ? 來源:掘金開發者社區 ? 作者:CVNot ? 2022-05-10 12:14 ? 次閱讀

如果你對Linux是如何實現 對用戶原始的網絡包進行協議頭封裝與解析,為什么會粘包拆包,期間網絡包經歷了哪些緩沖區、經歷了幾次拷貝(CPUDMA),TCP又是如何實現滑動/擁塞窗口 這幾個話題感興趣的話,不妨看下去吧。

1. Linux發送HTTP網絡包圖像

e8dcfdb8-d015-11ec-bce3-dac502259ad0.png

圖像解析

寫入套接字緩沖區(添加TcpHeader)

用戶態進程通過write()系統調用切到內核態用戶進程緩沖區中的HTTP報文數據通過Tcp Process處理程序為HTTP報文添加TcpHeader,并進行CPU copy寫入套接字發送緩沖區,每個套接字會分別對應一個Send-Q(發送緩沖區隊列)、Recv-Q(接收緩沖區隊列),可以通過ss -nt語句獲取當前的套接字緩沖區的狀態;

# ss -nt
State   Recv-Q   Send-Q         Local Address:Port              Peer Address:Port
ESTAB   0        0              192.168.183.130:52454           192.168.183.130:14465
State   Recv-Q   Send-Q         Local Address:Port              Peer Address:Port
ESTAB   0        1024           192.168.183.130:52454           192.168.183.130:14465
State   Recv-Q   Send-Q         Local Address:Port              Peer Address:Port
ESTAB   0        2048           192.168.183.130:52454           192.168.183.130:14465
......
State   Recv-Q   Send-Q         Local Address:Port              Peer Address:Port
ESTAB   0        13312          192.168.183.130:52454           192.168.183.130:14465
State   Recv-Q   Send-Q         Local Address:Port              Peer Address:Port
ESTAB   0        14336          192.168.183.130:52454           192.168.183.130:14465
State   Recv-Q   Send-Q         Local Address:Port              Peer Address:Port
ESTAB   0        14480          192.168.183.130:52454           192.168.183.130:14465

套接字緩沖區發送隊列由一個個struct sk_buff 結構體的鏈表組成,其中一個sk_buff數據結構對應一個網絡包;這個結構體后面會詳細講,是Linux實現網絡協議棧的核心數據結構。

IP層

接著對TCP包在IP Layer層進行網絡包IpHeader的組裝,并經由QDisc(排隊規則)進行轉發;

數據鏈路層/物理層

接著網卡設備通過DMA Engine將內存中RingBufferTx.ring塊中的IP包(sk_buff)copy到網卡自身的內存中,并生成CRC等校驗數據形成數據鏈路包頭部并進行網絡傳輸。

2. sk_buff數據結構解析

通過對sk_buff數據結構解析的過程中,我們回答文章頭部的幾個問題,以及窺見Linux中的一些設計思想;

e8f467b4-d015-11ec-bce3-dac502259ad0.png

進行協議頭的增添

我們知道,按照網絡棧的設定,發送網絡包時,每經過一層,都會增加對應協議層的協議首部,因此Linux采用在sk_buff中的一個Union結構體進行標識:

struct sk_buff {
    union {
           struct tcphdr*th; // TCP Header
           struct udphdr*uh; // UDP Header
           struct icmphdr*icmph; // ICMP Header
           struct igmphdr*igmph; 
           struct iphdr*ipiph; // IPv4 Header
           struct ipv6hdr*ipv6h; // IPv6 Header
           unsigned char*raw; // MAC Header
    } h;
}

結構體中存儲的是指向內存中各種協議的首部地址的指針,而在發送數據包的過程中,sk_buff中的data指針指向最外層的協議頭;

網絡包的大小占用

考慮一個包含2bytes的網絡包,需要包括 預留頭(64 bytes) + Mac頭(14bytes) + IP頭(20bytes) + Tcp頭(32bytes) + 有效負載為2bytes(len) + skb_shared_info(320bytes) = 452bytes,向上取整后為512bytes;sk_buff這個存儲結構占用256bytes;則一個2bytes的網絡包需要占用512+256=768bytes(truesize)的內存空間;

因此當發送這個網絡包時:

  • Case1:不存在緩沖區積壓,則新建一個sk_buff進行網絡包的發送;

skb->truesize = 768 
skb->datalen  = 0 skb_shared_info 結構有效負載 (非線性區域)
skb->len      = 2 有效負載 (線性區域 + 非線性區域(datalen),這里暫時不考慮協議頭部)
  • Case2:如果緩沖區積壓(存在未被ACK的已經發送的網絡包-即SEND-Q中存在sk_buff結構),Linux會嘗試將當前包合并到SEND-Q的最后一個sk_buff結構中(粘包);考慮我們上述的768bytes的結構體為SEND-Q的最后一個sk_buff,當用戶進程繼續調用write系統調用寫入2kb的數據時,前一個數據包還未達到MSS/MTU的限制、整個緩沖區的大小未達到SO_SENDBUF指定的限制,會進行包的合并,packet data = 2 + 2,頭部的相關信息都可以進行復用,因為套接字緩沖區與套接字是一一對應的;

tail_skb->truesize = 768 
tail_skb->datalen  = 0
tail_skb->len      = 4 (2 + 2)

發送窗口

我們在創建套接字的時候,通過SO_SENDBUF指定了發送緩沖區的大小,如果設置了大小為2048KB,則Linux在真實創建的時候會設置大小2048*2=4096,因為linux除了要考慮用戶的應用層數據,還需要考慮linux自身數據結構的開銷-協議頭部、指針、非線性內存區域結構等...

sk_buff結構中通過sk_wmem_queued標識發送緩沖區已經使用的內存大小,并在發包時檢查當前緩沖區大小是否小于SO_SENDBUF指定的大小,如果不滿足則阻塞當前線程,進行睡眠,等待發送窗口中有包被ACK后觸發內存free的回調函數喚醒后繼續嘗試發送;

接收窗口(擁塞窗口)

    |<---------- RCV.BUFF ---------------->|
          1             2            3
    |<-RCV.USER->|<--- RCV.WND ---->|
----|------------|------------------|------|----
              RCV.NXT

接收窗口主要分為3部分:

  • RCV.USER 為積壓的已經收到但尚未被用戶進程通過read等系統調用獲取的網絡數據包;當用戶進程獲取后窗口的左端會向右移動,并觸發回調函數將該數據包的內存free掉;

  • RCV.WND 為未使用的,推薦返回給該套接字的客戶端發送方當前剩余的可發送的bytes數,即擁塞窗口的大小;

  • 第三部分為未使用的,尚未預先內存分配的,并不計算在擁塞窗口的大小中;

進入網卡驅動層

NIC (network interface card)在系統啟動過程中會向系統注冊自己的各種信息,系統會分配RingBuffer隊列及一塊專門的內核內存區用于存放傳輸上來的數據包。每個 NIC 對應一個R x.ring 和一個 Tx.ring。一個 RingBuffer 上同一個時刻只有一個 CPU 處理數據。

每個網絡包對應的網卡存儲在sk_buff結構的dev_input中;

RingBuffer隊列內存放的是一個個描述符(Descriptor),其有兩種狀態:ready 和 used。

  • 初始時 Descriptor 是空的,指向一個空的 sk_buff,處在 ready 狀態。

  • 網卡收到網絡包:當NIC有網絡數據包傳入時,DMA負責從NIC取數據,并在Rx.ring上按順序找到下一個ready的Descriptor,將數據存入該 Descriptor指向的sk_buff中,并標記槽為used。

  • 網卡發送網絡包:當sk_buff已經在內核空間被寫入完成時,網卡的DMA Engine檢測Tx.ring有數據包完成時,觸發DMA Copy將數據傳輸到網卡內存中,并封裝MAC幀。

不同的網絡包發送函數有幾次拷貝?

read then write

常見的場景中,當我們要在網絡中發送一個文件,那么首先需要通過read系統調用陷入內核態讀取 PageCache 通過CPU Copy數據頁到用戶態內存中,接著將數據頁封裝成對應的應用層協議報文,并通過write系統調用陷入內核態將應用層報文CPU Copy到套接字緩沖區中,經過TCP/IP處理后形成IP包,最后通過網卡的DMA EngineRingBuffer Tx.ring中的sk_buff進行DMA Copy到網卡的內存中,并將IP包封裝為幀并對外發送。

PS:如果PageCache中不存在對應的數據頁緩存,則需要通過磁盤DMA Copy到內存中。

因此read then write需要兩次系統調用(4次上下文切換,因為系統調用需要將用戶態線程切換到內核態線程進行執行),兩次CPU Copy、兩次DMA Copy

sendFile

用戶線程調用sendFile系統調用陷入內核態,sendFile無需拷貝PageCache中的數據頁到用戶態內存中中,而是通過內核線程將 PageCache 中的數據頁直接通過CPU Copy拷貝到套接字緩沖區中,再經由相同的步驟經過一次網卡DMA對外傳輸。

因此sendFile需要一次系統調用,一次CPU Copy

相比于write,sendFile少了一次PageCache拷貝到內存的開銷,但是需要限制在網絡傳輸的是文件頁,而不是用戶緩沖區中的匿名頁,并且因為完全在內核態進行數據copy,因此無法添加用戶態的協議數據

Kafka因為基于操作系統文件系統進行數據存儲,并且文件量比較大,因此比較適合通過sendFile進行網絡傳輸的實現;

但是sendFile仍然需要一次內核線程的CPU Copy,因此零拷貝更偏向于無需拷貝用戶態空間中的數據。

mmap + write

相比于sendFile直接在內核態進行文件傳輸,mmap則是通過在進程的虛擬地址空間中映射PageCache,再經過write進行網絡寫入;比較適用于小文件的傳輸,因為mmap并沒有立即將數據拷貝到用戶態空間中,所以較大文件會導致頻繁觸發虛擬內存的 page fault 缺頁異常;

RocketMQ 選擇了 mmap+write 這種零拷貝方式,適用于消息這種小塊文件的數據持久化和傳輸。

原文標題:Linux中一個網絡包的發送/接收流程

文章出處:【微信公眾號:一口Linux】歡迎添加關注!文章轉載請注明出處。

審核編輯:湯梓紅
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Linux
    +關注

    關注

    87

    文章

    11292

    瀏覽量

    209332
  • HTTP
    +關注

    關注

    0

    文章

    504

    瀏覽量

    31197
  • 數據結構
    +關注

    關注

    3

    文章

    573

    瀏覽量

    40123

原文標題:Linux中一個網絡包的發送/接收流程

文章出處:【微信號:yikoulinux,微信公眾號:一口Linux】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    深度解析Linux網絡路徑及sk_buff struct 數據結構

    的 Segmentation Offloading 技術(接收端) 1. Linux 網絡路徑 1.1 發送端 1.1.1 應用層 (1) Socket 應用層的各種網絡應用程序基本上
    的頭像 發表于 10-22 15:04 ?5605次閱讀
    深度<b class='flag-5'>解析</b><b class='flag-5'>Linux</b><b class='flag-5'>網絡</b>路徑及<b class='flag-5'>sk_buff</b> struct <b class='flag-5'>數據結構</b>

    Linux sk_buff四大指針與相關操作

     在以上文章中,沒有分析過Linux內核網絡關鍵的數據結構-套接字數據緩存struct sk_buff,本文將第一次分享到
    發表于 10-13 17:23 ?4627次閱讀
    <b class='flag-5'>Linux</b> <b class='flag-5'>sk_buff</b>四大指針與相關操作

    嵌入式linux TCP/IP協議棧概述

    包包頭。3. 數據傳輸數據的傳輸使用了一個非常重要的結構sk_buff,該結構體用來實現數據
    發表于 12-07 10:05

    千兆網絡接口在S3C2440A系統中的應用方案

    AX88180分配一塊64K的地址空間, 其中8000H-FBFFH作為發送緩沖區地址,在系統調用驅動程序的xmit時,發送數據放在一個sk_buff
    發表于 04-17 07:00

    Linux網絡設備驅動程序

    當要發送數據包的時候,內核必須建立一個包含傳輸數據sk_buff,然后將sk_buff交給下層,各層在
    發表于 05-10 11:15 ?1824次閱讀

    Linux 內核數據結構:位圖(Bitmap)

    除了各種鏈式和樹形數據結構Linux內核還提供了位圖接口。位圖在Linux內核中大量使用。下面的源代碼文件包含這些結構的通用接口。
    發表于 05-14 17:24 ?3475次閱讀

    Linux0.11-進程控制塊數據結構

    嵌入式Linux中文站收集整理Linux0.11版本內核學習筆記,本文分析了Linux進程控制模塊的數據結構
    發表于 05-15 15:22 ?972次閱讀

    網卡的Ring Buffer詳解

    DMA 將 NIC 接收的數據包逐個寫入 sk_buff ,一個數據包可能占用多個 sk_buff , sk_buff 讀寫順序遵循FIFO
    的頭像 發表于 03-17 14:25 ?1526次閱讀

    Linux內核的鏈表數據結構

    Linux內核實現了自己的鏈表數據結構,它的設計與傳統的方式不同,非常巧妙也很通用。
    的頭像 發表于 03-24 11:34 ?832次閱讀
    <b class='flag-5'>Linux</b>內核的鏈表<b class='flag-5'>數據結構</b>

    網卡的Ring Buffer詳解

    DMA 將 NIC 接收的數據包逐個寫入 sk_buff ,一個數據包可能占用多個 sk_buff , sk_buff 讀寫順序遵循FIFO
    的頭像 發表于 04-04 09:15 ?1154次閱讀

    Linux如何操作將數據包發送出去

    ? Linux 服務器收到網絡數據包,需要經過哪些處理,一步步將數據傳給應用進程的呢?應用進程發送數據包
    的頭像 發表于 06-17 16:00 ?1040次閱讀
    <b class='flag-5'>Linux</b>如何操作將<b class='flag-5'>數據包</b><b class='flag-5'>發送</b>出去

    多CPU下的Ring Buffer處理

    1. 網卡處理數據包流程 一圖勝千言,先來看看網卡處理網絡數據流程圖: 圖片來自參考鏈接1 上圖中虛線步驟的解釋: 1 DMA 將 NIC 接收的數據包逐個寫入
    的頭像 發表于 06-22 10:13 ?865次閱讀
    多CPU下的Ring Buffer處理

    sk_buff內存空間布局情況與相關操作(一)

    套接字數據緩存(socket buffer)在Linux內核中表示為:struct sk_buff,是Linux內核中數據包管理的基本單元,
    的頭像 發表于 07-30 16:43 ?1202次閱讀
    <b class='flag-5'>sk_buff</b>內存空間布局情況與相關操作(一)

    sk_buff內存空間布局情況與相關操作(三)

    2、非線性區域 在1、中,可以看到每張sk_buff的圖: 在end指針緊挨著一個非線性區域 ; 在struct sk_buff中沒有指向skb_shared_info結構的指針,利用end指針
    的頭像 發表于 07-30 16:48 ?1164次閱讀
    <b class='flag-5'>sk_buff</b>內存空間布局情況與相關操作(三)

    Linux內核中使用的數據結構

    Linux內核代碼中廣泛使用了數據結構和算法,其中最常用的兩個是鏈表和紅黑樹。 鏈表 Linux內核代碼大量使用了鏈表這種數據結構。鏈表是在解決數組不能動態擴展這個缺陷而產生的一種
    的頭像 發表于 11-09 14:24 ?485次閱讀
    <b class='flag-5'>Linux</b>內核中使用的<b class='flag-5'>數據結構</b>
    主站蜘蛛池模板: 国产精品免费小视频| 精品国产品国语在线不卡| 好男人好资源在线播放| 麻豆免费观看高清完整视频在线| 亲伦在线观看| 亚洲午夜精品AV无码少妇| 99re6在线视频国产精品欧美| 国产99视频精品免费播放| 黄色网址在线看| 人体内射精一区二区三区| 亚洲视频中文字幕| 大香伊人久久精品一区二区| 精品无码三级在线观看视频| 日本无翼恶漫画大全优优漫画| 野花香在线观看免费高清播放视频| xxx性欧美在线| 精品一区二区三区在线成人| 日操夜操天天操| 在线电台收听| 国产午夜人做人免费视频中文| 明星三级电影| 亚洲伊人精品综合在合线| 东京热百度影音| 麻豆国产99在线中文| 亚洲精品m在线观看| 超碰97av 在线人人操| 久久无码AV亚洲精品色午夜| 忘忧草研究所 麻豆| caoporon超碰在线视频| 久久国产精品高清一区二区三区| 色狼亚洲色图| 99久久精品免费看国产一区二区| 红尘影院在线观看| 视频一区国产第一页| chinese极品嫩模videos| 久久99国产精品自在自在| 无码人妻丰满熟妇区五十路久久| 99久在线国内在线播放免费观看 | 俄罗斯9一14 young处| 鲁一鲁亚洲无线码| 亚洲幼女网|