1. 程式人生 > 實用技巧 >linux使用者空間與核心空間通訊——Netlink通訊機制【轉】

linux使用者空間與核心空間通訊——Netlink通訊機制【轉】

轉自:https://blog.csdn.net/zhao_h/article/details/80943226

一:什麼是Netlink通訊機制

Netlink是linux提供的用於核心和使用者態程序之間的通訊方式。

但是注意雖然Netlink主要用於使用者空間和核心空間的通訊,但是也能用於使用者空間的兩個程序通訊。只是程序間通訊有其他很多

方式,一般不用Netlink。除非需要用到Netlink的廣播特性時。

那麼Netlink有什麼優勢呢?

一般來說使用者空間和核心空間的通訊方式有三種:/proc、ioctl、Netlink。而前兩種都是單向的,但是Netlink可以實現雙工通訊。

Netlink協議基於BSD socket和AF_NETLINK地址簇(address family),使用32位的埠號定址(以前稱作PID),每個Netlink協議

(或稱作匯流排,man手冊中則稱之為netlink family),通常與一個或一組核心服務/元件相關聯,如NETLINK_ROUTE用於獲取和

設定路由與鏈路資訊、NETLINK_KOBJECT_UEVENT用於核心向用戶空間的udev程序傳送通知等。

netlink具有以下特點:

① 支援全雙工、非同步通訊(當然同步也支援)

② 使用者空間可使用標準的BSD socket介面(但netlink並沒有遮蔽掉協議包的構造與解析過程,推薦使用libnl等第三方庫)

③ 在核心空間使用專用的核心API介面

④ 支援多播(因此支援“匯流排”式通訊,可實現訊息訂閱)

⑤ 在核心端可用於程序上下文與中斷上下文

二:使用者態資料結構

首先看一下幾個重要的資料結構的關係:

1.struct msghdr

msghdr這個結構在socket變成中就會用到,並不算Netlink專有的,這裡不在過多說明。只說明一下如何更好理解這個結構的功能。我們

知道socket訊息的傳送和接收函式一般有這幾對:recv/send、readv/writev、recvfrom/sendto。當然還有recvmsg/sendmsg,

前面三對函式各有各的特點功能,而recvmsg/sendmsg就是要囊括前面三對的所有功能,當然還有自己特殊的用途。msghdr的前兩個

成員就是為了滿足recvfrom/sendto的功能,中間兩個成員msg_iov和msg_iovlen則是為了滿足readv/writev的功能,而最後的

msg_flags則是為了滿足recv/send中flag的功能,剩下的msg_control和msg_controllen則是滿足recvmsg/sendmsg特有的功能。

2.struct sockaddr_ln

struct sockaddr_ln為Netlink的地址,和我們通常socket程式設計中的sockaddr_in作用一樣,他們的結構對比如下:

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. };

(1) nl_pid:在Netlink規範裡,PID全稱是Port-ID(32bits),其主要作用是用於唯一的標識一個基於netlink的socket通道。通常

情況下nl_pid都設定為當前程序的程序號。前面我們也說過,Netlink不僅可以實現使用者-核心空間的通訊還可使現實使用者空間兩

個程序之間,或核心空間兩個程序之間的通訊。該屬性為0時一般指核心。

(2) nl_groups:如果使用者空間的程序希望加入某個多播組,則必須執行bind()系統呼叫。該欄位指明瞭呼叫者希望加入的多播組

號的掩碼(注意不是組號,後面我們會詳細講解這個欄位)。如果該欄位為0則表示呼叫者不希望加入任何多播組。對於每個隸屬於

Netlink協議域的協議,最多可支援32個多播組(因為nl_groups的長度為32位元),每個多播組用一個位元來表示。

3.struct nlmsghdr

Netlink的報文由訊息頭和訊息體構成,struct nlmsghdr即為訊息頭。訊息頭定義在檔案裡,由結構體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. };

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

(1) nlmsg_len:整個訊息的長度,按位元組計算。包括了Netlink訊息頭本身。

(2) nlmsg_type:訊息的型別,即是資料還是控制訊息。目前(核心版本2.6.21)Netlink僅支援四種類型的控制訊息,如下:

a) NLMSG_NOOP-空訊息,什麼也不做;

b) NLMSG_ERROR-指明該訊息中包含一個錯誤;

