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! ");

  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

?

/*

 * 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 ");

  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

?

/*

 * 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 ");

  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

原创文章,作者:XUEDD,如若转载,请注明出处:http://www.wangzhanshi.com/n/6470.html

(0)
XUEDD的头像XUEDD
上一篇 2025年1月1日 16:22:11
下一篇 2025年1月1日 16:22:13

相关推荐

发表回复

登录后才能评论