天天看點

USB開發步驟之軟體篇(轉)

  我這裡重點的介紹如何寫驅動程式,對于一些應用程式我就不做介紹了,因為我對于那些高層的東西寫得很少。倘若再講,有班門弄斧之嫌,呵呵!

   作為WIN98和WIN2K推薦的一項新技術來說,USB的驅動程式和以往的直接跟硬體打交道的WIN95的VXD的方式的驅動程式不同,它應該是WDM類型的。

   對于HID的裝置,就可以采用上圖左上邊的結構,其它類的話采用右上的結構,其實右邊的結構可以又細分成兩層,一層是Class Driver,一層是Miniport Driver。而倒數第三行的UHCD和OpenHCI分别是由INTEL和COMPAQ兩位老大定的一個和硬體有關的底層驅動程式标準,各位可以根據所需要的選擇。

   對于USB的驅動程式,大家還得去了解WDM驅動程式的寫法,或者早些時候的NT驅動程式,其實WDM驅動程式可以看做是NT驅動程式的一個update,隻是增加了一些新的特性。

   “寫驅動程式是一個很漫長和繁瑣的工作,在此之前,你最好要熟悉硬體,熟悉C/C++,還要用過DDK,會用一些調試程式,如SOFTICE和WINDBG之類。如果一切就緒,你就可以開始寫驅動程式,工作的程序有時侯會取決于你的運氣”。(這是一位留美的朋友對我說的,我寫出來和大家共享)

下面是我從一個朋友那裡得到的一篇文章的摘要:

NT驅動程式的分層結構

   驅動程式是指管理某個外圍裝置的一段程式代碼。NT采用更靈活的分層驅動方法,允許雜應用程式和硬體之間存在幾個驅動程式層次。分層機制允許NT更加廣泛地定義驅動程式,包括檔案系統、邏輯卷管理器和各種網絡元件,各種實體裝置驅動程式等等。

1、 裝置驅動程式

   這些是管理實際資料傳輸和控制特定類型的實體裝置的操作的驅動程式,包括開始和完成I/O操作,進行中斷和執行特定的裝置要求的任何差錯處理。

2、 中間驅動程式

   NT允許在實體裝置驅動程式上分層任意數目的中間驅動程式。這些中間層次提供擴充I/O系統的功能一種方法,而不必修改底層的驅動程式。這也是微軟鼓吹的他們的系統靈活的一面!實際上我覺得這樣反而犧牲了一些效率上的東西。

3、 檔案系統驅動程式(FSD)

   FSD是一類比較特殊的驅動程式,通常負責維護各種檔案系統所需要的磁盤結構。注意我們并不能使用DDK來開發FSD,而必須使用Microsoft的檔案系統開發人員工具包。

   一般比較少寫中間過濾驅動程式,過濾驅動程式它截獲和修改高層發送給類驅動程式的請求。這樣就允許利用現有類驅動程式的功能,而不必從頭開始寫所有程式。NT核心模式對象在我們的實際開發過程中的對象是裝置,由于端口驅動程式已經隐藏了硬體控制操作,是以我在這裡不講述跟硬體相關的部份。如果今後的開發對象不同,需要對硬體進行操作的時候,可能會對中斷、DMA等有比較詳細的了解,這些内容可以參考DDK幫助。

   NT使用對象技術管理所有的資料,下面分别對一般驅動程式所涉及的一些對象做一介紹。不過在介紹這些對象之前,有必要先對驅動程式的結構做一介紹。

 

