udev規則以及編寫

主要內容:node

  • udev簡介
  • 如何配置和使用udev
  • 如何編寫udev規則
  • 字符串替換和匹配
  • udev主要做用
  • 編寫udev規則實例
  • 難點解析

1. udev簡介linux

1.1 什麼是udev?redis

udev是Linux(linux2.6內核以後)默認的設備管理工具。udev 以守護進程的形式運行,經過偵聽內核發出來的 uevent 來管理 /dev目錄下的設備文件。shell

如何理解udev是守護進程呢?即系統內核啓動後init進程(好比busybox的init程序、sysinit、Upstart或systemd)根據runlevel運行等級進入某種模式,而後解析開啓哪些服務進程。其中udev就是哪些服務進程中的一個,服務進程是在後臺運行的。能夠經過命令ps -aux來獲取,好比在ubuntu終端中ps -aux | grep udev數據庫

root       328  0.0  0.0  52220   852 ?        Ss    2月23   0:00 /lib/systemd/systemd-udevd --daemonubuntu

因此只要有設備插入或刪除,守護進程udev就會管理它。網絡

也就是說使用了udev,全部的設備都能在/dev/目錄下找到對應的設備文件。async

1.2 使用udev的好處ide

動態管理:當設備添加 / 刪除時,udev 的守護進程偵聽來自內核的 uevent,以此添加或者刪除 /dev下的設備文件,因此 udev 只爲已經鏈接的設備產生設備文件,而不會在 /dev下產生大量虛無的設備文件。工具

自定義命名規則:經過 Linux 默認的規則文件,udev 在 /dev/ 裏爲全部的設備定義了內核設備名稱,好比 /dev/sda、/dev/hda、/dev/fd等等。因爲 udev 是在用戶空間 (user space) 運行,Linux 用戶能夠經過自定義的規則文件,靈活地產生標識性強的設備文件名,好比 /dev/boot_disk、/dev/root_disk、/dev/color_printer等等。

設定設備的權限和全部者 / 組:udev 能夠按必定的條件來設置設備文件的權限和設備文件全部者 / 組

1.3 udev工做流程

2. 如何配置和使用udev

2.1 udev的配置文件(/etc/udev/udev.conf)

[root@HOST_RHEL4 dev]# cat /etc/udev/udev.conf 
 # udev.conf 
 # The main config file for udev 
 # 
 # This file can be used to override some of udev's default values 
 # for where it looks for files, and where it places device nodes. 
 # 
 # WARNING: changing any value, can cause serious system breakage! 
 # 

 # udev_root - where in the filesystem to place the device nodes 
 udev_root="/dev/"

 # udev_db - The name and location of the udev database. 
 udev_db="/dev/.udev.tdb"

 # udev_rules - The name and location of the udev rules file 
 udev_rules="/etc/udev/rules.d/"

 # udev_permissions - The name and location of the udev permission file 
 udev_permissions="/etc/udev/permissions.d/"

 # default_mode - set the default mode for all nodes that have no 
 #                explicit match in the permissions file 
 default_mode="0600"

 # default_owner - set the default owner for all nodes that have no 
 #                 explicit match in the permissions file 
 default_owner="root"

 # default_group - set the default group for all nodes that have no 
 #                 explicit match in the permissions file 
 default_group="root"

 # udev_log - set to "yes" if you want logging, else "no"
 udev_log="no"

Linux 用戶能夠經過該文件設置如下參數:

udev_root:udev 產生的設備所存放的目錄,默認值是 /dev/。建議不要修改該參數,由於不少應用程序默認會從該目錄調用設備文件。
udev_db:udev 信息存放的數據庫或者所在目錄,默認值是 /dev/.udev.tdb
udev_rules:udev 規則文件的名字或者所在目錄,默認值是 /etc/udev/rules.d/或/lib/udev/rules.d
udev_permissions:udev 權限文件的名字或者所在目錄,默認值是 /etc/udev/permissions.d/。
default_mode/ default_owner/ default_group:若是設備文件的權限沒有在權限文件裏指定,就使用該參數做爲默認權限,默認值分別是:0600/root/root。
udev_log:是否須要 syslog記錄 udev 日誌的開關,默認值是 no。syslog記錄日誌的級別,默認值是 err。若是改成 info 或者 debug 的話,會有冗長的 udev 日誌被記錄下來。

其實,在咱們的終端上,udev配置文件沒有那麼複雜,由於大部分都是默認值,因此不用配置,看我ubuntu下的udev配置文件:

$cat /etc/udev/udev.conf 
# see udev(7) for details # # udevd is started in the initramfs, so when this file is modified the # initramfs should be rebuilt. #udev_log="info"

該文件都是註釋,說明都用默認值。

2.2 udev的規則和規則文件

規則文件是 udev 裏最重要的部分,默認是存放在 /etc/udev/rules.d/下。全部的規則文件必須以「.rules」爲後綴名。

規則文件裏的規則有一系列的鍵/值對組成,鍵/值對之間用逗號(,)分割。每個鍵或者是用戶匹配鍵,或者是一個賦值鍵。匹配鍵肯定規則是否被應用,而賦 值鍵表示分配某值給該鍵。這些值將影響udev建立的設備文件。賦值鍵能夠處理一個多值列表。匹配鍵和賦值鍵操做符解釋見下表:

2.2.1 udev 鍵/值對操做符

操做符   匹配或賦值   解釋
----------------------------------------
==     匹配      相等比較
!=      匹配        不等比較
=     賦值      分配一個特定的值給該鍵,他能夠覆蓋以前的賦值。
+=     賦值             追加特定的值給已經存在的鍵
:=           賦值             分配一個特定的值給該鍵,後面的規則不可能覆蓋它。

2.2.2 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,則該設備匹配該 匹配鍵。

2.2.3 udev 的重要賦值鍵

NAME:在 /dev下產生的設備文件名。只有第一次對某個設備的 NAME 的賦值行爲生效,以後匹配的規則再對該設備的 NAME 賦值行爲將被忽略。若是沒有任何規則對設備的 NAME 賦值,udev 將使用內核設備名稱來產生設備文件。

SYMLINK:爲 /dev/下的設備文件產生符號連接。因爲 udev 只能爲某個設備產生一個設備文件,因此爲了避免覆蓋系統默認的 udev 規則所產生的文件,推薦使用符號連接。

OWNER, GROUP, MODE:爲設備設定權限。

ENV{key}:導入一個環境變量。

2.2.4 udev 的值和可調用的替換操做符

在鍵值對中的鍵和操做符都介紹完了,最後是值 (value)。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規則

其實,在安裝udev時,會生成一系列的udev規則文件放在/etc/udev/rules.d/或/lib/udev/rules.d/目錄下,好比/lib/udev/rules.d/50-udev-default.rules等默認的udev規則文件。

咱們只需添加本身想改的udev規則文件讓其作咱們想作的事,好比當U盤插入時,我要自動掛載它(或者其餘什麼動做),其實安裝了udev,當U盤插入時,它有相應的.rules規則文件去解析它(即udev會動態管理它),在/dev/目錄下產生相應的設備文件,好比/dev/sda4.但如今我須要在U盤插入時,我自動掛載它(好比掛載到/mnt/udisk/目錄下),那麼我就能夠編寫本身的udev規則文件automount.rules。該文件以下:

# There are a number of modifiers that are allowed to be used in some
# of the different fields. They provide the following subsitutions:
#
# %n the "kernel number" of the device.
#    For example, 'sda3' has a "kernel number" of '3'
# %e the smallest number for that name which does not matches an existing node
# %k the kernel name for the device
# %M the kernel major number for the device
# %m the kernel minor number for the device
# %b the bus id for the device
# %c the string returned by the PROGRAM
# %s{filename} the content of a sysfs attribute
# %% the '%' char itself
#