c) NLMSG_DONE-如果核心通過Netlink佇列返回了多個訊息,那麼佇列的最後一條訊息的型別為NLMSG_DONE,其餘所有訊息的nlmsg_flags屬性都被設定NLM_F_MULTI位有效。

d) NLMSG_OVERRUN-暫時沒用到。

(3) nlmsg_flags:附加在訊息上的額外說明資訊,如上面提到的NLM_F_MULTI。

三:使用者空間Netlink socket API

1.建立socket

int socket(int domain, int type, int protocol)

domain指代地址族,即AF_NETLINK;

套接字型別為SOCK_RAW或SOCK_DGRAM,因為netlink是一個面向資料報的服務;

protocol選擇該套接字使用哪種netlink特徵。

以下是幾種預定義的協議型別:

NETLINK_ROUTE,

NETLINK_FIREWALL,

NETLINK_APRD,

NETLINK_ROUTE6_FW。

可以非常容易的新增自己的netlink協議。

為每一個協議型別最多可以定義32個多播組。

每一個多播組用一個bitmask來表示,1<<i(0<=i<= 31),這在一組程序和核心程序協同完成一項任務時非常有用。傳送多播

netlink訊息可以減少系統呼叫的數量,同時減少用來維護多播組成員資訊的負擔。

2.地址繫結bind()

bind(fd, (struct sockaddr*)&, nladdr, sizeof(nladdr));

3.傳送netlink訊息

為了傳送一條netlink訊息到核心或者其他的使用者空間程序,另外一個struct sockaddr_nl nladdr需要作為目的地址,這和使用

sendmsg()傳送一個UDP包是一樣的。

如果該訊息是傳送至核心的,那麼nl_pid和nl_groups都置為0.

如果訊息是傳送給另一個程序的單播訊息,nl_pid是另外一個程序的pid值而nl_groups為零。

如果訊息是傳送給一個或多個多播組的多播訊息,所有的目的多播組必須bitmask必須or起來從而形成nl_groups域。

sendmsg(fd, &, msg, 0);

4.接收netlink訊息

一個接收程式必須分配一個足夠大的記憶體用於儲存netlink訊息頭和訊息負載。然後其填充struct msghdr msg,再使用標準的

recvmsg()函式來接收netlink訊息。

當訊息被正確的接收之後,nlh應該指向剛剛接收到的netlink訊息的頭。nladdr應該包含接收訊息的目的地址,其中包括了訊息傳送

者的pid和多播組。同時,巨集NLMSG_DATA(nlh),定義在netlink.h中,返回一個指向netlink訊息負載的指標。呼叫close(fd)關閉fd

描述符所標識的socket。

recvmsg(fd, &, msg, 0);

四:核心空間Netlink socket API

1.建立 netlink socket

  1. struct sock *netlink_kernel_create(struct net *net,
  2. int unit,unsigned int groups,
  3. void (*input)(struct sk_buff *skb),
  4. struct mutex *cb_mutex,struct module *module);

引數說明:

(1) net:是一個網路名字空間namespace,在不同的名字空間裡面可以有自己的轉發資訊庫,有自己的一套net_device等等。

預設情況下都是使用 init_net這個全域性變數。

(2) unit:表示netlink協議型別,如NETLINK_TEST、NETLINK_SELINUX。

(3) groups:多播地址。

(4) input:為核心模組定義的netlink訊息處理函式,當有消 息到達這個netlink socket時,該input函式指標就會被引用,且只

有此函式返回時,呼叫者的sendmsg才能返回。

(5) cb_mutex:為訪問資料時的互斥訊號量。

(6) module: 一般為THIS_MODULE。

2.傳送單播訊息 netlink_unicast

int netlink_unicast(struct sock *ssk, struct sk_buff *skb, u32 pid, int nonblock)

引數說明:

(1) ssk:為函式 netlink_kernel_create()返回的socket。

(2) skb:存放訊息,它的data欄位指向要傳送的netlink訊息結構,而 skb的控制塊儲存了訊息的地址資訊,巨集

NETLINK_CB(skb)就用於方便設定該控制塊。

(3) pid:為接收此訊息程序的pid,即目標地址,如果目標為組或核心,它設定為 0。

