2.7 Структура драйвера
С учетом всего вышеизложенного требуется разработать классовый драйвер, который имеет монолитную структуру. Драйвер будет иметь следующие части:
· Инициализация драйвера
· Обработка запросов PnP и управления питанием
· Обработка запросов записи/чтения данных
· Обработка расширенных запросов
· Выгрузка драйвера
3. ТЕХНОЛОГИЧЕСКИЙ РАЗДЕЛ
3.1 Выбор и обоснование языка и среды программирования
Для разработки драйвера виртуального диска применялся пакет DDK (Driver Development Kit), который включает в себя все необходимые заголовочные файлы и библиотеки. Пакет DDK ориентирован на язык С. Драйвер разрабатывался на Windows 2003 Server DDK, т.к. она включает в себя как средства для разработки драйверов для Windows 2003 Server, так и последние версии средств для разработки драйверов ОС Windows 2000 и XP.
В качестве интегрированной среды разработки применялся Microsoft Visual C++ 6. Он предоставляет удобный интерфейс для написания программного кода на C, и позволяет компилировать его используя библиотеки пакета DDK.
Для отладки работы драйвера применялись программы DebugView и DriverMonitor из пакета NuMega Driver Studio 2.0, которые позволяют просматривать отладочные сообщения драйвера.
3.2 Структуры данных классового драйвераПараметры диска и его состояние хранятся в расширении устройства — структуре DEVICE_EXTENSION. Она всегда доступная через объект-устройство (DEVICE_OBJECT, поле DeviceExtension), она реализует наиболее удобный и рекомендованный Microsoft способ хранения таких данных. Структуру расширения определяет разработчик драйвера. Мы ее определим так:
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT DeviceObject;
PDEVICE_OBJECT LowerDeviceObject;
DEVICE_STATE DevState; // Текущее состояние устройство
IO_REMOVE_LOCK RemoveLock; // Блокировка устройства
ULONG Flags; // Флаг устройства
PUCHAR DiskImage; // Указатель на образ диска
DISK_GEOMETRY DiskGeometry; // Параметры геометрии диска
DISK_INFO DiskRegInfo; // Параметры диска из реестра
UNICODE_STRING SymbolicLink; // Символическая ссылка
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
Поля DeviceObject и LowerDeviceObject облегчают доступ к объектам-устройствам из различных участков драйвера. Они потребуются в дальнейшем. Поле RemoveLock служит для блокировки удаления устройства. DiskImage – содержит указатель на одномерный массив – образ диска. Поле DiskGeometry хранит геометрию диска. DevState содержит текущее состояние устройства. SymbolicLink – символическая ссылка, которая служит для обращения к драйверу.
Поле DiskRegInfo описывает параметры, которые расположены в реестре и используются драйвером для создания диска:
typedef struct _DISK_INFO
{
ULONG DiskSize; // Размер диска в байтах
ULONG RootDirEntries; // Количество элементов в корне
ULONG SectorsPerCluster; // Секторов на кластер
UNICODE_STRING DriveLetter; // Буква диска
} DISK_INFO, *PDISK_INFO;
Когда система производит удаление нижестоящего устройства, она посылает ему PnP-запрос с функцией IRP_MN_REMOVE_DEVICE. Если же в этот момент драйвер чем-то занят, то он должен защитить своё устройство от преждевременной выгрузки из памяти. Для этого в системе предусмотрен объект «блокировка выгрузки» (remove lock). Он может быть «захвачен» и «освобождён». Внутри этого объекта имеется счётчик. Этот счётчик устанавливается в ноль при инициализации объекта (функция IoInitializeRemoveLock). Счётчик увеличивается на единицу при захвате объекта (IoAcquireRemoveLock) перед обработкой запроса и уменьшается при освобождении (IoReleaseRemoveLock).
Обработчик PnP-запроса с функцией IRP_MN_REMOVE_DEVICE гарантированно исполняется в контексте системного процесса. В этом обработчике перед удалением объекта-устройства над блокировкой выполняется действие «освободить и ждать» (IoReleaseRemoveLockAndWait). Оно уменьшает счётчик на единицу и блокирует системный процесс, пока счётчик не станет равным нулю. Следовательно, пока драйвер обрабатывает хотя бы один запрос к устройству, это устройство не будет удалено.
3.4 Процедуры драйвера виртуального диска 3.4.1 Инициализация драйвераНачнём с процедуры, вызываемой при инициализации драйвера — DriverEntry. Она определяется следующим образом:
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
Тип NTSATUS, соответствующий возвращаемому значению, определяет тип ошибки. Многие функции драйвера возвращают значение этого типа. Если работа проходит успешно, результат принимает значение STATUS_SUCCESS.
В данной процедуре необходимо сообщить системе остальные процедуры обработки пакетов IRP:
Поскольку нам нужно сохранить параметры о пути реестра и признак того, что инициализировалось ли само устройство, то в структуре расширения драйвера заведем следующие поля:
DriverObject->MajorFunction[IRP_MJ_CREATE] = RamDskCreateClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = RamDskCreateClose; DriverObject->MajorFunction[IRP_MJ_READ] = RamDskReadWrite; DriverObject->MajorFunction[IRP_MJ_WRITE] = RamDskReadWrite; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = RamDskIOCtl; DriverObject->MajorFunction[IRP_MJ_PNP] = RamDskDispatchPnp; DriverObject->MajorFunction[IRP_MJ_POWER] = RamDskDispatchPower; DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = RamDskDispatchSystemControl; DriverObject->DriverExtension->AddDevice = RamDskAddDevice; DriverObject->DriverUnload = RamDskUnload;
Массив MajorFunction устанавливает соответствие между кодом типа запроса IRP_MJ_NNN и диспетчерской функцией, которая его обрабатывает. Наш драйвер обрабатывает запросы на открытие и закрытие устройства (RamDskCreateClose), чтение и запись (RamDskReadWrite), расширенные запросы обработки IOCTL(RamDskIOCtl), запросы Plug and Play (RamDskDispatchPnp), функции управления питанием (RamDskDispatchPower), запросы от подсистемы инструментария Windows (RamDskDispatchSystemControl).
Сам объект функционального устройства, который будет отвечать за наше устройство, создается далее, когда система вызовет процедуру RamDskAddDevice. Но для его создания необходимо узнать путь реестра с параметрами драйвера, а также необходим признак, что устройство добавлено. Данные параметры будем хранить в структуре расширения драйвера. Поля и размер структуры выбираются разработчиком, для хранения нужных ему данных. Объявим структуру расширения драйвера следующим образом:
typedef struct _RAMDSK_DRIVER_EXTENSION { UNICODE_STRING RegistryPath; ULONG DeviceInitialized; } RAMDSK_DRIVER_EXTENSION, *PRAMDSK_DRIVER_EXTENSION;
В процедуре DriverEntry мы создаем экземпляр структуры и инициализируем ее поля: копируем путь реестра RegistryPath, который передается в параметрах процедуры, и сбрасываем признак DeviceInitialized того, что устройство добавлено.
status = IoAllocateDriverObjectExtension(DriverObject, RAMDSK_DRIVER_EXTENSION_KEY, sizeof(RAMDSK_DRIVER_EXTENSION), &driverExtension); // Сохраним путь реестра driverExtension->RegistryPath.Length = RegistryPath->Length; driverExtension->RegistryPath.MaximumLength = RegistryPath->MaximumLength + sizeof(UNICODE_NULL); driverExtension->RegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool, driverExtension->RegistryPath.MaximumLength, RAMDSK_TAG_GENERAL); RtlCopyUnicodeString( &(driverExtension->RegistryPath), RegistryPath); driverExtension->DeviceInitialized = FALSE;
Процедура RamDskAddDevice будет вызвана, когда будет обнаружено устройство. Ее действие заключается в создании объекта устройства диска и создании символических ссылок для него.
Проверим, может быть уже создан объект устройство, а процедура RamDskAddDevice вызывается повторно. Для этого проанализируем признак DeviceInitialized расширения драйвера:
// Может присутствоать только одно устройство, если повторно вызывается запрос
// AddDevice для следующего устройства, отклоним запрос
if ( driverExtension->DeviceInitialized == TRUE )
{
DBGPRINT( DBG_COMP_INIT, DBG_LEVEL_ERROR, ("Device exists\n") );
return STATUS_DEVICE_ALREADY_ATTACHED;
}
Если процедура RamDskAddDevice вызвана в первый раз, создаем объект устройство с типом дисковый накопитель (FILE_DEVICE_DISK) и инициализируем расширение устройства DEVICE_EXTENSION:
status = IoCreateDevice(
DriverObject,
sizeof(DEVICE_EXTENSION),
&uniDeviceName,
FILE_DEVICE_DISK ,
(FILE_DEVICE_SECURE_OPEN),
FALSE,
&functionDeviceObject);
Если создание прошло успешно, мы читаем параметры диска из реестра и устанавливаем начальное состояние устройства остановлено (STOPPED) в расширении устройства, а также устанавливаем флаги сигнализирующие о том, что мы используем прямой метод передачи данных (DO_DIRECT_IO) и процедуры драйвера находятся в страничной памяти(DO_POWER_PAGABLE):
RamDskQueryDiskRegParameters(&driverExtension->RegistryPath,&devExt->DiskRegInfo);
devExt->DevState = STOPPED; // состояние остановлен
// Установим флаги
functionDeviceObject->Flags |= DO_POWER_PAGABLE;
functionDeviceObject->Flags |= DO_DIRECT_IO;
// Резервируем память под образ диска
devExt->DiskImage =
ExAllocatePoolWithTag ( NonPagedPool,
devExt->DiskRegInfo.DiskSize,
RAMDSK_TAG_DISK ) ;
Последней операцией мы резервируем память в нестраничном пуле под образ диска(размер образа devExt->DiskRegInfo.DiskSize байт).
Т.к. выделенная память может содержать произвольные данные, необходимо записать метаданные файловой системы и подсчитать геометрию диска, что делается вызовом
RamDskFormatDisk( functionDeviceObject );
Создаем символическую ссылку, которая ассоциирует с нашим драйвером указанную в параметрах букву диска и имеет формат «\DosDevices\X:», где «Х» - буква диска.
RtlInitUnicodeString( &uniWin32Name, DOS_DEVICE_NAME );
devExt->SymbolicLink.MaximumLength = DOS_DEVNAME_LENGTH;
devExt->SymbolicLink.Length = uniWin32Name.Length;
RtlCopyUnicodeString( &(devExt->SymbolicLink), &uniWin32Name );
RtlAppendUnicodeStringToString(&(devExt->SymbolicLink),&(devExt->DiskRegInfo.DriveLetter));
// Создаем символьную ссылку в пространстве имен Win32
status = IoCreateSymbolicLink( &devExt->SymbolicLink, &uniDeviceName );
Если символическая ссылка создана без ошибок, то установим в расширении устройства флаг, что символическая ссылка создана и устройство подключается к стеку устройств:. При выгрузке драйвера если флаг установлен будет удаляться и символическая ссылка драйвера:
devExt->Flags |= FLAG_LINK_CREATED;
devExt->LowerDeviceObject = // подключение к стеку устройств
IoAttachDeviceToDeviceStack( functionDeviceObject, PhysicalDeviceObject );
// Сбросим флаг DO_DEVICE_INITIALIZING
functionDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
// устройство добавлено успешно
driverExtension->DeviceInitialized = TRUE;
Последней операцией устанавливается признак DeviceInitialized расширения драйвера, что устройство добавлено.
3.4.2 Обработка запросов записи/чтенияКогда пользовательское приложение или компонент ядра пытается открыть драйвер виртуального диска для записи или чтения, то диспетчер ввода вывода формирует запросы IRP с кодом IRP_MJ_CREATE. При закрытии обрабатывается запрос IRP_MJ_CLOSE. Особых действий в обработке этих пакетов не требуется, поэтому драйвер возвращает успешный статус завершения. В данном драйвере оба пакета обрабатываются в одной процедуре RamDskCreateClose:
switch ( irpStack->MajorFunction )
{
case IRP_MJ_CREATE:
DBGPRINT( DBG_COMP_INIT, DBG_LEVEL_INFO, ("IRP_MJ_CREATE (%p)\n", Irp) );
COMPLETE_REQUEST( Irp, status, 0 );
break;
case IRP_MJ_CLOSE:
DBGPRINT( DBG_COMP_INIT, DBG_LEVEL_INFO, ("IRP_MJ_CLOSE (%p)\n", Irp) );
COMPLETE_REQUEST( Irp, status, 0 );
break;
default:
status = STATUS_NOT_IMPLEMENTED;
COMPLETE_REQUEST( Irp, status, 0 );
ASSERTMSG("BUG: we should never get here", 0);
break;
} // switch
В качестве проверки, если мы получили пакет с другим кодом, то возвратим ошибочный статус.
Обработка запросов на запись или чтение , также реализуется в одной процедуре, т.к. отличия в действиях заключаются в направлении копирования данных: из буфера в образ диска или наоборот. Для выполнения запроса, требуется, чтобы виртуальный диск находился в состоянии WORKING:
if ( devExt->DevState != WORKING )
{
// Устройство не готово или удалено, отменить любые запросы
DBGPRINT( DBG_COMP_READ, DBG_LEVEL_WARN, ("Device not ready\n" ) );
status = STATUS_INVALID_DEVICE_STATE;
COMPLETE_REQUEST( Irp, status, information );
}
Далее требуется проверить, что переданные параметры(начальное смещение Parameters.Read.ByteOffset и количество байт Parameters.Read.Length) не выходят за границы нашего диска, чтобы обеспечить корректность операции чтения/записи. Дополнительно количество байт должно быть кратно размеру сектора на диске:
if (RtlLargeIntegerGreaterThan(
RtlLargeIntegerAdd(
irpStack->Parameters.Read.ByteOffset,
RtlConvertUlongToLargeInteger(irpStack->Parameters.Read.Length)),
RtlConvertUlongToLargeInteger(devExt->DiskRegInfo.DiskSize)) ||
(irpStack->Parameters.Read.Length & (devExt->DiskGeometry.BytesPerSector - 1)))
{
DBGPRINT( DBG_COMP_READ, DBG_LEVEL_ERROR,
(
"Error invalid parameter\n"
"ByteOffset: %x\n"
"Length: %d\n"
"Operation: %x\n",
irpStack->Parameters.Read.ByteOffset,
irpStack->Parameters.Read.Length,
irpStack->MajorFunction
));
status = STATUS_INVALID_PARAMETER;
COMPLETE_REQUEST( Irp, status, information );
IoReleaseRemoveLock(&devExt->RemoveLock, Irp);
return status;
}
Если какой-либо из параметров не верен, то статус обработки запроса будет равен STATUS_INVALID_PARAMETER (неверные параметры).
Драйвер использует прямой метод передачи буфера данных, нам передается MDL список для буфера пользователя в параметре Irp->MdlAddress, который мы отображаем в адресное пространство ядра с помощью функции MmGetSystemAddressForMdlSafe:
ASSERT ( Irp->MdlAddress != NULL );
currentAddress = MmGetSystemAddressForMdlSafe(Irp->MdlAddress,NormalPagePriority);
if ( currentAddress == NULL )
{
status = STATUS_INSUFFICIENT_RESOURCES;
COMPLETE_REQUEST( Irp, status, information );
IoReleaseRemoveLock(&devExt->RemoveLock, Irp);
DBGPRINT( DBG_COMP_READ, DBG_LEVEL_ERROR, ("Unable to get the system-space virtual address\n" ) );
return status;}
Когда адрес получен адрес буфера (currentAddress), можно произвести копирование данных с помощью функции RtlMoveMemory
information = irpStack->Parameters.Read.Length;
switch (irpStack->MajorFunction)
{
case IRP_MJ_READ:
RtlMoveMemory(
currentAddress,
devExt->DiskImage + irpStack->Parameters.Read.ByteOffset.LowPart,
irpStack->Parameters.Read.Length);
break;
case IRP_MJ_WRITE:
RtlMoveMemory(
devExt->DiskImage + irpStack->Parameters.Read.ByteOffset.LowPart,
currentAddress, irpStack->Parameters.Read.Length);
break;
default:
information = 0;
break;
}
status = STATUS_SUCCESS;
COMPLETE_REQUEST( Irp, status, information );
При этом поле information содержит количество байт, которые были записаны/прочитаны.
Поскольку наш драйвер обращается с виртуальным диском, при следующих запросах выполнять каких-либо действий не требуется, просто сообщить, что запрос успешно обработан. Это запросы:
case IOCTL_DISK_IS_WRITABLE://проверка можно ли на диск записывать данные
{
DBGPRINT( DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_DISK_IS_WRITABLE \n" ) );
status = STATUS_SUCCESS;
break;
}
case IOCTL_MOUNTMGR_QUERY_POINTS: // сообщить о символической ссылке для тома
{
DBGPRINT( DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_MOUNTMGR_QUERY_POINTS\n" ) );
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
case IOCTL_DISK_FORMAT_TRACKS: //Форматировать дорожки
{
DBGPRINT( DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_DISK_FORMAT_TRACKS\n" ) );
status = STATUS_SUCCESS ;
break;
}
case IOCTL_DISK_MEDIA_REMOVAL: //блокировать извлечение носителя
{
DBGPRINT( DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_DISK_MEDIA_REMOVAL\n" ) );
status = STATUS_SUCCESS ;
break;
}
case IOCTL_DISK_VERIFY: //провреить данные
{
PVERIFY_INFORMATION verifyInformation;
DBGPRINT( DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_DISK_VERIFY\n" ) );
status = STATUS_SUCCESS ;
break;
}
case IOCTL_DISK_CHECK_VERIFY:// проверить, сменился ли носитель
{
DBGPRINT( DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_DISK_CHECK_VERIFY\n" ) );
status = STATUS_SUCCESS ;
break;
}
Запрос IOCTL_DISK_GET_PARTITION_INFO, требует сообщить информацию о разделах на диске. На рамдиске имеется один раздел. Результатом обработки запроса будет структура PARTITION_INFORMATION
typedef struct _PARTITION_INFORMATION
{
LARGE_INTEGER ; //смещение, с которого начинается раздел
LARGE_INTEGER ;//размер раздела
DWORD ; //скрытых секторов
DWORD ; //порядковый номер раздела
BYTE ; //тип раздела
BOOLEAN ; //TRUE - раздел является загрузочным
BOOLEAN ; //распознан ли раздел
BOOLEAN ; //TRUE – изменились параметры раздела
}
Тип раздела для виртуального диска может быть PARTITION_FAT_12 или PARTITION_FAT_16(он определяется при инициализации и хранится в расширении драйвера). Остальные поля заполняются следующим образом:
case IOCTL_DISK_GET_PARTITION_INFO:
{
DBGPRINT( DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("IOCTL_DISK_GET_PARTITION_INFO \n" ) );
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <sizeof(PARTITION_INFORMATION))
{
DBGPRINT( DBG_COMP_IOCTL, DBG_LEVEL_INFO, ("Output buffer too small... \n" ) );
status = STATUS_BUFFER_TOO_SMALL; // Нужен буфер больше
information = sizeof(PARTITION_INFORMATION);
}
else
{
PPARTITION_INFORMATION outputBuffer;
outputBuffer = ( PPARTITION_INFORMATION )Irp->AssociatedIrp.SystemBuffer;
outputBuffer->PartitionType = (UCHAR)devExt->DiskRegInfo.PartitionType;
outputBuffer->BootIndicator = FALSE;
outputBuffer->RecognizedPartition = FALSE
outputBuffer->RewritePartition = FALSE;
outputBuffer->StartingOffset = RtlConvertUlongToLargeInteger(0);
outputBuffer->PartitionLength = RtlConvertUlongToLargeInteger(devExt->DiskRegInfo.DiskSize);
outputBuffer->HiddenSectors = (ULONG) (1L);
outputBuffer->PartitionNumber = (ULONG) (1L);
status = STATUS_SUCCESS;
information = sizeof( PARTITION_INFORMATION );
}
break;
}
Запросы IOCTL_DISK_GET_MEDIA_TYPES, IOCTL_DISK_GET_DRIVE _GEOMETRY нужны для поучения информации о геометрии диска, для этого надо заполнить структуру DISK_GEOMETRY описанную в разделе 2.7.:
typedef struct _DISK_GEOMETRY {
LARGE_INTEGER ; //количество цилиндров
MEDIA_TYPE ; //тип носителя
ULONG ; //количество дорожек на цилиндр
ULONG ; //количество секторов на дорожку
ULONG ; //размер сектора в байтах
} DISK_GEOMETRY, *PDISK_GEOMETRY;
Все остальные параметры заранее рассчитаны и определены при инициализации, и нужно только эту структуру скопировать из расширения устройства:
PDISK_GEOMETRY outputBuffer;
outputBuffer = ( PDISK_GEOMETRY ) Irp->AssociatedIrp.SystemBuffer;
RtlCopyMemory( outputBuffer, &(devExt->DiskGeometry), sizeof(DISK_GEOMETRY) );
status = STATUS_SUCCESS;
DBGPRINT(DBG_COMP_IOCTL,DBG_LEVEL_INFO,("IOCTL_DISK_GET_DRIVE_GEOMETRY-OK!\n"));
information = sizeof( DISK_GEOMETRY );
3.4.4 Обработка запросов Plug and PlayПри запуске устройства мы получаем IRP пакет с кодом IRP_MN_START_DEVICE:
case IRP_MN_START_DEVICE:
{
KeInitializeEvent( &event, NotificationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext( Irp );
IoSetCompletionRoutine( Irp, (PIO_COMPLETION_ROUTINE) RamDskIoCompletionRoutine,
(PVOID) &event, TRUE, TRUE, TRUE );
status = IoCallDriver( devExt->LowerDeviceObject, Irp );
if (status == STATUS_PENDING)
{
KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, NULL );
}
if ( NT_SUCCESS(status) )
{
// успешно запущен нижний драйвер
devExt->DevState = WORKING;
}
COMPLETE_REQUEST( Irp, status, 0 );
break;
}
Мы пересылаем пакет драйверу, который находится ниже при помощи вызова функции IoCallDriver. Если нижние драйвер находится в состоянии обработки (STATUS_PENDING), то просто усыплем наш драйвер с помощью функции KeWaitForSingleObject и просыпаемся по событию завершения операции нижним драйвером.
При остановке устройства(IRP_MN_STOP_DEVICE), просто пересылаем запрос нижнему драйверу и устанавливаем состояние устройства STOPPED:
case IRP_MN_STOP_DEVICE:
{
devExt->DevState = STOPPED;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation( Irp );
status = IoCallDriver( devExt->LowerDeviceObject, Irp );
break;
}
При обработке запросов IRP_MN_SURPRISE_REMOVAL, IRP_MN_QUERY_ REMOVE_DEVICE, IRP_MN_QUERY_STOP_DEVICE, производятся аналогичные действия, с установкой соответствующего статуса:
case IRP_MN_QUERY_STOP_DEVICE:
{
devExt->DevState = PENDINGSTOP;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation( Irp );
status = IoCallDriver( devExt->LowerDeviceObject, Irp );
break;
}
case IRP_MN_STOP_DEVICE:
{
devExt->DevState = STOPPED;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation( Irp );
status = IoCallDriver( devExt->LowerDeviceObject, Irp );
break;
}
case IRP_MN_QUERY_REMOVE_DEVICE:
{
devExt->DevState = PENDINGREMOVE;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation( Irp );
status = IoCallDriver( devExt->LowerDeviceObject, Irp );
break;
}
case IRP_MN_SURPRISE_REMOVAL:
{
devExt->DevState = SURPRISEREMOVED;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation( Irp );
status = IoCallDriver( devExt->LowerDeviceObject, Irp );
break;
}
При удалении устройства IRP_MN_REMOVE_DEVICE, мы вызываем функцию RamDskRemoveDevice, которая освобождает выделенную при инициализации память, символическую ссылку (она будет описана далее):
case IRP_MN_REMOVE_DEVICE:
{
RamDskRemoveDevice( DeviceObject, Irp );
lockHeld = FALSE;
break;
}
Выгрузка драйвера состоит из двух частей: удаление объекта устройства и выгрузка самого драйвера.
При удалении объекта устройства, менеджер ввода вывода посылает IRP пакет IRP_MN_REMOVE_DEVICE, тогда вызывается процедура RamDskRemove Device. IRP пакет передаем дальше драйверу нижнего уровня с помощью функции IoCallDriver. Статус драйвера устанавливается в состояние устройство удалено, чтобы новые запросы не могли быть выполнены. С помощью функции IoReleaseRemoveLockAndWait ждем пока текущие запросы не обработаются.
Irp->IoStatus.Status = STATUS_SUCCESS;
IoSkipCurrentIrpStackLocation( Irp );
status = IoCallDriver( devExt->LowerDeviceObject, Irp );
devExt->DevState = REMOVED;
IoReleaseRemoveLockAndWait(&devExt->RemoveLock, Irp);
driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject,
RAMDSK_DRIVER_EXTENSION_KEY);
ASSERT ( driverExtension != NULL );
driverExtension->DeviceInitialized = FALSE;
RamDskCleanUp( DeviceObject );
Перед удалением расширения объекта устройства в процедуре RamDskCleanUp освобождается память под образ диска, удаляется символическая ссылка и сам функциональный объект устройства. Также далее сбрасывается признак DeviceInitialized инициализации устройства.
При выгрузке самого драйвера вызывается процедура RamDskUnload, которая должна обязательно присутствовать, чтобы драйвер мог выгружаться.
VOID RamDskUnload(IN PDRIVER_OBJECT DriverObject)
{
PRAMDSK_DRIVER_EXTENSION driverExtension;
DBGPRINT( DBG_COMP_INIT, DBG_LEVEL_INFO, ("Driver Unload\n") );
ASSERT(DriverObject->DeviceObject == NULL);
driverExtension = IoGetDriverObjectExtension(DriverObject,RAMDSK_DRIVER_EXTENSION_KEY);
ASSERT ( driverExtension != NULL );
if ( driverExtension->RegistryPath.Buffer )
{
ExFreePool( driverExtension->RegistryPath.Buffer );
}
return;
} // Конец RamDskUnload()
В данной процедуре удаляется уже расширение драйвера.
3.5 Программа настройки параметров виртуального дискаЧтобы предоставить пользователю возможность настроить параметры виртуального диска разработана программа RamDskConfig.exe. Общий вид программы изображен на рис. 4
Рис. 4 Программа настройки параметров виртуального диска
Программа позволят назначать для рамдиска букву диска и его размер. Чтобы обеспечить корректность работы драйвера, выбор буквы диска осуществляет из имеющихся букв, которые не назначены другим дискам в системе.
Размер диска также задается списком предопределенных значений, но пользователь может сам ввести требуемый размер диска в поле ввода.
Для сохранения изменений, пользователю требуется нажать кнопку «ОК». При этом происходит рестарт драйвера с новыми параметрами. Чтобы не сохранять изменения, нужно нажать кнопку «Отмена».
3.6 Установка драйвераУстановка драйвера производится с использованием установочного файла RAMDsk.inf, поставляемого вместе с драйвером. Для установки фильтра необходимо выполнить следующие действия:
1. Откройте окно Мастера оборудования (например, из вкладки Оборудование окна Свойства системы).
2. Выберите пункт Добавить/провести диагностику устройства
3. В появившемся списке устройств выберите Добавление нового устройства
4. Далее укажите Нет, выбрать оборудование из списка
5. Укажите тип устройства Другие устройства
6. Нажмите на кнопку Установить с диска и вставьте дискету с файлами драйвера-фильтра в дисковод.
7. После этого в появившемся окне выберите путь к дискете и нажмите ОК.
8. Нажмите кнопку Далее для начала установки.
9. Если в ходе установки система выдаст предупреждение о том, что устанавливаемый драйвер не имеет цифровой подписи, нажмите кнопку Все равно продолжить.
10. После установки нажмите кнопку Готово для закрытия мастера установки
11. При необходимости настройки драйвера скопируйте файл RamDskConfig.exe на диск.
4. ЭКСПЕРИМЕНТАЛЬНО-ИССЛЕДОВАТЕЛЬСКИЙ РАЗДЕЛ
4.1 Описание экспериментов
Для исследования временных характеристик использовалась программа HD TACH 2.70, исследовались следующие характеристики:
· скорость последовательного чтения
· время доступа при случайном обращении
· загрузка процессора при операциях записи/чтения
Эксперименты проводились на компьютере Intel Pentium III 566 МГц, 196 Мб ОП, Windows 2000 Server, размер рамдиска 32 Мбайта.
4.2 Результаты экспериментовВ таблице 4 показаны результаты тестирования для рамдиска, для сравнения приведены результаты для жесткого диска Segate Barracuda 4 7200
Таблица 4 Результаты тестирования
Характеристика | Рамдиск | Жесткий диск |
Скорость последовательного чтения Мб/с | 189,5 | 40,0 |
Время доступа при случайном обращении, мс | 0,0 | 15,2 |
Загрузка ЦП | 100% | 10% |
Как и ожидалось, для виртуального диска характерен мгновенный доступ к любому сектору на диске, в отличие от жестких дисков, время доступа которых зависит от быстродействия механики.
Скорость передачи данных для рамдиска также ограничивается скоростью обмена данными в памяти (для данной платформы обмен данными с оперативной памятью равен 250 Мб/с).
Платой за такие высокие показатели является полная загрузка процессора, т.к. он постоянно работает с оперативной памятью.
ЗАКЛЮЧЕНИЕ
В данной работе были исследованы вопросы, связанные с разработкой драйверов для устройств хранения, способами хранения данных, управление работой устройства с помощью команд PnP. Разработан классовый драйвер виртуального диска, который полностью удовлетворяет всем указанным требованиям.
Работа драйвера осуществляется корректно, рамдиск обеспечивает полную функциональность обычного жесткого диска. На данном диске можно создавать, читать, записывать, удалять файлы.
Драйвер не влияет на работу других устройств, и не приводит к ощутимым задержкам с работе системы.
Параметры диска изменяются динамически и без необходимости перезагрузки системы.
Также драйвер в ходе тестирования показал хорошие результаты производительности, которые ограничены характеристиками оперативной памяти компьютера.
СПИСОК ИСПОЛЬЗОВАННОЙ ЛИТЕРАТУРЫ
1. Oney W. Programming the Microsoft Windows Driver Model. — Redmond, Washington: Microsoft Press., 1999.
2. В.Г.Олифер, Н.А.Олифер Сетевые операционные системы. Учебник для ВУЗов – СПб.: Издательство «ПИТЕР», 2004 – 544 с.: ил.
3. Солдатов В.П. Программирование драйверов Windows. Изд. 2-е, перераб. и доп.-М.: ООО «Бином-Пресс», 2004.-480с., ил.
4. Microsoft Windows XP DDK Documentation.
... загрузочные сектора жестких дисков. Физическое и логическое подключение жестких дисков Какие же необходимо подключить разъемы и установить перемычки и другие операции при физической установке накопителя на жестких дисках? Это - интерфейсный шлейф, кабель питания, перемычки выбора статуса логического устройства и, возможно, индикатор состояния устройства (обращения к устройству), а также ...
... случае драйвера устройства, поддерживающего только один тип носителя (например драйвер RAM-диска), содержимое BPB может быть закодировано в теле самого драйвера. К несчастью, при работе с реальными дисками, включая жесткие диски, не все так просто и драйвер обязан определять содержимое BPB. Как правило, BPB является частью блока начальной загрузки, как показано на рис.6-7. В этом ...
аучного цикла является отсутствие возможности реальной постановки учебного, и лабораторного эксперимента. Хотя в настоящее время имеются разработки виртуальных лабораторных практикумов, однако окончательно решение проблемы требует пристального внимания специалистов различных профилей, в том числе и психолого-педагогического. 1.2 Роль технологии виртуальных приборов обучения в техническом вузе ...
... ) ФАКУЛЬТЕТ ЭЛЕКТРОНИКИ И ПРИБОРОСТРОЕНИЯ КАФЕДРА КЭС группа Э-92 ДАТА ЗАЩИТЫ апреля 1997 г. Отзыв на дипломную работу студента гр.Э-92 Сорокина Ю.В. “Разработка программы контроллера автоматически связываемых объектов для управления конструкторской документацией в среде Windows 95/NT”. Широкое использование вычислительной техники в народном хозяйстве требует увеличения производства и ...
0 комментариев