# Media automounting
SUBSYSTEM=="block", ACTION=="add"    RUN+="/etc/udev/rules.d/mount.sh"
SUBSYSTEM=="block", ACTION=="remove" RUN+="/etc/udev/rules.d/mount.sh"
SUBSYSTEM=="block", ACTION=="change", ENV{DISK_MEDIA_CHANGE}=="1" RUN+="/etc/udev/rules.d/mount.sh"

說明:當有U盤熱插拔時都會去跑/etc/udev/rules.d/mount.sh腳本。其中SUBSYSTEM=="block",U盤sda的子系統就是block。

mount.sh以下:

MOUNT="/bin/mount"
PMOUNT="/usr/bin/pmount"
UMOUNT="/bin/umount"
for line in `grep -v ^# /etc/udev/mount.blacklist`
do
    name="`basename "$DEVNAME"`"
    if [ ` expr match "$DEVNAME" "$line" ` -gt 0 ] || [ ` expr match "$name" "$line" ` -gt 0 ]
    then
        logger "udev/mount.sh" "[$DEVNAME] is blacklisted, ignoring"
        exit 0
    fi
done

automount() {    
    name="`basename "$DEVNAME"`"

    if [[ $name =~ sd ]];then
        mount_dir=/mnt/udisk
        ! test -d $mount_dir && mkdir -p $mount_dir
    elif [[ $name =~ mmcblk ]];then
        mount_dir=/mnt/sdisk
        ! test -d $mount_dir && mkdir -p $mount_dir
    fi
    
    # Silent util-linux's version of mounting auto
    if [ "x`readlink $MOUNT`" = "x/bin/mount.util-linux" ] ;
    then
        MOUNT="$MOUNT -o silent"
    fi
    
    # If filesystem type is vfat, change the ownership group to 'disk', and
    # grant it with  w/r/x permissions.
    case $ID_FS_TYPE in
    vfat|fat)
        MOUNT="$MOUNT -o umask=007,gid=`awk -F':' '/^disk/{print $3}' /etc/group`"
        ;;
    # TODO
    *)
        ;;
    esac

    if ! $MOUNT -t auto -o iocharset=cp936 $DEVNAME $mount_dir
    then
        logger "mount.sh/automount" "$MOUNT -t auto $DEVNAME $mount_dir failed!"
        rm_dir "/run/media/$name"
    else
        logger "mount.sh/automount" "Auto-mount of [ $mount_dir] successful"
        touch "/tmp/.automount-$name"
        killall -USR1 adas.exe
    fi
}
    
rm_dir() {
    # We do not want to rm -r populated directories
    if test "`find "$1" | wc -l | tr -d " "`" -lt 2 -a -d "$1"
    then
        ! test -z "$1" && rm -r "$1"
        killall -USR1 adas.exe
    else
        logger "mount.sh/automount" "Not removing non-empty directory [$1]"
    fi
}

# No ID_FS_TYPE for cdrom device, yet it should be mounted
name="`basename "$DEVNAME"`"
[ -e /sys/block/$name/device/media ] && media_type=`cat /sys/block/$name/device/media`

if [ "$ACTION" = "add" ] && [ -n "$DEVNAME" ] && [ -n "$ID_FS_TYPE" -o "$media_type" = "cdrom" ]; then
    if [ -x "$PMOUNT" ]; then
        $PMOUNT $DEVNAME 2> /dev/null
    elif [ -x $MOUNT ]; then
            $MOUNT $DEVNAME 2> /dev/null
    fi
    
    # If the device isn't mounted at this point, it isn't
    # configured in fstab (note the root filesystem can show up as
    # /dev/root in /proc/mounts, so check the device number too)
    if expr $MAJOR "*" 256 + $MINOR != `stat -c %d /`; then
        grep -q "^$DEVNAME " /proc/mounts || automount
    fi
fi


if [ "$ACTION" = "remove" ] || [ "$ACTION" = "change" ] && [ -x "$UMOUNT" ] && [ -n "$DEVNAME" ]; then
    for mnt in `cat /proc/mounts | grep "$DEVNAME" | cut -f 2 -d " " `
    do
        $UMOUNT $mnt
    done
    
    # Remove empty directories from auto-mounter
    name="`basename "$DEVNAME"`"
    if [[ $name =~ sd ]];then
        mount_dir=/mnt/udisk
    elif [[ $name =~ mmcblk ]];then
        mount_dir=/mnt/sdisk
    fi
    test -e "/tmp/.automount-$name" && rm_dir $mount_dir
fi

這裏只是舉一個例子而已。能夠編寫咱們想要的規則文件。

3.1 如何編寫規則文件呢?

其實就是兩點:匹配鍵賦值鍵,只需完善這兩點就能夠編寫咱們想要的規則文件

好比:KERNEL=="tty", NAME="%k", GROUP="tty", MODE="0666", OPTIONS="last_rule"

該規則說明:若是有一個設備的內核設備名稱爲tty(KERNEL=="tty"),那麼設置新的權限爲0600(MODE="0666"),所在的組是tty(GROUP="tty")。它也設置了一個特別的設備文件名:%K。在這裏例子裏,%k表明設備的內核名字。那也就意味着內核識別出這些設備是什麼名字,就建立什麼樣的設備文件名。

在這裏就是要完善兩點,匹配鍵KERNEL=="tty";賦值鍵NAME="%k", GROUP="tty", MODE="0666", OPTIONS="last_rule"

其實關鍵是要如何找到設備的屬性呢,即拿什麼區匹配,規則所須要的信息如何獲取?

