Programación del sistema Linux C (08) gestión de procesos de señales y procesamiento de señales

Señales y procesamiento de señales

La señal es un método de comunicación asíncrono típico, y también es uno de los métodos de comunicación de proceso comúnmente utilizados en Linux, también conocido como interrupción suave. Use el comando kill -l para ver la lista de señales admitidas por el sistema. Generalmente hay:


1 Conceptos básicos de la señal

1.1 Generación de señal

Hay 5 formas de generar señales en Linux.

  1. Opere la terminal, como ctrl + C, ctrl + D.
  2. El hardware es anormal.
  3. Ejecute el comando kill para enviar una señal a un proceso.
  4. Llame a la función kill para enviar un comando a otro proceso.
  5. Cuando el núcleo detecta una determinada condición de software, puede señalar el proceso.

Nota: No importa cuál de los cinco métodos anteriores envíe una señal, el proceso de recepción de la señal suspenderá la ejecución del programa y pasará a procesar la señal recibida. Si el proceso está en estado listo / estado de bloqueo, una vez que se obtiene el intervalo de tiempo de la CPU, la señal se procesará primero. Si el proceso está en estado suspendido, la señal recibida activará el proceso suspendido y el proceso procesará primero la señal. En resumen, el principio de dar prioridad al procesamiento de señales, incluso si el programa es un bucle sin fin.

1.2 Procesamiento de señal

Hay 3 métodos de procesamiento para procesos en Linux:

  1. Ignora esta señal: ignórala.
  2. Registre una función de procesamiento de señal: requiera que el núcleo cambie al modo de usuario para llamar a la función de procesamiento después de recibir la señal. Este método se llama capturar una señal. Los programas de usuario a menudo necesitan hacer un procesamiento personalizado en ciertas señales para resolver el problema.
  3. Realice las acciones predeterminadas del sistema: las diferentes señales tienen diferentes acciones Hay dos acciones predeterminadas conocidas por el sistema: terminar el proceso / ignorar las señales.

1.3 Cómo usar señales comunes

Teclas de acceso directo establecidas por el sistema para enviar señales a los procesos.

ctrl+C:结束当前的进程。
ctrl+/:程序运行结束,并且产生了core文件。core文件用于程序的调试,可以用gdb工具对其进行调试。

Use el comando kill para enviar una señal específica a un proceso específico.


2 Influencia de la señal

No importa dónde se ejecute el proceso actual, saltará a la función de procesamiento de señal para su ejecución, por lo que la señal también tendrá efectos secundarios, lo cual es inevitable. Por ejemplo, en un proceso, la función principal, otros hilos y las funciones de procesamiento de señal son procesos de ejecución independientes, que son paralelos. Si un proceso tiene múltiples procesos de ejecución y estos procesos acceden a los mismos recursos, puede haber conflictos.

2.1 Reingreso

Conceptos basicos:

  • Reentrada: para una función, hay diferentes llamadas de flujo de ejecución, es posible ingresar nuevamente a la función antes de que la primera llamada no haya regresado.
  • Función reentrante: si una función solo accede a variables / parámetros locales, se llama función reentrante / código puro / función segura para subprocesos.
  • Funciones no reentrantes: las funciones acceden a variables / parámetros globales y funciones que pueden causar errores debido a la reentrada.

Una función no es reentrante si cumple una de las siguientes condiciones:

  • Se utilizan datos globales, incluidas variables globales y variables estáticas.
  • Llame al método dinámico para obtener la memoria, porque el método de asignación dinámica de memoria también es administrar la asignación de memoria con una lista vinculada.
  • Se utilizan bibliotecas de E / S estándar, y muchas implementaciones en bibliotecas de E / S estándar utilizan estructuras de datos globales de manera no reentrante.

En resumen, el uso de datos / funciones con alcance global no es reentrante, y tales funciones / códigos se denominan códigos impuros.

2.2 manipulación atómica

  • Para algunas operaciones de un proceso, a veces para evitar que la señal lo afecte, la instrucción debe ejecutarse una vez, que es el origen de la operación atómica. La llamada operación atómica es una instrucción relativa al ensamblaje.
  • Para las declaraciones de asignación, para obtener operaciones atómicas, debe usar el tipo: sig_atomic_t.
  • Para programas con múltiples procesos, las dos palabras clave sig_atomic_t y volatile siempre se usan al mismo tiempo.
  • El papel de la palabra clave volátil es evitar la optimización.

