Manera adecuada de manejar EINTR en bibliotecas


10

¿Cuál es la etiqueta recomendada cuando se trata de EINTRbibliotecas?

Actualmente estoy escribiendo una función que realiza algunas tareas del sistema de archivos con la API POSIX, pero muchas de las llamadas que uso pueden volver potencialmente EINTR. Además, la función puede bloquearse en algunas circunstancias. (Para los interesados, implementa un mecanismo de bloqueo).

En aras de que esto sea lo más general posible, me gustaría saber cuál es la forma correcta de manejar una llamada interrumpida del sistema.

  • Según la mayoría de las fuentes que he leído, las personas suelen volver a intentar la llamada y continuar con sus negocios. Sin embargo, no estoy seguro de que sea lo correcto, ya que puede haber razones legítimas para interrumpir mi función dado que puede llevar una cantidad de tiempo considerable. Además, significa que la función EINTRsimplemente se tragaría y la persona que llama perdería cualquier indicio de que ocurrió.

  • Mi estrategia actual es abortar inmediatamente la operación si recibo EINTRy notifico a la persona que llama al respecto. De esta manera, la persona que llama puede decidir si quiere volver a intentar mi función, ¿verdad? (¿O tal vez mi comprensión de las señales es defectuosa?)


1
¿Qué está haciendo tu biblioteca? Tiendo a aprobar su estrategia actual (de abortar la operación EINTRe informarlo a la persona que llama), pero puede depender de lo que esté haciendo su biblioteca ...
Basile Starynkevitch

1
En este caso está realizando una operación de bloqueo.
Rufflewind

Respuestas:


2

Siempre deje la decisión de cómo manejar EINTRal usuario y facilite la reanudación de la operación según corresponda.

Por lo general, la mejor manera de hacerlo es regresar de la función de la biblioteca a la persona que llama EINTR, pero en algunos casos una devolución de llamada u otra implementación podría ser mejor; la mejor manera depende de otros factores, pero siempre permita que el usuario vuelva a intentar currículum.

Esto significa que si el código de su biblioteca puede tener éxito parcialmente antes de obtener un EINTR, entonces debe pensar cuidadosamente lo que el usuario podría necesitar saber sobre ese éxito parcial, o si el usuario podría necesitar reanudar la operación desde donde falló. Es posible que deba devolver información adicional o proporcionar una interfaz para reanudar desde cualquier lugar donde sea apropiado.

Esta es la razón por la cual el sistema llama como ready writehoy en día devuelve un éxito parcial, porque es muy frustrante para el usuario que le digan:

Intentaste escribir "foo"y nosotros escribimos con éxito nada, o "f", o "fo", y no sabes cuál . ¡Que te diviertas! ¡Espero que su sistema pueda manejar el reinicio de toda la escritura después de cualquiera de esos!

Por supuesto, en algunos casos, deberíamos escribir sistemas para manejar situaciones exactamente así, por ejemplo, tal vez después de una escritura parcial siempre recrea el archivo, o vuelve a abrir la conexión de red, o usa algún byte para significar "comenzar de nuevo" - por lo tanto, depende de los casos de uso a los que se dirija su biblioteca.

Si una función de biblioteca realiza varias operaciones, y no hay forma de saber en cuál de ellas falló, y esas operaciones no son idempotentes de manera segura y eficiente, eso básicamente hace que una biblioteca sea inutilizable para el código que necesita ser robusto.

Si todos los pasos en una función de biblioteca son idempotentes de manera segura y eficiente, o si todo es atómico, como adquirir un bloqueo, simplemente dejar que el usuario sepa que EINTRsucedió es suficiente.

Además, si volvemos a intentarlo EINTR, podríamos interrumpir el manejo de la señal . En el nivel bajo, los manejadores de señales solo pueden usar de manera segura un conjunto limitado de características, por lo que en muchos casos un manejador de señales solo establecerá un valor booleano que indica que se recibió la señal y luego regresará, esperando que cuando se reanude el código, lo haga. salir de lo que sea que estuviera haciendo. Si obtenemos un EINTRmensaje y luego volvemos a intentarlo en lugar de devolverle el control al usuario, es posible que evitemos que el código lo haga.

Lo que se debe hacer después de una decisión deEINTR un programa completo : no se puede saber la respuesta correcta sin saber qué está haciendo el programa y cómo debe responder a una señal, y tiene efectos en el resto del programa.

Saber cómo o si el usuario podría necesitar reanudar, y ayudarlo a hacerlo si es necesario, es una responsabilidad de la biblioteca: no se puede conocer la respuesta correcta sin saber lo que está haciendo la biblioteca.


4

Señales en Unix

cuando algún otro proceso envíe su Señal de proceso, su programa detendrá lo que está haciendo ...

1) Ejecute el código del controlador que escribió. No tiene idea de lo que su programa podría estar haciendo cuando llegue la señal. Esa es la idea con las señales, pueden ser completamente asíncronas.

2) Cuando el controlador de señal está listo, normalmente solo regresa, y su programa continúa donde lo dejó, como si nada hubiera pasado.

Encontré información útil en Richard Stevens

Una característica de los sistemas UNIX anteriores es que si un proceso capturó una señal mientras el proceso estaba bloqueado en una llamada de sistema "lenta", la llamada del sistema se interrumpía. La llamada al sistema devolvió un error y errno se configuró en EINTR. Esto se hizo bajo el supuesto de que, dado que se produjo una señal y el proceso la captó, existe una buena posibilidad de que haya sucedido algo que debería despertar la llamada del sistema bloqueado.

La semántica POSIX.1 para lecturas y escrituras interrumpidas cambió con la versión 2001 del estándar. Las versiones anteriores dieron a las implementaciones una opción sobre cómo lidiar con las lecturas y escrituras que han procesado cantidades parciales de datos. Si la lectura ha recibido y transferido datos al búfer de una aplicación, pero aún no ha recibido todo lo que la aplicación solicitó y luego se interrumpe, el sistema operativo podría fallar la llamada del sistema con errno configurado en EINTR o permitir que la llamada del sistema tenga éxito, regresando La cantidad parcial de datos recibidos. Del mismo modo, si la escritura se interrumpe después de transferir algunos de los datos en el búfer de una aplicación, el sistema operativo podría fallar la llamada del sistema con errno establecido en EINTR o permitir que la llamada del sistema tenga éxito, devolviendo la cantidad parcial de datos escritos. Históricamente, Las implementaciones derivadas del Sistema V fallan la llamada del sistema, mientras que las implementaciones derivadas de BSD devuelven un éxito parcial. Con la versión 2001 del estándar POSIX.1, se requiere la semántica de estilo BSD.

El problema con las llamadas interrumpidas del sistema es que ahora tenemos que manejar el error devuelto explícitamente. La secuencia de código típica (suponiendo una operación de lectura y suponiendo que queremos reiniciar la lectura incluso si está interrumpida) sería

again:
    if ((n = read(fd, buf, BUFFSIZE)) < 0) {
        if (errno == EINTR)
            goto again;     /* just an interrupted system call */
        /* handle other errors */
    }

Para evitar que las aplicaciones tengan que manejar llamadas interrumpidas del sistema, 4.2BSD introdujo el reinicio automático de ciertas llamadas interrumpidas del sistema.


3
Esto no responde la pregunta. así fue como las bibliotecas deberían abordar el problema. Si sé que quiero ignorar EINTR en ciertas operaciones, puedo hacerlo con este reintento de llamada ( goto again). Pero como desarrollador de la biblioteca, no sé si esto es lo que mi usuario de la biblioteca quiere.
Messa
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.