微軟的Windows作業系統是怎麼裝驅動程式的呢? (How Windows Installs Devices)

(原文出處: 這裡)

  1. The New Device is Identified (第一步:識別設備,獲取設備標識(ID))
  2. A Driver for the Device is Selected (第二步:選擇最優驅動安裝包)
  3. The Driver for the Device is Installed (第三部:安裝驅動)

Step 1: The New Device is Identified

當一個設備插入到計算機總線後,總線驅動檢測到設備插入,然後給設備分配一串硬件ID(hardware identifier (ID))。
例如,插入一個USB Key插入到電腦後,分配的硬件ID為:

USB\VID_096E&PID_0807&REV_0100
USB\VID_096E&PID_0807

Windows就靠此ID來尋找最優的那個驅動安裝包。

硬件ID的格式如下:

  • A bus-specific prefix, such as PCI\ or USB\.
  • Vendor-specific identifiers for the device, such as a vendor, model, and revision identifier. The format of these identifiers within the hardware ID is also specific to the bus driver.

Hardware ID
A hardware ID is a vendor-defined identification string that Windows uses to match a device to an INF file. In most cases, a device has associated with it a list of hardware IDs. (However, there are exceptions ? see Identifiers for 1394 Devices ). When an enumerator reports a list of hardware IDs for a device, the hardware IDs should be listed in order of decreasing suitability.
通過MSND中的解釋可以了解到:硬件ID是一組ID值,如上面例子中的兩列字符串。

Device ID
A device ID is a string reported by a device’s enumerator. A device has only one device ID. A device ID has the same format as a hardware ID.
設備ID不是一組ID值,只有一個唯一ID值,他唯一標識了此類設備,設備ID和硬件ID的格式一樣,硬件ID中最能嚴格定義此類設備的ID值就為設備ID。如上面兩條ID值中,第一條能更加準確的定義此類設備,所以第一條也叫設備ID。

Compatible ID
A compatible ID is a vendor-defined identification string that Windows uses to match a device to an INF file. A device can have associated with it a list of compatible IDs. The compatible IDs should be listed in order of decreasing suitability. If Windows cannot locate an INF file that matches one of a device’s hardware IDs, it uses compatible IDs to locate an INF file. Compatible IDs have the same format as hardware IDs. However, compatible IDs are typically more generic than hardware IDs.

關於設備ID,硬件ID,兼容ID等概念,可以參考MSDN:http://msdn.microsoft.com/EN-US/library/windows/hardware/ff541224(v=vs.85).aspx,或者《竹林蹊徑:深入淺出Windows驅動開發》中的第十二章。這裡不需要深入了解。

在設備管理器中,可以瀏覽硬件ID:

可以看到,硬件ID不只一個,它是一組ID,其中,最嚴格定義了該類設備的ID也被稱作設備ID。如上圖中,第一個條USB\VID_096E&PID_0807&REV_100為設備ID。

Step 2: A Driver for the Device is Selected

第一步得到硬件IDs,Windows開始搜索驅動安裝包。這節解決幾個問題:

搜索的對比規則是什麼?
搜索順序是怎樣的?
搜索結果有多個驅動匹配,如何選擇最優?
搜索對比規則

每個驅動安裝包中,都有一個INF文件,此文件記錄了廠商定義的設備信息等內容。以某個廠商的智能卡私有驅動為例,其INF片段如下:

[Manufacturer]
%MFGNAME1%=DeviceListMfg1,NTamd64

[DeviceListMfg1.NTamd64]
%USB\VID_08E6&PID_3437.DeviceDesc% =DriverInstall64, USB\VID_08E6&PID_3437 ;
%USB\VID_08E6&PID_3438.DeviceDesc% =DriverInstall64, USB\VID_08E6&PID_3438 ;
%USB\VID_08E6&PID_3480.DeviceDesc% =DriverInstall64, USB\VID_08E6&PID_3480 ;
%USB\VID_08E6&PID_34EC.DeviceDesc% =DriverInstall64, USB\VID_08E6&PID_34EC ;

可以看到[DeviceListMfg1.NTamd64]中,定義了每個產品的VID,PID信息,諸如:USB\VID_08E6&PID_3437,就是廠商自己定義的硬件ID,Windows搜索INF這個區域,對比第一步得到的硬件IDs和INF中的硬件ID,如果能精確匹配,那說明此驅動包符合安裝條件。

我們接著分析微軟的通用智能卡驅動是如何實現的,我們知道,微軟不可能知道我們的設備的硬件ID信息,也就說,在INF中,不能將全世界廠商的硬件ID全部記錄在INF文件中。所以,微軟引入了兼容ID(compatible ID)。兼容ID和硬件ID的格式一樣,但它更加通用,能囊括一大類設備。它被廠商定義在INF文件中。如果Windows沒有找到對應的硬件ID,它會去比對INF中的兼容ID,找到對應的驅動安裝包。

以微軟XP的CCID驅動為例,usbccid.inf中的片段:

[Manufacturer]
%CCID%=CCID,NTamd64

[CCID.NTamd64]
%USBCCID.DeviceDesc% = USBCCID.Install,USB\Class_0B&SubClass_00

此處的USB\Class_0B&SubClass_00,就是兼容ID,在設備管理器中也能顯示兼容ID:

驅動包的搜索順序

