2008-10-23 8 views
104

Estaba a punto de agregar un controlador de señal adicional a una aplicación que tenemos aquí y me di cuenta de que el autor había usado sigaction para configurar los otros manejadores de señales. Iba a usar la señal. Para seguir la convención, debería usar sigaction, pero si estaba escribiendo desde cero, ¿cuál debería elegir?¿Cuál es la diferencia entre sigaction y signal?

Respuesta

124

Use sigaction() a menos que tenga razones muy convincentes para no hacerlo.

La interfaz signal() tiene antigüedad (y por lo tanto disponibilidad) a su favor, y está definida en el estándar C. Sin embargo, tiene una serie de características indeseables que sigaction() evita, a menos que use los indicadores explícitamente agregados a sigaction() para permitirle simular fielmente el antiguo comportamiento de signal().

  1. La función signal() no bloquea (necesariamente) que lleguen otras señales mientras se está ejecutando el controlador actual; sigaction() puede bloquear otras señales hasta que regrese el manejador actual.
  2. La función signal() (normalmente) restablece la acción de la señal a SIG_DFL (predeterminado) para casi todas las señales. Esto significa que el controlador signal() debe reinstalarse como su primera acción. También abre una ventana de vulnerabilidad entre el momento en que se detecta la señal y el manejador se reinstala, durante el cual si llega una segunda instancia de la señal, se produce el comportamiento predeterminado (generalmente final, a veces con prejuicio, también conocido como volcado del núcleo).
  3. El comportamiento exacto de signal() varía entre sistemas, y los estándares permiten esas variaciones.

En general, estas son buenas razones para usar sigaction() en lugar de signal(). Sin embargo, la interfaz de sigaction() es indudablemente más complicada.

Cualquiera de los dos que utiliza, no se deje tentar por las interfaces de señal alternativos como sighold(), sigignore(), sigpause() y sigrelse(). Son nominalmente alternativas a sigaction(), pero están apenas estandarizados y están presentes en POSIX para compatibilidad con versiones anteriores en lugar de para uso serio. Tenga en cuenta que el estándar POSIX dice que su comportamiento en programas de subprocesos múltiples no está definido.

Los programas y señales de subprocesos múltiples son otra historia complicada. AFAIK, ambos signal() y sigaction() son correctos en aplicaciones de subprocesos múltiples.

Cornstalksobserves:

La página de manual de Linux para signal() dice:

  Los efectos de signal() en un proceso de múltiples subprocesos no están especificados.

Por lo tanto, creo que sigaction() es el único que se puede usar de forma segura en un proceso de subprocesos múltiples.

Eso es interesante. La página del manual de Linux es más restrictiva que POSIX en este caso. POSIX especifica para signal():

Si el proceso es multi-hilo, o si el proceso es de un solo subproceso y un manejador de señal se ejecuta otro que como el resultado de:

  • el proceso de llamada abort() , raise(), kill(), pthread_kill(), o sigqueue() para generar una señal que no esté bloqueado
  • una señal de espera de ser desbloqueada y ser entregados antes de la llamada que desbloqueó devuelve

el comportamiento es indefinido si el manejador de la señal se refiere a cualquier objeto que no sea errno con la duración de almacenamiento estático que no sea mediante la asignación de un valor a un objeto declarado como volatile sig_atomic_t, o si el controlador de señal llama a cualquier función definida en este estándar otro de una de las funciones enumeradas en Signal Concepts.

Así que POSIX especifica claramente el comportamiento de signal() en una aplicación de subprocesos múltiples.

Sin embargo, sigaction() es preferible esencialmente en todas las circunstancias - y portátil código multihilo debe utilizar sigaction() a menos que haya una razón principal por la que no puede (por ejemplo, "sólo utilizar las funciones definidas por el estándar C" - y sí , El código C11 puede ser multiproceso). Que es básicamente lo que dice el párrafo inicial de esta respuesta.

+9

Esta descripción de 'señal' es en realidad del comportamiento del Sistema V de Unix. POSIX permite este comportamiento o el comportamiento de BSD mucho más sano, pero como no puede estar seguro de cuál obtendrá, es mejor usar 'sigaction'. –

+1

a menos que use los indicadores explícitamente agregados a sigaction() para permitirle simular fielmente el comportamiento de la señal() anterior. ¿Qué banderas serían ésas (específicamente)? – ChristianCuevas

+0

@AlexFritz: principalmente 'SA_RESETHAND', también' SA_NODEFER'. –

2

Usaría la señal() ya que es más portátil, al menos en teoría. Votaré a cualquier comentarista que pueda proponer un sistema moderno que no tenga una capa de compatibilidad POSIX y admita la señal().

Citando del GLIBC documentation:

Es posible utilizar funciones tanto la señal como sigaction dentro un solo programa, pero hay que tener cuidado ya que pueden interactuar de manera poco extrañas.

