linux下udev和mdev的使用

wmate發表於2020-08-22

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

相關文章