udev实现原理
来源:互联网 发布:易语言数据采集 编辑:程序博客网 时间:2024/05/20 00:15
udev实现原理
转载时请注明出处和作者联系方式:http://blog.csdn.net/absurd
作者联系方式: 李先静 <xianjimli at hotmail dot com>
更新时间: 2007-4-29
相对于 linux 来说, udev 还是一个新事物。然而,尽管它 03 年才出现,尽管它很低调 ( J ) ,但它无疑已经成为 linux 下不可或缺的组件了。 udev 是什么?它是如何实现的?最近研究 Linux 设备管理时,花了一些时间去研究 udev 的实现。
udev 是什么? u 是指 user space , dev 是指 device , udev 是用户空间的设备驱动程序吗?最初我也这样认为,调试内核空间的程序要比调试用户空间的程序复杂得多,内核空间的程序的 BUG 所引起的后果也严重得多, device driver 是内核空间中所占比较最大的代码,如果把这些 device driver 中硬件无关的代码,从内核空间移动到用户空间,自然是一个不错的想法。
但我的想法并不正确, udev 的文档是这样说的,
1. dynamic replacement for /dev 。作为 devfs 的替代者,传统的 devfs 不能动态分配 major 和 minor 的值,而 major 和 minor 非常有限,很快就会用完了。 udev 能够像 DHCP 动态分配 IP 地址一样去动态分配 major 和 minor 。
2. device naming 。提供设备命名持久化的机制。传统设备命名方式不具直观性,像 /dev/hda1 这样的名字肯定没有 boot_disk 这样的名字直观。 udev 能够像 DNS 解析域名一样去给设备指定一个有意义的名称。
3. API to access info about current system devices 。提供了一组易用的 API 去操作 sysfs ,避免重复实现同样的代码,这没有什么好说的。
我们知道,用户空间的程序与设备通信的方法,主要有以下几种方式,
1. 通过 ioperm 获取操作 IO 端口的权限,然后用 inb/inw/ inl/ outb/outw/outl 等函数,避开设备驱动程序,直接去操作 IO 端口。(没有用过)
2. 用 ioctl 函数去操作 /dev 目录下对应的设备,这是设备驱动程序提供的接口。像键盘、鼠标和触摸屏等输入设备一般都是这样做的。
3. 用 write/read/mmap 去操作 /dev 目录下对应的设备,这也是设备驱动程序提供的接口。像 framebuffer 等都是这样做的。
上面的方法在大多数情况下,都可以正常工作,但是对于热插拨 (hotplug) 的设备,比如像 U 盘,就有点困难了,因为你不知道:什么时候设备插上了,什么时候设备拔掉了。这就是所谓的 hotplug 问题了。
处理 hotplug 传统的方法是,在内核中执行一个称为 hotplug 的程序,相关参数通过环境变量传递过来,再由 hotplug 通知其它关注 hotplug 事件的应用程序。这样做不但效率低下,而且感觉也不那么优雅。新的方法是采用 NETLINK 实现的,这是一种特殊类型的 socket ,专门用于内核空间与用户空间的异步通信。下面的这个简单的例子,可以监听来自内核 hotplug 的事件。
#include < stdio .h>
#include <stdlib.h>
#include < string .h>
#include < ctype .h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/ socket .h>
#include <linux/types.h>
#include <linux/netlink.h>
#include < errno .h>
static int init_hotplug_sock ( void )
{
struct sockaddr_nl snl ;
const int buffersize = 16 * 1024 * 1024;
int retval ;
memset (& snl , 0x00, sizeof ( struct sockaddr_nl));
snl .nl_family = AF_NETLINK;
snl .nl_pid = getpid ();
snl .nl_groups = 1;
int hotplug_sock = socket (PF_NETLINK, SOCK_DGRAM , NETLINK_KOBJECT_UEVENT);
if ( hotplug_sock == -1) {
printf ( "error getting socket: %s" , strerror ( errno ));
return -1;
}
/* set receive buffersize */
setsockopt ( hotplug_sock , SOL_SOCKET , SO_RCVBUFFORCE, & buffersize , sizeof ( buffersize ));
retval = bind ( hotplug_sock , ( struct sockaddr *) & snl , sizeof ( struct sockaddr_nl));
if ( retval < 0) {
printf ( "bind failed: %s" , strerror ( errno ));
close ( hotplug_sock );
hotplug_sock = -1;
return -1;
}
return hotplug_sock ;
}
#define UEVENT_BUFFER_SIZE 2048
int main ( int argc , char * argv [])
{
int hotplug_sock = init_hotplug_sock ();
while (1)
{
char buf [ UEVENT_BUFFER_SIZE *2] = {0};
recv ( hotplug_sock , & buf , sizeof ( buf ), 0);
printf ( "%s/n" , buf );
}
return 0;
}
编译:
gcc -g hotplug.c -o hotplug_monitor
运行后插 / 拔 U 盘,可以看到:
add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1
add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/usbdev2.2_ep00
add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0
add@/class/scsi_host/host2
add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep81
add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep02
add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep83
add@/class/usb_device/usbdev2.2
add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/host2/target2:0:0/2:0:0:0
add@/class/scsi_disk/2:0:0:0
add@/block/sda
add@/block/sda/sda1
add@/class/scsi_device/2:0:0:0
add@/class/scsi_generic/sg0
remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep81
remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep02
remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep83
remove@/class/scsi_generic/sg0
remove@/class/scsi_device/2:0:0:0
remove@/class/scsi_disk/2:0:0:0
remove@/block/sda/sda1
remove@/block/sda
remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/host2/target2:0:0/2:0:0:0
remove@/class/scsi_host/host2
remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0
remove@/class/usb_device/usbdev2.2
remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/usbdev2.2_ep00
remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1
udev 的主体部分在 udevd.c 文件中,它主要监控来自 4 个文件描述符的事件 / 消息,并做出处理:
1. 来自客户端的控制消息。这通常由 udevcontrol 命令通过地址为 /org/kernel/udev/udevd 的本地 socket ,向 udevd 发送的控制消息。其中消息类型有:
l UDEVD_CTRL_STOP_EXEC_QUEUE 停止处理消息队列。
l UDEVD_CTRL_START_EXEC_QUEUE 开始处理消息队列。
l UDEVD_CTRL_SET_LOG_LEVEL 设置 LOG 的级别。
l UDEVD_CTRL_SET_MAX_CHILDS 设置最大子进程数限制。好像没有用。
l UDEVD_CTRL_SET_MAX_CHILDS_RUNNING 设置最大运行子进程数限制 ( 遍历 proc 目录下所有进程,根据 session 的值判断 ) 。
l UDEVD_CTRL_RELOAD_RULES 重新加载配置文件。
2. 来自内核的 hotplug 事件。如果有事件来源于 hotplug ,它读取该事件,创建一个 udevd_uevent_msg 对象,记录当前的消息序列号,设置消息的状态为 EVENT_QUEUED, 然后并放入 running_list 和 exec_list 两个队列中,稍后再进行处理。
3. 来自 signal handler 中的事件。 signal handler 是异步执行的,即使有 signal 产生,主进程的 select 并不会唤醒,为了唤醒主进程的 select ,它建立了一个管道,在 signal handler 中,向该管道写入长度为 1 个子节的数据,这样就可以唤醒主进程的 select 了。
4. 来自配置文件变化的事件。 udev 通过文件系统 inotify 功能,监控其配置文件目录 /etc/udev/rules.d ,一旦该目录中文件有变化,它就重新加载配置文件。
其中最主要的事件,当然是来自内核的 hotplug 事件,如何处理这些事件是 udev 的关键。 udev 本身并不知道如何处理这些事件,也没有必要知道,因为它只实现机制,而不实现策略。事件的处理是由配置文件决定的,这些配置文件即所谓的 rule 。
关于 rule 的编写方法可以参考《 writing_udev_rules 》, udev_rules.c 实现了对规则的解析。
在规则中,可以让外部应用程序处理某个事件,这有两种方式,一种是直接执行命令,通常是让 modprobe 去加载驱动程序,或者让 mount 去加载分区。另外一种是通过本地 socket 发送消息给某个应用程序。
在 udevd.c:udev_event_process 函数中,我们可以看到,如果 RUN 参数以 ”socket:” 开头则认为是发到 socket ,否则认为是执行指定的程序。
下面的规则是执行指定程序:
60-pcmcia.rules: RUN+="/sbin/modprobe pcmcia"
下面的规则是通过 socket 发送消息:
90-hal.rules:RUN+="socket:/org/freedesktop/hal/udev_event"
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/hongjiujing/archive/2009/09/07/4526370.aspx
- udev的实现原理
- udev实现原理
- udev的实现原理
- udev实现原理
- udev的实现原理
- udev实现原理
- udev实现原理
- udev实现原理
- udev的实现原理
- udev实现原理
- udev的实现原理
- udev的实现原理
- udev的实现原理
- udev实现原理
- udev的实现原理
- udev实现原理
- udev实现原理
- udev原理
- CE-tutorial问题整理
- 学点Java正则表达式
- ExtJS fileupload组件上传文件后从服务端解析JSON格式数据
- usb鼠标驱动注解及测试
- CSS选择器
- udev实现原理
- 报异常:android.view.ViewRoot$CalledFromWrongThreadException
- 互斥信号量和二进制信号量的区别
- 在一台机器上同时启动2个tomcat
- 指针加1
- Linux设备驱动之I2C架构分析
- 使用线程加载UIImagePickerController,解决卡屏问题
- htaccess详解
- Linux-USB Gadget : Part 4: 最简单的 gadget驱动:g_zero