一、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描述符,将描述符绑定到接收地址,就可以实现热拔插事件的监听了。
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 11 void MonitorNetlinkUevent() 12 { 13 int sockfd; 14 struct sockaddr_nl sa; 15 int len; 16 char buf[4096]; 17 struct iovec iov; 18 struct msghdr msg; 19 int i; 20 21 memset(&sa,0,sizeof(sa)); 22 sa.nl_family=AF_NETLINK; 23 sa.nl_groups=NETLINK_KOBJECT_UEVENT; 24 sa.nl_pid = 0;//getpid(); both is ok 25 memset(&msg,0,sizeof(msg)); 26 iov.iov_base=(void *)buf; 27 iov.iov_len=sizeof(buf); 28 msg.msg_name=(void *)&sa; 29 msg.msg_namelen=sizeof(sa); 30 msg.msg_iov=&iov; 31 msg.msg_iovlen=1; 32 33 sockfd=socket(AF_NETLINK,SOCK_RAW,NETLINK_KOBJECT_UEVENT); 34 if(sockfd==-1) 35 printf("socket creating failed:%s ",strerror(errno)); 36 if(bind(sockfd,(struct sockaddr *)&sa,sizeof(sa))==-1) 37 printf("bind error:%s ",strerror(errno)); 38 39 len=recvmsg(sockfd,&msg,0); 40 if(len<0) 41 printf("receive error "); 42 else if(len<32||len>sizeof(buf)) 43 printf("invalid message"); 44 for(i=0;i<len;i++) 45 if(*(buf+i)=='