導航:首頁 > 編程系統 > linux協議棧實現分析

linux協議棧實現分析

發布時間:2023-03-03 10:04:49

① 關於linux學習路線的問題 請教前輩

很多同學接觸Linux不多,對Linux平台的開發更是一無所知。而現在的趨勢越來越表明,作為一 個優秀的軟體開發人員,或計算機IT行業從業人員,掌握Linux是一種很重要的謀生資源與手段。下來我將會結合自己的幾年的個人開發經驗,及對 Linux,更是類UNIX系統,及開源軟體文化,談談Linux的學習方法與學習中應該注意的一些事。
就如同剛才說的,很多同學以前可能連Linux是什麼都不知道,對UNIX更是一無所知。所以我們從最基礎的講起,對於Linux及UNIX的歷史我們不做多談,直接進入入門的學習。
Linux入門是很簡單的,問題是你是否有耐心,是否愛折騰,是否不排斥重裝一類的大修。沒折騰可以說是學不好Linux的,鳥哥說過,要真正了解Linux的分區機制,對LVM使用相當熟練,沒有20次以上的Linux裝機經驗是積累不起來的,所以一定不要怕折騰。
由於大家之前都使用Windows,所以我也盡可能照顧這些「菜鳥」。我的推薦,如果你第一次接觸Linux,那麼首先在虛擬機中嘗試它。虛擬機我推薦Virtual Box,我並不主張使用VM,原因是VM是閉源的,並且是收費的,我不希望推動盜版。當然如果你的Money足夠多,可以嘗試VM,但我要說的是即使是VM,不一定就一定好。付費的軟體不一定好。首先,Virtual Box很小巧,Windows平台下安裝包在80MB左右,而VM動輒600MB,雖然功能強大,但資源消耗也多,何況你的需求Virtual Box完全能夠滿足。所以,還是自己選。如何使用虛擬機,是你的事,這個我不教你,因為很簡單,不會的話Google或Bai都可以,英文好的可以直接看官方文檔。
現在介紹Linux發行版的知識。正如你所見,Linux發行版並非Linux,Linux僅是指操作系統的內核,作為科班出生的你不要讓我解釋,我也沒時間。我推薦的發行版如下:
UBUNTU適合純菜鳥,追求穩定的官方支持,對系統穩定性要求較弱,喜歡最新應用,相對來說不太喜歡折騰的開發者。
Debian,相對UBUNTU難很多的發行版,突出特點是穩定與容易使用的包管理系統,缺點是企業支持不足,為社區開發驅動。
Arch,追逐時尚的開發者的首選,優點是包更新相當快,無縫升級,一次安裝基本可以一直運作下去,沒有如UBUNTU那樣的版本概念,說的專業點叫滾動升級,保持你的系統一定是最新的。缺點顯然易見,不穩定。同時安裝配置相對Debian再麻煩點。
Gentoo,相對Arch再難點,考驗使用者的綜合水平,從系統安裝到微調,內核編譯都親歷親為,是高手及黑客顯示自己技術手段,按需配置符合自己要求的系統的首選。
Slackware與Gentoo類似。
CentOS,社區維護的RedHat的復刻版本,完全使用RedHat的源碼重新編譯生成,與RedHat的兼容性在理論上來說是最好的。如果你專注於Linux伺服器,如網路管理,架站,那麼CentOS是你的選擇。
LFS,終極黑客顯擺工具,完全從源代碼安裝,編譯系統。安裝前你得到的只有一份文檔,你要做的就是照文檔你的說明,一步步,一條條命令,一個個軟體包的去構建你的Linux,完全由你自己控制,想要什麼就是什麼。如果你做出了LFS,證明你的Linux功底已經相當不錯,如果你能拿LFS文檔活學活用,再將Linux從源代碼開始移植到嵌入式系統,我敢說中國的企業你可以混的很好。
你得挑一個適合你的系統,然後在虛擬機安裝它,開始使用它。如果你想快速學會Linux,我有一個建議就是忘記圖形界面,不要想圖形界面能不能提供你問題的答案,而是滿世界的去找,去問,如何用命令行解決你的問題。在這個過程中,你最好能將Linux的命令掌握的不錯,起碼常用的命令得知道,同時建立了自己的知識庫,裡面是你積累的各項知識。
再下個階段,你需要學習的是Linux平台的C/C++開發,同時還有Bash腳本編程,如果你對Java興趣很深還有Java。同樣,建議你拋棄掉圖形界面的IDE,從VIM開始,為什麼是VIM,而不是Emacs,我無意挑起編輯器大戰,但我覺得VIM適合初學者,適合手比較笨,腦袋比較慢的開發者。Emacs的鍵位太多,太復雜,我很畏懼。然後是GCC,Make,Eclipse(Java,C++或者)。雖然將C++列在了Eclipse中,但我並不推薦用IDE開發C++,因為這不是Linux的文化,容易讓你忽略一些你應該注意的問題。IDE讓你變懶,懶得跟豬一樣。如果你對程序調試,測試工作很感興趣,GDB也得學的很好,如果不是GDB也是必修課。這是開發的第一步,注意我並沒有提過一句Linux系統API的內容,這個階段也不要關心這個。你要做的就是積累經驗,在Linux平台的開發經驗。我推薦的書如下:C語言程序設計,譚浩強的也可以。C語言,白皮書當然更好。C++推薦C++ Primer Plus,Java我不喜歡,就不推薦了。工具方面推薦VIM的官方手冊,GCC中文文檔,GDB中文文檔,GNU開源軟體開發指導(電子書),匯編語言程序設計(讓你對庫,鏈接,內嵌匯編,編譯器優化選項有初步了解,不必深度)。
如果你這個階段過不了就不必往下做了,這是底線,最基礎的基礎,否則離開,不要霍霍Linux開發。不專業的Linux開發者作出的程序是與Linux文化或UNIX文化相背的,程序是走不遠的,不可能像Bash,VIM這些神品一樣。所以做不好乾脆離開。
接下來進入Linux系統編程,不二選擇,APUE,UNIX環境高級編程,一遍一遍的看,看10遍都嫌少,如果你可以在大學將這本書翻爛,裡面的內容都實踐過,有作品,你口頭表達能力夠強,你可以在面試時說服所有的考官。(可能有點誇張,但APUE絕對是聖經一般的讀物,即使是Windows程序員也從其中汲取養分,Google創始人的案頭書籍,扎爾伯克的床頭讀物。)
這本書看完後你會對Linux系統編程有相當的了解,知道Linux與Windows平台間開發的差異在哪?它們的優缺點在哪?我的總結如下:做Windows平台開發,很苦,微軟的系統API總在擴容,想使用最新潮,最高效的功能,最適合當前流行系統的功能你必須時刻學習。Linux不是,Linux系統的核心API就100來個,記憶力好完全可以背下來。而且經久不變,為什麼不變,因為要同UNIX兼容,符合POSIX標准。所以Linux平台的開發大多是專注於底層的或伺服器編程。這是其優點,當然圖形是Linux的軟肋,但我站在一個開發者的角度,我無所謂,因為命令行我也可以適應,如果有更好的圖形界面我就當作恩賜吧。另外,Windows閉源,系統做了什麼你更本不知道,永遠被微軟牽著鼻子跑,想想如果微軟說Win8不支持qq,那騰訊不得哭死。而Linux完全開源,你不喜歡,可以自己改,只要你技術夠。另外,Windows雖然使用的人多,但使用場合單一,專注與桌面。而Linux在各個方面都有發展,尤其在雲計算,伺服器軟體,嵌入式領域,企業級應用上有廣大前景,而且兼容性一流,由於支持POSIX可以無縫的運行在UNIX系統之上,不管是蘋果的Mac還是IBM的AS400系列,都是完全支持的。另外,Linux的開發環境支持也絕對是一流的,不管是C/C++,Java,Bash,Python,PHP,Javascript,。。。。。。就連C#也支持。而微軟除Visual Stdio套件以外,都不怎麼友好,不是嗎?
如果你看完APUE的感觸有很多,希望驗證你的某些想法或經驗,推薦UNIX程序設計藝術,世界頂級黑客將同你分享他的看法。
現在是時候做分流了。 大體上我分為四個方向:網路,圖形,嵌入式,設備驅動。
如果選擇網路,再細分,我對其他的不是他熟悉,只說伺服器軟體編寫及高性能的並發程序編寫吧。相對來說這是網路編程中技術含量最高的,也是底層的。需要很多的經驗,看很多的書,做很多的項目。
我的看法是以下面的順序來看書:
APUE再深讀 – 尤其是進程,線程,IPC,套接字
多核程序設計 - Pthread一定得吃透了,你很NB
UNIX網路編程 – 卷一,卷二
TCP/IP網路詳解 – 卷一 再看上面兩本書時就該看了
5.TCP/IP 網路詳解 – 卷二 我覺得看到卷二就差不多了,當然卷三看了更好,努力,爭取看了
6.Lighttpd源代碼 - 這個伺服器也很有名了
7.Nginx源代碼 – 相較於Apache,Nginx的源碼較少,如果能看個大致,很NB。看源代碼主要是要學習裡面的套接字編程及並發控制,想想都激動。如果你有這些本事,可以試著往暴雪投簡歷,為他們寫伺服器後台,想一想全球的魔獸都運行在你的伺服器軟體上。
Linux內核 TCP/IP協議棧 – 深入了解TCP/IP的實現
如果你還喜歡驅動程序設計,可以看看更底層的協議,如鏈路層的,寫什麼路由器,網卡,網路設備的驅動及嵌入式系統軟體應該也不成問題了。
當然一般的網路公司,就算網路級別的也該毫不猶豫的僱用你。只是看後面這些書需要時間與經驗,所以35歲以前辦到吧!跳槽到給你未來的地方!
圖形方向,我覺得圖形方向也是很有前途的,以下幾個方面。
Opengl的工業及游戲開發,國外較成熟。
影視動畫特效,如皮克斯,也是國外較成熟。
GPU計算技術,可以應用在瀏覽器網頁渲染上,GPU計算資源利用上,由於開源的原因,有很多的文檔程序可以參考。如果能進火狐開發,或google做瀏覽器開發,應該會很好 。
嵌入式方向:嵌入式方向沒說的,Linux很重要。
掌握多個架構,不僅X86的,ARM的,單片機什麼的也必須得懂。硬體不懂我預見你會死在半路上,我也想走嵌入式方向,但我覺得就學校教授嵌入式的方法,我連學電子的那幫學生都競爭不過。奉勸大家,一定得懂硬體再去做,如果走到嵌入式應用開發,只能祝你好運,不要碰上像Nokia,Hp這樣的公司,否則你會很慘的。
驅動程序設計:軟體開發周期是很長的,硬體不同,很快。每個月誕生那麼多的新硬體,如何讓他們在Linux上工作起來,這是你的工作。由於Linux的兼容性很好,如果不是太低層的驅動,基本C語言就可以搞定,系統架構的影響不大,因為有系統支持,你可能做些許更改就可以在ARM上使用PC的硬體了,所以做硬體驅動開發不像嵌入式,對硬體知識的要求很高。可以從事的方向也很多,如家電啊,特別是如索尼,日立,希捷,富士康這樣的廠子,很稀缺的。
LDD – Linux驅動程序設計與內核編程的基礎讀物
深入理解Linux內核 – 進階的
Linux源代碼 – 永無止境的
當然你還的看個方面的書,如網路啊什麼的。

