<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    jasmine214--love

    只有當(dāng)你的內(nèi)心總是充滿快樂、美好的愿望和寧靜時(shí),你才能擁有強(qiáng)壯的體魄和明朗、快樂或者寧靜的面容。
    posts - 731, comments - 60, trackbacks - 0, articles - 0

    netlink socket理解

    Posted on 2012-06-01 16:25 幻海藍(lán)夢 閱讀(10235) 評(píng)論(0)  編輯  收藏 所屬分類: C語言學(xué)習(xí)

    由于開發(fā)和維護(hù)內(nèi)核的復(fù)雜性,只用最為關(guān)鍵同時(shí)對性能要求最高的代碼才會(huì)放在內(nèi)核中。其他的諸如GUI,管理和控制代碼,通常放在用戶空間運(yùn)行。這種將實(shí)現(xiàn)分離在內(nèi)核和用戶空間的思想在Linux中非常常見。現(xiàn)在的問題是內(nèi)核代碼和用戶代碼如果彼此通信。

    答案是內(nèi)核空間和用戶空間存在的各種IPC方法,例如系統(tǒng)調(diào)用,ioctl,proc文件系統(tǒng)和netlink socket。這篇文章討論netlink socket和討論其作為一種網(wǎng)絡(luò)特征IPC的優(yōu)勢。

    簡介

    Netlink socket是用于內(nèi)核和用戶空間之間交換信息的特殊的IPC機(jī)制。它提供了一種全復(fù)用的通信鏈路。和TCP/IP使用的地址族AF_INET相 比,Netlink socket使用地址族AF_NETLINK,每個(gè)的netlink socket特征定義協(xié)議類型在內(nèi)核頭文件中include/linux/netlink.h

    以下是netlink socket當(dāng)前支持的特征和他們的協(xié)議類型的子集:

    • NETLINK_ROUTE:用戶空間路由damon,如BGP,OSPF,RIP和內(nèi)核包轉(zhuǎn)發(fā)模塊的通信信道。用戶空間路由damon通過此種netlink協(xié)議類型更新內(nèi)核路由表
    • NETLINK_FIREWALL:接收IPv4防火墻代碼發(fā)送的包
    • NETLINK_NFLOG:用戶空間iptable管理工具和內(nèi)核空間Netfilter模塊的通信信道
    • NETLINK_ARPD:用戶空間管理arp表

    為 什么以上的特征使用netlink而不是系統(tǒng)調(diào)用,ioctl或者proc文件系統(tǒng)來完成通信?為新特性添加系統(tǒng)調(diào)用,ioctl和proc文件系統(tǒng)相對 而言是一項(xiàng)比較復(fù)雜的工作,我們冒著污染內(nèi)核和損害系統(tǒng)穩(wěn)定性的風(fēng)險(xiǎn)。netlink socket相對簡單:只有一個(gè)常量,協(xié)議類型,需要加入到netlink.h中。然后,內(nèi)核模塊和用戶程序可以通過socket類型的API進(jìn)行通信。

    和 其他socket API一樣,Netlink是異步的,它提供了一個(gè)socket隊(duì)列來平滑突發(fā)的信息。發(fā)送一個(gè)netlink消息的系統(tǒng)調(diào)用將消息排列到接受者的 netlink隊(duì)列中然后調(diào)用接收者的接收處理函數(shù)。接收者,在接收處理函數(shù)的上下文中,可以決定是否立即處理該消息還是等待在另一個(gè)上下文中處理。不想 netlink,系統(tǒng)調(diào)用需要同步處理。因此,如果我們使用了一個(gè)系統(tǒng)來傳遞一條消息到內(nèi)核,如果需要處理該條信息的時(shí)間很長,那么內(nèi)核調(diào)度粒度可以會(huì)受 影響。

    在內(nèi)核中實(shí)現(xiàn)的系統(tǒng)調(diào)用代碼在編譯時(shí)被靜態(tài)的鏈接到內(nèi)核中,因此在一個(gè)可以動(dòng)態(tài)加載的模塊中包括系統(tǒng)調(diào)用代碼是不合適的。在netlink socket中,內(nèi)核中的netlink核心和在一個(gè)可加載的模塊中沒有編譯時(shí)的相互依賴。

    netlink socket支持多播,這也是其與其他交互手段相比較的優(yōu)勢之一。一個(gè)進(jìn)程可以將一條消息廣播到一個(gè)netlink組地址。任意多的進(jìn)程可以監(jiān)聽那個(gè)組地址。這提供了一種從內(nèi)核到用戶空間進(jìn)行事件分發(fā)接近完美的機(jī)制。

    從 會(huì)話只能由用戶空間應(yīng)用發(fā)起的角度來看,系統(tǒng)調(diào)用和ioctl是單一的IPC。但是,如果一個(gè)內(nèi)核模塊有一個(gè)用戶空間應(yīng)用的緊急消息,沒有一種直接的方法 來實(shí)現(xiàn)這些功能。通常,應(yīng)用需要階段性的輪詢內(nèi)核來獲取狀態(tài)變化,盡管密集的輪詢會(huì)有很大的開銷。netlink通過允許內(nèi)核初始化一個(gè)對話來優(yōu)雅的解決 這個(gè)問題。我們稱之為netlink的復(fù)用特性。

    最后,netlink提供了bsd socket風(fēng)格的API,而這些API是被軟件開發(fā)社區(qū)所熟知。因此,培訓(xùn)費(fèi)用相較較小。

    和BSD路由socket的關(guān)系

    在BSC TCP/IP的棧實(shí)現(xiàn)中,有一種叫做路由套接字的特殊的socket。它有AF_ROUTE地址族,PF_ROUTE協(xié)議族和SOCK_RAWsocket類型。在bsd中,路由套接字用于在內(nèi)核路由表中添加和刪除路由。

    在Linux中,路由套接字的實(shí)現(xiàn)通過netlink套接字的NETLINK_ROUTE協(xié)議類型來支持。netlink套接字提供了bsd路由套接字的功能的超集。

    Netlink套接字API

    標(biāo) 準(zhǔn)的套接字API,socket(),sendmsg(),recvmsg()和close(),可以被用戶態(tài)程序使用。可以通過查詢man手冊頁來看這 些函數(shù)的具體定義。這兒,我們討論在netlink上下文中為這些API選擇參數(shù)。對于寫過TCP/IP套接字程序的人對這些API都應(yīng)該非常熟悉。

    創(chuàng)建一個(gè)套接字,

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

    domain指代地址族,AF_NETLINK,套接字類型不是SOCK_RAW就是SOCK_DGRAM,因?yàn)閚etlink是一個(gè)面向數(shù)據(jù)報(bào)的服務(wù)。

    protocol選擇該套接字使用那種netlink特征。以下是幾種預(yù)定義的協(xié)議類型:NETLINK_ROUTE,NETLINK_FIREWALL,NETLINK_APRD,NETLINK_ROUTE6_FW。你也可以非常容易的添加自己的netlink協(xié)議。

    為 每一個(gè)協(xié)議類型最多可以定義32個(gè)多播組。每一個(gè)多播組用一個(gè)bit mask來表示,1<<i(0<= i<= 31),這在一組進(jìn)程和內(nèi)核進(jìn)程協(xié)同完成一項(xiàng)任務(wù)時(shí)非常有用。發(fā)送多播netlink消息可以減少系統(tǒng)調(diào)用的數(shù)量,同時(shí)減少用來維護(hù)多播組成員信息的負(fù) 擔(dān)。

    bind()

    和TCP/IP套接字一樣,netlink bind()API用來將一個(gè)本地socket地址和一個(gè)打開的socket關(guān)聯(lián)。一個(gè)netlink地址結(jié)構(gòu)如下所示:

       1:  struct sockaddr_nl
       2:  {
       3:    sa_family_t    nl_family;  /* AF_NETLINK   */
       4:    unsigned short nl_pad;     /* zero         */
       5:    __u32          nl_pid;     /* process pid */
       6:    __u32          nl_groups;  /* mcast groups mask */
       7:  } nladdr;

    當(dāng)使用bind()調(diào)用的時(shí)候,nl_pid域可以被賦值為調(diào)用進(jìn)程的pid。nl_pid在這兒被當(dāng)做該netlink套接字的本地地址。程序負(fù)責(zé)找一個(gè)獨(dú)一無二的32位整數(shù)在填充該域。一種常見的做法是:

       1:  NL_PID Formula 1:  nl_pid = getpid();

    公式一使用進(jìn)程ID號(hào)作為nl_pid的值,如果說該進(jìn)程只需要一個(gè)netlink套接字,這是一個(gè)自然的選擇。當(dāng)一個(gè)進(jìn)程中的不同線程需要同一個(gè)netlink協(xié)議多個(gè)netlink套接字。公式而可以用來產(chǎn)生nl_pid號(hào):

       1:  NL_PID Formula 2: pthread_self() << 16 | getpid();

    通過這種方法,同一個(gè)進(jìn)程中的不同線程可以有同一種netlink協(xié)議類型的netlink套接字。事實(shí)上,即使在同一個(gè)線程中,在可能使用同一種協(xié)議類型的多個(gè)套接字。開發(fā)者必須要更有創(chuàng)造力來產(chǎn)生一個(gè)唯一的nl_pid。

    如果應(yīng)用想接受發(fā)送給特定多播組的netlink消息,所有感興趣的多播組bit應(yīng)該or在一起,并填充到nl_groups域。否則, nl_groups應(yīng)該被顯式至零,說明該應(yīng)用只接受到該應(yīng)用的消息,填充完上述域, 使用如下方式進(jìn)行綁定:

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

    發(fā)送一條netlink消息

    為了發(fā)送一條netlink消息到內(nèi)核或者其他的用戶空間進(jìn)程,另外一個(gè)struct sockaddr_nl nladdr需要作為目的地址,這和使用sendmsg()發(fā)送一個(gè)UDP包是一樣的。如果該消息是發(fā)送至內(nèi)核的,那么nl_pid和nl_groups 都置為0.

    如果說消息時(shí)發(fā)送給另一個(gè)進(jìn)程的單播消息,nl_pid是另外一個(gè)進(jìn)程的pid值而nl_groups為零。

    如果消息是發(fā)送給一個(gè)或多個(gè)多播組的多播消息,所有的目的多播組必須bitmask必須or起來從而形成nl_groups域。當(dāng)我們填充struct msghdr結(jié)構(gòu)用于sendmsg時(shí),使用如下:

       1:  struct msghdr msg;
       2:  msg.msg_name = (void *)&(nladdr);
       3:  msg.msg_namelen = sizeof(nladdr);

    netlink套接字也需要它自己本身的消息頭,這是為了給所有協(xié)議類型的netlink消息提供一個(gè)統(tǒng)一的平臺(tái)。

    因?yàn)長inux內(nèi)核netlink核心假設(shè)每個(gè)netlink消息中存在著以下的頭,所有應(yīng)用也必須在其發(fā)送的消息中提供這些頭信息:

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

    nlmsg_len指整個(gè)netlink消息的長度,包括頭信息,這也是netlink核心所必須的。nlmsg_type用于應(yīng)用但是對于 netlink核心而言其是透明的。nlmsg_flags用于給定附加的控制信息,其被netlink核心讀取和更新。nlmsg_seq和 mlmsg_pid,應(yīng)用用來跟蹤消息,這些對于netlink核心也是透明的。

    所以一個(gè)netlink消息由消息頭和消息負(fù)載組成。一旦一個(gè)消息被加入,它就加入到一個(gè)通過nlh指針指向的緩沖區(qū)。我們也可以將消息發(fā)送到struct msghdr msg:

       1:  struct iovec iov;
       2:   
       3:  iov.iov_base = (void *)nlh;
       4:  iov.iov_len = nlh->nlmsg_len;
       5:   
       6:  msg.msg_iov = &iov;
       7:  msg.msg_iovlen = 1;

    經(jīng)過以上步驟,調(diào)用sendmsg()函數(shù)來發(fā)送netlink消息:

       1:  sendmsg(fd, &msg, 0);
     
    接收netlink消息

    一個(gè)接收程序必須分配一個(gè)足夠大的內(nèi)存用于保存netlink消息頭和消息負(fù)載。然后其填充struct msghdr msg,然后使用標(biāo)準(zhǔn)的recvmsg()函數(shù)來接收netlink消息,假設(shè)緩存通過nlh指針指向:

       1:  struct sockaddr_nl nladdr;
       2:  struct msghdr msg;
       3:  struct iovec iov;
       4:   
       5:  iov.iov_base = (void *)nlh;
       6:  iov.iov_len = MAX_NL_MSG_LEN;
       7:  msg.msg_name = (void *)&(nladdr);
       8:  msg.msg_namelen = sizeof(nladdr);
       9:   
      10:  msg.msg_iov = &iov;
      11:  msg.msg_iovlen = 1;
      12:  recvmsg(fd, &msg, 0);

    當(dāng)消息被正確的接收之后,nlh應(yīng)該指向剛剛接收到的netlink消息的頭。nladdr應(yīng)該包含接收消息的目的地址,其中包括了消息發(fā)送者的 pid和多播組。同時(shí),宏NLMSG_DATA(nlh),定義在netlink.h中,返回一個(gè)指向netlink消息的負(fù)載的指針。 close(fd)調(diào)用關(guān)閉fd描述符所標(biāo)識(shí)的socket。

    內(nèi)核空間netlink API

    內(nèi)核空間的netlinkAPI在內(nèi)核中被netlink核心支持,即net/core/af_netlink.c。從內(nèi)核角度看,這些API不同 于用戶空間的API。這些API可以被內(nèi)核模塊使用從而存取netlink套接字與用戶空間程序通信。除非你使用現(xiàn)存的netlink套接字協(xié)議類型,否 則你必須通過在netlink.h中定義一個(gè)常量來添加你自己的協(xié)議類型。例如,我們需要添加一個(gè)netlink協(xié)議類型用于測試,則在 netlink.h中加入下面的語句:

       1:  #define NETLINK_TEST  17

    之后,亦可以在linux內(nèi)核中的任何地方引用添加的協(xié)議類型。

    在用戶空間,我們使用socket()來創(chuàng)建一個(gè)netlink套接字,但是在內(nèi)核空間,我們使用如下的API:

       1:  truct sock *
       2:  netlink_kernel_create(int unit,
       3:             void (*input)(struct sock *sk, int len));

    參數(shù)unit,即為netlink協(xié)議類型,如NETLINK_TEST,回調(diào)函數(shù)會(huì)在消息到達(dá)netlink套接字時(shí)調(diào)用。當(dāng)用戶態(tài)程序發(fā)送一個(gè)NETLINK_TEST協(xié)議類型的消息給內(nèi)核時(shí),input()函數(shù)被調(diào)用。下面是一個(gè)實(shí)現(xiàn)回調(diào)函數(shù)的例子:

       1:  void input (struct sock *sk, int len)
       2:  {
       3:   struct sk_buff *skb;
       4:   struct nlmsghdr *nlh = NULL;
       5:   u8 *payload = NULL;
       6:   
       7:   while ((skb = skb_dequeue(&sk->receive_queue))
       8:         != NULL) {
       9:   /* process netlink message pointed by skb->data */
      10:   nlh = (struct nlmsghdr *)skb->data;
      11:   payload = NLMSG_DATA(nlh);
      12:   /* process netlink message with header pointed by
      13:    * nlh    and payload pointed by payload
      14:    */
      15:   }
      16:  }

    input()函數(shù)在發(fā)送進(jìn)程的sendmsg()系統(tǒng)調(diào)用上下文執(zhí)行。如果在input中處理netlink消息非常快,那是沒有問題的。如果處 理netlink消息需要很長的時(shí)間,我們希望在input()外面處理消息來避免阻塞其他系統(tǒng)調(diào)用進(jìn)入內(nèi)核。事實(shí)上,我們可以使用一個(gè)指定的內(nèi)核線程來 來不斷執(zhí)行以下的步驟。使用skb=skb_recv_datagram(nl_sk),其中nl_sk是 netlink_kernel_create()返回的netlink套接字。然后,處理由skb->data指向的netlink消息。

    以下的內(nèi)核線程在沒有netlink消息在nl_sk中時(shí)睡眠,在回調(diào)函數(shù)input中,我們只要喚醒睡眠的內(nèi)核線程,如下所示:

       1:  void input (struct sock *sk, int len)
       2:  {
       3:    wake_up_interruptible(sk->sleep);
       4:  }

    這是一個(gè)更具有擴(kuò)展性的用戶和內(nèi)核通信的模型。其也提高了上下文交換的粒度。

    在內(nèi)核中發(fā)送netlink消息

    真如在用戶空間中一樣,在發(fā)送一個(gè)netlink消息時(shí)需要設(shè)置源和目的netlink消息地址。假設(shè)socket緩存中包含了將要發(fā)送的netlink消息,本地地址可以通過以下方式設(shè)置:

       1:  NETLINK_CB(skb).groups = local_groups;
       2:  NETLINK_CB(skb).pid = 0;   /* from kernel */

    目的地址可以如下設(shè)置:

       1:  NETLINK_CB(skb).dst_groups = dst_groups;
       2:  NETLINK_CB(skb).dst_pid = dst_pid;

    這些信息不是存儲(chǔ)在skb->data,而是存儲(chǔ)在skb中的netlink控制塊中。發(fā)送一個(gè)消息,使用:

       1:  int
       2:  netlink_unicast(struct sock *ssk, struct sk_buff
       3:                  *skb, u32 pid, int nonblock);

    其中ssk是netlink_kernel_create返回的netlink套接字,skb->data指向netlink將要發(fā)送的消息而pid是接受該消息的用戶程序id。nonblock用于標(biāo)識(shí)在接收緩存不可用時(shí),API是阻塞還是立即返回失敗。

    你也可以發(fā)送一個(gè)多播消息。以下的API用于將消息傳送到指定的進(jìn)程,同時(shí)多播至指定的多播組。

       1:  void
       2:  netlink_broadcast(struct sock *ssk, struct sk_buff
       3:           *skb, u32 pid, u32 group, int allocation);

    group是所有接收多播組的bitmask。allocation是內(nèi)核內(nèi)存分配的類型。通常,GFP_ATOMIC用于中斷上下文而在其他情況下是用GFP_KERNEL.只是因?yàn)锳PI可能需要分配一個(gè)或者多個(gè)套接字緩存來克隆多播消息。

    在內(nèi)核中關(guān)閉一個(gè)netlink套接字

    給定了netlink_kernel_create()函數(shù)返回的struct sock *nl_sk,我們可以通過調(diào)用以下的API關(guān)閉netlink套接字。

       1:  sock_release(nl_sk->socket);

    在內(nèi)核和用戶態(tài)使用單播通信

       1:  #include <sys/socket.h>
       2:  #include <linux/netlink.h>
       3:   
       4:  #define MAX_PAYLOAD 1024  /* maximum payload size*/
       5:  struct sockaddr_nl src_addr, dest_addr;
       6:  struct nlmsghdr *nlh = NULL;
       7:  struct iovec iov;
       8:  int sock_fd;
       9:   
      10:  void main() {
      11:   sock_fd = socket(PF_NETLINK, SOCK_RAW,NETLINK_TEST);
      12:   
      13:   memset(&src_addr, 0, sizeof(src_addr));
      14:   src__addr.nl_family = AF_NETLINK;
      15:   src_addr.nl_pid = getpid();  /* self pid */
      16:   src_addr.nl_groups = 0;  /* not in mcast groups */
      17:   bind(sock_fd, (struct sockaddr*)&src_addr,
      18:        sizeof(src_addr));
      19:   
      20:   memset(&dest_addr, 0, sizeof(dest_addr));
      21:   dest_addr.nl_family = AF_NETLINK;
      22:   dest_addr.nl_pid = 0;   /* For Linux Kernel */
      23:   dest_addr.nl_groups = 0; /* unicast */
      24:   
      25:   nlh=(struct nlmsghdr *)malloc(
      26:                   NLMSG_SPACE(MAX_PAYLOAD));
      27:   /* Fill the netlink message header */
      28:   nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
      29:   nlh->nlmsg_pid = getpid();  /* self pid */
      30:   nlh->nlmsg_flags = 0;
      31:   /* Fill in the netlink message payload */
      32:   strcpy(NLMSG_DATA(nlh), "Hello you!");
      33:   
      34:   iov.iov_base = (void *)nlh;
      35:   iov.iov_len = nlh->nlmsg_len;
      36:   msg.msg_name = (void *)&dest_addr;
      37:   msg.msg_namelen = sizeof(dest_addr);
      38:   msg.msg_iov = &iov;
      39:   msg.msg_iovlen = 1;
      40:   
      41:   sendmsg(fd, &msg, 0);
      42:   
      43:   /* Read message from kernel */
      44:   memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
      45:   recvmsg(fd, &msg, 0);
      46:   printf(" Received message payload: %s\n",
      47:      NLMSG_DATA(nlh));
      48:   
      49:   /* Close Netlink Socket */
      50:   close(sock_fd);
      51:  }
       1:  struct sock *nl_sk = NULL;
       2:   
       3:  void nl_data_ready (struct sock *sk, int len)
       4:  {
       5:    wake_up_interruptible(sk->sleep);
       6:  }
       7:   
       8:  void netlink_test() {
       9:   struct sk_buff *skb = NULL;
      10:   struct nlmsghdr *nlh = NULL;
      11:   int err;
      12:   u32 pid;
      13:   
      14:   nl_sk = netlink_kernel_create(NETLINK_TEST,
      15:                                     nl_data_ready);
      16:   /* wait for message coming down from user-space */
      17:   skb = skb_recv_datagram(nl_sk, 0, 0, &err);
      18:   
      19:   nlh = (struct nlmsghdr *)skb->data;
      20:   printk("%s: received netlink message payload:%s\n",
      21:          __FUNCTION__, NLMSG_DATA(nlh));
      22:   
      23:   pid = nlh->nlmsg_pid; /*pid of sending process */
      24:   NETLINK_CB(skb).groups = 0; /* not in mcast group */
      25:   NETLINK_CB(skb).pid = 0;      /* from kernel */
      26:   NETLINK_CB(skb).dst_pid = pid;
      27:   NETLINK_CB(skb).dst_groups = 0;  /* unicast */
      28:   netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT);
      29:   sock_release(nl_sk->socket);
      30:  }

    在內(nèi)核和用戶態(tài)使用單播通信

       1:  #include <sys/socket.h>
       2:  #include <linux/netlink.h>
       3:   
       4:  #define MAX_PAYLOAD 1024  /* maximum payload size*/
       5:  struct sockaddr_nl src_addr, dest_addr;
       6:  struct nlmsghdr *nlh = NULL;
       7:  struct iovec iov;
       8:  int sock_fd;
       9:   
      10:  void main() {
      11:   sock_fd=socket(PF_NETLINK, SOCK_RAW, NETLINK_TEST);
      12:   
      13:   memset(&src_addr, 0, sizeof(local_addr));
      14:   src_addr.nl_family = AF_NETLINK;
      15:   src_addr.nl_pid = getpid();  /* self pid */
      16:   /* interested in group 1<<0 */
      17:   src_addr.nl_groups = 1;
      18:   bind(sock_fd, (struct sockaddr*)&src_addr,
      19:        sizeof(src_addr));
      20:   
      21:   memset(&dest_addr, 0, sizeof(dest_addr));
      22:   
      23:   nlh = (struct nlmsghdr *)malloc(
      24:                            NLMSG_SPACE(MAX_PAYLOAD));
      25:   memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
      26:   
      27:   iov.iov_base = (void *)nlh;
      28:   iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
      29:   msg.msg_name = (void *)&dest_addr;
      30:   msg.msg_namelen = sizeof(dest_addr);
      31:   msg.msg_iov = &iov;
      32:   msg.msg_iovlen = 1;
      33:   
      34:   printf("Waiting for message from kernel\n");
      35:   
      36:   /* Read message from kernel */
      37:   recvmsg(fd, &msg, 0);
      38:   printf(" Received message payload: %s\n",
      39:          NLMSG_DATA(nlh));
      40:   close(sock_fd);

    41: }

     

       1:  #define MAX_PAYLOAD 1024
       2:  struct sock *nl_sk = NULL;
       3:   
       4:  void netlink_test() {
       5:   sturct sk_buff *skb = NULL;
       6:   struct nlmsghdr *nlh;
       7:   int err;
       8:   
       9:   nl_sk = netlink_kernel_create(NETLINK_TEST,
      10:                                 nl_data_ready);
      11:   skb=alloc_skb(NLMSG_SPACE(MAX_PAYLOAD),GFP_KERNEL);
      12:   nlh = (struct nlmsghdr *)skb->data;
      13:   nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
      14:   nlh->nlmsg_pid = 0;  /* from kernel */
      15:   nlh->nlmsg_flags = 0;
      16:   strcpy(NLMSG_DATA(nlh), "Greeting from kernel!");
      17:   /* sender is in group 1<<0 */
      18:   NETLINK_CB(skb).groups = 1;
      19:   NETLINK_CB(skb).pid = 0;  /* from kernel */
      20:   NETLINK_CB(skb).dst_pid = 0;  /* multicast */
      21:   /* to mcast group 1<<0 */
      22:   NETLINK_CB(skb).dst_groups = 1;
      23:   
      24:   /*multicast the message to all listening processes*/
      25:   netlink_broadcast(nl_sk, skb, 0, 1, GFP_KERNEL);
      26:   sock_release(nl_sk->socket);
      27:  }

    Netlink可靠性機(jī)制

    在基于netlink的通信中,有兩種可能的情形會(huì)導(dǎo)致消息丟失:

    1. 內(nèi)存耗盡,沒有足夠多的內(nèi)存分配給消息
    2. 緩存復(fù)寫,接收隊(duì)列中沒有空間存儲(chǔ)消息,這在內(nèi)核空間和用戶空間之間通信時(shí)可能會(huì)發(fā)生

    緩存復(fù)寫在以下情況很可能會(huì)發(fā)生:

    1. 內(nèi)核子系統(tǒng)以一個(gè)恒定的速度發(fā)送netlink消息,但是用戶態(tài)監(jiān)聽者處理過慢
    2. 用戶存儲(chǔ)消息的空間過小

       如果netlink傳送消息失敗,那么recvmsg()函數(shù)會(huì)返回No  buffer space available(ENOBUFS)錯(cuò)誤。那么,用戶空間進(jìn)程知道它丟失了信息,如果內(nèi)核子系統(tǒng)支持dump操作,它可以重新同步來獲取最新的消息。在 dump操作中,netlink通過在每次調(diào)用recvmsg()函數(shù)時(shí)傳輸一個(gè)包的流控機(jī)制來防止接收隊(duì)列的復(fù)寫。改包消耗一個(gè)內(nèi)存頁,其包含了幾個(gè)多 部分netlink消息。圖6中的序列圖顯示了在一個(gè)重新同步的過程中所使用的dump操作。

     

    image

    另一方面,緩存復(fù)寫不會(huì)發(fā)生在用戶和內(nèi)核空間的通信中,因?yàn)閟endmsg()同步的將netlink消息發(fā)送到內(nèi)核子系統(tǒng)。如果使用的是阻塞套接字,那么netlink在從用戶空間到內(nèi)核空間的通信時(shí)完全可靠的,因?yàn)閮?nèi)存分配可以等待,所以沒有內(nèi)存耗盡的可能。

    netlink也可以提供應(yīng)答機(jī)制。所以如果用戶空間進(jìn)程發(fā)送了一個(gè)設(shè)置了NLM_F_ACK標(biāo)志的請求,netlink會(huì)在netlink錯(cuò)誤消息中報(bào)告給用戶空間剛才請求操作的結(jié)果。

    從用戶空間的角度來看,Netlink套接字在通用的BSD套接字接口之上實(shí)現(xiàn)。因此,netlink套接字編程與通用的TCP/IP編程類似。但是,我們也應(yīng)該考慮幾個(gè)與netlink相關(guān)的特殊問題:

    1. netlink套接字沒有像其他協(xié)議一樣對用戶空間隱藏協(xié)議細(xì)節(jié)。事實(shí)上,netlink傳遞的是整個(gè)消息,包括netlink頭和其他信 息。因此,這就導(dǎo)致了數(shù)據(jù)處理函數(shù)與通用的TCP/IP套接字不同,所以用戶態(tài)程序必須根據(jù)其格式解析和構(gòu)建netlink信息。然而,沒有標(biāo)準(zhǔn)的工具來 完成這些工作,所以你必須實(shí)現(xiàn)自己的函數(shù)或者使用一些現(xiàn)成的庫。
    2. 來自netlink和內(nèi)核子系統(tǒng)的錯(cuò)誤不是通過recvmsg()函數(shù)返回的整數(shù)值來表現(xiàn)的。事實(shí)上,錯(cuò)誤信息時(shí)被包裝在netlink錯(cuò)誤 消息中的。唯一的例外是(ENOBUFS)錯(cuò)誤,該錯(cuò)誤不是包裝在netlink消息中,因?yàn)閳?bào)告該錯(cuò)誤的原因就是我們沒有足夠的空間來緩存新的 netlink消息。標(biāo)準(zhǔn)的通用套接字錯(cuò)誤,如(EAGAIN),通常和其他輪詢原語,例如poll()和select(),也是通過recvmsg() 返回整數(shù)值。
    原文:
    http://my.oschina.net/longscu/blog/59534
    主站蜘蛛池模板: 午夜免费1000部| 97在线观免费视频观看| 精品国产人成亚洲区| 成人毛片100免费观看| 亚洲久本草在线中文字幕| 无码国产精品一区二区免费式影视 | 国产成人无码区免费A∨视频网站 国产成人涩涩涩视频在线观看免费 | 亚洲成A人片在线播放器| 国产zzjjzzjj视频全免费| 无遮挡a级毛片免费看| 久久久久亚洲Av片无码v| 美女被免费喷白浆视频| 暖暖在线视频免费视频| 亚洲人成网站在线播放2019 | 特级av毛片免费观看| 久久久久无码精品亚洲日韩| 四虎永久免费影院在线| 成全影视免费观看大全二| 中文字幕不卡免费高清视频| 特级aa**毛片免费观看| 亚洲色大成网站WWW国产| 亚洲国产精品无码久久一线| 一区二区无码免费视频网站 | 亚洲国产精华液网站w| 国产偷窥女洗浴在线观看亚洲 | 久久不见久久见免费影院www日本| 久久精品国产亚洲AV无码麻豆| 四虎影库久免费视频| 日韩免费观看一级毛片看看| 久久综合给合久久国产免费| 精品一区二区三区无码免费直播 | 最近免费中文在线视频| 黄页网站在线免费观看| 精品国产亚洲一区二区三区在线观看 | 国产精品无码免费专区午夜 | 免费v片在线观看视频网站| 91高清免费国产自产拍2021| a一级毛片免费高清在线| 一级毛片正片免费视频手机看| 亚洲av无码不卡久久| 亚洲AV中文无码乱人伦下载|