Linux之设备管理器及热插拔处理

前言

Linux内核设备管理器主要来管理/dev 中的设备节点,并在添加或删除设备时处理所有用户空间操作,比如U盘SD卡等热插拔的处理–自动挂载与卸载。

Linux内核设备管理器主要有mdevudeveudev等,今天就简单来看看它们。

  • mdev是udev的简化版本,是busybox的一部分,最适合用在简单的嵌入式系统,使用 busybox 的地方。
  • udev是systemd的设备管理器,相对mdev来说要复杂些,一般用在PC上的linux或比较高端点的嵌入式系统,使用 systemd 的地方。
  • eudev是 udev 的fork版本,所以很多规则这些都是和udev类似,主要是为了独立于systemd等initx系统和Linux发行版。

udevmdev 是两个使用 uevent 机制处理热插拔问题的用户空间程序,两者的实现机理不同。

  • udev 基于 netlink 机制
  • mdev 基于 uevent_helper 机制
    每当设备状态发生变化时,内核都会发出一个 uevent 事件,设备管理器会接收到该事件,根据 uevent 中包含的信息,会去匹配触发制定的规则并执行所需的操作,包括创建或删除设备文件,触发将特定固件文件加载到内核内存中,执行相应的脚本等等。
    具体的实现原理就深入展开了,这里只介绍些简单的概念,感兴趣的同学可以去研究下源码。

mdev

mdev源码:https://github.com/mirror/busybox/blob/master/util-linux/mdev.c
busybox官网:http://www.busybox.net/source.html
更多详情可查看上面的源码。

"mdev -s" 会扫描 /sys/class/xxx,查找包含dev文件的目录,然后mdev 会创建 /dev/device_name 节点。
当设备状态发生变化时,比如热插拔,mdev 会去匹配设定的相关规则,去做相应的处理,比如创建节点时,修改节点权限,执行相应命令等。
下面来看看 mdev 的有关规则

mdev规则

格式如下:

<device regex> <uid>:<gid> <octal permissions> [<@$*><cmd>]

后面操作说明:

  • =xxx/: 移动到/dev/xxx
  • >xxx/yyy%1: 移动到 /dev/xxx/yyyN,并创建符号链接到 /dev/yyyN
  • @: 创建节点后执行后面的
  • $: 删除节点前执行后面的
  • *: 创建后和删除前都运行后面的

默认规则文件为:/etc/mdev.conf

简单示例

规则例子:

mice 0:0 0660 =input/
mouse.* 0:0 0660 =input/
event.* 0:0 0660 =input/
pcm.* 0:0 0660 =snd/
control.* 0:0 0660 =snd/
timer 0:0 0660 =snd/

$DEVNAME=bus/usb/([0-9]+)/([0-9]+) 0:0 0660 =bus/usb/%1/%2
sd[a-z][0-9]+   0:0 666 * /etc/usb/usb_hotplug.sh
sd[a-z]   0:0 666 * /etc/usb/usb_hotplug.sh

mmcblk[0-9]p[0-9] 0:0 660 @/etc/hotplug/sdcard_insert
mmcblk[0-9] 0:0 660 $/etc/hotplug/sdcard_remove

U盘热插拔一个简单脚本/etc/usb/usb_hotplug.sh

#!/bin/sh
if [ $ACTION = "add" ]; then
    if [ ${#DEVNAME} -eq 3 ];then
        sleep 1
    fi
        mkdir -p /vendor/udisk_$DEVNAME
        mount -o iocharset=utf8 /dev/$DEVNAME /vendor/udisk_$DEVNAME
        if [ $? -ne 0 ];then
            rm -rf /vendor/udisk_$DEVNAME
        fi
else
        umount /vendor/udisk_$DEVNAME
        if [ $? -eq 0 ];then
            rm -rf /vendor/udisk_$DEVNAME
        fi
fi

udev与eudev

udev与eudev使用上比较类似,这里就放在一起了。

udev源码: https://cgit.freedesktop.org/systemd/systemd/tree/src/udev

eudev源码: https://github.com/eudev-project/eudev

eudev is a standalone dynamic and persistent device naming support (aka userspace devfs) daemon that runs independently from the init system. eudev strives to remain init system and linux distribution neutral. It is currently used as the devfs manager for more than a dozen different linux distributions.

This git repo is a fork of git://anongit.freedesktop.org/systemd/systemd with the aim of isolating udev from any particular flavor of system initialization. In this case, the isolation is from systemd.

This is a project started by Gentoo developers and testing was initially being done mostly on OpenRC. We welcome contribution from others using a variety of system initializations to ensure eudev remains system initialization and distribution neutral. On 2021-08-20 Gentoo decided to abandon eudev and a new project was established on 2021-09-14 by Alpine, Devuan and Gentoo contributors (alphabetical order).

udev规则

默认规则文件:

  • /etc/udev/rules.d/
  • /lib/udev/rules.d
    上面的目录中带有很多设备默认的配置规则,大家可以根据自己的需求去研究和修改。

简单示例

一个SD卡热插拔的规则配置例子:
/lib/udev/rules.d/61-sd-cards-auto-mount.rules

KERNEL!="mmcblk*[0-9]", GOTO="sd_cards_auto_mount_end"
SUBSYSTEM!="block", GOTO="sd_cards_auto_mount_end"
ACTION=="add", PROGRAM!="/sbin/blkid %N", GOTO="sd_cards_auto_mount_end"
ATTRS{type}!="SD", GOTO="sd_cards_auto_mount_end"

IMPORT{program}="/sbin/blkid -o udev -p %N"

ACTION=="add", ENV{mount_options_vfat}="rw,uid=1000,gid=1000,dmask=022,fmask=133,noatime"

ACTION=="add", ENV{ID_FS_TYPE}=="vfat", RUN+="/bin/mount -t vfat -o %E{mount_options_vfat} /dev/%k '/mnt/sdcard'"

ACTION=="add", ENV{mount_options_ntfs}="rw,uid=1000,gid=1000,dmask=022,fmask=133,noatime"

ACTION=="add", ENV{ID_FS_TYPE}=="ntfs", RUN+="/bin/mount -t ntfs-3g -o %E{mount_options_ntfs} /dev/%k '/mnt/sdcard'"

ACTION=="add", ENV{mount_options_exfat}="rw,uid=1000,gid=1000,dmask=022,fmask=133,noatime"

ACTION=="add", ENV{ID_FS_TYPE}=="exfat", RUN+="/bin/mount -t exfat-fuse -o %E{mount_options_exfat} /dev/%k '/mnt/sdcard'"

ACTION=="add", ENV{mount_options_ext2}="users,exec,noatime"

ACTION=="add", ENV{ID_FS_TYPE}=="ext2", RUN+="/bin/mount -t ext2 -o %E{mount_options_ext2} /dev/%k '/mnt/sdcard'"

ACTION=="add", ENV{mount_options_ext3}="users,exec,noatime"

ACTION=="add", ENV{ID_FS_TYPE}=="ext3", RUN+="/bin/mount -t ext3 -o %E{mount_options_ext3} /dev/%k '/mnt/sdcard'"

ACTION=="add", ENV{mount_options_ext4}="users,exec,noatime"

ACTION=="add", ENV{ID_FS_TYPE}=="ext4", RUN+="/bin/mount -t ext4 -o %E{mount_options_ext4} /dev/%k '/mnt/sdcard'"

ACTION=="remove", RUN+="/bin/umount '/mnt/sdcard'"

LABEL="sd_cards_auto_mount_end"

参考