加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 创业 > C语言 > 正文

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

发布时间:2020-12-15 01:02:50 所属栏目:C语言 来源:网络整理
导读:简介 linux下用户程序同内核通信的方式一般有ioctl, proc文件系统,剩下一个就是Netlink套接字了。 这里先介绍下netlink。 Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的 socket API 就可以使用 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 %,$(KERNEL_MODULES)): 
 @echo 
 @echo Building $(patsubst _dir_%,%,$@) 
 $(MAKE) -C $(patsubst _dir_%,$@) all 
 
 
romfs: $(patsubst %,_romfs_%,$(KERNEL_MODULES)) 
$(patsubst %,$(KERNEL_MODULES)): 
 $(MAKE) -C $(patsubst _romfs_%,$@) romfs 
 
 
clean: $(patsubst %,_clean_%,$(KERNEL_MODULES)) 
 $(RM) $(BUILD_DIR) 
$(patsubst %,$(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,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 successn"); 
} 
 
static int process_hello_get(int dest,int len) 
{ 
 printk(KERN_ALERT "In process_hello get!n"); 
 memcpy(buf,"I known you !",13); 
 netlink_to_user(dest,13); 
 return NET_OK; 
} 
 
static int process_hello_set(int dest,int len) 
{ 
 printk(KERN_ALERT "In process_hello set! %sn",(char *)buf); 
 memcpy(buf,"S known you !",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,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,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,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 failn"); 
  goto error; 
 } 
 ret = recvmsg(netlink_s->sock,0); 
 if(ret < 0) 
 { 
  printf("Read failn"); 
  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,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,} 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 protocol); 
int netlink_sock_deinit(netlink_sock *netlink_s); 
int netlink_send(netlink_sock *netlink_s,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("errorn"); 
  goto exit_p; 
 case NET_MEM: 
  printf("Memory errorn"); 
  goto exit_p; 
 case NET_PARAM: 
  printf("Param errorn"); 
  goto exit_p; 
 case NET_SOCK: 
  printf("Socket errorn"); 
  goto exit_p; 
 default:break; 
 } 
exit_p: 
 return NET_ERROR; 
} 
 
void usage(void) 
{ 
 printf("Usage: Netlink -G <param>nt-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",2)) 
  type = HELLO_SET; 
 
 strcpy(sbuf,argv[2]); 
 slen = strlen(sbuf); 
 ret = netlink_send(&h_sock,slen,rbuf,&rlen); 
 if(NET_OK != parse_ret(ret)) 
  goto exit_p; 
 
 if(rlen > 0) 
 { 
  rbuf[rlen] = ''; 
  printf("K rep [len = %d]:%sn",rlen,rbuf); 
 } 
 printf("K[len = %d]: %sn",rbuf); 
 
exit_p: 
 netlink_sock_deinit(&h_sock); 
 return 0; 
} 

总结

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

您可能感兴趣的文章:

  • Linux利用Sysctl命令调整内核参数
  • Linux内核参数调整方法
  • Linux 内核通用链表学习小结
  • Linux内核启动参数详解
  • 简单谈谈Linux内核定时器
  • Linux中的内核链表实例详解
  • 浅谈安装ORACLE时在Linux上设置内核参数的含义
  • Centos下编译Linux内核的具体实现方法

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读