¿Qué es una operación de bloqueo?

Operación de bloqueo

Operación de bloqueo significa que cuando el dispositivo está en ejecución, si no se pueden obtener recursos, el proceso se suspenderá hasta que se cumplan las condiciones de funcionamiento. Los procesos con operaciones sin bloqueo no se bloquean cuando el dispositivo no se puede operar. El proceso suspendido entra en suspensión y se elimina de la cola de ejecución del programador hasta que se cumpla la condición de espera.

En el controlador de Linux, podemos utilizar colas de espera para implementar operaciones de bloqueo. Las colas de espera aparecieron en el kernel de Linux como una unidad funcional básica hace mucho tiempo. Se basa en la estructura de datos de la cola y está estrechamente integrado con el mecanismo de programación de procesos, y puede usarse para implementar el mecanismo central de notificación de eventos asincrónicos. Las colas de espera se pueden utilizar para sincronizar el acceso a los recursos del sistema.

El dispositivo "globalvar" se define a continuación, que puede ser abierto por múltiples procesos, pero este proceso u otros procesos solo pueden leerlo después de que un proceso haya escrito los datos; de lo contrario, siempre estará bloqueado.

# incluir ltLinux/módulo . h gt; # incluir ltLinux/init . # incluir ltLinux/fs . h gt; # incluye ltASM/semaphore . h gt; Module_License("GPL"); # define MAJOR_NUM 254 static ssize_t global var_write(archivo de estructura *, const char *, size_t, loff_t *); struct file_operatives global var_fops = { leer: global var_read, escribir: globalvar_write, }; static struct semaphore sem static wait_queue_head_t outq static int flag = 0; (void){ int ret; ret = Register_chrdev(MAJOR_NUM, "globalvar", ampglobal var_fops); if (ret) { printk ("el registro globalvar falló"); else { printk ("registro globalvar exitoso"); cerrar con llave. SEM); init_waitqueue_head.

