《電子技術(shù)應(yīng)用》
您所在的位置:首頁 > 嵌入式技術(shù) > 業(yè)界動(dòng)態(tài) > Windows NT 4.0下設(shè)備驅(qū)動(dòng)程序的開發(fā)與應(yīng)用

Windows NT 4.0下設(shè)備驅(qū)動(dòng)程序的開發(fā)與應(yīng)用

2009-06-02
作者:湯冬誼 于恒春

  摘 要: 介紹了Windows NT4.0內(nèi)核模式設(shè)備驅(qū)動(dòng)程序開發(fā)中的一般性過程。通過提供一個(gè)最小化驅(qū)動(dòng)程序的核心代碼,解釋各組成部分的結(jié)構(gòu)功能和使用方法。在實(shí)踐中,結(jié)合自身的開發(fā)需要,可編寫出具有實(shí)用價(jià)值的驅(qū)動(dòng)程序。
  關(guān)鍵詞: Win32子系統(tǒng) 設(shè)備驅(qū)動(dòng) 系統(tǒng)注冊(cè)表 I/O請(qǐng)求包

?

  Windows NT 以其安全、穩(wěn)定及界面友好等特性逐漸成為工業(yè)控制領(lǐng)域的前臺(tái)操作系統(tǒng)。面對(duì)工業(yè)控制中大量采用的串/并行通信及總線控制等技術(shù),要求用戶不斷開發(fā)出滿足自身需要的硬件設(shè)備,同時(shí)又要求用戶應(yīng)用程序與這些硬件設(shè)備進(jìn)行通信,發(fā)送控制命令,讀取狀態(tài)信息等等。Windows NT出于安全性、穩(wěn)定性等考慮,不允許用戶應(yīng)用程序?qū)ξ锢碛布M(jìn)行直接訪問,這就需要使用設(shè)備驅(qū)動(dòng)程序跨越操作系統(tǒng)邊界對(duì)物理硬件進(jìn)行操作,并向上提供客戶應(yīng)用程序控制接口以供調(diào)用。
1 分層結(jié)構(gòu)與設(shè)備驅(qū)動(dòng)程序
  Windows NT分層結(jié)構(gòu)(如圖1所示)包括運(yùn)行于用戶模式及內(nèi)核模式的各種部件,設(shè)備驅(qū)動(dòng)程序在圖1的左下角,處于內(nèi)核模式下I/O管理器之中。


2 驅(qū)動(dòng)程序工作方式
  內(nèi)核模式驅(qū)動(dòng)程序與應(yīng)用程序之間的最大差別之一是驅(qū)動(dòng)程序的控制結(jié)構(gòu)。內(nèi)核模式驅(qū)動(dòng)程序沒有main或WinMain,而是由I/O管理器根據(jù)需要調(diào)用一個(gè)驅(qū)動(dòng)程序例程
  · 驅(qū)動(dòng)程序被裝入時(shí);
  · 驅(qū)動(dòng)程序被卸出或系統(tǒng)關(guān)閉時(shí);
  · 用戶程序發(fā)出I/O系統(tǒng)服務(wù)調(diào)用時(shí);
  · 共享硬件資源對(duì)驅(qū)動(dòng)程序可用時(shí);
  · 設(shè)備操作過程中的任何時(shí)候。
3 初始化過程
3.1 系統(tǒng)注冊(cè)表中有關(guān)設(shè)備驅(qū)動(dòng)程序的項(xiàng)目是系統(tǒng)

  加載設(shè)備驅(qū)動(dòng)程序的入口點(diǎn)
  系統(tǒng)注冊(cè)表中用于系統(tǒng)加載設(shè)備驅(qū)動(dòng)程序的項(xiàng)目如下:
  [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName]
  ″Type″ = dword:00000001
  ″Start″ = dword:00000002
  ″Group″ = ″Extended Base″
  ″ErrorControl″ = dword:00000001
  其中Start含義如下:
  SERVICE_BOOT_START (0×0) 操作系統(tǒng)裝入時(shí)
  SERVICE_SYSTEM_START (0×01) 操作系統(tǒng)初始化時(shí)
  SERVICE_AUTO_START (0×02) 服務(wù)控制管理器啟動(dòng)時(shí)
  SERVICE_DEMAND_START (0×03) 服務(wù)控制管理器手工啟動(dòng)
  SERVICE_DISABLED (0×04) 不啟動(dòng)
  Type含義如下:
  SERVICE_KERNEL_DRIVER (0×1)
  SERVICE_FILE_SYSTEM_DRIVER (0×2)
  SERVICE_ADAPTER (0×4)
  系統(tǒng)注冊(cè)表中用于設(shè)備驅(qū)動(dòng)程序加載后讀取的項(xiàng)目如下:
  [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DriverName\Parameters]
  ″Parameter1″ = dword:00000001
  ″Parameter2″ = dword:00000004