Windows搜索INF文件的位置,是按照一定規則來執行的,不同的操作系統,搜索順序會有所不同:

Search phase

Windows Server 2003, Windows XP, and Windows 2000

Windows Vista and
Windows Server 2008

Windows 7 and later versions of Windows

Without user interaction

DevicePath

Driver store

Windows Update

Driver store

DevicePath

With user interaction

Prompt for distribution media

Windows Update

DevicePath

Windows Update

Prompt for distribution media

Not applicable

DevicePath對應的路徑存儲在註冊裡:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion下,定義了DevicePath字符串鍵值。通常,對應的目錄為 Windows\inf。

Driver store在Vista以後,對應的路徑為:Windows\System32\DriverStore\FileRepository。 Vista以前的系統,路徑為:Windows\Driver Cache。從Vista以後的系統,系統開始重用Driver Store,如果找到一個匹配的驅動包,第一步就是先把這個安裝包拷貝到Driver Store中,然後安裝過程在Driver Store中進行。

下面舉例說明:

當前系統如果為Vista,當一個用戶插入一個智能卡到USB hub中時:

USB 總線驅動為智能卡創建一串硬件IDs,Windows首先會搜索Driver store(Windows\System32\DriverStore\FileRepository)中的INF文件。
如果未找到合適的驅動包,Windows啟動 添加新硬件嚮導,在嚮導裡,用戶可以選擇從Windows Update搜索(聯機搜索),也可以自己指定INF安裝路徑。

選擇聯機搜索,Windows搜索順序如下:

DevicePath(Windows\inf)
Windows Update
如果搜索不到,提示用戶插入光盤、軟盤,或者指定驅動包位置

如果驅動包找到,Windows將驅動包運輸(Staging)到Driver Store(Windows\System32\DriverStore\FileRepository)中。
當前系統如果為Win7,當一個用戶插入一個智能卡到USB hub中時:

USB 總線驅動為智能卡創建一串硬件ID,Windows首先會搜索Windows Update,如果找到,將驅動運輸到Driver store(Windows\System32\DriverStore\FileRepository)中。
如果未找到合適的驅動包,Windows繼續搜索Driver store(Windows\System32\DriverStore\FileRepository)和DevicePath(Windows\Inf).
如果找到了安裝,如果未找到,安裝失敗。
Win7不會自動運行安裝嚮導來讓用戶選擇驅動包,但這個嚮導仍然存在,在設備管理器中,選擇未識別的設備,右鍵,選擇更新驅動,同樣能夠達到安裝的目的。

選擇最優驅動包

搜索完成後,可能會有多個驅動包匹配成功,如不同版本號的驅動都匹配成功了。如何進一步篩選,選出最優驅動包?

如果Windows只找到一個驅動包,那好說,直接安裝。
如果Windows找到不只一個驅動包,Windows為每個驅動包的匹配度進行打分,打分最低的那個,就是最優的那個。
如何給驅動打分,有一套規則,規則比較複雜,打分在vista之前和vista之後會有所不同,有所不同,可以參考MSDN:http://msdn.microsoft.com/EN-US/library/ff686700(v=VS.85,d=hv.2).aspx

這里大致說明一下,影響分值有幾個因素:

驅動是如何被簽名(微軟簽名?第三方簽名?未簽名?等)
驅動ID是如何匹配的(根據硬件ID?根據兼容ID?等)
… …
如果打分最低的有多個(就是倒數並列第一的概念),Windows使用如下規則,進一步篩選:
是否已被簽名
通過INF的Version節中的DriverVer字段值判斷哪兒個版本最新,就選哪個。
下面摘自《竹林蹊徑:深入淺出Windows驅動開發》一個解釋片段:

Step 3: The Driver for the Device is Installed

選擇好了驅動,就開始安裝驅動了,安裝驅動的步驟如下:

通過INF文件中的CopyFiles指令,拷貝二進製文件(驅動和其他文件)到指定目錄
如果INF 中有DDInstall.Coinstallers段,註冊device-specific co-installer。 co-installer是一個DLL庫,是為了輔助設備驅動安裝的一個庫,它主要做一些在INF文件中無法描述的操作。例如,添加一些額外的信息到註冊表,讀取註冊表的一些關鍵信息用以執行不同的操作,安裝完成顯示完成頁面等。關於Co-installer更多的信息,可以參考MSDN:http://msdn.microsoft.com/EN-US/library/windows/hardware/ff554011(v=vs.85).aspx
讀取INF中的Class 和 ClassGuid字段,將設備進行歸類(軟件方式歸類,如將一個插入的鼠標歸類為Mouse,將所有的智能卡設備歸類為SmartCard)。如果此Class是一個系統中不存在的設備類型,廠商需要提供 類安裝庫(class installer),來安裝設備類。這也是一個DLL,而且類安裝過程中,也可以有class co-installers輔助安裝庫,具體信息參考MSDN:http://msdn.microsoft.com/EN-US/library/windows/hardware/ff554009(v =vs.85).aspx
當所有的驅動和co-installders都被註冊,Windows將控制權交給Plug and Play (PnP) manager,它負責加載和驅動設備。
PNP管理器首選調用驅動入口函數:DriverEntry,接著調用AddDevice,……,發送IRP_MN_START_DEVICE給驅動設備對象。驅動開始工作。

發表迴響