国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Linux用戶空間與內(nèi)核空間通信(Netlink通信機制)

這篇具有很好參考價值的文章主要介紹了Linux用戶空間與內(nèi)核空間通信(Netlink通信機制)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

1,什么是Netlink通信機制

Netlink是linux提供的用于內(nèi)核和用戶態(tài)進程之間的通信方式。但是注意雖然Netlink主要用于用戶空間和內(nèi)核空間的通信,但是也能用于用戶空間的兩個進程通信。只是進程間通信有其他很多方式,一般不用Netlink。除非需要用到Netlink的廣播特性時。

那么Netlink有什么優(yōu)勢呢?

一般來說用戶空間和內(nèi)核空間的通信方式有三種:/proc、ioctl、Netlink。而前兩種都是單向的,但是Netlink可以實現(xiàn)雙工通信。Netlink協(xié)議基于BSD socket和AF_NETLINK地址簇(address family),使用32位的端口號尋址(以前稱作PID),每個Netlink協(xié)議(或稱作總線,man手冊中則稱之為netlink family),通常與一個或一組內(nèi)核服務(wù)/組件相關(guān)聯(lián),如NETLINK_ROUTE用于獲取和設(shè)置路由與鏈路信息、NETLINK_KOBJECT_UEVENT用于內(nèi)核向用戶空間的udev進程發(fā)送通知等。

netlink具有以下特點:

  • ① 支持全雙工、異步通信(當(dāng)然同步也支持)
  • ② 用戶空間可使用標(biāo)準(zhǔn)的BSD socket接口(但netlink并沒有屏蔽掉協(xié)議包的構(gòu)造與解析過程,推薦使用libnl等第三方庫)
  • ③ 在內(nèi)核空間使用專用的內(nèi)核API接口
  • ④ 支持多播(因此支持“總線”式通信,可實現(xiàn)消息訂閱)
  • ⑤ 在內(nèi)核端可用于進程上下文與中斷上下文

2,用戶態(tài)數(shù)據(jù)結(jié)構(gòu)

首先看一下幾個重要的數(shù)據(jù)結(jié)構(gòu)的關(guān)系:

Linux用戶空間與內(nèi)核空間通信(Netlink通信機制)

1.struct msghdr

msghdr這個結(jié)構(gòu)在socket變成中就會用到,并不算Netlink專有的,這里不在過多說明。只說明一下如何更好理解這個結(jié)構(gòu)的功能。我們知道socket消息的發(fā)送和接收函數(shù)一般有這幾對:recv/send、readv/writev、recvfrom/sendto。當(dāng)然還有recvmsg/sendmsg,前面三對函數(shù)各有各的特點功能,而recvmsg/sendmsg就是要囊括前面三對的所有功能,當(dāng)然還有自己特殊的用途。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作用一樣,他們的結(jié)構(gòu)對比如下:

Linux用戶空間與內(nèi)核空間通信(Netlink通信機制)

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

struct sockaddr_nl
{
    sa_family_t nl_family; /*該字段總是為AF_NETLINK */
    unsigned short nl_pad; /* 目前未用到,填充為0*/
    __u32 nl_pid; /* process pid */
    __u32 nl_groups; /* multicast groups mask */
};

(1) nl_pid:在Netlink規(guī)范里,PID全稱是Port-ID(32bits),其主要作用是用于唯一的標(biāo)識一個基于netlink的socket通道。通常情況下nl_pid都設(shè)置為當(dāng)前進程的進程號。前面我們也說過,Netlink不僅可以實現(xiàn)用戶-內(nèi)核空間的通信還可使現(xiàn)實用戶空間兩個進程之間,或內(nèi)核空間兩個進程之間的通信。該屬性為0時一般指內(nèi)核。

(2) nl_groups:如果用戶空間的進程希望加入某個多播組,則必須執(zhí)行bind()系統(tǒng)調(diào)用。該字段指明了調(diào)用者希望加入的多播組號的掩碼(注意不是組號,后面我們會詳細講解這個字段)。如果該字段為0則表示調(diào)用者不希望加入任何多播組。對于每個隸屬于Netlink協(xié)議域的協(xié)議,最多可支持32個多播組(因為nl_groups的長度為32比特),每個多播組用一個比特來表示。

