linux下用户程序同内核通信详解(netlink机制)

吾爱主题 阅读:148 2024-04-05 13:52:44 评论:0

简介

linux下用户程序同内核通信的方式一般有ioctl, proc文件系统,剩下一个就是Netlink套接字了。 这里先介绍下netlink。

Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket API 就可以使用 netlink 提供的强大功能,内核态需要使用专门的内核 API 来使用 netlink。

Netlink 相对于系统调用,ioctl 以及 /proc 文件系统而言具有以下优点:

1,为了使用 netlink,用户仅需要在 include/linux/netlink.h 中增加一个新类型的 netlink 协议定义即可, 如 #define NETLINK_MYTEST 17 然后,内核和用户态应用就可以立即通过 socket API 使用该 netlink 协议类型进行数据交换。但系统调用需要增加新的系统调用,ioctl 则需要增加设备或文件, 那需要不少代码,proc 文件系统则需要在 /proc 下添加新的文件或目录,那将使本来就混乱的 /proc 更加混乱。

2. netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接 收队列,而不需要等待接收者收到消息,但系统调用与 ioctl 则是同步通信机制,如果传递的数据太长,将影响调度粒度。

3.使用 netlink 的内核部分可以采用模块的方式实现,使用 netlink 的应用部分和内核部分没有编译时依赖,但系统调用就有依赖,而且新的系统调用的实现必须静态地连接到内核中,它无法在模块中实现,使用新系统调用的应用在编译时需要依赖内核。

4.netlink 支持多播,内核模块或应用可以把消息多播给一个netlink组,属于该neilink 组的任何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性,任何对内核事件感兴趣的应用都能收到该子系统发送的内核事件,在 后面的文章中将介绍这一机制的使用。

5.内核可以使用 netlink 首先发起会话,但系统调用和 ioctl 只能由用户应用发起调用。

6.netlink 使用标准的 socket API,因此很容易使用,但系统调用和 ioctl则需要专门的培训才能使用。

下面这两部分代码主要的目的是用netlink机制实现用户程序和内核的通信。 具体就是用户程序执行./netlink -S [我是参数] 或./netlink -G 时,内核会返回"S know you!" 和“I know you!” 这两种字符串, 然后输出。 内核和用户程序均加有打印。

内核模块

1. Makefile依赖的编译规则 ruler.dir

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 PWD := $(shell pwd)   all: modules romfs   modules:   $(MAKE) -C $(KDIR) M=$(PWD) modules   @echo $(OBJ)   modules_install:   $(MAKE) -C $(KDIR) M=$(PWD) modules_install   romfs:   cp -rf *.ko $(MODULES_BUILD_DIR)   clean:   rm *.o *.ko *.mod.* Module.* modules.*   rm -rf $(MODULES_BUILD_DIR)   obj-m := $(MOD_NAME).o

2.Makefile

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 KERNEL_MODULES := netlink   export MODULES_ROOT_DIR := $(shell pwd) export MODULES_BUILD_DIR := $(MODULES_ROOT_DIR)/build export KDIR := /lib/modules/$(shell uname -r)/build #这行是为了取出系统下内核的目录(ubuntu)   all: init modules romfs   init:   mkdir -p $(MODULES_BUILD_DIR)   modules:$(patsubst %, _dir_%, $(KERNEL_MODULES))   $(patsubst %, _dir_%, $(KERNEL_MODULES)):   @echo   @echo Building $(patsubst _dir_%, %, $@)   $(MAKE) -C $(patsubst _dir_%, %, $@) all     romfs: $(patsubst %, _romfs_%, $(KERNEL_MODULES)) $(patsubst %, _romfs_%, $(KERNEL_MODULES)):   $(MAKE) -C $(patsubst _romfs_%, %, $@) romfs     clean: $(patsubst %, _clean_%, $(KERNEL_MODULES))   $(RM) $(BUILD_DIR) $(patsubst %, _clean_%, $(KERNEL_MODULES)):   @echo   @echo Cleaning $(patsubst _dir_%, %, $@)   $(MAKE) -C $(patsubst _clean_%, %, $@) clean   .PHONY:

3. ./netlink/netlink.c

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 /*   * netlink.c   *   * Created on: 2014 *  Author: cr   */   #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/skbuff.h> #include <linux/ip.h> #include <linux/types.h> #include <linux/sched.h> #include <linux/netlink.h> #include <net/sock.h> #include "usrlink.h"   MODULE_LICENSE( "Dual BSD/GPL" ); MODULE_AUTHOR( "MDAXIA" );   struct sock *netlink_fd;     static void netlink_to_user( int dest, void *buf, int len) {   struct nlmsghdr *nl;   struct sk_buff *skb;   int size;     size = NLMSG_SPACE(len);   skb = alloc_skb(size, GFP_ATOMIC);   if (!skb || !buf)   {    printk(KERN_ALERT "netlink_to_user skb of buf null! " );    return ;   }   nl = nlmsg_put(skb, 0, 0, 0, NLMSG_SPACE(len) - sizeof ( struct nlmsghdr), 0);   NETLINK_CB(skb).pid = 0;   NETLINK_CB(skb).dst_group = 0;     memcpy (NLMSG_DATA(nl), buf, len);   nl->nlmsg_len = (len > 2) ? (len - 2):len;     netlink_unicast(netlink_fd, skb, dest, MSG_DONTWAIT);   printk(KERN_ALERT "K send packet success " ); }   static int process_hello_get( int dest, void *buf, int len) {   printk(KERN_ALERT "In process_hello get! " );   memcpy (buf, "I known you !" , 13);   netlink_to_user(dest, buf, 13);   return NET_OK; }   static int process_hello_set( int dest, void *buf, int len) {   printk(KERN_ALERT "In process_hello set! %s " , ( char *)buf);   memcpy (buf, "S known you !" , 13);   netlink_to_user(dest, buf, 13);   return NET_OK; }     static void netlink_process_packet( struct nlmsghdr *nl) {   int ret;     switch (nl->nlmsg_type)   {   case HELLO_GET:    ret = process_hello_get(nl->nlmsg_pid, NLMSG_DATA(nl), nl->nlmsg_len);    break ;   case HELLO_SET:    ret = process_hello_set(nl->nlmsg_pid, NLMSG_DATA(nl), nl->nlmsg_len);    break ;   default : break ;   } }   static void netlink_recv_packet( struct sk_buff *__skb) {   struct sk_buff *skb;   struct nlmsghdr *nlhdr;     skb = skb_get(__skb);   if (skb->len >= sizeof ( struct nlmsghdr))   {    nlhdr = ( struct nlmsghdr *)skb->data;    if (nlhdr->nlmsg_len >= sizeof ( struct nlmsghdr) &&      __skb->len >= nlhdr->nlmsg_len)    {     netlink_process_packet(nlhdr);    }   }   else    printk(KERN_ALERT "Kernel receive msg length error! " ); }   static int __init netlink_init( void ) {   netlink_fd = netlink_kernel_create(&init_net, USER_NETLINK_CMD, 0, netlink_recv_packet, NULL, THIS_MODULE);   if (NULL == netlink_fd)   {    printk(KERN_ALERT "Init netlink! " );    return -1;   }   printk(KERN_ALERT "Init netlink success! " );   return 0; }   static void __exit netlink_exit( void ) {   netlink_kernel_release(netlink_fd);   printk(KERN_ALERT "Exit netlink! " ); }   module_init(netlink_init); module_exit(netlink_exit);

4. ./netlink/usrlink.h

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 /*   * usrlink.h   *   * Created on: 2014骞?鏈?7鏃? *  Author: cr   */ #ifndef USRLINK_H_ #define USRLINK_H_   #define USER_NETLINK_CMD 25 #define MAXMSGLEN   1024   typedef enum error_e {   NET_ERROR,   NET_OK,   NET_PARAM,   NET_MEM,   NET_SOCK, } netlink_err;   typedef enum module_e {   HELLO_CMD = 1, } netlink_module;   typedef enum type_e {   HELLO_SET,   HELLO_GET, } netlink_type;   #endif /* USRLINK_H_ */

5. ./netlink/Makefile

?
1 2 3 4 5 6 7 MOD_NAME := netlink   $(MOD_NAME)-objs : netlink.o   -include $(MODULES_ROOT_DIR)/rules.dir   .PHONY:

6. 编译方式

其中Makefile、rulers.dir 在Knetlink/下, netlink.c 、netlink.h 、Makefile在Knetlink/netlink/目录下。 编译时在Knetlink目录下执行Make即可

用户程序

用户程序的Makefile 这里就不放出了。 我是直接在eclipse下建的工程 自动编译的、...

