Netlink实现热拔插监控

新的Linux内核使用udev代替了hotplug作为热拔插管理, 虽然有udevd管理热拔插,但有时候我们还是需要在应用程序中检测热拔插事件以便快速地处理,比如在读写SD卡的时候拔下SD卡,那么需要立即检测出该情况,然后结束读写线程,防止VFS崩溃。Netlink是面向数据包的服务,为内核与用户层搭建了一个高速通道,是udev实现的基础。该工作方式是异步的,用户空间程序不必使用轮询等技术来检测热拔插事件。
    内核中使用uevent事件通知用户空间,uevent首先在内核中调用netlink_kernel_create()函数创建一个socket套接字,该函数原型在netlink.h有定义,其类型是表示往用户空间发送消息的NETLINK_KOBJECT_UEVENT,groups=1,由于uevent只往用户空间发送消息而不接受,因此其输入回调函数input和cb_mutex都设置为NULL。
#include
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);
 
ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT,
                                                        1, NULL, NULL, THIS_MODULE);
当有事件发生的时候,调用 kobject_uevent()函数,实际上最终是调用
 netlink_broadcast_filtered(uevent_sock, skb , 0, 1, GFP_KERNEL , 
                                        kobj_bcast_filter, kobj);
完成广播任务。
    用户空间程序只需要创建一个socket描述符,将描述符绑定到接收地址,就可以实现热拔插事件的监听了。

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <errno.h>
  5. #include <sys/types.h>
  6. #include <asm/types.h>
  7. //该头文件需要放在netlink.h前面防止编译出现__kernel_sa_family未定义
  8. #include <sys/socket.h>  
  9. #include <linux/netlink.h>

  10. void MonitorNetlinkUevent()
  11. {
  12.     int sockfd;
  13.     struct sockaddr_nl sa;
  14.     int len;
  15.     char buf[4096];
  16.     struct iovec iov;
  17.     struct msghdr msg;
  18.     int i;

  19.     memset(&sa,0,sizeof(sa));
  20.     sa.nl_family=AF_NETLINK;
  21.     sa.nl_groups=NETLINK_KOBJECT_UEVENT;
  22.     sa.nl_pid = 0;//getpid(); both is ok
  23.     memset(&msg,0,sizeof(msg));
  24.     iov.iov_base=(void *)buf;
  25.     iov.iov_len=sizeof(buf);
  26.     msg.msg_name=(void *)&sa;
  27.     msg.msg_namelen=sizeof(sa);
  28.     msg.msg_iov=&iov;
  29.     msg.msg_iovlen=1;

  30.     sockfd=socket(AF_NETLINK,SOCK_RAW,NETLINK_KOBJECT_UEVENT);
  31.     if(sockfd==-1)
  32.         printf("socket creating failed:%s\n",strerror(errno));
  33.     if(bind(sockfd,(struct sockaddr *)&sa,sizeof(sa))==-1)
  34.         printf("bind error:%s\n",strerror(errno));

  35.     len=recvmsg(sockfd,&msg,0);
  36.     if(len<0)
  37.         printf("receive error\n");
  38.     else if(len<32||len>sizeof(buf))
  39.         printf("invalid message");
  40.     for(i=0;i<len;i++)
  41.         if(*(buf+i)=='\0')
  42.             buf[i]='\n';
  43.     printf("received %d bytes\n%s\n",len,buf);
  44. }

  45. int main(int argc,char **argv)
  46. {
  47.     MonitorNetlinkUevent();
  48.     return 0;
  49. }

创建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报头,包括指定接收缓存等工作。该函数会阻塞直到有热拔插事件产生。

运行程序,然后我插入一个U盘,得到下面的结果:
$ ./netlink 
received 289 bytes
add@/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.1
ACTION=add
DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.1
SUBSYSTEM=usb
MAJOR=189
MINOR=8
DEVNAME=bus/usb/001/009
DEVTYPE=usb_device
DEVICE=/proc/bus/usb/001/009
PRODUCT=781/5530/100
TYPE=0/0/0
BUSNUM=001
DEVNUM=009
SEQNUM=2306

运行程序,拔掉U盘
$ ./netlink 
received 294 bytes
remove@/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.1/1-1.1:1.0/host10/target10:0:0/10:0:0:0/bsg/10:0:0:0
ACTION=remove
DEVPATH=/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.1/1-1.1:1.0/host10/target10:0:0/10:0:0:0/bsg/10:0:0:0
SUBSYSTEM=bsg
MAJOR=253
MINOR=2
DEVNAME=bsg/10:0:0:0
SEQNUM=2345

程序正确地接收到了U盘热拔插事件,通过该信息用户程序可以在第一时间得到事件通知。事实上热拔插的时候产生的消息可不止一条呢,可以在revmsg的时候用一个循环接收更多的消息。


转自: http://blog.chinaunix.net/uid-24943863-id-3223000.html

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
A: 在 Linux 中,可以使用 UDev(Linux Kernel 中的一个守护程序)监听 USB 设备的入和拔出事件。通过 UDev ,我们可以使用 Qt 的 QProcess 来与 UDev 通信,并监测 U 盘的热拔事件。以下是使用 UDev 和 Qt 进行 U 盘热拔检测的实现步骤: 1. 引入 Qt 的 QProcess 类和 UDev 头文件: ``` c++ #include <QProcess> #include <libudev.h> ``` 2. 定义 UDev 上下文和监听器: ``` c++ struct udev *udev; struct udev_monitor *mon; ``` 3. 初始化 UDev 上下文和监听器: ``` c++ udev = udev_new(); mon = udev_monitor_new_from_netlink(udev, "udev"); udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", "usb_device"); udev_monitor_enable_receiving(mon); ``` 4. 初始化 Qt 的 QProcess 实例用于运行监测程序: ``` c++ QProcess *process = new QProcess(this); ``` 5. 启动监测程序并循环监听 UDev 中的事件: ``` c++ process->start("udevadm monitor --udev -s usb"); while (/*!done*/ true) { fd_set fds; FD_ZERO(&fds); FD_SET(udev_monitor_get_fd(mon), &fds); if (select(udev_monitor_get_fd(mon) + 1, &fds, nullptr, nullptr, nullptr) > 0) { if (FD_ISSET(udev_monitor_get_fd(mon), &fds)) { struct udev_device *dev = udev_monitor_receive_device(mon); // 对设备的属性和信息进行分析 udev_device_unref(dev); } } } ``` 通过以上实现,我们可以在 Linux 中使用 Qt 监测 U 盘的热拔事件。需要注意的是,在实现中需要对 UDev 中的设备信息进行分析,并且需要在程序结束时释放 UDev 相关的数据结构。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值