2011-04-16 6 views
8

Estoy trabajando para implementar la cancelación de subprocesos en Linux sin ninguno de los "comportamientos desagradables" (algunos podrían decir errores) discutidos en algunas de mis otras preguntas recientes. El enfoque de Linux/glibc para la cancelación de pthread hasta ahora ha sido tratarlo como algo que no necesita soporte de Kernel, y que puede manejarse en el nivel de biblioteca simplemente habilitando la cancelación asincrónica antes de hacer un syscall, y restaurar el estado de cancelación anterior después de que regrese syscall Esto tiene por lo menos 2 problemas, uno de ellos muy grave:Implementando syscalls cancelables en el espacio de usuario

  1. cancelación puede actuar después de la llamada al sistema ha regresado de kernelspace, pero antes de que el espacio de usuario guarda el valor de retorno. Esto da como resultado una pérdida de recursos si el syscall asignó un recurso y no hay forma de parchearlo con los manejadores de cancelación.
  2. Si se maneja una señal mientras el hilo está bloqueado en un syscall cancelable, todo el manejador de señal se ejecuta con cancelación asíncrona habilitada. Esto podría ser extremadamente peligroso, ya que el manejador de señal puede llamar a funciones que son asincrónicas de señal segura pero no asincrónicas canceladas.

Mi primera idea para solucionar el problema consistía en establecer un indicador de que el hilo está en un punto cancelación, en lugar de permitir la cancelación asíncrono, y cuando se establece este indicador, tener el manejador de señal de cancelación comprobar el puntero de instrucción salvado para ver si apunta a una instrucción syscall (específica del arco). Si es así, esto indica que el syscall no se completó y se reiniciará cuando regrese el controlador de señal, por lo que podemos cancelar. Si no, asumí que el syscall ya había regresado y cancelé la cancelación. Sin embargo, también existe una condición de carrera: es posible que el subproceso aún no haya alcanzado la instrucción syscall, en cuyo caso, el syscall podría bloquear y nunca responder a la cancelación. Otro pequeño problema es que las llamadas de sis- tema no cancelables realizadas desde un manejador de señal se convirtieron erróneamente en cancelables, si el indicador de punto de cancelación se configuró cuando se ingresó el manejador de señal.

Estoy buscando un nuevo enfoque, y en busca de comentarios sobre él. Las condiciones que se deben cumplir:

  • Cualquier solicitud de cancelación recibida antes de la finalización de la llamada al sistema se ha de actuar antes de que los bloques de llamadas al sistema para cualquier intervalo de tiempo significativo, pero no mientras está pendiente el reinicio debido a la interrupción por una controlador de señal.
  • Cualquier solicitud de cancelación recibida después de la finalización del syscall debe diferirse al siguiente punto de cancelación.

La idea que tengo en mente requiere un montaje especializado para el contenedor de syscall cancelable. La idea básica sería:

  1. Introduzca la dirección de la próxima instrucción syscall en la pila.
  2. Almacena el puntero de pila en el almacenamiento local de subprocesos.
  3. Pruebe una bandera de cancelación desde el almacenamiento local de subprocesos; saltar para cancelar la rutina si está configurada.
  4. Haga el syscall.
  5. Borre el puntero guardado en el almacenamiento local de subprocesos.

La operación de cancelación sería entonces implicar:

  1. Active la marca cancelación en el almacenamiento local de subprocesos del subproceso de destino.
  2. Pruebe el puntero en el almacenamiento local de subprocesos de la secuencia de destino; si no es nulo, envíe una señal de cancelación al hilo de destino. manejador de señales

La cancelación haría a continuación:

  1. Compruebe que el puntero de pila salvado (en el contexto de la señal) es igual al puntero guardado en el almacenamiento local de subprocesos. De lo contrario, el punto de cancelación fue interrumpido por un manejador de señal y no hay nada que hacer en este momento.
  2. Compruebe que el registro de contador de programa (guardado en el contexto de señal) es menor o igual que la dirección guardada en el puntero de pila guardado. Si es así, esto significa que el syscall aún no se ha completado, y ejecutamos la cancelación.

El problema que veo hasta ahora es en el paso 1 del manejador de la señal: si decide no actuar, a continuación, después de las declaraciones de manejador de la señal, el hilo se podría dejar de bloqueo en la llamada al sistema, sin hacer caso, la baja pendiente solicitud. Para esto, veo dos posibles soluciones:

  1. En este caso, instale un temporizador para entregar señales al hilo específico, esencialmente reintentando cada milisegundo más o menos hasta que tengamos suerte.
  2. Levante la señal de cancelación nuevamente, pero regrese desde el controlador de señal de cancelación sin desenmascarar la señal de cancelación. Se desenmascarará automáticamente cuando regrese el manejador de señal interrumpido, y luego podremos volver a intentarlo. Sin embargo, esto podría interferir con el comportamiento de los puntos de cancelación dentro del controlador de señal.

¿Alguna idea de qué enfoque es el mejor, o si hay otros defectos más fundamentales que me faltan?

Respuesta

3

La solución 2 parece menos pirata. No creo que pueda causar el problema que sugieres, porque las llamadas cancelables llamadas dentro del controlador de syscall verificarán el indicador de cancelación en TLS, que ya debe haberse configurado si el manejador de señal de cancelación se ha ejecutado y monkeyed con la máscara de señal de todos modos.

(Parece que sería mucho más fácil para los implementadores si cada syscall de bloqueo tomara un parámetro sigmask a la pselect()).

+0

Su comentario entre paréntesis es exactamente la solución ideal. Todo el problema se debe a la falta de un mecanismo para desbloquear atómicamente la cancelación y hacer el llamado al sistema, y ​​está requiriendo hacks en el espacio de usuario (que parecen pertenecer al kernel) para evitarlo. –

+0

Y tiene razón en que mi preocupación fue errónea. En el punto en que la señal de cancelación se bloquea "erróneamente", la bandera de cancelación ya estará configurada y cualquier punto de cancelación ejecutado por el manejador de señal actuará inmediatamente, sin necesidad de señales para entregarlo. Exceptuando cualquier descubrimiento de problemas imprevistos, me inclino por marcar esta respuesta como aceptada. –

+0

@R .: El único otro problema que se me ocurre es que necesitará una barrera de memoria entre la operación de cancelar y el contenedor syscall. – caf

Cuestiones relacionadas