3.struct nlmsghdr

Netlink的報文由消息頭和消息體構(gòu)成,struct nlmsghdr即為消息頭。消息頭定義在文件里,由結(jié)構(gòu)體nlmsghdr表示:

struct nlmsghdr
{
    __u32 nlmsg_len; /* Length of message including header */
    __u16 nlmsg_type; /* Message content */
    __u16 nlmsg_flags; /* Additional flags */
    __u32 nlmsg_seq; /* Sequence number */
    __u32 nlmsg_pid; /* Sending process PID */
};

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

(1) nlmsg_len:整個消息的長度,按字節(jié)計算。包括了Netlink消息頭本身。

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

  • a) NLMSG_NOOP-空消息,什么也不做;
  • b) NLMSG_ERROR-指明該消息中包含一個錯誤;
  • c) NLMSG_DONE-如果內(nèi)核通過Netlink隊列返回了多個消息,那么隊列的最后一條消息的類型為NLMSG_DONE,其余所有消息的nlmsg_flags屬性都被設(shè)置NLM_F_MULTI位有效。
  • d) NLMSG_OVERRUN-暫時沒用到。

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

3,用戶空間Netlink socket API

1.創(chuàng)建socket

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

domain指代地址族,即AF_NETLINK;

套接字類型為SOCK_RAW或SOCK_DGRAM,因為netlink是一個面向數(shù)據(jù)報的服務(wù);

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

以下是幾種預(yù)定義的協(xié)議類型:

  • NETLINK_ROUTE,
  • NETLINK_FIREWALL,
  • NETLINK_APRD,
  • NETLINK_ROUTE6_FW。

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

內(nèi)核資料領(lǐng)取, Linux內(nèi)核源碼學(xué)習(xí)地址。

2.地址綁定bind()

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

3.發(fā)送netlink消息

為了發(fā)送一條netlink消息到內(nèi)核或者其他的用戶空間進程,另外一個struct sockaddr_nl nladdr需要作為目的地址,這和使用sendmsg()發(fā)送一個UDP包是一樣的。

  • 如果該消息是發(fā)送至內(nèi)核的,那么nl_pid和nl_groups都置為0.
  • 如果消息是發(fā)送給另一個進程的單播消息,nl_pid是另外一個進程的pid值而nl_groups為零。
  • 如果消息是發(fā)送給一個或多個多播組的多播消息,所有的目的多播組必須bitmask必須or起來從而形成nl_groups域。sendmsg(fd, &, msg, 0);

4.接收netlink消息

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

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

4、內(nèi)核空間Netlink socket API

1.創(chuàng)建 netlink socket

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

參數(shù)說明:

  • (1) net:是一個網(wǎng)絡(luò)名字空間namespace,在不同的名字空間里面可以有自己的轉(zhuǎn)發(fā)信息庫,有自己的一套net_device等等。默認情況下都是使用 init_net這個全局變量。
  • (2) unit:表示netlink協(xié)議類型,如NETLINK_TEST、NETLINK_SELINUX。
  • (3) groups:多播地址。
  • (4) input:為內(nèi)核模塊定義的netlink消息處理函數(shù),當(dāng)有消 息到達這個netlink socket時,該input函數(shù)指針就會被引用,且只有此函數(shù)返回時,調(diào)用者的sendmsg才能返回。
  • (5) cb_mutex:為訪問數(shù)據(jù)時的互斥信號量。
  • (6) module: 一般為THIS_MODULE。

2.發(fā)送單播消息 netlink_unicast

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

參數(shù)說明:

  • (1) ssk:為函數(shù) netlink_kernel_create()返回的socket。
  • (2) skb:存放消息,它的data字段指向要發(fā)送的netlink消息結(jié)構(gòu),而 skb的控制塊保存了消息的地址信息,宏NETLINK_CB(skb)就用于方便設(shè)置該控制塊。
  • (3) pid:為接收此消息進程的pid,即目標(biāo)地址,如果目標(biāo)為組或內(nèi)核,它設(shè)置為 0。
  • (4) nonblock:表示該函數(shù)是否為非阻塞,如果為1,該函數(shù)將在沒有接收緩存可利用時立即返回;而如果為0,該函數(shù)在沒有接收緩存可利用定時睡眠。