② 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、狀態相關協議處理結構

③ 從ip addr add和ifconfig的區別看linux網卡ip地址的結構

如果你非常理解網路協議的原理以及網路的分層架構那麼我想你就不會有這個問題,實際上,每一個網卡設備都有一個mac地址,但是卻可 以有多個網路層地址,比如IP地址,然而這個事實無法很好地像用戶提供操作介面,所以就引出了ip別名(IP aliases)和輔助ip(secondary IP addresses)的概念。其實很容易理解這個事實,按照分層的思想,下層總是為上層服務,也就是為上層提供舞台,上層利用下層的服務,而不必讓下層知 道自己的情況,如果一個擁有合理mac地址的網卡沒有配置網路層地址(比如IP地址)這件事合理的話,那麼為這個設備配置多個IP地址也是合理的,正好像 一個ip可以對應多個應用層埠一樣,也就是說,下層對上層總是一對多的關系,在分層架構中這種關系是合理的。下面我們就看一下linux的網卡的ip地 址結構。剛才說了在linux中,一個網卡可以有多個IP,那麼這多個ip有什麼關系呢?其實這些ip組成了一個吊鏈結構,所謂吊鏈結構就是一些節點鏈接 成一條鏈,然後每個節點帶有自己的一條鏈


每個節點代表的ip地址標識一個網段,這個節點的ip就是這個網段的 Primary地址,它下面所帶的ip就是這個網段的Secondary地址,也就是說一個網卡可以帶有各個節點所帶鏈表長度之和個ip地址,而且這些 ip不是線形的,而是上述的吊鏈結構。我們看一下這么做有什麼好處。玩過Cisco路由器的朋友可能都知道有個Secondary IP的概念,這個特性可以創建邏輯子網,也就是說在一個物理網口上連接兩個子網,這咋看起來好像不可思議,其實很簡單,比如這個網口接到一台交換機上,如 果這個網口沒有配置Secondary IP的話,那麼這台交換機只能連接一個網段的主機,比如192.168.1.1/24,但是,如果它配置了Secondary IP,那麼就可以連接兩個網段的主機,比如192.168.1.1/24和10.0.0.1/24,道理就是這么簡單,但是卻很有用,該機制可以被路由匯 總策略所使用。注意上面這個例子中的Secondary IP不是這里說的linux的Secondary address,在linux中恰恰相反,只要一個網卡上配置的ip不是一個網段的,那麼都是Primary IP,就是吊鏈結構中上面的那條主鏈中的IP,linux中的Secondary address是主鏈結點的子鏈結點中的IP,這一點一定注意,概念是不能混淆的。前面說的只是吊鏈中主鏈的作用,那麼子鏈呢?其實想像一下也很簡單,比 如一台機器上運行著一個代理伺服器或者負載均衡服務,代理伺服器或者負載均衡服務和主伺服器要監聽相同的埠,那麼就可以用secondary address來解決了,只要需要在同一網段監聽同一個埠的應用都是吊鏈中子鏈存在的原因,因此可以說,主鏈對外部或者說對下面鏈路層虛擬了多塊網卡, 而子鏈向上層虛擬了多台機器,配置了吊鏈結構的linux主機如果說只有一塊網卡,那麼外部會認為它有多塊網卡,對於內部,應用層會認為彼此在不同的主機 上,這就是效果。
除了上面大體的介紹之外,還有很多細節,吊鏈在主鏈上是沒有主次的,子鏈除了第一個節點其它節點也不分主次,都是平行的關系,但是子鏈中的第一個節點總是 鏈接在主鏈中,它們攜帶的地址就是primary地址,它們下面隸屬的子鏈攜帶的地址就是這個primary地址的secondary地址,如此看來,一 旦主鏈上一個節點被刪除了,那麼它的子鏈也將不復存在,所謂皮之不存毛將焉附。但是這種策略總是顯得不是那麼優美,因為父親犯錯,兒子也要受連累,這在現 代社會早就不時行了,那麼就需要改變機制了,因此linux中特意有了一個選項,就是當一個primary地址被刪除時,如果它有secondary地址 的話,那麼它的第一個secondary地址(長子)繼承被刪除的primary地址的位置成為primary地址,這樣就顯得很合理了,要不然在刪除 primary地址的時候,如果有程序用secondary地址,那麼要麼延遲刪除,要麼程序崩潰,採用自動提升策略的話就不會出現問題。
至於說IP aliases,那是以前版本有的了,就是一個實現問題,解決的問題和現在的secondary IP機制一樣,它主要就是在物理網卡名字後面加上後綴從而成為虛擬網路介面,本質上和secondary IP機制沒有區別,區別就是IP aliases顯得不是那麼直觀,而secondary IP卻是真正讓應用看到了一個網卡的多個地址,比如你要是用IP aliases的話,有的時候你總是會問eth0:0是什麼?我就曾經在內核裡面拚命找eth0:0這個網路設備的注冊代碼,都要瘋掉了也沒有找到,其實 我並不是很傻,但是我卻因為那個該死的名字作出了傻事。
下面就可以看看linux內核的實現代碼了,首先弄明白一些數據結構,最重要的就是net_device,其次就是in_device,然後就是in_ifaddr,明白了這三個數據結構,一切就明白了,這是真的。

structnet_device
{
...
void*ip_ptr;//指向一個in_device結構,這欄位從net_device中分離表明一個網卡可以支持多種網路層協議的
...
}
structin_device
{
structnet_device*dev;//指向它隸屬的net_device,也就是網卡
atomic_trefcnt;//引用計數
intdead;
structin_ifaddr*ifa_list;//所有的ip地址鏈表
...
};
structin_ifaddr//代表一個ip地址
{
structin_ifaddr*ifa_next;//上面的in_device中的ifa_list欄位就是靠這個欄位連成鏈的
structin_device*ifa_dev;//回指in_device結構
structrcu_headrcu_head;
u32ifa_local;//ip地址
u32ifa_address;
u32ifa_mask;//掩碼
u32ifa_broadcast;//廣播地址
u32ifa_anycast;
unsignedcharifa_scope;
unsignedcharifa_flags;//只有IFA_F_SECONDARY標志,因為除了這個就是primary地址了
unsignedcharifa_prefixlen;
charifa_label[IFNAMSIZ];//名字,在ipaliases時代,它就可能是ethx:y的形式,在secondaryip時代,它統一就是ethx
};


注 意,上面的結構並沒有將linux網卡的ip地址結構表示為吊鏈結構,所謂的吊鏈結構只是邏輯上的,在數據結構上,一個網卡所有的ip地址全部都在 ifa_list中被鏈接成一個線性的鏈表,至於是primary地址還是secondary地址就看in_ifaddr的ifa_flags欄位了。每 當有新的地址被設置的時候,inet_insert_ifa總是被調用,linux為何沒有在代碼上將ip地址表示為吊鏈結構呢?我也不知道,個人感覺一 個net_device帶有一個primary ip鏈表,然後每個primary ip節點帶有一個secondary ip鏈表,這樣會更好一些的,我覺得inet_insert_ifa實現的十分拙劣。添加地址可以通過兩個用戶空間程序搞定,一個是ifconfig,另 一個是ip addr add,ifconfig是基於ioctl進行地址添加的,而ip程序是基於netlink進行地址添加的,不管哪一種方式都可以達到目的,現在就可以看 看另一個問題了:為何用ip addr add添加的ip地址用ifconfig看不到,而ifconfig設置的地址ip addr show卻是可以看到。這個問題通過看代碼一眼就可以明白,在ifconfig獲得ip地址的時候,代碼:

for(ifap=&in_dev->ifa_list;(ifa=*ifap)!=NULL;ifap=&ifa->ifa_next)
{
if(!strcmp(ifr.ifr_name,ifa->ifa_label)&&sin_orig.sin_addr.s_addr==ifa->ifa_address)
{
break;
}
}


取 的是這個被找到的ifa的ip地址,而我們知道,所有的ifa鏈接成一個線性鏈表,那麼找到了第一個就不會再往後走了,因此只能得到一個結果,就是鏈表最 前面的那個,而ip add show就不同了,具體在函數inet_mp_ifaddr中實現,該函數遍歷所有的ifa,並且傳到用戶空間緩沖區。這里可以做一個實驗:首先用 ip addr add添加幾個不在同一個網段的primary ip地址,然後再ifconfig一個和前面的ip都不在一個網段的ip,然後可以用ifconfig查看一下,發現不是剛剛用ifconfig設置進去 的那個ip,而是用ip addr add添加進去的,這就說明ifconfig永遠都是取的ifa鏈表最前面的那一個,還有一點要注意,就是如果你用ip addr add添加了很多的secondary ip地址,那麼恰好你用ifconfig設置的ip地址和那些secondary ip在一個網段,那麼所有的secondary ip都將被刪除,這些都是sencondary ip的規范決定的,而且在代碼中也有體現。另外還要注意,路由表的表項都是基於primary ip的,因為所有的操作都是以primary ip為主的,比如在添加路由的時候:

voidfib_add_ifaddr(structin_ifaddr*ifa)
{
structin_device*in_dev=ifa->ifa_dev;
structnet_device*dev=in_dev->dev;
structin_ifaddr*prim=ifa;
...
if(ifa->ifa_flags&IFA_F_SECONDARY){//如果ifa是個sencondary地址,那麼就找到它隸屬的primary地址後然後以這個primary為主進行設置
prim=inet_ifa_byprefix(in_dev,prefix,mask);
if(prim==NULL){
printk(KERN_DEBUG"fib_add_ifaddr:bug:prim==NULL/n");
return;
}
}
fib_magic(RTM_NEWROUTE,RTN_LOCAL,addr,32,prim);//添加進路由表
...
}


到 此為止我們知道了不少東西,最重要的就是linux中網卡ip地址的吊鏈結構以及這么設計的好處,另外就是設置ip地址的方式有ioctl和 netlink。其實網卡擁有多個ip並不會帶來什麼沖突,本質上ip和網卡沒有什麼關系,它們唯一的關系就是靠網路分層模型聯系在一起的,細節上就是靠 路由聯系在一起的,比如我添加路由的時候指定了一個目的地址和下一跳ip地址以及一個網卡出口,那麼內核會根據提供的目的地址將路由插在合式的位置,然後 將nh的網路設備設置為你提供的網卡出口,等到傳輸數據的時候就會查找路由從而找到出口,就是這么簡單,你自己手動設置的路由可以隨意設置,即使完全錯誤 內核也會將之加入路由表的,還有一種路由是內核自動生成的,就是在網卡剛剛up的時候,這時通過網卡的net_device找到其in_device然後 找到其ip地址,這樣的路由稱為鏈路路由。
通過secondary IP機制,你可以認為你的機器有很多網卡,對於應用,監聽同一埠的應用會認為它們在區域網中不同的機器上,你可以隨意使用這些ip地址而不會發生混亂,路由和底層的arp會處理好這一切,當然前提是你將路由設置對。
附: 用戶空間有ifup/ifdown,/sbin/ip,ifconfig,還有netplugd守護進程,這些有何關系嗎?這中間ip程序是最基本的,沒 有任何策略,策略就是參數指定,要麼就是別的程序調用它,而netplugd就是一個監控守護進程,通過netlink監控網卡狀態,然後根據不同的監控 結果調用/etc/netplug.d/netplug腳本,進而可能調用ifup/ifdown腳本,而後者就是腳本,其中會調用ifup-eth腳 本,最終整理好參數後調用ip程序(典型的就是:ip link set eth0 up/down),當然ip程序完全可以自己調用,比如ip addr add以及ip route add等等,而ifconfig沒有那麼繞圈子,就是通過ioctl進行設置,可以通過strace來觀察。這其中奧妙大了去了,說白了就是策略和機制分 離,另外還體現出linux中的很多功能都是很小的程序組合而成的。

Linux的ip地址的吊鏈結構以及ip地址的定址特性(詳見《關於IP網段間互訪的問題—路由是根本》)充分說明了linux的協議棧實現多麼的完美,完全符合分層和封裝模型,使得下層的邏輯和上層的邏輯完全解除耦合,也就是說ip層完全不依賴鏈路層以及物理層的物理布局,最後記住,ip層事情比如定址路由只由ip層實現,之所有有鏈路層發現的路由,完全是為了方便。

④ Linux TCP/IP協議棧數據包處理流程及代碼實現分析

好吧復,我來回答吧,首先制是網卡驅動程序捕獲到數據包,做檢驗無誤後,和DMA以及CPU交互,然後由DMA和驅動程序創建BD表,然後分配skbuf(LINUX下)數據結構保存獲得的數據幀,內核通過協議棧處理這個skbuf,通常是層層剝離每個層的首部,然後傳到上一層,細節就是一個變數做偏移量,每次做一個首部偏移讀取首部數據,識別本層協議類型以及下一層協議類型,具體過程就是這個網路原理的過程,請參考《TCP/IP詳解卷一》《linux設備驅動程序》《understanding linux network internals》《Unix網路編程卷一》等。

⑤ linux環境下的進程調度演算法有哪些

第一部分: 實時調度演算法介紹

對於什麼是實時系統,POSIX 1003.b作了這樣的定義:指系統能夠在限定的響應時間內提供所需水平的服務。而一個由Donald Gillies提出的更加為大家接受的定義是:一個實時系統是指計算的正確性不僅取決於程序的邏輯正確性,也取決於結果產生的時間,如果系統的時間約束條件得不到滿足,將會發生系統出錯。

實時系統根據其對於實時性要求的不同,可以分為軟實時和硬實時兩種類型。硬實時系統指系統要有確保的最壞情況下的服務時間,即對於事件的響應時間的截止期限是無論如何都必須得到滿足。比如航天中的宇宙飛船的控制等就是現實中這樣的系統。其他的所有有實時特性的系統都可以稱之為軟實時系統。如果明確地來說,軟實時系統就是那些從統計的角度來說,一個任務(在下面的論述中,我們將對任務和進程不作區分)能夠得到有確保的處理時間,到達系統的事件也能夠在截止期限到來之前得到處理,但違反截止期限並不會帶來致命的錯誤,像實時多媒體系統就是一種軟實時系統。

