zoukankan      html  css  js  c++  java
  • linux下udev和mdev的使用

    linux下设备文件系统有devfs、udev和mdev这三种。

    一、devfs

    devfs是由Linux 2.4内核引入的,引入时被许多工程师给予了高度评价,它的出现使得设备驱动程序能自主地管理自己的设备文件。

    具体来说,devfs具有如下优点:

    1、可以通过程序在设备初始化时在/dev目录下创建设备文件,卸载设备时将它删除。

    2、设备驱动程序可以指定设备名、所有者和权限位,用户空间程序仍可以修改所有者和权限位。

    3、不再需要为设备驱动程序分配主设备号以及处理次设备号。

    devfs也存在着一些缺点:

    1、不确定的设备映射。

    2、没有足够的主/辅设备号。

    3、/dev目录下文件太多。

    4、命名不够灵活。

    5、存在内核空间。

    二、udevdevfs的区别

    在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规则

    格式如下:

    <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 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

  • 相关阅读:
    树与树的表示
    队列的顺序/链式存储实现
    堆栈的链式存储实现
    堆栈的顺序存储实现
    线性表的链式存储求表长,查找,插入,删除
    C语言博客作业--函数
    C语言博客作业--嵌套循环
    C语言第三次博客作业---单层循环结构
    C语言第二次博客作业---分支结构
    C语言第一次博客作业——输入输出格式
  • 原文地址:https://www.cnblogs.com/wmate/p/13518444.html
Copyright © 2011-2022 走看看