La función sigaction especifica más información que la función de señal , por lo que el valor de retorno de la señal no puede expresar el rango completo de posibilidades de acción de . Por lo tanto, si usa la señal para guardar y luego restablece una acción, es posible que no pueda restablecer correctamente un controlador que se estableció con sigaction.

Para evitar problemas como resultado, siempre use sigaction para guardar y restaure un controlador si su programa usa sigaction en absoluto. Dado que sigaction es más general, puede guardar y restablecer correctamente cualquier acción , independientemente de si se estableció originalmente con la señal o sigaction.

En algunos sistemas si establece una acción con señal y luego examínelo con sigaction, la dirección del controlador que obtenga no puede ser igual a la especificada con la señal. Puede que ni siquiera sea adecuado para usar como argumento de acción con señal. Pero puede confiar en usarlo como argumento para sigaction. Este problema nunca ocurre en el sistema GNU.

Por lo tanto, es mejor utilizar uno u otro de los mecanismos consistentemente en un solo programa.

Portabilidad Nota: La función de señal básica es una característica de ISO C, , mientras que sigaction es parte del estándar POSIX.1. Si está preocupado por la portabilidad de sistemas que no son POSIX, entonces debe usar la función de señal en su lugar.

Copyright (C) 1996-2008 Free Software Foundation, Inc.

Se concede permiso para copiar, distribuir y/o modificar este documento bajo los términos de la licencia de documentación libre GNU, versión 1.2 o cualquier versión posterior publicada por la Fundación de Software Libre ; sin secciones invariantes, sin textos de portada, y sin textos de contraportada. Se incluye una copia de la licencia en en la sección titulada "Licencia de documentación libre de GNU".

2

Desde la página signal(3) hombre:

DESCRIPCIÓN

This signal() facility is a simplified interface to the more 
general sigaction(2) facility. 

Tanto invocar la misma instalación subyacente. Es de suponer que no debe manipular la respuesta una sola señal con ambos, pero mezclarlos no debería hacer que se rompa nada ...

+0

¡Eso no está en la página de mi hombre! Todo lo que obtengo es "DESCRIPCIÓN La llamada al sistema signal() instala un nuevo manejador de señal para la señal con el número de señal." Necesito actualizar al paquete _useful_ man pages. – MattSmith

+1

Eso está fuera de las páginas Mac OS X 10.5. – dmckee

+0

También verificado desde el código fuente de glibc. signal() simplemente llama a sigaction() – bmdhacks

5

Son interfaces diferentes para las funciones de señal del sistema operativo. Uno debería preferir usar sigaction para señalar si es posible, ya que la señal() tiene un comportamiento definido por la implementación (a menudo propenso a la raza) y se comporta de manera diferente en Windows, OS X, Linux y otros sistemas UNIX.

Consulte este security note para más detalles.

+0

Acabo de ver el código fuente de glibc y la señal() solo llama a sigaction(). También vea arriba donde la página del manual de MacOS dice lo mismo. – bmdhacks

+0

que es bueno saber. Solo he visto que los manejadores de señales solían cerrar las cosas ordenadamente antes de salir, por lo que normalmente no confiaría en el comportamiento que tiene que ver con la reinstalación del controlador. – MattSmith

4

Para mí, esta línea de abajo fue suficiente para decidir:

El sigaction() función proporciona una más completa y fiable mecanismo para el control de las señales; nuevas aplicaciones deben utilizar sigaction() en lugar de la señal()

http://pubs.opengroup.org/onlinepubs/009695399/functions/signal.html#tag_03_690_07

sea que esté comenzando desde cero o modificar un programa de edad, sigaction debería ser la opción correcta.

1

señal() es estándar C, sigaction() no lo es.

Si puede usar cualquiera (es decir, está en un sistema POSIX), entonces use sigaction(); no se especifica si signal() restablece el controlador, lo que significa que para ser portátil debe volver a llamar a signal() dentro del controlador. Lo que es peor es que hay una carrera: si obtiene dos señales en sucesión rápida, y la segunda se entrega antes de reinstalar el controlador, tendrá la acción predeterminada, que probablemente será para matar su proceso. sigaction(), por otro lado, se garantiza el uso de semántica de señal "confiable". No necesita volver a instalar el controlador, ya que nunca se restablecerá. Con SA_RESTART, también puede hacer que algunas llamadas al sistema se reinicien automáticamente (para que no tenga que verificar manualmente si hay EINTR). sigaction() tiene más opciones y es confiable, por lo que se recomienda su uso.

Psst ... no le digas a nadie que te dije esto, pero POSIX actualmente tiene una función bsd_signal() que actúa como señal() pero le da semántica a BSD, lo que significa que es confiable. Su uso principal es para portar aplicaciones antiguas que asumieron señales confiables, y POSIX no recomienda su uso.

1

También sugiero usar sigaction() sobre la señal() y me gustaría agregar un punto más. sigaction() le da más opciones como pid del proceso que murió (es posible usar la estructura siginfo_t).

Cuestiones relacionadas