udev introduction

1.udev简介
        udev 的u是指 user space,也就是用户空间设备管理。它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等。设备文件通常放在/dev目录下。使用udev后,在/dev目录下就只包含系统中真正存在
        的设备。它只支持linux-2.6内核,严重依赖于sysfs文件系统提供的信息。它替代了原来的devfs,那为什么要替换devfs呢?原因如下:
       (1)不确定的设备映射。特别是那些动态设备,比如USB设备,设备文件到实际设备的映射并不可靠和确定。举一个例子:如果你有两个USB打印机。一个可能称为/dev/usb/lp0,另外一个便是/dev/usb/lp1。
           但是到底哪个是哪个并不清楚,lp0,lp1和实际的设备没有一一对应的关系,因为他可能因为发现设备的顺序,打印机本身关闭等原因而导致这种映射并不确定。理想的方式应该是:两个打印机应该采用基于他们的
           序列号或者其他标识信息来对设备文件进行映射。但是静态文件和devfs都无法做到这点。

       (2)没有足够的主/辅设备号。我们知道,每一个设备文件是有两个8位的数字:一个是主设备号 ,另外一个是辅设备号来分配的。这两个8位的数字加上设备类型(块设备或者字符设备)来唯一标识一个设备。
            而这些主次设备号非常有限,很快就会分配完。

        (3)/dev目录下文件太多。一个系统采用静态设备文件关联的方式,那么这个目录下必然有足够多的文件。与此同时,你又不知道在你的系统上有哪些设备文件是激活的。

       (4)命名不够灵活。尽管devfs解决了以前的一些问题,但是它自身又带来了一些问题。其中一个就是命名不够灵活;不能够非常简单容易的修改设备文件的名字。
       (5)内核内存使用,devfs存在的另外一个问题是,作为内核驱动模块,devfs需要消耗大量的内存,特别是当系统上有大量的设备时。

           而udev的出现彻底解决了上面提到的这些缺陷,成为当前Linux默认的设备管理工具。它不仅能够实现所有devfs实现的功能,而且具有很多devfs所无法比拟的优越性。

       (1)动态管理设备:udev 以守护进程的形式运行,通过侦听内核发出来的 uevent 来管理 /dev目录下的设备文件。不像之前的设备管理工具,udev 在用户空间 (user space) 运行,而不在内核空间 (kernel space) 运行。
           当设备添加/删除时,udev 的守护进程侦听来自内核的 uevent,以此添加或者删除/dev下的设备文件,所以udev只为已经连接的设备产生设备文件,这样在/dev目录下,只包含系统中真正存在的设备,而不会在/dev下产生大量虚无的设备文件。此处所讲的设备文件是泛指在 /dev/下,可被应用程序用来和设备驱动交互的文件。而不会特别地区分设备文件、设备节点或者设备特殊文件。

       (2)设备命名持久化机制:通过 Linux 默认的规则文件,udev 在 /dev/ 里为所有的设备定义了内核设备名称,比如 /dev/sda、/dev/hda、/dev/fd等等。由于 udev 是在用户空间 (user space) 运行,Linux 用户可以通过
           自定义的规则文件,灵活地产生标识性强的设备文件名,比如 /dev/boot_disk、/dev/root_disk、/dev/color_printer等等。
       (3)设定设备的权限和所有者或组:udev 可以按一定的条件来设置设备文件的权限和设备文件所有者或组。在不同的udev版本中,实现的方法不同。

 2.udev的工作过程

   1. 当内核检测到在系统中出现了新设备后,内核会在sysfs文件系统中为该新设备生成一项新的记录,一般sysfs文件系统会被mount 到 /sys目录中。新记录是以一个或多个文件或目录的方式来表示。每个文件都包含有特定的信息。
   2. udev在系统中是以守护进程的方式udevd在运行,它通过uevent检测到新设备的出现,通过查找设备对应的sysfs中的记录得到设备的一些信息。
   3. udev会根据/etc/udev/udev.conf文件中的udev_rules指定的目录,逐个检查该目录下的文件,这个目录下的文件都是针对某类或某个设备应该施行什么措施的规则文件。udev读取文件是按照文件名的ASCII字母顺序来读取的,
      如果udev一旦找到了与新加入的设备匹配的规则,udev就会根据规则定义的措施对新设备进行配置。同时不再读后续的规则文件。
      下面的流程图显示udev添加/删除设备文件的过程:
       [[Image(udev1.jpg)]]
3.udev的配置文件存放位置
    udev是一个用户模式程序。它的配置文件是/etc/udev/udev.conf。这个文件一般缺省有这样几项:
    udev_root="/dev" ; udev产生的设备文件的根目录是/dev
    udev_db="/dev/.udevdb" ; 通过udev产生的设备文件形成的数据库
    udev_rules="/etc/udev/rules.d" ;用于指导udev工作的规则所在目录。
    udev_log="err" ;当出现错误时,用syslog记录错误信息。

4.udev的规则文件
    udev 能通过定义一个 udev 规则 (rule) 来产生匹配设备属性的设备文件,这些设备属性可以是内核设备名称、总线路径、厂商名称、型号、序列号或者磁盘大小等等。udev按照规则文件名的字母顺序来查询全部规则文件,然后为匹配规则的设备管理其设备文件或者文件链接。规则文件以.rules作为结尾。
    udev的规则文件以行为单位,以"#"开头的行代表注释行。其余的每一行代表一个规则。每个规则分成一个或多个“匹配”和“赋值”部分。“匹配”部分用“匹配“专用的键来表示,相应的“赋值”部分用“赋值”专用的键来表示。特定的操作符决定了是匹配还是赋值。

(1)udev规则的所有操作符具体如下:
“ ==”:比较键、值,若等于,则该条件满足;
“!=”: 比较键、值,若不等于,则该条件满足;
'''“ ='''”: 对一个键赋值;
“'''+='''”:为一个表示多个条目的键赋值。
“''':='''”:对一个键赋值,并拒绝之后所有对该键的改动。目的是防止后面的规则文件对该键赋值。
(2)udev规则的匹配键
  
'''ACTION''': 事件 (uevent) 的行为,例如:add( 添加设备 )、remove( 删除设备 )。
'''KERNEL''': 内核设备名称,例如:sda, cdrom。
'''DEVPATH''':设备的 devpath 路径。
'''SUBSYSTEM''': 设备的子系统名称,例如:sda 的子系统为 block。
'''BUS''': 设备在 devpath 里的总线名称,例如:usb。
'''DRIVER''': 设备在 devpath 里的设备驱动名称,例如:ide-cdrom。
'''ID''': 设备在 devpath 里的识别号。
'''SYSFS{filename}''': 设备的 devpath 路径下,设备的属性文件“filename”里的内容。
例如:SYSFS{model}==“ST936701SS”表示:如果设备的型号为 ST936701SS,则该设备匹配该'''匹配键'''。
在一条规则中,可以设定最多五条 SYSFS 的 '''匹配键'''。
'''ENV{key}''': 环境变量。在一条规则中,可以设定最多五条环境变量的 '''匹配键'''。
'''PROGRAM''':调用外部命令。
'''RESULT''': 外部命令 PROGRAM 的返回结果。例如: PROGRAM=="/lib/udev/scsi_id -g -s $devpath", RESULT=="35000c50000a7ef67"
调用外部命令 /lib/udev/scsi_id查询设备的 SCSI ID,如果返回结果为 35000c50000a7ef67,则该设备匹配该 '''匹配键'''。
(3)udev规则的赋值键
  '''NAME:'''在 /dev下产生的设备文件名。只有第一次对某个设备的 NAME 的赋值行为生效,之后匹配的规则再对该设备的 NAME 赋值行为将被忽略。如果没有任何规则对设备的 NAME 赋值,udev 将使用内核设备名称来产生设备文件。也是设备在 sysfs里的名称,是 udev 默认使用的设备文件名。
'''SYMLINK''':为 /dev/下的设备文件产生符号链接。由于 udev 只能为某个设备产生一个设备文件,所以为了不覆盖系统默认的 udev 规则所产生的文件,推荐使用符号链接。
'''OWNER, GROUP, MODE:'''为设备设定权限。
'''ENV{key}''':导入一个环境变量。

(4) udev的值和可调用的替换操作符
      
      在键值对中除了键和操作符之外,就是值 (value)。Linux 用户可以随意地定制 udev 规则文件的值。例如:my_root_disk, my_printer。同时也可以引用下面的替换操作符:
'''$kernel, %k''':设备的内核设备名称,例如:sda、cdrom。
'''$number, %n''':设备的内核号码,例如:sda3 的内核号码是 3。
'''$devpath, %p:'''设备的 '''devpath'''路径。一般是指一个设备在 sysfs文件系统 (/sys)下的相对路径,该路径包含了该设备的属性文件。udev 里的多数命令都是针对 devpath操作的。例如:sda的 devpath是 /block/sda,sda2 的 devpath是/block/sda/sda2。
'''$id, %b:'''设备在 '''devpath'''里的 ID 号。
'''$sysfs{file}, %s{file}:'''设备的 '''sysfs'''里 file 的内容。其实就是设备的属性值。
例如:$sysfs{size} 表示该设备 ( 磁盘 ) 的大小。
'''$env{key}, %E{key}:'''一个环境变量的值。
'''$major, %M:'''设备的 major 号。
'''$minor %m:'''设备的 minor 号。
'''$result, %c:'''PROGRAM 返回的结果。
'''$parent, %P:'''父设备的设备文件名。
'''$root, %r:udev_root'''的值,默认是 /dev/。
'''$tempnode, %N:'''临时设备名。
'''%%:'''符号 % 本身。
'''$$:符号 $ 本身。'''
'''For example:'''

'''KERNEL=="sd*", PROGRAM="/lib/udev/scsi_id -g -s %p", \'''
'''RESULT=="35000c50000a7ef67", SYMLINK="%k_%c"'''

'''该规则的执行:如果有一个内核设备名称以 sd 开头,且 SCSI ID 为 35000c50000a7ef67,则为设备文件产生一个符号链接“sda_35000c50000a7ef67”.'''

 SUBSYSTEM=="net", ACTION=="add", SYSFS{address}=="00:0d:87:f6:59:f3", IMPORT="/sbin/rename_netiface %k eth0"
    这个规则中的“匹配”部分有三项,分别是SUBSYSTEM,ACTION和SYSFS。而"赋值"部分有一项,是IMPORT。这个规则就是说,当系统中出现的新硬件属于net子系统范畴,系统对该硬件采取的动作是加入这个硬件,且这个硬件在SYSFS文件系统中的“address”信息等于“00: 0d..."时,对这个硬件在udev层次施行的动作是调用外部程序/sbin/rename_netiface,传递的参数有两个,一个是“%k”,代表内核对该新设备定义的名称。另一个是”eth0“。从上面这个例子中可以看出,udev的规则的写法比较灵活的,尤其在“匹配”部分中,可以通过诸如”*“, ”?“,[a-c],[1-9]等shell通配符来灵活匹配多个匹配项.

5.制定udev的规则时,如何查找设备的属性信息
  (1)查询sysfs文件系统 :sysfs里包含了很多设备和驱动的信息
          设备sda的SYSFS{size},可以通过cat /sys/block/sda/size得到;
                           SYSFS{model}信息可以通过cat /sys/block/sda/device/model得到
   (2)udevinfo命令
           udevinfo可以查询udev数据库里的设备信息。
           例如: udevinfo -a -p  /block/sda |egrep “model | size”     
                    SYSFS{size}==”71096640”
                    SYSFS{model}==”ST936701SS”
 6.常用的udev命令
     (1)udevtest  会针对一个设备,在不需要uevent触发的情况下模拟一次udev的运行,并输出查询
                              规则文件的过程、所执行的行为、规则文件的执结果。通常使用udevtest来调试规则
                               文件。 # udevtest /block/sda 
     (2)start_udev 命令重启udev守护进程,并对所有的设备重新查询规则目录下所有的规则文件,然后
                                执行所匹配的规则里的行为。通常使用该命令让新的规则文件立即生效。
                                直接执行start_udev,它一般没有标准输出,所有的udev相关信息都按照文件
                                (udev.conf)的参数设置,由syslog记录。
7.Linux向udev发出设备事件的方法
       关于udev的一些关键机制需要我们弄清楚,方便更深入的了解udev的工作流程。内核通过何种措施向驻留在用户态的udev进程发送事件,通知udev进程去删除/dev下的设备文件。
    基本过程如下:当用户层发送删除一个块设备时,通常通过ioctl将删除的消息传递给内核驱动,驱动程序以mmc为例,会调用它的remove函数,接下来会调用del_gendisk()函数删除设备。在del_gendisk函数中会执行释放资源,并且调用sysfs_remove_link 和 blk_unregister_queue ()函数,blk_unregister_queue ()函数会通过kobject_uevent发送REMOVE事件。REMOVE事件通过netlink接口向驻留在用户层的udevd进程传递过去,就这样内核的事件通过netlink机制就传输给了用户进程。udev用户进程接收到事件之后,会根据udev的规则文件调用具体的执行脚本,执行脚本完成该事件的具体方法。这一过程可以通过下图进行描述:
[[Image(udev2.jpg)]]
   理清上述思路之后,就可以很容易的从内核向udev发送事件,从而让udev完成内核所要求的任务。实现此操作需要如下步骤:
1、 修改udev的规则文件,规则文件位于/etc/udev/rules.d目录。
2、 实现执行脚本,即事件所对应的执行脚本。
3、在内核通过kobject_uevent向udevd发送事件,事件类型可以为:ADD、REMOVE、UPDATE、OFFLINE、ONLINE以及UMOUNT。
8.udev的主体部分介绍
     udev的主体部分在/tools/udev/udev/udevd.c,它主要monitor来自4个文件描述符的事件或者消息,并作出相应的处理:
    (1)来自客户端的控制消息。这个通常是通过udevadm control socket 向udevd发送的控制信息。其中消息类型为:
      enum udev_ctrl_msg_type {
	UDEV_CTRL_UNKNOWN,
	UDEV_CTRL_SET_LOG_LEVEL,    设置log的级别
	UDEV_CTRL_STOP_EXEC_QUEUE,   停止处理消息队列
	UDEV_CTRL_START_EXEC_QUEUE,  开始处理消息队列
	UDEV_CTRL_RELOAD_RULES,              重新加载配置文件
	UDEV_CTRL_SET_ENV,                             设置环境变量
	UDEV_CTRL_SET_CHILDREN_MAX,       设置最大子进程数限制
	UDEV_CTRL_SETTLE,
      };
     (2)来自内核的uevent事件,如有事件来源于udev monitor socket,通过udev_monitor_receive_device函数读取socket信息后 分配一个新的udev设备, 将接受到的数据信息为udev设备进行填充后,返回此设备结构体dev,并通过函数event_queue_insert(dev)将此事件设备加入队列。并设置事件状态为EVENT_QUEUED, event->state = EVENT_QUEUED;之后会执行event_run函数,其中会对device进行具体的处理。
     (3)  来自signal handler中的事件,signal handler 是异步执行的,即使有signal产生,主进程的poll并不会唤醒,为了唤醒主进程的poll,它建立了一个管道,在signal handler中,向该管道写入长度为1个字节的数据,这样就可以唤醒主进程的poll了。

     (4) 来自配置文件变化的事件,udev通过文件系统的inotify机制,监控其配置文件目录/etc/udev/rules.d,一旦该目录中文件有所变化,它就重新加载配置文件。
        其中最主要的事件,当然来自内核的uevent事件,如何处理这些事件是udev的关键。udev本身并不知道如何处理这些事件,也没有必要知道,因为它只是实现机制,而不是实现策略。事件的处理由配置文件决定的。这些配置文件即所谓的rule。
在规则中,可以让外部应用程序处理某个事件,有两种方式,一种是直接执行命令,通常是让modprobe去加载驱动程序,或者让mount去加载分区。另外一种是通过本地socket发送消息给某个应用程序。
9.涉及到的主目录有以下:
  主要的文件在以下路径:
   /tools/udev/udev/udevd.c
   /tools/udev/udev/udev-event.c
  /tools/udev/udev/udev-rules.c
   /tools/udev/udev/udev-node.c  等等。

你可能感兴趣的:(kernel,内核,文件系统,udev)