驅動程式結構

   NT驅動程式和一般的DOS/Windows C語言程式不一樣,它沒有main()或者WinMain()函數入口。和DLL類似地,它向作業系統顯露一個名稱為DriverEntry()的函數,在啟動驅動程式的時候,作業系統将調用這個入口。DriverEntry除了做一些必要的裝置初始化工作外,還初始化一些Dispatch例程入口。我們知道,NT應用和裝置驅動程式打交道主要是通過CreateFile、 ReadFile、WriteFile 和DeviceIoControl等Win32 API來進行的。這些API其實都對應着驅動程式的一些Dispatch例程。而驅動程式除了DriverEntry以外,主要就是由這些Dispatch例程組成的。例如調用Win32 API CreateFile的時候,作業系統最終轉化為對驅動程式IRP_MJ_CREATE功能代碼所對應的 Dispatch例程的調用,如果驅動程式沒有提供該例程, CreateFile調用就會失敗。

   NT中一些常用的功能代碼和Win32 API的對象關系如下所示。

功能代                                         說明

IRP_MJ_CREATE                                           打開裝置CreateFile

IRP_MJ_CLEANUP                                             在關閉裝置時,取消挂起的I/O請求CloseHandle

IRP_MJ_CLOSE                                                   關閉裝置CloseHandle

IRP_MJ_READ                                                    從裝置獲得資料ReadFile

IRP_MJ_WRITE                                                       向裝置發送資料WriteFile

IRP_MJ_DEVICE_CONTROL                               對使用者模式或核心模式客戶程式可用的控制操作DeviceIoControl

IRP_MJ_INTERNAL_DEVICE_CONTROL            隻對核心模式客戶程式可用的控制操作

IRP_MJ_QUERY_INFORMATION                           得到檔案的長度GetFileLength

IRP_MJ_SET_INFORMATION                                 設定檔案的長度SetFileLength

IRP_MJ_FLUSH_BUFFERS                                  寫輸出緩沖區或丢棄輸入緩沖區

FlushFileBuffers

FlushConsoleInputBuffer

PurgeComm

IRP_MJ_SHUTDOWN                                           系統關閉InitialSystemShutdown

和上面的驅動程式支援的功能代碼相對應,一般的驅動程式看起來就象下面的樣子。

DriverEntry(…) // 驅動程式入口

{

DeviceObject->MajorFunction[IRP_MJ_CREATE] = XXDriverCreateClose; //XX對應的是你自己給你的驅動程式的命名

DeviceObject->MajorFunction[IRP_MJ_CLOSE] = XXDriverCreateClose;

DeviceObject->MajorFunction[IRP_MJ_READ] = XXDriverReadWrite;

DeviceObject->MajorFunction[IRP_MJ_WRITE] = XXDriverReadWrite;

}

XXDriverCreateClose(…) // 對應IRP_MJ_CREATE和IRP_MJ_CLOSE的例程

{

//……….

}

XXDriverDeviceControl(…)// 對應IRP_MJ_DEVICE_CONTROL的例程

{

//……….

}

XXDriverReadWrite(…) // 對應IRP_MJ_READ和IRP_MJ_WRITE的例程

{

//……….

}

   一個驅動程式并不需要支援所有的功能代碼,比如如果一個驅動程式根本就不必要與使用者模式客戶程式互動,那麼就不用支援IRP_MJ_CREATE和IRP_MJ_CLOSE。又如裝置不支援裝置讀寫,就不用支援IRP_MJ_READ和IRP_MJ_WRITE。驅動程式對象是在作業系統啟動驅動程式、在調用驅動程式入口DriverEntry之前就已經建立好了的,并且作為DriverEntry 函數的參數傳遞給驅動程式。如果驅動程式啟動失敗,作業系統将删除該對象。該對象的資料結構如下。注意下表并不是完整地列出了ntddk.h中的DEVICE_OBJECT結構體的所有資料項,這裡僅列出了一般驅動程式可能使用到的資料項。 

Driver對象資料項   說明 

PDEVICE_OBJECT  DeviceObject    由本驅動程式建立的Device對象的連結清單

ULONG Flags     PDRIVER_INITIALIZE DriverInit     驅動程式初始化例程(一般較少用) 