2.3 Llamada al sistema de interrupción

  • El controlador de señal cargado con la función de señal siempre reiniciará la llamada interrumpida del sistema después de que se complete el procesamiento de la señal.
  • Use la función sigaction para establecer si se reiniciará la llamada interrumpida del sistema cuando regrese el controlador de señal.

3 funciones de procesamiento de señal

3.1 Establecer la función de procesamiento de señal

En Linux, los usuarios pueden proporcionar sus propias funciones de procesamiento de señal, usar funciones de señal para cargar las funciones de procesamiento y notificar al sistema:

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

Consulte el manual de referencia de la función de Linux para más detalles . De hecho, esto está en el archivo signal.h:

#define SIG_IGN ((void *) (*)()) 1
#define SIG_DFL ((void *) (*)()) 0
#define SIG_ERR ((void *) (*)()) -1

El marco general del programa de procesamiento de señales en el sistema es este:

void signal_handler(int signo)
{
   switch(signo){
   case SIG1:          /*处理信号1*/
        ...
   case SIG2:          /*处理信号2*/
        ...
   case SIGn:          /*处理信号n*/
        ...
   }
}

El sistema Linux no permite a los usuarios crear nuevas señales, pero proporciona dos señales SIGUSR1 y SIGUSR2 específicamente para la comunicación de señales entre aplicaciones. Estas dos señales no tienen un significado especial, y el valor predeterminado del sistema es ignorarlas.

3.2 Envío de señales y envío de señales al proceso mismo

La señal @ 1 se puede aplicar bien a la comunicación entre procesos. La función kill se usa para enviar señales a procesos / grupos de procesos en Linux.El prototipo de la función es el siguiente:

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

Consulte el manual de referencia de la función de Linux para más detalles . Un proceso debe prestar atención a los siguientes puntos al enviar señales a otro proceso:

  1. El proceso tiene la autoridad para enviar señales al proceso especificado. Si la señal se puede enviar de manera casual, dará una oportunidad a los programas maliciosos.
  2. Los procesos del sistema no pueden recibir señales. Los programas maliciosos pueden hacer que el sistema se bloquee.
  3. El usuario root puede enviar una señal a cualquier proceso en el sistema, por lo que puede eliminar cualquier proceso malicioso, pero al mismo tiempo, un programa malicioso con autoridad de usuario root también puede matar otros procesos en el sistema.
  4. Los procesos también pueden usar kill (getpid (), signo) para enviarse señales a sí mismos.

@ 2 linux proporciona una función para enviar una señal al proceso en sí, que puede reemplazar la forma anterior. El prototipo de la función es el siguiente:

#include <signal.h>
int raise(int sig);

Consulte el manual de referencia de la función de Linux para más detalles . El uso de la función elevar puede realizar la función de la función de salida, la diferencia es que no realiza ningún tratamiento posterior (como enjuagar la secuencia, cerrar el archivo, etc.) . Por lo tanto, si el método de procesamiento de señal es terminar el proceso, debe escribir una función que maneje todos los asuntos de cuidado posterior como el programa de procesamiento de señal para esta señal, y use la función de señal para configurarla y usarla.

3.3 Establecer temporizador de Linux

En algunas ocasiones, se debe configurar un temporizador, y el proceso de configuración del temporizador se notificará después de un cierto período de tiempo. Use la función de alarma para configurar un temporizador en Linux. El prototipo de la función es el siguiente:

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

Consulte el manual de referencia de la función de Linux para más detalles . La alarma también se denomina función de reloj de alarma. Puede establecer un temporizador en el proceso. Cuando expira el tiempo especificado por el temporizador, envía una señal SIGALRM al proceso. Por supuesto, algunos puntos aún deben tenerse en cuenta:

  1. Un proceso solo puede tener una hora de alarma. Si la hora de alarma se ha configurado antes de llamar a alarma, cualquier hora de alarma anterior será reemplazada por el nuevo valor.
  2. Después de que la señal de alarma pasa el número especificado de segundos, el núcleo genera la señal. Debido a la demora de la programación del proceso, el proceso tarda un tiempo en controlarse para poder procesar la señal, por lo que la función de alarma no establece un temporizador muy preciso.
  3. La función de alarma también se puede utilizar para lograr el bloqueo de tiempo, es decir, cuando el dispositivo que necesita leerse y escribirse no está listo, solo espera un tiempo limitado. Por supuesto, es posible hacer esto para un sistema que no está muy cargado, pero una vez que la carga es muy grave, cuando la función de alarma establece el temporizador, la autoridad de la CPU se pierde. Debido a demasiados procesos en el sistema, la alarma debe ejecutarse. La función es fácil de expirar, entonces algunas operaciones ya no están restringidas por la función de alarma.

