我是靠谱客的博主 光亮盼望,最近开发中收集的这篇文章主要介绍Linux 24 内核和用户通信之netlink,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Netlink的通信地址和协议

一般来说用户空间和内核空间的通信方式有三种:/proc、ioctl、Netlink。而前两种都是单向的,而Netlink可以实现双工通信。
所有socket之间的通信,必须有个地址结构,Netlink也不例外。我们最熟悉的就是IPV4的地址了,netlink的地址结构如下:

struct sockaddr_nl  
{  
    sa_family_t nl_family;          //必须为AF_NETLINK或者PF_NETLINK  
    unsigned short  nl_pad;             //必须为0  
    __u32       nl_pid;             //通信端口  
__u32       nl_groups;              //组播掩码  
};  

本质上,nl_pid就是netlink的通信地址(双方约定好)。除了通信地址,netlink还提供“协议”来标示通信实体,在创建socket的时候,需要指定netlink的通信协议号。每个协议号代表一种“应用”,上层可以用内核已经定义的协议和内核进行通信,获得内核已经提供的信息。具体支持的协议列表如下:

#define NETLINK_ROUTE       0   /* Routing/device hook              */  
#define NETLINK_UNUSED      1   /* Unused number                */  
#define NETLINK_USERSOCK    2   /* Reserved for user mode socket protocols  */  
#define NETLINK_FIREWALL    3   /* Firewalling hook             */  
#define NETLINK_INET_DIAG   4   /* INET socket monitoring           */  
#define NETLINK_NFLOG       5   /* netfilter/iptables ULOG */  
#define NETLINK_XFRM        6   /* ipsec */  
#define NETLINK_SELINUX     7   /* SELinux event notifications */  
#define NETLINK_ISCSI       8   /* Open-iSCSI */  
#define NETLINK_AUDIT       9   /* auditing */  
#define NETLINK_FIB_LOOKUP  10    
#define NETLINK_CONNECTOR   11  
#define NETLINK_NETFILTER   12  /* netfilter subsystem */  
#define NETLINK_IP6_FW      13  
#define NETLINK_DNRTMSG     14  /* DECnet routing messages */  
#define NETLINK_KOBJECT_UEVENT  15  /* Kernel messages to userspace */  
#define NETLINK_GENERIC     16  
/* leave room for NETLINK_DM (DM Events) */  
#define NETLINK_SCSITRANSPORT   18  /* SCSI Transports */  
#define NETLINK_ECRYPTFS    19  
struct msghdr {
    void         *msg_name;       /* optional address */
    socklen_t     msg_namelen;    /* size of address */
    struct iovec *msg_iov;        /* scatter/gather array */
    size_t        msg_iovlen;     /* # elements in msg_iov */
    void         *msg_control;    /* ancillary data, see below */
    size_t        msg_controllen; /* ancillary data buffer len */
    int           msg_flags;      /* flags (unused) */
};

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 */
};

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 */
};

协议号一般作为测试可以直接使用NETLINK_GENERIC
通信端口一般原则如下:
生成式1:选择应用程序的pid作为nl_pid的值。nl_pid = getpid();
生成式2:这种方式下,同一进程内的不同线程都可以为同一Netlink 协议类型申请自己特有的socket。实际上,即使在一个线程内,创建多个基于相同协议类型的Netlink Socket也是可能的。然而,开发者需要在如何生成唯一nl_pid上更具创造性。nl_pid = pthread_self() << 16 | getpid();

netlink采用协议号+通信端口的方式构建自己的地址体系。

nlmsg_type:消息状态,内核在include/uapi/linux/netlink.h中定义了以下4种通用的消息类型,它们分别是

#define NLMSG_NOOP      0x1 /* Nothing.     */
#define NLMSG_ERROR     0x2 /* Error        */
#define NLMSG_DONE      0x3 /* End of a dump    */
#define NLMSG_OVERRUN       0x4 /* Data lost        */
#define NLMSG_MIN_TYPE      0x10    /* < 0x10: reserved control messages */

