1. 程式人生 > >LINUX熱插拔

LINUX熱插拔

有 2 個不同角度來看待熱插拔:
從核心角度看,熱插拔是在硬體、核心和核心驅動之間的互動。
從使用者角度看,熱插拔是核心和使用者空間之間,通過呼叫使用者空間程式(如hotplug、udev 和 mdev)的互動。 當需要通知使用者核心發生了某種熱插拔事件時,核心才呼叫這個使用者空間程式。
現在的計算機系統,要求 Linux 核心能夠在硬體從系統中增刪時,可靠穩定地執行。這就對裝置驅動作者增加了壓力,因為在他們必須處理一個毫無徵兆地突然出現或消失的裝置。

熱插拔工具
當用戶向系統新增或刪除裝置時,核心會產生一個熱插拔事件,並在 /proc/sys/kernel/hotplug 檔案裡查詢處理裝置連線的使用者空間程式。這個使用者空間程式主要有

hotplug:這個程式是一個典型的 bash 指令碼, 只傳遞執行權給一系列位於 /etc/hot-plug.d/ 目錄樹的程式。hotplug 指令碼搜尋所有的有 .hotplug 字尾的可能對這個事件進行處理的程式並呼叫它們, 並傳遞給它們許多不同的已經被核心設定的環境變數。(基本已被淘汰,具體內容請參閱《LDD3》)
udev :用於linux2.6.13或更高版本的核心上,為使用者空間提供使用固定裝置名的動態/dev目錄的解決方案。它通過在 sysfs 的 /class/ 和/block/ 目錄樹中查詢一個稱為 dev 的檔案,以確定所建立的裝置節點檔案的主次裝置號。所以要使用udev,驅動必須為裝置在sysfs中建立類介面及其dev屬性檔案,方法和sculld模組中建立dev屬性相同。 udev的資料網上十分豐富,我就不在這廢話了,給出以下連結有興趣的自己研究:
《UDEV Primer》(英文),地址:

http://webpages.charter.net/decibelshelp/LinuxHelp_UDEVPrimer.html

mdev:一個簡化版的udev,是busybox所帶的程式,十分適合嵌入式系統。

因為hotplug現在也在被慢慢地淘汰,udev不再依賴hotplug了,所以這裡不再介紹;

udev較mdev複雜,不太適合嵌入式使用。(本人也有做udev的實驗,交叉編譯是通過了,但是使用上有問題,沒有實現其功能。也許是我的檔案系統沒做好,以後有時間再研究和寫記錄。有成功高人的通知一聲,交流一下經驗。^_^謝謝!);

mdev簡單易用,比較適合嵌入式系統,實驗成功。以下詳細介紹mdev的使用。

================================

裝置節點的建立,是通過sysfs介面分析dev文件取得裝置節點號,這個很顯而易見。那麼udevd是通過什麼機制來得知核心裡模組的變化情況,如何得知裝置的插入移除情況呢?當然是通過hotplug機制了,那 hotplug又是怎麼實現的?或說核心是如何通知使用者空間一個事件的發生的呢?
答案是通過netlink socket通訊,在核心和使用者空間之間傳遞資訊。
核心呼叫kobject_uevent函式傳送netlink message給使用者空間,這部分工作通常無需驅動去自己處理,在統一裝置模型裡面,在子系統這一層面,已將這部分程式碼處理好了,包括在裝置對應的特定的 Kobject建立和移除的時候都會發送相應add和remove訊息,當然前提是您在核心中配置了hotplug的支援。
Netlink socket作為一種核心和使用者空間的通訊方式,不但僅用在hotplug機制中,同樣還應用在其他很多真正和網路相關的核心子系統中。
Udevd通過標準的socket機制,建立socket連線來獲取核心廣播的uevent事件 並解析這些uevent事件

