用戶空間和內(nèi)核空間通訊之【Netlink 上】

轉(zhuǎn)載:http://blog.chinaunix.net/uid-23069658-id-3400761.html

引言

Alan Cox在內(nèi)核1.3版本的開發(fā)階段最先引入了Netlink圆到,剛開始時(shí)Netlink是以字符驅(qū)動(dòng)接口的方式提供內(nèi)核與用戶空間的雙向數(shù)據(jù)通信震檩;隨后,在2.1內(nèi)核開發(fā)過程中是目,Alexey Kuznetsov將Netlink改寫成一個(gè)更加靈活妹萨、且易于擴(kuò)展的基于消息通信接口年枕,并將其應(yīng)用到高級(jí)路由子系統(tǒng)的基礎(chǔ)框架里。自那時(shí)起乎完,Netlink就成了Linux內(nèi)核子系統(tǒng)和用戶態(tài)的應(yīng)用程序通信的主要手段之一熏兄。

2001年,F(xiàn)orCES IETF委員會(huì)正式對(duì)Netlink進(jìn)行了標(biāo)準(zhǔn)化的工作树姨。Jamal Hadi Salim提議將Netlink定義成一種用于網(wǎng)絡(luò)設(shè)備的路由引擎組件和其控制管理組件之間通信的協(xié)議摩桶。不過他的建議最終沒有被采納,取而代之的是我們今天所看到的格局:Netlink被設(shè)計(jì)成一個(gè)新的協(xié)議域娃弓,domain典格。

Linux之父托瓦斯曾說過“Linux is evolution, not intelligent design”。什么意思台丛?就是說耍缴,Netlink也同樣遵循了Linux的某些設(shè)計(jì)理念砾肺,即沒有完整的規(guī)范文檔,亦沒有設(shè)計(jì)文檔防嗡。只有什么变汪?你懂得---“Read the f**king source code”。

當(dāng)然蚁趁,本文不是分析Netlink在Linux上的實(shí)現(xiàn)機(jī)制裙盾,而是就“什么是Netlink”以及“如何用好Netlink”的話題和大家做個(gè)分享,只有在遇到問題時(shí)才需要去閱讀內(nèi)核源碼弄清個(gè)所以然他嫡。

什么是Netlink

關(guān)于Netlink的理解番官,需要把握幾個(gè)關(guān)鍵點(diǎn):

1、面向數(shù)據(jù)報(bào)的無連接消息子系統(tǒng)

2钢属、基于通用的BSD Socket架構(gòu)而實(shí)現(xiàn)

關(guān)于第一點(diǎn)使我們很容易聯(lián)想到UDP協(xié)議徘熔,能想到這一點(diǎn)就非常棒了。按著UDP協(xié)議來理解Netlink不是不無道理淆党,只要你能觸類旁通酷师,做到“活學(xué)”,善于總結(jié)歸納染乌、聯(lián)想,最后實(shí)現(xiàn)知識(shí)遷移這就是學(xué)習(xí)的本質(zhì)荷憋。Netlink可以實(shí)現(xiàn)內(nèi)核->用戶以及用戶->內(nèi)核的雙向勒庄、異步的數(shù)據(jù)通信贼邓,同時(shí)它還支持兩個(gè)用戶進(jìn)程之間塑径、甚至兩個(gè)內(nèi)核子系統(tǒng)之間的數(shù)據(jù)通信。本文中肋拔,對(duì)后兩者我們不予考慮锈津,焦點(diǎn)集中在如何實(shí)現(xiàn)用戶<->內(nèi)核之間的數(shù)據(jù)通信。

看到第二點(diǎn)腦海中是不是瞬間閃現(xiàn)了下面這張圖片呢只损?如果是一姿,則說明你確實(shí)有慧根;當(dāng)然跃惫,不是也沒關(guān)系叮叹,慧根可以慢慢長嘛,呵呵爆存。