nlmsg_flags:消息标记,它们用以表示消息的类型,如下

/* Flags values */
#define NLM_F_REQUEST       1   /* It is request message.   */
#define NLM_F_MULTI     2   /* Multipart message, terminated by NLMSG_DONE */
#define NLM_F_ACK       4   /* Reply with ack, with zero or error code */
#define NLM_F_ECHO      8   /* Echo this request        */
#define NLM_F_DUMP_INTR     16  /* Dump was inconsistent due to sequence change */

/* Modifiers to GET request */
#define NLM_F_ROOT  0x100   /* specify tree root    */
#define NLM_F_MATCH 0x200   /* return all matching  */
#define NLM_F_ATOMIC    0x400   /* atomic GET       */
#define NLM_F_DUMP  (NLM_F_ROOT|NLM_F_MATCH)

/* Modifiers to NEW request */
#define NLM_F_REPLACE   0x100   /* Override existing        */
#define NLM_F_EXCL  0x200   /* Do not touch, if it exists   */
#define NLM_F_CREATE    0x400   /* Create, if it does not exist */
#define NLM_F_APPEND    0x800   /* Add to end of list       */
用户态接口
用户态创建、销毁socket的过程:

1、 用socket函数创建,socket(PF_NETLINK, SOCK_DGRAM, NETLINK_XXX);第一个参数必须是PF_NETLINK或者AF_NETLINK,第二个参数用SOCK_DGRAM和SOCK_RAW都没问题,第三个参数就是netlink的协议号。
2、 用bind函数绑定自己的地址。
3、 用close关闭套接字。

发送Netlink message

如果消息是发往内核的,nl_pid和nl_groups字段都应该置0。
如果是发往另一个进程的单播消息, nl_pid应该是目标进程的pid而nl_groups字段置0(假设系统采用生成式1计算nl_pid)。
如果是发往一个或多个多播组的消息,所有目标多播组对应的掩码应该执行"OR"操作后填入nl_groups字段。
按如下方式,向sendmsg()API 所需要的 msghdr结构提供目标Netlink 地址。

struct msghdr msg;  
msg.msg_name = (void *)&(nladdr);  
msg.msg_namelen = sizeof(nladdr);  

Netlink Socket 还需要有自己的消息头部。这是为了为所有Netlink协议类型提供一个公共基础。
由于Linux内核中的Netlink核心假设如下头部在每个Netlink message中的存在,用户必须为每个发送的Netlink message提供这个头部。

struct nlmsghdr  
{  
  __u32 nlmsg_len;   /* Length of message */                //消息总长度  
  __u16 nlmsg_type;  /* Message type*/                        //消息类型  
  __u16 nlmsg_flags; /* Additional flags */                //附加控制  
  __u32 nlmsg_seq;   /* Sequence number */                 //序列号  
  __u32 nlmsg_pid;   /* Sending process PID */             //发送方的pid  
}; 
struct iovec iov;  
iov.iov_base = (void *)nlh;  
iov.iov_len = nlh->nlmsg_len;  
msg.msg_iov = &iov;  
msg.msg_iovlen = 1; /* iovec数目 */
接收Netlink message

接收进程需要分配足够大的缓冲区来存放Netlink message(包括消息头部消息负载)。然后需要填写如下的struct msghdr,并调用标准的recvmsg()来接收Netlink message(此处假设nth指向缓冲区)

struct sockaddr_nl nladdr;  
struct msghdr msg;  
struct iovec iov;  
iov.iov_base = (void *)nlh;  
iov.iov_len = MAX_NL_MSG_LEN;  
msg.msg_name = (void *)&(nladdr);  
msg.msg_namelen = sizeof(nladdr);  
msg.msg_iov = &iov;  
msg.msg_iovlen = 1;  
recvmsg(fd, &msg, 0);  
内核空间使用的Netlink API

