一、UDEV是什么?

Udev是一个针对Linux内核2.6的可提供自动创建的设备节点和命名的解决方法的一个文件系统;其实与/etc/目录下的fstab文件类似

二、Udev如何获取内核这些模块的变化信息?

参考博客:http://blog.chinaunix.net/uid-24943863-id-3223000.html

设备节点的创建,是通过sysfs接口分析dev文件取得设备节点号,这个很显而易见。那么udevd是通过什么机制来得知内核里模块的变化情况,如何得知设备的插入移除情况呢?当然是通过hotplug机制了,那hotplug又是怎么实现的?或者说内核是如何通知用户空间一个事件的发生的呢?
 
答案是通过netlink socket通讯,在内核和用户空间之间传递信息。
 

新的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。

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描述符,将描述符绑定到接收地址,就可以实现热拔插事件的监听了。
 
 #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <asm/types.h>
//该头文件需要放在netlink.h前面防止编译出现__kernel_sa_family未定义
#include <sys/socket.h>
#include <linux/netlink.h> void MonitorNetlinkUevent()
{
int sockfd;
struct sockaddr_nl sa;
int len;
char buf[];
struct iovec iov;
struct msghdr msg;
int i; memset(&sa,,sizeof(sa));
sa.nl_family=AF_NETLINK;
sa.nl_groups=NETLINK_KOBJECT_UEVENT;
sa.nl_pid = ;//getpid(); both is ok
memset(&msg,,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=; sockfd=socket(AF_NETLINK,SOCK_RAW,NETLINK_KOBJECT_UEVENT);
if(sockfd==-)
printf("socket creating failed:%s\n",strerror(errno));
if(bind(sockfd,(struct sockaddr *)&sa,sizeof(sa))==-)
printf("bind error:%s\n",strerror(errno)); len=recvmsg(sockfd,&msg,);
if(len<)
printf("receive error\n");
else if(len<||len>sizeof(buf))
printf("invalid message");
for(i=;i<len;i++)
if(*(buf+i)=='\0')
buf[i]='\n';
printf("received %d bytes\n%s\n",len,buf);
} int main(int argc,char **argv)
{
MonitorNetlinkUevent();
return ;
}

创建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的时候用一个循环接收更多的消息。
 

udev实现热插拔的更多相关文章

  1. I.MX6 Linux udev porting

    /*********************************************************************** * I.MX6 Linux udev porting ...

  2. Android手机一键Root原理分析

    图/文 非虫 一直以来,刷机与Root是Android手机爱好者最热衷的事情.即使国行手机的用户也不惜冒着失去保修的风险对Root手机乐此不疲.就在前天晚上,一年一度的Google I/O大会拉开了帷 ...

  3. 使用udev实现显示器的热插拔和usb的自动挂载

    udev:用来监听硬件设备是否发生改变,并可以给硬件设备命名 ,也可以在硬件发生改变之后执行脚本 使用udev检测显示器是否发生变化,然后执行脚本,解决linux显示器热插拔问题 先补充一点: [ro ...

  4. 关于热插拔usb hotplug /proc/sys/kernel mdev udev b...

    转:http://www.360doc.com/content/10/0527/18/9922_29835045.shtml 这篇文章说的很好http://blog.chinaunix.net/u1/ ...

  5. (转载)linux中设备文件配置程序udev详解

    如果你使用Linux比较长时间了,那你就知道,在对待设备文件这块,Linux改变了几次策略.在Linux早期,设备文件仅仅是是一些带有适当的属性集的普通文件,它由mknod命令创建,文件存放在/dev ...

  6. 嵌入式 详解udev

    如果你使用Linux比较长时间了,那你就知道,在对待设备文件这块,Linux改变了几次策略.在Linux早期,设备文件仅仅是是一些带有适当的属性集的普通文件,它由mknod命令创建,文件存放在/dev ...

  7. Linux设备管理之权限倾斜——mem、proc、devfs、sysfs、udev(下)

    linux发展第一阶段 01devfs(linux2.6之前) 02udev(用户空间) 03sysfs(linux2.6之后,描述设备属性) linux发展第二阶段 01sysfs+udev(ude ...

  8. linux设备驱动----利用mdev(udev)自动创建设备文件节点

    1.mdev的使用方法和原理: mdev是busybox 自带的一个简化版的udev,适合于嵌入式的应用埸合.其具有使用简单的特点.它的作用,就是在系统启动和热插拔或动态加载驱动程序时,自动产生驱动程 ...

  9. 详解udev

    如果你使用Linux比较长时间了,那你就知道,在对待设备文件这块,Linux改变了几次策略.在Linux早期,设备文件仅仅是是一些带有适当的属性集的普通文件,它由mknod命令创建,文件存放在/dev ...

随机推荐

  1. VirtualBox 安装 Ubuntu 14.04 无法调节分辨率问题

    基础环境 宿主系统:Windows 10 虚拟机系统:Ubuntu 14.04-32bit.Ubuntu 14.04-64bit VirtualBox:5.2.0 r118431 (Qt5.6.2) ...

  2. SSH之免密登陆

    又来了,上头让小轩我在服务器中写一个Shell脚本,主要用来在机器B中定时备份机器A中的一些文件.那么,小轩是怎么想的呢? 在小轩的知识库里,现在有scp和ssh两个玩具.别的还真没有其他什么东西了. ...

  3. MVC+EF 入门教程(一)

    一.前言 本人小白,写这篇博客是为了记录今天一天所学的知识,可能表达不是那么的准确和清楚,欢迎在留言区写下您的建议,欢迎纠错.希望这篇文章对你有所帮助学习 .Net MVC有所帮助.废话不多说了,我们 ...

  4. 修改Intellij IDEA中工程对应的Java SDK、Scala SDK

    如果编译Scala工程时,遇到如下异常: can't expand macros compiled by previous versions of Scala 很可能是工程的scala版本,和依赖的包 ...

  5. 【HTML5】增强的表单

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  6. 【ANT】创建删除目录,复制移动重命名文件

    一.创建目录: <?xml version="1.0"?> <project default="test_mkdir"> <tar ...

  7. 垃圾回收机制GC知识再总结兼谈如何用好GC(转)

    作者:Jeff Wong 出处:http://jeffwongishandsome.cnblogs.com/ 本文版权归作者和博客园共有,欢迎围观转载.转载时请您务必在文章明显位置给出原文链接,谢谢您 ...

  8. LAMP第三部分php,mysql配置

    php配置 1. 配置disable_functiondisable_functions = eval,assert,popen,passthru,escapeshellarg,escapeshell ...

  9. linux防火墙之 ufw

    Usage: ufw COMMAND Commands: enable enables the firewall 开启ufw防火墙 disable disables the firewall 禁用防火 ...

  10. Linux命令每日一个

    2014-3-31 1:39 (1)tree linux以树状的结构显示当前目录及其包含的子目录下的文件 #apt-get install tree #tree   //在当前目录下直接使用该命令即可 ...