一個計算機系統為了提供對於實時性的支持,它的操作系統必須對於CPU和其他資源進行有效的調度和管理。在多任務實時系統中,資源的調度和管理更加復雜。本文下面將先從分類的角度對各種實時任務調度演算法進行討論,然後研究普通的 Linux操作系統的進程調度以及各種實時Linux系統為了支持實時特性對普通Linux系統所做的改進。最後分析了將Linux操作系統應用於實時領域中時所出現的一些問題,並總結了各種實時Linux是如何解決這些問題的。

1. 實時CPU調度演算法分類

各種實時操作系統的實時調度演算法可以分為如下三種類別[Wang99][Gopalan01]:基於優先順序的調度演算法(Priority-driven scheling-PD)、基於CPU使用比例的共享式的調度演算法(Share-driven scheling-SD)、以及基於時間的進程調度演算法(Time-driven scheling-TD),下面對這三種調度演算法逐一進行介紹。

1.1. 基於優先順序的調度演算法

基於優先順序的調度演算法給每個進程分配一個優先順序,在每次進程調度時,調度器總是調度那個具有最高優先順序的任務來執行。根據不同的優先順序分配方法,基於優先順序的調度演算法可以分為如下兩種類型[Krishna01][Wang99]:

靜態優先順序調度演算法:

這種調度演算法給那些系統中得到運行的所有進程都靜態地分配一個優先順序。靜態優先順序的分配可以根據應用的屬性來進行,比如任務的周期,用戶優先順序,或者其它的預先確定的策略。RM(Rate-Monotonic)調度演算法是一種典型的靜態優先順序調度演算法,它根據任務的執行周期的長短來決定調度優先順序,那些具有小的執行周期的任務具有較高的優先順序。

動態優先順序調度演算法:

這種調度演算法根據任務的資源需求來動態地分配任務的優先順序,其目的就是在資源分配和調度時有更大的靈活性。非實時系統中就有很多這種調度演算法,比如短作業優先的調度演算法。在實時調度演算法中, EDF演算法是使用最多的一種動態優先順序調度演算法,該演算法給就緒隊列中的各個任務根據它們的截止期限(Deadline)來分配優先順序,具有最近的截止期限的任務具有最高的優先順序。

1.2. 基於比例共享調度演算法

雖然基於優先順序的調度演算法簡單而有效,但這種調度演算法提供的是一種硬實時的調度,在很多情況下並不適合使用這種調度演算法:比如象實時多媒體會議系統這樣的軟實時應用。對於這種軟實時應用,使用一種比例共享式的資源調度演算法(SD演算法)更為適合。

比例共享調度演算法指基於CPU使用比例的共享式的調度演算法,其基本思想就是按照一定的權重(比例)對一組需要調度的任務進行調度,讓它們的執行時間與它們的權重完全成正比。

我們可以通過兩種方法來實現比例共享調度演算法[Nieh01]:第一種方法是調節各個就緒進程出現在調度隊列隊首的頻率,並調度隊首的進程執行;第二種做法就是逐次調度就緒隊列中的各個進程投入運行,但根據分配的權重調節分配個每個進程的運行時間片。

比例共享調度演算法可以分為以下幾個類別:輪轉法、公平共享、公平隊列、彩票調度法(Lottery)等。

比例共享調度演算法的一個問題就是它沒有定義任何優先順序的概念;所有的任務都根據它們申請的比例共享CPU資源,當系統處於過載狀態時,所有的任務的執行都會按比例地變慢。所以為了保證系統中實時進程能夠獲得一定的CPU處理時間,一般採用一種動態調節進程權重的方法。

1.3. 基於時間的進程調度演算法

對於那些具有穩定、已知輸入的簡單系統,可以使用時間驅動(Time-driven:TD)的調度演算法,它能夠為數據處理提供很好的預測性。這種調度演算法本質上是一種設計時就確定下來的離線的靜態調度方法。在系統的設計階段,在明確系統中所有的處理情況下,對於各個任務的開始、切換、以及結束時間等就事先做出明確的安排和設計。這種調度演算法適合於那些很小的嵌入式系統、自控系統、感測器等應用環境。

這種調度演算法的優點是任務的執行有很好的可預測性,但最大的缺點是缺乏靈活性,並且會出現有任務需要被執行而CPU卻保持空閑的情況。

2. 通用Linux系統中的CPU調度

通用Linux系統支持實時和非實時兩種進程,實時進程相對於普通進程具有絕對的優先順序。對應地,實時進程採用SCHED_FIFO或者SCHED_RR調度策略,普通的進程採用SCHED_OTHER調度策略。

在調度演算法的實現上,Linux中的每個任務有四個與調度相關的參數,它們是rt_priority、policy、priority(nice)、counter。調度程序根據這四個參數進行進程調度。