在后面實(shí)戰(zhàn)Netlink套接字編程時(shí)我們主要會(huì)用到socket()蛉顽,bind(),sendmsg()

和recvmsg()等系統(tǒng)調(diào)用先较,當(dāng)然還有socket提供的輪訓(xùn)(polling)機(jī)制携冤。

Netlink通信類型

Netlink支持兩種類型的通信方式:?jiǎn)尾ズ投嗖ァ?/p>

單播:經(jīng)常用于一個(gè)用戶進(jìn)程和一個(gè)內(nèi)核子系統(tǒng)之間1:1的數(shù)據(jù)通信。用戶空間發(fā)送命令到內(nèi)核闲勺,然后從內(nèi)核接受命令的返回結(jié)果曾棕。

多播:經(jīng)常用于一個(gè)內(nèi)核進(jìn)程和多個(gè)用戶進(jìn)程之間的1:N的數(shù)據(jù)通信。內(nèi)核作為會(huì)話的發(fā)起者菜循,用戶空間的應(yīng)用程序是接收者翘地。為了實(shí)現(xiàn)這個(gè)功能,內(nèi)核空間的程序會(huì)創(chuàng)建一個(gè)多播組癌幕,然后所有用戶空間的對(duì)該內(nèi)核進(jìn)程發(fā)送的消息感興趣的進(jìn)程都加入到該組即可接收來自內(nèi)核發(fā)送的消息了衙耕。如下:

其中進(jìn)程A和子系統(tǒng)1之間是單播通信,進(jìn)程B勺远、C和子系統(tǒng)2是多播通信橙喘。上圖還向我們說明了一個(gè)信息。從用戶空間傳遞到內(nèi)核的數(shù)據(jù)是不需要排隊(duì)的胶逢,即其操作是同步完成厅瞎;而從內(nèi)核空間向用戶空間傳遞數(shù)據(jù)時(shí)需要排隊(duì)饰潜,是異步的。了解了這一點(diǎn)在開發(fā)基于Netlink的應(yīng)用模塊時(shí)可以使我們少走很多彎路磁奖。假如囊拜,你向內(nèi)核發(fā)送了一個(gè)消息需要獲取內(nèi)核中某些信息,比如路由表比搭,或其他信息冠跷,如果路由表過于龐大,那么內(nèi)核在通過Netlink向你返回?cái)?shù)據(jù)時(shí)身诺,你可以好生琢磨一下如何接收這些數(shù)據(jù)的問題蜜托,畢竟你已經(jīng)看到了那個(gè)輸出隊(duì)列了,不能視而不見啊霉赡。

Netlink的消息格式

Netlink消息由兩部分組成:消息頭和有效數(shù)據(jù)載荷橄务,且整個(gè)Netlink消息是4字節(jié)對(duì)齊,一般按主機(jī)字節(jié)序進(jìn)行傳遞穴亏。消息頭為固定的16字節(jié)蜂挪,消息體長度可變:

Netlink的消息頭

消息頭定義在文件里,由結(jié)構(gòu)體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:整個(gè)消息的長度嗓化,按字節(jié)計(jì)算棠涮。包括了Netlink消息頭本身。

nlmsg_type:消息的類型刺覆,即是數(shù)據(jù)還是控制消息严肪。目前(內(nèi)核版本2.6.21)Netlink僅支持四種類型的控制消息,如下:

  • NLMSG_NOOP-空消息谦屑,什么也不做驳糯;

  • NLMSG_ERROR-指明該消息中包含一個(gè)錯(cuò)誤;

  • NLMSG_DONE-如果內(nèi)核通過Netlink隊(duì)列返回了多個(gè)消息氢橙,那么隊(duì)列的最后一條消息的類型為NLMSG_DONE酝枢,其余所有消息的nlmsg_flags屬性都被設(shè)置 - -

  • NLM_F_MULTI位有效。

  • NLMSG_OVERRUN-暫時(shí)沒用到悍手。