1. netlink.c

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 /*   * usrlink.c   *   * Created on: 2014骞?鏈?7鏃? *  Author: cr   */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "usrlink.h"   int netlink_sock_init(netlink_sock *netlink_s, int module, int protocol) {   netlink_s->sock = socket(PF_NETLINK, SOCK_RAW, protocol);   if (netlink_s->sock < 0)    return NET_SOCK;   memset (&netlink_s->src, 0 , sizeof (netlink_s->src));   netlink_s->src.nl_family = AF_NETLINK;   netlink_s->src.nl_pid = module;   netlink_s->src.nl_groups = 0;     if (bind(netlink_s->sock, ( struct sockaddr *)&netlink_s->src, sizeof (netlink_s->src)) < 0)    return NET_SOCK;     netlink_s->dest.nl_family = AF_NETLINK;   netlink_s->dest.nl_pid = 0;   netlink_s->dest.nl_groups = 0;     return NET_OK; }   int netlink_send(netlink_sock *netlink_s, int type, char *sbuf, int slen, char *rbuf, int *rlen) {   struct msghdr msg;   struct nlmsghdr *nlhdr = NULL;   struct iovec iov;   int ret;     nlhdr = ( struct nlmsghdr *) malloc (NLMSG_SPACE(MAXMSGLEN));   if (NULL == nlhdr)    return NET_MEM;     memcpy (NLMSG_DATA(nlhdr), sbuf, slen);   nlhdr->nlmsg_len = NLMSG_SPACE(slen);   nlhdr->nlmsg_pid = netlink_s->src.nl_pid;   nlhdr->nlmsg_type = type;   nlhdr->nlmsg_flags = 0;     iov.iov_base = ( void *)nlhdr;   iov.iov_len = nlhdr->nlmsg_len;     msg.msg_name = ( void *)&(netlink_s->dest);   msg.msg_namelen = sizeof (netlink_s->dest);   msg.msg_iov = &iov;   msg.msg_iovlen = 1;     ret = sendmsg(netlink_s->sock, &msg, 0);   if (ret < 0)   {    printf ( "Send fail " );    goto error;   }   ret = recvmsg(netlink_s->sock, &msg, 0);   if (ret < 0)   {    printf ( "Read fail " );    goto error;   }   memcpy (rbuf, NLMSG_DATA(nlhdr), nlhdr->nlmsg_len);   *rlen = nlhdr->nlmsg_len;   return NET_OK;   error:   free (nlhdr);   return NET_SOCK; }   int netlink_sock_deinit(netlink_sock *netlink_s) {   close(netlink_s->sock);   memset (netlink_s, 0, sizeof (netlink_sock));   return NET_OK; }

2. netlink.h

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 /*   * usrlink.h   *   * Created on: 2014 *  Author: cr   */ #include <sys/types.h> #include <sys/socket.h> #include <asm/types.h> #include <linux/socket.h> #include <linux/netlink.h> #ifndef USRLINK_H_ #define USRLINK_H_   #define USER_NETLINK_CMD 25 #define MAXMSGLEN   1024   typedef enum error_e {   NET_ERROR,   NET_OK,   NET_PARAM,   NET_MEM,   NET_SOCK, } netlink_err;   typedef enum module_e {   HELLO_CMD = 1, } netlink_module;   typedef enum type_e {   HELLO_SET,   HELLO_GET, } netlink_type;   typedef struct usr_sock_h {   int sock;   struct sockaddr_nl dest;   struct sockaddr_nl src; } netlink_sock;   int netlink_sock_init(netlink_sock *netlink_s, int module, int protocol); int netlink_sock_deinit(netlink_sock *netlink_s); int netlink_send(netlink_sock *netlink_s, int type, char *sbuf, int slen, char *rbuf, int *rlen);   #endif /* USRLINK_H_ */

3. main.c

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 /*   * main.c   *   * Created on: 2014骞?鏈?7鏃? *  Author: cr   */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "usrlink.h"   int parse_ret( int ret) {   switch (ret)   {   case NET_OK:    return ret;   case NET_ERROR:    printf ( "error " );    goto exit_p;   case NET_MEM:    printf ( "Memory error " );    goto exit_p;   case NET_PARAM:    printf ( "Param error " );    goto exit_p;   case NET_SOCK:    printf ( "Socket error " );    goto exit_p;   default : break ;   } exit_p:   return NET_ERROR; }   void usage( void ) {   printf ( "Usage: Netlink -G <param> -S <param> " ); }   int main( int argc, char **argv) {   netlink_sock h_sock;   char rbuf[1024];   char sbuf[1024];   int ret, type, slen = 0, rlen = 0;     ret = netlink_sock_init(&h_sock, HELLO_CMD, USER_NETLINK_CMD);   if (NET_OK != parse_ret(ret))    goto exit_p;     bzero(&rbuf, sizeof (rbuf));   bzero(&sbuf, sizeof (sbuf));   if (argc < 3)   {    usage();    goto exit_p;   }   if (! strncmp ( "-G" , argv[1], 2))    type = HELLO_GET;   else if (! strncmp ( "-S" , argv[1], 2))    type = HELLO_SET;     strcpy (sbuf, argv[2]);   slen = strlen (sbuf);   ret = netlink_send(&h_sock, type, sbuf, slen, rbuf, &rlen);   if (NET_OK != parse_ret(ret))    goto exit_p;     if (rlen > 0)   {    rbuf[rlen] = '' ;    printf ( "K rep [len = %d]:%s " , rlen, rbuf);   }   printf ( "K[len = %d]: %s " , rlen, rbuf);   exit_p:   netlink_sock_deinit(&h_sock);   return 0; }

总结

以上就是本文关于linux下用户程序同内核通信详解(netlink机制)的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

原文链接:http://blog.csdn.net/daydring/article/details/24000081

可以去百度分享获取分享代码输入这里。
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

【腾讯云】云服务器产品特惠热卖中
搜索
标签列表
    关注我们

    了解等多精彩内容