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}=="" , ACTION=="add" , \
RUN+="/usr/local/bin/sdisk_mounts.sh %k %n -k"
KERNEL=="sd?[0-9]", SUBSYSTEM=="block", ATTRS{vendor}=="Generic ", ATTRS{scsi_level}=="" , 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}=="" , ACTION=="add" , \
RUN+="/usr/local/bin/sdisk_mounts.sh %k %n -k"
KERNEL=="sd??[0-9]", SUBSYSTEM=="block", ATTRS{vendor}=="Generic ", ATTRS{scsi_level}=="" , 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=
mkdir /dev/pts /dev/shm
chmod /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 :
cpu_dma_latency :
fb0:
full :
initctl :
ircomm[-].* :
kmem :
kmsg :
log :
loop[-].* :
mem :
network_latency :
network_throughput :
null :
port :
ptmx :
ram[-].* :
random :
sd[a-z]* :
sd[a-z]*[-]* : */etc/mdev/automountusbstorage.sh
tty :
tty.* :
urandom :
sda* :
sd[a-z][-] : */etc/mdev/automountusb.sh
vcs.* :
zero : hwrng : =hw_random
pcm.* : =snd/
control.* : =snd/
timer : =snd/ event.* : =input/ @/etc/mdev/find-touchscreen.sh
mice : =input/
mouse.* : =input/ tun[-]* : =net/ mmcblk[-]* : */etc/mdev/autoformat.sh
mmcblk[-]*p[-]* : */etc/mdev/automountsdcard.sh

2、mdev规则

格式如下:

<device regex> <uid>:<gid> <octal permissions> [<@$*><cmd>]
    @ 创建节点后执行的
    $ 删除节点前执行的
    * 创建后和删除前都运行的

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 ] && umount $usb_mount
} mount_usb()
{
if [ `fsck.ext4 -a /dev/$` -a `fsck.fat -a /dev/$` ]
then
echo "Error: Unsupported FS!!!"
exit
fi
mount -t auto "/dev/$1" "$usb_mount"
[ $? -ne ] && echo "mount /dev/$1 fail!" && exit
} 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

05-08 15:01