PDRIVER_STARTIO DriverStartIo        StartIo例程入口,一般該例程對低層裝置驅動程式用得較多,高層驅動程式較少使用本例程。

PDRIVER_UNLOAD  DriverUnload   解除安裝驅動程式例程,如果想在控制面版的裝置裡停止該裝置,應該提供本例程。

PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]      驅動程式的Dispatch例程表 

   在上面提到過驅動程式是管理同類型的所有裝置,是以上面的結構中DeviceObject指向的就不是單個的裝置對象,而是一個對象連結清單,這個連結清單的維護在下面介紹Device對象時可以看到。 Device對象與Device Extension 驅動程式在調用IoCreateDevice函數成功後就建立了一個Device 對象。下面對Device對象幾個比較重要的資料做一介紹。

Device對象資料項       說明 

PVOID DeviceExtension   指向Device Extension結構的指針

PDRIVER_OBJECT DriverObject 指向這個裝置的Driver對象的指針,IoCreateDevice會自動填寫本資料。

ULONG Flags  指定這個裝置的緩沖政策

PDEVICE_OBJECT NextDevice  指向屬于這個驅動程式的下一個裝置對象,依靠本資料來維護裝置對象連結清單

CCHAR StackSize   發送到這個裝置的IRP需要的I/O堆棧單元的最小數目,一般對分層驅動程式來說,本資料應該比其下層裝置的大1

ULONG AlignmentRequirement 緩沖區要求的記憶體對齊,一般對分層驅動程式來說,本值應該和其下層裝置的對齊一緻

   Device記錄着裝置的特徵和狀态資訊,對系統上的每個虛拟的、邏輯的和實體的裝置都有一個Device對象。例如對一個硬碟驅動程式,對一個實體硬碟有一個名稱為Partition0的Device對象,對應整個實體磁盤,同時對硬碟的每個分區,也都有一個Device對象,它們的名稱分别為PartitionX(X從1開始,每個分區對應一個數字)。

   Device Extension是連接配接到Device對象的一個很重要的資料結構,它的資料結構是由驅動程式設計者自己來确定的,在調用IoCreateDevice的時候應該指定它的大小,Device Extension其實是由作業系統在非份頁記憶體池中為每個Device 對象配置設定的一塊記憶體。由于驅動程式必須是完全可重入的,是以使用任何全局變量和靜态變量都不是好的辦法,一般來說和裝置有關的任何需要保持的資訊都應該放到Device Extension裡去。

   裝置的緩沖政策也必須提一下,這裡的Flag的緩沖政策主要決定裝置讀寫(功能代碼IRP_MJ_READ和IRP_MJ_WRITE)時候的緩沖政策,另外功能代碼IRP_MJ_DEVICE_CONTROL時候的緩沖政策是由IOCTL控制代碼本身來決定的。兩者不能混為一談。在下面我将專門用一節來讨論I/O的緩沖政策。

I/O請求包(IRP)

在上面的結構裡面已經出現了IRP了,在這裡對它做一說明。在NT中,幾乎所有的I/O都是包驅動的,可以說驅動程式和作業系統其他部份都是通過I/O請求包來進行互動的。我們來看看一個I/O請求的執行過程。

(1) 作業系統的I/O管理器從非分頁記憶體配置設定一個IRP,響應一個I/O請求。基于由客戶指定的I/O函數,I/O管理器将該 IRP傳遞給合适的驅動程式的Dispatch例程。

(2) Dispatch例程檢查請求的參數是否有效,如果有效,驅動程式根據請求的内容進行一系列的操作。否則設定錯誤狀态資訊直接傳回。

(3) 操作完成時,将資料(如果有)和狀态資訊存放到IRP中并傳回給I/O管理器。

(4) I/O管理器對傳回的IRP進行适當的處理後将最後狀态和資料(如果有)傳回給使用者。

   一個IRP的主要資料項如下表所示。

   IRP包括一個IRP頭和一個IRP stack 的區域。由于WDM的模式下都是包驅動的,所裡IRP可以說是一個非常重要的東東。還有那個該死的URB(God damn URB!)

