博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Linux中Netlink实现热插拔监控——内核与用户空间通信
阅读量:4165 次
发布时间:2019-05-26

本文共 6056 字,大约阅读时间需要 20 分钟。

1、什么是NetLink?

 它 是一种特殊的 socket,它是 Linux 所特有的,由于传送的消息是暂存在socket接收缓存中,并不被接收者立即处理,所以netlink是一种异步通信机制。 系统调用和 ioctl 则是同步通信机制。Netlink是面向数据包的服务,为内核与用户层搭建了一个高速通道。

用户空间进程可以通过标准socket API来实现消息的发送、接收。进程间通信的方式有:管道(Pipe)及命名管道(Named Pipe),信号(Signal),消息队列(Message queue),共享内存(Shared Memory),信号量(Semaphore),套接字(Socket)。

 为了完成内核空间用户空间通信,Linux提供了基于socket的Netlink通信机制,可以实现内核与用户空间数据的及时交换。

2、在Linux3.0的内核版本中定义了下面的21个用于Netlink通信的宏

在include/linux/netlink.h文件中定义:

#define NETLINK_ROUTE        0    /* Routing/device hook                */#define NETLINK_UNUSED        1    /* Unused number                */#define NETLINK_USERSOCK    2    /* Reserved for user mode socket protocols     */#define NETLINK_FIREWALL    3    /* Firewalling hook                */#define NETLINK_INET_DIAG    4    /* INET socket monitoring            */#define NETLINK_NFLOG        5    /* netfilter/iptables ULOG */#define NETLINK_XFRM        6    /* ipsec */#define NETLINK_SELINUX        7    /* SELinux event notifications */#define NETLINK_ISCSI        8    /* Open-iSCSI */#define NETLINK_AUDIT        9    /* auditing */#define NETLINK_FIB_LOOKUP    10    #define NETLINK_CONNECTOR    11#define NETLINK_NETFILTER    12    /* netfilter subsystem */#define NETLINK_IP6_FW        13#define NETLINK_DNRTMSG        14    /* DECnet routing messages */#define NETLINK_KOBJECT_UEVENT    15    /* Kernel messages to userspace */#define NETLINK_GENERIC        16/* leave room for NETLINK_DM (DM Events) */#define NETLINK_SCSITRANSPORT    18    /* SCSI Transports */#define NETLINK_ECRYPTFS    19#define NETLINK_RDMA        20 #define MAX_LINKS 32

3、建立Netlink会话过程如下:

(1)首先通过netlink_kernel_create()创建套接字,该函数的原型如下:

struct sock *netlink_kernel_create(struct net *net,                              int unit,unsigned int groups,                              void (*input)(struct sk_buff *skb),                              struct mutex *cb_mutex,                              struct module *module);

其中net参数是网络设备命名空间指针,input函数是netlink socket在接受到消息时调用的回调函数指针,module默认为THIS_MODULE.

(2)用户空间进程使用标准Socket API来创建套接字,将进程ID发送至内核空间,用户空间创建使用socket()创建套接字,该函数的原型如下:

int socket(int domain, int type, int protocol);

其中domain值为PF_NETLINK,即Netlink使用协议族。protocol为Netlink提供的协议或者是用户自定义的协议,Netlink提供的协议包括NETLINK_ROUTE, NETLINK_FIREWALL, NETLINK_ARPD, NETLINK_ROUTE6和 NETLINK_IP6_FW。

(3)接着使用bind函数绑定。Netlink的bind()函数把一个本地socket地址(源socket地址)与一个打开的socket进行关联。完成绑定,内核空间接收到用户进程ID之后便可以进行通讯。

(4)用户空间进程发送数据使用标准socket API中sendmsg()函数完成,使用时需添加struct msghdr消息和nlmsghdr消息头。一个netlink消息体由nlmsghdr和消息的payload部分组成,输入消息后,内核会进入nlmsghdr指向的缓冲区。

4、实例:热插拔监听

内核中使用uevent事件通知用户空间,uevent首先在内核中调用netlink_kernel_create()函数创建一个socket套接字,该函数原型在netlink.h有定义,其类型是表示往用户空间发送消息的NETLINK_KOBJECT_UEVENT,groups=1,由于uevent只往用户空间发送消息而不接受,因此其输入回调函数input和cb_mutex都设置为NULL。

当有事件发生的时候,调用 kobject_uevent()函数,实际上最终是调用

 netlink_broadcast_filtered(uevent_sock, skb , 0, 1, GFP_KERNEL , kobj_bcast_filter, kobj);完成广播任务。
  用户空间程序只需要创建一个socket描述符,将描述符绑定到接收地址,就可以实现热拔插事件的监听了