3.2 加載驅(qū)動(dòng)程序的裝入例程
  I/O管理器調(diào)用驅(qū)動(dòng)程序的DriverEntry例程,執(zhí)行初始化。該例程完成:
  · 初始化其它例程的入口;
  · 創(chuàng)建命名設(shè)備對(duì)象;
  · 讀取系統(tǒng)注冊(cè)表中相關(guān)項(xiàng)目并聲明必要的資源;
  · 設(shè)置內(nèi)核驅(qū)動(dòng)程序名與Win32子系統(tǒng)名的聯(lián)接;
  · 創(chuàng)建或初始化任意驅(qū)動(dòng)程序使用的對(duì)象、類型和資源;
  · 返回狀態(tài)值。
  I/O管理器建立與設(shè)備關(guān)聯(lián)的Driver對(duì)象,并將其傳遞給DriverEntry例程。實(shí)際上Driver對(duì)象基本上是一個(gè)目錄,含有指向各個(gè)驅(qū)動(dòng)程序服務(wù)例程函數(shù)的指針,其結(jié)構(gòu)如表1所示。


  I/O管理器能夠找到DriverEntry例程,是因?yàn)樗幸粋€(gè)公認(rèn)的名字,而其他的例程則通過下列兩種方法查找:
  ·在Driver對(duì)象中有明確槽的函數(shù),如DirverObject->DriverUnload;
  ·在Driver對(duì)象的MajorFunction數(shù)組中——Driver對(duì)象的MajorFunction支持兩種類型的功能代碼。一種為標(biāo)準(zhǔn)的功能代碼,如IRP_MJ_CREATE。另一種是用戶自定義的功能代碼,如IRP_MJ_DEVICE_CONTROL。
  所有驅(qū)動(dòng)程序必須支持IRP_MJ_CREATE功能代碼,這是因?yàn)閃in32子系統(tǒng)下的用戶程序調(diào)用CreateFile函數(shù)創(chuàng)建設(shè)備時(shí),產(chǎn)生該功能代碼。如果不處理這個(gè)功能代碼,Win32程序就不能得到設(shè)備句柄。
  用戶自定義的功能代碼IRP_MJ_DEVICE_CONTROL只有在用戶模式下的客戶程序執(zhí)行自定義的功能時(shí)可用。
  NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
  {
  //聲明設(shè)備對(duì)象
  PDEVICE_OBJECT DeviceObject;
  //生成函數(shù)接口指針
  DriverObject->MajorFunction[IRP_MJ_CREATE]=XxSelfDispatch;
  DriverObject->MajorFunction[IRP_MJ_CLOSE]=XxSelfDispatch;
  DriverObject->MajorFunction[IRP_MJ_READ]=XxReadDispatch;
  DriverObject->MajorFunction[IRP_MJ_WRITE]=XxWriteDispatch;
  DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=XxSelfDispatch;
  DriverObject->DriverUnload=XxUnload;
  //生成Windows NT Executive知道的設(shè)備名
  RtlInitUnicodeString(&NtDeviceName, SelfDeviceName);
  //生成自己的設(shè)備
  Status=IoCreateDevice(
  DriverObject, // Driver對(duì)象
    sizeof(SELF_DEVICE_INFO), // Device對(duì)象
    Extension結(jié)構(gòu)大小
    &NtDeviceName,
    DeviceType,
    0,
    FALSE, // 不執(zhí)行
    &DeviceObject //Device對(duì)象指針
  );
  //生成Win32子系統(tǒng)下的用戶程序可識(shí)別的設(shè)備名
  RtlInitUnicodeString(&Win32DeviceName, SelfWin32Name);
  //聯(lián)接內(nèi)部設(shè)備名與Win32子系統(tǒng)下的設(shè)備名
  Status = IoCreateSymbolicLink( &Win32DeviceName, &NtDeviceName );
  //利用RtlQueryRegistryValues函數(shù)讀出注冊(cè)表中Parameters下的參數(shù)值,初始化自己的硬件
  ...
  }