IRP主要資料項 說明 

IO_STATUS_BLOCK IoStatus 存放I/O請求的狀态

PVOID AssociatedIrp.SystemBuffer 如果裝置執行緩沖I/O,則為指向系統空間緩沖區的指針。否則為NULL

PMDL MdlAddress  如果裝置執行直接I/O,指向使用者空間緩沖區的記憶體描述表的指針

PVOID UserBuffer I/O緩沖區的使用者空間位址 

BOOLEAN Cancel    訓示IRP已被取消 

   關于AssociatedIrp.SystemBuffer、MdlAddress和UserBuffer将在下面的I/O緩沖區政策裡面更詳細地讨論。

   NT還有更多其他的對象,例如中斷對象、Controller對象、定時器對象等等,但在我們開發的驅動程式中并沒有用到,是以在這裡不做介紹。

I/O緩沖政策

很明顯的,驅動程式和客戶應用程式經常需要進行資料交換,但我們知道驅動程式和客戶應用程式可能不在同一個位址空間,是以作業系統必須解決兩者之間的資料交換。這就就設計到裝置的I/O緩沖政策。

讀寫請求的I/O緩沖政策

前面說到通過設定Device對象的Flag可以選擇控制處理讀寫請求的I/O緩沖政策。下面對這些緩沖政策分别做一介紹。

1、緩沖I/O(DO_BUFFERED_IO)

在讀寫請求的一開始,I/O管理器檢查使用者緩沖區的可通路性,然後配置設定與調用者的緩沖區一樣大的非分頁池,并把它的位址放在IRP的AssociatedIrp.SystemBuffer域中。驅動程式就利用這個域來進行實際資料的傳輸。

對于IRP_MJ_READ讀請求,I/O管理器還把IRP的UserBuffer域設定成調用者緩沖區的使用者空間位址。當請求完成時,I/O管理器利用這個位址将資料從驅動程式的系統空間拷貝回調用者的緩沖區。對于IRP_MJ_WRITE寫請求,UserBuffer被設定為NULL,并把使用者緩沖區的資料拷貝到系統緩沖區中。

2、 直接I/O(DO_DIRECT_IO)

I/O管理器首先檢查使用者緩沖區的可通路性,并在實體記憶體中鎖定它。然後它為該緩沖區建立一個記憶體描述表(MDL),并把MDL的位址存放在IRP的MdlAddress域中。AssociatedIrp.SystemBuffer和 UserBuffer都被設定為NULL。驅動程式可以調用函數 MmGetSystemAddressForMdl得到使用者緩沖區的系統空間位址,進而進行資料操作。這個函數将調用者的緩沖區映射到非份頁的位址空間。驅動程式完成I/O請求後,系統自動從系統空間解除緩沖區的映射。

3、 這兩種方法都不是

這種情況比較少用,因為這需要驅動程式自己來處理緩沖問題。 I/O管理器僅把調用者緩沖區的使用者空間位址放到IRP的UserBuffer 域中。我們并不推薦這種方式。

IOCTL緩沖區的緩沖政策

IOCTL請求涉及來自調用者的輸入緩沖區和傳回到調用者的輸出緩沖區。為了了解IOCTL請求,我們先來看看WIN32 API DeviceIoControl函數的原型。

BOOL DeviceIoControl (

HANDLE hDevice, // 裝置句柄

DWORD dwIoControlCode, // IOCTL請求操作代碼

LPVOID lpInBuffer, // 輸入緩沖區位址

DWORD nInBufferSize, // 輸入緩沖區大小

LPVOID lpOutBuffer, // 輸出緩沖區位址

DWORD nOutBufferSize, // 輸出緩沖區大小

LPDWORD lpBytesReturned, // 存放傳回位元組數的指針

LPOVERLAPPED lpOverlapped // 用于同步操作的Overlapped結構體指針

);