在SCHED_OTHER 調度策略中,調度器總是選擇那個priority+counter值最大的進程來調度執行。從邏輯上分析,SCHED_OTHER調度策略存在著調度周期(epoch),在每一個調度周期中,一個進程的priority和counter值的大小影響了當前時刻應該調度哪一個進程來執行,其中 priority是一個固定不變的值,在進程創建時就已經確定,它代表了該進程的優先順序,也代表這該進程在每一個調度周期中能夠得到的時間片的多少; counter是一個動態變化的值,它反映了一個進程在當前的調度周期中還剩下的時間片。在每一個調度周期的開始,priority的值被賦給 counter,然後每次該進程被調度執行時,counter值都減少。當counter值為零時,該進程用完自己在本調度周期中的時間片,不再參與本調度周期的進程調度。當所有進程的時間片都用完時,一個調度周期結束,然後周而復始。另外可以看出Linux系統中的調度周期不是靜態的,它是一個動態變化的量,比如處於可運行狀態的進程的多少和它們priority值都可以影響一個epoch的長短。值得注意的一點是,在2.4以上的內核中, priority被nice所取代,但二者作用類似。

可見SCHED_OTHER調度策略本質上是一種比例共享的調度策略,它的這種設計方法能夠保證進程調度時的公平性--一個低優先順序的進程在每一個epoch中也會得到自己應得的那些CPU執行時間,另外它也提供了不同進程的優先順序區分,具有高priority值的進程能夠獲得更多的執行時間。

對於實時進程來說,它們使用的是基於實時優先順序rt_priority的優先順序調度策略,但根據不同的調度策略,同一實時優先順序的進程之間的調度方法有所不同:

SCHED_FIFO:不同的進程根據靜態優先順序進行排隊,然後在同一優先順序的隊列中,誰先准備好運行就先調度誰,並且正在運行的進程不會被終止直到以下情況發生:1.被有更高優先順序的進程所強佔CPU;2.自己因為資源請求而阻塞;3.自己主動放棄CPU(調用sched_yield);

SCHED_RR:這種調度策略跟上面的SCHED_FIFO一模一樣,除了它給每個進程分配一個時間片,時間片到了正在執行的進程就放棄執行;時間片的長度可以通過sched_rr_get_interval調用得到;

由於Linux系統本身是一個面向桌面的系統,所以將它應用於實時應用中時存在如下的一些問題:

Linux系統中的調度單位為10ms,所以它不能夠提供精確的定時;

當一個進程調用系統調用進入內核態運行時,它是不可被搶占的;

Linux內核實現中使用了大量的封中斷操作會造成中斷的丟失;

由於使用虛擬內存技術,當發生頁出錯時,需要從硬碟中讀取交換數據,但硬碟讀寫由於存儲位置的隨機性會導致隨機的讀寫時間,這在某些情況下會影響一些實時任務的截止期限;

雖然Linux進程調度也支持實時優先順序,但缺乏有效的實時任務的調度機制和調度演算法;它的網路子系統的協議處理和其它設備的中斷處理都沒有與它對應的進程的調度關聯起來,並且它們自身也沒有明確的調度機制;

3. 各種實時Linux系統

3.1. RT-Linux和RTAI

RT -Linux是新墨西哥科技大學(New Mexico Institute of Technology)的研究成果[RTLinuxWeb][Barabanov97]。它的基本思想是,為了在Linux系統中提供對於硬實時的支持,它實現了一個微內核的小的實時操作系統(我們也稱之為RT-Linux的實時子系統),而將普通Linux系統作為一個該操作系統中的一個低優先順序的任務來運行。另外普通Linux系統中的任務可以通過FIFO和實時任務進行通信。RT-Linux的框架如圖 1所示:

圖 1 RT-Linux結構

RT -Linux的關鍵技術是通過軟體來模擬硬體的中斷控制器。當Linux系統要封鎖CPU的中斷時時,RT-Linux中的實時子系統會截取到這個請求,把它記錄下來,而實際上並不真正封鎖硬體中斷,這樣就避免了由於封中斷所造成的系統在一段時間沒有響應的情況,從而提高了實時性。當有硬體中斷到來時, RT-Linux截取該中斷,並判斷是否有實時子系統中的中斷常式來處理還是傳遞給普通的Linux內核進行處理。另外,普通Linux系統中的最小定時精度由系統中的實時時鍾的頻率決定,一般Linux系統將該時鍾設置為每秒來100個時鍾中斷,所以Linux系統中一般的定時精度為 10ms,即時鍾周期是10ms,而RT-Linux通過將系統的實時時鍾設置為單次觸發狀態,可以提供十幾個微秒級的調度粒度。

RT-Linux實時子系統中的任務調度可以採用RM、EDF等優先順序驅動的演算法,也可以採用其他調度演算法。

RT -Linux對於那些在重負荷下工作的專有系統來說,確實是一個不錯的選擇,但他僅僅提供了對於CPU資源的調度;並且實時系統和普通Linux系統關系不是十分密切,這樣的話,開發人員不能充分利用Linux系統中已經實現的功能,如協議棧等。所以RT-Linux適合與工業控制等實時任務功能簡單,並且有硬實時要求的環境中,但如果要應用與多媒體處理中還需要做大量的工作。

義大利的RTAI( Real-Time Application Interface )源於RT-Linux,它在設計思想上和RT-Linux完全相同。它當初設計目的是為了解決RT-Linux難於在不同Linux版本之間難於移植的問題,為此,RTAI在 Linux 上定義了一個實時硬體抽象層,實時任務通過這個抽象層提供的介面和Linux系統進行交互,這樣在給Linux內核中增加實時支持時可以盡可能少地修改 Linux的內核源代碼。