Udevtrigger的工作機制
執行udevd以後,使用udevtrigger的時候,會把核心中已存在的裝置的節點創建出來,那麼他是怎麼做到這一點的? 分析udevtrigger的程式碼能夠看出:
udevtrigger通過向/sysfs 文件系統下現有裝置的uevent節點寫”add”字串,從而觸發uevent事件,使得udevd能夠接收到這些事件,並建立buildin的裝置驅動的裝置節點連同任何已insmod的模組的裝置節點。
所以,我們也能夠手工用命令列來模擬這一過程:
/ # echo “add” > /sys/block/mtdblock2/uevent
/ #
/ # UEVENT[178.415520] add /block/mtdblock2 (block)
但是,進一步看程式碼,您會發現,實際上,不管您往uevent裡面寫什麼,都會觸發add事件,這個從kernel內部對uevent屬性的實現函式能夠看出來,預設的實現是:
static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
kobject_uevent(&dev->kobj, KOBJ_ADD);
return count;
}
所以不管寫的內容是什麼,都是觸發add操作,真遺憾,我還想通過這個屬性實驗remove的操作。 不知道這樣限制的原因是什麼。
而udevstart的實現方式和udevtrigger就不同了,他基本上是重複實現了udevd裡面的機制,通過遍歷sysfs,自己完成裝置節點的建立,不通過udevd來完成。
udevd建立每一個節點的時候,都會fork出一個新的程序來單獨完成這個節點的建立工作。
Uevent_seqnum 用來標識當前的uevent事件的序號(已產生了多少uevent事件),您能夠通過如下操作來檢視:
$ cat /sys/kernel/uevent_seqnum
2673

  udev的工作原理 當系統核心發現安裝或者解除安裝了某一個硬體裝置時,核心會執行hotplug,以便讓hotplug去安裝或解除安裝該硬體的驅動程式;hotplug在處理完硬體的驅動程式後,就會去呼叫執行udevd,以便讓udevd可以產生或者刪除硬體的裝置檔案。 接著udevd會通過libsysfs讀取sys檔案系統,以便取得該硬體裝置的資訊;然後再向namedev查詢該外部裝置的裝置檔案資訊,例如檔案的名稱、許可權等。最後,udevd就依據上述的結果,在/dev/目錄中自動建立該外部裝置的裝置檔案,同時在/etc/udev/rules.d下檢查有無針對該裝置的使用許可權

====================
1.kobject, ktype, kset

kobject代表sysfs中的目錄。

ktype代表kobject的型別,主要包含release函式和attr的讀寫函式。比如,所有的bus都有同一個bus_type;所有的class都有同一個class_type。

kset包含了subsystem概念,kset本身也是一個kobject,所以裡面包含了一個kobject物件。另外,kset中包含kset_uevent_ops,裡面主要定義了三個函式

   int (*filter)(struct kset *kset, struct kobject *kobj);

   const char *(*name)(struct kset *kset, struct kobject *kobj);

   int (*uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env);

這三個函式都與uevent相關。filter用於判斷uevent是否要發出去。name用於得到subsystem的名字。uevent用於填充env變數。
2.uevent核心部分

uevent是sysfs向用戶空間發出的訊息。比如,device_add函式中,會呼叫kobject_uevent(&dev->kobj, KOBJ_ADD); 這裡kobj是發訊息的kobj,KOBJ_ADD是發出的事件。uevent的事件在kobject_action中定義:

enum kobject_action {

   KOBJ_ADD,

   KOBJ_REMOVE,

   KOBJ_CHANGE,

   KOBJ_MOVE,

   KOBJ_ONLINE,

   KOBJ_OFFLINE,

   KOBJ_MAX

};

int kobject_uevent(struct kobject *kobj, enum kobject_action action)

{

   return kobject_uevent_env(kobj, action, NULL);

}

kobject_uevent_env:

   由kobject的parent向上查詢,直到找到一個kobject包含kset。

   如果kset中有filter函式,呼叫filter函式,看看是否需要過濾uevent訊息。

   如果kset中有name函式,呼叫name函式得到subsystem的名字;否則,subsystem的名字是kset中kobject的名字。

   分配一個kobj_uevent_env,並開始填充env環境變數:

   增加環境變數ACTION=<action name>

   增加環境變數DEVPATH=<kobj’s path>

   增加環境變數SUBSYSTEM=<subsystem name>

   增加環境變數kobject_uevent_env中引數envp_ext指定的環境變數。

   呼叫kset的uevent函式,這個函式會繼續填充環境變數。

   增加環境變數SEQNUM=<seq>,這裡seq是靜態變數,每次累加。

   呼叫netlink傳送uevent訊息。

   呼叫uevent_helper,最終轉換成對使用者空間sbin/mdev的呼叫。

3.uevent使用者空間部分

uevent的使用者空間程式有兩個,一個是udev,一個是mdev。

udev通過netlink監聽uevent訊息,它能完成兩個功能:

   1.自動載入模組

   2.根據uevent訊息在dev目錄下新增、刪除裝置節點。

另一個是mdev,mdev在busybox的程式碼包中能找到,它通過上節提到的uevent_helper函式被呼叫。

下面簡要介紹udev的模組自動載入過程:

etc目錄下有一個uevent規則檔案/etc/udev/rules.d/50-udev.rules

