linux下设备文件系统有devfs、udev和mdev这三种。
一、devfs
devfs是由Linux 2.4内核引入的,引入时被许多工程师给予了高度评价,它的出现使得设备驱动程序能自主地管理自己的设备文件。
具体来说,devfs具有如下优点:
1、可以通过程序在设备初始化时在/dev目录下创建设备文件,卸载设备时将它删除。
2、设备驱动程序可以指定设备名、所有者和权限位,用户空间程序仍可以修改所有者和权限位。
3、不再需要为设备驱动程序分配主设备号以及处理次设备号。
devfs也存在着一些缺点:
1、不确定的设备映射。
2、没有足够的主/辅设备号。
3、/dev目录下文件太多。
4、命名不够灵活。
5、存在内核空间。
二、udev与devfs的区别
在Linux 2.6内核中,devfs被认为是过时的方法,并最终被抛弃了,udev取代了它。udev取代devfs的几个原因:
1、devfs所做的工作被确信可以在用户态来完成。
2、发现devfs有一些无法修复的bug。
3、devfs的维护者和作者对它感到失望并且已经停止了对代码的维护工作。
4、udev完全在用户态工作,利用设备加入或移除内核所发送的热插拔事件来工作。在热插拔时,设备的详细信息会由内核输入到位于/sys的sysfs文件系统。udev的设备名策略、权限控制和事件处理都是在用户态下完成的,它利用sysfs中的信息来进行创建设备文件节点工作。
5、由于udev根据系统中硬件设备的状态动态更新设备文件、进行设备文件的创建和删除等,因此,在使用udev后,在/dev目录下就只包含系统中真正存在的设备了。
6、devfs与udev的另一个显著区别在于:采用devfs,当一个并不存在的/dev节点被打开的时候,devfs能自动加载对应的驱动,而udev则不能。这是因为udev的设计者认为Linux应该在设备被发现的时候加载驱动模块,而不是当它被访问的时候。udev的设计者认为devfs所提供的打开/dev节点时自动加载驱动的功能对于一个配置正确的计算机是多余的。系统中所有的设备都应该产生热插拔事件并加载恰当的驱动,而udev能注意到这点并且为它创建对应的设备节点。
三、udev
udev是使用uevent机制处理热插拔问题的用户空间程序。udev是基于netlink机制的,它在系统启动时运行了一个deamon程序udevd,通过监听内核发送的uevent来执行相应的热拔插动作,包括创建/删除设备节点,加载/卸载驱动模块等等。udev使用的netlink机制在有大量uevent的场合效率高,适合用在PC机上。
1、udev配置文件
udev的配置文件是/etc/udev/udev.conf。内容如下:
udev_root="/dev/" udev_rules="/etc/udev/rules.d" udev_log="err"
udev_rules表示udev规则存储的目录,这个目录存储的是以.rules结束的文件。这些规则文件的文件名通常是两个数字开头,它表示系统应用该规则的顺序。比如:96-disk_mounts.rules、98-disk_umounts.rules。
2、udev规则
udev规则文件以行为单位,以“#”开头的代表注释行,其余的一行代表一个规则。规则分为匹配和赋值两部分,两部分皆有自己的关键字。
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,则该设备匹配该 匹配键。
在一条规则中,可以设定最多五条 SYSFS 的 匹配键。
ENV{key}: 环境变量。在一条规则中,可以设定最多五条环境变量的 匹配键。
PROGRAM:调用外部命令。
RESULT: 外部命令 PROGRAM 的返回结果。例如:
PROGRAM=="/lib/udev/scsi_id -g -s $devpath", RESULT=="35000c50000a7ef67"
调用外部命令 /lib/udev/scsi_id查询设备的 SCSI ID,如果返回结果为 35000c50000a7ef67,则该设备匹配该 匹配键。
udev的重要赋值键:
NAME:在 /dev下产生的设备文件名。只有第一次对某个设备的 NAME 的赋值行为生效,之后匹配的规则再对该设备的 NAME 赋值行为将被忽略。如果没有任何规则对设备的 NAME 赋值,udev 将使用内核设备名称来产生设备文件。
SYMLINK:为 /dev/下的设备文件产生符号链接。由于 udev 只能为某个设备产生一个设备文件,所以为了不覆盖系统默认的 udev 规则所产生的文件,推荐使用符号链接。
OWNER, GROUP, MODE:为设备设定权限。
ENV{key}:导入一个环境变量。
RUN:执行设备的程序。
udev的值和可调用的替换操作符:
Linux用户可以随意地定制udev规则文件的值。例如:my_root_disk, my_printer。同时也可以引用下面的替换操作符:
$kernel, %k:设备的内核设备名称,例如:sda、cdrom。
$number, %n:设备的内核号码,例如:sda3 的内核号码是 3。
$devpath, %p:设备的 devpath路径。
$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:临时设备名。
%%:符号 % 本身。
$$:符号 $ 本身。
3、udev示例
96-disk_mounts.rules内容如下:
# sdisk case a KERNEL=="sd?[0-9]", SUBSYSTEM=="block", ATTRS{vendor}=="Generic ", ATTRS{scsi_level}=="3" , ACTION=="add" , \ RUN+="/usr/local/bin/sdisk_mounts.sh %k %n -k" KERNEL=="sd?[0-9]", SUBSYSTEM=="block", ATTRS{vendor}=="Generic ", ATTRS{scsi_level}=="0" , ACTION=="add" , \ RUN+="/usr/local/bin/sdisk_mounts.sh %k %n -k" # sdisk case b KERNEL=="sd??[0-9]", SUBSYSTEM=="block", ATTRS{vendor}=="Generic ", ATTRS{scsi_level}=="3" , ACTION=="add" , \ RUN+="/usr/local/bin/sdisk_mounts.sh %k %n -k" KERNEL=="sd??[0-9]", SUBSYSTEM=="block", ATTRS{vendor}=="Generic ", ATTRS{scsi_level}=="0" , ACTION=="add" , \ RUN+="/usr/local/bin/sdisk_mounts.sh %k %n -k" # other disk case a KERNEL=="sd?[0-9]", SUBSYSTEM=="block", ATTRS{vendor}!="Generic ", ACTION=="add" , \ RUN+="/usr/local/bin/confirm_disk_and_mounts.sh %k %n" KERNEL=="sd?[0-9]", SUBSYSTEM=="block", ATTRS{vendor}!="Generic ", ACTION=="add" , \ RUN+="/usr/local/bin/confirm_disk_and_mounts.sh %k %n" # other disk case b KERNEL=="sd??[0-9]", SUBSYSTEM=="block", ATTRS{vendor}!="Generic ", ACTION=="add" , \ RUN+="/usr/local/bin/confirm_disk_and_mounts.sh %k %n" KERNEL=="sd??[0-9]", SUBSYSTEM=="block", ATTRS{vendor}!="Generic ",ACTION=="add" , \ RUN+="/usr/local/bin/confirm_disk_and_mounts.sh %k %n"
四、mdev
mdev是busybox自带的一个简化版的udev。mdev也是使用uevent机制处理热插拔问题的用户空间程序。mdev是基于uevent_helper机制的,它在系统启动时修改了内核中的uevnet_helper变量(通过写/proc/sys/kernel/hotplug),值为“/sbin/mdev”。这样内核产生uevent时会调用uevent_helper所指的用户级程序,也就是mdev,来执行相应的热拔插动作。mdev使用的uevent_helper机制实现简单,适合用在嵌入式系统中。
busybox中的mdev.txt文档详细介绍了mdev的使用。示例:
mount -t tmpfs tmpfs /dev -o size=64k,mode=0755 mkdir /dev/pts /dev/shm chmod 777 /dev/shm mount -t devpts devpts /dev/pts touch /dev/mdev.seq echo "/sbin/mdev" > /proc/sys/kernel/hotplug mdev -s
1、mdev配置文件
mdev的配置文件是/etc/mdev.conf。内容如下:
console 0:0 0600 cpu_dma_latency 0:0 0660 fb0:0 44 0660 full 0:0 0666 initctl 0:0 0600 ircomm[0-9].* 0:20 0660 kmem 0:15 0640 kmsg 0:0 0660 log 0:0 0666 loop[0-9].* 0:6 0640 mem 0:15 0640 network_latency 0:0 0660 network_throughput 0:0 0660 null 0:0 0666 port 0:15 0640 ptmx 0:5 0666 ram[0-9].* 0:6 0640 random 0:0 0666 sd[a-z]* 0:6 0660 sd[a-z]*[0-9]* 0:6 0660 */etc/mdev/automountusbstorage.sh tty 0:5 0666 tty.* 0:0 0620 urandom 0:0 0666 sda* 0:6 0660 sd[a-z][0-9] 0:6 0660 */etc/mdev/automountusb.sh vcs.* 0:5 0660 zero 0:0 0666 hwrng 10:183 0660 =hw_random pcm.* 0:0 0660 =snd/ control.* 0:0 0660 =snd/ timer 0:0 0660 =snd/ event.* 0:0 0660 =input/ @/etc/mdev/find-touchscreen.sh mice 0:0 0660 =input/ mouse.* 0:0 0660 =input/ tun[0-9]* 0:0 0660 =net/ mmcblk[0-9]* 0:6 660 */etc/mdev/autoformat.sh mmcblk[0-9]*p[0-9]* 0:6 660 */etc/mdev/automountsdcard.sh
2、mdev规则
格式如下:
@ 创建节点后执行的
$ 删除节点前执行的
* 创建后和删除前都运行的
3、mdev示例
automountusb.sh内容如下:
#!/bin/sh usb_mount="/media/usb" if [ -d $usb_mount ] then echo "$usb_mount exist!" else mkdir -p $usb_mount fi umount_usb() { grep -qs "^/dev/$1" /proc/mounts [ $? -eq 0 ] && umount $usb_mount } mount_usb() { if [ `fsck.ext4 -a /dev/$1` -a `fsck.fat -a /dev/$1` ] then echo "Error: Unsupported FS!!!" exit 1 fi mount -t auto "/dev/$1" "$usb_mount" [ $? -ne 0 ] && echo "mount /dev/$1 fail!" && exit 1 } case "${ACTION}" in add|"") umount_usb ${MDEV} mount_usb ${MDEV} ;; remove) umount_usb ${MDEV} ;; esac
参考资料:
https://blog.csdn.net/qq_31505483/article/details/52866037
https://www.cnblogs.com/linhaostudy/archive/2018/07/08/9279041.html
https://www.cnblogs.com/fah936861121/p/6496608.html