3.2. Kurt-Linux

Kurt -Linux由Kansas大學開發,它可以提供微秒級的實時精度[KurtWeb] [Srinivasan]。不同於RT-Linux單獨實現一個實時內核的做法,Kurt -Linux是在通用Linux系統的基礎上實現的,它也是第一個可以使用普通Linux系統調用的基於Linux的實時系統。

Kurt-Linux將系統分為三種狀態:正常態、實時態和混合態,在正常態時它採用普通的Linux的調度策略,在實時態只運行實時任務,在混合態實時和非實時任務都可以執行;實時態可以用於對於實時性要求比較嚴格的情況。

為了提高Linux系統的實時特性,必須提高系統所支持的時鍾精度。但如果僅僅簡單地提高時鍾頻率,會引起調度負載的增加,從而嚴重降低系統的性能。為了解決這個矛盾, Kurt-Linux採用UTIME所使用的提高Linux系統中的時鍾精度的方法[UTIMEWeb]:它將時鍾晶元設置為單次觸發狀態(One shot mode),即每次給時鍾晶元設置一個超時時間,然後到該超時事件發生時在時鍾中斷處理程序中再次根據需要給時鍾晶元設置一個超時時間。它的基本思想是一個精確的定時意味著我們需要時鍾中斷在我們需要的一個比較精確的時間發生,但並非一定需要系統時鍾頻率達到此精度。它利用CPU的時鍾計數器TSC (Time Stamp Counter)來提供精度可達CPU主頻的時間精度。

對於實時任務的調度,Kurt-Linux採用基於時間(TD)的靜態的實時CPU調度演算法。實時任務在設計階段就需要明確地說明它們實時事件要發生的時間。這種調度演算法對於那些循環執行的任務能夠取得較好的調度效果。

Kurt -Linux相對於RT-Linux的一個優點就是可以使用Linux系統自身的系統調用,它本來被設計用於提供對硬實時的支持,但由於它在實現上只是簡單的將Linux調度器用一個簡單的時間驅動的調度器所取代,所以它的實時進程的調度很容易受到其它非實時任務的影響,從而在有的情況下會發生實時任務的截止期限不能滿足的情況,所以也被稱作嚴格實時系統(Firm Real-time)。目前基於Kurt-Linux的應用有:ARTS(ATM Reference Traffic System)、多媒體播放軟體等。另外Kurt-Linux所採用的這種方法需要頻繁地對時鍾晶元進行編程設置。

3.3. RED-Linux

RED -Linux是加州大學Irvine分校開發的實時Linux系統[REDWeb][ Wang99],它將對實時調度的支持和Linux很好地實現在同一個操作系統內核中。它同時支持三種類型的調度演算法,即:Time-Driven、 Priority-Dirven、Share-Driven。

為了提高系統的調度粒度,RED-Linux從RT-Linux那兒借鑒了軟體模擬中斷管理器的機制,並且提高了時鍾中斷頻率。當有硬體中斷到來時,RED-Linux的中斷模擬程序僅僅是簡單地將到來的中斷放到一個隊列中進行排隊,並不執行真正的中斷處理程序。

另外為了解決Linux進程在內核態不能被搶占的問題, RED-Linux在Linux內核的很多函數中插入了搶占點原語,使得進程在內核態時,也可以在一定程度上被搶占。通過這種方法提高了內核的實時特性。

RED-Linux的設計目標就是提供一個可以支持各種調度演算法的通用的調度框架,該系統給每個任務增加了如下幾項屬性,並將它們作為進程調度的依據:

Priority:作業的優先順序;

Start-Time:作業的開始時間;

Finish-Time:作業的結束時間;

Budget:作業在運行期間所要使用的資源的多少;

通過調整這些屬性的取值及調度程序按照什麼樣的優先順序來使用這些屬性值,幾乎可以實現所有的調度演算法。這樣的話,可以將三種不同的調度演算法無縫、統一地結合到了一起。

⑥ 關於 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表示全連接隊列的最大長度

閱讀全文

與linux協議棧實現分析相關的資料

熱點內容
ps怎麼剪貼到別的文件 瀏覽:352
如何導出文件 瀏覽:595
apk中的xml文件 瀏覽:239
umeng打包工具 瀏覽:765
g76錐度牙怎麼編程 瀏覽:430
win10企業版關機很慢 瀏覽:163
微信短視頻是保存在哪個文件夾 瀏覽:985
win10列印機設置紙張大小設置 瀏覽:427
衛星測控數據有哪些 瀏覽:451
格式工廠330教程 瀏覽:421
童美編程課怎麼樣 瀏覽:40
網頁代碼調試器 瀏覽:54
讀取文件string 瀏覽:500
ug自動編程軟體怎麼畫數控圖 瀏覽:920
什麼網站可以介紹主機 瀏覽:340
移動簡訊查詢代碼 瀏覽:192
怎麼看公司網路是什麼時候開通 瀏覽:960
cad2015文件參照怎麼關閉 瀏覽:678
最小二乘逼近程序 瀏覽:610
鐵路12306密碼找不回 瀏覽:352

友情鏈接