IOCTL請求有四種緩沖政策,下面一一介紹。

1、 輸入輸出緩沖I/O(METHOD_BUFFERED)

I/O管理器首先配置設定一個非分頁池,它足夠大地存放調用者的輸入或輸出緩沖區(不管哪個更大)。非分頁緩沖區的位址放在IRP的AssociatedIrp.SystemBuffer域中,然後把IOCTL的輸入資料拷貝到這個非份頁緩沖區中,并把IRP的UserBuffer域設定成調用者輸出緩沖區的使用者空間位址。當驅動程式完成IOCTL請求時,I/O管理器将這個非份頁緩沖區中的資料拷貝到調用者的輸出緩沖區。注意這裡同一個非份頁池同時用于輸入和輸出緩沖區,是以驅動程式在向緩沖區寫東西之前應該把輸入的所有資料讀出來。

2、 直接輸入緩沖輸出I/O(METHOD_IN_DIRECT)

I/O管理器首先檢查調用者輸入緩沖區的可通路性,并在實體記憶體中将其鎖定。然後為該輸入緩沖區建立一個MDL,并把指定該MDL的指針存放到IRP的MdlAddress域中。同時,I/O管理器還在非份頁池中配置設定一輸出緩沖區,并把這個緩沖區的位址存放在IRP的AssociatedIrp.SystemBuffer域中,并把IRP的UserBuffer域設定成調用者輸出緩沖區的使用者空間位址。當驅動程式完成IOCTL請求時,I/O管理器将非份頁緩沖區中的資料拷貝到調用者的輸出緩沖區。

3、 緩沖輸入直接輸出I/O(METHOD_OUT_DIRECT)

I/O管理器首先檢查調用者輸出緩沖區的可通路性,并在實體記憶體中将其鎖定。然後為該輸出緩沖區建立一個MDL,并把指定該MDL的指針存放到IRP的MdlAddress域中。同時,I/O管理器還在非份頁池中配置設定一輸入緩沖區,并把這個緩沖區的位址存放在IRP的AssociatedIrp.SystemBuffer域中,同時把調用者使用者輸入緩沖區中的資料拷貝到系統緩沖區中,并把IRP的 UserBuffer域設定為NULL。

4、 上面三種方法都不是(METHOD_NEITHER)

I/O管理器把調用者的輸入緩沖區的位址放到IRP目前I/O堆棧單元的Parameters.Devi ceIoControl.TypeInputBuffer域中,把輸出緩沖區的位址存放到IRP的UserBuffer域中。這兩個位址都是使用者空間位址。

從上面的說明可以看出,在執行緩沖I/O時,I/O管理器将在非份頁池中配置設定記憶體,如果調用者的緩沖區比較大時,配置設定的非份頁池也将比較大。非份頁池是系統比較寶貴的資源,是以,如果調用者的緩沖區比較大時,我們一般采用直接I/O的方式(例如磁盤讀寫請求等),這樣不僅節省系統資源,另一方面由于省去了I/O管理器在系統緩沖區和調用者緩沖區之間的資料拷貝,也提高了效率,這對存在大量資料傳送的驅動程式尤其明顯。

可以注意到DDK中的Samples下,幾乎所有的例程的讀寫請求都是直接I/O的,而對于IOCTL請求則是緩沖區I/O的居多。

開始驅動程式設計

下面的文字是從Microsoft的DDK幫助中節選出來的,它讓我們明白在開始設計驅動程式應該注意些什麼問題,這些都是具有普遍意義的開發準則。應該支援哪些I/O請求在開始寫任何代碼之前,應該首先确定我們的驅動程式應該處理哪些IRP例程。

如果你在設計一個裝置驅動程式,你應該支援和其他相同類型裝置的NT驅動程式相同的IRP_MJ_XXX和IOCTL請求代碼。