4 驅(qū)動(dòng)程序服務(wù)例程
  驅(qū)動(dòng)程序初始化之后,始終等待發(fā)自用戶的命令或由其它事件源引起的事件。一旦命令或事件發(fā)生,I/O管理器就調(diào)用相應(yīng)的服務(wù)例程提供服務(wù)。而幾乎所有的I/O都是通過I/O請(qǐng)求包(IRP)驅(qū)動(dòng)的。所謂IRP驅(qū)動(dòng),就是I/O管理器負(fù)責(zé)在非分頁的系統(tǒng)內(nèi)存中分配一定空間,當(dāng)接受用戶發(fā)出的命令或由事件引發(fā)后,將工作指令按一定的數(shù)據(jù)結(jié)構(gòu)置于其中,傳遞到驅(qū)動(dòng)程序服務(wù)例程。換言之,IRP包含了驅(qū)動(dòng)程序服務(wù)例程所需要的信息指令。表2、表3為IRP的一些數(shù)據(jù)結(jié)構(gòu)。

?


  同時(shí),I/O管理器和驅(qū)動(dòng)程序都需要在所有時(shí)候知道一個(gè)I/O設(shè)備所進(jìn)行的情況。系統(tǒng)提供Device對(duì)象以滿足此要求。該對(duì)象在DriverEntry例程中生成設(shè)備時(shí)由系統(tǒng)創(chuàng)建后,分配給驅(qū)動(dòng)程序,并在整個(gè)驅(qū)動(dòng)程序生存期內(nèi)有效。當(dāng)I/O管理器調(diào)用驅(qū)動(dòng)程序服務(wù)例程時(shí),傳遞該對(duì)象。表4為Device對(duì)象的外部可見域。


  其中,DeviceExtension域是一個(gè)重要的數(shù)據(jù)結(jié)構(gòu)。它是由I/O管理器創(chuàng)建并自動(dòng)掛接到Device對(duì)象的非分頁池,是保存驅(qū)動(dòng)程序任意全局變量的最好辦法。因?yàn)镈eviceExtension是驅(qū)動(dòng)程序特定的,要自定義它的數(shù)據(jù)結(jié)構(gòu)。
  下面是一個(gè)驅(qū)動(dòng)程序服務(wù)例程利用Device對(duì)象和IRP的片段:
  NTSTATUS XxSelfDispatch(IN PDEVICE_OBJECT pDO, IN PIRP pIrp);
  {
  PLOCAL_DEVICE_INFO pLDI;
  PIO_STACK_LOCATION pIrpStack;
  PULONG pIOBuffer;//得到全局信息
  pLDI = (PSELF_DEVICE_INFO)pDO->DeviceExtension;
  pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
  //得到由用戶應(yīng)用程序發(fā)來的用戶數(shù)據(jù),并在需要時(shí),將結(jié)果通過此變量返回給用戶
  pIOBuffer=(PULONG)pIrp->AssociatedIrp.System Buffer;
  // 由IRP攜帶的信息決定驅(qū)動(dòng)程序的執(zhí)行
  switch (pIrpStack->MajorFunction)
  {
    case IRP_MJ_CREATE:
    case IRP_MJ_CLOSE:
    Status = STATUS_SUCCESS;
    break;
    case IRP_MJ_DEVICE_CONTROL:
    //由Parameters進(jìn)一步解釋控制代碼含義
  switch (pIrpStack->Parameters.DeviceIoControl.IoControlCode)
  {
  case IOCTL_Function1:  //執(zhí)行功能代碼
  Field1 = pLDI->SelfField1;
  ...
  break;
  case IOCTL_Function2:  //執(zhí)行功能代碼
  ...
  break;
  }
  break;
  }
  // 返回I/O操作的狀態(tài)
  pIrp->IoStatus.Status = Status;
  IoCompleteRequest(pIrp, IO_NO_INCREMENT );
  return Status;
  }
5 驅(qū)動(dòng)程序終止例程
  Unload例程負(fù)責(zé)取消由DriverEntry例程所做的任何事情,包括解除屬于該驅(qū)動(dòng)程序的任何硬件資源的分配,以及刪除屬于驅(qū)動(dòng)程序的任何內(nèi)核對(duì)象。通常這僅在系統(tǒng)關(guān)閉時(shí)需要。
  VOID XxUnload(PDRIVER_OBJECT DriverObject)
  {
  PLOCAL_DEVICE_INFO pLDI;
  UNICODE_STRING Win32DeviceName;
  // 得到全局?jǐn)?shù)據(jù),根據(jù)全局?jǐn)?shù)據(jù)進(jìn)行清理工作
  pLDI=(PLOCAL_DEVICE_INFO)DriverObject->Device
  Object->DeviceExtension;
  if (pLDI->Field2 == TRUE)
  {
    ...
  }
  // 刪除分配的設(shè)備名及設(shè)備
  RtlInitUnicodeString(&Win32DeviceName, SelfWin32Name);
  IoDeleteSymbolicLink(&Win32DeviceName);
  IoDeleteDevice(pLDI->DeviceObject);
  }