nlmsg_flags:附加在消息上的額外說明信息隧枫,如上面提到的NLM_F_MULTI。摘錄如下:

標(biāo)記 作用及說明
NLM_F_REQUEST 如果消息中有該標(biāo)記位谓苟,說明這是一個(gè)請(qǐng)求消息。所有從用戶空間到內(nèi)核空間的消息都要設(shè)置該位协怒,否則內(nèi)核將向用戶返回一個(gè)EINVAL無效參數(shù)的錯(cuò)誤
NLM_F_MULTI 消息從用戶->內(nèi)核是同步的立刻完成涝焙,而從內(nèi)核->用戶則需要排隊(duì)。如果內(nèi)核之前收到過來自用戶的消息中有NLM_F_DUMP位為1的消息孕暇,那么內(nèi)核就會(huì)向用戶空間發(fā)送一個(gè)由多個(gè)Netlink消息組成的鏈表仑撞。除了最后個(gè)消息外赤兴,其余每條消息中都設(shè)置了該位有效。
NLM_F_ACK 該消息是內(nèi)核對(duì)來自用戶空間的NLM_F_REQUEST消息的響應(yīng)
NLM_F_ECHO 如果從用戶空間發(fā)給內(nèi)核的消息中該標(biāo)記為1隧哮,則說明用戶的應(yīng)用進(jìn)程要求內(nèi)核將用戶發(fā)給它的每條消息通過單播的形式再發(fā)送給用戶進(jìn)程桶良。和我們通常說的“回顯”功能類似。

大家只要知道nlmsg_flags有多種取值就可以沮翔,至于每種值的作用和意義陨帆,通過谷歌和源代碼一定可以找到答案,這里就不展開了采蚀。上一張2.6.21內(nèi)核中所有的取值情況:

nlmsg_seq:消息序列號(hào)疲牵。因?yàn)镹etlink是面向數(shù)據(jù)報(bào)的,所以存在丟失數(shù)據(jù)的風(fēng)險(xiǎn)榆鼠,但是Netlink提供了如何確保消息不丟失的機(jī)制纲爸,讓程序開發(fā)人員根據(jù)其實(shí)際需求而實(shí)現(xiàn)。消息序列號(hào)一般和NLM_F_ACK類型的消息聯(lián)合使用妆够,如果用戶的應(yīng)用程序需要保證其發(fā)送的每條消息都成功被內(nèi)核收到的話识啦,那么它發(fā)送消息時(shí)需要用戶程序自己設(shè)置序號(hào),內(nèi)核收到該消息后對(duì)提取其中的序列號(hào)神妹,然后在發(fā)送給用戶程序回應(yīng)消息里設(shè)置同樣的序列號(hào)颓哮。有點(diǎn)類似于TCP的響應(yīng)和確認(rèn)機(jī)制。

注意:當(dāng)內(nèi)核主動(dòng)向用戶空間發(fā)送廣播消息時(shí)灾螃,消息中的該字段總是為0题翻。

nlmsg_pid:當(dāng)用戶空間的進(jìn)程和內(nèi)核空間的某個(gè)子系統(tǒng)之間通過Netlink建立了數(shù)據(jù)交換的通道后,Netlink會(huì)為每個(gè)這樣的通道分配一個(gè)唯一的數(shù)字標(biāo)識(shí)腰鬼。其主要作用就是將來自用戶空間的請(qǐng)求消息和響應(yīng)消息進(jìn)行關(guān)聯(lián)嵌赠。說得直白一點(diǎn),假如用戶空間存在多個(gè)用戶進(jìn)程熄赡,內(nèi)核空間同樣存在多個(gè)進(jìn)程姜挺,Netlink必須提供一種機(jī)制用于確保每一對(duì)“用戶-內(nèi)核”空間通信的進(jìn)程之間的數(shù)據(jù)交互不會(huì)發(fā)生紊亂。