udev程式收到uevent訊息後,在這個規則檔案裡匹配,如果匹配成功,則執行這個匹配定義的shell命令。例如,規則檔案裡有這麼一行:

ACTION==”add”, SUBSYSTEM==”?“, ENV{MODALIAS}==”?“, RUN+=”/sbin/modprobe $env{MODALIAS}”

所以,當收到uevent的add事件後,shell能自動載入在MODALIAS中定義的模組。

mdev的模組自動載入過程與之類似,它的配置檔案在/etc/mdev.conf中。例如:

MODALIAS=.0:0660@modprobe"MODALIAS”

這條規則指的是:當收到的環境變數中含有MODALIAS,那麼載入MODALIAS代表的模組。

mdev的詳細說明在busybox的docs/mdev.txt中。
4.uevent在裝置驅動模型中的應用

在sys目錄下有一個子目錄devices,代表一個kset。

建立裝置時,呼叫的device_initialize函式中,預設會把kset設定成devices_kset,即devices子目錄代表的kset。

devices_kset中設定了uevent操作集device_uevent_ops。

static struct kset_uevent_ops device_uevent_ops = {

   .filter =    dev_uevent_filter,

   .name =   dev_uevent_name,

   .uevent = dev_uevent,

};

dev_uevent_filter中,主要是規定了要想傳送uevent,dev必須有class或者bus。

dev_uevent_name中,返回dev的class或者bus的名字。

dev_uevent函式:

   如果dev有裝置號,新增環境變數MAJOR與MINOR。

   如果dev->type有值,設定DEVTYPE=<dev->type->name>。

   如果dev->driver,設定DRIVER=<dev->driver->name>。

   如果有bus,呼叫bus的uevent函式。

   如果有class,呼叫class的uevent函式。

如果有dev->type,呼叫dev->type->uevent函式。

一般在bus的uevent函式中,都會新增MODALIAS環境變數,設定成dev的名字。這樣,uevent傳到使用者空間後,就可以通過對MODALIAS的匹配自動載入模組。這樣的bus例子有platform和I2C等等。

==========================

熱插拔(hotplug,打這個詞的時候我常常想到熱乾麵)不一定非要指類似U盤那樣的插入拔出,此處的熱插拔廣義上講,是指一個裝置加入系統,核心如何通知使用者空間。舉個簡單的例子,如果你的電腦中有塊PCI網絡卡,針對該網絡卡的驅動程式以核心模組的形式被編譯(obj-m),那麼Linux系統在啟動過程中是如何自動載入該網絡卡的驅動模組呢?大家都知道現在udev負責幹這事,其實除了udev,還可以有其他的手法,你自己就可以這樣做。

我們先討論udev,udev最關鍵的東西是當系統發現一個裝置時,它要能夠被通知該事件,一旦它知道了這件事,那麼餘下的事情就都好說了,無非是個如何查詢模組並載入的過程。所以我們看到,這裡的關鍵是熱插拔事件的通知機制。Linux的裝置模型為此提供了非常完美的支援,其原理其實發源於kset這一層,對此在《深入Linux裝置驅動程式核心機制》一書中有詳細的描述,雖然這部分看起來蠻複雜,貌似挺能嚇唬住一些新手,其實說白了,要點就是通過sysfs建立關係,溝通核心與使用者空間,然後就是uevent,也就是下面要說的熱插拔事件。

當然裝置驅動程式一般不會和這些太底層的kobject/kset傢伙打交道,因為更高層次的device,bus和driver把kobject/kset那一層的細節實現都給封裝了起來。所以裝置熱插拔的uevent事件最終的源頭來自於device_add,本帖這裡肯定不會討論device與driver如何繫結那一攤子事情。下面看看device_add的原始碼,是如何實現uevent機制的:

    <drivers/base/core.c>
    int device_add(struct device *dev)
    {
          ...
          kobject_uevent(&dev->kobj, KOBJ_ADD);
          ...
    }

複製程式碼

熱插拔的核心實現就那一個函式呼叫,這裡device_add對應的是KOBJ_ADD,那麼移除裝置自然對應KOBJ_REMOVE了。kobject_uevent函式最終呼叫的是kobject_uevent_env,後者才是真正幹事的夥計。
下面給出kobject_uevent_env函式的核心框架:

    int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
                           char *envp_ext[])
    {
            ...
    #if defined(CONFIG_NET)
            /* send netlink message */
            ...
    #endif
            /* call uevent_helper, usually only enabled during early boot */
            if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
                    char *argv [3];
                    argv [0] = uevent_helper;
                    argv [1] = (char *)subsystem;
                    argv [2] = NULL;
                    retval = add_uevent_var(env, "HOME=/");
                    if (retval)
                            goto exit;
                    retval = add_uevent_var(env,
                                            "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
                    if (retval)
                            goto exit;
                    retval = call_usermodehelper(argv[0], argv,
                                                 env->envp, UMH_WAIT_EXEC);
            }
            ...
    }

