linux中netlink机制的实例讲解

原创 2017年11月28日 17:52:14
netlink作为内核层与用户层双向通信的交互机制广泛的应用在网络驱动及字符驱动的uevent机制中。
Netlink 相对于系统调用,ioctl 以及 /proc文件系统而言具有以下优点:
1,netlink使用简单,只需要在include/linux/netlink.h中增加一个新类型的 netlink 协议定义即可,就是定义一个还未使用的整数。
2. netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息;
3.使用 netlink 的内核部分可以采用模块的方式实现,使用 netlink 的应用部分和内核部分没有编译时依赖;
4.netlink 支持多播,内核模块或应用可以把消息多播给一个netlink组,属于该neilink 组的任何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性;
5.内核可以使用 netlink 首先发起会话;
6.netlink通信是全双工通信。

用户层代码示例:
netlink机制用户层的使用与普通的socket几乎相同
#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[])
{
    int state;
    struct sockaddr_nl src_addr, dest_addr;
    struct nlmsghdr *nlh = NULL; //Netlink数据包头
    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:设置源端端口号
    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:设置目的端口号
    dest_addr.nl_groups = 0;
    nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
    nlh->nlmsg_pid = 100; //C:设置源端口
    nlh->nlmsg_flags = 0;
    strcpy(NLMSG_DATA(nlh),"Hello you!"); //设置消息体
    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;
}
内核层代码示例:
在内核中的实现尤其是netlink_kernel_create函数在2.6内核发生过变化
linux 2.6.22 kernel层代码
#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;
//向用户态进程回发消息
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;
}
static void nl_data_handler(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);
	printk("radia 1 skb->len:%d NLMSG_SPACE(0):%d\n", skb->len, NLMSG_SPACE(0));


	while (skb->len >= NLMSG_SPACE(0)) {
	 printk("radia 2\n");
         nlh = nlmsg_hdr(skb);
	 printk("radia 3\n");
         memcpy(str, NLMSG_DATA(nlh), sizeof(str));
         printk("Message received:%s\n",str) ;
         pid = nlh->nlmsg_pid;
         while(i--)
        {//我们使用completion做延时,每3秒钟向用户态回发一个消息
	 printk("radia 4\n");
            init_completion(&cmpl);
	 printk("radia 5\n");
            wait_for_completion_timeout(&cmpl,3 * HZ);
	 printk("radia 6\n");
            sendnlmsg("I am from kernel!",pid);
        }
         flag = 1;
         kfree_skb(skb);
    }
}

static void nl_data_recv(struct sock *sk, int len)
{
	struct sk_buff *skb;


	while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
		nl_data_handler(skb);
		kfree_skb(skb);
	}
}