3.發(fā)送廣播消息 netlink_broadcast

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

前面的三個參數(shù)與 netlink_unicast相同,參數(shù)group為接收消息的多播組,該參數(shù)的每一個位代表一個多播組,因此如果發(fā)送給多個多播組,就把該參數(shù)設(shè)置為多個多播組組ID的位或。參數(shù)allocation為內(nèi)核內(nèi)存分配類型,一般地為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)

5、用戶態(tài)

范例一

#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include <errno.h>
#define MAX_PAYLOAD 1024 // maximum payload size
#define NETLINK_TEST 25 //自定義的協(xié)議
int main(int argc, char* argv[])
{
    int state;
    struct sockaddr_nl src_addr, dest_addr;
    struct nlmsghdr *nlh = NULL; //Netlink數(shù)據(jù)包頭
    struct iovec iov;
    struct msghdr msg;
    int sock_fd, retval;
    int state_smg = 0;
    // Create a socket
    sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
    if(sock_fd == -1){
        printf("error getting socket: %s", strerror(errno));
        return -1;
    }
    // To prepare binding
    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = 100; //A:設(shè)置源端端口號
    src_addr.nl_groups = 0;
    //Bind
    retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
    if(retval < 0){
        printf("bind failed: %s", strerror(errno));
        close(sock_fd);
        return -1;
    }
    // To orepare create mssage
    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
    if(!nlh){
        printf("malloc nlmsghdr error!\n");
        close(sock_fd);
        return -1;
}
    memset(&dest_addr,0,sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0; //B:設(shè)置目的端口號
    dest_addr.nl_groups = 0;
    nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
    nlh->nlmsg_pid = 100; //C:設(shè)置源端口
    nlh->nlmsg_flags = 0;
    strcpy(NLMSG_DATA(nlh),"Hello you!"); //設(shè)置消息體
    iov.iov_base = (void *)nlh;
    iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
    //Create mssage
    memset(&msg, 0, sizeof(msg));
    msg.msg_name = (void *)&dest_addr;
    msg.msg_namelen = sizeof(dest_addr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;
    //send message
    printf("state_smg\n");
    state_smg = sendmsg(sock_fd,&msg,0);
    if(state_smg == -1)
    {
        printf("get error sendmsg = %s\n",strerror(errno));
    }
    memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));
    //receive message
    printf("waiting received!\n");
    while(1){
        printf("In while recvmsg\n");
        state = recvmsg(sock_fd, &msg, 0);
        if(state<0)
        {
            printf("state<1");
        }
        printf("Received message: %s\n",(char *) NLMSG_DATA(nlh));
    }
    close(sock_fd);
    return 0;
}

上面程序首先向內(nèi)核發(fā)送一條消息;“Hello you”,然后進入循環(huán)一直等待讀取內(nèi)核的回復(fù),并將收到的回復(fù)打印出來。如果看上面程序感覺很吃力,那么應(yīng)該首先復(fù)習(xí)一下UDP中使用sendmsg的用法,特別時struct msghdr的結(jié)構(gòu)要清楚,這里再贅述。

下面主要分析與UDP發(fā)送數(shù)據(jù)包的不同點:

  • \1. socket地址結(jié)構(gòu)不同,UDP為sockaddr_in,Netlink為struct sockaddr_nl;
  • \2. 與UDP發(fā)送數(shù)據(jù)相比,Netlink多了一個消息頭結(jié)構(gòu)struct nlmsghdr需要我們構(gòu)造。

注意代碼注釋中的A、B、C三處分別設(shè)置了pid。首先解釋一下什么是pid,網(wǎng)上很多文章把這個字段說成是進程的pid,其實這完全是望文生義。這里的pid和進程pid沒有什么關(guān)系,僅僅相當(dāng)于UDP的port。對于UDP來說port和ip標(biāo)示一個地址,那對我們的NETLINK_TEST協(xié)議(注意Netlink本身不是一個協(xié)議)來說,pid就唯一標(biāo)示了一個地址。所以你如果用進程pid做為標(biāo)示當(dāng)然也是可以的。當(dāng)然同樣的pid對于NETLINK_TEST協(xié)議和內(nèi)核定義的其他使用Netlink的協(xié)議是不沖突的(就像TCP的80端口和UDP的80端口)。