6 用戶層應(yīng)用程序與驅(qū)動(dòng)程序間的接口
  驅(qū)動(dòng)程序完成后,將在系統(tǒng)重新引導(dǎo)時(shí)裝入并初始化(由DriverEntry例程完成)。此時(shí),驅(qū)動(dòng)程序處于可用狀態(tài),等待用戶層應(yīng)用程序使用。用戶層應(yīng)用程序可以:
  ·打開該設(shè)備文件(由IRP_MJ_CREATE功能代碼完成)
  ·讀出數(shù)據(jù)(由IRP_MJ_READ功能代碼完成)
  ·寫入數(shù)據(jù)(由IRP_MJ_WRITE功能代碼完成)
  ·執(zhí)行用戶自定義的功能代碼(由IRP_MJ_DEVICE_CONTROL功能代碼完成)
  ·關(guān)閉該設(shè)備文件(由IRP_MJ_CLOSE功能代碼完成)
  以下是部分實(shí)現(xiàn)代碼:
  void main()
  {
  HANDLE hndFile; // 由CreateFile得到
  union {
    ULONG LongData;
    USHORT ShortData;
    UCHAR CharData;
    } DataBuffer; //從設(shè)備驅(qū)動(dòng)程序中得到的數(shù)據(jù)
    LONG IoctlCode; //功能代碼
        ULONG DataLength;
        LONG Parameter1;
        //調(diào)用IRP中的IRP _MJ_CREATE功能
        hndFile = CreateFile(
        ″\\\\.\\SelfWin32Name″, // 打開設(shè)備文件″SelfWin32Name″
        GENERIC_READ | GENERIC_WRITE,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL
        );
        if (hndFile == INVALID_HANDLE_VALUE)
        {
        printf(″Unable to open the device.\n″);
        exit(1);
        }
        IoctlCode = IOCTL_Function1; //自定義功能代碼
        Parameter1 = 1;
        DataLength = sizeof(DataBuffer.CharData);
        IoctlResult = DeviceIoControl(
        hndFile, //設(shè)備文件句柄
        IoctlCode,//功能代碼,對(duì)應(yīng)IRP中的Parameter.
        //DeviceIoControl.IoControlCode域&Parameter1,//傳遞到驅(qū)動(dòng)程序的參數(shù)緩沖區(qū),對(duì)應(yīng)
        //IRP中的AssociatedIrp.SystemBuffer
        sizeof(Parameter1), //參數(shù)緩沖區(qū)長度
        &DataBuffer, //從驅(qū)動(dòng)程序傳出的數(shù)據(jù)緩沖區(qū)
        DataLength, //緩沖區(qū)長度
        &ReturnedLength, //返回的實(shí)際緩沖區(qū)長度
        NULL //等待,直到操作完成
    );
    if (!CloseHandle(hndFile)) //關(guān)閉設(shè)備
    {
    printf(″Failed to close device.\n″);
   }
  }
  以上介紹了Windows NT4.0設(shè)備驅(qū)動(dòng)程序開發(fā)中的一般性過程。用戶可利用NT SDK 及DDK開發(fā)工具包,并根據(jù)自身需要,對(duì)以上核心代碼進(jìn)行擴(kuò)充完成所需任務(wù)。
參考文獻(xiàn)
1 Art Baker. Windows NT設(shè)備驅(qū)動(dòng)程序設(shè)計(jì)指南.
2 Microsoft. Windows NT DDK聯(lián)機(jī)手冊(cè).

本站內(nèi)容除特別聲明的原創(chuàng)文章之外,轉(zhuǎn)載內(nèi)容只為傳遞更多信息,并不代表本網(wǎng)站贊同其觀點(diǎn)。轉(zhuǎn)載的所有的文章、圖片、音/視頻文件等資料的版權(quán)歸版權(quán)所有權(quán)人所有。本站采用的非本站原創(chuàng)文章及圖片等內(nèi)容無法一一聯(lián)系確認(rèn)版權(quán)者。如涉及作品內(nèi)容、版權(quán)和其它問題,請(qǐng)及時(shí)通過電子郵件或電話通知我們,以便迅速采取適當(dāng)措施,避免給雙方造成不必要的經(jīng)濟(jì)損失。聯(lián)系電話:010-82306118;郵箱:aet@chinaaet.com。