jasmine214--love

          只有當你的內心總是充滿快樂、美好的愿望和寧靜時,你才能擁有強壯的體魄和明朗、快樂或者寧靜的面容。
          posts - 731, comments - 60, trackbacks - 0, articles - 0

          netlink socket理解

          Posted on 2012-06-01 16:25 幻海藍夢 閱讀(10244) 評論(0)  編輯  收藏 所屬分類: C語言學習

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

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

          簡介

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

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

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

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

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

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

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

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

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

          和BSD路由socket的關系

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

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

          Netlink套接字API

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

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

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

          domain指代地址族,AF_NETLINK,套接字類型不是SOCK_RAW就是SOCK_DGRAM,因為netlink是一個面向數(shù)據(jù)報的服務。

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

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

          bind()

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

             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;

          當使用bind()調用的時候,nl_pid域可以被賦值為調用進程的pid。nl_pid在這兒被當做該netlink套接字的本地地址。程序負責找一個獨一無二的32位整數(shù)在填充該域。一種常見的做法是:

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

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

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

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

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

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

          發(fā)送一條netlink消息

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

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

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

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

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

          因為Linux內核netlink核心假設每個netlink消息中存在著以下的頭,所有應用也必須在其發(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指整個netlink消息的長度,包括頭信息,這也是netlink核心所必須的。nlmsg_type用于應用但是對于 netlink核心而言其是透明的。nlmsg_flags用于給定附加的控制信息,其被netlink核心讀取和更新。nlmsg_seq和 mlmsg_pid,應用用來跟蹤消息,這些對于netlink核心也是透明的。

          所以一個netlink消息由消息頭和消息負載組成。一旦一個消息被加入,它就加入到一個通過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)過以上步驟,調用sendmsg()函數(shù)來發(fā)送netlink消息:

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

          一個接收程序必須分配一個足夠大的內存用于保存netlink消息頭和消息負載。然后其填充struct msghdr msg,然后使用標準的recvmsg()函數(shù)來接收netlink消息,假設緩存通過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);

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

          內核空間netlink API

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

             1:  #define NETLINK_TEST  17

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

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

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

          參數(shù)unit,即為netlink協(xié)議類型,如NETLINK_TEST,回調函數(shù)會在消息到達netlink套接字時調用。當用戶態(tài)程序發(fā)送一個NETLINK_TEST協(xié)議類型的消息給內核時,input()函數(shù)被調用。下面是一個實現(xiàn)回調函數(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ā)送進程的sendmsg()系統(tǒng)調用上下文執(zhí)行。如果在input中處理netlink消息非常快,那是沒有問題的。如果處 理netlink消息需要很長的時間,我們希望在input()外面處理消息來避免阻塞其他系統(tǒng)調用進入內核。事實上,我們可以使用一個指定的內核線程來 來不斷執(zhí)行以下的步驟。使用skb=skb_recv_datagram(nl_sk),其中nl_sk是 netlink_kernel_create()返回的netlink套接字。然后,處理由skb->data指向的netlink消息。

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

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

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

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

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

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

          目的地址可以如下設置:

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

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

             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用于標識在接收緩存不可用時,API是阻塞還是立即返回失敗。

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

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

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

          在內核中關閉一個netlink套接字

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

             1:  sock_release(nl_sk->socket);

          在內核和用戶態(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:  }

          在內核和用戶態(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可靠性機制

          在基于netlink的通信中,有兩種可能的情形會導致消息丟失:

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

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

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

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

           

          image

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

          netlink也可以提供應答機制。所以如果用戶空間進程發(fā)送了一個設置了NLM_F_ACK標志的請求,netlink會在netlink錯誤消息中報告給用戶空間剛才請求操作的結果。

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

          1. netlink套接字沒有像其他協(xié)議一樣對用戶空間隱藏協(xié)議細節(jié)。事實上,netlink傳遞的是整個消息,包括netlink頭和其他信 息。因此,這就導致了數(shù)據(jù)處理函數(shù)與通用的TCP/IP套接字不同,所以用戶態(tài)程序必須根據(jù)其格式解析和構建netlink信息。然而,沒有標準的工具來 完成這些工作,所以你必須實現(xiàn)自己的函數(shù)或者使用一些現(xiàn)成的庫。
          2. 來自netlink和內核子系統(tǒng)的錯誤不是通過recvmsg()函數(shù)返回的整數(shù)值來表現(xiàn)的。事實上,錯誤信息時被包裝在netlink錯誤 消息中的。唯一的例外是(ENOBUFS)錯誤,該錯誤不是包裝在netlink消息中,因為報告該錯誤的原因就是我們沒有足夠的空間來緩存新的 netlink消息。標準的通用套接字錯誤,如(EAGAIN),通常和其他輪詢原語,例如poll()和select(),也是通過recvmsg() 返回整數(shù)值。
          原文:
          http://my.oschina.net/longscu/blog/59534
          主站蜘蛛池模板: 沈丘县| 大洼县| 叙永县| 彭泽县| 禹州市| 福州市| 库伦旗| 博兴县| 山丹县| 油尖旺区| 濉溪县| 东至县| 义马市| 花垣县| 武胜县| 蓬溪县| 商河县| 登封市| 莆田市| 报价| 浠水县| 阆中市| 开封市| 卢龙县| 平昌县| 黄梅县| 高阳县| 周口市| 鸡东县| 永清县| 铜川市| 伊金霍洛旗| 贺州市| 邯郸县| 许昌县| 沾益县| 岳普湖县| 定州市| 平潭县| 新宁县| 红安县|