目录
要求:实现一个网络链路通信模块,用户发送字符串数据后,内核将字符串翻转,然后回复给用户态程序
与系统调用,/ proc,sysfs等类似,netlink也是一种用于用户进程与内核通信的机制,它是基于BSD套接字协议,使用AF_NETLINK地址簇。与系统调用,proc,sysfs文件系统等方式相比,网络链路具有简单,支持双向通信的特点,并支持消息多播机制。
1,编写内核模块
vim netlink_mod.c:
#include <linux/module.h> #include <net/sock.h> #include <linux/netlink.h> #include <linux/skbuff.h> #define MY_NETLINK_TYPE 31 //max is 32, see netlink.h struct sock *my_nl_sock = NULL; static void hello_nl_recv_msg(struct sk_buff *skb) { struct nlmsghdr *nlh; int pid; struct sk_buff *skb_out; int msg_size; char *msg=NULL; char *msg_zj=NULL; int res; int i; printk(KERN_INFO "Entering: %s\n", __FUNCTION__); //msg_size = strlen(msg); nlh = (struct nlmsghdr *)skb->data;//Kernel Space receive message from user printk(KERN_INFO "Netlink receive msg: %s\n", (char *)nlmsg_data(nlh)); msg =nlmsg_data(nlh); printk(KERN_INFO "msg:%s\n",msg); msg_size = strlen(msg); printk(KERN_INFO "size of msg:%d\n",msg_size); //String inversion msg_zj = (char *)kmalloc(msg_size + 1,GFP_KERNEL); memset(msg_zj, 0, msg_size + 1); for(i = 0; i < msg_size;i++) { msg_zj[i]=msg[msg_size-i-1]; } printk(KERN_INFO "msg_zj:%s\n",msg_zj); pid = nlh->nlmsg_pid; skb_out = nlmsg_new(msg_size, 0); if (!skb_out) { printk(KERN_INFO "alloc nlmsg failed\n"); return; } nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0); NETLINK_CB(skb_out).dst_group = 0; //strncpy(nlmsg_data(nlh), msg, msg_size); strncpy(nlmsg_data(nlh), msg_zj, msg_size); res = nlmsg_unicast(my_nl_sock, skb_out, pid);//Kernel Space send message toUser Space if (res < 0) printk(KERN_INFO "nlmsg unicast failed\n"); kfree(msg_zj); return; } static int __init hello_init(void) { struct netlink_kernel_cfg cfg = { .groups = 0, .input = hello_nl_recv_msg }; printk("Entering: %s:\n", __FUNCTION__); my_nl_sock = netlink_kernel_create(&init_net, MY_NETLINK_TYPE, &cfg); if (!my_nl_sock) { printk(KERN_ALERT "netlink create sock failed\n"); return -10; } return 0; } static void __exit hello_exit(void) { printk(KERN_INFO "Exiting hello module\n"); netlink_kernel_release(my_nl_sock); my_nl_sock = NULL; } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL");
2,增加相应的生成文件
vim Makefile:
obj-m += netlink_mod.o #KVERSION = $(shell uname -r) #PWD = $(shell pwd) all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
如果觉得文章对你有帮助,欢迎关注微信公众号:小牛呼噜噜
3,编写用户进程代码
用户进程主要使用netlink的socket与内核进行通信,首先向内核发送一条消息,然后再接收内核的消息,代码如下:
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <sys/socket.h> #include <linux/netlink.h> #define MY_NETLINK_TYPE 31 #define MAX_PAYLOAD 1024 /* maximum payload size*/ struct sockaddr_nl src_addr, dest_addr; struct nlmsghdr *nlh = NULL; struct iovec iov; int sock_fd; struct msghdr msg; int main(int argc, char *argv[]) { sock_fd = socket(PF_NETLINK, SOCK_DGRAM, MY_NETLINK_TYPE); if (sock_fd < 0) return -1; memset(&src_addr, 0, sizeof(src_addr)); src_addr.nl_family = AF_NETLINK; src_addr.nl_pid = getpid(); /* self pid */ bind(sock_fd, (struct sockaddr *)&src_addr, sizeof(src_addr)); memset(&dest_addr, 0, sizeof(dest_addr)); 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 */ nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD)); memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD)); nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); nlh->nlmsg_pid = getpid(); nlh->nlmsg_flags = 0; strcpy(NLMSG_DATA(nlh), "Hello"); 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; printf("Sending message to kernel<<<%s\n",NLMSG_DATA(nlh)); sendmsg(sock_fd, &msg, 0);//User Space send message to kernel printf("Waiting for message from kernel\n"); /* Read message from kernel */ recvmsg(sock_fd, &msg, 0);//User Space receive message from kernel printf("Received message payload>>>%s\n", NLMSG_DATA(nlh)); close(sock_fd); return 0; }
4,测试结果
编译模块:
使
加载该模块:
sudo insmod netlink_mod.ko
加载后可用的lsmod查看是否加载成功,然后编译并运行用户进程hello_user
gcc user.c -o user
运行
./user
可在终端中看到如下结果:
Sending message to kernel<<<Hello Waiting for message from kernel Received message payload>>>olleH 上面是用户进程的输出,可用dmesg命令查看内核模块的输出(或者直接查看/var/log/kern).
卸载模块
sudo rmmod netlink_mod
参考:https://yangjin-unique.github.io/ru-he-zai-linuxnei-he-mo-kuai-zhong-jia-ru-netlinktong-xin-jie-kou.html