内核空间的Netlink API是由Netlink核心在net/core/af_netlink.c文件提供的。内核使用与用户空间不同的API。内核模块可以调用这些API来操纵Netlink Socket,并与用户空间程序通讯。若不打算利用已有的Netlink协议类型,用户必须通过在netlink.h中添加常量来添加自己的协议。

创建socket
static inline struct sock *
netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
/* net: net指向所在的网络命名空间, 一般默认传入的是&init_net(不需要定义);  定义在net_namespace.c(extern struct net init_net);
   unit:netlink协议类型
   cfg: cfg存放的是netlink内核配置参数(如下)
*/

/* optional Netlink kernel configuration parameters */
struct netlink_kernel_cfg {
    unsigned int    groups;  
    unsigned int    flags;  
    void        (*input)(struct sk_buff *skb); /* input 回调函数 */
    struct mutex    *cb_mutex; 
    void        (*bind)(int group); 
    bool        (*compare)(struct net *net, struct sock *sk);
};

函数指针input,指向一个回调函数,该函数在有消息到达Netink Socket时被调用。
在内核创建了一个类型为NETLINK_GENERIC的Netlink Socket后,无论何时用户空间向内核发送一条类型为NETLINK_GENERIC的Netlink message时,之前调用netlink_kernel_create()时通过input参数注册的回调函数被调用。
input()函数是在由发送进程所激发的sendmeg()系统调用的上下文环境中执行的。如果对该Netlink message的处理速度很快的话,在input()函数中执行对消息的处理是没有问题的。但是如果对该Netlink message的处理是耗时操作,为了避免阻止其他系统调用"陷入"内核,应该将处理操作移出input()函数。这种情况下可以使用一个内核线程来无限循环的完成下述操作。

使用 skb = skb_recv_datagram(nl_sk),其中nl_sk是 netlink_kernel_create()返回的Netlink Socket。然后,处理由skb->data所指向的netlink message。

内核线程在nl_sk中没有Netlink message时睡眠。因此,在回调函数input()中,只需要唤醒睡眠的内核进程,如下:

void input (struct sock *sk, int len)
{
   wake_up_interruptible(sk->sleep);
}
从内核发送Netlink message
/* 发送单播消息 */
extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
/*
 ssk: netlink socket 
 skb: skb buff 指针
 portid: 通信的端口号
 nonblock:表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利用定时睡眠
*/

/* 发送多播消息 */
extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid,
                 __u32 group, gfp_t allocation);
/* 
   ssk: 同上(对应netlink_kernel_create 返回值)、
   skb: 内核skb buff
   portid: 端口id
   group: 是所有目标多播组对应掩码的"OR"操作的合值。
   allocation: 指定内核内存分配方式,通常GFP_ATOMIC用于中断上下文,而GFP_KERNEL用于其他场合。这个参数的存在是因为该API可能需要分配一个或多个缓冲区来对多播消息进行clone
*/
关闭socket
sock_release(nl_sk->socket);

netlink常用宏:

#define NLMSG_ALIGNTO   4U
/* 宏NLMSG_ALIGN(len)用于得到不小于len且字节对齐的最小数值 */
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )

/* Netlink 头部长度 */
#define NLMSG_HDRLEN     ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))

/* 计算消息数据len的真实消息长度(消息体 + 消息头)*/
#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)

/* 宏NLMSG_SPACE(len)返回不小于NLMSG_LENGTH(len)且字节对齐的最小数值 */
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))

/* 宏NLMSG_DATA(nlh)用于取得消息的数据部分的首地址,设置和读取消息数据部分时需要使用该宏 */
#define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))

/* 宏NLMSG_NEXT(nlh,len)用于得到下一个消息的首地址, 同时len 变为剩余消息的长度 */
#define NLMSG_NEXT(nlh,len)  ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), 
                  (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))

/* 判断消息是否 >len */
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && 
               (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && 
               (nlh)->nlmsg_len <= (len))