#include 
#include
#include
#include
#include
#include
//该头文件需要放在netlink.h前面防止编译出现__kernel_sa_family未定义 #include
#include
void MonitorNetlinkUevent() { int sockfd; struct sockaddr_nl sa; int len; char buf[4096]; struct iovec iov; struct msghdr msg; int i; memset(&sa,0,sizeof(sa)); sa.nl_family=AF_NETLINK; sa.nl_groups=NETLINK_KOBJECT_UEVENT; sa.nl_pid = 0;//getpid(); both is ok memset(&msg,0,sizeof(msg)); iov.iov_base=(void *)buf; iov.iov_len=sizeof(buf); msg.msg_name=(void *)&sa; msg.msg_namelen=sizeof(sa); msg.msg_iov=&iov; msg.msg_iovlen=1; sockfd=socket(AF_NETLINK,SOCK_RAW,NETLINK_KOBJECT_UEVENT); if(sockfd==-1) printf("socket creating failed:%s\n",strerror(errno)); if(bind(sockfd,(struct sockaddr *)&sa,sizeof(sa))==-1) printf("bind error:%s\n",strerror(errno)); len=recvmsg(sockfd,&msg,0); if(len<0) printf("receive error\n"); else if(len<32||len>sizeof(buf)) printf("invalid message"); for(i=0;i

我们Cmake编译好程序,在设备上执行:开始从设备上拔掉SD卡,之后运行程序,再插入SD卡,如下结果是拔插事件。

创建socket描述符的时候指定协议族为AF_NETLINK或者PF_NETLINK,套接字type选择SOCK_RAW或者SOCK_DGRAM,Netlink协议并不区分这两种类型,第三个参数协议填充NETLINK_KOBJECT_UEVENT表示接收内核uevent信息。

接着就绑定该文件描述符到sockadd_nl,注意该结构体nl_groups是接收掩码,取~0是将接收所有来自内核的消息,我们接收热拔插只需要填NETLINK_KOBJECT_UEVENT即可。接下来调用recvmsg开始接收内核消息,recvmsg函数需要我们填充message报头,包括指定接收缓存等工作。该函数会阻塞直到有热拔插事件产生。因此根据实际的运用来实现自己的代码。

****************************************************************************************************************************************

****************************************************************************************************************************************

Notice:补充内容

当初的demo只是验证这种实现机制是正确的,但是在具体的实际应用中,我们的程序从一开始启动创建一个线程,去接收每一次监控的结果,我发现每一次插拔,会有很多种消息,比如SD卡插入,还有块信息(一次操作一共4个消息),SD卡拔出,同样的情况,;

//日志信息count = 1 received 204 bytesadd@/devices/platform/jzmmc_v1.2.0/mmc_host/mmc0/mmc0:0001ACTION=addDEVPATH=/devices/platform/jzmmc_v1.2.0/mmc_host/mmc0/mmc0:0001SUBSYSTEM=mmc          //SD卡MMC_TYPE=SDMMC_NAME=00000MODALIAS=mmc:blockSEQNUM=521count = 2 received 102 bytesadd@/devices/virtual/bdi/179:0ACTION=addDEVPATH=/devices/virtual/bdi/179:0SUBSYSTEM=bdiSEQNUM=522count = 3 received 244 bytesadd@/devices/platform/jzmmc_v1.2.0/mmc_host/mmc0/mmc0:0001/block/mmcblk0ACTION=addDEVPATH=/devices/platform/jzmmc_v1.2.0/mmc_host/mmc0/mmc0:0001/block/mmcblk0SUBSYSTEM=block                     //块MAJOR=179MINOR=0DEVNAME=mmcblk0DEVTYPE=diskNPARTS=1SEQNUM=523count = 4 received 270 bytesadd@/devices/platform/jzmmc_v1.2.0/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p1ACTION=addDEVPATH=/devices/platform/jzmmc_v1.2.0/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0p1SUBSYSTEM=blockMAJOR=179MINOR=1DEVNAME=mmcblk0p1DEVTYPE=partitionPARTN=1SEQNUM=524

因此我们需要详细解析接收到的信息!!!,recvmsg函数会阻塞,因此代码要注意!!!!

/*线程里处理的事情,创建socket,绑定,销毁socket等都在创建线程、关闭线程时实现,*/	while ((kSocketfd >= 0 )&&(kThreadisRunning >=0))  //	{		MessageLength = recvmsg(kSocketfd, &message, 0);				if (MessageLength > 0 )		{						/*			4 : pesae message			*/			for(int i=0;i
sizeof(MeaasgeBuffer)) { printf("invalid message !"); } } }

 

转载地址:http://lcqxi.baihongyu.com/

你可能感兴趣的文章
kermit的安装和配置
查看>>
vim 配置
查看>>
openocd zylin
查看>>
进程创建时文件系统处理
查看>>
内核线程创建
查看>>
linux中cat命令使用详解
查看>>
java中的异常机制
查看>>
java SE面向对象思维导图
查看>>
三维分析之视频投放
查看>>
SuperMap iDesktop之栅格值怎么查
查看>>
SuperMap iClient3D for WebGL教程-orientation
查看>>
SuperMap iClient3D for WebGL教程-description描述属性
查看>>
SuperMap iClient3D for WebGL教程-CallbackProperty
查看>>
如何修改leaflet聚合图的层级和样式
查看>>
三维分析之开敞度分析
查看>>
BIM+GIS应用的八大挑战
查看>>
.net实现.aspx页面自动加载.cs程序定义的变量并按照格式输出
查看>>
[Leetcode]最后一个单词的长度
查看>>
merges sort use c++
查看>>
插入排序用递归实现
查看>>