如果你是在設計一個中間層NT驅動程式,應該首先确認你下層驅動程式所管理的裝置,因為一個高層的驅動程式必須具有低層驅動程式絕大多數IRP_MJ_XXX例程入口。高層驅動程式在接到I/O 請求時,在确定自身IRP目前堆棧單元參數有效的前提下,設定好IRP中下一個低層驅動程式的堆棧單元,然後再調用IoCallDriver 将請求傳遞給下層驅動程式處理。

一旦決定好了你的驅動程式應該處理哪些IRP_MJ_XXX,就可以開始确定驅動程式應該有多少個Dispatch例程。當然也可以考慮把某些 RP_MJ_XXX處理的例程合并為同一例程處理。例如在ChangerDisk和 VDisk裡,對IRP_MJ_CREATE和IRP_MJ_CLOSE處理的例程就是同一函數。對IRP_MJ_READ和IRP_MJ_WRITE處理的例程也是同一個函數。

應該有多少個Device對象?

一個驅動程式必須為它所管理的每個可能成為I/O請求的目标的實體和邏輯裝置建立一個命名Device對象。一些低層的驅動程式還可能要建立一些不确定數目的Device對象。例如一個硬碟驅動程式必須為每一個實體硬碟建立一個Device對象,同時還必須為每個實體磁盤上的每個邏輯分區建立一個Device對象。

一個高層驅動驅動程式必須為它所代表的虛拟裝置建立一個Device 對象,這樣更高層的驅動程式才能連接配接它們的Device對象到這個驅動程式的Device對象。另外,一個高層驅動程式通常為它低層驅動程式所建立的Device對象建立一系列的虛拟或邏輯Device對象。

盡管你可以分階段來設計你的驅動程式,是以一個處在開發階段的驅動程式不必一開始就建立出所有它将要處理的所有Device對象。但從一開始就确定好你最終要建立的所有Device對象将有助于設計者所要解決的任何同步問題。另外,确定所要建立的Device對象還有助于你定義Device對象的Device Extension的内容和資料結構。

開始驅動程式開發

驅動程式的開發是一個從粗到細逐漸求精的過程。NT DDK的src/ 目錄下有一個龐大的樣闆代碼,幾乎覆寫了所有類型的裝置驅動程式、高層驅動程式和過濾器驅動程式。在開始開發你的驅動程式之前,你應該在這個樣闆庫下面尋找是否有和你所要開發的類似類型的例程。例如我們所開發的驅動程式,雖然DDK對USB描述得不是很詳細,我們還是可以在src/storage/class目錄發現很多和USB裝置有關的驅動程式。下面我們來看開發驅動程式的基本步驟。

最簡的驅動程式架構

1、寫一個DriverEntry例程,在裡面調用IoCreateDevice建立一個Device對象。

2、 寫一個處理IRP_MJ_CREATE請求的Dispatch例程的基本架構 (參見DDK Kernel-Mode Drivers 4.4.3描述的一個DispatchCreate 例程所要完成的最基本工作。當然寫了DispatchCreate例程後,要在DriverEntry例程為IRP_MJ_CREATE初始化例程入口)。如果驅動程式建立了多于一個Device對象,則必須為IRP_MJ_CLOSE 請求寫一個例程,該例程通常情況下可以和DispatchCreate共用一個例程,參見參見DDK Kernel-Mode Drivers 4.4.3。

3、 編譯連接配接你的驅動程式。

用下面的方法來測試你的驅動程式。

首先按上面介紹的方法安裝好驅動程式。

其次我們還得為NT邏輯裝置名稱和目标Device對象名稱之間建立起符号連接配接,我們在前面已經知道Device對象名稱對WIN32使用者模式是不可見的,是不能直接通過API來通路的,WIN 32 API隻能通路NT 邏輯裝置名稱。我們可以通過修改系統資料庫來建立這兩種名稱之間的符号連接配接。運作REGEDT32.EXE在/HKEY_LOCAL_MACHINE/ System/ CurrentControlSet/Control/ Session Manager/ DOS Devices下建立起符号連接配接(這種符号連接配接也可以在驅動程式裡調用函數 IoCreateSymbolicLink來建立)。

