Linux 熱插拔(Hot Plug)處理機制系列
阿新 • • 發佈:2019-02-04
將可移動裝置連入系統時,系統的後臺中會依次發生如下事件:
核心檢測到新硬體插入,然後分別通知hotplug和udev。前者用來裝入相應的核心模組(如usb-storage),而後者用來在/dev中建立相應的裝置節點(如/dev/sda1)
udev建立了相應的裝置節點之後,會將這一訊息通知hal的守護程式(hald)。當然udev還得保證新建立的裝置節點可以被普通使用者訪問。
hotplug裝入了相應的核心模組之後,會把這一訊息通知給hald。
hald在受到hotplug和udev發出的訊息之後,認為新硬體已經正式被系統認可了。此時它會通過一系列精心編寫的規則檔案(就是傳說中的xxx-policy.fdi),把發現新硬體的訊息通過dbus傳送出去,同時還會呼叫update-fstab或fstab-sync來更新/etc/fstab,為相應的裝置節點建立適合的掛載點。
卷管理器會監聽dbus中發現新硬體的訊息。根據所插入的硬體(區分U盤和數碼相機等)不同,卷管理器會先將相應的裝置節點掛載到hald建立的掛載點上,然後再開啟不同的應用程式。
當然,如果是在CDROM中插入光碟,過程可能比較簡單。因為CDROM本身就是一個固定的硬體,無需hotplug和udev的協助:
hald會自己監視CDROM,並且將光碟托架開合的訊息通過dbus發出去。
卷管理器負責檢查CDROM中的碟片內容,進行掛載,並呼叫合適的應用程式。
要注意,hald的工作是從上游得到硬體就緒的訊息,然後將這個訊息轉發到dbus中。儘管它會呼叫程式來更新fstab,但實際上它自己並不執行掛載的工作。
下面是上面的過程中涉及的模組和工具:
hotplug
hotplug 包和核心裡的hotplug模組不是一回事,2.6核心裡的pci_hotplug.ko是一個核心模組,而hotplug包是用來處理核心產生的hotplug事件。這個軟體包還在引導時檢測現存的硬體並在執行的核心中載入相關模組。
不但有熱插拔,還有冷插拔(cold pluging)。熱插拔在核心啟動之後發生,而“cold pluging”發生在核心啟動的過程中。
/etc/hotplug/*.rc 這些指令碼用於冷插拔(檢測和啟用在系統啟動時已經存在的硬體)。它們被 hotplug 初始化指令碼呼叫。*.rc 指令碼會嘗試恢復系統引導時丟失的熱插拔事件,舉例來說,核心沒有掛載根檔案系統。
/etc/hotplug/*.agent這些指令碼將被 hotplug
呼叫以響應核心產生的各種不同的熱插拔事件,導致插入相應的核心模組和呼叫使用者預定義的指令碼。
/sbin/hotplug核心預設情況下將在核心態的某些事情發生變化時(如硬體的插入和拔出)呼叫此指令碼。
傳送熱插拔事件的子系統(subsystem)包括匯流排驅動(USB、PCI等)和一些裝置的抽象層(網路介面、磁碟分割槽等)。它們通過/sbin/hotplug的第一個引數來識別。
對於裝置驅動來說,需要在程式碼裡設定MODULE_DEVICE_TABLE,指向驅動程式感興趣的裝置的裝置ID列表。
udev
在2.6核心裡,使用了udev來取代hotplug。據udev的作者Greg K.H說,之所以廢棄了hotplug原因是sysfs的出現,這個東西會產生非常多的hotplug事件,遠遠超過了2.4的核心(只要實現了了kobject模型的裝置驅動都回產生該事件)。所以hotplug變得複雜,而且因為hotplug都是bash所寫,所以開始變得沒有效率。於是出現了一個名叫hotplug-ng的專案,就是為了解決這個過於複雜以及缺乏效率的問題,ng應該是next generation的意思。但這個專案目前為止還不能勝任角色,所以udev挺身而出,充當了救火隊員。
2.6.15之後,/proc/sys/kernel/hotplug會成空的,因為核心通知使用者空間的介面變成了netlink,所以最新的udev也採用了netlink介面去寫,廢棄了/sbin/hotplug或者/sbin/udevsend。udev在2.6.15以後的核心上可以直接通過netlink接聽裝置事件,sysfs提供了uevent檔案,對該檔案的“寫”可以送出裝置事件!
udev 完全在使用者態 (userspace) 工作,利用裝置加入或移除時核心所傳送的hotplug 事件 (event) 來工作。關於裝置的詳細資訊是由核心輸出 (export) 到位於 /sys 的 sysfs 檔案系統的。所有的裝置命名策略、許可權控制和事件處理都是在使用者態下完成的。與此相反,devfs 是作為核心的一部分工作的。
傳統上一般 Linux 系統使用建立靜態裝置的方法,因此在 /dev 目錄下建立了大量的裝置節點(有時會有數千個節點),而不管對應的硬體裝置實際上是否存在。這通常是由 MAKEDEV 指令碼完成的,這個指令碼包含許多呼叫 mknod 程式的命令,為這個世界上可能存在的每個裝置建立相應的主裝置號和次裝置號。而使用 udev 方式的時候,只有被核心檢測到的裝置才為其建立裝置節點。因為每次系統啟動的時候都要重新建立這些裝置節點,所以它們被儲存在 tmpfs 檔案系統上,裝置節點不需要很多磁碟空間,所佔用的記憶體可以忽略不計。
udev 初始化指令碼負責在 Linux 啟動的時候建立裝置節點,該指令碼首先將 /sbin/udevsend 註冊為熱插拔事件處理程式。熱插拔事件(隨後將討論)本不應該在這個階段發生,註冊 udev 只是為了以防萬一。然後 udevstart 遍歷 /sys 檔案系統,並在 /dev 目錄下建立符合描述的裝置。例如,/sys/class/tty/vcs/dev 裡含有"7:0"字串,udevstart 就根據這個字串建立主裝置號為 7 、次裝置號為 0 的 /dev/vcs 裝置。udevstart 建立的每個裝置的名字和許可權由 /etc/udev/rules.d/ 目錄下的檔案指定的規則來設定。如果 udev 找不到所建立裝置的許可權檔案,就將其許可權設定為預設的 660 ,所有者為 root:root 。上面的步驟完成後,那些已經存在並且已經內建驅動的裝置就可以使用了。
對於以模組驅動的裝置,當核心檢測到一個新裝置連線時,核心會產生一個熱插拔事件,並在 /proc/sys/kernel/hotplug 檔案裡查詢處理裝置連線的使用者空間程式(新的核心通知介面改變,/proc/sys/kernel/hotplug為空了)。udev
初始化指令碼將 udevsend
註冊為該處理程式。當產生熱插拔事件的時候,核心讓 udev 在 /sys 檔案系統裡檢測與新裝置的有關資訊,併為新裝置在 /dev 裡建立專案。
所有在 sysfs 中顯示的裝置都可以由 udev 來建立節點。如果核心中增加了其它裝置的支援,udev 也就自動地可以為它們工作了。
大多數 Linux 發行版通過 /etc/modules.conf 配置檔案來處理模組載入,對某個裝置節點的訪問導致相應的核心模組被載入。對 udev 這個方法就行不通,因為在模組載入前,裝置節點根本不存在。Linux 的設計是在裝置被發現的時候載入模組,而不是當它被訪問的時候。通過在 /etc/sysconfig/modules檔案裡新增模組名,就可以在系統啟動的時候載入這些模組,這樣 udev
就可以檢測到裝置,並建立相應的裝置節點了。
如何寫udev規則。通過udevinfo程式來找到那些可以作為規則檔案裡的匹配項的專案。分為兩種情況:第一種情況是,當你把裝置插入系統後,系統為裝置產生了裝置名(如/dev/sda)。那樣的話,你先用udevinfo -q path -n /dev/sda,命令會產生一個該裝置名對應的在sysfs下的路徑,如/block/sda。然後,你再用udevinfo -a -p /sys/block/sda,這個命令會顯示一堆資訊,資訊分成很多塊。這些資訊實際來自於作業系統維護的sysfs連結串列,不同的塊對應不同的路徑。你就可以用這些資訊來作為udev規則檔案中的匹配項。但需要注意的是,同一個規則只能使用同一塊中顯示的資訊,不能跨塊書寫規則;第二種情況是,不知道系統產生的裝置名,那就只有到/sys目錄下去逐個目錄查找了,反覆用udevinfo -a -p /sys/path...這個命令看資訊,如果對應的資訊是這個裝置的,那就恭喜你。否則就再換個目錄。當然,在這種情況下,成功的可能性比較小。
HAL
HAL位於裝置驅動程式和應用程式之間。
l
D-BUS
D-BUS 是一個大有前途的訊息匯流排和活動系統,正開始深入地滲透到 Linux 桌面之中。D-BUS 本質上是程序間通訊(inter-process communication)(IPC)的一個實現,設計用於桌面應用程式和 OS 通訊。
典型的 D-BUS 設定將由幾個匯流排構成。一個持久的系統匯流排(system bus),它在引導時就會啟動。這個匯流排由作業系統和後臺程序使用,安全性非常好,以使得任意的應用程式不能欺騙系統事件。還將有很多會話匯流排(session buses),這些匯流排當用戶登入後啟動,屬於那個使用者私有。
一個更為有趣但很不實用的例子是 Jamboree 和 Ringaling 的結合。Jamboree 是一個簡單的音樂播放器,它具有 D-BUS 介面,以使得它可以被告知播放、到下一首歌、改變音量等等。Ringaling 是一個小程式,它開啟 /dev/ttyS0(一個串列埠)並觀察接收到的內容。當 Ringaling 發現文字“RING”時,就通過 D-BUS 告知 Jamboree 減小音量。最終的結果是,如果您的計算機上插入了一個調變解調器,而且電話鈴響,則音樂音量就會為您減小。
這正是計算機所追求的!
l
一些檢視硬體資訊的工具
lspci
列出所有PCI 裝置。有兩個引數是比較常用,-b 和-v,lspci也會把usb介面列出來。
lshal 列出系統硬體裝置。
Usbmodules
列出可用於已插入usb裝置的驅動模組
核心檢測到新硬體插入,然後分別通知hotplug和udev。前者用來裝入相應的核心模組(如usb-storage),而後者用來在/dev中建立相應的裝置節點(如/dev/sda1)
udev建立了相應的裝置節點之後,會將這一訊息通知hal的守護程式(hald)。當然udev還得保證新建立的裝置節點可以被普通使用者訪問。
hotplug裝入了相應的核心模組之後,會把這一訊息通知給hald。
hald在受到hotplug和udev發出的訊息之後,認為新硬體已經正式被系統認可了。此時它會通過一系列精心編寫的規則檔案(就是傳說中的xxx-policy.fdi),把發現新硬體的訊息通過dbus傳送出去,同時還會呼叫update-fstab或fstab-sync來更新/etc/fstab,為相應的裝置節點建立適合的掛載點。
卷管理器會監聽dbus中發現新硬體的訊息。根據所插入的硬體(區分U盤和數碼相機等)不同,卷管理器會先將相應的裝置節點掛載到hald建立的掛載點上,然後再開啟不同的應用程式。
當然,如果是在CDROM中插入光碟,過程可能比較簡單。因為CDROM本身就是一個固定的硬體,無需hotplug和udev的協助:
hald會自己監視CDROM,並且將光碟托架開合的訊息通過dbus發出去。
卷管理器負責檢查CDROM中的碟片內容,進行掛載,並呼叫合適的應用程式。
要注意,hald的工作是從上游得到硬體就緒的訊息,然後將這個訊息轉發到dbus中。儘管它會呼叫程式來更新fstab,但實際上它自己並不執行掛載的工作。
下面是上面的過程中涉及的模組和工具:
hotplug
hotplug 包和核心裡的hotplug模組不是一回事,2.6核心裡的pci_hotplug.ko是一個核心模組,而hotplug包是用來處理核心產生的hotplug事件。這個軟體包還在引導時檢測現存的硬體並在執行的核心中載入相關模組。
不但有熱插拔,還有冷插拔(cold pluging)。熱插拔在核心啟動之後發生,而“cold pluging”發生在核心啟動的過程中。
/etc/hotplug/*.rc 這些指令碼用於冷插拔(檢測和啟用在系統啟動時已經存在的硬體)。它們被 hotplug 初始化指令碼呼叫。*.rc 指令碼會嘗試恢復系統引導時丟失的熱插拔事件,舉例來說,核心沒有掛載根檔案系統。
/etc/hotplug/*.agent這些指令碼將被 hotplug
呼叫以響應核心產生的各種不同的熱插拔事件,導致插入相應的核心模組和呼叫使用者預定義的指令碼。
/sbin/hotplug核心預設情況下將在核心態的某些事情發生變化時(如硬體的插入和拔出)呼叫此指令碼。
傳送熱插拔事件的子系統(subsystem)包括匯流排驅動(USB、PCI等)和一些裝置的抽象層(網路介面、磁碟分割槽等)。它們通過/sbin/hotplug的第一個引數來識別。
對於裝置驅動來說,需要在程式碼裡設定MODULE_DEVICE_TABLE,指向驅動程式感興趣的裝置的裝置ID列表。
udev
在2.6核心裡,使用了udev來取代hotplug。據udev的作者Greg K.H說,之所以廢棄了hotplug原因是sysfs的出現,這個東西會產生非常多的hotplug事件,遠遠超過了2.4的核心(只要實現了了kobject模型的裝置驅動都回產生該事件)。所以hotplug變得複雜,而且因為hotplug都是bash所寫,所以開始變得沒有效率。於是出現了一個名叫hotplug-ng的專案,就是為了解決這個過於複雜以及缺乏效率的問題,ng應該是next generation的意思。但這個專案目前為止還不能勝任角色,所以udev挺身而出,充當了救火隊員。
2.6.15之後,/proc/sys/kernel/hotplug會成空的,因為核心通知使用者空間的介面變成了netlink,所以最新的udev也採用了netlink介面去寫,廢棄了/sbin/hotplug或者/sbin/udevsend。udev在2.6.15以後的核心上可以直接通過netlink接聽裝置事件,sysfs提供了uevent檔案,對該檔案的“寫”可以送出裝置事件!
udev 完全在使用者態 (userspace) 工作,利用裝置加入或移除時核心所傳送的hotplug 事件 (event) 來工作。關於裝置的詳細資訊是由核心輸出 (export) 到位於 /sys 的 sysfs 檔案系統的。所有的裝置命名策略、許可權控制和事件處理都是在使用者態下完成的。與此相反,devfs 是作為核心的一部分工作的。
傳統上一般 Linux 系統使用建立靜態裝置的方法,因此在 /dev 目錄下建立了大量的裝置節點(有時會有數千個節點),而不管對應的硬體裝置實際上是否存在。這通常是由 MAKEDEV 指令碼完成的,這個指令碼包含許多呼叫 mknod 程式的命令,為這個世界上可能存在的每個裝置建立相應的主裝置號和次裝置號。而使用 udev 方式的時候,只有被核心檢測到的裝置才為其建立裝置節點。因為每次系統啟動的時候都要重新建立這些裝置節點,所以它們被儲存在 tmpfs 檔案系統上,裝置節點不需要很多磁碟空間,所佔用的記憶體可以忽略不計。
udev 初始化指令碼負責在 Linux 啟動的時候建立裝置節點,該指令碼首先將 /sbin/udevsend 註冊為熱插拔事件處理程式。熱插拔事件(隨後將討論)本不應該在這個階段發生,註冊 udev 只是為了以防萬一。然後 udevstart 遍歷 /sys 檔案系統,並在 /dev 目錄下建立符合描述的裝置。例如,/sys/class/tty/vcs/dev 裡含有"7:0"字串,udevstart 就根據這個字串建立主裝置號為 7 、次裝置號為 0 的 /dev/vcs 裝置。udevstart 建立的每個裝置的名字和許可權由 /etc/udev/rules.d/ 目錄下的檔案指定的規則來設定。如果 udev 找不到所建立裝置的許可權檔案,就將其許可權設定為預設的 660 ,所有者為 root:root 。上面的步驟完成後,那些已經存在並且已經內建驅動的裝置就可以使用了。
對於以模組驅動的裝置,當核心檢測到一個新裝置連線時,核心會產生一個熱插拔事件,並在 /proc/sys/kernel/hotplug 檔案裡查詢處理裝置連線的使用者空間程式(新的核心通知介面改變,/proc/sys/kernel/hotplug為空了)。udev
初始化指令碼將 udevsend
註冊為該處理程式。當產生熱插拔事件的時候,核心讓 udev 在 /sys 檔案系統裡檢測與新裝置的有關資訊,併為新裝置在 /dev 裡建立專案。
所有在 sysfs 中顯示的裝置都可以由 udev 來建立節點。如果核心中增加了其它裝置的支援,udev 也就自動地可以為它們工作了。
大多數 Linux 發行版通過 /etc/modules.conf 配置檔案來處理模組載入,對某個裝置節點的訪問導致相應的核心模組被載入。對 udev 這個方法就行不通,因為在模組載入前,裝置節點根本不存在。Linux 的設計是在裝置被發現的時候載入模組,而不是當它被訪問的時候。通過在 /etc/sysconfig/modules檔案裡新增模組名,就可以在系統啟動的時候載入這些模組,這樣 udev
就可以檢測到裝置,並建立相應的裝置節點了。
如何寫udev規則。通過udevinfo程式來找到那些可以作為規則檔案裡的匹配項的專案。分為兩種情況:第一種情況是,當你把裝置插入系統後,系統為裝置產生了裝置名(如/dev/sda)。那樣的話,你先用udevinfo -q path -n /dev/sda,命令會產生一個該裝置名對應的在sysfs下的路徑,如/block/sda。然後,你再用udevinfo -a -p /sys/block/sda,這個命令會顯示一堆資訊,資訊分成很多塊。這些資訊實際來自於作業系統維護的sysfs連結串列,不同的塊對應不同的路徑。你就可以用這些資訊來作為udev規則檔案中的匹配項。但需要注意的是,同一個規則只能使用同一塊中顯示的資訊,不能跨塊書寫規則;第二種情況是,不知道系統產生的裝置名,那就只有到/sys目錄下去逐個目錄查找了,反覆用udevinfo -a -p /sys/path...這個命令看資訊,如果對應的資訊是這個裝置的,那就恭喜你。否則就再換個目錄。當然,在這種情況下,成功的可能性比較小。
HAL
HAL位於裝置驅動程式和應用程式之間。
l
D-BUS
D-BUS 是一個大有前途的訊息匯流排和活動系統,正開始深入地滲透到 Linux 桌面之中。D-BUS 本質上是程序間通訊(inter-process communication)(IPC)的一個實現,設計用於桌面應用程式和 OS 通訊。
典型的 D-BUS 設定將由幾個匯流排構成。一個持久的系統匯流排(system bus),它在引導時就會啟動。這個匯流排由作業系統和後臺程序使用,安全性非常好,以使得任意的應用程式不能欺騙系統事件。還將有很多會話匯流排(session buses),這些匯流排當用戶登入後啟動,屬於那個使用者私有。
一個更為有趣但很不實用的例子是 Jamboree 和 Ringaling 的結合。Jamboree 是一個簡單的音樂播放器,它具有 D-BUS 介面,以使得它可以被告知播放、到下一首歌、改變音量等等。Ringaling 是一個小程式,它開啟 /dev/ttyS0(一個串列埠)並觀察接收到的內容。當 Ringaling 發現文字“RING”時,就通過 D-BUS 告知 Jamboree 減小音量。最終的結果是,如果您的計算機上插入了一個調變解調器,而且電話鈴響,則音樂音量就會為您減小。
這正是計算機所追求的!
l
一些檢視硬體資訊的工具
lspci
列出所有PCI 裝置。有兩個引數是比較常用,-b 和-v,lspci也會把usb介面列出來。
lshal 列出系統硬體裝置。
Usbmodules
列出可用於已插入usb裝置的驅動模組