在 Linux 使用 systemd-udevd 管理你的接入硬件
Linux 能夠出色地自動識別、加載、并公開接入的無數(shù)廠商的硬件設備。事實上,很多年以前,正是這個特性說服我,堅持讓我的雇主將整個基礎設施轉換到 Linux。痛點在于 Redmond 的某家公司(LCTT 譯注:指微軟)不能在我們的 Compaq 臺式機上加載集成網(wǎng)卡的驅動,而 Linux 可以輕松實現(xiàn)這一點。
從那以后的歲月里,Linux 的識別設備庫隨著該過程的復雜化而與日俱增,而 udev 就是解決這個問題的希望之星。udev 負責監(jiān)聽 Linux 內核發(fā)出的改變設備狀態(tài)的事件。它可能是一個新 USB 設備被插入或拔出,也可能是一個無線鼠標因浸入灑出的咖啡中而脫機。
udev 負責處理所有的狀態(tài)變更,比如指定訪問設備使用的名稱和權限。這些更改的記錄可以通過 dmesg 獲取。由于 dmesg 的輸出通常有幾千行,對結果進行過濾通常是聰明的選擇。下面的例子說明了 Linux 如何識別我的 WiFi 接口。這個例子展示了我的無線設備使用的芯片組(ath9k)、啟動過程早期階段分配的原始名稱(wlan0)、以及正在使用的又臭又長的永久名稱(wlxec086b1ef0b3):
$ dmesg | grep wlan [ 5.396874] ath9k_htc 1-3:1.0 wlxec086b1ef0b3: renamed from wlan0
在這篇文章中,我會討論為何有人想要使用這樣的名稱。在這個過程中,我會探索剖析 udev 的配置文件,然后展示如何更改 udev 的設置,包括編輯系統(tǒng)命名設備的方式。這篇文件基于我的新課程中《Linux 系統(tǒng)優(yōu)化》的一個模塊。
理解 udev 配置系統(tǒng)
使用 systemd 的機器上,udev 操作由 systemd-udevd 守護進程管理,你可以通過常規(guī)的 systemd 方式使用 systemctl status systemd-udevd 檢查 udev 守護進程的狀態(tài)。
嚴格來說,udev 的工作方式是試圖將它收到的每個系統(tǒng)事件與 /lib/udev/rules.d/ 和 /etc/udev/rules.d/ 目錄下找到的規(guī)則集進行匹配。規(guī)則文件包括匹配鍵和分配鍵,可用的匹配鍵包括 action、name 和 subsystem。這意味著如果探測到一個屬于某個子系統(tǒng)的、帶有特定名稱的設備,就會給設備指定一個預設的配置。
接著,“分配”鍵值對被拿來應用想要的配置。例如,你可以給設備分配一個新名稱、將其關聯(lián)到文件系統(tǒng)中的一個符號鏈接、或者限制為只能由特定的所有者或組訪問。這是從我的工作站摘出的一條規(guī)則:
$ cat /lib/udev/rules.d/73-usb-net-by-mac.rules # Use MAC based names for network interfaces which are directly or indirectly # on USB and have an universally administered (stable) MAC address (second bit # is 0). Don't do this when ifnames is disabled via kernel command line or # customizing/disabling 99-default.link (or previously 80-net-setup-link.rules). IMPORT{cmdline}="net.ifnames" ENV{net.ifnames}=="0", GOTO="usb_net_by_mac_end" ACTION=="add", SUBSYSTEM=="net", SUBSYSTEMS=="usb", NAME=="", \ ATTR{address}=="?[014589cd]:*", \ TEST!="/etc/udev/rules.d/80-net-setup-link.rules", \ TEST!="/etc/systemd/network/99-default.link", \ IMPORT{builtin}="net_id", NAME="$env{ID_NET_NAME_MAC}"
add 動作告訴 udev,只要新插入的設備屬于網(wǎng)絡子系統(tǒng),并且是一個 USB 設備,就執(zhí)行操作。此外,如果我理解正確的話,只有設備的 MAC 地址由特定范圍內的字符組成,并且 80-net-setup-link.rules 和 99-default.link 文件不存在時,規(guī)則才會生效。
假定所有的條件都滿足,接口 ID 會改變以匹配設備的 MAC 地址。還記得之前的 dmesg 信息顯示我的接口名稱從 wlan0 改成了討厭的 wlxec086b1ef0b3 嗎?那都是這條規(guī)則的功勞。我怎么知道?因為 ec:08:6b:1e:f0:b3 是設備的 MAC 地址(不包括冒號)。
$ ifconfig -a wlxec086b1ef0b3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.0.103 netmask 255.255.255.0 broadcast 192.168.0.255 inet6 fe80::7484:3120:c6a3:e3d1 prefixlen 64 scopeid 0x20<link> ether ec:08:6b:1e:f0:b3 txqueuelen 1000 (Ethernet) RX packets 682098 bytes 714517869 (714.5 MB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 472448 bytes 201773965 (201.7 MB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Linux 默認包含這條 udev 規(guī)則,我不需要自己寫。但是為什么費力進行這樣的命名呢——尤其是看到這樣的接口命名這么難使用后?仔細看一下包含在規(guī)則中的注釋:
對直接或間接插入在 USB 上的網(wǎng)絡接口使用基于 MAC 的名稱,并且用一個普遍提供的(穩(wěn)定的)MAC 地址(第二位是 0)。當 ifnames 通過內核命令行或 customizing/disabling 99-default.link(或之前的 80-net-setup-link.rules)被禁用時,不要這樣做。 |
注意,這個規(guī)則專為基于 USB 的網(wǎng)絡接口設計的。和 PCI 網(wǎng)絡接口卡(NIC)不同,USB 設備很可能時不時地被移除或者替換,這意味著無法保證它們的 ID 不變。某一天 ID 可能是 wlan0,第二天卻變成了 wlan3。為了避免迷惑應用程序,指定絕對 ID 給設備——就像分配給我的 USB 接口的 ID。
操作 udev 的設置
下一個示例中,我將從 VirtualBox 虛擬機里抓取以太網(wǎng)接口的 MAC 地址和當前接口 ID,然后用這些信息創(chuàng)建一個改變接口 ID 的 udev 新規(guī)則。為什么這么做?也許我打算從命令行操作設備,需要輸入那么長的名稱讓人十分煩惱。下面是工作原理。
改變接口 ID 之前,我需要關閉 Netplan 當前的網(wǎng)絡配置,促使 Linux 使用新的配置。下面是 /etc/netplan/ 目錄下我的當前網(wǎng)絡接口配置文件:
$ less /etc/netplan/50-cloud-init.yaml # This file is generated from information provided by # the datasource. Changes to it will not persist across an instance. # To disable cloud-init's network configuration capabilities, write a file # /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following: # network: {config: disabled} network: ethernets: enp0s3: addresses: [] dhcp4: true version: 2
50-cloud-init.yaml 文件包含一個非?;镜慕涌诙x,但是注釋中也包含一些禁用配置的重要信息。為此,我將移動到 /etc/cloud/cloud.cfg.d 目錄,創(chuàng)建一個名為 /etc/cloud/cloud.cfg.d 的新文件,插入 network: {config: disabled} 字符串。
盡管我只在 Ubuntu 發(fā)行版上測試了這個方法,但它應該在任何一個帶有 systemd 的 Linux(幾乎所有的 Linux 發(fā)行版都有 systemd)上都可以工作。不管你使用哪個,都可以很好地了解編寫 udev 配置文件并對其進行測試。
接下來,我需要收集一些系統(tǒng)信息。執(zhí)行 ip 命令,顯示我的以太網(wǎng)接口名為 enp0s3,MAC 地址是 08:00:27:1d:28:10。
$ ip a 2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:1d:28:10 brd ff:ff:ff:ff:ff:ff inet 192.168.0.115/24 brd 192.168.0.255 scope global dynamic enp0s3
現(xiàn)在,我要在 /etc/udev/rules.d 目錄創(chuàng)建一個名為 peristent-net.rules 的新文件。我將給文件一個以較小的數(shù)字開頭的名稱,比如 10:
$ cat /etc/udev/rules.d/10-persistent-network.rules ACTION=="add", SUBSYSTEM=="net",ATTR{address}=="08:00:27:1d:28:10",NAME="eth3"
數(shù)字越小,Linux 越早執(zhí)行文件,我想要這個文件早點執(zhí)行。文件被添加時,包含其中的代碼就會分配名稱 eth3 給網(wǎng)絡設備——只要設備的地址能夠匹配 08:00:27:1d:28:10,即我的接口的 MAC 地址 。
保存文件并重啟計算機后,我的新接口名應該就會生效。我可能需要直接登錄虛擬機,使用 dhclient 手動讓 Linux 為這個新命名的網(wǎng)絡請求一個 IP 地址。在執(zhí)行下列命令前,可能無法打開 SSH 會話:
$ sudo dhclient eth3
大功告成?,F(xiàn)在你能夠促使 udev 控制計算機按照你想要的方式指向一個網(wǎng)卡,但更重要的是,你已經(jīng)有了一些工具,可以弄清楚如何管理任何不聽話的設備。