Red de conocimiento informático - Problemas con los teléfonos móviles - Cómo pasar información del controlador de multiplexación por división de longitud de onda a aplicaciones de capa superior y cómo las aplicaciones de capa superior obtienen información del controlador. Por favor proporcione descripciones en lengua vernácula, ¡sin enlaces!

Cómo pasar información del controlador de multiplexación por división de longitud de onda a aplicaciones de capa superior y cómo las aplicaciones de capa superior obtienen información del controlador. Por favor proporcione descripciones en lengua vernácula, ¡sin enlaces!

Los controladores son una estructura jerárquica. Un dispositivo de hardware no es administrado por un solo controlador. Hay muchos controladores de filtro encima del controlador de dispositivo físico asociado a él. Asociados con estos controladores de filtro hay objetos de dispositivo de filtro para ese objeto de dispositivo físico. Por lo tanto, las solicitudes en modo de usuario deben pasar a través del objeto del dispositivo de filtrado de capa superior capa por capa antes de llegar finalmente al objeto del dispositivo físico. Esto es un poco como el modelo de arquitectura en capas TCP/IP, es decir, los paquetes de la capa de aplicación deben pasar a través de la capa de transporte y la capa de red capa por capa antes de llegar finalmente a la capa física y ser transmitidos a la red. Creo que el propósito de diseñar un modelo de capas de este tipo es facilitar la expansión. Por ejemplo, si desea agregar una nueva operación de administración a un determinado dispositivo, entonces no necesita modificar el controlador de dispositivo físico ni el controlador de filtro existentes. pero simplemente agregue un nuevo objeto de dispositivo de filtro y el controlador correspondiente, agregue nuevas operaciones en él.

Además, es importante tener en cuenta que en gran parte de la programación en modo kernel, el controlador no tiene que estar asociado con un dispositivo físico real; simplemente cree un objeto de dispositivo virtual que no esté asociado con ningún dispositivo físico real; . Esto se debe a que en muchos casos los usuarios escriben controladores simplemente para que su código se ejecute en el estado del núcleo del sistema.

Escribir un controlador WDM

¿Punto de entrada del controlador? -Cuando el controlador se carga en la memoria, se llama a la función DriverEntry (equivalente a la función principal en lenguaje C).

La función DriverEntry tiene dos parámetros. El primer parámetro, PDRIVER_OBJECT?pDriverObj, es un puntero al objeto del controlador correspondiente a este controlador.

Una tarea importante es configurar varios punteros de función para el objeto del controlador, de modo que el objeto del dispositivo asociado con el objeto del controlador pueda pasar el IRP establecido en el objeto del controlador cuando recibe un IRP de la capa superior. El puntero de función encuentra la función correspondiente para procesar:

pDriverObj->DriverUnload?=?DriverUnload;?

pDriverObj->MajorFunction[IRP_MJ_CREATE] =?

pDriverObj->MajorFunction[IRP_MJ_CLOSE]?

pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL]? =?DriverDispatch;?

Otra tarea importante es crear el objeto de dispositivo y asignárselo Crea un enlace simbólico. (Cabe señalar que en un programa WDM típico, la tarea de crear el objeto de dispositivo se completará mediante la función AddDevice, que también se encuentra a través del puntero de función del objeto controlador. En este programa WDM típico, una vez que se instala el nuevo hardware Agregado, el sistema ubicará automáticamente la función AddDevice a través del puntero de función del objeto del controlador y la llamará para crear el objeto del dispositivo. Pero aquí, no estoy escribiendo un controlador para el hardware real, sino simplemente escribiendo un programa en modo kernel. Así que simplemente cree un objeto de dispositivo en la función DriverEntry).

?

IoCreateDevice(?pDriverObj, ?

0,?

&nombre del dispositivo,?

FILE_DEVICE_UNKNOWN,?

FILE_DEVICE_SECURE_OPEN,?

true,?

&pDeviceObj?);?//¿Crear un objeto de dispositivo?

IoCreateSymbolicLink(? &linkName,? &deviceName?); //¿crear enlace simbólico?

También se puede ver en los parámetros anteriores de llamar a la función IoCreateDevice que el objeto del dispositivo y el El objeto controlador está relacionado, esto explica por qué el dispositivo recibe un IRP y la función de controlador correspondiente se ubica a través del puntero de función en el objeto controlador. En cuanto al establecimiento de enlaces simbólicos, es para facilitar la llamada a objetos del dispositivo en modo de usuario.

Como se puede ver en el puntero de función en el objeto de controlador establecido anteriormente, hay dos funciones principales: la función de descarga DriverUnload y la función de envío DriverDispatch. La función driverUnload debe ser fácil de entender. Se llama cuando el controlador se descarga de la memoria. Principalmente realiza algunas tareas, como liberar memoria. En mi programa, todos los IRP se manejan en la misma función, la función DriverDispatch (de hecho, muchos programas WDM hacen esto). Estas dos funciones se presentarán por separado a continuación.

La tarea principal de la función DriverUnload es eliminar los objetos del dispositivo creados y las conexiones simbólicas. Por supuesto, si se asigna otra memoria que debe liberarse en el programa, también se completará aquí.

IoDeleteSymbolicLink(? &linkName?);?

IoDeleteDevice(?pDriverObj->DeviceObject?);?