(4) nonblock:表示該函式是否為非阻塞,如果為1,該函式將在沒有接收快取可利用時立即返回;而如果為0,該函式在沒有接

收快取可利用定時睡眠。

3.傳送廣播訊息 netlink_broadcast

  1. int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, u32 group, gfp_t allocation)

前面的三個引數與 netlink_unicast相同,引數group為接收訊息的多播組,該引數的每一個位代表一個多播組,因此如果傳送給

多個多播組,就把該引數設定為多個多播組組ID的位或。引數allocation為核心記憶體分配型別,一般地為GFP_ATOMIC或

GFP_KERNEL,GFP_ATOMIC用於原子的上下文(即不可以睡眠),而GFP_KERNEL用於非原子上下文。

4.釋放 netlink socket

int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, u32 group, gfp_t allocation)

五:使用者態範例一

  1. #include <sys/stat.h>
  2. #include <unistd.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <sys/socket.h>
  6. #include <sys/types.h>
  7. #include <string.h>
  8. #include <asm/types.h>
  9. #include <linux/netlink.h>
  10. #include <linux/socket.h>
  11. #include <errno.h>
  12. #define MAX_PAYLOAD 1024 // maximum payload size
  13. #define NETLINK_TEST 25 //自定義的協議
  14. int main(int argc, char* argv[])
  15. {
  16. int state;
  17. struct sockaddr_nl src_addr, dest_addr;
  18. struct nlmsghdr *nlh = NULL; //Netlink資料包頭
  19. struct iovec iov;
  20. struct msghdr msg;
  21. int sock_fd, retval;
  22. int state_smg = 0;
  23. // Create a socket
  24. sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
  25. if(sock_fd == -1){
  26. printf("error getting socket: %s", strerror(errno));
  27. return -1;
  28. }
  29. // To prepare binding
  30. memset(&src_addr, 0, sizeof(src_addr));
  31. src_addr.nl_family = AF_NETLINK;
  32. src_addr.nl_pid = 100; //A:設定源端埠號
  33. src_addr.nl_groups = 0;
  34. //Bind
  35. retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
  36. if(retval < 0){
  37. printf("bind failed: %s", strerror(errno));
  38. close(sock_fd);
  39. return -1;
  40. }
  41. // To orepare create mssage
  42. nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
  43. if(!nlh){
  44. printf("malloc nlmsghdr error!\n");
  45. close(sock_fd);
  46. return -1;
  47. }
  48. memset(&dest_addr,0,sizeof(dest_addr));
  49. dest_addr.nl_family = AF_NETLINK;
  50. dest_addr.nl_pid = 0; //B:設定目的埠號
  51. dest_addr.nl_groups = 0;
  52. nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
  53. nlh->nlmsg_pid = 100; //C:設定源埠
  54. nlh->nlmsg_flags = 0;
  55. strcpy(NLMSG_DATA(nlh),"Hello you!"); //設定訊息體
  56. iov.iov_base = (void *)nlh;
  57. iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
  58. //Create mssage
  59. memset(&msg, 0, sizeof(msg));
  60. msg.msg_name = (void *)&dest_addr;
  61. msg.msg_namelen = sizeof(dest_addr);
  62. msg.msg_iov = &iov;
  63. msg.msg_iovlen = 1;
  64. //send message
  65. printf("state_smg\n");
  66. state_smg = sendmsg(sock_fd,&msg,0);
  67. if(state_smg == -1)
  68. {
  69. printf("get error sendmsg = %s\n",strerror(errno));
  70. }
  71. memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));
  72. //receive message
  73. printf("waiting received!\n");
  74. while(1){
  75. printf("In while recvmsg\n");
  76. state = recvmsg(sock_fd, &msg, 0);
  77. if(state<0)
  78. {
  79. printf("state<1");
  80. }
  81. printf("Received message: %s\n",(char *) NLMSG_DATA(nlh));
  82. }
  83. close(sock_fd);
  84. return 0;
  85. }

上面程式首先向核心傳送一條訊息;“Hello you”,然後進入迴圈一直等待讀取核心的回覆,並將收到的回覆打印出來。如果

看上面程式感覺很吃力,那麼應該首先複習一下UDP中使用sendmsg的用法,特別時struct msghdr的結構要清楚,這裡再贅