/* NLMSG_PAYLOAD(nlh,len) 用于返回payload的长度*/
#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
实例

内核态:

/* ker_space.c */

#include <linux/kernel.h>  
#include <linux/module.h>  
#include <linux/init.h>  
#include <linux/sched.h>
#include <net/sock.h>  
#include <linux/netlink.h>

#define MAX_MSGSIZE 64
#define NETLINK_TEST 30

#define USER_PID 100

static struct sock *netlinkfd;

static void sendData(char *data, int pid, int len)
{
    struct sk_buff *skb;
    struct nlmsghdr *nlh;

    /* 分配sk_buff */
    skb = alloc_skb(NLMSG_SPACE(len), GFP_KERNEL);
    if(!skb)
    {
        printk("alloc_skb error!n");
        return;
    }

    nlh = nlmsg_put(skb, 0, 0, 0, len, 0);
    NETLINK_CB(skb).portid = 0;
    NETLINK_CB(skb).dst_group = 0;
    memcpy(NLMSG_DATA(nlh), data, len); /* 拷贝数据到netlink msg的数据部分 */
    netlink_unicast(netlinkfd, skb, pid, MSG_DONTWAIT); /* 内核态发包 */
}

static void recvData(struct sk_buff *skb)
{
    struct nlmsghdr *nlh; 
    int pid = 0;
    char *data;
    char *sendBuf = "Hello user, I am kernel!";
    
    if(skb->len >= NLMSG_SPACE(0))
    {
		nlh = nlmsg_hdr(skb);
		data = NLMSG_DATA(nlh);
		printk("msg from user is:%sn", data ? data : "");

        pid = nlh->nlmsg_pid;   /* 获取用户pid */
        sendData(sendBuf, pid, strlen(sendBuf));
    }
}

static int netlink_start(void)  
{  
    struct netlink_kernel_cfg cfg = {
    	.input = recvData,              /* 收包回调 */
    };

    /* 内核态创建socket */
    netlinkfd = netlink_kernel_create(&init_net, NETLINK_TEST, &cfg);
    if (!netlinkfd)
    {
    	printk("create netlink socket failed!n");
    	return -1;
    }
    printk("create netlink socket success!n");
    return 0;  
}  

static void netlink_stop(void)  
{  
    if (netlinkfd)
    {
        netlink_kernel_release(netlinkfd);
    }
    printk("netlink stop!n");  
}  

module_init(netlink_start);  
module_exit(netlink_stop);  
MODULE_LICENSE("GPL");


用户态1(使用sendto和recvfrom):

/* 使用sendto和recvfrom和内核通信 */
/* user_space.c */

#include <sys/socket.h>  
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/netlink.h>   
#include <errno.h>

#define MAX_MSGSIZE 64
#define NETLINK_TEST 30

#define USER_PID 100 

struct sockaddr_nl src_addr, dest_addr;  
struct nlmsghdr *nlh = NULL; 
struct msghdr msg;  
struct iovec iov;  
int sock_fd;  

