Controlador de dispositivo LINUX: carrera, bloque, código de reingreso

  

1. Acerca de las carreras

Si dos procesos abren el dispositivo al mismo tiempo para escribir datos, será nuevo cuando el proceso A escriba datos. Aplique una memoria de dispositivo rápida y agregue un nuevo cuanto a la tabla de enlace de datos de desarrollo del dispositivo, de modo que el puntero apunte al nuevo bloque de memoria del dispositivo, y la operación de escritura del proceso B también tenga la misma operación, por lo que si no realiza ninguna modificación del controlador, el dispositivo es de dos. Cuando los procesos se abren al mismo tiempo, los dos procesos tienen la misma información de la tabla de enlace de datos del dispositivo, y se modificarán los mismos datos. Es obvio que los datos creados por la primera operación serán sobrescritos por el proceso posterior, que es el estado de carrera.

Sin embargo, el argumento menos positivo es que esto no ocurre en un solo procesador porque el código que se ejecuta en el kernel no es preventivo, lo que significa que el procesador solo puede procesar un código a la vez. Pero puede suceder un sistema multiprocesador.

LINUX ofrece una solución de carrera:

1) semáforo semáforo, utilizado para la exclusión mutua

#include

Es muy simple, es decir Se debe definir una etiqueta en el bloque de datos que se debe evitar. Cuando se usa un proceso, la bandera se establece en 0, lo que indica que la señal ya está ocupada y ya no se puede usar. Todos los procesos deben primero verificar la etiqueta si desean acceder al bloque de datos. Si es 0, significa que un proceso está ocupando, debe esperar.

Por lo tanto, hay una etiqueta de semáforo en la estructura de datos Scull_Dev de scull0.

Pero el semáforo es manejado por el kernel, porque queremos que el proceso sea un semáforo, si se usa el semáforo, el proceso debe ser entregado al kernel, esperando, no en bucle por nosotros mismos. Revisa y espera los semáforos.

ok, ya que tiene que ser entregado a la administración del kernel, el semáforo debe ser inicializado.

sema_init (&scull_devices.sem, 1); //; Registre un semáforo, inicializado en 1, lo que indica que está disponible.

Cuando necesite obtener un semáforo, llame a down_interruptable (&sem), suelte el semáforo (&sem), active y active el proceso que está esperando el semáforo.

if (down_interruptable (&dev- > sem)) return -ERESTARTSYS; //; Si falla, devuélvelo directamente, no puedo llamar (&sem) //; operaciones de datos ...

arriba (&dev- > sem);

Tenga en cuenta que debe tener cuidado con el uso de semáforos, visible si un proceso contiene un semáforo y falla cuando libera el semáforo Si lo haces, otros procesos se bloquearán.

Además, como el semáforo hará que el proceso se duerma, el semáforo no se puede aplicar en el procesamiento de interrupción.

2) Bloqueo

Como puede ver, utilizando un semáforo, si un proceso contiene un semáforo, otro proceso se pondrá en suspensión. En muchos casos, el proceso no necesita esperar a la suspensión. Por ejemplo, no se permite que el procesamiento de interrupción entre en suspensión, o en algunos casos, es simplemente para comprobar si los datos públicos están ocupados por otros procesos. Si está ocupado, se volverá a probar hasta que se pueda usar. Utilice un spinlock. Por supuesto, cuando se utiliza el bloqueo de giro, el procesador está ocupado, por lo que el bloqueo de giro es adecuado para mantener los datos durante un tiempo relativamente corto, y es absolutamente imposible ir a dormir cuando se mantiene el bloqueo.

#include

spinlock_t my_lock = SPIN_LOCK_UNLOCKED; o spin_lock_init (&my_lock); declara /crea un bloqueo