下面分析這三處設(shè)置pid分別有什么作用,首先A和B位置的比較好理解,這是在地址(sockaddr_nl)上進行的設(shè)置,就是相當(dāng)于設(shè)置源地址和目的地址(其實是端口),只是注意B處設(shè)置pid為0,0就代表是內(nèi)核,可以理解為內(nèi)核專用的pid,那么用戶進程就不能用0做為自己的pid嗎?這個只能說如果你非要用也是可以的,只是會產(chǎn)生一些問題,后面在分析。

接下來看為什么C處的消息頭仍然需要設(shè)置pid呢?這里首先要知道一個前提:內(nèi)核不會像UDP一樣根據(jù)我們設(shè)置的原、目的地址為我們構(gòu)造消息頭,所以我們不在包頭寫入我們自己的地址(pid),那內(nèi)核怎么知道是誰發(fā)來的報文呢?當(dāng)然如果內(nèi)核只是處理消息不需要回復(fù)進程的話舍不設(shè)置這個消息頭pid都可以。

所以每個pid的設(shè)置功能不同:A處的設(shè)置是要設(shè)置發(fā)送者的源地址,有人會說既然源地址又不會自動填充到報文中,我們?yōu)槭裁催€要設(shè)置這個,因為你還可能要接收回復(fù)啊。就像寄信,你連“門牌號”都沒有,即使你在寫信時候?qū)懮夏愕牡刂肥?00號,對方回信目的地址也是100號,但是郵局發(fā)現(xiàn)根本沒有這個地址怎么可能把信送到你手里呢?所以A的主要作用是注冊源地址,保證可以收到回復(fù),如果不需要回復(fù)當(dāng)然可以簡單將pid設(shè)置為0;B處自然就是收信人的地址,pid為0代表內(nèi)核的地址,假如有一個進程在101號上注冊了地址,并調(diào)用了recvmsg,如果你將B處的pid設(shè)置為101,那數(shù)據(jù)包就發(fā)給了另一個進程,這就實現(xiàn)了使用Netlink進行進程間通信;C相當(dāng)于你在信封上寫的源地址,通常情況下這個應(yīng)該和你的真實地址(A)處注冊的源地址相同,當(dāng)然你要是不想收到回信,又想惡搞一下或者有特殊需求,你可以寫成其他進程注冊的pid(比如101)。這和我們現(xiàn)實中寄信是一樣的,你給你朋友寫封情書,把寫信人寫成你的另一個好基友,然后后果你懂得……

好了,有了這個例子我們就大概知道用戶態(tài)怎么使用Netlink了。

6、內(nèi)核態(tài)程序

范例一

#include <linux/init.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/time.h>
#include <linux/types.h>
#include <net/sock.h>
#include <net/netlink.h>
#define NETLINK_TEST 25
#define MAX_MSGSIZE 1024
int stringlength(char *s);
int err;
struct sock *nl_sk = NULL;
int flag = 0;
//向用戶態(tài)進程回發(fā)消息
void sendnlmsg(char *message, int pid)
{
    struct sk_buff *skb_1;
    struct nlmsghdr *nlh;
    int len = NLMSG_SPACE(MAX_MSGSIZE);
    int slen = 0;
    if(!message || !nl_sk)
    {
        return ;
    }
    printk(KERN_ERR "pid:%d\n",pid);
    skb_1 = alloc_skb(len,GFP_KERNEL);
    if(!skb_1)
    {
        printk(KERN_ERR "my_net_link:alloc_skb error\n");
    }
    slen = stringlength(message);
    nlh = nlmsg_put(skb_1,0,0,0,MAX_MSGSIZE,0);
    NETLINK_CB(skb_1).pid = 0;
    NETLINK_CB(skb_1).dst_group = 0;
    message[slen]= '\0';
    memcpy(NLMSG_DATA(nlh),message,slen+1);
    printk("my_net_link:send message '%s'.\n",(char *)NLMSG_DATA(nlh));
    netlink_unicast(nl_sk,skb_1,pid,MSG_DONTWAIT);
}
int stringlength(char *s)
{
    int slen = 0;
    for(; *s; s++)
    {
        slen++;
    }
    return slen;
}
//接收用戶態(tài)發(fā)來的消息
void nl_data_ready(struct sk_buff *__skb)
 {
     struct sk_buff *skb;
     struct nlmsghdr *nlh;
     char str[100];
     struct completion cmpl;
     printk("begin data_ready\n");
     int i=10;
     int pid;
     skb = skb_get (__skb);
     if(skb->len >= NLMSG_SPACE(0))
     {
         nlh = nlmsg_hdr(skb);
         memcpy(str, NLMSG_DATA(nlh), sizeof(str));
         printk("Message received:%s\n",str) ;
         pid = nlh->nlmsg_pid;
         while(i--)
        {//我們使用completion做延時,每3秒鐘向用戶態(tài)回發(fā)一個消息
            init_completion(&cmpl);
            wait_for_completion_timeout(&cmpl,3 * HZ);
            sendnlmsg("I am from kernel!",pid);
        }
         flag = 1;
         kfree_skb(skb);
    }
 }
