使用者空間和核心空間通訊之【Netlink 上】

來源:互聯網
上載者:User

標籤:lex   之間   執行   興趣   標記   計算   style   self   標頭檔   

原文地址:使用者空間和核心空間通訊之【Netlink 上】 wjlkoorey258

引言

         Alan Cox在核心1.3版本的開發階段最先引入了Netlink,剛開始時Netlink是以字元驅動介面的方式提供核心與使用者空間的雙向資料通訊;隨後,在2.1核心開發過程中,Alexey Kuznetsov將Netlink改寫成一個更加靈活、且易於擴充的基於訊息通訊介面,並將其應用到進階路由子系統的基礎架構裡。自那時起,Netlink就成了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的理解,需要把握幾個關鍵點:

       1、面向資料報的無串連訊息子系統

       2、基於通用的BSD Socket架構而實現

      關於第一點使我們很容易聯想到UDP協議,能想到這一點就非常棒了。按著UDP協議來理解Netlink不是不無道理,只要你能觸類旁通,做到“活學”,善於總結歸納、聯想,最後實現知識遷移這就是學習的本質。Netlink可以實現核心->使用者以及使用者->核心的雙向、非同步資料通訊,同時它還支援兩個使用者進程之間、甚至兩個核心子系統之間的資料通訊。本文中,對後兩者我們不予考慮,焦點集中在如何?使用者<->核心之間的資料通訊。

      看到第二點腦海中是不是瞬間閃現了下面這張圖片呢?如果是,則說明你確實有慧根;當然,不是也沒關係,慧根可以慢慢長嘛,呵呵。

     在後面實戰Netlink通訊端編程時我們主要會用到socket(),bind(),sendmsg()和recvmsg()等系統調用,當然還有socket提供的輪訓(polling)機制。         

Netlink通訊類型

      Netlink支援兩種類型的通訊方式:單播和多播。

      單播:經常用於一個使用者進程和一個核心子系統之間1:1的資料通訊。使用者空間發送命令到核心,然後從核心接受命令的返回結果。

      多播:經常用於一個核心進程和多個使用者進程之間的1:N的資料通訊。核心作為會話的發起者,使用者空間的應用程式是接收者。為了實現這個功能,核心空間的程式會建立一個多播組,然後所有使用者空間的對該核心進程發送的訊息感興趣的進程都加入到該組即可接收來自核心發送的訊息了。如下:     其中進程A和子系統1之間是單播通訊,進程B、C和子系統2是多播通訊。還向我們說明了一個資訊。從使用者空間傳遞到核心的資料是不需要排隊的,即其操作是同步完成;而從核心空間向使用者空間傳遞資料時需要排隊,是非同步。瞭解了這一點在開發基於Netlink的應用模組時可以使我們少走很多彎路。假如,你向核心發送了一個訊息需要擷取核心中某些資訊,比如路由表,或其他資訊,如果路由表過於龐大,那麼核心在通過Netlink向你返回資料時,你可以好生琢磨一下如何接收這些資料的問題,畢竟你已經看到了那個輸出隊列了,不能視而不見啊。  

Netlink的訊息格式

       Netlink訊息由兩部分組成:訊息頭和有效資料載荷,且整個Netlink訊息是4位元組對齊,一般按主機位元組序進行傳遞。訊息頭為固定的16位元組,訊息體長度可變: 

Netlink的訊息頭

      訊息頭定義在<include/linux/netlink.h>檔案裡,由結構體nlmsghdr表示:

點擊(此處)摺疊或開啟

  1. struct nlmsghdr
  2. {
  3.     __u32        nlmsg_len;    /* Length of message including header */
  4.     __u16        nlmsg_type;    /* Message content */
  5.     __u16        nlmsg_flags;    /* Additional flags */
  6.     __u32        nlmsg_seq;    /* Sequence number */
  7.     __u32        nlmsg_pid;    /* Sending process PID */
  8. };

      訊息頭中各成員屬性的解釋及說明:

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_flags有多種取值就可以,至於每種值的作用和意義,通過Google和原始碼一定可以找到答案,這裡就不展開了。上一張2.6.21核心中所有的取值情況: 

nlmsg_seq:訊息序號。因為Netlink是面向資料報的,所以存在遺失資料的風險,但是Netlink提供了如何確保訊息不丟失的機制,讓程式開發人員根據其實際需求而實現。訊息序號一般和NLM_F_ACK類型的訊息聯合使用,如果使用者的應用程式需要保證其發送的每條訊息都成功被核心收到的話,那麼它發送訊息時需要使用者程式自己設定序號,核心收到該訊息後對提取其中的序號,然後在發送給使用者程式回應訊息裡設定同樣的序號。有點類似於TCP的響應和確認機制。

注意:當核心主動向使用者空間發送廣播訊息時,訊息中的該欄位總是為0。

 