int main() 
{  
	int sendlen = 0;
    int ret = 0;
    socklen_t addrlen = sizeof(struct sockaddr_nl);
    
	/* create socket, protocol type:NETLINK_TEST */
    sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);
    if (sock_fd < 0)
    {
        perror("create netlink socket error");
        return -1;
    }
    
    /* set source(for send) socket addr:nl_family + nl_pid */
    memset(&src_addr, 0, sizeof(src_addr));  
    src_addr.nl_family = AF_NETLINK;       
    src_addr.nl_pid = USER_PID;//getpid();  /* self pid */  
    src_addr.nl_groups = 0;  /* not in mcast groups */
    
    /* bind addr to socket */
    bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));  
    
    /* set dst(for recv) socket addr:nl_family + nl_pid */
    memset(&dest_addr, 0, sizeof(dest_addr));  
    dest_addr.nl_family = AF_NETLINK;  
    dest_addr.nl_pid = 0;   /* For Linux Kernel */  
    dest_addr.nl_groups = 0; /* unicast */  

    /* alloc nlmsghdr */
    nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));  
    
    /* Fill the netlink message header */  
    nlh->nlmsg_len = NLMSG_SPACE(MAX_MSGSIZE);  
    nlh->nlmsg_type = 0;
    nlh->nlmsg_flags = 0; 
    nlh->nlmsg_seq = 0;
    nlh->nlmsg_pid = src_addr.nl_pid; /* pid */  
    
    /* Fill in the netlink message payload */  
    strcpy(NLMSG_DATA(nlh), "Hello kernel!");  

    ret = sendto(sock_fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_nl));
    if(!ret)
    {
        perror("sendto errorn");
        close(sock_fd);
        exit(-1);
    }
    #if 0
    iov.iov_base = (void *)nlh;  
    iov.iov_len = nlh->nlmsg_len;
    
    msg.msg_name = (void *)&dest_addr;  
    msg.msg_namelen = sizeof(dest_addr);  
    msg.msg_iov = &iov;  
    msg.msg_iovlen = 1;  
    sendlen = sendmsg(sock_fd, &msg, 0);
    if (sendlen < 0)
    {
        perror("send error");
        close(sock_fd);
        return;
    }
    
    /* Read message from kernel */  
    memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));  
    recvmsg(sock_fd, &msg, 0);  
    printf("received message from kernel: %s/n", NLMSG_DATA(nlh));
    #endif

    if( recvfrom(sock_fd, nlh, NLMSG_SPACE(MAX_MSGSIZE), 0, (struct sockaddr *)&src_addr, (socklen_t *)&addrlen) < 0 ) 
    {
        printf("recvmsg error!n");
        close(sock_fd); 
        return -1;
    }

    printf("received message from kernel: %sn", (char *)NLMSG_DATA(nlh));
    
    /* Close Netlink Socket */  
    close(sock_fd);
    return 0;
}    

用户态2(使用sendmsg和recvmsg):

/* 使用sendmsg和recvmsg和内核通信 */
/* user_space_rx.c */

#include <sys/stat.h>  
#include <unistd.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <sys/socket.h>  
#include <string.h>  
#include <asm/types.h>  
#include <linux/netlink.h>  
#include <linux/socket.h>  
#include <errno.h>  
  
#define MAX_MSGSIZE 64
#define NETLINK_TEST 30

#define USER_PID 100 
  
int netlink_create_socket(void)  
{  
    //create a socket, protocol NETLINK_TEST
    return socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);  
}  
  
int netlink_bind(int sock_fd)  
{  
    struct sockaddr_nl addr;  
  
    memset(&addr, 0, sizeof(struct sockaddr_nl));  
    addr.nl_family = AF_NETLINK;  
    addr.nl_pid = USER_PID;  
    addr.nl_groups = 0;  
  
    return bind(sock_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_nl));  
}  
  
int netlink_send_message(int sock_fd, const unsigned char *message, int len,  
                                        unsigned int pid, unsigned int group)  
{  
    struct nlmsghdr *nlh = NULL;  
    struct sockaddr_nl dest_addr;  
    struct iovec iov;  
    struct msghdr msg;  
  
    if (!message) 
    {  
        return -1;  
    }  
  
    /* create message */
    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(len));  
    if( !nlh ) 
    {  
        perror("malloc");  
        return -2;  
    }  
    nlh->nlmsg_len = NLMSG_SPACE(len);  
    nlh->nlmsg_pid = USER_PID;  
    nlh->nlmsg_flags = 0;  
    memcpy(NLMSG_DATA(nlh), message, len);  /* copy data */

    iov.iov_base = (void *)nlh;  
    iov.iov_len = nlh->nlmsg_len;  
    memset(&dest_addr, 0, sizeof(struct sockaddr_nl));  
    dest_addr.nl_family = AF_NETLINK;  
    dest_addr.nl_pid = pid;  
    dest_addr.nl_groups = group;  

    /* set msghdr */
    memset(&msg, 0, sizeof(struct msghdr));  
    msg.msg_name = (void *)&dest_addr;  
    msg.msg_namelen = sizeof(struct sockaddr_nl);  
    msg.msg_iov = &iov;  
    msg.msg_iovlen = 1;  

    /* send message */  
    if( sendmsg(sock_fd, &msg, 0) < 0 )  
    {  
        printf("send error!n");  
        free(nlh);  
        return -3;  
    }  

    free(nlh);
    
    return 0;  
}  
  
