我们在开发嵌入式产品时,会需要U盘读写功能,但是Linux系统不像Windows系统那样能默认配置U盘自动挂载,所以我们需要在Linux系统中手动添加rule规则,实现U盘自动挂载功能。
Udev规则
是什么?
udev 是Linux 系统的一个设备管理器,它主要的功能是管理/dev目录底下的设备节点。
特色功能是对设备有极端需求的站点(比如需要控制上千个硬盘)和热插拔设备(比如USB摄像头和MP3播放器)有着较完善的处理能力。
比如在处理热插拔设备时,udev在用户空间,根据用户设置的规则,在监测到设备被插入后,在/dev/下自动创建并命名设备文件节点 或者 自动设置设备属性。
溯源历史
早期:通过mknod静态添加
中期:devfs基于内核的动态设备文件系统
现在:udev 一个用户空间程序
Udev和Devfs的区别
1.devfs运行在内核空间(使用devfs_register等API在内核空间注册设备节点),而udev运行在用户空间。
2.当用户访问/dev下的某个节点时,devfs会自动加载对应的驱动;而udev则并不负责自动加载驱动的工作。
Udev的热插拔工作机制
Udev工作在用户空间,当一个设备被插入或者移除时,内核会通过netlink套接字发送设备详细信息到用户空间,udev获取到设备信息,根据信息内容在/dev下创建并命名设备节点 或者 配置属性。
Udev的配置规则
1.配置文件
主配置文件: /etc/udev/udev.conf
udev_root="/dev/" 设备目录,默认是/dev
udev_rules="/etc/udev/rules.d/" udev规则存储的目录
udev_log="err" 日志等级(表示严重程度),跟 syslog 一致,例如: err, info, debug
2.规则文件
udev的规则文件一般位于 /lib/udev/rules.d/,有的位于 /etc/udev/rules.d/下。
规则文件是按照字母顺序处理的,不管放在哪个目录。对于相同名字的规则文件, /etc/udev/rules.d 比 /lib/udev/rules.d 优先。
规则文件必须以 .rules 作为扩展名,否则不被当作规则文件。
规则文件的每一行都是 key=value 格式。 key 有两个类型:1)匹配型 key 2)赋值型 key
当所有匹配型 key 都匹配时,该规则即被采用,赋值型 key就会获得相应的值。
一条规则由多个key=value 组成,以英文逗号隔开。 每个key 有一个操作,取决于操作符,有效的操作符如下:
== 比较是否相等
!= 比较是否不相等
= 给一个key 赋值。表示一个列表的key会被重置,并且把这个唯一的值传给它
+= 将一个值增加到key中
:= 将一个值传给一个key,并且不允许再修改这个key。
3.匹配型Key
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
的返回结果。
通配符 * 代表 [ 0 个到无穷多个 ] 任意字符
通配符 ? 代表 [一定有一个] 任意字符
通配符 [ ] 代表 [一定有一个在括号内] 的字符(非任意字符)
4.赋值型Key
NAME: 在 /dev下产生的设备文件名。只有第一次对某个设备的 NAME 的赋值行为生效,之后匹配的规则再对该设备的 NAME 赋值行为将被忽略。如果没有任何规则对设备的 NAME 赋值,udev 将使用内核设备名称来产生设备文件。
SYMLINK: 为 /dev/下的设备文件产生符号链接。由于 udev 只能为某个设备产生一个设备文件,所以为了不覆盖系统默认的 udev 规则所产生的文件,推荐使用符号链接。
LABEL GOTO 可以跳到的地方
GOTO 跳到下一个带有匹配名字的 LABEL 处。
ENV{key}: 导入一个环境变量。
OWNER, GROUP, MODE: 为设备设定权限。
$kernel, %k : 该设备的内核名字(%k 替换$kernel)
$number, %n:该设备的内核号码。例如 sda3 的内核号码是 3。
$devpath, %p: 该设备的 devpath
$id, %b:当向上搜索devpath,寻找 SUBSYSTEMS, KERNELS, DRIVERS 和 ATTRS 时,被匹配的设备名字
$driver: 当向上搜索devpath,寻找 SUBSYSTEMS, KERNELS, DRIVERS 和 ATTRS 时,被匹配的驱动名字
$attr { file }, %s { file }: 一个被发现的设备的sysfs 属性的值。如果该设备没有该属性,且前面的 KERNELS, SUBSYSTEMS, DRIVERS或 ATTRS 测试选择的是一个父设备,那么就用父设备的属性。如果属性是一个符号链,符号链的最后一个元素作为返回值。
$env { key }, %E { key }: 一个设备属性值
$major, %M: 该设备的内核主号码
$minor, %m: 该设备的内核次号码
$result, %c: 由 PROGRAM 调用的外部程序返回的字符串。如果这个字符串包含空格,可以用 %c{N} 选中第N个字段。如果这个数字N,后面有一个 + 字符, 则表示选中从这个字段开始的所有后面的字符串 %c { N + }
$parent, %p:父设备的节点名字
$name:设备节点的名字,用一个空格作为分隔符。该值只有在前面的规则赋值之后才存在,或者是remove事件。
$links:当前符号链的列表,用空格隔开。该值只有在前面的规则赋值之后才存在,或者是remove事件。
$root, %r:udev_root 的值
$sys, %S:sysfs 挂载点
$tempnode, %N:在真正的设备节点创建之前,创建的一个临时的设备节点的名字,这个临时设备节点供外部程序使用。
$$: ‘$‘字符自己
%%: ‘%‘ 字符自己
Udev的应用举例
前提是系统里存在udev工具
在/etc/udev/rules.d/下添加usb-mount.rules规则,参考如下:
KERNEL!=
"sd[a-z][0-9]"
,GOTO=
"automount_exit"
ACTION==
"add"
, SUBSYSTEMS==
"usb"
, SUBSYSTEM==
"block"
,RUN+=
"/bin/mkdir /mnt/usb"
,RUN+=
"/bin/mount --no-block --automount=yes $devnode /mnt/usb"
ACTION==
"remove"
, RUN+=
"/bin/umount /mnt/usb"
,RUN+=
"/bin/rmdir /mnt/usb"
LABEL=
"automount_exit"
以上例子中的udev规则中匹配sd[a-z][0-9]的设备, 当系统中检查usb设备插入时, 就会执行add中的操作, 当检查到usb设备拔出后, 就会执行remove中的操作。
规则中的remove操作是在拔出U盘后执行的, 为了保证写入U盘的数据不丢失, 在拔出U盘之前需要执行sync命令把数据写入U盘中。
在有的Linux里,存在systemd服务的话,尽量还是用systemd的mount和umount为好。
欢迎转载,欢迎指正,更欢迎点赞。转载请附原文链接。