// Initialize netlink
int netlink_init(void)
{
    nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 1,
                                 nl_data_ready, NULL, THIS_MODULE);
    if(!nl_sk){
        printk(KERN_ERR "my_net_link: create netlink socket error.\n");
        return 1;
    }
    printk("my_net_link_4: create netlink socket ok.\n");
    return 0;
}
static void netlink_exit(void)
{
    if(nl_sk != NULL){
        sock_release(nl_sk->sk_socket);
    }
    printk("my_net_link: self module exited\n");
}
module_init(netlink_init);
module_exit(netlink_exit);
MODULE_AUTHOR("zhao_h");
MODULE_LICENSE("GPL");

附上內(nèi)核代碼的Makefile文件:

ifneq ($(KERNELRELEASE),)
obj-m :=netl.o
else
KERNELDIR ?=/lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif

我們將內(nèi)核模塊insmod后,運行用戶態(tài)程序,結(jié)果如下:

Linux用戶空間與內(nèi)核空間通信(Netlink通信機制)

這個結(jié)果復(fù)合我們的預(yù)期,但是運行過程中打印出“state_smg”卡了好久才輸出了后面的結(jié)果。這時候查看客戶進程是處于D狀態(tài)的(不了解D狀態(tài)的同學(xué)可以google一下)。這是為什么呢?因為進程使用Netlink向內(nèi)核發(fā)數(shù)據(jù)是同步,內(nèi)核向進程發(fā)數(shù)據(jù)是異步。什么意思呢?也就是用戶進程調(diào)用sendmsg發(fā)送消息后,內(nèi)核會調(diào)用相應(yīng)的接收函數(shù),但是一定到這個接收函數(shù)執(zhí)行完用戶態(tài)的sendmsg才能夠返回。我們在內(nèi)核態(tài)的接收函數(shù)中調(diào)用了10次回發(fā)函數(shù),每次都等待3秒鐘,所以內(nèi)核接收函數(shù)30秒后才返回,所以我們用戶態(tài)程序的sendmsg也要等30秒后才返回。相反,內(nèi)核回發(fā)的數(shù)據(jù)不用等待用戶程序接收,這是因為內(nèi)核所發(fā)的數(shù)據(jù)會暫時存放在一個隊列中。

再來回到之前的一個問題,用戶態(tài)程序的源地址(pid)可以用0嗎?我把上面的用戶程序的A和C處pid都改為了0,結(jié)果一運行就死機了。為什么呢?我們看一下內(nèi)核代碼的邏輯,收到用戶消息后,根據(jù)消息中的pid發(fā)送回去,而pid為0,內(nèi)核并不認為這是用戶程序,認為是自身,所有又將回發(fā)的10個消息發(fā)給了自己(內(nèi)核),這樣就陷入了一個死循環(huán),而用戶態(tài)這時候進程一直處于D。