能夠利用udev的命令:好比udevadm info -a -p $(udevadm info -q path -n /dev/sda4). 其中udevadm info -q path -n /dev/sda4返回sysfs中的設備路徑;udevadm info -a -p $(設備路徑),這將查詢這個設備路徑,把結果信息輸出來:以下:

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18/target18:0:0/18:0:0:0/block/sdb/sdb4':
    KERNEL=="sdb4"
    SUBSYSTEM=="block"
    DRIVER==""
    ATTR{ro}=="0"
    ATTR{size}=="15177600"
    ATTR{stat}=="     202      382     1562      212        0        0        0        0        0      164      212"
    ATTR{partition}=="4"
    ATTR{start}=="14880"
    ATTR{discard_alignment}=="0"
    ATTR{alignment_offset}=="0"
    ATTR{inflight}=="       0        0"

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18/target18:0:0/18:0:0:0/block/sdb':
    KERNELS=="sdb"
    SUBSYSTEMS=="block"
    DRIVERS==""
    ATTRS{ro}=="0"
    ATTRS{size}=="15204352"
    ATTRS{stat}=="     295      382     2306     1208        0        0        0        0        0     1156     1204"
    ATTRS{range}=="16"
    ATTRS{discard_alignment}=="0"
    ATTRS{events}=="media_change"
    ATTRS{ext_range}=="256"
    ATTRS{events_poll_msecs}=="2000"
    ATTRS{alignment_offset}=="0"
    ATTRS{inflight}=="       0        0"
    ATTRS{removable}=="1"
    ATTRS{capability}=="51"
    ATTRS{events_async}==""

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18/target18:0:0/18:0:0:0':
    KERNELS=="18:0:0:0"
    SUBSYSTEMS=="scsi"
    DRIVERS=="sd"
    ATTRS{rev}=="1.00"
    ATTRS{type}=="0"
    ATTRS{scsi_level}=="3"
    ATTRS{model}=="                "
    ATTRS{state}=="running"
    ATTRS{queue_type}=="none"
    ATTRS{iodone_cnt}=="0x152"
    ATTRS{iorequest_cnt}=="0x152"
    ATTRS{device_busy}=="0"
    ATTRS{evt_capacity_change_reported}=="0"
    ATTRS{timeout}=="30"
    ATTRS{evt_media_change}=="0"
    ATTRS{max_sectors}=="240"
    ATTRS{ioerr_cnt}=="0x1"
    ATTRS{queue_depth}=="1"
    ATTRS{vendor}=="        "
    ATTRS{evt_soft_threshold_reached}=="0"
    ATTRS{device_blocked}=="0"
    ATTRS{evt_mode_parameter_change_reported}=="0"
    ATTRS{evt_lun_change_reported}=="0"
    ATTRS{evt_inquiry_change_reported}=="0"
    ATTRS{iocounterbits}=="32"
    ATTRS{eh_timeout}=="10"

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18/target18:0:0':
    KERNELS=="target18:0:0"
    SUBSYSTEMS=="scsi"
    DRIVERS==""

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18':
    KERNELS=="host18"
    SUBSYSTEMS=="scsi"
    DRIVERS==""

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0':
    KERNELS=="1-8:1.0"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb-storage"
    ATTRS{bInterfaceClass}=="08"
    ATTRS{bInterfaceSubClass}=="06"
    ATTRS{bInterfaceProtocol}=="50"
    ATTRS{bNumEndpoints}=="02"
    ATTRS{supports_autosuspend}=="1"
    ATTRS{bAlternateSetting}==" 0"
    ATTRS{bInterfaceNumber}=="00"

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8':
    KERNELS=="1-8"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{devpath}=="8"
    ATTRS{idVendor}=="1516"
    ATTRS{speed}=="480"
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{busnum}=="1"
    ATTRS{devnum}=="17"
    ATTRS{configuration}==""
    ATTRS{bMaxPower}=="500mA"
    ATTRS{authorized}=="1"
    ATTRS{bmAttributes}=="80"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{maxchild}=="0"
    ATTRS{bcdDevice}=="0100"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{quirks}=="0x0"
    ATTRS{version}==" 2.00"
    ATTRS{urbnum}=="1001"
    ATTRS{ltm_capable}=="no"
    ATTRS{manufacturer}=="SKYMEDI"
    ATTRS{removable}=="removable"
    ATTRS{idProduct}=="1226"
    ATTRS{bDeviceClass}=="00"
    ATTRS{product}=="USB Drive"

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1':
    KERNELS=="usb1"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceProtocol}=="01"
    ATTRS{devpath}=="0"
    ATTRS{idVendor}=="1d6b"
    ATTRS{speed}=="480"
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{authorized_default}=="1"
    ATTRS{busnum}=="1"
    ATTRS{devnum}=="1"
    ATTRS{configuration}==""
    ATTRS{bMaxPower}=="0mA"
    ATTRS{authorized}=="1"
    ATTRS{bmAttributes}=="e0"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{maxchild}=="15"
    ATTRS{bcdDevice}=="0402"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{quirks}=="0x0"
    ATTRS{serial}=="0000:00:14.0"
    ATTRS{version}==" 2.00"
    ATTRS{urbnum}=="348"
    ATTRS{ltm_capable}=="no"
    ATTRS{manufacturer}=="Linux 4.2.0-42-generic xhci-hcd"
    ATTRS{removable}=="unknown"
    ATTRS{idProduct}=="0002"
    ATTRS{bDeviceClass}=="09"
    ATTRS{product}=="xHCI Host Controller"

  looking at parent device '/devices/pci0000:00/0000:00:14.0':
    KERNELS=="0000:00:14.0"
    SUBSYSTEMS=="pci"
    DRIVERS=="xhci_hcd"
    ATTRS{irq}=="27"
    ATTRS{subsystem_vendor}=="0x1028"
    ATTRS{broken_parity_status}=="0"
    ATTRS{class}=="0x0c0330"
    ATTRS{driver_override}=="(null)"
    ATTRS{consistent_dma_mask_bits}=="64"
    ATTRS{dma_mask_bits}=="64"
    ATTRS{local_cpus}=="f"
    ATTRS{device}=="0x8c31"
    ATTRS{enable}=="1"
    ATTRS{msi_bus}=="1"
    ATTRS{local_cpulist}=="0-3"
    ATTRS{vendor}=="0x8086"
    ATTRS{subsystem_device}=="0x05a5"
    ATTRS{numa_node}=="-1"
    ATTRS{d3cold_allowed}=="1"

  looking at parent device '/devices/pci0000:00':
    KERNELS=="pci0000:00"
    SUBSYSTEMS==""
    DRIVERS==""

從中能夠獲得KERNEL,SUBSYSTEM等信息,這樣就能夠利用這些屬性值去匹配。其實咱們能夠從那裏看到「looking at parent device」是一層往一層打印出該設備的信息。

這樣咱們就能夠KERNEL=="sdb4", SUBSYSTEM=="block"去匹配,好比寫udev規則以下:

KERNEL=="sdb4", SUBSYSTEM=="block", RUN+="/etc/udev/rules.d/mount.sh"

4. 字符串替換和匹配

4.1 字符串替換

$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:臨時設備名

%%:符號 % 自己

$$:符號 $ 自己

例子以下:

KERNEL=="fb[0-9]*", NAME="fb/%n", SYMLINK+="%k"

規則意思是:匹配一個內核命名爲fb[0-9]的設備,而後給它命名爲fb/%n,符號連接爲%k

好比有一個fb3設備,匹配成功後那麼久有一個名爲fb/3,符號連接爲fb3

4.2 字符串匹配

不只有字符串精確匹配, udev也容許你使用shell風格的模式匹配. 支持的3種模式爲:
* - 匹配任何字符, 匹配0次或屢次
? - 匹配任何字符,但只匹配一次.
[] - 匹配任何單個字符, 這些字符在方括號裏面指定, 範圍是受限的.
這裏有一些例子, 注意字符串替換符的使用:
KERNEL=="fd[0-9]*", NAME="floppy/%n", SYMLINK+="%k"
KERNEL=="hiddev*", NAME="usb/%k"
第一條規則匹配全部軟盤驅動並確保設備節點放置在/dev/floppy目錄下, 也建立一個缺省名字的符號連接. 第二條規則確保hiddev設備節點放在/dev/usb目錄下面.

5. udev主要做用

  • 重命名設備節點的缺省名字爲其餘名字
  • 經過建立符號連接到缺省設備節點來提供一個可選的固定的設備節點名字
  • 基於程序的輸出命名設備節點
  • 改變設備節點的權限和全部權
  • 在設備節點被建立或刪除時(一般是添加設備或拔出設備時)執行一個腳本
  • 重命名網絡接口

5.1 重命名設備節點的缺省名字爲其餘名字

此時使用NAME賦值鍵,例子以下:

一個硬盤,它的設備屬性KERNEL是hdb,在/dev/目錄下是/dev/hdb,那麼咱們能夠給他重命名爲

KERNEL=="hdb", NAME="my_spare_disk"

規則意思是:匹配一個設備命名爲hdb的設備,把它從新命名爲my_spare_disk. 設備節點出如今/dev/my_spare_disk

執行如下命令:ls /dev/my_spare_disk -l

/dev/my_spare_disk ---> /dev/hdb產生一個符號連接指向/dev/hdb

注意:僅僅第一行的NAME描述是有效的,後面的均忽略。即udev按順序解析udev規則文件時,第一個NAME賦值鍵的名字有用,假如後面對同一個設備還有NAME賦值鍵,那麼那個賦值的名稱將被忽略。若是你想使用使用兩個以上的名字來訪問一個設備的話,能夠考慮SYMLINK鍵

若是你想你命名的名字獲得實現,你必須把你的規則文件命名順序在前面。

5.2 經過建立符號連接到缺省設備節點來提供一個可選的固定的設備節點名字

例子以下:

KERNEL=="hdb", DRIVER=="ide-disk", SYMLINK+="sparedisk"

規則意思是:匹配一個內核命名爲hdb以及驅動爲ide-disk的設備,命名設備節點爲缺省名字並建立一個指向它的sparedisk符號連接,設備節點出如今/dev/sparedisk

注意:符號連接能夠是多個,這些符號連接都指向/dev/hdb

5.3 基於程序的輸出命名設備節點

某些狀況下你可能要求比udev標準規則提供的更多彈性, 這種狀況下你能夠請求udev運行一個程序並運用程序的標準輸出來提供設備命名.

要使用這個功能,你只需簡單的在PROGRAM賦值中指定要運行程序(以及任何闡述)的完整路徑, 而後在NAME/SYMLINK賦值中使用一些%c替換.

例子以下:

引用一個位於/bin/device_namer的虛構程序. device_namer帶一個表示內核名字的命令行參數, 基於內核名device_namer作一些變化而後輸出

KERNEL=="hda", PROGRAM="/bin/device_namer %k", SYMLINK+="%c"

規則意思是:匹配一個內核命名爲hdb的設備,而後運行一個/bin/device_name程序,這個程序須要帶一個表示內核名字的命令行參數即%k。而後這個程序運行的結果(即輸出)把它賦值給SYMLINK,這樣就能夠知足要求(使用外部程序來命名設備)

4.4 改變設備節點的權限和全部權

udev容許你在規則中使用另外的賦值來控制每一個設備的全部權和權限屬性.

例子以下:

KERNEL=="fb[0-9]*", NAME="fb/%n", SYMLINK+="%k", GROUP="video", MODE="0666"

規則意思是:匹配一個內核命名爲fb[0-9]的設備,而後給它命名爲fb/%n,符號連接爲%k, 屬於video組, 權限爲0666

好比有一個fb3設備,匹配成功後那麼久有一個名爲fb/3,符號連接爲fb3,屬組爲video,權限爲0666

5.5 在設備節點被建立或刪除時(一般是添加設備或拔出設備時)執行一個腳本

特別針對熱插拔的設備,目的是爲了在設備鏈接或者斷開時運行一個特定程序. 例如, 你可能想在你的數碼相機連到系統時執行一個腳原本自動下載相機裏面的全部照片.

例子以下:

KERNEL=="sdb", ACTION=="add", RUN+="/usr/bin/my_program"

規則意思是:匹配一個內核名爲sdb的設備,當插入時,執行程序/usr/bin/my_program

5.6 重命名網絡接口

在規則中簡單的匹配網卡MAC地址是有意義的,由於它們是惟一的. 
# udevadm info -a -p /sys/class/net/eth0
looking at class device '/sys/class/net/eth0':
KERNEL=="eth0"
ATTR{address}=="00:52:8b:d5:04:48"
規則以下:
KERNEL=="eth*", ATTR{address}=="00:52:8b:d5:04:48", NAME="lan"

這樣就重命名了eth*爲lan

6. 編寫udev規則實例

USB打印機
我啓動個人打印機, 它就被賦予了一個設備節點/dev/lp0. 我對這樣的單調的名字不滿意並打算使用udevinfo幫我寫一個規則來提供一個可選名字:
# udevinfo -a -p $(udevinfo -q path -n /dev/lp0)
looking at device '/class/usb/lp0':
KERNEL=="lp0"
SUBSYSTEM=="usb"
DRIVER==""
ATTR{dev}=="180:0"

looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb1/1-1':
SUBSYSTEMS=="usb"
ATTRS{manufacturer}=="EPSON"
ATTRS{product}=="USB Printer"
ATTRS{serial}=="L72010011070626380"
個人規則變成了這樣:
SUBSYSTEM=="usb", ATTRS{serial}=="L72010011070626380", SYMLINK+="epson_680"

7. 難點解析

7.1 ATTR

ATTR{value}  sysfs設備屬性值,能夠爲任意值,用於匹配

能夠利用udev的命令:好比udevadm info -a -p $(udevadm info -q path -n /dev/sda4). 其中udevadm info -q path -n /dev/sda4返回sysfs中的設備路徑;udevadm info -a -p $(設備路徑),這將查詢這個設備路徑,把結果信息輸出來:以下:

主要內容:

  • udev簡介
  • 如何配置和使用udev
  • 如何編寫udev規則
  • 字符串替換和匹配
  • udev主要做用
  • 編寫udev規則實例
  • 難點解析

1. udev簡介

1.1 什麼是udev?

udev是Linux(linux2.6內核以後)默認的設備管理工具。udev 以守護進程的形式運行,經過偵聽內核發出來的 uevent 來管理 /dev目錄下的設備文件。

如何理解udev是守護進程呢?即系統內核啓動後init進程(好比busybox的init程序、sysinit、Upstart或systemd)根據runlevel運行等級進入某種模式,而後解析開啓哪些服務進程。其中udev就是哪些服務進程中的一個,服務進程是在後臺運行的。能夠經過命令ps -aux來獲取,好比在ubuntu終端中ps -aux | grep udev

root       328  0.0  0.0  52220   852 ?        Ss    2月23   0:00 /lib/systemd/systemd-udevd --daemon

因此只要有設備插入或刪除,守護進程udev就會管理它。

也就是說使用了udev,全部的設備都能在/dev/目錄下找到對應的設備文件。

1.2 使用udev的好處

動態管理:當設備添加 / 刪除時,udev 的守護進程偵聽來自內核的 uevent,以此添加或者刪除 /dev下的設備文件,因此 udev 只爲已經鏈接的設備產生設備文件,而不會在 /dev下產生大量虛無的設備文件。

自定義命名規則:經過 Linux 默認的規則文件,udev 在 /dev/ 裏爲全部的設備定義了內核設備名稱,好比 /dev/sda、/dev/hda、/dev/fd等等。因爲 udev 是在用戶空間 (user space) 運行,Linux 用戶能夠經過自定義的規則文件,靈活地產生標識性強的設備文件名,好比 /dev/boot_disk、/dev/root_disk、/dev/color_printer等等。

設定設備的權限和全部者 / 組:udev 能夠按必定的條件來設置設備文件的權限和設備文件全部者 / 組

1.3 udev工做流程

2. 如何配置和使用udev

2.1 udev的配置文件(/etc/udev/udev.conf)

[root@HOST_RHEL4 dev]# cat /etc/udev/udev.conf 
 # udev.conf 
 # The main config file for udev # # This file can be used to override some of udev's default values # for where it looks for files, and where it places device nodes. # # WARNING: changing any value, can cause serious system breakage! # # udev_root - where in the filesystem to place the device nodes udev_root="/dev/" # udev_db - The name and location of the udev database. udev_db="/dev/.udev.tdb" # udev_rules - The name and location of the udev rules file udev_rules="/etc/udev/rules.d/" # udev_permissions - The name and location of the udev permission file udev_permissions="/etc/udev/permissions.d/" # default_mode - set the default mode for all nodes that have no # explicit match in the permissions file default_mode="0600" # default_owner - set the default owner for all nodes that have no # explicit match in the permissions file default_owner="root" # default_group - set the default group for all nodes that have no # explicit match in the permissions file default_group="root" # udev_log - set to "yes" if you want logging, else "no" udev_log="no"

Linux 用戶能夠經過該文件設置如下參數:

udev_root:udev 產生的設備所存放的目錄,默認值是 /dev/。建議不要修改該參數,由於不少應用程序默認會從該目錄調用設備文件。
udev_db:udev 信息存放的數據庫或者所在目錄,默認值是 /dev/.udev.tdb
udev_rules:udev 規則文件的名字或者所在目錄,默認值是 /etc/udev/rules.d/或/lib/udev/rules.d
udev_permissions:udev 權限文件的名字或者所在目錄,默認值是 /etc/udev/permissions.d/。
default_mode/ default_owner/ default_group:若是設備文件的權限沒有在權限文件裏指定,就使用該參數做爲默認權限,默認值分別是:0600/root/root。
udev_log:是否須要 syslog記錄 udev 日誌的開關,默認值是 no。syslog記錄日誌的級別,默認值是 err。若是改成 info 或者 debug 的話,會有冗長的 udev 日誌被記錄下來。

其實,在咱們的終端上,udev配置文件沒有那麼複雜,由於大部分都是默認值,因此不用配置,看我ubuntu下的udev配置文件:

$cat /etc/udev/udev.conf 
# see udev(7) for details # # udevd is started in the initramfs, so when this file is modified the # initramfs should be rebuilt. #udev_log="info"

該文件都是註釋,說明都用默認值。

2.2 udev的規則和規則文件

規則文件是 udev 裏最重要的部分,默認是存放在 /etc/udev/rules.d/下。全部的規則文件必須以「.rules」爲後綴名。

規則文件裏的規則有一系列的鍵/值對組成,鍵/值對之間用逗號(,)分割。每個鍵或者是用戶匹配鍵,或者是一個賦值鍵。匹配鍵肯定規則是否被應用,而賦 值鍵表示分配某值給該鍵。這些值將影響udev建立的設備文件。賦值鍵能夠處理一個多值列表。匹配鍵和賦值鍵操做符解釋見下表:

2.2.1 udev 鍵/值對操做符

操做符   匹配或賦值   解釋
----------------------------------------
==     匹配      相等比較
!=      匹配        不等比較
=     賦值      分配一個特定的值給該鍵,他能夠覆蓋以前的賦值。
+=     賦值             追加特定的值給已經存在的鍵
:=           賦值             分配一個特定的值給該鍵,後面的規則不可能覆蓋它。

2.2.2 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,則該設備匹配該 匹配鍵。

2.2.3 udev 的重要賦值鍵

NAME:在 /dev下產生的設備文件名。只有第一次對某個設備的 NAME 的賦值行爲生效,以後匹配的規則再對該設備的 NAME 賦值行爲將被忽略。若是沒有任何規則對設備的 NAME 賦值,udev 將使用內核設備名稱來產生設備文件。

SYMLINK:爲 /dev/下的設備文件產生符號連接。因爲 udev 只能爲某個設備產生一個設備文件,因此爲了避免覆蓋系統默認的 udev 規則所產生的文件,推薦使用符號連接。

OWNER, GROUP, MODE:爲設備設定權限。

ENV{key}:導入一個環境變量。

2.2.4 udev 的值和可調用的替換操做符

在鍵值對中的鍵和操做符都介紹完了,最後是值 (value)。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規則

其實,在安裝udev時,會生成一系列的udev規則文件放在/etc/udev/rules.d/或/lib/udev/rules.d/目錄下,好比/lib/udev/rules.d/50-udev-default.rules等默認的udev規則文件。

咱們只需添加本身想改的udev規則文件讓其作咱們想作的事,好比當U盤插入時,我要自動掛載它(或者其餘什麼動做),其實安裝了udev,當U盤插入時,它有相應的.rules規則文件去解析它(即udev會動態管理它),在/dev/目錄下產生相應的設備文件,好比/dev/sda4.但如今我須要在U盤插入時,我自動掛載它(好比掛載到/mnt/udisk/目錄下),那麼我就能夠編寫本身的udev規則文件automount.rules。該文件以下:

# There are a number of modifiers that are allowed to be used in some
# of the different fields. They provide the following subsitutions:
#
# %n the "kernel number" of the device. # For example, 'sda3' has a "kernel number" of '3' # %e the smallest number for that name which does not matches an existing node # %k the kernel name for the device # %M the kernel major number for the device # %m the kernel minor number for the device # %b the bus id for the device # %c the string returned by the PROGRAM # %s{filename} the content of a sysfs attribute # %% the '%' char itself # # Media automounting SUBSYSTEM=="block", ACTION=="add" RUN+="/etc/udev/rules.d/mount.sh" SUBSYSTEM=="block", ACTION=="remove" RUN+="/etc/udev/rules.d/mount.sh" SUBSYSTEM=="block", ACTION=="change", ENV{DISK_MEDIA_CHANGE}=="1" RUN+="/etc/udev/rules.d/mount.sh"

說明:當有U盤熱插拔時都會去跑/etc/udev/rules.d/mount.sh腳本。其中SUBSYSTEM=="block",U盤sda的子系統就是block。

mount.sh以下:

MOUNT="/bin/mount"
PMOUNT="/usr/bin/pmount" UMOUNT="/bin/umount" for line in `grep -v ^# /etc/udev/mount.blacklist` do name="`basename "$DEVNAME"`" if [ ` expr match "$DEVNAME" "$line" ` -gt 0 ] || [ ` expr match "$name" "$line" ` -gt 0 ] then logger "udev/mount.sh" "[$DEVNAME] is blacklisted, ignoring" exit 0 fi done automount() { name="`basename "$DEVNAME"`" if [[ $name =~ sd ]];then mount_dir=/mnt/udisk ! test -d $mount_dir && mkdir -p $mount_dir elif [[ $name =~ mmcblk ]];then mount_dir=/mnt/sdisk ! test -d $mount_dir && mkdir -p $mount_dir fi # Silent util-linux's version of mounting auto if [ "x`readlink $MOUNT`" = "x/bin/mount.util-linux" ] ; then MOUNT="$MOUNT -o silent" fi # If filesystem type is vfat, change the ownership group to 'disk', and # grant it with w/r/x permissions. case $ID_FS_TYPE in vfat|fat) MOUNT="$MOUNT -o umask=007,gid=`awk -F':' '/^disk/{print $3}' /etc/group`" ;; # TODO *) ;; esac if ! $MOUNT -t auto -o iocharset=cp936 $DEVNAME $mount_dir then logger "mount.sh/automount" "$MOUNT -t auto $DEVNAME $mount_dir failed!" rm_dir "/run/media/$name" else logger "mount.sh/automount" "Auto-mount of [ $mount_dir] successful" touch "/tmp/.automount-$name" killall -USR1 adas.exe fi } rm_dir() { # We do not want to rm -r populated directories if test "`find "$1" | wc -l | tr -d " "`" -lt 2 -a -d "$1" then ! test -z "$1" && rm -r "$1" killall -USR1 adas.exe else logger "mount.sh/automount" "Not removing non-empty directory [$1]" fi } # No ID_FS_TYPE for cdrom device, yet it should be mounted name="`basename "$DEVNAME"`" [ -e /sys/block/$name/device/media ] && media_type=`cat /sys/block/$name/device/media` if [ "$ACTION" = "add" ] && [ -n "$DEVNAME" ] && [ -n "$ID_FS_TYPE" -o "$media_type" = "cdrom" ]; then if [ -x "$PMOUNT" ]; then $PMOUNT $DEVNAME 2> /dev/null elif [ -x $MOUNT ]; then $MOUNT $DEVNAME 2> /dev/null fi # If the device isn't mounted at this point, it isn't # configured in fstab (note the root filesystem can show up as # /dev/root in /proc/mounts, so check the device number too) if expr $MAJOR "*" 256 + $MINOR != `stat -c %d /`; then grep -q "^$DEVNAME " /proc/mounts || automount fi fi if [ "$ACTION" = "remove" ] || [ "$ACTION" = "change" ] && [ -x "$UMOUNT" ] && [ -n "$DEVNAME" ]; then for mnt in `cat /proc/mounts | grep "$DEVNAME" | cut -f 2 -d " " ` do $UMOUNT $mnt done # Remove empty directories from auto-mounter name="`basename "$DEVNAME"`" if [[ $name =~ sd ]];then mount_dir=/mnt/udisk elif [[ $name =~ mmcblk ]];then mount_dir=/mnt/sdisk fi test -e "/tmp/.automount-$name" && rm_dir $mount_dir fi

這裏只是舉一個例子而已。能夠編寫咱們想要的規則文件。

3.1 如何編寫規則文件呢?

其實就是兩點:匹配鍵賦值鍵,只需完善這兩點就能夠編寫咱們想要的規則文件

好比:KERNEL=="tty", NAME="%k", GROUP="tty", MODE="0666", OPTIONS="last_rule"

該規則說明:若是有一個設備的內核設備名稱爲tty(KERNEL=="tty"),那麼設置新的權限爲0600(MODE="0666"),所在的組是tty(GROUP="tty")。它也設置了一個特別的設備文件名:%K。在這裏例子裏,%k表明設備的內核名字。那也就意味着內核識別出這些設備是什麼名字,就建立什麼樣的設備文件名。

在這裏就是要完善兩點,匹配鍵KERNEL=="tty";賦值鍵NAME="%k", GROUP="tty", MODE="0666", OPTIONS="last_rule"

其實關鍵是要如何找到設備的屬性呢,即拿什麼區匹配,規則所須要的信息如何獲取?

能夠利用udev的命令:好比udevadm info -a -p $(udevadm info -q path -n /dev/sda4). 其中udevadm info -q path -n /dev/sda4返回sysfs中的設備路徑;udevadm info -a -p $(設備路徑),這將查詢這個設備路徑,把結果信息輸出來:以下:

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device found, all possible attributes in the udev rules key format. A rule to match, can be composed by the attributes of the device and the attributes from one single parent device. looking at device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18/target18:0:0/18:0:0:0/block/sdb/sdb4': KERNEL=="sdb4" SUBSYSTEM=="block" DRIVER=="" ATTR{ro}=="0" ATTR{size}=="15177600" ATTR{stat}==" 202 382 1562 212 0 0 0 0 0 164 212" ATTR{partition}=="4" ATTR{start}=="14880" ATTR{discard_alignment}=="0" ATTR{alignment_offset}=="0" ATTR{inflight}==" 0 0" looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18/target18:0:0/18:0:0:0/block/sdb': KERNELS=="sdb" SUBSYSTEMS=="block" DRIVERS=="" ATTRS{ro}=="0" ATTRS{size}=="15204352" ATTRS{stat}==" 295 382 2306 1208 0 0 0 0 0 1156 1204" ATTRS{range}=="16" ATTRS{discard_alignment}=="0" ATTRS{events}=="media_change" ATTRS{ext_range}=="256" ATTRS{events_poll_msecs}=="2000" ATTRS{alignment_offset}=="0" ATTRS{inflight}==" 0 0" ATTRS{removable}=="1" ATTRS{capability}=="51" ATTRS{events_async}=="" looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18/target18:0:0/18:0:0:0': KERNELS=="18:0:0:0" SUBSYSTEMS=="scsi" DRIVERS=="sd" ATTRS{rev}=="1.00" ATTRS{type}=="0" ATTRS{scsi_level}=="3" ATTRS{model}==" " ATTRS{state}=="running" ATTRS{queue_type}=="none" ATTRS{iodone_cnt}=="0x152" ATTRS{iorequest_cnt}=="0x152" ATTRS{device_busy}=="0" ATTRS{evt_capacity_change_reported}=="0" ATTRS{timeout}=="30" ATTRS{evt_media_change}=="0" ATTRS{max_sectors}=="240" ATTRS{ioerr_cnt}=="0x1" ATTRS{queue_depth}=="1" ATTRS{vendor}==" " ATTRS{evt_soft_threshold_reached}=="0" ATTRS{device_blocked}=="0" ATTRS{evt_mode_parameter_change_reported}=="0" ATTRS{evt_lun_change_reported}=="0" ATTRS{evt_inquiry_change_reported}=="0" ATTRS{iocounterbits}=="32" ATTRS{eh_timeout}=="10" looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18/target18:0:0': KERNELS=="target18:0:0" SUBSYSTEMS=="scsi" DRIVERS=="" looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18': KERNELS=="host18" SUBSYSTEMS=="scsi" DRIVERS=="" looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0': KERNELS=="1-8:1.0" SUBSYSTEMS=="usb" DRIVERS=="usb-storage" ATTRS{bInterfaceClass}=="08" ATTRS{bInterfaceSubClass}=="06" ATTRS{bInterfaceProtocol}=="50" ATTRS{bNumEndpoints}=="02" ATTRS{supports_autosuspend}=="1" ATTRS{bAlternateSetting}==" 0" ATTRS{bInterfaceNumber}=="00" looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8': KERNELS=="1-8" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{bDeviceSubClass}=="00" ATTRS{bDeviceProtocol}=="00" ATTRS{devpath}=="8" ATTRS{idVendor}=="1516" ATTRS{speed}=="480" ATTRS{bNumInterfaces}==" 1" ATTRS{bConfigurationValue}=="1" ATTRS{bMaxPacketSize0}=="64" ATTRS{busnum}=="1" ATTRS{devnum}=="17" ATTRS{configuration}=="" ATTRS{bMaxPower}=="500mA" ATTRS{authorized}=="1" ATTRS{bmAttributes}=="80" ATTRS{bNumConfigurations}=="1" ATTRS{maxchild}=="0" ATTRS{bcdDevice}=="0100" ATTRS{avoid_reset_quirk}=="0" ATTRS{quirks}=="0x0" ATTRS{version}==" 2.00" ATTRS{urbnum}=="1001" ATTRS{ltm_capable}=="no" ATTRS{manufacturer}=="SKYMEDI" ATTRS{removable}=="removable" ATTRS{idProduct}=="1226" ATTRS{bDeviceClass}=="00" ATTRS{product}=="USB Drive" looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1': KERNELS=="usb1" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{bDeviceSubClass}=="00" ATTRS{bDeviceProtocol}=="01" ATTRS{devpath}=="0" ATTRS{idVendor}=="1d6b" ATTRS{speed}=="480" ATTRS{bNumInterfaces}==" 1" ATTRS{bConfigurationValue}=="1" ATTRS{bMaxPacketSize0}=="64" ATTRS{authorized_default}=="1" ATTRS{busnum}=="1" ATTRS{devnum}=="1" ATTRS{configuration}=="" ATTRS{bMaxPower}=="0mA" ATTRS{authorized}=="1" ATTRS{bmAttributes}=="e0" ATTRS{bNumConfigurations}=="1" ATTRS{maxchild}=="15" ATTRS{bcdDevice}=="0402" ATTRS{avoid_reset_quirk}=="0" ATTRS{quirks}=="0x0" ATTRS{serial}=="0000:00:14.0" ATTRS{version}==" 2.00" ATTRS{urbnum}=="348" ATTRS{ltm_capable}=="no" ATTRS{manufacturer}=="Linux 4.2.0-42-generic xhci-hcd" ATTRS{removable}=="unknown" ATTRS{idProduct}=="0002" ATTRS{bDeviceClass}=="09" ATTRS{product}=="xHCI Host Controller" looking at parent device '/devices/pci0000:00/0000:00:14.0': KERNELS=="0000:00:14.0" SUBSYSTEMS=="pci" DRIVERS=="xhci_hcd" ATTRS{irq}=="27" ATTRS{subsystem_vendor}=="0x1028" ATTRS{broken_parity_status}=="0" ATTRS{class}=="0x0c0330" ATTRS{driver_override}=="(null)" ATTRS{consistent_dma_mask_bits}=="64" ATTRS{dma_mask_bits}=="64" ATTRS{local_cpus}=="f" ATTRS{device}=="0x8c31" ATTRS{enable}=="1" ATTRS{msi_bus}=="1" ATTRS{local_cpulist}=="0-3" ATTRS{vendor}=="0x8086" ATTRS{subsystem_device}=="0x05a5" ATTRS{numa_node}=="-1" ATTRS{d3cold_allowed}=="1" looking at parent device '/devices/pci0000:00': KERNELS=="pci0000:00" SUBSYSTEMS=="" DRIVERS==""

從中能夠獲得KERNEL,SUBSYSTEM等信息,這樣就能夠利用這些屬性值去匹配。其實咱們能夠從那裏看到「looking at parent device」是一層往一層打印出該設備的信息。

這樣咱們就能夠KERNEL=="sdb4", SUBSYSTEM=="block"去匹配,好比寫udev規則以下:

KERNEL=="sdb4", SUBSYSTEM=="block", RUN+="/etc/udev/rules.d/mount.sh"

4. 字符串替換和匹配

4.1 字符串替換

$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:臨時設備名

%%:符號 % 自己

$$:符號 $ 自己

例子以下:

KERNEL=="fb[0-9]*", NAME="fb/%n", SYMLINK+="%k"

規則意思是:匹配一個內核命名爲fb[0-9]的設備,而後給它命名爲fb/%n,符號連接爲%k

好比有一個fb3設備,匹配成功後那麼久有一個名爲fb/3,符號連接爲fb3

4.2 字符串匹配

不只有字符串精確匹配, udev也容許你使用shell風格的模式匹配. 支持的3種模式爲:
* - 匹配任何字符, 匹配0次或屢次
? - 匹配任何字符,但只匹配一次.
[] - 匹配任何單個字符, 這些字符在方括號裏面指定, 範圍是受限的.
這裏有一些例子, 注意字符串替換符的使用:
KERNEL=="fd[0-9]*", NAME="floppy/%n", SYMLINK+="%k"
KERNEL=="hiddev*", NAME="usb/%k"
第一條規則匹配全部軟盤驅動並確保設備節點放置在/dev/floppy目錄下, 也建立一個缺省名字的符號連接. 第二條規則確保hiddev設備節點放在/dev/usb目錄下面.

5. udev主要做用

  • 重命名設備節點的缺省名字爲其餘名字
  • 經過建立符號連接到缺省設備節點來提供一個可選的固定的設備節點名字
  • 基於程序的輸出命名設備節點
  • 改變設備節點的權限和全部權
  • 在設備節點被建立或刪除時(一般是添加設備或拔出設備時)執行一個腳本
  • 重命名網絡接口

5.1 重命名設備節點的缺省名字爲其餘名字

此時使用NAME賦值鍵,例子以下:

一個硬盤,它的設備屬性KERNEL是hdb,在/dev/目錄下是/dev/hdb,那麼咱們能夠給他重命名爲

KERNEL=="hdb", NAME="my_spare_disk"

規則意思是:匹配一個設備命名爲hdb的設備,把它從新命名爲my_spare_disk. 設備節點出如今/dev/my_spare_disk

執行如下命令:ls /dev/my_spare_disk -l

/dev/my_spare_disk ---> /dev/hdb產生一個符號連接指向/dev/hdb

注意:僅僅第一行的NAME描述是有效的,後面的均忽略。即udev按順序解析udev規則文件時,第一個NAME賦值鍵的名字有用,假如後面對同一個設備還有NAME賦值鍵,那麼那個賦值的名稱將被忽略。若是你想使用使用兩個以上的名字來訪問一個設備的話,能夠考慮SYMLINK鍵

若是你想你命名的名字獲得實現,你必須把你的規則文件命名順序在前面。

5.2 經過建立符號連接到缺省設備節點來提供一個可選的固定的設備節點名字

例子以下:

KERNEL=="hdb", DRIVER=="ide-disk", SYMLINK+="sparedisk"

規則意思是:匹配一個內核命名爲hdb以及驅動爲ide-disk的設備,命名設備節點爲缺省名字並建立一個指向它的sparedisk符號連接,設備節點出如今/dev/sparedisk

注意:符號連接能夠是多個,這些符號連接都指向/dev/hdb

5.3 基於程序的輸出命名設備節點

某些狀況下你可能要求比udev標準規則提供的更多彈性, 這種狀況下你能夠請求udev運行一個程序並運用程序的標準輸出來提供設備命名.

要使用這個功能,你只需簡單的在PROGRAM賦值中指定要運行程序(以及任何闡述)的完整路徑, 而後在NAME/SYMLINK賦值中使用一些%c替換.

例子以下:

引用一個位於/bin/device_namer的虛構程序. device_namer帶一個表示內核名字的命令行參數, 基於內核名device_namer作一些變化而後輸出

KERNEL=="hda", PROGRAM="/bin/device_namer %k", SYMLINK+="%c"

規則意思是:匹配一個內核命名爲hdb的設備,而後運行一個/bin/device_name程序,這個程序須要帶一個表示內核名字的命令行參數即%k。而後這個程序運行的結果(即輸出)把它賦值給SYMLINK,這樣就能夠知足要求(使用外部程序來命名設備)

4.4 改變設備節點的權限和全部權

udev容許你在規則中使用另外的賦值來控制每一個設備的全部權和權限屬性.

例子以下:

KERNEL=="fb[0-9]*", NAME="fb/%n", SYMLINK+="%k", GROUP="video", MODE="0666"

規則意思是:匹配一個內核命名爲fb[0-9]的設備,而後給它命名爲fb/%n,符號連接爲%k, 屬於video組, 權限爲0666

好比有一個fb3設備,匹配成功後那麼久有一個名爲fb/3,符號連接爲fb3,屬組爲video,權限爲0666

5.5 在設備節點被建立或刪除時(一般是添加設備或拔出設備時)執行一個腳本

特別針對熱插拔的設備,目的是爲了在設備鏈接或者斷開時運行一個特定程序. 例如, 你可能想在你的數碼相機連到系統時執行一個腳原本自動下載相機裏面的全部照片.

例子以下:

KERNEL=="sdb", ACTION=="add", RUN+="/usr/bin/my_program"

規則意思是:匹配一個內核名爲sdb的設備,當插入時,執行程序/usr/bin/my_program

5.6 重命名網絡接口

在規則中簡單的匹配網卡MAC地址是有意義的,由於它們是惟一的. 
# udevadm info -a -p /sys/class/net/eth0
looking at class device '/sys/class/net/eth0':
KERNEL=="eth0"
ATTR{address}=="00:52:8b:d5:04:48"
規則以下:
KERNEL=="eth*", ATTR{address}=="00:52:8b:d5:04:48", NAME="lan"

這樣就重命名了eth*爲lan

6. 編寫udev規則實例

USB打印機
我啓動個人打印機, 它就被賦予了一個設備節點/dev/lp0. 我對這樣的單調的名字不滿意並打算使用udevinfo幫我寫一個規則來提供一個可選名字:
# udevinfo -a -p $(udevinfo -q path -n /dev/lp0)
looking at device '/class/usb/lp0':
KERNEL=="lp0"
SUBSYSTEM=="usb"
DRIVER==""
ATTR{dev}=="180:0"

looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb1/1-1':
SUBSYSTEMS=="usb"
ATTRS{manufacturer}=="EPSON"
ATTRS{product}=="USB Printer"
ATTRS{serial}=="L72010011070626380"
個人規則變成了這樣:
SUBSYSTEM=="usb", ATTRS{serial}=="L72010011070626380", SYMLINK+="epson_680"

7. 難點解析

7.1 ATTR

ATTR{value}  sysfs設備屬性值,能夠爲任意值,用於匹配

能夠利用udev的命令:好比udevadm info -a -p $(udevadm info -q path -n /dev/sda4). 其中udevadm info -q path -n /dev/sda4返回sysfs中的設備路徑;udevadm info -a -p $(設備路徑),這將查詢這個設備路徑,把結果信息輸出來:以下:

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18/target18:0:0/18:0:0:0/block/sdb/sdb4':
    KERNEL=="sdb4"
    SUBSYSTEM=="block"
    DRIVER==""
    ATTR{ro}=="0"
    ATTR{size}=="15177600"
    ATTR{stat}=="     202      382     1562      212        0        0        0        0        0      164      212"
    ATTR{partition}=="4"
    ATTR{start}=="14880"
    ATTR{discard_alignment}=="0"
    ATTR{alignment_offset}=="0"
    ATTR{inflight}=="       0        0"

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18/target18:0:0/18:0:0:0/block/sdb':
    KERNELS=="sdb"
    SUBSYSTEMS=="block"
    DRIVERS==""
    ATTRS{ro}=="0"
    ATTRS{size}=="15204352"
    ATTRS{stat}=="     295      382     2306     1208        0        0        0        0        0     1156     1204"
    ATTRS{range}=="16"
    ATTRS{discard_alignment}=="0"
    ATTRS{events}=="media_change"
    ATTRS{ext_range}=="256"
    ATTRS{events_poll_msecs}=="2000"
    ATTRS{alignment_offset}=="0"
    ATTRS{inflight}=="       0        0"
    ATTRS{removable}=="1"
    ATTRS{capability}=="51"
    ATTRS{events_async}==""

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18/target18:0:0/18:0:0:0':
    KERNELS=="18:0:0:0"
    SUBSYSTEMS=="scsi"
    DRIVERS=="sd"
    ATTRS{rev}=="1.00"
    ATTRS{type}=="0"
    ATTRS{scsi_level}=="3"
    ATTRS{model}=="                "
    ATTRS{state}=="running"
    ATTRS{queue_type}=="none"
    ATTRS{iodone_cnt}=="0x152"
    ATTRS{iorequest_cnt}=="0x152"
    ATTRS{device_busy}=="0"
    ATTRS{evt_capacity_change_reported}=="0"
    ATTRS{timeout}=="30"
    ATTRS{evt_media_change}=="0"
    ATTRS{max_sectors}=="240"
    ATTRS{ioerr_cnt}=="0x1"
    ATTRS{queue_depth}=="1"
    ATTRS{vendor}=="        "
    ATTRS{evt_soft_threshold_reached}=="0"
    ATTRS{device_blocked}=="0"
    ATTRS{evt_mode_parameter_change_reported}=="0"
    ATTRS{evt_lun_change_reported}=="0"
    ATTRS{evt_inquiry_change_reported}=="0"
    ATTRS{iocounterbits}=="32"
    ATTRS{eh_timeout}=="10"

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18/target18:0:0':
    KERNELS=="target18:0:0"
    SUBSYSTEMS=="scsi"
    DRIVERS==""

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0/host18':
    KERNELS=="host18"
    SUBSYSTEMS=="scsi"
    DRIVERS==""

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8/1-8:1.0':
    KERNELS=="1-8:1.0"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb-storage"
    ATTRS{bInterfaceClass}=="08"
    ATTRS{bInterfaceSubClass}=="06"
    ATTRS{bInterfaceProtocol}=="50"
    ATTRS{bNumEndpoints}=="02"
    ATTRS{supports_autosuspend}=="1"
    ATTRS{bAlternateSetting}==" 0"
    ATTRS{bInterfaceNumber}=="00"

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1/1-8':
    KERNELS=="1-8"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{devpath}=="8"
    ATTRS{idVendor}=="1516"
    ATTRS{speed}=="480"
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{busnum}=="1"
    ATTRS{devnum}=="17"
    ATTRS{configuration}==""
    ATTRS{bMaxPower}=="500mA"
    ATTRS{authorized}=="1"
    ATTRS{bmAttributes}=="80"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{maxchild}=="0"
    ATTRS{bcdDevice}=="0100"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{quirks}=="0x0"
    ATTRS{version}==" 2.00"
    ATTRS{urbnum}=="1001"
    ATTRS{ltm_capable}=="no"
    ATTRS{manufacturer}=="SKYMEDI"
    ATTRS{removable}=="removable"
    ATTRS{idProduct}=="1226"
    ATTRS{bDeviceClass}=="00"
    ATTRS{product}=="USB Drive"

  looking at parent device '/devices/pci0000:00/0000:00:14.0/usb1':
    KERNELS=="usb1"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{bDeviceProtocol}=="01"
    ATTRS{devpath}=="0"
    ATTRS{idVendor}=="1d6b"
    ATTRS{speed}=="480"
    ATTRS{bNumInterfaces}==" 1"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{authorized_default}=="1"
    ATTRS{busnum}=="1"
    ATTRS{devnum}=="1"
    ATTRS{configuration}==""
    ATTRS{bMaxPower}=="0mA"
    ATTRS{authorized}=="1"
    ATTRS{bmAttributes}=="e0"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{maxchild}=="15"
    ATTRS{bcdDevice}=="0402"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{quirks}=="0x0"
    ATTRS{serial}=="0000:00:14.0"
    ATTRS{version}==" 2.00"
    ATTRS{urbnum}=="348"
    ATTRS{ltm_capable}=="no"
    ATTRS{manufacturer}=="Linux 4.2.0-42-generic xhci-hcd"
    ATTRS{removable}=="unknown"
    ATTRS{idProduct}=="0002"
    ATTRS{bDeviceClass}=="09"
    ATTRS{product}=="xHCI Host Controller"

  looking at parent device '/devices/pci0000:00/0000:00:14.0':
    KERNELS=="0000:00:14.0"
    SUBSYSTEMS=="pci"
    DRIVERS=="xhci_hcd"
    ATTRS{irq}=="27"
    ATTRS{subsystem_vendor}=="0x1028"
    ATTRS{broken_parity_status}=="0"
    ATTRS{class}=="0x0c0330"
    ATTRS{driver_override}=="(null)"
    ATTRS{consistent_dma_mask_bits}=="64"
    ATTRS{dma_mask_bits}=="64"
    ATTRS{local_cpus}=="f"
    ATTRS{device}=="0x8c31"
    ATTRS{enable}=="1"
    ATTRS{msi_bus}=="1"
    ATTRS{local_cpulist}=="0-3"
    ATTRS{vendor}=="0x8086"
    ATTRS{subsystem_device}=="0x05a5"
    ATTRS{numa_node}=="-1"
    ATTRS{d3cold_allowed}=="1"

  looking at parent device '/devices/pci0000:00':
    KERNELS=="pci0000:00"
    SUBSYSTEMS==""
    DRIVERS==""

查找上面信息,就能夠利用ATTR任何值來匹配,好比ATTR{size}=="15177600", ATTR{start}=="14880"等等來匹配均可以

7.2 ENV

ENV{ key}  環境變量,能夠表示任意

用於賦值和匹配均可以

例子以下:

先賦值一個ENV,而後再用它來匹配

賦值一個ENV:

KERNEL=="sda4", ENV{test_value}="value", SYMLINK+="udisk4"

這樣就賦值了一個ENV{test_value}爲value,那麼能夠在別的設備匹配時引用那個ENV進行匹配

匹配ENV:

SUBSYSTEM=="block", ENV{test_value}=="value", NAME="hda"

匹配ENV{test_value},而且匹配成功,因此會進行命名

若SUBSYSTEM=="block", ENV{test_value}=="test", NAME="hda"

匹配ENV{test_value},但匹配失敗,因此上面這條規則不執行

相關文章
相關標籤/搜索