複製程式碼

怎麼樣,夠簡潔吧,其實看實際的程式碼比這要鬱悶地多,不過骨架清晰就行了。程式碼中的netlink message就不用多說了吧,給udev發通知用(有時間的話可以分析分析udev的程式碼)。本帖重點討論後半段的if (uevent_helper[0] && !kobj_usermode_filter(kobj))程式碼,這裡的核心呼叫是call_usermodehelper,這個函式最有意思的地方就在於在核心空間呼叫使用者空間的程式,它的詳細實現機制在書中已經講得很多,這裡就不再贅述了。call_usermodehelper在kobject_uevent_env函式中要呼叫的使用者空間程式由uevent_helper[0]來指定,所以如果我們能控制這個uevent_helper[0],就能接收到裝置加入系統移出系統等事件。那個if中的kobj_usermode_filter條件一般都會滿足(除非這是個特別注意個人隱私的裝置,那就不好說了,人家偷偷加入系統就是不想讓你知道你也沒有辦法,但是udev還是能知道的)。

下面看看uevent_helper[0]來自何處:

    <lib/kobject_uevent.c>
    char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;

複製程式碼

貌似要通過核心配置來指定,我看了一下我係統中Linux目錄下的.config檔案,找到了下面這行:

<linux-3.1.6/.config>
#
# Generic Driver Options
#
CONFIG_UEVENT_HELPER_PATH=""

複製程式碼

丫的,居然沒指定,那麼uevent_helper[0]=”“,這樣的話我們在kobject_uevent_env函式中的那個if語句就沒法滿足了,看來要重新配置再編譯核心了。不過想想sysfs這麼強大,核心開發的那幫人好歹給留個使用者空間的接口出來吧,一檢視還真有:

相關推薦

LINUX

有 2 個不同角度來看待熱插拔: 從核心角度看,熱插拔是在硬體、核心和核心驅動之間的互動。 從使用者角度看,熱插拔是核心和使用者空間之間,通過呼叫使用者空間程式(如hotplug、udev 和 mdev)的互動。 當需要通知使用者核心發生了某種熱

Linux (Hot Plug)處理機制系列

將可移動裝置連入系統時,系統的後臺中會依次發生如下事件: 核心檢測到新硬體插入,然後分別通知hotplug和udev。前者用來裝入相應的核心模組(如usb-storage),而後者用來在/dev中建立相應的裝置節點(如/dev/sda1) udev建立了相應的裝置節點之後,

linux之udev的使用方法

核心呼叫kobject_uevent函式傳送netlink message給使用者空間,這部分工作通常不需要驅動去自己處理,在統一裝置模型裡面,在子系統這一層面,已經將這部分程式碼處理好了,包括在裝置對應的特定的Kobject建立和移除的時候都會發送相應add和remove訊息,當然前提是你在核心中配置了ho

Linux USB 驅動開發(四)—— 那點事

Linux USB 驅動開發(四)—— 熱插拔那點事         學習USB熱插拔之前,先學習一些USB的硬體知識: 一、USB基礎概念 1、硬體知識(USB插座和插頭)        在最初的標

【玩轉開源】Linux C 檢測網口