另外一個問題,如果同時啟動兩個用戶進程會是什么情況?答案是再調(diào)用bind時出錯:“Address already in use”,這個同UDP一樣,同一個地址同一個port如果沒有設(shè)置SO_REUSEADDR兩次bind就會出錯,之后我用同樣的方式再Netlink的socket上設(shè)置了SO_REUSEADDR,但是并沒有什么效果。

7、用戶態(tài)

范例二

之前我們說過UDP可以使用sendmsg/recvmsg也可以使用sendto/recvfrom,那么Netlink同樣也可以使用sendto/recvfrom。具體實現(xiàn)如下:

#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/socket.h>
#include <errno.h>
#define MAX_PAYLOAD 1024 // maximum payload size
#define NETLINK_TEST 25
int main(int argc, char* argv[])
{
    struct sockaddr_nl src_addr, dest_addr;
    struct nlmsghdr *nlh = NULL;
    int sock_fd, retval;
    int state,state_smg = 0;
    // Create a socket
    sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
    if(sock_fd == -1){
        printf("error getting socket: %s", strerror(errno));
        return -1;
    }
    // To prepare binding
    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = 100;
    src_addr.nl_groups = 0;
    //Bind
    retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
    if(retval < 0){
        printf("bind failed: %s", strerror(errno));
        close(sock_fd);
        return -1;
    }
    // To orepare create mssage head
    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
    if(!nlh){
        printf("malloc nlmsghdr error!\n");
        close(sock_fd);
        return -1;
    }
    memset(&dest_addr,0,sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0;
    dest_addr.nl_groups = 0;
    nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
    nlh->nlmsg_pid = 100;
    nlh->nlmsg_flags = 0;
    strcpy(NLMSG_DATA(nlh),"Hello you!");
    //send message
    printf("state_smg\n");
    sendto(sock_fd,nlh,NLMSG_LENGTH(MAX_PAYLOAD),0,(struct sockaddr*)(&dest_addr),sizeof(dest_addr));
    if(state_smg == -1)
    {
        printf("get error sendmsg = %s\n",strerror(errno));
    }
    memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));
    //receive message
    printf("waiting received!\n");
while(1){
        printf("In while recvmsg\n");
state=recvfrom(sock_fd,nlh,NLMSG_LENGTH(MAX_PAYLOAD),0,NULL,NULL);
        if(state<0)
        {
            printf("state<1");
        }
        printf("Received message: %s\n",(char *) NLMSG_DATA(nlh));
        memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD));
    }
    close(sock_fd);
    return 0;
}

熟悉UDP編程的同學(xué)看到這個程序一定很熟悉,除了多了一個Netlink消息頭的設(shè)置。但是我們發(fā)現(xiàn)程序中調(diào)用了bind函數(shù),這個函數(shù)再UDP編程中的客戶端不是必須的,因為我們不需要把UDP socket與某個地址關(guān)聯(lián),同時再發(fā)送UDP數(shù)據(jù)包時內(nèi)核會為我們分配一個隨即的端口。但是對于Netlink必須要有這一步bind,因為Netlink內(nèi)核可不會為我們分配一個pid。再強調(diào)一遍消息頭(nlmsghdr)中的pid是告訴內(nèi)核接收端要回復(fù)的地址,但是這個地址存不存在內(nèi)核并不關(guān)心,這個地址只有用戶端調(diào)用了bind后才存在。

我們看到這兩個例子都是用戶態(tài)首先發(fā)起的,那Netlink是否支持內(nèi)核態(tài)主動發(fā)起的情況呢?

當(dāng)然是可以的,只是內(nèi)核一般需要事件觸發(fā),這里,只要和用戶態(tài)約定號一個地址(pid),內(nèi)核直接調(diào)用netlink_unicast就可以了。

ock_fd);
    return 0;
}

熟悉UDP編程的同學(xué)看到這個程序一定很熟悉,除了多了一個Netlink消息頭的設(shè)置。但是我們發(fā)現(xiàn)程序中調(diào)用了bind函數(shù),這個函數(shù)再UDP編程中的客戶端不是必須的,因為我們不需要把UDP socket與某個地址關(guān)聯(lián),同時再發(fā)送UDP數(shù)據(jù)包時內(nèi)核會為我們分配一個隨即的端口。但是對于Netlink必須要有這一步bind,因為Netlink內(nèi)核可不會為我們分配一個pid。再強調(diào)一遍消息頭(nlmsghdr)中的pid是告訴內(nèi)核接收端要回復(fù)的地址,但是這個地址存不存在內(nèi)核并不關(guān)心,這個地址只有用戶端調(diào)用了bind后才存在。

