免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1117 | 回复: 0
打印 上一主题 下一主题

udev实现原理 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-09-11 11:04 |只看该作者 |倒序浏览

                                       
                                        udev实现原理
                                       
                                               
[url=javascript:d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(saveit=window.open('http://wz.csdn.net/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'saveit','scrollbars=no,width=590,height=300,left=75,top=20,status=no,resizable=yes'));saveit.focus();]收藏[/url]
                                       
                                       
                               
                               
                                       
                                       
                                        udev实现原理
              
转载时请注明出处和作者联系方式:
http://blog.csdn.net/absurd
作者联系方式:
李先静
更新时间:
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
#include
string
.h>
#include
ctype
.h>
#include
#include
#include
socket
.h>
#include
#include
#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

        
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"
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/90973/showart_2050195.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP