Linux系统中udev详解

本文是参考网上很多资料整合在一起的,这里只是为了方便学习和记忆,没有别的目的。如有侵犯到版权,请及时联系。

udev介绍

udev是在Linux kernel2.6系列上新添加的设备文件管理模块。其功能主要为动态管理设备文件,如增加、删除等操作。当新插入的设备被驱动检测到后会在sysfs上注册新的数据(与此设备相关的一些信息文件)。同时内核会发送设备的uevent给udev,udev收到后便会访问配置规则。然后按照配置规则来执行相应的操作,比如修改设备文件的属组、组、权限、创建链接文件以及挂载等等。同样在设备拔出后,udev也会收到内核发送的uevent事件,udev会根据uevnet的规则来执行如删除设备文件、去挂载、删除链接文件等等操作。这样就能很好的动态地管理设备文件,可以支持设备的热插拔。而无需预先在/dev目录下创建一大堆的静态设备文件
除了上述的动态管理设备文件外,udev的另外一个亮点是其在用户空间运行,可以根据客户自定义的规则来执行相应的操作。比如设备上有多个USB接口,其中有两台USB接口打印机,假如同时接在设备上,那么在设备上/dev/目录下会生成两个设备文件,如/dev/sda和/dev/sdb。那么他们的对应关系是怎么样的呢?sda对应的是打印机1还是打印机2这和客户的操作相关,如插入的先后顺序,以及中途设备有关机的情况。这在以前的静态设备文件管理和devfs上无法处理的。咱们只需要简单地修改udev的规则便可以实现一一对应的映射。比如根据设备的序列号、vendorID等之类的来创建设备文件链接printer1、printer2。只要插入打印机1便会生成一个链接文件printer1,咱们再也不需要定时设备插入的先后顺序等之类的约束了。由于udev是运行于用户空间的模块,所以内核依旧会生成sda、sdab等设备文件
udev的工作原理:在Linux2.6版本中引入了sysfs文件系统。sysfs把连接在系统上的设备和总线组织成一个分级的文件,并提供给用户空间存取使用。udev运行在用户模式,而非内核中。udev的初始化脚本在系统启动时创建设备节点,并且在新的设备被检测到后,会在sysfs上生成一系列的文件,udev会创建新的设备节点。udev必须需要内核中的sysfs和tmpfs支持,sysfs为udev提供设备入口和uevent通道,tmpfs为udev设备文件提供存放空间。下图为udev的工作流程图,配置文件存放在/etc/udev/udev.conf文件中。
在这里插入图片描述
为什么使用udev,因为之前使用的设备文件管理方法(静态文件和devfs)存在一些缺陷:

  • 不确定的设备映射。特别是那些动态设备,比如USB设备,设备文件到实际设备的映射并不可靠和确定。举一个例子:如果你有两个USB打印机。一个可能称为 /dev/usb/lp0,另外一个便是/dev/usb/lp1。但是到底哪个是哪个并不清楚,lp0,lp1和实际的设备没有一一对应的关系,因为他可能因为发现设备的顺序,打印机本身关闭等原因而导致这种映射并不确定。理想的方式应该是:两个打印机应该采用基于他们的序列号或者其他标识信息的唯一设备文件来映射。但是静态文件和devfs都无法做到这点。
  • 没有足够的主/辅设备号。我们知道,每一个设备文件是有两个8位的数字:一个是主设备号 ,另外一个是辅设备号来分配的。这两个8位的数字加上设备类型(块设备或者字符设备)来唯一标识一个设备。不幸的是,关联这些身边的的数字并不足够。
  • /dev目录下文件太多。一个系统采用静态设备文件关联的方式,那么这个目录下的文件必然是足够多。而同时你又不知道在你的系统上到底有那些设备文件是激活的。
  • 命名不够灵活。尽管devfs解决了以前的一些问题,但是它自身又带来了一些问题。其中一个就是命名不够灵活;你别想非常简单的就能修改设备文件的名字。缺省的devfs命令机制本身也很奇怪,他需要修改大量的配置文件和程序。;
  • 内核内存使用,devfs特有的另外一个问题是,作为内核驱动模块,devfs需要消耗大量的内存,特别当系统上有大量的设备时(比如上面我们提到的系统一个上有好几千磁盘时)

udev的目标是想解决上面提到的这些问题,他通采用用户空间(user-space)工具来管理/dev/目录树,他和文件系统分开。知道如何改变缺省配置能让你知道如何定制自己的系统,比如创建设备字符连接,改变设备文件属组,权限等。

udev规则

上述中说了,udev会根据用户规则来执行相关的操作。那么这些规则文件放在哪里以及有哪些规则?其中关于规则部分,本文只讲解一些通用的,详细的请查看man udev文档。
主要的udev配置文件是**/etc/udev/udev.conf**。这个文件通常很短,常见如下图。其中udev_root指定了设备文件存放的根目录,udev_rules则指定了规则的存放路径,udev_log则指定记录日志的等级。还有其他的一些配置这里就暂不讲解,如日志存放的数据库。

udev_root="/dev/"

udev_rules="/etc/udev/rules.d/"

udev_log="err"

规则文件存放在udev_rules指定的目录下,并且文件名的后缀为.lures。规则文件可能有多个,匹配的先后顺序是安装ASCII码来进行的。如找到了比配的规则,则暂停匹配,不在去匹配后续的规则文件。所以自定义的规则文件基本上都是以数字开头,这样提高了规则文件的优先级。下图是简单的规则文件规则文件以行为单位,一行就是一条规则

KERNEL=="*", OWNER="root" GROUP="root", MODE="0600"
KERNEL=="tty", NAME="%k", GROUP="tty", MODE="0666", OPTIONS="last_rule"
KERNEL=="scd[0-9]*", SYMLINK+="cdrom cdrom-%k"
KERNEL=="hd[a-z]", BUS=="ide", SYSFS{removable}=="1", SYSFS{device/media}=="cdrom", SYMLINK+="cdrom cdrom-%k"
ACTION=="add", SUBSYSTEM=="scsi_device", RUN+="/sbin/modprobe sg"

在规则文件中,规则是由一系列的键值对组成,键值对以逗号分隔。键值对分为匹配键和赋值键。匹配键用来指定匹配规则,而赋值键则用来进行赋值的,赋值键可以处理一个多值列表。

udev规则所有操作符

  • “==”:  比较键、值,若等于,则该条件满足;
  • “!=”:   比较键、值,若不等于,则该条件满足;
  • “=”:    对一个键赋值;
  • “+=”:  为一个表示多个条目的键赋值。
  • “:=”:  对一个键赋值,并拒绝之后所有对该键的改动。目的是防止后面的规则文件对该键赋值。

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,则该设备匹配该匹配键。
  • ENV{key}:          环境变量。在一条规则中,可以设定最多五条环境变量的 匹配键。
  • PROGRAM:        调用外部命令。
  • RESULT:          外部命令 PROGRAM 的返回结果。

udev的重要赋值键

  • NAME:  在 /dev下产生的设备文件名。只有第一次对某个设备的 NAME 的赋值行为生效,之后匹配的规则再对该设备的 NAME 赋值行为将被忽略。如果没有任何规则对设备的 NAME 赋值,udev 将使用内核设备名称来产生设备文件。
  • SYMLINK:  为 /dev/下的设备文件产生符号链接。由于 udev 只能为某个设备产生一个设备文件,所以为了不覆盖系统默认的 udev 规则所产生的文件,推荐使用符号链接。
  • OWNER, GROUP, MODE:  为设备设定权限。
  • ENV{key}:         导入一个环境变量。

udev的值和可调用的替换操作符

  • $kernel, %k:        设备的内核设备名称,例如:sda、cdrom。
  • $number, %n:        设备的内核号码,例如:sda3 的内核号码是 3。
  • $devpath, %p:       设备的 devpath路径。
  • $id, %b:          设备在 devpath里的 ID 号。
  • $sysfs{file}, %s{file}:   设备的 sysfs里 file 的内容。其实就是设备的属性值。
  • $env{key}, %E{key}:    一个环境变量的值。
  • $major, %M:        设备的 major 号。
  • $minor %m:        设备的 minor 号。
  • $result, %c:        PROGRAM 返回的结果。
  • $parent, %P:       父设备的设备文件名。
  • $root, %r:         udev_root的值,默认是 /dev/。
  • $tempnode, %N:      临时设备名。
  • %%:            符号 % 本身。
  • $$:             符号 $ 本身。

下图的规则是根据扫描仪的IDvendor 和IDproduct来生产指定的链接名,每次打开扫描仪后其链接都是固定的,所以能很好的确定映射关系。

SYSFS{idVendor}=="0686",SYSFS{idProduct}=="400e", SYMLINK+="scanner", MODE="0664", group="scanner"

在修改规则后要是规则生效,我们可以插拔我们的设备产生一个事件或在设备中的event文件增加信息以达到发送事件的目的来更新我们的udev规则。更加方便的方法是运行下列命令

udevadm test /sys/class/block/sdc4
udevinfo -q path -n /dev/sda 这个命令会产生一个该设备名对应的在sysfs下的路径
udevinfo -a -p /sys/block/sda 这个命令会显示一堆信息,这些信息实际来自于操作系统维护的sysfs链表。

udev移植

udev下载网址可以下载到udev的源码。下面为移植步骤。

  • 下载udev源码包,并解压
  • 修改Makefile中的CROSS交叉编译工具为自己开发板的编译器
  • 执行make进行编译。
  • 然后执行strip udev uded udevstart udevinfo udevtest。并将这些文件拷贝到rootfs/bin目录下。
  • 添加对udev的支持,修改/etc/init.d/rcS脚本,添加如下命令:
/bin/mount -t sysfs sysfs /sys
/bin/mount -t tmpfs tmpfs /dev
/bin/udevd --deamon
/bin/udevstart 
  • 在/etc下创建目录udev
  • 在/etc/udev下创建rules.d和文件udev.conf,udev.conf内容如下:
    # udev.conf
    # The initial syslog(3) priority: 'err', 'info', 'debug' or its
    # numerical equivalent. For runtime debugging, the daemons internal
    # state can be changed with: 'udevcontrol log_priority='.
    udev_root="/dev/"
    udev_rules="/etc/udev/rules.d"
    udev_log="err"
  • 在rules.d目录中创建自己的规则文件。文件必须使用.rules后缀。
    由于udev需要sysfs的支持以及tmpfs的支持,所以在启动udev前需要先挂载这两个目录。
发布了35 篇原创文章 · 获赞 1 · 访问量 1870

猜你喜欢

转载自blog.csdn.net/lzj_linux188/article/details/105094697