nlmsg_pid:當使用者空間的進程和核心空間的某個子系統之間通過Netlink建立了資料交換的通道後,Netlink會為每個這樣的通道分配一個唯一的數位識別碼。其主要作用就是將來自使用者空間的請求訊息和響應訊息進行關聯。說得直白一點,假如使用者空間存在多個使用者進程,核心空間同樣存在多個進程,Netlink必須提供一種機制用於確保每一對“使用者-核心”空間通訊的進程之間的資料互動不會發生紊亂。     即,進程A、B通過Netlink向子系統1擷取資訊時,子系統1必須確保回送給進程A的響應資料不會發到進程B那裡。主要適用於使用者空間的進程從核心空間擷取資料的情境。通常情況下,使用者空間的進程在向核心發送訊息時一般通過系統調用getpid()將當前進程的進程號賦給該變數,即使用者空間的進程希望得到核心的響應時才會這麼做。從核心主動發送到使用者空間的訊息該欄位都被設定為0。 

Netlink的訊息體

      Netlink的訊息體採用TLV(Type-Length-Value)格式:       Netlink每個屬性都由<include/linux/netlink.h>檔案裡的 struct nlattr{}來表示: 

 

Netlink提供的錯誤指示訊息

      當使用者空間的應用程式和核心空間的進程之間通過Netlink通訊時發生了錯誤,Netlink必須向使用者空間通報這種錯誤。Netlink對錯誤訊息進行了單獨封裝,<include/linux/netlink.h>:

點擊(此處)摺疊或開啟

  1. struct nlmsgerr
  2. {
  3.     int        error; //標準的錯誤碼,定義在errno.h標頭檔中。可以用perror()來解釋
  4.     struct nlmsghdr msg; //指明了哪條訊息觸發了結構體中error這個錯誤值
  5. };

 

Netlink編程需要注意的問題

      基於Netlink的使用者-核心通訊,有兩種情況可能會導致丟包:

      1、記憶體耗盡;

      2、使用者空間接收進程的緩衝區溢位。導致緩衝區溢位的主要原因有可能是:使用者空間的進程運行太慢;或者接收隊列太短。

      如果Netlink不能將訊息正確傳遞到使用者空間的接收進程,那麼使用者空間的接收進程在調用recvmsg()系統調用時就會返回一個記憶體不足(ENOBUFS)的錯誤,這一點需要注意。換句話說,緩衝區溢位的情況是不會發送在從使用者->核心的sendmsg()系統調用裡,原因前面我們也說過了,請大家自己思考一下。

      當然,如果使用的是阻塞型socket通訊,也就不存在記憶體耗盡的隱患了,這又是為什麼呢?趕緊去Google一下,查查什麼是阻塞型socket吧。學而不思則罔,思而不學則殆嘛。

 

Netlink的地址結構體

      在TCP博文中我們提到過在Internet編程過程中所用到的地址結構體和標準地址結構體,它們和Netlink地址結構體的關係如下:  

    struct sockaddr_nl{}的詳細定義和描述如下:

點擊(此處)摺疊或開啟

  1. struct sockaddr_nl
  2. {
  3.     sa_family_t    nl_family;    /*該欄位總是為AF_NETLINK    */
  4.     unsigned short    nl_pad;        /* 目前未用到,填充為0*/
  5.     __u32        nl_pid;        /* process pid    */
  6.     __u32        nl_groups;    /* multicast groups mask */
  7. };

nl_pid:該屬性為發送或接收訊息的進程ID,前面我們也說過,Netlink不僅可以實現使用者-核心空間的通訊還可使現實使用者空間兩個進程之間,或核心空間兩個進程之間的通訊。該屬性為0時一般適用於如下兩種情況:

        第一,我們要發送的目的地是核心,即從使用者空間發往核心空間時,我們構造的Netlink地址結構體中nl_pid通常情況下都置為0。這裡有一點需要跟大家交代一下,在Netlink規範裡,PID全稱是Port-ID(32bits),其主要作用是用於唯一的標識一個基於netlink的socket通道。通常情況下nl_pid都設定為當前進程的進程號。然而,對於一個進程的多個線程同時使用netlink socket的情況,nl_pid的設定一般採用如下這個樣子來實現:

點擊(此處)摺疊或開啟

  1. pthread_self() << 16 | getpid();

       第二,從核心發出的多播報文到使用者空間時,如果使用者空間的進程處在該多播組中,那麼其地址結構體中nl_pid也設定為0,同時還要結合下面介紹到的另一個屬性。

nl_groups:如果使用者空間的進程希望加入某個多播組,則必須執行bind()系統調用。該欄位指明了調用者希望加入的多播組號的掩碼(注意不是組號,後面我們會詳細講解這個欄位)。如果該欄位為0則表示調用者不希望加入任何多播組。對於每個隸屬於Netlink協議域的協議,最多可支援32個多播組(因為nl_groups的長度為32位元),每個多播組用一個位元來表示。 

       關於Netlink剩下的知識點,我們在後面的實戰環節有用到時再討論。

       未完,待續…

使用者空間和核心空間通訊之【Netlink 上】

相關關鍵詞:
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.