outq); ret return; } static void _ _ salir global var _ exit(void) { int ret = anular el registro _ chrdev(MAJOR _ NUM, "global var"); falló"); } else { printk("la cancelación del registro de var global se realizó correctamente"); } } tamaño estático _ t global var _ read(struct file * filp, char * buf, size _ t len, loft _ t * off) {/ /esperar la adquisición de datos if(Wait_event_interruptible(outq, flag!= 0)){ return-ERESTARTSYS;} if(down_interruptible(amp;SEM)){ return-ERESTARTSYS;} flag = 0 if (copy_to_user (buf, ampglobal_var, sizeof(int))) { up( amp; SEM); retorno - predeterminado } up( amp; SEM return sizeof(int } static ssize_t global var_write(struct file * filp, const char *buf), size_t len , loff_t *off){ if(down_interruptible(amp;SEM)){ return-ERESTARTSYS;} if(copy_from_user(amp;global_var,buf,sizeof( int))) { up(amp;SEM); up(amp;SEM); flag = 1; //Se pueden pasar datos de notificación wake_up_interruptible(;outq); return sizeof(int);Module_initialization (global var_init); Escriba dos programas en modo de usuario para realizar pruebas, uno para evitar la lectura de /dev/globalvar y el otro utilizado para escribir en /dev/globalvar. Solo después de la entrada posterior de /dev/globalvar se puede devolver la lectura anterior.

Lea el programa de la siguiente manera:

# include ltsys/types . # include ltsys/stat h gt # include ltfcntl.h gtmain. (){ int fd, numfd = open("/dev/globalvar ", O_RDWR, S_I rusr | S_IWUSR); if (fd!=-1){ while (1){ leer(FD, ampnum, sizeof (int)) ; //A menos que haya una entrada printf de una variable global ("la variable global es d \ n ", num), el programa bloqueará esta declaración //Si la entrada es 0, salga If(num == 0 ){ close(FD); break; } } } else { printf("Error al abrir el dispositivo\ n "); h > S_IWUSR); if (fd!=-1){ while(1){ printf("Ingrese la variable global: \n "); scanf("d ", ampnum); ( int)); // Si se ingresa 0, salga If(num == 0){ close(FD); break;

Abra dos terminales y ejecute las dos aplicaciones anteriores respectivamente. Se descubre que cuando no se ingresan datos al segundo terminal, el primer terminal no emite (cuadro). Siempre que ingresamos un valor en globalvar en la segunda terminal, la primera terminal generará ese valor.

Respecto al programa anterior, agregamos que si se cambia la función de lectura en el controlador a:

tamaño estático _ t global var _ read(struct file * filp, char * buf , size_t len, loft_t * off) {//Obtener semáforo: if (down_interruptible(amp(amp;SEM)){ return-ERESTARTSYS;}//Esperar a que los datos estén disponibles: if (wait_event_interruptible (outq, flag!= 0 )){ return-ERESTARTSYS; } flag = 0; //Acceso a recursos clave if (copy_to_user (buf, global_var, sizeof(int))){ up(amp;SEM);Return - default }//Liberar semáforo up( ; SEM); return sizeof(int); }

Es decir, intercambia wait_event_interruptible(outq, flag!= 0) y down_intermittent (;Sem), la unidad dejará de funcionar.

De hecho, cuando aparecen dos eventos que pueden bloquearse al mismo tiempo, es decir, se juntan dos eventos de espera o caídas, se vuelve muy peligroso y la posibilidad de un punto muerto es muy alta. En este momento se debe prestar especial atención a su orden. ¡Por supuesto, intenta evitar esto!

Hay otro tema estrechamente relacionado con el bloqueo de dispositivos y el acceso sin bloqueo, es decir, seleccionar y sondear son esencialmente lo mismo. El primero se introdujo en BSD Unix y el segundo se introdujo en el sistema V. La encuesta y la selección se utilizan para consultar el estado del dispositivo y permitir que el programa de usuario sepa si se puede acceder al dispositivo sin bloqueo. Ambos requieren soporte de la función de encuesta en el controlador del dispositivo.

Poll_wait es la API más utilizada en la función Poll del controlador. Su prototipo es el siguiente:

void poll_wait(struct file *filp, wait_queue_heat_t *queue, poll_table *wait);

El trabajo de la función poll_wait es agregar el proceso actual a la lista de espera (poll_table) especificada por el parámetro de espera. Agreguemos una función de sondeo al controlador de globalvar:

static unsigned int global var_poll(struct file *filp, poll_table *wait) { unsigned int mask = 0; poll_wait (filp & outq, etc.); / ¿Están los datos disponibles? if (flag! = 0) { mask | = POLLIN | POLLRDNORM; // Se pueden obtener datos marcados } return mask }

Cabe señalar que la función poll_wait no está bloqueada, poll_wait (filp, ampOutq, esperar) no significa esperar a que el semáforo outq esté disponible. La acción de bloqueo real se completa en la función de selección/encuesta superior. Select/poll llamará a su propia función de soporte de sondeo para cada dispositivo que deba ser monitoreado en un bucle para agregar el proceso actual a la lista de espera de cada dispositivo. Si no hay ningún dispositivo monitoreado listo actualmente, el kernel programará (llamará a la programación) para poner la CPU en un estado de bloqueo. Cuando la programación regrese, realizará un bucle nuevamente para verificar si hay operaciones disponibles, y así sucesivamente, si las hay; El dispositivo está listo, la selección/encuesta volverá inmediatamente.

Escribimos una aplicación en modo usuario para probar el controlador reescrito. En el programa se utilizará la función Select introducida en BSD Unix, y su prototipo es:

int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct time val * time out );

Entre ellos, readfds, writefds y exceptfds son el conjunto de descriptores de archivos monitoreados por select() para lectura, escritura y manejo de excepciones respectivamente. El valor de numfds es el descriptor de archivo con el número más alto que se debe verificar. Añade 1. El parámetro de tiempo de espera es un puntero a un tipo de estructura de tiempo de espera, que permite que select() regrese si no hay ningún descriptor de archivo listo después de esperar el tiempo de espera.

La estructura de datos de struct timeval es:

struct timeval { int tv _ secint tv _ usec}

Además, usaremos la siguiente API:

FD _ zero(FD_set * set) - Borrar el conjunto de descriptores de archivos

Fd_set (int FD, FD_set * set) - Agregar el descriptor de archivos al conjunto de descriptores de archivos FD_clr (int FD, FD_set * set); ) - Borra un descriptor de archivo del conjunto de descriptores de archivo

Fd_isset (int FD, FD_set * set) - Determina si un descriptor de archivo está configurado.

El siguiente programa de prueba en modo de usuario espera a que /dev/globalvar sea legible, pero establece un tiempo de espera de 5 segundos. Si no se pueden leer datos después de más de 5 segundos, se generará "No hay datos en 5 segundos":

# include ltsys/types h gt; # include ltsys/stat . ltstdio .h gt# incluir ltfcntl.h gt# incluir ltsys/time .h gt; # incluir ltsys/types .h gt; # incluir ltunistd.h gtmain() { int fd, numfd_set rfds estructurado fd = open (" /dev/globalvar", O_RDWR, S_I rusr | S_IWUSR); if (fd!=-1) { while (1) {//Compruebe si globalvar tiene entrada FD_zero(;rfds); FD _ SET(FD amp; rfds) ; //Establece el tiempo de espera en 5TV. tv_sec = 5; tv. tv_usec = 0; select(fd 1, amprfds, NULL, NULL amp tv); if (FD_ISSET(fd, amp{ read(fd, ampnum, sizeof(int))); printf("La variable global es d\n ", num); //Si la entrada es 0, salimos If(num == 0 ) { close(FD); break; } } else printf("No hay datos en 5 segundos.\n "} } else { printf("Error al abrir el dispositivo\n "); Abrir Dos terminales ejecutan el programa respectivamente: uno escribe globalvar y el otro lee globalvar usando el programa anterior. Cuando ingresamos un valor a globalvar en el terminal de escritura, el terminal de lectura puede generar inmediatamente el valor cuando no lo ingresamos. 5 segundos, lea. El terminal muestra "No hay datos en 5 segundos".