述。下面主要分析與UDP傳送資料包的不同點:

1. socket地址結構不同,UDP為sockaddr_in,Netlink為struct sockaddr_nl;

2. 與UDP傳送資料相比,Netlink多了一個訊息頭結構struct nlmsghdr需要我們構造。

注意程式碼註釋中的A、B、C三處分別設定了pid。首先解釋一下什麼是pid,網上很多文章把這個欄位說成是程序的pid,其實這

完全是望文生義。這裡的pid和程序pid沒有什麼關係,僅僅相當於UDP的port。對於UDP來說port和ip標示一個地址,那對我們

的NETLINK_TEST協議(注意Netlink本身不是一個協議)來說,pid就唯一標示了一個地址。所以你如果用程序pid做為標示當然

也是可以的。當然同樣的pid對於NETLINK_TEST協議和核心定義的其他使用Netlink的協議是不衝突的(就像TCP的80埠和

UDP的80埠)。

下面分析這三處設定pid分別有什麼作用,首先A和B位置的比較好理解,這是在地址(sockaddr_nl)上進行的設定,就是相當

於設定源地址和目的地址(其實是埠),只是注意B處設定pid為0,0就代表是核心,可以理解為核心專用的pid,那麼使用者進

程就不能用0做為自己的pid嗎?這個只能說如果你非要用也是可以的,只是會產生一些問題,後面在分析。接下來看為什麼C處

的訊息頭仍然需要設定pid呢?這裡首先要知道一個前提:核心不會像UDP一樣根據我們設定的原、目的地址為我們構造訊息

頭,所以我們不在包頭寫入我們自己的地址(pid),那核心怎麼知道是誰發來的報文呢?當然如果核心只是處理訊息不需要回

復程序的話舍不設定這個訊息頭pid都可以。

所以每個pid的設定功能不同:A處的設定是要設定傳送者的源地址,有人會說既然源地址又不會自動填充到報文中,我們為什麼

還要設定這個,因為你還可能要接收回復啊。就像寄信,你連“門牌號”都沒有,即使你在寫信時候寫上你的地址是100號,對

方回信目的地址也是100號,但是郵局發現根本沒有這個地址怎麼可能把信送到你手裡呢?所以A的主要作用是註冊源地址,保證

可以收到回覆,如果不需要回復當然可以簡單將pid設定為0;B處自然就是收信人的地址,pid為0代表核心的地址,假如有一個

程序在101號上註冊了地址,並呼叫了recvmsg,如果你將B處的pid設定為101,那資料包就發給了另一個程序,這就實現了使

用Netlink進行程序間通訊;C相當於你在信封上寫的源地址,通常情況下這個應該和你的真實地址(A)處註冊的源地址相同,

當然你要是不想收到回信,又想惡搞一下或者有特殊需求,你可以寫成其他程序註冊的pid(比如101)。這和我們現實中寄信是

一樣的,你給你朋友寫封情書,把寫信人寫成你的另一個好基友,然後後果你懂得……

好了,有了這個例子我們就大概知道使用者態怎麼使用Netlink了。

六:核心態程式範例一

  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. #include <linux/timer.h>
  4. #include <linux/time.h>
  5. #include <linux/types.h>
  6. #include <net/sock.h>
  7. #include <net/netlink.h>
  8. #define NETLINK_TEST 25
  9. #define MAX_MSGSIZE 1024
  10. int stringlength(char *s);
  11. int err;
  12. struct sock *nl_sk = NULL;
  13. int flag = 0;
  14. //向用戶態程序回發訊息
  15. void sendnlmsg(char *message, int pid)
  16. {
  17. struct sk_buff *skb_1;
  18. struct nlmsghdr *nlh;
  19. int len = NLMSG_SPACE(MAX_MSGSIZE);
  20. int slen = 0;
  21. if(!message || !nl_sk)
  22. {
  23. return ;
  24. }
  25. printk(KERN_ERR "pid:%d\n",pid);
  26. skb_1 = alloc_skb(len,GFP_KERNEL);
  27. if(!skb_1)
  28. {
  29. printk(KERN_ERR "my_net_link:alloc_skb error\n");
  30. }
  31. slen = stringlength(message);
  32. nlh = nlmsg_put(skb_1,0,0,0,MAX_MSGSIZE,0);
  33. NETLINK_CB(skb_1).pid = 0;
  34. NETLINK_CB(skb_1).dst_group = 0;
  35. message[slen]= '\0';
  36. memcpy(NLMSG_DATA(nlh),message,slen+1);
  37. printk("my_net_link:send message '%s'.\n",(char *)NLMSG_DATA(nlh));
  38. netlink_unicast(nl_sk,skb_1,pid,MSG_DONTWAIT);
  39. }
  40. int stringlength(char *s)
  41. {
  42. int slen = 0;
  43. for(; *s; s++)
  44. {
  45. slen++;
  46. }
  47. return slen;
  48. }
  49. //接收使用者態發來的訊息
  50. void nl_data_ready(struct sk_buff *__skb)
  51. {
  52. struct sk_buff *skb;
  53. struct nlmsghdr *nlh;
  54. char str[100];
  55. struct completion cmpl;
  56. printk("begin data_ready\n");
  57. int i=10;
  58. int pid;
  59. skb = skb_get (__skb);
  60. if(skb->len >= NLMSG_SPACE(0))
  61. {
  62. nlh = nlmsg_hdr(skb);
  63. memcpy(str, NLMSG_DATA(nlh), sizeof(str));
  64. printk("Message received:%s\n",str) ;
  65. pid = nlh->nlmsg_pid;
  66. while(i--)
  67. {//我們使用completion做延時,每3秒鐘向用戶態回發一個訊息
  68. init_completion(&cmpl);
  69. wait_for_completion_timeout(&cmpl,3 * HZ);
  70. sendnlmsg("I am from kernel!",pid);
  71. }
  72. flag = 1;
  73. kfree_skb(skb);
  74. }
  75. }
  76. // Initialize netlink
  77. int netlink_init(void)
  78. {
  79. nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 1,
  80. nl_data_ready, NULL, THIS_MODULE);
  81. if(!nl_sk){
  82. printk(KERN_ERR "my_net_link: create netlink socket error.\n");
  83. return 1;
  84. }
  85. printk("my_net_link_4: create netlink socket ok.\n");
  86. return 0;
  87. }
  88. static void netlink_exit(void)
  89. {
  90. if(nl_sk != NULL){
  91. sock_release(nl_sk->sk_socket);
  92. }
  93. printk("my_net_link: self module exited\n");
  94. }
  95. module_init(netlink_init);
  96. module_exit(netlink_exit);
  97. MODULE_AUTHOR("zhao_h");
  98. MODULE_LICENSE("GPL");

附上核心程式碼的Makefile檔案:

  1. ifneq ($(KERNELRELEASE),)
  2. obj-m :=netl.o
  3. else
  4. KERNELDIR ?=/lib/modules/$(shell uname -r)/build
  5. PWD :=$(shell pwd)
  6. default:
  7. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
  8. endif

我們將核心模組insmod後,執行使用者態程式,結果如下:

這個結果複合我們的預期,但是執行過程中打印出“state_smg”卡了好久才輸出了後面的結果。這時候檢視客戶程序是處於D

狀態的(不瞭解D狀態的同學可以google一下)。這是為什麼呢?因為程序使用Netlink向核心發資料是同步,核心向程序發數

據是非同步。什麼意思呢?也就是使用者程序呼叫sendmsg傳送訊息後,核心會呼叫相應的接收函式,但是一定到這個接收函式執行

完使用者態的sendmsg才能夠返回。我們在核心態的接收函式中呼叫了10次回發函數,每次都等待3秒鐘,所以核心接收函式30秒

後才返回,所以我們使用者態程式的sendmsg也要等30秒後才返回。相反,核心回發的資料不用等待使用者程式接收,這是因為核心

所發的資料會暫時存放在一個佇列中。

再來回到之前的一個問題,使用者態程式的源地址(pid)可以用0嗎?我把上面的使用者程式的A和C處pid都改為了0,結果一執行

就宕機了。為什麼呢?我們看一下核心程式碼的邏輯,收到使用者訊息後,根據訊息中的pid傳送回去,而pid為0,核心並不認為這

是使用者程式,認為是自身,所有又將回發的10個訊息發給了自己(核心),這樣就陷入了一個死迴圈,而使用者態這時候程序一直

處於D。