int  
netlink_recv_message(int sock_fd, unsigned char *message, int *len)  
{  
    struct nlmsghdr *nlh = NULL;  
    struct sockaddr_nl source_addr;  
    struct iovec iov;  
    struct msghdr msg;  

    if( !message || !len ) 
    {  
        return -1;  
    }  

    //create message  
    nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));  
    if( !nlh ) 
    {  
        perror("malloc");  
        return -2;  
    }  
    
    iov.iov_base = (void *)nlh;  
    iov.iov_len = NLMSG_SPACE(MAX_MSGSIZE);  
    memset(&source_addr, 0, sizeof(struct sockaddr_nl));  
    memset(&msg, 0, sizeof(struct msghdr));  
    msg.msg_name = (void *)&source_addr;  
    msg.msg_namelen = sizeof(struct sockaddr_nl);  
    msg.msg_iov = &iov;  
    msg.msg_iovlen = 1;  

    if ( recvmsg(sock_fd, &msg, 0) < 0 ) 
    {  
        printf("recvmsg error!n");  
        return -3;  
    }  
    *len = nlh->nlmsg_len - NLMSG_SPACE(0);  
    memcpy(message, (unsigned char *)NLMSG_DATA(nlh), *len);  

    free(nlh); 
    
    return 0;  
}  
  
int main(int argc, char **argv)  
{  
        int sock_fd;  
        char buf[MAX_MSGSIZE];  
        int len;  
  
        if( argc < 2) 
        {  
            printf("enter message!n");  
            exit(EXIT_FAILURE);  
        }  
  
        sock_fd = netlink_create_socket();  
        if(sock_fd == -1) 
        {  
            printf("socket error!n");  
            return -1;  
        }  
  
        if( netlink_bind(sock_fd) < 0 ) 
        {  
            perror("bind");  
            close(sock_fd);  
            exit(EXIT_FAILURE);  
        }  
  
        netlink_send_message(sock_fd, argv[1], strlen(argv[1]) + 1, 0, 0);
        
        if( netlink_recv_message(sock_fd, buf, &len) == 0 ) 
        {  
            printf("recv:%s len:%dn", buf, len);  
        }  
  
        close(sock_fd); 
        
        return 0;  
}  

Makefile:

USER_APP = user_space
USER_OBJ = user_space.o
USER_APP_RX = user_space_rx
USER_OBJ_RX = user_space_rx.o
KER_APP = ker_space
obj-m += ker_space.o

KERNEL_PATH = /lib/modules/$(shell uname -r)/build/
  
all: $(USER_APP) $(USER_APP_RX) $(KER_APP)

$(USER_APP): $(USER_OBJ)
	gcc $(USER_OBJ) -o $(USER_APP)

$(USER_APP_RX): $(USER_OBJ_RX)
	gcc $(USER_OBJ_RX) -o $(USER_APP_RX)
	
$(KER_APP):
	make -C $(KERNEL_PATH) M=$(PWD) modules
	
clean:  
	make -C $(KERNEL_PATH) M=$(PWD) clean
	rm -f *.o user_space user_space_rx
	

其他比较好的例子:
Linux Netlink 编程 :https://www.jianshu.com/p/073bcd9c3b08

最后

以上就是光亮盼望为你收集整理的Linux 24 内核和用户通信之netlink的全部内容,希望文章能够帮你解决Linux 24 内核和用户通信之netlink所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部