引言css
Alan Cox在內核1.3版本的開發階段最早引入了Netlink,剛開始時Netlink是以字符驅動接口的方式提供內核與用戶空間的雙向數據通訊;隨後,在2.1內核開發過程當中,Alexey Kuznetsov將Netlink改寫成一個更加靈活、且易於擴展的基於消息通訊接口,並將其應用到高級路由子系統的基礎框架裏。自那時起,Netlink就成了Linux內核子系統和用戶態的應用程序通訊的主要手段之一。linux
2001年,ForCES IETF委員會正式對Netlink進行了標準化的工做。Jamal Hadi Salim提議將Netlink定義成一種用於網絡設備的路由引擎組件和其控制管理組件之間通訊的協議。不過他的建議最終沒有被採納,取而代之的是咱們今天所看到的格局:Netlink被設計成一個新的協議域,domain。編程
Linux之父託瓦斯曾說過「Linux is evolution, not intelligent design」。什麼意思?就是說,Netlink也一樣遵循了Linux的某些設計理念,即沒有完整的規範文檔,亦沒有設計文檔。只有什麼?你懂得---「Read the f**king source code」。網絡
固然,本文不是分析Netlink在Linux上的實現機制,而是就「什麼是Netlink」以及「如何用好Netlink」的話題和你們作個分享,只有在遇到問題時才須要去閱讀內核源碼弄清個因此然。架構
什麼是Netlink框架
關於Netlink的理解,須要把握幾個關鍵點:dom
一、面向數據報的無鏈接消息子系統異步
二、基於通用的BSD Socket架構而實現socket
關於第一點使咱們很容易聯想到UDP協議,能想到這一點就很是棒了。按着UDP協議來理解Netlink不是不無道理,只要你能舉一反三,作到「活學」,善於總結概括、聯想,最後實現知識遷移這就是學習的本質。Netlink能夠實現內核->用戶以及用戶->內核的雙向、異步的數據通訊,同時它還支持兩個用戶進程之間、甚至兩個內核子系統之間的數據通訊。本文中,對後二者咱們不予考慮,焦點集中在如何實現用戶<->內核之間的數據通訊。ide
看到第二點腦海中是否是瞬間閃現了下面這張圖片呢?若是是,則說明你確實有慧根;固然,不是也不要緊,慧根能夠慢慢長嘛,呵呵。
Netlink通訊類型
Netlink支持兩種類型的通訊方式:單播和多播。
單播:常常用於一個用戶進程和一個內核子系統之間1:1的數據通訊。用戶空間發送命令到內核,而後從內核接受命令的返回結果。
Netlink的消息格式
Netlink消息由兩部分組成:消息頭和有效數據載荷,且整個Netlink消息是4字節對齊,通常按主機字節序進行傳遞。消息頭爲固定的16字節,消息體長度可變:Netlink的消息頭
消息頭定義在
點擊(此處)摺疊或打開
消息頭中各成員屬性的解釋及說明:
nlmsg_len:整個消息的長度,按字節計算。包括了Netlink消息頭自己。
nlmsg_type:消息的類型,便是數據仍是控制消息。目前(內核版本2.6.21)Netlink僅支持四種類型的控制消息,以下:
NLMSG_NOOP-空消息,什麼也不作;
NLMSG_ERROR-指明該消息中包含一個錯誤;
NLMSG_DONE-若是內核經過Netlink隊列返回了多個消息,那麼隊列的最後一條消息的類型爲NLMSG_DONE,其他全部消息的nlmsg_flags屬性都被設置NLM_F_MULTI位有效。
NLMSG_OVERRUN-暫時沒用到。
nlmsg_flags:附加在消息上的額外說明信息,如上面提到的NLM_F_MULTI。摘錄以下: 標記
|
做用及說明
|
NLM_F_REQUEST
|
若是消息中有該標記位,說明這是一個請求消息。全部從用戶空間到內核空間的消息都要設置該位,不然內核將向用戶返回一個EINVAL無效參數的錯誤
|
NLM_F_MULTI
|
消息從用戶->內核是同步的馬上完成,而從內核->用戶則須要排隊。若是內核以前收到過來自用戶的消息中有NLM_F_DUMP位爲1的消息,那麼內核就會向用戶空間發送一個由多個Netlink消息組成的鏈表。除了最後個消息外,其他每條消息中都設置了該位有效。
|
NLM_F_ACK
|
該消息是內核對來自用戶空間的NLM_F_REQUEST消息的響應
|
NLM_F_ECHO
|
若是從用戶空間發給內核的消息中該標記爲1,則說明用戶的應用進程要求內核將用戶發給它的每條消息經過單播的形式再發送給用戶進程。和咱們一般說的「回顯」功能相似。
|
…
|
…
|
nlmsg_seq:消息序列號。由於Netlink是面向數據報的,因此存在丟失數據的風險,可是Netlink提供瞭如何確保消息不丟失的機制,讓程序開發人員根據其實際需求而實現。消息序列號通常和NLM_F_ACK類型的消息聯合使用,若是用戶的應用程序須要保證其發送的每條消息都成功被內核收到的話,那麼它發送消息時須要用戶程序本身設置序號,內核收到該消息後對提取其中的序列號,而後在發送給用戶程序迴應消息裏設置一樣的序列號。有點相似於TCP的響應和確認機制。
注意:當內核主動向用戶空間發送廣播消息時,消息中的該字段老是爲0。
Netlink的消息體
Netlink的消息體採用TLV(Type-Length-Value)格式:Netlink提供的錯誤指示消息
當用戶空間的應用程序和內核空間的進程之間經過Netlink通訊時發生了錯誤,Netlink必須向用戶空間通報這種錯誤。Netlink對錯誤消息進行了單獨封裝,點擊(此處)摺疊或打開
Netlink編程須要注意的問題
基於Netlink的用戶-內核通訊,有兩種狀況可能會致使丟包:
1、內存耗盡;
2、用戶空間接收進程的緩衝區溢出。致使緩衝區溢出的主要緣由有多是:用戶空間的進程運行太慢;或者接收隊列過短。
若是Netlink不能將消息正確傳遞到用戶空間的接收進程,那麼用戶空間的接收進程在調用recvmsg()系統調用時就會返回一個內存不足(ENOBUFS)的錯誤,這一點須要注意。換句話說,緩衝區溢出的狀況是不會發送在從用戶->內核的sendmsg()系統調用裏,緣由前面咱們也說過了,請你們本身思考一下。
固然,若是使用的是阻塞型socket通訊,也就不存在內存耗盡的隱患了,這又是爲何呢?趕忙去谷歌一下,查查什麼是阻塞型socket吧。學而不思則罔,思而不學則殆嘛。
Netlink的地址結構體
在TCP博文中咱們提到過在Internet編程過程當中所用到的地址結構體和標準地址結構體,它們和Netlink地址結構體的關係以下:struct sockaddr_nl{}的詳細定義和描述以下:
點擊(此處)摺疊或打開
nl_pid:該屬性爲發送或接收消息的進程ID,前面咱們也說過,Netlink不只能夠實現用戶-內核空間的通訊還可以使現實用戶空間兩個進程之間,或內核空間兩個進程之間的通訊。該屬性爲0時通常適用於以下兩種狀況:
第一,咱們要發送的目的地是內核,即從用戶空間發往內核空間時,咱們構造的Netlink地址結構體中nl_pid一般狀況下都置爲0。這裏有一點須要跟你們交代一下,在Netlink規範裏,PID全稱是Port-ID(32bits),其主要做用是用於惟一的標識一個基於netlink的socket通道。一般狀況下nl_pid都設置爲當前進程的進程號。然而,對於一個進程的多個線程同時使用netlink socket的狀況,nl_pid的設置通常採用以下這個樣子來實現:
點擊(此處)摺疊或打開
第二,從內核發出的多播報文到用戶空間時,若是用戶空間的進程處在該多播組中,那麼其地址結構體中nl_pid也設置爲0,同時還要結合下面介紹到的另外一個屬性。
nl_groups:若是用戶空間的進程但願加入某個多播組,則必須執行bind()系統調用。該字段指明瞭調用者但願加入的多播組號的掩碼(注意不是組號,後面咱們會詳細講解這個字段)。若是該字段爲0則表示調用者不但願加入任何多播組。對於每一個隸屬於Netlink協議域的協議,最多可支持32個多播組(由於nl_groups的長度爲32比特),每一個多播組用一個比特來表示。
關於Netlink剩下的知識點,咱們在後面的實戰環節有用到時再討論。
未完,待續…