1 Linux 文件系統(tǒng)
1.1 Linux 文件系統(tǒng)與設(shè)備驅(qū)動關(guān)系
下圖表明了 Linux 中虛擬文件系統(tǒng)概荷、磁盤/Flash文件系統(tǒng)以及一般的設(shè)備文件與設(shè)備驅(qū)動程序之間的關(guān)系。
應(yīng)用程序與 VFS 之間的接口是系統(tǒng)調(diào)用,而 VFS 與文件系統(tǒng)以及設(shè)備文件之間的接口是 file_operations 結(jié)構(gòu)體成員函數(shù)糯俗,此結(jié)構(gòu)體包含了對文件進(jìn)行打開珍手、關(guān)閉真竖、讀寫荐开、控制的一系列成員函數(shù)。由于字符設(shè)備的上層沒有類似于磁盤的 ext2 等文件系統(tǒng)尖阔,所以字符設(shè)備的 file_operations 成員函數(shù)就直接由設(shè)備驅(qū)動提供了贮缅。
由此可以看出對于塊設(shè)備的訪問有兩種方法:
- 一種方法是不通過文件系統(tǒng)直接訪問裸設(shè)備,Linux 內(nèi)核實現(xiàn)了統(tǒng)一的 def_blk_fops這一個 file_operations介却,他的源碼位于 fs/block_dev.c谴供。所以當(dāng)運行類似于 "dd if=/dev/sd1 of=sdb1.img" 的命令把整個 /dev/sdb1裸分區(qū)復(fù)制到 sdb1.img 的時候,內(nèi)核走的是 def_blk_fops這個 file_operations齿坷;
- 另一種方法是通過文件系統(tǒng)來訪問設(shè)備桂肌,file_operations 的實現(xiàn)則位于文件系統(tǒng)內(nèi),文件系統(tǒng)會把正對文件的讀寫轉(zhuǎn)換為針對設(shè)備原始扇區(qū)的讀寫永淌。ext2崎场、fat、btrfs 等文件系統(tǒng)中會實現(xiàn)針對 VFS 的file_operations 成員函數(shù)遂蛀,設(shè)備驅(qū)動層將看不到 file_operations的存在谭跨。
1.2 設(shè)備驅(qū)動程序
在設(shè)備驅(qū)動程序設(shè)計中,一般而言李滴,會關(guān)心 file 和 inode 這兩個結(jié)構(gòu)體螃宙。
file 結(jié)構(gòu)體
file 結(jié)構(gòu)體代表一個打開的文件,系統(tǒng)中每個打開的文件在內(nèi)核空間都有一個與之關(guān)聯(lián)的 struct file所坯。它由內(nèi)核在打開文件時創(chuàng)建谆扎,并傳遞給在文件上進(jìn)行任何操作的函數(shù)。在文件的所有實例都關(guān)閉后芹助,內(nèi)核釋放此結(jié)構(gòu)體的數(shù)據(jù)堂湖。結(jié)構(gòu)體原型如下:
struct file {
union{
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
#define f_dentry f_path.dentry;
struct inode *f_inode; //cached value
const struct file_operations *f_op; //和文件關(guān)聯(lián)的操作
//Protects f_ep_links, flags.
//must not be taken from IRQ context
spinlock_t f_lock;
atomic_long_t f_count;
unsigned int f_flags; //文件標(biāo)志,如 O_RDONLY O_NONBLOCK OSYNC
fmode_t f_mode; //文件讀寫模式 FMODE_READ FMODE_WRITE
struct fown_struct f_owner;
const struct cred *fcred;
struct file_ra_state f_ra;
u64 v_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
//need for tty driver, and maybe others
void *private_data; //文件私有數(shù)據(jù)
#ifdef CONFIG_EPOLL
//used tye fs/eventpoll.c to link all the hooks to this file
struct list_head f_ep_links;
struct list_head f_tfile_llink;
#endif
struct address_space *f_mapping;
} __attribute__((aligned(4))); //lest something weird decides that 2 is OK
文件讀/寫模式 mode状土、標(biāo)志 f_flags 都是設(shè)備驅(qū)動關(guān)心的內(nèi)容无蜂,而私有數(shù)據(jù)指針 private_data 在設(shè)備驅(qū)動中被廣泛應(yīng)用,大多被指向設(shè)備驅(qū)動自定義以用于描述設(shè)備的結(jié)構(gòu)體声诸。
inode 結(jié)構(gòu)體
VFS node 包含文件的訪問權(quán)限、屬性退盯、組彼乌、大小泻肯、生成時間、訪問時間慰照、最后修改時間等信息灶挟。它是 Linux管理文件系統(tǒng)的最基本的單位,也是文件系統(tǒng)連接任何子目錄毒租、文件的橋梁稚铣。其定義如下:
struct inode{
...
umode_t i_mode; //inode 的權(quán)限
uid_t i_uid; //inode 的擁有者id
gid_t i_gid; //inode 所屬的群組 id
dev_t i_rdve; //若是設(shè)備文件,此字段將記錄設(shè)備的設(shè)備號
loff_t i_size; //inode 所代表文件的大小
unsigned int i_blkbits;
blkcnt_t i_blocks; //inode 所使用的block數(shù)墅垮, 一個block為 512 個字節(jié)
union{
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev; //若是塊設(shè)備惕医,為其對應(yīng)的block_device結(jié)構(gòu)體指針
struct cdev *i_cdev; //若是字符設(shè)備,為其對應(yīng)的 cdev 的結(jié)構(gòu)體指針
};
...
};
對于表示設(shè)備文件的 inode 結(jié)構(gòu)算色,i_rdev 字段包含設(shè)備編號抬伺。linux 內(nèi)核設(shè)備編號分為主設(shè)備編號和次設(shè)備編號,前者為 dev_t 的高 12 位灾梦,后者位 dev_t 的低 20 位峡钓。下列操作用于從設(shè)備號中獲取主設(shè)備號和次設(shè)備號:
unsigned int iminor(struct inode *inode);
unsigned int imajor(struct inode *inode);
查看 /proc/devices 文件可以獲知系統(tǒng)中注冊的設(shè)備,第一列位主設(shè)備號若河,第 2 列為設(shè)備名稱能岩。如:
Character devices:
1 mem
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
5 ttyprintk
6 lp
7 vcs
10 misc
Block devices:
259 blkext
7 loop
8 sd
9 md
128 sd
134 sd
135 sd
253 device-mapper
254 mdp
查看/dev 目錄可以獲知系統(tǒng)中包含的設(shè)備文件,日期的前兩列分別給出了主設(shè)備號和次設(shè)備號萧福。如:
crw--w---- 1 root tty 4, 29 4月 9 16:39 tty29
crw--w---- 1 root tty 4, 3 4月 9 16:39 tty3
crw--w---- 1 root tty 4, 30 4月 9 16:39 tty30
crw--w---- 1 root tty 4, 31 4月 9 16:39 tty31
crw--w---- 1 root tty 4, 32 4月 9 16:39 tty32
主設(shè)備號是與驅(qū)動對應(yīng)的概念拉鹃,同一類設(shè)備使用相同的主設(shè)備號。
同一驅(qū)動可支持多個同類設(shè)備统锤,因此次設(shè)備號就是用來表示同一驅(qū)動下不同設(shè)備毛俏。
2 devfs
devfs(設(shè)備文件系統(tǒng))是由 linux2.4 內(nèi)核引入,它使得設(shè)備驅(qū)動程序能自主管理自己的設(shè)備文件饲窿。具體來說煌寇,devfs具有如下優(yōu)點:
- 可以通過程序在設(shè)備初始化時在 /dev 目錄下創(chuàng)建設(shè)備文件,卸載設(shè)備時將它刪除逾雄。
- 設(shè)備驅(qū)動程序可以指定設(shè)備名阀溶、所有者和權(quán)限位,用戶空間程序仍可以修改所有者和權(quán)限位鸦泳。
- 不再需要為設(shè)備驅(qū)動程序分配主設(shè)備號以及處理次設(shè)備號银锻,在程序中可以直接給 register_chr_dev() 傳遞 0 主設(shè)備號以獲得可用的主設(shè)備號,并在 devfs_register() 中指定次設(shè)備號做鹰。
驅(qū)動程序應(yīng)調(diào)用下面這些函數(shù)來進(jìn)行設(shè)備文件的創(chuàng)建和撤銷工作击纬。
//創(chuàng)建設(shè)備目錄
devfs_handle_t devfs_mk_dir(devfs_handle_t dir, const char *name, void *info);
//創(chuàng)建設(shè)備文件
devfs_handle_t devfs_register(devfs_handle_t dir, const char *name, unsigned int flags, \
unsigned int major, unsigned int minor, umode_t mode, void *ops, void *info);
//撤銷設(shè)備文件
void devfs_unregister(devfs_handle_t de);
在 Linux 2.4 的設(shè)備編程中,分別在模塊加載钾麸、卸載函數(shù)中創(chuàng)建和撤銷設(shè)備文件是被普遍采用并值得大力推薦的好方法更振。以下為示例代碼:
static devfs_handle_t devfs_handle;
static int __init xxx_init(void)
{
int ret;
int i;
//在內(nèi)核中注冊設(shè)
ret = register_chr_dev(XXX_MAJOR, DEVICE_NAME, &xxx_fops);
if(ret < 0)
{
printk(DEVICE_NAME " can't register major number\n");
return ret;
}
//創(chuàng)建設(shè)備文件
devfs_handle = devfs_register(NULL, DEVICE_NAME, DEVFS_FL_DEFAULT,
XXX_MAJOR, 0, S_IFCHR | S_IWUSR | S_IRUSR, &xxx_fops, NULL);
...
printk(DEVICE_NAME " initialized.\n");
return 0;
}
static void __exit xxx_exit(void)
{
devfs_unregister(devfs_handle); //撤銷設(shè)備文件
unregister_chrdev(XXX_MAJOR, DEVICE_NAME); //注銷設(shè)備
}
module_init(xxx_init);
moudule_exit(xxx_exit);
3 udev 用戶空間設(shè)備管理
3.1 udev 和 devfs 的區(qū)別
在 Linux 2.6 內(nèi)核中炕桨,devfs 被認(rèn)為是過時的方法,并最終被淘汰了肯腕,udev 取代了它献宫。Linux VFS 內(nèi)核維護(hù)者 Al viro 指出了幾點 udev 取代 devfs 的原因:
- devfs 所做的工作被確信可以在用戶態(tài)來完成。
- devfs 被加入內(nèi)核之時实撒,大家期望它的質(zhì)量可以迎頭趕上姊途。
- 發(fā)現(xiàn) devfs 有一些可修復(fù)和無法修復(fù)的bug。
- 對于無法修復(fù)的 bug 在相當(dāng)長的一段時間內(nèi)沒有改觀知态。
- devfs 的維護(hù)者和作者對齊感到失望并停止對其維護(hù)捷兰。
Linux 設(shè)計中強調(diào)的一個基本觀點是機(jī)制和策略的分離。機(jī)制是穩(wěn)定的肴甸,而策略是靈活的导坟,機(jī)制應(yīng)該在內(nèi)核完成崩侠,而策略最好在用戶空間實現(xiàn)驻粟。
3.2 熱插拔事件
udev 完全在用戶態(tài)工作役电,利用設(shè)備加入或移除時內(nèi)核所發(fā)送的熱插拔事件(Hotplug Event)來工作。在熱插拔時庶柿,設(shè)備的詳細(xì)信息會由內(nèi)核通過 netlink 套接字發(fā)送出來村怪,發(fā)出來的事情叫 uevent。udev 的設(shè)備命名策略浮庐、權(quán)限控制和事件處理都是在用戶態(tài)下完成的甚负,它利用從內(nèi)核收到的信息進(jìn)行創(chuàng)建設(shè)備文件節(jié)點等工作。以下是一個獲取熱插拔事件 uevent的范例:
#include <linux/netlink.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <poll.h>
#include <stdio.h>
static void die(char *s)
{
write(2, s, strlen(s));
exit(1);
}
int main(int argc, char* argv[])
{
struct sockaddr_nl nls;
struct pollfd pfd;
char buf[512];
//open hotplug event netlink socket
memset(&nls, 0, sizeof(struct sockaddr_nl));
nls.nl_family = AF_NETLINK;
nls.nl_pid = getpid();
nls.nl_groups = -1;
pfd.events = POLLIN;
pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
if(-1 == pfd.fd)
die("Not root\n");
//Listen to netlink socket
if(bind(pfd.fd, (void *)&nls, sizeof(struct sockaddr_nl)))
die("Bind failed.\n");
while(-1 != poll(&pfd, 1, -1))
{
int i, len;
len = recv(pfd.fd, buf, sizeof(buf), MSG_DONTWAIT);
if(-1 == len)
die("recv\n");
//print the data to stdout
i = 0;
while(i < len){
printf("%s\n", buf + i);
i += strlen(buf + i) + 1;
}
}
die("poll\n");
return 0;
}
編譯上述代碼审残,在虛擬機(jī)上運行梭域,插拔光盤等設(shè)備,會打印熱插拔事件搅轿。
udev 就是采用這種方式接收 netlink 消息病涨,并根據(jù)它的內(nèi)容和用戶設(shè)置給 udev 的規(guī)則做匹配來進(jìn)行工作。
對于冷插拔的設(shè)備來說璧坟,Linux 內(nèi)核提供了 sysfs 下面的一個 uevent 節(jié)點既穆,可以往該節(jié)點寫一個 “add”,導(dǎo)致內(nèi)核重新發(fā)送 netlink雀鹃,之后 udev 就可以收到冷插拔的 netlink 消息了幻工。上述程序會dump 出如下信息:
ACTION=add
DEVPATH=/module/psmouse
SEQNUM=1682
SUBSYSTEM=module
UDEV_LOG=3
USEC_INITIALIZED=220903546792
devfs 與 udev 的一個顯著區(qū)別:
- 采用 devfs,當(dāng)一個并不存在的 /dev 節(jié)點被打開的時候黎茎,devfs 能自動加載對應(yīng)的驅(qū)動囊颅,而udev不這么做。
- udev 任務(wù)驅(qū)動應(yīng)該在設(shè)備被發(fā)現(xiàn)時,而不是設(shè)備被訪問時加載踢代。故 udev 獲取設(shè)備發(fā)現(xiàn)時的熱插拔事件先鱼,在設(shè)備發(fā)現(xiàn)時加載設(shè)備的驅(qū)動。
3.3 sysfs 文件系統(tǒng)與 Linux 設(shè)備模型
Linux 2.6 以后內(nèi)核引入了 sysfs 文件系統(tǒng)奸鬓,與 proc 類似。sysfs 把連接在系統(tǒng)上的設(shè)備和總線組織成為一個分級的文件掸读,它們可以由用戶空間存取串远,向用戶空間導(dǎo)出內(nèi)核數(shù)據(jù)結(jié)構(gòu)以及它們的屬性。sysfs 的一個目的就是展示設(shè)備驅(qū)動模型中各組件的層次關(guān)系儿惫。其頂級目錄包含 block澡罚、bus、devices肾请、class留搔、fs、kernel铛铁、power 和 firmware 等隔显。
block 目錄包含所有的塊設(shè)備;devices 目錄包含系統(tǒng)所有的設(shè)備饵逐,并根據(jù)設(shè)備掛接的總線類型組織稱層次結(jié)構(gòu)括眠;bus 目錄包含系統(tǒng)中所有的總線類型;class 目錄包含系統(tǒng)中的設(shè)備類型(如網(wǎng)卡設(shè)備倍权、聲卡設(shè)備掷豺、輸入設(shè)備等)。
在 /sys/bus 的 pci 等子目錄下薄声,又會再分出 drivers 和 devices 目錄中的文件是對 /sys/devices 目錄中文件的符號鏈接当船。同樣地,/sys/class 目錄下也包含許多對 /sys/devices 下文件的鏈接默辨。Linux 2.6 以后內(nèi)核的設(shè)備模型德频。
Linux 內(nèi)核中,分別使用 bus_type廓奕、device_driver 和 device 來描述總線抱婉、驅(qū)動和設(shè)備,這 3 個結(jié)構(gòu)體定義于 include/linux/device.h 頭文件中桌粉,其定義如代碼清單:
struct bus_type{
const char *name;
const char *dev_name;
struct device *dev_root;
...
int (*match)(struct device *dev, struct device_driver *drv);
...
};
struct device_driver{
const char *name;
struct bus_type *bus;
...
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
...
};
struct device{
...
struct bus_type *bus; //type of bus device is on
struct device_driver *driver; //which driver has allocated this device
...
};
device_driver 和 device 分別表示驅(qū)動和設(shè)備蒸绩,而這兩者都必須依附于一種總線,因此都包含 struct bus_type 指針铃肯。在 Linux 內(nèi)核中患亿,設(shè)備和驅(qū)動都是分開注冊的,注冊一個設(shè)備時并不需要驅(qū)動已經(jīng)存在,而 1個驅(qū)動被注冊的時候步藕,也不需要對應(yīng)設(shè)備已經(jīng)存在惦界,設(shè)備和驅(qū)動各自涌向內(nèi)核,并尋找自己的另一半咙冗,而正是 bus_type 的match() 成員函數(shù)將兩者捆綁在一起沾歪,一旦配對成功,對應(yīng)總線的xxx_driver 的probe() 就被執(zhí)行(xxx是總線名雾消,如platform灾搏、pci、i2c立润、spi狂窑、usb等)。
總線桑腮、驅(qū)動和設(shè)備最終都會落實為 sysfs 中的 1 個目錄泉哈,因為進(jìn)一步追蹤代碼會發(fā)現(xiàn),它們實際上可以認(rèn)為是 kobject 的派生類破讨,kobject 可以看作是所有總線丛晦、設(shè)備和驅(qū)動的抽象基類,1 個 kobject 對應(yīng) sysfs 中的一個目錄提陶。
總線采呐、設(shè)備和驅(qū)動中的各個 attribute 則直接落實為 sysfs 中的 1 個文件,attribute 會伴隨著 show() 和 store() 這兩個函數(shù)搁骑,分別用于讀寫 attribute 對應(yīng)的 sysfs 文件斧吐,代碼給出了 attribute、bus_attribute仲器、driver_attribute 和 device_attribute 這幾個結(jié)構(gòu)體的定義煤率。
struct attribute{
const char *name;
umode_t mode;
#ifdef CONFiG_DEBUG_LOCK_ALLOC
bool ignore_lockdep:1;
struct lock_class_key *key;
struct lock_class_key *key;
#endif
};
struct bus_attribute{
struct attribute sttr;
ssize_t (*show)(struct bus_type *bus, char *buf);
ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
};
struct driver_attribute{
struct attribute attr;
ssize_t (*show)(struct device_driver *driver, char *buf);
ssize_t (*store)(struct device_driver *driver, const char *buf, size_t count);
};
struct device_attribute{
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
事實上,sysfs 中的目錄來源于 bus_type乏冀、device_driver蝶糯、device,而目錄中的文件則來源于 attribute辆沦。
比如昼捍,我們在 drivers/base/bus.c文件中可以找到這樣的代碼:
static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);
static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,
show_drivers_autoprobe, store_drivers_autoprobe);
static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);
3.4 udev 的組成
udev 在用戶空間執(zhí)行,動態(tài)建立/刪除設(shè)備文件肢扯,允許每個人都不用關(guān)心主/次設(shè)備號而提供LSB(Linux 標(biāo)準(zhǔn)規(guī)范妒茬, Linux Standard Base)名稱,并可以根據(jù)需要固定名稱蔚晨。
udev 的工作過程如下:
- 當(dāng)內(nèi)核檢測到系統(tǒng)中出現(xiàn)了新設(shè)備后乍钻,內(nèi)核會通過 netlink 套接字發(fā)送uevent。
- udev 獲取內(nèi)核發(fā)送的信息,進(jìn)行規(guī)則匹配银择。匹配的事物包括 SUBSYSTEM多糠、ACTION、attribute浩考、內(nèi)核提供的名稱(通過 KERNEL ==)夹孔,以及其它環(huán)境變量。
以下是利用命令行工具 udevadm monitor --kernel --property --udev
捕獲到一個虛擬機(jī) Ubuntu 16.4.1 退出虛擬 ISO 文件的打游瞿酢:
KERNEL[15730.816373] change /devices/pci0000:00/0000:00:1f.1/ata2/host1/target1:0:0/1:0:0:0/block/sr0 (block)
ACTION=change
DEVNAME=/dev/sr0
DEVPATH=/devices/pci0000:00/0000:00:1f.1/ata2/host1/target1:0:0/1:0:0:0/block/sr0
DEVTYPE=disk
DISK_MEDIA_CHANGE=1
MAJOR=11
MINOR=0
SEQNUM=4324
SUBSYSTEM=block
UDEV [15730.923482] change /devices/pci0000:00/0000:00:1f.1/ata2/host1/target1:0:0/1:0:0:0/block/sr0 (block)
ACTION=change
DEVLINKS=/dev/cdrom /dev/disk/by-id/ata-VBOX_CD-ROM_VB2-01700376 /dev/dvd /dev/disk/by-path/pci-0000:00:1f.1-ata-2
DEVNAME=/dev/sr0
DEVPATH=/devices/pci0000:00/0000:00:1f.1/ata2/host1/target1:0:0/1:0:0:0/block/sr0
DEVTYPE=disk
DISK_MEDIA_CHANGE=1
ID_ATA=1
ID_BUS=ata
ID_CDROM=1
ID_CDROM_CD=1
ID_CDROM_DVD=1
ID_CDROM_MRW=1
ID_CDROM_MRW_W=1
ID_FOR_SEAT=block-pci-0000_00_1f_1-ata-2
ID_MODEL=VBOX_CD-ROM
ID_MODEL_ENC=VBOX\x20CD-ROM\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20
ID_PATH=pci-0000:00:1f.1-ata-2
ID_PATH_TAG=pci-0000_00_1f_1-ata-2
ID_REVISION=1.0
ID_SERIAL=VBOX_CD-ROM_VB2-01700376
ID_SERIAL_SHORT=VB2-01700376
ID_TYPE=cd
MAJOR=11
MINOR=0
SEQNUM=4324
SUBSYSTEM=block
SYSTEMD_READY=0
TAGS=:uaccess:systemd:seat:
USEC_INITIALIZED=1513182
可以根據(jù)以上信息析蝴,創(chuàng)建一個規(guī)則,以便每次插入的時候绿淋,為該盤創(chuàng)建一個 /dev/testISO 的符號鏈接。
#iso
SUBSYSTEM=="block", ACTION=="change", KERNEL="*sr?", ENV{ID_TYPE}=="cd", SYMLINK+="testISO"
TO-DO: 實驗未成功
3.5 udev 規(guī)則文件
udev 的規(guī)則文件以行為單位尝盼,以 “#” 開頭的行代表注釋行吞滞。其余的每一行代表一個規(guī)則。每個規(guī)則分成多個匹配部分和賦值部分盾沫。匹配部分用匹配專用的關(guān)鍵字來表示裁赠,匹配關(guān)鍵字包括:ACTION、KERNEL赴精、BUS佩捞、SUBSYSTEM、ATTR等蕾哟;賦值部分用賦值專用關(guān)鍵字一忱,賦值關(guān)鍵字包括:NAME(創(chuàng)建的設(shè)備文件文件名)、SYSLINK(符號創(chuàng)建鏈接名)谭确、OWNER(設(shè)置設(shè)備的所有者)帘营、GROUP(設(shè)置設(shè)備的組)、IMPORT(調(diào)用外部程序)逐哈、MODE(節(jié)點訪問權(quán)限)等芬迄。
由此可知,udev 可以實現(xiàn)設(shè)備的名稱或者符號鏈接的自定義昂秃,通過規(guī)則文件禀梳,匹配相應(yīng)設(shè)備,從而實現(xiàn)自定義設(shè)備名稱肠骆,而devfs 則無法做到算途。