即彼硫,進(jìn)程A炊豪、B通過Netlink向子系統(tǒng)1獲取信息時(shí),子系統(tǒng)1必須確迸±海回送給進(jìn)程A的響應(yīng)數(shù)據(jù)不會(huì)發(fā)到進(jìn)程B那里词渤。主要適用于用戶空間的進(jìn)程從內(nèi)核空間獲取數(shù)據(jù)的場(chǎng)景。通常情況下串绩,用戶空間的進(jìn)程在向內(nèi)核發(fā)送消息時(shí)一般通過系統(tǒng)調(diào)用getpid()將當(dāng)前進(jìn)程的進(jìn)程號(hào)賦給該變量缺虐,即用戶空間的進(jìn)程希望得到內(nèi)核的響應(yīng)時(shí)才會(huì)這么做。從內(nèi)核主動(dòng)發(fā)送到用戶空間的消息該字段都被設(shè)置為0礁凡。

Netlink的消息體

Netlink的消息體采用TLV(Type-Length-Value)格式:

Netlink每個(gè)屬性都由文件里的struct nlattr{}來表示:

Netlink提供的錯(cuò)誤指示消息

當(dāng)用戶空間的應(yīng)用程序和內(nèi)核空間的進(jìn)程之間通過Netlink通信時(shí)發(fā)生了錯(cuò)誤高氮,Netlink必須向用戶空間通報(bào)這種錯(cuò)誤慧妄。Netlink對(duì)錯(cuò)誤消息進(jìn)行了單獨(dú)封裝,:

1.  struct nlmsgerr

2.  {

3.  int        error; //標(biāo)準(zhǔn)的錯(cuò)誤碼剪芍,定義在errno.h頭文件中塞淹。可以用perror()來解釋

4.  struct nlmsghdr msg; //指明了哪條消息觸發(fā)了結(jié)構(gòu)體中error這個(gè)錯(cuò)誤值

5.  };

Netlink編程需要注意的問題

基于Netlink的用戶-內(nèi)核通信罪裹,有兩種情況可能會(huì)導(dǎo)致丟包:

1饱普、內(nèi)存耗盡;

2坊谁、用戶空間接收進(jìn)程的緩沖區(qū)溢出费彼。導(dǎo)致緩沖區(qū)溢出的主要原因有可能是:用戶空間的進(jìn)程運(yùn)行太慢;或者接收隊(duì)列太短口芍。

如果Netlink不能將消息正確傳遞到用戶空間的接收進(jìn)程箍铲,那么用戶空間的接收進(jìn)程在調(diào)用recvmsg()系統(tǒng)調(diào)用時(shí)就會(huì)返回一個(gè)內(nèi)存不足(ENOBUFS)的錯(cuò)誤,這一點(diǎn)需要注意鬓椭。換句話說颠猴,緩沖區(qū)溢出的情況是不會(huì)發(fā)送在從用戶->內(nèi)核的sendmsg()系統(tǒng)調(diào)用里,原因前面我們也說過了小染,請(qǐng)大家自己思考一下翘瓮。

當(dāng)然,如果使用的是阻塞型socket通信裤翩,也就不存在內(nèi)存耗盡的隱患了资盅,這又是為什么呢?趕緊去谷歌一下踊赠,查查什么是阻塞型socket吧呵扛。學(xué)而不思則罔,思而不學(xué)則殆嘛筐带。

Netlink的地址結(jié)構(gòu)體

在TCP博文中我們提到過在Internet編程過程中所用到的地址結(jié)構(gòu)體和標(biāo)準(zhǔn)地址結(jié)構(gòu)體今穿,它們和Netlink地址結(jié)構(gòu)體的關(guān)系如下:

struct sockaddr_nl{}的詳細(xì)定義和描述如下:

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:該屬性為發(fā)送或接收消息的進(jìn)程ID伦籍,前面我們也說過蓝晒,Netlink不僅可以實(shí)現(xiàn)用戶-內(nèi)核空間的通信還可使現(xiàn)實(shí)用戶空間兩個(gè)進(jìn)程之間,或內(nèi)核空間兩個(gè)進(jìn)程之間的通信帖鸦。該屬性為0時(shí)一般適用于如下兩種情況:

第一芝薇,我們要發(fā)送的目的地是內(nèi)核,即從用戶空間發(fā)往內(nèi)核空間時(shí)作儿,我們構(gòu)造的Netlink地址結(jié)構(gòu)體中nl_pid通常情況下都置為0剩燥。這里有一點(diǎn)需要跟大家交代一下,在Netlink規(guī)范里,PID全稱是Port-ID(32bits)灭红,其主要作用是用于唯一的標(biāo)識(shí)一個(gè)基于netlink的socket通道。通常情況下nl_pid都設(shè)置為當(dāng)前進(jìn)程的進(jìn)程號(hào)口注。然而变擒,對(duì)于一個(gè)進(jìn)程的多個(gè)線程同時(shí)使用netlink socket的情況,nl_pid的設(shè)置一般采用如下這個(gè)樣子來實(shí)現(xiàn):

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

第二寝志,從內(nèi)核發(fā)出的多播報(bào)文到用戶空間時(shí)娇斑,如果用戶空間的進(jìn)程處在該多播組中,那么其地址結(jié)構(gòu)體中nl_pid也設(shè)置為0材部,同時(shí)還要結(jié)合下面介紹到的另一個(gè)屬性毫缆。

nl_groups:如果用戶空間的進(jìn)程希望加入某個(gè)多播組,則必須執(zhí)行bind()系統(tǒng)調(diào)用乐导。該字段指明了調(diào)用者希望加入的多播組號(hào)的<u style="overflow-wrap: break-word;">掩碼</u>(注意不是組號(hào)苦丁,后面我們會(huì)詳細(xì)講解這個(gè)字段)。如果該字段為0則表示調(diào)用者不希望加入任何多播組物臂。對(duì)于每個(gè)隸屬于Netlink協(xié)議域的協(xié)議旺拉,最多可支持32個(gè)多播組(因?yàn)閚l_groups的長度為32比特),每個(gè)多播組用一個(gè)比特來表示棵磷。

關(guān)于Netlink剩下的知識(shí)點(diǎn)蛾狗,我們?cè)诤竺娴膶?shí)戰(zhàn)環(huán)節(jié)有用到時(shí)再討論。

未完仪媒,待續(xù)…

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末沉桌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子算吩,更是在濱河造成了極大的恐慌留凭,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赌莺,死亡現(xiàn)場(chǎng)離奇詭異冰抢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)艘狭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門挎扰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人巢音,你說我怎么就攤上這事遵倦。” “怎么了官撼?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵梧躺,是天一觀的道長。 經(jīng)常有香客問我,道長掠哥,這世上最難降的妖魔是什么巩踏? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮续搀,結(jié)果婚禮上塞琼,老公的妹妹穿的比我還像新娘。我一直安慰自己禁舷,他們只是感情好彪杉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著牵咙,像睡著了一般派近。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上洁桌,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天渴丸,我揣著相機(jī)與錄音,去河邊找鬼战坤。 笑死曙强,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的途茫。 我是一名探鬼主播碟嘴,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼囊卜!你這毒婦竟也來了娜扇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤栅组,失蹤者是張志新(化名)和其女友劉穎雀瓢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體玉掸,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡刃麸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了司浪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片泊业。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖啊易,靈堂內(nèi)的尸體忽然破棺而出吁伺,到底是詐尸還是另有隱情,我是刑警寧澤租谈,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布篮奄,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏窟却。R本人自食惡果不足惜昼丑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望夸赫。 院中可真熱鬧矾克,春花似錦、人聲如沸憔足。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽滓彰。三九已至,卻和暖如春州袒,著一層夾襖步出監(jiān)牢的瞬間揭绑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來泰國打工郎哭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留他匪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓夸研,卻偏偏與公主長得像邦蜜,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子亥至,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355