华域联盟 Linux linux下用户程序同内核通信详解(netlink机制)

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

目录

    简介

    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

    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

    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

    /* 
     * 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!\n"); 
      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\n"); 
    } 
     
    static int process_hello_get(int dest, void *buf, int len) 
    { 
     printk(KERN_ALERT "In process_hello get!\n"); 
     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\n", (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!\n"); 
    } 
     
    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!\n"); 
      return -1; 
     } 
     printk(KERN_ALERT "Init netlink success!\n"); 
     return 0; 
    } 
     
    static void __exit netlink_exit(void) 
    { 
     netlink_kernel_release(netlink_fd); 
     printk(KERN_ALERT "Exit netlink!\n"); 
    } 
     
    module_init(netlink_init); 
    module_exit(netlink_exit); 

    4. ./netlink/usrlink.h

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

    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

    /* 
     * 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\n"); 
      goto error; 
     } 
     ret = recvmsg(netlink_s->sock, &msg, 0); 
     if(ret < 0) 
     { 
      printf("Read fail\n"); 
      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

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

    /* 
     * 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\n"); 
      goto exit_p; 
     case NET_MEM: 
      printf("Memory error\n"); 
      goto exit_p; 
     case NET_PARAM: 
      printf("Param error\n"); 
      goto exit_p; 
     case NET_SOCK: 
      printf("Socket error\n"); 
      goto exit_p; 
     default:break; 
     } 
    exit_p: 
     return NET_ERROR; 
    } 
     
    void usage(void) 
    { 
     printf("Usage: Netlink -G <param>\n\t-S <param>\n"); 
    } 
     
    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] = '
    /* 
    * 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\n"); 
    goto exit_p; 
    case NET_MEM: 
    printf("Memory error\n"); 
    goto exit_p; 
    case NET_PARAM: 
    printf("Param error\n"); 
    goto exit_p; 
    case NET_SOCK: 
    printf("Socket error\n"); 
    goto exit_p; 
    default:break; 
    } 
    exit_p: 
    return NET_ERROR; 
    } 
    void usage(void) 
    { 
    printf("Usage: Netlink -G <param>\n\t-S <param>\n"); 
    } 
    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] = '\0'; 
    printf("K rep [len = %d]:%s\n", rlen, rbuf); 
    } 
    printf("K[len = %d]: %s\n", rlen, rbuf); 
    exit_p: 
    netlink_sock_deinit(&h_sock); 
    return 0; 
    } 
    '; printf("K rep [len = %d]:%s\n", rlen, rbuf); } printf("K[len = %d]: %s\n", rlen, rbuf); exit_p: netlink_sock_deinit(&h_sock); return 0; }

    总结

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

    您可能感兴趣的文章:

    本文由 华域联盟 原创撰写:华域联盟 » linux下用户程序同内核通信详解(netlink机制)

    转载请保留出处和原文链接:https://www.cnhackhy.com/69185.htm

    本文来自网络,不代表华域联盟立场,转载请注明出处。

    作者: sterben

    发表回复

    联系我们

    联系我们

    2551209778

    在线咨询: QQ交谈

    邮箱: [email protected]

    工作时间:周一至周五,9:00-17:30,节假日休息

    关注微信
    微信扫一扫关注我们

    微信扫一扫关注我们

    关注微博
    返回顶部