spin_lock (spinlock_t * my_lock); obtiene el bloqueo proporcionado Si el bloqueo está ocupado, gira hasta que el bloqueo está disponible. Cuando el spin_lock vuelve, la función de llamada mantiene el bloqueo hasta que se libera spin_unlock (spinlock_t * my_lock); el bloqueo se libera

2 Acerca del bloqueo y el no bloqueo < Br>

2.1 Acerca del bloqueo

Hay un problema con la llamada de lectura. Cuando el dispositivo no tiene datos para leer, hay dos maneras de resolverlo. Una es evitar que la falla de lectura directa salte. El segundo es bloquear la operación de lectura, el proceso se pone en suspensión y se activa cuando hay datos.

Discuta el bloqueo de E /S aquí para controlar el sueño y despertarse.

La suspensión se produce cuando un proceso debe esperar un evento, debe suspenderse temporalmente, dejar salir la CPU y despertarse después de que llegue el evento.

Una forma de controlar la suspensión es agregar un proceso a la cola de espera:

1) Primero debe declarar e inicializar una entrada de cola de espera.

#include

wait_queue_head_t my_queue;

init_waitqueue_head (&my_queue);

Si está esperando una cola de espera global estática, puede usar las dos definiciones anteriores en lugar de

DECLARE_WAIT_QUEUE_HEAD ( My_queue); //la declaración estática se inicializará automáticamente en el momento de la compilación

2) usa el elemento de la cola de espera inicializada

call interrupt30_and_on (&my_queue) cuando necesite unirse a la cola de espera del kernel; o sleep_on (&my_queue)

Cuando se requiere la activación, llame a wake_up_interruptible (&my_queue); o wake_up (&my_queue)

3) interruptible_sleep_on () defectuoso

a. La carrera resultante:

Para comprender el posible estado de carrera causado por interruptible_sleep_on () y otras funciones sleep_on, necesita saber más sobre la implementación de interruptible_sleep_on ().

La cola de espera es en realidad una lista de colas. Los datos en la lista enlazada son del tipo wait_queue_t. La interruptible_sleep_on () simplificada interna es probablemente así:

#include

wait_queue_t wait; //; Definir una cola de espera

init_wait_queue_entry (&wait, current); //; Inicializar

current- > state = TASK_INTERRUPTILBE; //; Establecer en estado de suspensión, entrará Sleep

add_wait_queue (&my_queue, &wait); //; Agrega el elemento de la cola de espera que definimos a esta cola de espera

schedule (); //;

remove_wait_queue (&my_queue, &wait); //; El evento llega, schedule () devuelve

La carrera ocurre en current- > state = TASK_INTERRUPTIBLE and schedule () En algunos casos, cuando el controlador está listo para irse a dormir, es decir, cuando se ha establecido el estado actual->, puede haber solo llegada de datos. En este momento, wake_up no activará el proceso que no se ha puesto en suspensión, lo que puede ocasionar El proceso ha estado durmiendo porque no ha respondido al despertar, generando así esta competencia. Esta carrera es muy propenso. La solución es no usar interruptible_sleep_on (), sino usar su implementación interna directamente.

Ejemplo:

#include

wait_queue_t wait; //; Definir una cola de espera

init_wait_queue_entry (&wait, current); //; Inicializar

add_wait_queue (&my_queue, &wait); //; Agregue el elemento de cola de espera que definimos a esta cola de espera

while (1) {

current- > state = TASK_INTERRUPTILBE; //; configurado para dormir, se irá a dormir

if (short_head! = short_tail) break; //; prueba si hay datos llegando, si los hay, saltan

schedule (); //; Real to sleep

}

set_current_state (TASK_RUNNING);

remove_wait_queue (&my_queue, &wait); //; El evento llega, schedule () devuelve

De hecho, podemos hacer estas cosas complicadas sin el kernel. El núcleo define una macro

wait_event_interruptible (wq, condition); o wait_event (wq, Condición) condición es la condición de la prueba

Copyright © Conocimiento de Windows All Rights Reserved