int NetDetect(char *net_name, int *statue) { int ret = 0; int skfd = 0; struct ifreq ifr; skfd = socket(AF_INET, SOCK_DGRAM, 0); //建議s

Linux中Netlink實現監控——核心與使用者空間通訊

1、什麼是NetLink?  它 是一種特殊的 socket,它是 Linux 所特有的,由於傳送的訊息是暫存在socket接收快取中,並不被接收者立即處理,所以netlink是一種非同步通訊機制。 系統呼叫和 ioctl 則是同步通訊機制。Netlink是面向資料包的服務

linux scsi硬碟

在生產環境中,有很多情況不能隨便重啟裝置,例如scsi硬碟的更換(非raid卡環境)。 在linux下面修改/proc/scsi/scsi檔案可以實現熱插拔。 測試環境red hat 5.5+vmware8.0 具體步驟: 1.硬碟新增到服務上,看好介面,插槽。 2.管理員登陸,看一個/pro

linux c檢測網線(netlink)

#include <sys/types.h> #include <sys/socket.h> #include <asm/types.h> #include <linux/netlink.h> #include

Linux下實現USB口的事件觸發

目前要做一個在嵌入式平臺上的USB口的熱插拔事件。 經過我現在的分析總結目前有如下方法: 1,定時檢查/proc/scsi/scsi檔案 此方法只能在PC上,但在嵌入式平臺上不可用。 2,netlink方式 使用netlink. #include <s

qt linux下自動檢測U盤

如果用U盤來更新軟體,需要先刪除當前的可執行檔案,但是如果刪除之後,複製失敗的話會導致後續沒有檔案可以運行了。一個方法是將新的可執行檔案放到另一個目錄中,當copy成功後修改linux的啟動檔案,然後reboot.    使用qt自帶的QDBus可以實現。 D-B

Linux下自動檢測USB

做嵌入式開發,尤其在閘道器、路由器或者其他支援USB裝置的終端上,為了提高使用者體驗,我們常常需要支援自動識別並掛載USB裝置功能。某些應用程式,在使用USB裝置的過程中,也希望能夠偵測到USB斷開事件,不至於某些工作因為USB已經不存在而白做。在Linux下,我們主要有

linux app應用如何檢測USB裝置

框架是這樣的。原理就是建立一個socket捕獲核心發過來的netlink訊息,很簡單的。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <

ARM上的linux如何實現無線網絡卡的冷

ARM上的linux如何實現無線網絡卡的冷插拔和熱插拔 fulinux 1. 冷插拔 如果在系統上電之前就將RT2070/RT3070晶片的無線網絡卡(以下簡稱wlan)插上,即冷插拔。我們通過分析系統啟動流程過程中的執行的指令碼,將啟動wlan的指令碼加入

libvirt-內存分配和內存

簡單的 nbsp mman src 疑惑 get 使用 熱插拔 加載 在使用libvirt給虛擬機分配內存大小時,涉及到memory和currentMemory兩個字段,很多人都有這個疑惑,為什麽需要兩個呢。其實是和內存氣泡動態調整內存有關系的。簡單的來說,me

【產品功能】配置網卡從此與關機無緣,彈性網卡支持功能

控制臺摘要: 本文主要介紹了彈性網卡的熱插撥功能改進,彈性網卡插拔對應的實例不再要求必須為 Stopped 狀態,現在只要求是穩定狀態( Running Stopped )即可。以及介紹了控制臺、OpenAPI如何對彈性網卡進行熱插拔操作。背景在雲計算飛速發展的今天,用戶ECS服務器身處的網絡環境越來越復雜,

記一次U盤導致的問題

解決 顯示 沒有 設備管理器 網上 提示 導致 電腦 boot 32G U盤拿過來做啟動盤,全部刻印好了,在新電腦上啟動PE,進行分區的時候卡住了,直接就拔出U盤。導致的問題是:之前把U盤刻印啟動盤的電腦,已經新電腦上都識別不出來U盤。問題:電腦上設備管理器上能顯示出U盤,

在 ESXi 6.x和5.x虛擬機中禁用添加/功能

客戶端 警告選項 虛機 所有 man nic win manage 下一步 現象:網卡和 SCSI 控制器顯示為可移除設備。與該虛擬硬件對應的“安全移除硬件”選項顯示在 Windows 系統任務欄中。如果正在使用 VMware View,您會註意到具有持久磁盤的 View

詳解oracle 12c數據庫新特征CDB與PDB(數據庫)以及表空間管理

dia emp 圖片 朋友 不可 管理 ada pfile esp 表空間概念 表空間是數據庫的邏輯劃分,一個表空間只能屬於一個數據庫。所有的數據庫對象都存放在指定的表空間中。但主要存放的是表, 所以稱作表空間。Oracle數據庫中至少存在一個表空間,即SYSTEM的表空間

C# Winform下一個的MIS/MRP/ERP框架(多語言方案)

文件加載 全局 查詢 分享 技術 變量 支持 對象 style 個別時候,我們需要一種多語種切換方案。   我的想方案是這樣的: 1、使用文本文本存儲多語言元素,應用程序啟動時加載到內存表中; 2、應用程序啟動時從配置文件加載語種定義; 3、所有窗體繼承自一個Base

C# Winform下一個的MIS/MRP/ERP框架11(啟航)

aer tab chan byname 可能 清理 contex cati break   初學時,有了想法卻完全不知道該從何下指,此序列將拋磚引玉,與大家共同學習進步。   一個程序的初始,必然是啟動。   我的要求:   1、應用程序保持單例;   2、從配置文件加載一