⑴ linux網路 - 數據包在內核中接收和發送的過程(轉)
本文將介紹在Linux系統中, 數據包是如何一步一步從網卡傳到進程手中的 以及 數據包是如何一步一步從應用程序到網卡並最終發送出去的 。
如果英文沒有問題,強烈建議閱讀後面參考里的文章,裡面介紹的更詳細。
本文只討論乙太網的物理網卡,不涉及虛擬設備,並且以一個UDP包的接收過程作為示例.
網卡需要有驅動才能工作,驅動是載入到內核中的模塊,負責銜接網卡和內核的網路模塊,驅動在載入的時候將自己注冊進網路模塊,當相應的網卡收到數據包時,網路模塊會調用相應的驅動程序處理數據。
下圖展示了數據包(packet)如何進入內存,並被內核的網路模塊開始處理:
軟中斷會觸發內核網路模塊中的軟中斷處理函數,後續流程如下
由於是UDP包,所以第一步會進入IP層,然後一級一級的函數往下調:
應用層一般有兩種方式接收數據,一種是recvfrom函數阻塞在那裡等著數據來,這種情況下當socket收到通知後,recvfrom就會被喚醒,然後讀取接收隊列的數據;另一種是通過epoll或者select監聽相應的socket,當收到通知後,再調用recvfrom函數去讀取接收隊列的數據。兩種情況都能正常的接收到相應的數據包。
了解數據包的接收流程有助於幫助我們搞清楚我們可以在哪些地方監控和修改數據包,哪些情況下數據包可能被丟棄,為我們處理網路問題提供了一些參考,同時了解netfilter中相應鉤子的位置,對於了解iptables的用法有一定的幫助,同時也會幫助我們後續更好的理解Linux下的網路虛擬設備。
ndo_start_xmit會綁定到具體網卡驅動的相應函數,到這步之後,就歸網卡驅動管了,不同的網卡驅動有不同的處理方式,這里不做詳細介紹,其大概流程如下:
在網卡驅動發送數據包過程中,會有一些地方需要和netdevice子系統打交道,比如網卡的隊列滿了,需要告訴上層不要再發了,等隊列有空閑的時候,再通知上層接著發數據。
⑵ Linux網路協議棧7--ipsec收發包流程
流程路徑:ip_rcv() --> ip_rcv_finish() --> ip_local_deliver() --> ip_local_deliver_finish()
解封側一定是ip報文的目的端,ip_rcv_finish中查到的路由肯定是本機路由(RTCF_LOCAL),調用 ip_local_deliver 處理。
下面是貼的網上的一張圖片。
ip_local_deliver_finish中 根據上次協議類型,調用對應的處理函數。inet_protos 中掛載了各類協議的操作集,對於AH或者ESP來說,是xfrm4_rcv,對於ipsec nat-t情況下,是udp協議的處理函數udp_rcv,內部才是封裝的ipsec報文(AH或者ESP)。
xfrm4_rcv --> xfrm4_rcv_spi --> xfrm4_rcv_encap --> xfrm_input
最終調用 xfrm_input 做收包解封裝流程。
1、創建SKB的安全路徑;
2、解析報文,獲取daddr、spi,加上協議類型(esp、ah等),就可以查詢到SA了,這些是SA的key,下面列出了一組linux ipsec的state(sa)和policy,方便一眼就能看到關鍵信息;
3、調用SA對應協議類型的input函數,解包,並返回更上層的協議類型,type可為esp,ah,ipcomp等。對應的處理函數esp_input、ah_input等;
4、解碼完成後,再根據ipsec的模式做解封處理,常用的有隧道模式和傳輸模式。對應xfrm4_mode_tunnel_input 和 xfrm4_transport_inout,處理都比較簡單,隧道模式去掉外層頭,傳輸模式只是設置一些skb的數據。
5、協議類型可以多層封裝,如ESP+AH,所以需要再次解析內存協議,如果還是AH、ESP、COMP,則解析新的spi,返回2,查詢新的SA處理報文。
6、經過上面流程處理,漏出了用戶數據報文(IP報文),根據ipsec模式:
流程路徑如下圖,這里以轉發流程為例,本機發送的包主要流程類似。
轉發流程:
ip_forward 函數中調用xfrm4_route_forward,這個函數:
1、解析用戶報文,查找對應的Ipsec policy(__xfrm_policy_lookup);
2、再根據policy的模版tmpl查找對應最優的SA(xfrm_tmpl_resolve),模版的內容以及和SA的對應關系見上面貼出的ip xfrm命令顯示;
3、最後根據SA生成安全路由,掛載再skb的dst上; 一條用戶流可以聲明多個安全策略(policy),所以會對應多個SA,每個SA處理會生成一個安全路由項struct dst_entry結構(xfrm_resolve_and_create_bundle),這些安全路由項通過 child 指針鏈接為一個鏈表,其成員 output掛載了不同安全協議的處理函數,這樣就可以對數據包進行連續的處理,比如先壓縮,再ESP封裝,再AH封裝。
安全路由鏈的最後一個路由項一定是普通IP路由項,因為最終報文都得走普通路由轉發出去,如果是隧道模式,在tunnel output封裝完完成ip頭後還會再查一次路由掛載到安全路由鏈的最後一個。
註: SA安全聯盟是IPsec的基礎,也是IPsec的本質。 SA是通信對等體間對某些要素的約定,例如使用哪種協議、協議的操作模式、加密演算法、特定流中保護數據的共享密鑰以及SA的生存周期等。
然後,經過FORWARD點後,調用ip_forward_finish()-->dst_output,最終調用skb_dst(skb)->output(skb),此時掛載的xfrm4_output
本機發送流程簡單記錄一下,和轉發流程殊途同歸:
查詢安全路由: ip_queue_xmit --> ip_route_output_flow --> __xfrm_lookup
封裝發送: ip_queue_xmit --> ip_local_out --> dst_output --> xfrm4_output
註:
1). 無論轉發還是本地發送,在查詢安全路由之前都會查一次普通路由,如果查不到,報文丟棄,但這條路由不一定需要指向真實的下一跳的出介面,只要能匹配到報文DIP即可,如配置一跳其它介面的defualt。
2). strongswan是一款用的比較多的ipsec開源軟體,協商完成後可以看到其創建了220 table,經常有人問裡面的路由有啥用、為什麼有時有有時無。這里做個測試記錄: 1、220中貌似只有在tunnel模式且感興趣流是本機發起(本機配置感興趣流IP地址)的時候才會配置感興趣流相關的路由,路由指定了source;2、不配置也沒有關系,如1)中所說,只要存在感興趣流的路由即可,只不過ping的時候需要指定source,否者可能匹配不到感興趣流。所以感覺220這個表一是為了保證
ipsec封裝發送流程:
xfrm4_output-->xfrm4_output_finish-->xfrm_output-->xfrm_output2-->xfrm_output_resume-->xfrm_output_one
xfrm4_output 函數先過POSTROUTING點,在封裝之前可以先做SNAT。後面則調用xfrm_output_resume-->xfrm_output_one 做IPSEC封裝最終走普通路由走IP發送。
貼一些網上的幾張數據結構圖
1、安全路由
2、策略相關協議處理結構
3、狀態相關協議處理結構
⑶ Linux網路設備驅動完成數據包發送的流程
從網路設備驅動程序的結構分析可知,Linux網路子系統在發送數據包時,會調用驅動程序提供的hard_start_transmit()函數,該函數用於啟動數據包的發送。在設備初始化的時候,這個函數指針需被初始化以指向設備的xxx_tx ()函數。網路設備驅動完成數據包發送的流程如下:1)網路設備驅動程序從上層協議傳遞過來的sk_buff參數獲得數據包的有效數據和長度,將有效數據放入臨時緩沖區。2)對於乙太網,如果有效數據的長度小於乙太網沖突檢測所要求數據幀的最小長度ETH ZLEN,則給臨時緩沖區的末尾填充0。3)設置硬體的寄存器,驅使網路設備進行數據發送操作。特別要強調對netif_ stop_queue()的調用,當發送隊列為滿或因其他原因來不及發送當前上層傳下來的數據包時,則調用此函數阻止上層繼續向網路設備驅動傳遞數據包。當忙於發送的數據包被發送完成後,在以TX結束的中斷處理中,應該調用netif_wake_queue ()喚醒被阻塞的上層,以啟動它繼續向網路設備驅動傳送數據包。當數據傳輸超時時,意味著當前的發送操作失敗或硬體已陷入未知狀態,此時,數據包發送超時處理函數xxx _tx _timeout ()將被調用。這個函數也需要調用由Linux內核提供的netif_wake _queue()函數以重新啟動設備發送隊列。
⑷ 1、Linux系統基本網路
1.1、伺服器注意事項:
遠程伺服器不允許關機,只能重啟
重啟時應該先關閉服務
不要在伺服器訪問高峰運行高負載命令
遠程配置防火牆時不要把自己踢出伺服器
指定合理的密碼規范並定期更新
合理分配許可權
定期備份重要的數據和日誌
1.2、設置高段網路橋接命令和(ANT模式):
systemctl restart network------重啟網卡service network restart---------重啟網路服務
systemctl stop NetworkManager 臨時暫停網路管理器systemctl disable NetworkManager 永久關閉網路管理器
systemctl start NetworkManager 擁有root用戶的可執行許可權
systemctl stop NetworkManager 停止並禁用虛擬機 NetworkManager 服務
systemctl disable NetworkManager
注意:修改網路配置文件後,需要重新載入網路連接,如果是通過network.service則使用命令:systemctl restart network;如果是通慎扮過NetworkManager.service則使用nmcli命令:nmcli connection reload。
設置網路主要操作(橋接模式和)
[root@localhost network-scripts]# cd etc/sysconfig/network-scripts/
[root@localhost network-scripts]# vi ifcfg-ens33
systemctl restart network------重啟網卡
service network restart---------重啟網路服務
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=static 設置靜態
DEFROUTE=yes
IPV4_FAILURE_FATAL=yes
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=ens33
UUID=10c17057-e9c9-4831-a8ff-0757ea0abc0b
DEVICE=ens33
ONBOOT=yes #開機重啟
#IPADDR=192.168.43.168 #ip地址(需要跟主機同一個網段,不同一個IP)
IPADDR=10.63.73.20
#NETMASK=255.255.255.0 #子網掩碼(下面三個都跟主機一樣)
NETMASK=255.255.0.0
#GATEWAY=192.168.43.1 #網關
GATEWAK=10.63.255.254
#GATEWAK=10.200.0.3
#DNS1=192.168.43.1 #DNS服務
設置網路主要步驟(Nat模式)
1.打開Vm,點擊編輯->虛擬網路編輯
2.選擇VMnet8,將VMnet信息改為NAT模式,比如我的本機IP是192.168.138.1,子網掩碼為255.255.255.0
所以我戚孝譽將下面的IP配置成192.168.138.0 子網掩碼配置成255.255.255
3.再點擊NAT設置將網管配置成192.168.138.2(ps:網關不要配置成和自己IP地址一樣)
然後再應用確定
4.右鍵點擊虛擬機設置,網路適配器改為自定義(選擇Vmnet8 Nat模式)
5.接下來開啟虛擬機
cd /etc/sysconfig/network-scripts/
vim ifcfg-eno16777736
然後再執行命令:/etc/init.d/network restart
1.3、設置防火牆
1.3.1、防火牆命令
停止防火牆:
systemctl stop firewalld.service #停止firewall 防火牆
service iptables stop #(centos7版本之前)
永久關閉防火牆:
systemctl disable firewalld.service #禁止firewall開機啟動 ( 永久關閉防火牆 )
chkconfig iptables off #永久關閉防火牆
開始防火牆:
systemctl start firewalld #啟動防火牆
systemctl restart iptables.service #重啟防火牆使配置生效
systemctl enable iptables.service #設置防火牆開機啟動(重啟)
查看防火牆狀態:
systemctl status firewalld
service iptables status # (7版本之前)
1.3.2、Linux chkconfig 命令
Linux chkconfig 命令用於檢查,設置系統的各種服務。
這是Red Hat公司遵循GPL規則所開發的程序,它可查詢操作系統在每一個執行等級中會執行哪些系統服務,其中包括各類常駐服務。
語法
chkconfig [--add][--del][--list][系統服務] 或 chkconfig [--level <等級代號>][系統服務][on/off/reset]
參數 :
--add 增加所指定的系統服務,讓 chkconfig 指令得以管理它,並同時在系統啟動的敘述文件內增加相關數據。
--del 刪除所指定的系統服務,不再由 chkconfig 指令管理,並同時在系統啟動的敘述文件內刪除相關數據。
--level<等級代號> 指定讀系統服務要在哪一個執行等級中開啟或關畢。
實例
列出chkconfig 所知道的所有命令,可以用chkconfig –list查看所有的服務及其在每個級別的開啟狀態。
# chkconfig --list
開啟服務
# chkconfig telnet on //開啟 Telnet 服務
# chkconfig --list //列出 chkconfig 所知道的所有的服務的情況
關閉服務
# chkconfig telnet off // 關閉 Telnet 服務
# chkconfig --list // 列出 chkconfig 所知道的所有的服務的情況
[root@cent01 sbin]# chkconfig --level 3 network off //關閉3級別的network服務
[root@cent01 sbin]# chkconfig --list //3級別已關閉
network 0:關 1:關 2:開 3:關 4:開 5:開 6:關
[root@cent01 sbin]# chkconfig network on //不輸入級別,默認打開2,3,4,5級別
[root@cent01 sbin]# chkconfig --list
network 0:關 1:關 2:開 3:開 4:開 5:開 6:關
[root@cent01 sbin]# chkconfig --del network //刪除network
[root@cent01 sbin]# chkconfig --list //network已消失
mysqld 0:關 1:關 2:開 3:開 4:開 5:開 6:關
netconsole 0:關 1:關 2:關 3:關 4:關 5:關 6:關
[root@cent01 sbin]# chkconfig --add network //增加network服務
[root@cent01 sbin]# chkconfig --list //network服務又恢復了
mysqld 0:關 1:關 2:開 3:開 4:開 5:開 6:關
netconsole 0:關 1:關 2:關 3:關 4:關 5:關 6:關
network 0:關 1:關 2:開 3:開 4:開 5:開 6:關
[root@cent01 sbin]# chkconfig --list
註:該輸出結果只顯示 SysV 服務,並不包含
原生 systemd 服務。SysV 配置數據
可能被原生 systemd 配置覆蓋。
要列出 systemd 服務,請執行 'systemctl list-unit-files'。
查看在具體 target 啟用的服務請執行
'systemctl list-dependencies [target]'。
mysqld 0:關 1:關 2:開 3:開 4:開 5:開 6:關
netconsole 0:關 1:關 2:關 3:關 4:關 5:關 6:關
注意: 但是這里只顯示了SysV管理的服務,centos6及之前都是這個管理工具,但是在centos7用的是systemd管理,所以systemd管理的服務在這里沒有顯示出來。
運行級別為系統啟動級別,具體含義如下:
0 shutdown關機
1 單用戶模式
2 無NFS支持的多用戶模式
3 完全多用戶模式,常用的命令行模式
4 保留給用戶自定義
5 圖形界面登錄,比3多了一個圖形界面
6 重啟
1.3.2、Linux systemd命令
systemd是管理開機啟動程序的工具(SysV啟動開機進程時一次只能啟動一個,而systemd則一次可以啟動多個服務,這樣就導致systemd的開機速度會更快。)
[root@localhost ~]# systemctl list-units --all --type=service
UNIT LOAD ACTIVE SUB DESCRIPTION
abrt-ccpp.service loaded active exited Install ABRT coremp hook
abrt-oops.service loaded active running ABRT kernel log watcher
UNIT LOAD ACTIVE SUB DESCRIPTION
[root@cent01 sbin]# ls /usr/lib/systemd/system //啟動的腳本文件目錄
[root@cent01 ~]# systemctl list-units //列出正在運行的unit
[root@cent01 ~]# systemctl list-units --all //列出所有的unit,包括active和inactive
[root@cent01 ~]# systemctl list-units --all --state=inactive //列出inactive的unit
[root@cent01 ~]# systemctl list-units --all --type=service //列出所有狀態的service
[root@cent01 ~]# systemctl list-units --type=service //列出active的service
[root@cent01 ~]# systemctl is-active crond.service //查看某個unit是否active
systemctl enable crond.service //開機啟動 .service可以省略
systemctl disable crond.service //禁止開機啟動
systemctl status crond.service //查看服務狀態
systemctl start crond.service //啟動服務
systemctl stop crond.service //停止服務
systemctl restart crond.service //重啟服務
systemctl is-enabled crond.service //查看某個服務是否開機啟動
unit /usr/lib/systemd/system 此目錄下列出了很多文件,這些文件都是unit。類別如下:
service 系統服務target 多個unit組成的組device 硬體設備mount 文件系統掛載點automount 自動掛載點path 文件或路徑scope 不是由systemd啟動的外部進程slice 進程組snapshot systemd快照socket 進程間通信的套接字swap swap文件timer 定時器
target target類似於centos6的啟動級別,target內又包含多個unit的組合,當然target內也可以包含target。啟動target就是啟動多個unit,用target來管理這些unit。
[root@cent01 ~]# systemctl list-units --all --type=target //查看當前所有的target
UNIT LOAD ACTIVE SUB DESCRIPTION
basic.target loaded active active Basic System
bluetooth.target loaded active active Bluetooth
[root@localhost ~]# systemctl list-dependencies multi-user.target
multi-user.target
● ├─abrt-ccpp.service
● ├─abrt-oops.service
● ├─abrt-vmcore.service
● ├─abrt-xorg.service
[root@localhost ~]# systemctl get-default //查看系統默認的target
multi-user.target
systemctl set-default multi-user.target //設置默認的target
multi-user.target等同於centos6的運行級別3。他們的對應關系如下:
SysV運行級別systemd target備注
0poweroff.target關閉系統
1rescure.target單用戶模式
2multiuser.target用戶自定義級別,通常識別為3
3multiuser.target多用戶命令行模式
4multiuser.target用戶自定義級別,通常識別為3
5graphical.target多用戶圖形界面,比級別3隻多一個GUI
6reboot.target重啟
所以總結起來,一個service屬於一種unit,多個unit組成一個target,當然target裡面也可以包含target。
1.4、ifconfig命令配置IP網路參數
格式:
[root@localhost /]#ifconfig [網路設備] [ip地址] [MAC地址] [netmask掩碼地址] [broadcast廣播地址(NDC)] [up/down]
[root@localhost /]#ifconfig eth0 192.168.74.130 netmask 255.255.255.0 up
用ifconfig命令配置eth0別名設備,為eth0綁定多個IP地址。
[root@localhost /]#ifconfig eth0:1 192.168.74.130
[root@localhost /]#ifconfig eth0:2 192.168.73.130
1.5、使用routedel命令添加路由
格式:
[root@localhost /]#routedel [-net|host] [網域或主機] netmask [mask] [gw]
[root@localhost /]#route #查看路由信息
功能:添加路由
-net : 表示後面接的路由為一個網路。
-host : 表示後面接的為連接到單部主機的路由。
netmask : 與網路有關,可以設定netmask決定網路的大小。
gw : gateway (網關)的簡寫,後面接的是ip地址。
1.6、使用hostname命令修改主機名稱
[root@localhost /]#hostname service.jw.com
hostnamectl set-hostname nod1
⑸ 關於 Linux 網路,你必須知道這些
我們一起學習了文件系統和磁碟 I/O 的工作原理,以及相應的性能分析和優化方法。接下來,我們將進入下一個重要模塊—— Linux 的網路子系統。
由於網路處理的流程最復雜,跟我們前面講到的進程調度、中斷處理、內存管理以及 I/O 等都密不可分,所以,我把網路模塊作為最後一個資源模塊來講解。
同 CPU、內存以及 I/O 一樣,網路也是 Linux 系統最核心的功能。網路是一種把不同計算機或網路設備連接到一起的技術,它本質上是一種進程間通信方式,特別是跨系統的進程間通信,必須要通過網路才能進行。隨著高並發、分布式、雲計算、微服務等技術的普及,網路的性能也變得越來越重要。
說到網路,我想你肯定經常提起七層負載均衡、四層負載均衡,或者三層設備、二層設備等等。那麼,這里說的二層、三層、四層、七層又都是什麼意思呢?
實際上,這些層都來自國際標准化組織制定的開放式系統互聯通信參考模型(Open System Interconnection Reference Model),簡稱為 OSI 網路模型。
但是 OSI 模型還是太復雜了,也沒能提供一個可實現的方法。所以,在 Linux 中,我們實際上使用的是另一個更實用的四層模型,即 TCP/IP 網路模型。
TCP/IP 模型,把網路互聯的框架分為應用層、傳輸層、網路層、網路介面層等四層,其中,
為了幫你更形象理解 TCP/IP 與 OSI 模型的關系,我畫了一張圖,如下所示:
當然了,雖說 Linux 實際按照 TCP/IP 模型,實現了網路協議棧,但在平時的學習交流中,我們習慣上還是用 OSI 七層模型來描述。比如,說到七層和四層負載均衡,對應的分別是 OSI 模型中的應用層和傳輸層(而它們對應到 TCP/IP 模型中,實際上是四層和三層)。
OSI引入了服務、介面、協議、分層的概念,TCP/IP借鑒了OSI的這些概念建立TCP/IP模型。
OSI先有模型,後有協議,先有標准,後進行實踐;而TCP/IP則相反,先有協議和應用再提出了模型,且是參照的OSI模型。
OSI是一種理論下的模型,而TCP/IP已被廣泛使用,成為網路互聯事實上的標准。
有了 TCP/IP 模型後,在進行網路傳輸時,數據包就會按照協議棧,對上一層發來的數據進行逐層處理;然後封裝上該層的協議頭,再發送給下一層。
當然,網路包在每一層的處理邏輯,都取決於各層採用的網路協議。比如在應用層,一個提供 REST API 的應用,可以使用 HTTP 協議,把它需要傳輸的 JSON 數據封裝到 HTTP 協議中,然後向下傳遞給 TCP 層。
而封裝做的事情就很簡單了,只是在原來的負載前後,增加固定格式的元數據,原始的負載數據並不會被修改。
比如,以通過 TCP 協議通信的網路包為例,通過下面這張圖,我們可以看到,應用程序數據在每個層的封裝格式。
這些新增的頭部和尾部,增加了網路包的大小,但我們都知道,物理鏈路中並不能傳輸任意大小的數據包。網路介面配置的最大傳輸單元(MTU),就規定了最大的 IP 包大小。在我們最常用的乙太網中,MTU 默認值是 1500(這也是 Linux 的默認值)。
一旦網路包超過 MTU 的大小,就會在網路層分片,以保證分片後的 IP 包不大於 MTU 值。顯然,MTU 越大,需要的分包也就越少,自然,網路吞吐能力就越好。
理解了 TCP/IP 網路模型和網路包的封裝原理後,你很容易能想到,Linux 內核中的網路棧,其實也類似於 TCP/IP 的四層結構。如下圖所示,就是 Linux 通用 IP 網路棧的示意圖:
我們從上到下來看這個網路棧,你可以發現,
這里我簡單說一下網卡。網卡是發送和接收網路包的基本設備。在系統啟動過程中,網卡通過內核中的網卡驅動程序注冊到系統中。而在網路收發過程中,內核通過中斷跟網卡進行交互。
再結合前面提到的 Linux 網路棧,可以看出,網路包的處理非常復雜。所以,網卡硬中斷只處理最核心的網卡數據讀取或發送,而協議棧中的大部分邏輯,都會放到軟中斷中處理。
我們先來看網路包的接收流程。
當一個網路幀到達網卡後,網卡會通過 DMA 方式,把這個網路包放到收包隊列中;然後通過硬中斷,告訴中斷處理程序已經收到了網路包。
接著,網卡中斷處理程序會為網路幀分配內核數據結構(sk_buff),並將其拷貝到 sk_buff 緩沖區中;然後再通過軟中斷,通知內核收到了新的網路幀。
接下來,內核協議棧從緩沖區中取出網路幀,並通過網路協議棧,從下到上逐層處理這個網路幀。比如,
最後,應用程序就可以使用 Socket 介面,讀取到新接收到的數據了。
為了更清晰表示這個流程,我畫了一張圖,這張圖的左半部分表示接收流程,而圖中的粉色箭頭則表示網路包的處理路徑。
了解網路包的接收流程後,就很容易理解網路包的發送流程。網路包的發送流程就是上圖的右半部分,很容易發現,網路包的發送方向,正好跟接收方向相反。
首先,應用程序調用 Socket API(比如 sendmsg)發送網路包。
由於這是一個系統調用,所以會陷入到內核態的套接字層中。套接字層會把數據包放到 Socket 發送緩沖區中。
接下來,網路協議棧從 Socket 發送緩沖區中,取出數據包;再按照 TCP/IP 棧,從上到下逐層處理。比如,傳輸層和網路層,分別為其增加 TCP 頭和 IP 頭,執行路由查找確認下一跳的 IP,並按照 MTU 大小進行分片。
分片後的網路包,再送到網路介面層,進行物理地址定址,以找到下一跳的 MAC 地址。然後添加幀頭和幀尾,放到發包隊列中。這一切完成後,會有軟中斷通知驅動程序:發包隊列中有新的網路幀需要發送。
最後,驅動程序通過 DMA ,從發包隊列中讀出網路幀,並通過物理網卡把它發送出去。
多台伺服器通過網卡、交換機、路由器等網路設備連接到一起,構成了相互連接的網路。由於網路設備的異構性和網路協議的復雜性,國際標准化組織定義了一個七層的 OSI 網路模型,但是這個模型過於復雜,實際工作中的事實標准,是更為實用的 TCP/IP 模型。
TCP/IP 模型,把網路互聯的框架,分為應用層、傳輸層、網路層、網路介面層等四層,這也是 Linux 網路棧最核心的構成部分。
我結合網路上查閱的資料和文章中的內容,總結了下網卡收發報文的過程,不知道是否正確:
當發送數據包時,與上述相反。鏈路層將數據包封裝完畢後,放入網卡的DMA緩沖區,並調用系統硬中斷,通知網卡從緩沖區讀取並發送數據。
了解 Linux 網路的基本原理和收發流程後,你肯定迫不及待想知道,如何去觀察網路的性能情況。具體而言,哪些指標可以用來衡量 Linux 的網路性能呢?
實際上,我們通常用帶寬、吞吐量、延時、PPS(Packet Per Second)等指標衡量網路的性能。
除了這些指標,網路的可用性(網路能否正常通信)、並發連接數(TCP 連接數量)、丟包率(丟包百分比)、重傳率(重新傳輸的網路包比例)等也是常用的性能指標。
分析網路問題的第一步,通常是查看網路介面的配置和狀態。你可以使用 ifconfig 或者 ip 命令,來查看網路的配置。我個人更推薦使用 ip 工具,因為它提供了更豐富的功能和更易用的介面。
以網路介面 eth0 為例,你可以運行下面的兩個命令,查看它的配置和狀態:
你可以看到,ifconfig 和 ip 命令輸出的指標基本相同,只是顯示格式略微不同。比如,它們都包括了網路介面的狀態標志、MTU 大小、IP、子網、MAC 地址以及網路包收發的統計信息。
第一,網路介面的狀態標志。ifconfig 輸出中的 RUNNING ,或 ip 輸出中的 LOWER_UP ,都表示物理網路是連通的,即網卡已經連接到了交換機或者路由器中。如果你看不到它們,通常表示網線被拔掉了。
第二,MTU 的大小。MTU 默認大小是 1500,根據網路架構的不同(比如是否使用了 VXLAN 等疊加網路),你可能需要調大或者調小 MTU 的數值。
第三,網路介面的 IP 地址、子網以及 MAC 地址。這些都是保障網路功能正常工作所必需的,你需要確保配置正確。
第四,網路收發的位元組數、包數、錯誤數以及丟包情況,特別是 TX 和 RX 部分的 errors、dropped、overruns、carrier 以及 collisions 等指標不為 0 時,通常表示出現了網路 I/O 問題。其中:
ifconfig 和 ip 只顯示了網路介面收發數據包的統計信息,但在實際的性能問題中,網路協議棧中的統計信息,我們也必須關注。你可以用 netstat 或者 ss ,來查看套接字、網路棧、網路介面以及路由表的信息。
我個人更推薦,使用 ss 來查詢網路的連接信息,因為它比 netstat 提供了更好的性能(速度更快)。
比如,你可以執行下面的命令,查詢套接字信息:
netstat 和 ss 的輸出也是類似的,都展示了套接字的狀態、接收隊列、發送隊列、本地地址、遠端地址、進程 PID 和進程名稱等。
其中,接收隊列(Recv-Q)和發送隊列(Send-Q)需要你特別關注,它們通常應該是 0。當你發現它們不是 0 時,說明有網路包的堆積發生。當然還要注意,在不同套接字狀態下,它們的含義不同。
當套接字處於連接狀態(Established)時,
當套接字處於監聽狀態(Listening)時,
所謂全連接,是指伺服器收到了客戶端的 ACK,完成了 TCP 三次握手,然後就會把這個連接挪到全連接隊列中。這些全連接中的套接字,還需要被 accept() 系統調用取走,伺服器才可以開始真正處理客戶端的請求。
與全連接隊列相對應的,還有一個半連接隊列。所謂半連接是指還沒有完成 TCP 三次握手的連接,連接只進行了一半。伺服器收到了客戶端的 SYN 包後,就會把這個連接放到半連接隊列中,然後再向客戶端發送 SYN+ACK 包。
類似的,使用 netstat 或 ss ,也可以查看協議棧的信息:
這些協議棧的統計信息都很直觀。ss 只顯示已經連接、關閉、孤兒套接字等簡要統計,而 netstat 則提供的是更詳細的網路協議棧信息。
比如,上面 netstat 的輸出示例,就展示了 TCP 協議的主動連接、被動連接、失敗重試、發送和接收的分段數量等各種信息。
接下來,我們再來看看,如何查看系統當前的網路吞吐量和 PPS。在這里,我推薦使用我們的老朋友 sar,在前面的 CPU、內存和 I/O 模塊中,我們已經多次用到它。
給 sar 增加 -n 參數就可以查看網路的統計信息,比如網路介面(DEV)、網路介面錯誤(EDEV)、TCP、UDP、ICMP 等等。執行下面的命令,你就可以得到網路介面統計信息:
這兒輸出的指標比較多,我來簡單解釋下它們的含義。
其中,Bandwidth 可以用 ethtool 來查詢,它的單位通常是 Gb/s 或者 Mb/s,不過注意這里小寫字母 b ,表示比特而不是位元組。我們通常提到的千兆網卡、萬兆網卡等,單位也都是比特。如下你可以看到,我的 eth0 網卡就是一個千兆網卡:
其中,Bandwidth 可以用 ethtool 來查詢,它的單位通常是 Gb/s 或者 Mb/s,不過注意這里小寫字母 b ,表示比特而不是位元組。我們通常提到的千兆網卡、萬兆網卡等,單位也都是比特。如下你可以看到,我的 eth0 網卡就是一個千兆網卡:
我們通常使用帶寬、吞吐量、延時等指標,來衡量網路的性能;相應的,你可以用 ifconfig、netstat、ss、sar、ping 等工具,來查看這些網路的性能指標。
小狗同學問到: 老師,您好 ss —lntp 這個 當session處於listening中 rec-q 確定是 syn的backlog嗎?
A: Recv-Q為全連接隊列當前使用了多少。 中文資料里這個問題講得最明白的文章: https://mp.weixin.qq.com/s/yH3PzGEFopbpA-jw4MythQ
看了源碼發現,這個地方講的有問題.關於ss輸出中listen狀態套接字的Recv-Q表示全連接隊列當前使用了多少,也就是全連接隊列的當前長度,而Send-Q表示全連接隊列的最大長度