Android:内核与用户层通信之netlink(源码实际运行测试)

                   Android:内核与用户层通信之netlink

 

一、一般来说用户空间和内核空间的通信方式有三种:proc、ioctl、Netlink。而前两种都是单向的,但是Netlink可以实现双工通信。Netlink协议基于BSD socket和AF_NETLINK地址簇(address family),使用32位的端口号寻址(以前称作PID),每个Netlink协议(或称作总线,man手册中则称之为netlink family),通常与一个或一组内核服务/组件相关联,如NETLINK_ROUTE用于获取和设置路由与链路信息、NETLINK_KOBJECT_UEVENT用于内核向用户空间的udev进程发送通知等。

 

二、实例测试

        1.内核驱动c程序 netlink.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
 
#include <net/netlink.h>
#include <net/sock.h>
 
#define NETLINK_TEST (25)
#define MAX_MSGSIZE 1024 
static dev_t devId;
static struct class *cls = NULL;
struct sock *nl_sk = NULL;


int stringlength(char *s)
{
    int slen = 0;
    for(; *s; s++)
    {
        slen++;
    }
    return slen;
}

static void
hello_cleanup(void)
{
        netlink_kernel_release(nl_sk);
        device_destroy(cls, devId);
        class_destroy(cls);
        unregister_chrdev_region(devId, 1);
}
 
static void
netlink_send(int pid, uint8_t *message, int len)
{
        struct sk_buff *skb_1;
        struct nlmsghdr *nlh;
 
        if(!message || !nl_sk) {
                return;
        }
 
        skb_1 = alloc_skb(NLMSG_SPACE(len), GFP_KERNEL);
        if( !skb_1 ) {
                printk(KERN_ERR "alloc_skb error!\n");
        }
 
        nlh = nlmsg_put(skb_1, 0, 0, 0, len, 0);
        NETLINK_CB(skb_1).portid = 0;
        NETLINK_CB(skb_1).dst_group = 0;
        memcpy(NLMSG_DATA(nlh), message, len);
        netlink_unicast(nl_sk, skb_1, pid, MSG_DONTWAIT);
}
//向用户态进程回发消息
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).portid = 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);
}

static void
netlink_input(struct sk_buff *__skb)
{
        struct sk_buff *skb;
        char str[100];
        struct nlmsghdr *nlh;
 
        if( !__skb ) {
                return;
        }
 
        skb = skb_get(__skb);
        if( skb->len < NLMSG_SPACE(0)) {
                return;
        }
 
        nlh = nlmsg_hdr(skb);
        memset(str, 0, sizeof(str));
        memcpy(str, NLMSG_DATA(nlh), sizeof(str));
        printk(KERN_INFO "receive message (pid:%d):%s\n", nlh->nlmsg_pid, str);
        printk(KERN_INFO "space:%d\n", NLMSG_SPACE(0));
        printk(KERN_INFO "size:%d\n", nlh->nlmsg_len);
		sendnlmsg("message from kernel!",nlh->nlmsg_pid);
        netlink_send(nlh->nlmsg_pid, NLMSG_DATA(nlh), nlh->nlmsg_len - NLMSG_SPACE(0));
        
        return;
}

static __init int netlink_init(void)
{
        int result;
        struct netlink_kernel_cfg nkc;
 
        printk(KERN_WARNING "netlink init start!\n");
 
        //动态注册设备号
        if(( result = alloc_chrdev_region(&devId, 0, 1, "stone-alloc-dev") ) != 0) {
                printk(KERN_WARNING "register dev id error:%d\n", result);
                goto err;
        } else {
                printk(KERN_WARNING "register dev id success!\n");
        }
        //动态创建设备节点
        cls = class_create(THIS_MODULE, "stone-class");
        if(IS_ERR(cls)) {
                printk(KERN_WARNING "create class error!\n");
                goto err;
        }
        if(device_create(cls, NULL, devId, "", "hello%d", 0) == NULL) {
                printk(KERN_WARNING "create device error!\n");
                goto err;
        }
 
        //初始化netlink
        nkc.groups = 0;
        nkc.flags = 0;
        nkc.input = netlink_input;
        nkc.cb_mutex = NULL;
        nkc.bind = NULL;
        nkc.unbind = NULL;
        nkc.compare = NULL;
        nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, &nkc);
        if( !nl_sk ) {
                printk(KERN_ERR "[netlink] create netlink socket error!\n");
                goto err;
        }
 
        printk(KERN_ALERT "netlink init success!\n");
        return 0;
err:
        hello_cleanup();
        return -1;
}
static __exit void netlink_exit(void)
{
        hello_cleanup();
        printk(KERN_WARNING "netlink exit!\n");
}
 
module_init(netlink_init);
module_exit(netlink_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("haiyuexichen");

 

     2、Makefile文件

obj-m += netlink.o 
 
KDIR := ~/xxxxxxxxxxxxxx/kernel
PWD ?= $(shell pwd)
 
 
all:
	make -C $(KDIR) M=$(PWD) modules
		
clean:
	rm -rf *.o

     3、用户态c文件socket.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 NETLINK_TEST    (25)
#define MAX_PAYLOAD     (1024)
#define TEST_PID        (100)
 
int netlink_create_socket(void)
{
        //create a socket
        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 = TEST_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 = TEST_PID;
        nlh->nlmsg_flags = 0;
        memcpy(NLMSG_DATA(nlh), message, len);
 
        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;
 
        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;
        char buffer[1024];
		int ret;
		int state;
        if( !message || !len ) {
                return -1;
        }
 
        //create message
        nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
        if( !nlh ) {
                perror("malloc");
                return -2;
        }
        iov.iov_base = (void *)nlh;
        iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
        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;

     /*   ret = read(sock_fd, &buffer, sizeof(buffer));
		 //printf("read buffer=%s ret=%d\r\n",buffer,ret);
		 
		for (int i = 0; i < ret;) {
           char *event = buffer + i;
           printf("read buffer=%s ret=%d\r\n",event,ret);
           i += strlen(event) + 1;
         }
	*/
        printf("ready to receive message!\n");
        state = recvmsg(sock_fd, &msg, 0);
        if(state<0)
            {
                printf("recvmsg error!\n");
                return -3;
            }
        *len = nlh->nlmsg_len - NLMSG_SPACE(0);
        memcpy(message, (unsigned char *)NLMSG_DATA(nlh), *len);

 /*
        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_PAYLOAD];
        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);
		while(1){
        if( netlink_recv_message(sock_fd, buf, &len) == 0 ) {
                printf("recv:%s len:%d\n", buf, len);
        }
        }
        close(sock_fd);
        return 0;
}

4、编译模块ko和用户态文件

5、把ko和用户层可执行文件push到android系统下,真机执行测试效果。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值