重新啟動系統。

編寫一個簡單的測試程式調用WIN32API CreateFile函數以剛才你命名的NT邏輯裝置名打開這個裝置。如果打開成功,那麼你也就成功地寫出了一個最簡單的驅動程式了。

支援更多的裝置I/O請求

例如你的驅動程式可能需要對IRP_MJ_READ請求做出響應(完成後可用WIN32 API ReadFile函數進行測試)。如果你的驅動程式需要能夠手工解除安裝,那麼還必須對IRP_MJ_CLOSE做出響應。為你所需要處理IRP_MJ_XXX寫好處理例程,并在DriverEntry裡面初始化好這些例程入口。

一個低層的驅動程式可能需要最起碼一個StartIo,ISR和DpcForIsr 例程,可能需要一個SynchCritSection例程,如果裝置使用了DMA,那麼可能還需要一個AdapterControl例程。關于這些例程,請參考 DDK相應文檔。

對于高層驅動程式可能需要一個或多個IoCompletion例程,最起碼完成檢查I/O狀态塊然後調用IoCompleteRequest的工作。如果需要,還要對Device Extension資料結構和内容做些修改。

   驅動程式的書寫過程的确是很煩人的,從你開始了解結構開始,你就像掉在一個泥潭裡一樣,無論你如何出拳,發覺總是稀泥一堆。即使你是計算機高手,可以寫三千行源代碼沒犯一個錯,一次寫完,一次就編譯通過(我的一個“同僚”在面試的時候對我們老闆說的,我想他說的對,他沒犯一個錯,而是犯了三十萬零一個半錯,不過,由不的你不信,俺朋友老闆就信世間有這類高手,并供為上賓),你還得了解一些基礎的硬體知識,你還要了解你的驅動程式的裝置的種類,裝置的硬體結構,一些特殊的寄存器,或許一些更基礎的彙程式設計式你也的去跑一遍。還的去看什麼微微有點軟出的什麼鳥DDK(這玩意是最重要的),我看像敵敵畏(啊啊,給我一杯敵敵畏,讓我不用寫程式........哈哈,我的水準直逼牛得滑了,好耍!好耍!)。然後你開始寫了一大堆你自認為不比“葵花寶典”差的驅動程式,嘿嘿,你發覺整個程式就是編譯不過去,就好像你花十塊零五毛RMB買了本“葵花寶典”,終于下定決心按照書的首頁要求的引刀什麼的,可是你發覺費了九馬二騾之力引完了刀,神功依舊未成,點解!你又得去學什麼程式調試,去Debug,俺們稱其為捉蟲,NN的,TMD,蟲沒有捉到,腦袋可腫的大大的。什麼SOFTICE,WINDBG之類,盡是一些系統殺手的角色,一不小心改錯了一個記憶體位址,哼,我CRASH你的機器,你隻好又裝機,又調試,又死翹翹,你不見密西西比河不死心,又重來一遍,如此三番,惹得你無名火起,起身飲茶,又見隔壁部門的老闆正和小蜜在讨論周末去哪裡加班工作,不由的氣不打一處出,大吼一聲“呔,來将何人!灑家張翼德在此!”..........哎,人在老闆下,哪能不幹活,隻有硬頭再上.............無數次的失敗,無數次的徒勞之後,終于讓你的機器跑的歡極了,你不由的小哼一句“對面的小蜜看過來,這裡的男孩很能幹!”

驅動程式真的得看個人造化,若你有張無忌般奇遇,有韋小寶般豔福,有段輿般韌勁,(對了,還要有東方不敗般的勇氣)還有什麼做不了的。哈哈!

繼續閱讀