3.4 Suspender el proceso

Cuando las condiciones de ejecución de un proceso ya se cumplen, el proceso aún debe bloquearse (por ejemplo, se espera que el proceso se retrase y la función de suspensión a menudo juega este papel). Esta situación en la que el recurso del proceso entra en estado bloqueado se llama suspensión del proceso. Use la función de pausa en Linux para suspender un proceso. El prototipo de la función es el siguiente:

#include <unistd.h>
int pause(void);
/*pause函数执行成功返回-1,同时将errno设置为EINTR。
pause函数使调用该函数的进程进入挂起状态,直到有一个终止进程的信号/一个信号处理程序从其返回后,pause函数才返回。*/

Consulte el manual de referencia de la función de Linux para más detalles .

3.5 Proceso de sueño

La función de pausa es suspender el proceso sin límite de tiempo. Si desea reanudar el proceso dentro de un tiempo determinado, use la función de suspensión. El prototipo de la función es el siguiente:

#include <unistd.h>
unsigned int sleep(unsigned int seconds);
int usleep(useconds_t usec);

Consulte el manual de referencia de la función de Linux para más detalles .


4 Conjunto de señales y señal blindada

4.1 Conjunto de señales y función de procesamiento del conjunto de señales

El enmascaramiento de señales es una función importante en el sistema Linux, ya que permite que los procesos del usuario reciban y procesen señales selectivamente. Las señales que deben protegerse están representadas por conjuntos de señales. Este conjunto es un vector de bits, donde cada bit corresponde a una señal. Este tipo de datos es sigset_t. El funcionamiento de la señal no puede cambiarse directamente, y la función del sistema es necesaria para realizar la función relevante. En Linux, se proporciona un conjunto de funciones de procesamiento de conjunto de señales:

#include <signal.h>
int sigemptyset(sigset_t *set);     //将信号集清空
int sigfillset(sigset_t *set);          //将所有信号添加到信号集
int sigaddset(sigset_t *set, int signum);     //将信号signum添加到信号集set中
int sigdelset(sigset_t *set, int signum);     //将信号signum从信号集set中删除
int sigismember(const sigset_t *set, int signum);     //在信号集set中查看signum信号是否被设置,被设置函数返回1,没被设置函数返回0,查看失败返回-1。

Consulte el manual de referencia de la función de Linux para más detalles .

Nota: El número de señal comienza desde 1, y el número de bit en la función de procesamiento de señal comienza desde 0, por lo que hay:

signum==信号编码-1

4.2 Señal blindada

La señal necesita ser procesada en el programa. Naturalmente, hay algunos casos en los que la señal no necesita ser procesada. Por lo tanto, debe proteger la función de señal. Cada proceso tiene una palabra de máscara de señal que marca la señal que se está enmascarando. La esencia de la palabra de máscara de señal es la misma que el conjunto de señales, que es un vector de bits. El bit correspondiente de la codificación de señal es 1 para enmascarar la señal, y el bit correspondiente es 0 para procesar la señal. En Linux, use la función sigpromask para detectar o cambiar la palabra de máscara de señal del proceso. El prototipo de la función sigpromask es el siguiente:

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

Consulte el manual de referencia de la función de Linux para más detalles . (Nota: las dos señales SIG_KILL y SIG_STOP no se pueden enmascarar. Si se enmascaran estas dos señales, el proceso malicioso no tiene que preocuparse de que el comando root del usuario raíz lo elimine y puede destrozarlo).

4.3 Manejo de señales pendientes

La señal "pendiente" es un estado, que se refiere al período desde la generación de la señal hasta el momento antes de que se procese la señal; se puede ver mediante int sigpending (sigset_t * set) y int sigismember (const sigset_t * set, int signum) Cierta señal está pendiente. La función de señalización se utiliza para verificar las señales pendientes en Linux. El prototipo de la función es el siguiente:

#include <signal.h>
int sigpending(sigset_t *set);

Consulte el manual de referencia de la función de Linux para más detalles .

Publicado 289 artículos originales · elogiados 47 · 30,000+ vistas

Supongo que te gusta

Origin blog.csdn.net/vviccc/article/details/105159225
Recomendado
Clasificación