另外一個問題,如果同時啟動兩個使用者程序會是什麼情況?答案是再呼叫bind時出錯:“Address already in use”,這個同

UDP一樣,同一個地址同一個port如果沒有設定SO_REUSEADDR兩次bind就會出錯,之後我用同樣的方式再Netlink的socket

上設定了SO_REUSEADDR,但是並沒有什麼效果。

七:使用者態範例二

之前我們說過UDP可以使用sendmsg/recvmsg也可以使用sendto/recvfrom,那麼Netlink同樣也可以使用sendto/

recvfrom。具體實現如下:

  1. #include <sys/stat.h>
  2. #include <unistd.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <sys/socket.h>
  6. #include <sys/types.h>
  7. #include <string.h>
  8. #include <asm/types.h>
  9. #include <linux/netlink.h>
  10. #include <linux/socket.h>
  11. #include <errno.h>
  12. #define MAX_PAYLOAD 1024 // maximum payload size
  13. #define NETLINK_TEST 25
  14. int main(int argc, char* argv[])
  15. {
  16. struct sockaddr_nl src_addr, dest_addr;
  17. struct nlmsghdr *nlh = NULL;
  18. int sock_fd, retval;
  19. int state,state_smg = 0;
  20. // Create a socket
  21. sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
  22. if(sock_fd == -1){
  23. printf("error getting socket: %s", strerror(errno));
  24. return -1;
  25. }
  26. // To prepare binding
  27. memset(&src_addr, 0, sizeof(src_addr));
  28. src_addr.nl_family = AF_NETLINK;
  29. src_addr.nl_pid = 100;
  30. src_addr.nl_groups = 0;
  31. //Bind
  32. retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
  33. if(retval < 0){
  34. printf("bind failed: %s", strerror(errno));
  35. close(sock_fd);
  36. return -1;
  37. }
  38. // To orepare create mssage head
  39. nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
  40. if(!nlh){
  41. printf("malloc nlmsghdr error!\n");
  42. close(sock_fd);
  43. return -1;
  44. }
  45. memset(&dest_addr,0,sizeof(dest_addr));
  46. dest_addr.nl_family = AF_NETLINK;
  47. dest_addr.nl_pid = 0;
  48. dest_addr.nl_groups = 0;
  49. nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
  50. nlh->nlmsg_pid = 100;
  51. nlh->nlmsg_flags = 0;
  52. strcpy(NLMSG_DATA(nlh),"Hello you!");
  53. //send message
  54. printf("state_smg\n");
  55. sendto(sock_fd,nlh,NLMSG_LENGTH(MAX_PAYLOAD),0,(struct sockaddr*)(&dest_addr),sizeof(dest_addr));
  56. if(state_smg == -1)
  57. {
  58. printf("get error sendmsg = %s\n",strerror(errno));
  59. }
  60. memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));
  61. //receive message
  62. printf("waiting received!\n");
  63. while(1){
  64. printf("In while recvmsg\n");
  65. state=recvfrom(sock_fd,nlh,NLMSG_LENGTH(MAX_PAYLOAD),0,NULL,NULL);
  66. if(state<0)
  67. {
  68. printf("state<1");
  69. }
  70. printf("Received message: %s\n",(char *) NLMSG_DATA(nlh));
  71. memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));
  72. }
  73. close(sock_fd);
  74. return 0;
  75. }

熟悉UDP程式設計的同學看到這個程式一定很熟悉,除了多了一個Netlink訊息頭的設定。但是我們發現程式中呼叫了bind函式,這

個函式再UDP程式設計中的客戶端不是必須的,因為我們不需要把UDP socket與某個地址關聯,同時再發送UDP資料包時核心會為

我們分配一個隨即的埠。但是對於Netlink必須要有這一步bind,因為Netlink核心可不會為我們分配一個pid。再強調一遍消

息頭(nlmsghdr)中的pid是告訴核心接收端要回復的地址,但是這個地址存不存在核心並不關心,這個地址只有使用者端呼叫了

bind後才存在。

我們看到這兩個例子都是使用者態首先發起的,那Netlink是否支援核心態主動發起的情況呢?

當然是可以的,只是核心一般需要事件觸發,這裡,只要和使用者態約定號一個地址(pid),核心直接呼叫netlink_unicast就可以了。