2012-03-06 9 views
23

Estoy aprendiendo acerca de las funciones de bloqueo de E/S para escribir el controlador del dispositivo Linux y me pregunto cuál es el uso de ERESTARTSYS. Considere lo siguiente:¿Qué usa ERESTARTSYS al escribir el controlador de Linux?

Variable global:

wait_queue_head_t my_wait_q_head; 
int read_avail = 0; 


Device_Init():

init_waitqueue_head(&my_wait_q_head);


device_read():

printk("I'm inside driver read!\n"); 
wait_event_interruptible(&my_wait_q_head, read_avail != 0); 
printk("I'm awaken!\n"); 


DEVICE_WRITE():

read_avail = 1; 
wake_up_interruptible(&my_wait_q_head); 


Cuando llamo a la read() desde el espacio de usuario, el símbolo del sistema cuelgue hasta que yo llamo el write() como se esperaba. Los mensajes printk aparecen en consecuencia también en dmesg. Sin embargo, estoy viendo algunos de los conductores escribe así:

Otra versión de device_read():

printk("I'm inside driver read!\n"); 
if(wait_event_interruptible(&my_wait_q_head, read_avail != 0))  
{return -ERESTARTSYS;} 
printk("I'm awaken!\n"); 

Probé la segunda versión de device_read() utilizando el mismo método en el espacio de usuario, y el resultado es exactamente lo mismo, entonces, ¿de qué sirve ERESTARTSYS?

p/s: He leído el libro Linux controlador de dispositivo en esto, pero yo no lo entiendo, alguien puede dar un ejemplo a eleborate ?:

Una vez que tengamos pasado esa llamada, algo nos ha despertado, pero no sabemos . Una posibilidad es que el proceso recibió una señal. La instrucción if que contiene la llamada wait_event_interruptible marca para este caso. Esta declaración garantiza la reacción adecuada y esperada a las señales, que podrían haber sido responsables de reactivar el proceso (ya que estábamos en una suspensión interrumpible). Si ha llegado una señal y no ha sido bloqueada por el proceso, el comportamiento correcto de es permitir que las capas superiores del kernel manejen el evento. A este extremo, el controlador devuelve -ERESTARTSYS a la persona que llama; este valor es utilizado internamente por la capa del sistema de archivos virtual (VFS), que reinicia la llamada al sistema o devuelve -EINTR al espacio del usuario. Usamos el mismo tipo de cheque para manejar el manejo de señales para cada lectura y la implementación de escritura .

Fuente: http://www.makelinux.net/ldd3/chp-6-sect-2

Respuesta

43

-ERESTARTSYS está conectada con el concepto de una llamada al sistema reiniciables. Una llamada reiniciable del sistema es una que el kernel puede volver a ejecutar de forma transparente cuando hay alguna interrupción.

Por ejemplo, el proceso de espacio de usuario que está durmiendo en una llamada al sistema puede recibir una señal, ejecutar un controlador y luego, cuando el controlador regresa, parece regresar al kernel y sigue durmiendo en la llamada original del sistema.

Utilizando el indicador SA_RESTART de la API POSIX sigaction, los procesos pueden organizar el comportamiento de reinicio asociado con las señales.

En el kernel de Linux, cuando un controlador u otro módulo que bloquea en el contexto de una llamada al sistema detecta que una tarea se ha despertado debido a una señal, puede devolver -EINTR. Pero -ININTR burbujeará hasta el espacio de usuario y hará que la llamada del sistema devuelva -1 con errno establecido en EINTR.

Si devuelve -ERESTARTSYS en su lugar, significa que su llamada al sistema es reiniciable. El código ERESTARTSYS no se verá necesariamente en el espacio de usuario. O bien se traduce en un retorno -1 y se establece errno en EINTR (luego, obviamente, se ve en el espacio de usuario), o se traduce en un comportamiento de reinicio de llamada del sistema, lo que significa que su syscall se llama nuevamente con los mismos argumentos (por ninguna acción en parte del proceso de espacio de usuario: el kernel hace esto almacenando la información en un bloque de reinicio especial).

Observe el problema obvio con "los mismos argumentos" en el párrafo anterior: algunas llamadas al sistema no se pueden reiniciar con los mismos parámetros, ¡porque no son idempotentes! Por ejemplo, supongamos que hay una llamada de espera como nanosleep, durante 5.3 segundos. Se interrumpe después de 5 segundos. Si se reinicia ingenuamente, dormirá durante otros 5.3 segundos. Tiene que pasar nuevos parámetros a la llamada reiniciada para dormir solo por los 0.3 segundos restantes; es decir, alterar el contenido del bloque de reinicio. Hay una forma de hacerlo: carga argumentos diferentes en el bloque de reinicio de la tarea y usa el valor de retorno -ERESTART_RESTARTBLOCK.

Para responder a la segunda pregunta: ¿cuál es la diferencia? ¿Por qué no simplemente escribir la rutina de lectura sin verificar el valor de retorno y devolver -ERESTARTSYS? Bueno, porque eso es incorrecto en el caso de que la activación se deba a una señal. ¿Desea que una lectura devuelva 0 bytes leídos cada vez que llega una señal? Eso podría ser malinterpretado por el espacio de usuario como el final de los datos. Este tipo de problema no aparecerá en los casos de prueba que no usan señales.

+1

@Noge: para ver la diferencia, envíe una señal al proceso después de haber llamado 'read()' y antes de llamar a 'write()'. – caf

Cuestiones relacionadas