La función de programación DriverDispatch es la principal responsable de procesar el IRP de capa superior. Es importante mencionar aquí que cada IRP está asociado con dos estructuras de datos, a saber, el IRP en sí y la pila de IRP: estructura IO_STACK_LOCATION. Estas dos estructuras contienen toda la información pasada desde la capa superior al dispositivo objeto de esta capa. Una de las piezas de información más importantes es que la estructura IO_STACK_LOCATION contiene el código de función MajorFunction y MinorFunction del IRP (el código de función del IRP identifica con precisión la solicitud para ese IRP, por ejemplo, el valor de MajorFunction para una solicitud de lectura es IRP_MJ_READ).

El flujo de procesamiento de la función DriverDispatch es generalmente el siguiente: primero obtenga la pila IPR a través del IRP; luego obtenga la MajorFunction del IRP de la pila IRP, juzgue el código MajorFunction y procese en consecuencia; la solicitud se procesa, de acuerdo con la situación específica. Elija completar la solicitud o pasar la solicitud a un objeto de dispositivo de nivel inferior. Después de procesar la solicitud, según la situación, puede optar por completar la solicitud o pasar el IRP al objeto de dispositivo del siguiente nivel.

Obtener la pila IRP es muy simple, simplemente llame a la función IoGetCurrentIrpStackLocation: ?

PIO_STACK_LOCATION?pIrpStack?=?IoGetCurrentIrpStackLocation(?pIrp?);?

Determine el código de la función principal y maneje en consecuencia. Este paso generalmente se realiza con una declaración switch-case:

switch(?pIrpStack->MajorFunction?)?

{? IRP_MJ_CREATE:? DbgPrint(?" Información:?Crear!\n"?) ;? romper;?

caso?IRP_MJ_CLOSE:? IRP_MJ_CLOSE:? Información:?Cerrar!\n"?)

DbgPrint(?);? romper;?

caso?IRP_MJ_DEVICE_CONTROL:? {? switch(?pIrpStack->Parámetros .DeviceIoControl.IoControlCode?)? ?)

información =?23;?

interrupción;?

}?

predeterminado:?

romper; ?

predeterminado:?

romper;?} }?

predeterminado:? /p>

El último paso, si necesita completar la solicitud, debe configurar el campo IoStatus en la estructura IRP y luego llamar a la función IoCompleteRequest:?

pIrp->IoStatus.Status? =?STATUS_SUCCESS;?

pIrp->IoStatus.Information?=?information;?

IoCompleteRequest(pIrp,?IO_NO_INCREMENT);?

Si lo necesita para pasar el IRP al siguiente objeto del dispositivo, inicialice el IRP.Stack para pasar los IRP hacia abajo (puede copiar el IRP.Stack actual en un IRP.Stack inferior), luego llame a IoCompleteRequest(pIrp??IO_NO_INCREMENT;?Stack), luego llame a la función IoCallDriver para El IRP se pasa al IRP subordinado:?

IoCompleteRequest(pIrp,?IO_NO_INCREMENT);?

IoCompleteRequest(pIrp,?IO_NO_INCREMENT);?

IoCopyCurrentIrpStackLocationToNext(pIrp) ;?

status?=?IoCallDriver(pLowerDeviceObj,?pIrp);

Entonces, ¿cómo llamar al controlador desde un programa en modo de usuario?

Para llamar a un controlador desde un programa en modo de usuario, primero debe abrir el dispositivo, que es un objeto de dispositivo creado en el controlador. Esto se logra llamando a la función CreateFile, que abre un archivo con el nombre del archivo como primer argumento.

La función CreateFile se utiliza para abrir un archivo y su primer parámetro es el nombre del archivo. Aquí, pasamos el nombre del dispositivo como primer parámetro, para que la función abra el dispositivo. El nombre del dispositivo aquí es en realidad el nombre del enlace simbólico del objeto del dispositivo establecido en el controlador. Por ejemplo, si el nombre del dispositivo proporcionado en el modo de usuario es "?\\.\MyDevice", el Administrador de E/S convertirá automáticamente "\.\" en "?\" antes de realizar una búsqueda de nombre y convertirá automáticamente "\. ...\" a "\...\" y se convierte en "?\?\MyDevice", que es el nombre del enlace simbólico establecido en el controlador. Una vez encendido el dispositivo, se activa el programa en modo usuario. El ReadFile , Se pueden llamar a las funciones WriteFile y DeviceIoControl para realizar solicitudes al controlador.

Finalmente, damos el código fuente del programa de prueba

Otros

WDM. El programa no se compila en un archivo .exe, sino en un archivo .sys. No se puede compilar directamente usando VC sin configurar el entorno de compilación (es por eso que aparecen cientos de errores). Muchos de estos archivos se encuentran en System32\Drivers. De hecho, el controlador también es un archivo PE. También comienza con DOS.MZ.header, tiene DOS.stub y PE.header completos, y también tiene Import.table y Export. table. - hoho! ... Entonces, ¿cuál es la diferencia entre este y los archivos PE normales?

De hecho, .sys y .exe son archivos PE. la importación de archivos .exe suele ser NTOSKRNL.EXE, y la importación de archivos .exe suele ser KERNEL32.DLL y USER32.DLL

¿De qué sirve conocerlos, ya que normalmente son .sys? no llama a KERNEL32 y USER32.DLL, por lo que no puede llamar a ninguna función de C, C++ o Win32 dentro del controlador del dispositivo, ni puede usar las palabras clave de C++ new y delete, etc. (puede usar malloc y free en su lugar). , pero debes utilizar una gran cantidad de funciones del kernel

.