// Initialize netlink
int netlink_init(void)
{
    nl_sk = netlink_kernel_create(NETLINK_TEST, 1,
                                 nl_data_recv, 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("yilong");
MODULE_LICENSE("GPL");
linux 2.6.24 kernel中netlink_kernel_create接口还有一次变化,此处没有给出示例,为方便使用一般参考drivers\scsi\scsi_netlink.c中对netlink的使用
linux 3.18.66 kernel层代码

#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;
struct netlink_kernel_cfg nkc;

int flag = 0;
//向用户态进程回发消息
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 "radia enter sendnlmsg pid:%d len:%d\n",pid, len);
    skb_1 = nlmsg_new(len,GFP_KERNEL);
    if(!skb_1)
    {
        printk(KERN_ERR "radia 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).dst_group = 0;
    memcpy(NLMSG_DATA(nlh),message,slen+1);
    printk("radia 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;
}
//接收用户态发来的消息
void nl_data_recv(struct sk_buff *__skb)
 {
     struct sk_buff *skb;
     struct nlmsghdr *nlh;
     char str[100];
     struct completion cmpl;
     int i=10;
     int pid;
     skb = skb_get (__skb);
     printk("radia begin data_ready\n");
     if(skb->len >= NLMSG_SPACE(0))
     {
		nlh = nlmsg_hdr(skb);
		memcpy(str, NLMSG_DATA(nlh), sizeof(str));
		printk("radia Message received:%s\n",str) ;
		pid = nlh->nlmsg_pid;
		while(i--)
		{//我们使用completion做延时,每3秒钟向用户态回发一个消息
			printk("radia netlink enter while loop %d\n", i);
			init_completion(&cmpl);
			printk("radia netlink init_completion!\n");
			wait_for_completion_timeout(&cmpl,3 * HZ);
			printk("radia netlink after completion!\n");
			sendnlmsg("I am from kernel!",pid);
		}
		flag = 1;
		kfree_skb(skb);
    }
 }
// Initialize netlink
int netlink_init(void)
{
	nkc.groups = 1;
	nkc.input = nl_data_recv;
	nkc.cb_mutex = NULL;
    nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, &nkc);
    if(!nl_sk){
        printk(KERN_ERR "radia my_net_link: create netlink socket error.\n");
        return 1;
    }
    printk("radia 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("radia my_net_link: self module exited\n");
}
module_init(netlink_init);
module_exit(netlink_exit);
MODULE_AUTHOR("radia");
MODULE_LICENSE("GPL");



版权声明:本文为博主原创文章,未经博主允许不得转载。如本文对您有帮助,欢迎点赞评论。

linux netlink机制介绍与实例

开发和维护内核是一件很繁杂的工作,因此,只有那些最重要或者与系统性能息息相关的代码才将其安排在内核中。其它程序,比如GUI,管理以及控制部分的代码,一般都会作为用户态程序。在linux系统中,把系统的...

Linux中与内核通信的Netlink机制(实例)

Netlink在2.6版本的内核中变化也是很大的,在最新的2.6.37内核中,其定义已经改成下面这种形式,传递的参数已经达到6个。其中第一个参数和mutex参数都是最新添加的。Mutex也可以为空。这...

Linux中与内核通信的Netlink机制(实例)

Netlink在2.6版本的内核中变化也是很大的,在最新的2.6.37内核中,其定义已经改成下面这种形式,传递的参数已经达到6个。其中第一个参数和mutex参数都是最新添加的。Mutex也可以为空。这...

Linux中与内核通信的Netlink机制(实例)

Netlink在2.6版本的内核中变化也是很大的,在最新的2.6.37内核中,其定义已经改成下面这种形式,传递的参数已经达到6个。其中第一个参数和mutex参数都是最新添加的。Mutex也可以为空。这...

netlink机制和udev实例

LINUX netlink机制   用户空间的程序与设备通信的方法,主要有以下几种方式:   1 通过ioperm获取操作IO端口的权限,然后用inb/inw/ inl/ outb/out...

inux netlink机制介绍与实例

开发和维护内核是一件很繁杂的工作,因此,只有那些最重要或者与系统性能息息相关的代码才将其安排在内核中。其它程序,比如GUI,管理以及控制部分的代码,一般都会作为用户态程序。在linux系统中,把系统的...

netlink机制和udev实例

http://blog.csdn.net/smartmz/article/details/6036554 LINUX netlink机制     用户空间的程序与设备通信的方法,...

linux用户态和内核态通信之netlink机制

这是一篇学习笔记,主要是对《Linux 系统内核空间与用户空间通信的实现与分析》中的源码imp2的分析。其中的源码,可以到以下URL下载:  http://www-128.ibm.com/d...

Linux Netlink通信机制详解

前面有一篇文章其实已经介绍过Netlink方面的知识,还有一个内核和用户空间之间的一个交互例子,这篇文章主要是更细节和基础的知识介绍! Netlink 是一种特殊的 socket,它是 Linux ...

netlink---Linux下基于socket的内核和上层通信机制(上)

目录(?)[+] 我最近有一个项目需求,需要在linux网卡驱动中加入一个自己的驱动,实现在内核态完成一些报文处理(这个过程可以实现一种零COPY的网络报文截获),对于复杂报文C...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:linux中netlink机制的实例讲解
举报原因:
原因补充:

(最多只允许输入30个字)