我們看到這兩個例子都是用戶態(tài)首先發(fā)起的,那Netlink是否支持內(nèi)核態(tài)主動發(fā)起的情況呢?

當(dāng)然是可以的,只是內(nèi)核一般需要事件觸發(fā),這里,只要和用戶態(tài)約定號一個地址(pid),內(nèi)核直接調(diào)用netlink_unicast就可以了。

版權(quán)聲明:本文為知乎博主「玩轉(zhuǎn)Linux內(nèi)核」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://zhuanlan.zhihu.com/p/458996875文章來源地址http://www.zghlxwxcb.cn/news/detail-440013.html

到了這里,關(guān)于Linux用戶空間與內(nèi)核空間通信(Netlink通信機制)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • Linux下C語言使用 netlink sockets與內(nèi)核模塊通信

    Linux下C語言使用 netlink sockets與內(nèi)核模塊通信

    Netlink套接字是用以實現(xiàn)用戶進程與內(nèi)核進程通信的一種特殊的進程間通信(IPC) ,也是網(wǎng)絡(luò)應(yīng)用程序與內(nèi)核通信的最常用的接口。在Linux標(biāo)準(zhǔn)內(nèi)核中,系統(tǒng)默認集成了很多netlink實例,比如日志上報、路由系統(tǒng)等,netlink消息是雙向的,應(yīng)用層可以發(fā)送消息到內(nèi)核,同時內(nèi)核也可以

    2024年02月07日
    瀏覽(84)
  • ACE通信設(shè)計空間指導(dǎo)

    通信是網(wǎng)絡(luò)應(yīng)用程序設(shè)計的基礎(chǔ)。本章就通信設(shè)計空間做了領(lǐng)域分析,介紹了網(wǎng)絡(luò)應(yīng)用程序之間互相作用的規(guī)則、形式和抽象層次。 本章我們將討論一下設(shè)計空間: 所謂協(xié)議,就是一組規(guī)則,用來指定“控制信息”和“數(shù)據(jù)信息”如何在通信實體(譬如,在網(wǎng)絡(luò)計算環(huán)境內(nèi)部

    2024年02月10日
    瀏覽(19)
  • Netlink 通信機制

    Netlink套接字是用以實現(xiàn)用戶進程與內(nèi)核進程通信的一種特殊的進程間通信(IPC) ,也是網(wǎng)絡(luò)應(yīng)用程序與內(nèi)核通信的最常用的接口。 在Linux 內(nèi)核中,使用netlink 進行應(yīng)用與內(nèi)核通信的應(yīng)用有很多,如 路由 daemon(NETLINK_ROUTE) 用戶態(tài) socket 協(xié)議(NETLINK_USERSOCK) 防火墻(NETLINK_FIREWA

    2024年02月01日
    瀏覽(12)
  • Linux用戶與內(nèi)核空間交互—ioctl

    目錄 簡介 一、交互方法筆記與總結(jié) 二、ioctl 三、實戰(zhàn) 1、頭文件 2、應(yīng)用程序 3、內(nèi)核程序 4、 程序輸出 用戶空間與內(nèi)核的交互方式,使用copy_from_user(), copy_to_user(). 除了這兩種交互方式,內(nèi)核還提供了其他高級的方式,對于寫驅(qū)動來說很重要。有proc、sysfs、debugfs、netlink、

    2024年02月10日
    瀏覽(17)
  • Linux字符設(shè)備驅(qū)動(設(shè)備文件,用戶空間與內(nèi)核空間進行數(shù)據(jù)交互,ioctl接口)

    Linux字符設(shè)備驅(qū)動(設(shè)備文件,用戶空間與內(nèi)核空間進行數(shù)據(jù)交互,ioctl接口)

    在Linux系統(tǒng)中“一切皆文件”,上一篇講述了cdev結(jié)構(gòu)體就描述了一個字符設(shè)備驅(qū)動,主要包括設(shè)備號和操作函數(shù)集合。但是要怎么操作這個驅(qū)動呢?例如,使用open()該打開誰,read()該從哪讀取數(shù)據(jù)等等。所以就需要創(chuàng)建一個設(shè)備文件來代表設(shè)備驅(qū)動。 應(yīng)用程序要操縱外部硬件

    2024年02月12日
    瀏覽(27)
  • 1024程序員狂歡節(jié)有好禮 | 前沿技術(shù)、人工智能、集成電路科學(xué)與芯片技術(shù)、新一代信息與通信技術(shù)、網(wǎng)絡(luò)空間安全技術(shù)

    1024程序員狂歡節(jié)有好禮 | 前沿技術(shù)、人工智能、集成電路科學(xué)與芯片技術(shù)、新一代信息與通信技術(shù)、網(wǎng)絡(luò)空間安全技術(shù)

    ??歡迎來到 愛書不愛輸?shù)某绦蛟?的博客, 本博客致力于知識分享,與更多的人進行學(xué)習(xí)交流 ?????? 點擊直達福利 一年一度的1024程序員狂歡節(jié)又到啦!成為更卓越的自己,堅持閱讀和學(xué)習(xí),別給自己留遺憾,行動起來吧! 那么,都有哪些好書值得入手呢?小編為大家整理

    2024年02月08日
    瀏覽(23)
  • 進程空間管理:用戶態(tài)和內(nèi)核態(tài)

    進程空間管理:用戶態(tài)和內(nèi)核態(tài)

    用戶態(tài)虛擬空間里面有幾類數(shù)據(jù),例如代碼、全局變量、堆、棧、內(nèi)存映射區(qū)等。在 struct mm_struct 里面,有下面這些變量定義了這些區(qū)域的統(tǒng)計信息和位置。 其中,total_vm 是總共映射的頁的數(shù)目。我們知道,這么大的虛擬地址空間,不可能都有真實內(nèi)存對應(yīng),所以這里是映射

    2024年02月06日
    瀏覽(14)
  • 內(nèi)核和用戶空間中的TID,GID, PID,uid

    內(nèi)核和用戶空間中的TID,GID, PID,uid

    要獲取關(guān)于eBPF中的進程信息,可以使用以下函數(shù): bpf_get_current_pid_tgid()、 bpf_get_current_uid_gid()、 bpf_get_current_comm(char *buf, int size_of_buf)。 當(dāng)程序被綁定到對某個內(nèi)核函數(shù)調(diào)用時,就可以使用它們。UID/GID應(yīng)該比較明確,但對于那些以前沒有接觸過內(nèi)核操作細節(jié)的人來說,還是需要

    2024年02月07日
    瀏覽(20)
  • Windows驅(qū)動(用戶層R3與內(nèi)核層R0通信)

    Windows驅(qū)動(用戶層R3與內(nèi)核層R0通信)

    內(nèi)存空間分為用戶層和系統(tǒng)層,普通的應(yīng)用程序只能運行在用戶層,為了可以操作系統(tǒng)層的內(nèi)存 所以引入了驅(qū)動程序,有了驅(qū)動就可以通過用戶層來操作系統(tǒng)層的內(nèi)存及函數(shù),所以驅(qū)動就是應(yīng)用層和系統(tǒng)層之間的一個橋梁 在應(yīng)用層通過創(chuàng)建符號鏈接,自動產(chǎn)生驅(qū)動層的IRP事

    2024年02月14日
    瀏覽(18)
  • 深入理解Linux 內(nèi)核追蹤機制

    深入理解Linux 內(nèi)核追蹤機制

    Linux 存在眾多 tracing tools,比如 ftrace、perf,他們可用于內(nèi)核的調(diào)試、提高內(nèi)核的可觀測性。眾多的工具也意味著繁雜的概念,諸如 tracepoint、trace events、kprobe、eBPF 等,甚至讓人搞不清楚他們到底是干什么的。本文嘗試理清這些概念。 ? Probe Handler 如果我們想要追蹤內(nèi)核的一

    2024年02月15日
    瀏覽(30)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包