Señales de proceso (Linux)

Primeros pasos con las señales

La señalización se divide en cuatro fases:

  1. preparación
  2. producir
  3. mantener
  4. tratar con

señal alrededor

Use una simple castaña para explicar las cuatro etapas de la señal:
Cuando cruzamos la calle y nos encontramos con semáforos, en primer lugar, podemos reconocer los semáforos (excepto para los daltónicos). El reconocimiento incluye dos factores importantes: el reconocimiento y la capacidad de producir comportamientos correspondientes; pero ¿por qué conocemos los semáforos? Debe ser educado por alguien, tal vez aprendido en la escuela, o educado por miembros de la familia. Esta es en realidad la etapa preparatoria de la señal, cuando el semáforo se pone verde, no tenemos que cruzar la calle de inmediato. Si tenemos más importante cosas con las que lidiar, elegiremos esperar la próxima vez, cambiar la luz en realidad está generando una señal, elegir esperar la próxima vez es el procesamiento de la señal, todavía hay una preservación de la señal entre la generación y el procesamiento de la señal, es decir, la señal debe procesarse Recuerde: los métodos de procesamiento de señales también se pueden dividir en tres tipos: acción predeterminada, que consiste en detenerse en un semáforo en rojo e ir en un semáforo en verde; acción personalizada, como esperar unos minutos segundos después de cambiar la luz verde antes de cruzar la calle; ignorar, pero no cruzar la calle

señal de proceso

Migración de los conceptos anteriores al proceso
Primero, tenemos un consenso: el sistema operativo envía la señal al proceso; el proceso reconoce la señal y tomará las acciones correspondientes; no es que el proceso procesará la señal inmediatamente tan pronto a medida que se genera la señal. Por lo tanto, el proceso en sí debe tener la capacidad de guardar señales; hay tres formas en que un proceso procesa señales: predeterminado, personalizado, ignorar y el procesamiento de señales también se denomina captura de señales

conjunto de señales, solo se aprenden [1,31]las señales comunes
inserte la descripción de la imagen aquí

Aquí hay otro concepto para entender: dado que el proceso puede guardar la señal, ¿dónde debería guardarse? ? ?
De hecho, no es difícil imaginar que la señal se almacena en PCB, y hay un atributo en la estructura que se usa para almacenar
inserte la descripción de la imagen aquí
la posición del bit de la señal, que representa el número de la señal; el contenido del bit representa si el se ha recibido una señal, 0 significa no, 1 significa Sí; por lo tanto, la esencia de enviar una señal es modificar PCBel PCBmapa de bits de la señal. Dado que es el objeto de estructura de datos mantenido por el kernel, y el sistema operativo es el administrador del proceso, no No importa qué señal se envíe, es esencialmente una señal enviada por el sistema operativo al proceso. Se deben proporcionar llamadas al sistema relacionadas con el envío de señales y el procesamiento de señales.

generar señal

La tecla terminal genera una señal.

Por ejemplo, observe la señal en el proceso.

int main()
{
    
    
    while(true)
    {
    
    
        cout<<"我是一个进程"<<endl;
        sleep(2);
    }
}

inserte la descripción de la imagen aquí

La tecla de acceso rápido ctrl+cpuede terminar el proceso, la esencia es que el sistema operativo lo interpreta como la señal No. 2SIGINT

inserte la descripción de la imagen aquí

introducir una función

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
  1. signum: número de señal
  2. handler: puntero de función, procesamiento personalizado de la señal

Modificar la castaña de arriba

void handler(int signo)
{
    
    
    cout<<"进程捕捉到一个信号,信号编号:"<<signo<<endl;
}

int main()
{
    
    
    signal(2,handler);
    while(true)
    {
    
    
        cout<<"我是一个进程"<<getpid()<<endl;
        sleep(2);
    }
}

inserte la descripción de la imagen aquí

Aunque signalla función , el proceso todavía imprime cuatro veces normalmente, porque la señal no se captura handler, la función no se llama; cuando se ingresa la tecla de acceso rápido, la señal se captura, porque el método de procesamiento está personalizado, por lo que el proceso no salir directamente

Llame a una función del sistema para señalar el proceso de destino

matar

int kill(pid_t pid, int sig);
  1. pid: del proceso en ejecuciónpid
  2. sig: sigEnviar una señal al proceso de destino; puede ser cualquier señal

Los ejemplos son los siguientes:
mysignal.cpp

void Usage(const string&proc)
{
    
    
    cout<<"\nUsage "<<proc<<" pid signo\n"<<endl;
}
int main(int argc,char*argv[])
{
    
    
    if(argc!=3)
    {
    
    
        Usage(argv[0]);
        exit(1);
    }
    pid_t pid=atoi(argv[1]);
    int signo=atoi(argv[2]);
    int n=kill(pid,signo);
    if(n!=0)
    {
    
    
        perror("kill");
    }
    return 0;
}

inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

Signal 19 puede detener el proceso

aumentar

int raise(int sig);

Enviar la señal sigdirectamente al proceso (auto)

int main()
{
    
    
    int cnt=0;
    while(cnt<=10)
    {
    
    
        printf("cnt:%d,pid:%d\n",cnt++,getpid());
        sleep(1);
        if(cnt>=5)
        {
    
    
            raise(9);
        }
    }
    return 0;
}

inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

La señal No. 9 mata directamente el proceso

abortar

void abort(void);

Enviar una señal específica al proceso (auto) para terminar el proceso

int main()
{
    
    
    int cnt=0;
    while(cnt<=10)
    {
    
    
        printf("cnt:%d,pid:%d\n",cnt++,getpid());
        sleep(1);
        if(cnt>=5)
        {
    
    
            abort();
        }
    }
    return 0;
}

inserte la descripción de la imagen aquí

inserte la descripción de la imagen aquí

La señal designada enviada es en realidad la señal No. 6

señal de excepción de hardware

La generación de la señal no tiene que ser enviada explícitamente por el usuario

Observa el siguiente código:

int main()
{
    
    
    while(true)
    {
    
    
        cout<<"我正在运行中..."<<endl;
        sleep(1);
        int a=10;
        a/=0;
    }
}

inserte la descripción de la imagen aquí

El resultado de la ejecución del programa es desbordamiento. ¿Por qué a/=0termina el proceso? La terminación del proceso es una señal enviada por el sistema operativo. ¿Cómo se hace?

inserte la descripción de la imagen aquí

Realice cálculos en la CPU y almacene los resultados del cálculo en registros. Dado que los a/=0resultados del cálculo se desbordan, el registro de estado registrará el indicador de desbordamiento como 1. Dado que el sistema operativo administra los recursos de software y hardware, las excepciones de operación de la CPU serán conocidas por él. y luego envíe la señal No. 8 al proceso para permitir que el proceso termine

Verifique de la siguiente manera:

void catchSIG(int signo)
{
    
    
    cout<<"获取一个信号,信号编号是: "<<signo<<endl;
}

int main()
{
    
    
    signal(SIGFPE,catchSIG);
    while(true)
    {
    
    
        cout<<"我正在运行中..."<<endl;
        sleep(1);
        int a=10;
        a/=0;
    }
}

inserte la descripción de la imagen aquí

Se capta la señal, pero el proceso no sale directamente, recibir la señal no necesariamente hace que el proceso salga, ya que el proceso no ha salido, se puede adquirir de nuevo

Solo hay un registro dentro de la CPU, y la memoria en el registro guarda el contexto del proceso; cuando se cambia el proceso, el registro de estado se guardará y restaurará, y cada vez que se restablezca, el sistema operativo reconocerá el estado del registro de estado dentro de la CPU Si el indicador de desbordamiento es 1, la señal No. 8 se enviará al proceso

Señales generadas por las condiciones del software

En la canalización del capítulo anterior, si el extremo de lectura de la canalización está cerrado, el proceso recibirá la señal 13 y terminará el proceso. Esta señal se genera bajo condiciones de software, aquí hay otra señal generada bajo condiciones de software.

unsigned int alarm(unsigned int seconds);

Llamar alarma la función puede configurar un reloj de alarma, es decir, decirle al kernel secondsque envíe una señal al proceso actual en segundos SIGALRM, y el procesamiento predeterminado es terminar el proceso actual

Código:

int main()
{
    
    
    int cnt=0;
    alarm(1);
    while(true)
    {
    
    
        cout<<"cnt: "<<cnt++<<endl;
    }
}

inserte la descripción de la imagen aquí

¿ Por qué se dice alarmque una función es información generada bajo condiciones de software? ? ?

El reloj de alarma en realidad está implementado por software. Cualquier proceso puede alarmconfigurar un reloj de alarma en el kernel a través de una llamada al sistema. Puede haber muchos relojes de alarma en el sistema operativo. Para administrar estos relojes de alarma, el sistema operativo necesita describir y luego organizar

describir primero
inserte la descripción de la imagen aquí

reorganizar

inserte la descripción de la imagen aquí

El sistema operativo comprueba periódicamente estas alarmas; si expiran, el sistema operativo envía una señal SIGALARMal proceso, que alarm.pllama

Finalmente, hay un punto más, el volcado del núcleo cuando finaliza el proceso
Observe el código

int main()
{
    
    
    int a[10];
    a[1000]=10;
    return 0;
}

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Se produce un error de segmento en el programa, y ​​el proceso coresale , y hay una señal encima termde la salida, que se llama salida normal; coreal salir, el programa realizará otras operaciones

coreAl salir del servidor en la nube , no se puede ver ningún fenómeno obvio y está cerrado de forma predeterminadacore file
inserte la descripción de la imagen aquí

Puede abrir el servidor en la nube usted mismocore file

inserte la descripción de la imagen aquí

ejecutar el programa de nuevo

inserte la descripción de la imagen aquí

Aparece un nuevo contenido después de la falla del segmento core dumped, que es el almacenamiento principal: cuando el proceso es anormal, el sistema operativo volcará los datos válidos del proceso en el momento correspondiente al disco. Después de ejecutarse, habrá un archivo adicional core.2545. proceso problemáticopid

El significado del volcado del núcleo es para la depuración, y es más conveniente para los usuarios verificar la causa del bloqueo del proceso
.
inserte la descripción de la imagen aquí

Resumir

  1. Toda la generación de señales anterior la realiza el sistema operativo, porque el sistema operativo administra los recursos de hardware y software.
  2. El procesamiento de la señal no es inmediato, pero cuando sea apropiado
  3. La señal no se procesa inmediatamente, la señal se almacenará temporalmente en la PCB
  4. Antes de que el proceso reciba la señal, ya sabe qué hacer
  5. El sistema operativo envía una señal al proceso, que en realidad es para modificar el mapa de bits en la PCB

señal de bloqueo

Señal Otros conceptos comunes relacionados

  1. La ejecución real de la acción de procesamiento de señales se denomina entrega de señales.
  2. El estado entre la generación y la entrega de la señal se denomina señal pendiente.
  3. Un proceso puede optar por bloquear una señal
  4. Después de generar la señal bloqueada, se mantendrá en estado pendiente hasta que el proceso desbloquee la señal antes de realizar la acción de entrega.
  5. Bloquear e ignorar son diferentes. Mientras la señal esté bloqueada, no se entregará. Ignorar es esencialmente una acción de procesamiento seleccionada después de la entrega.

Representación en el kernel

inserte la descripción de la imagen aquí

Cada proceso tiene dos bits de bandera que representan bloqueo y pendiente respectivamente, y un puntero de función que representa la acción de procesamiento; cuando se genera una señal, el núcleo establece la bandera pendiente de la señal en el bloque de control de proceso y la señal no se borra hasta que la señal se entrega bandera; si no se genera señal, no impide que se bloquee primero

sigset_t

sigset_tCada señal tiene solo un bit, que es 0 o 1. No registra cuántas veces se ha generado la señal, y lo mismo ocurre con la bandera de bloqueo, tanto pendiente como bloqueada se pueden almacenar en el mismo tipo de datos, Llamado conjunto de señales, que puede sigset_tser Indica el estado válido o no válido de cada señal; la señal de bloqueo también se denomina palabra de máscara de señal del proceso actual, y el enmascaramiento es bloquear en lugar de ignorar

Funciones de manipulación de conjuntos de señales

sigset_tEl tipo usa un bit para cada señal para indicar si es válida o no, y cómo almacenar estos bits dentro del tipo depende de la implementación del sistema.

int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set,int signo);
int sigdelset(sigset_t *set,int signo);
int sigismember(const sigset_t *set,int signo);
  1. La función sigemptyinicializa setel conjunto de señales señalado por y borra los bits correspondientes a todas las señales, lo que indica que el conjunto de señales no incluye ninguna señal válida.
  2. La función sigfillsetinicializa setel conjunto de señales al que apunta, de modo que las posiciones de bit correspondientes a todas las señales en él sean 1, lo que indica que las señales efectivas de este conjunto de señales incluyen todas las señales admitidas por el sistema.
  3. Función sigaddsetpara agregar una señal al conjunto de señales; función sigdelsetpara eliminar una señal del conjunto de señales; función sigismemberpara determinar si una determinada señal está en el conjunto de señales

sigprocmáscara

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

Si oldsetes un puntero no nulo, lee la máscara de señal actual del proceso y oldsetla envía a través del parámetro.Si es setun puntero no nulo, cambia la máscara de señal actual del proceso.El parámetro howindica cómo cambiarlo; si oldsetambos setson punteros no nulos, la palabra de máscara de la señal original se respalda en oldset, y luego la palabra de máscara de señal actual se cambia de acuerdo con setlos parámetros yhow

SIG_BLOCK el conjunto contiene todas las señales que se agregarán al conjunto de señales
SIG_UNBLOCK el conjunto contiene todas las señales que se eliminarán del conjunto de señales
SIG_SETMASK Establezca la palabra de máscara de señal actual en el valor señalado por set

pendiente

int sigpending(sigset_t *set);

Lea el conjunto de señales pendientes del proceso actual y setenvíelo a través de parámetros

De forma predeterminada, todas las señales no están bloqueadas. Si la señal está bloqueada, la señal no se entregará.
Implementación de código: bloquear señal 2

#include<iostream>
#include<signal.h>
#include<unistd.h>
#define BLOCK_SIGNAL 2
#define MAX_SIGNUM 31
using namespace std;
void show_pending(const sigset_t& pending)
{
    
    
    for(int signo=MAX_SIGNUM;signo>=1;signo--)
    {
    
    
        if(sigismember(&pending,signo))
        {
    
    
            cout<<"1";
        }
        cout<<"0";
    }
    cout<<"\n";
}

int main()
{
    
    
    //1.屏蔽指定信号
    sigset_t block,oldblock,pending;
    //1.1初始化
    sigemptyset(&block);
    sigemptyset(&oldblock);
    sigemptyset(&pending);
    //1.2添加要屏蔽的信息
    sigaddset(&block,BLOCK_SIGNAL);
    //1.3开始屏蔽
    sigprocmask(SIG_SETMASK,&block,&oldblock);
    
    //2.遍历打印pending信号集
    while(true)
    {
    
    
        //2.1初始化
        sigemptyset(&pending);
        //2.2获取
        sigpending(&pending);
        //2.3打印
        show_pending(pending);
        //间隔一秒
        sleep(1);
    }
    return 0;
}

inserte la descripción de la imagen aquí

señal de captura

Cómo implementa el kernel la captura de señales

Los procesos en el sistema operativo existen en dos estados: modo de usuario y modo kernel; el modo de usuario generalmente accede a los recursos del sistema operativo y los recursos de hardware. Para lograr este propósito, se debe usar la interfaz de llamada del sistema proporcionada por el sistema y la identidad del sistema. la llamada debe ser Es el núcleo, ¿por qué los usuarios pueden acceder a la interfaz de llamada del sistema? ? ?

Hay muchos registros en la CPU, que se dividen en: registros visibles y registros invisibles, siempre que estén fuertemente relacionados con el proceso, almacenan los datos de contexto del proceso, el registro denominado CR3 almacena el nivel de ejecución del proceso actual . proceso: 0 significa modo Kernel, 3 significa modo usuario, en la posición inicial de la interfaz de llamada del sistema, hay un ensamblado int 80 , que modificará el modo de usuario al modo kernel, para que se pueda acceder a la interfaz de llamada del sistema como el modo de núcleo

¿Qué pasa con el proceso específico del proceso que accede a la interfaz de llamada del sistema como kernel? ? ?
Aprendí en el espacio de proceso anterior que el espacio de proceso incluye espacio de usuario y espacio de núcleo, y la interfaz de llamada del sistema está relacionada con este espacio de núcleo: cada proceso tiene su propio espacio de proceso, que es exclusivo del espacio de usuario, y solo hay una copia del espacio del kernel, es decir, se dice que todos los procesos comparten el mismo espacio del kernel; cuando un proceso accede a una interfaz, solo necesita saltar en el espacio del proceso, que es similar a cargar una biblioteca dinámica en el espacio del proceso

Ilustración:

inserte la descripción de la imagen aquí

Al arrancar, el sistema operativo se cargará desde el disco en el área del kernel en la memoria. Cuando el proceso acceda a la llamada del sistema como el estado del kernel, saltará al espacio del kernel en el espacio del proceso y lo asignará a la memoria a través de la tabla de páginas de nivel de kernel Sistema operativo Complete la llamada correspondiente y luego vuelva al espacio de usuario

Cuando se genera la señal, no será procesada inmediatamente, sino que será procesada por el sistema operativo en el momento apropiado. Este momento apropiado es cuando regresa del estado del kernel al estado del usuario; por lo tanto, el proceso primero ingresa al estado del kernel. al cambiar procesos o llamadas al sistema, para realizar la detección de señal en modo kernel, es decir, dos bits de bandera ( pending/block) y un puntero de función ( handler*) en el proceso: si la señal está pendiente y no está bloqueada, verifique si el puntero de función tiene un personalizado correspondiente método de procesamiento, si es así, cambie la identidad del estado del kernel del proceso a la identidad del usuario para completar el método de procesamiento correspondiente, luego restaure la identidad del kernel y complete las llamadas al sistema restantes. estado de usuario y continuar ejecutando códigos subsiguientes

Ilustración:

inserte la descripción de la imagen aquí

Cabe señalar que el código del modo de usuario no se puede ejecutar como el modo kernel, porque el sistema operativo no confía en nadie para evitar daños maliciosos.

seguiracción

int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
  1. signum: número de señal a capturar
  2. act: puntero de estructura
    inserte la descripción de la imagen aquí

que contiene métodos de procesamiento sa_handlery conjuntos de señalessa_mask

Por ejemplo, utilice esta función para capturar la señal n.° 2, tome un descanso de 20 segundos después de capturar la señal, envíe la señal n.° 2 al proceso varias veces y observe los resultados de ejecución del proceso.

void Count(int cnt)
{
    
    
    while(cnt)
    {
    
    
        printf("cnt:%2d\r",cnt);
        fflush(stdout);
        cnt--;
        sleep(1);
    }
    printf("\n");
}

void handler(int signo)
{
    
    
    cout<<"get a signo"<<signo<<"正在处理..."<<endl;
    Count(20);
}

int main()
{
    
    
    struct sigaction act,oldact;
    act.sa_handler=handler;
    act.sa_flags=0;
    sigemptyset(&act.sa_mask);
    sigaction(SIGINT,&act,&oldact);
    while(true)
    sleep(1);
    return 0;
}

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Aunque la misma señal se envía al proceso varias veces, el proceso solo la capta dos veces, porque cuando el proceso entrega una determinada señal, no se puede alcanzar el mismo tipo de señal y el sistema agregará automáticamente la señal actual a la máscara. En la palabra, pendingcambie la posición de la señal en el mapa de bits a 0 y envíe la misma señal nuevamente, y la pendingposición de la señal en el mapa de bits cambiará a 1; cuando el proceso complete la entrega de la señal, el sistema liberará automáticamente la señal Shield, por lo que el sistema entregará inmediatamente pendingla señal en el mapa de bits, es decir, capturará la segunda señal

Cuando estamos procesando la señal No. 2, también queremos bloquear la señal No. 3. En este momento, solo necesitamos agregar la señal No. 3 al sa_maskconjunto de señales.

void Count(int cnt)
{
    
    
    while(cnt)
    {
    
    
        printf("cnt:%2d\r",cnt);
        fflush(stdout);
        cnt--;
        sleep(1);
    }
    printf("\n");
}

void handler(int signo)
{
    
    
    cout<<"get a signo"<<signo<<"正在处理..."<<endl;
    Count(20);
}

int main()
{
    
    
    struct sigaction act,oldact;
    act.sa_handler=handler;
    act.sa_flags=0;
    sigemptyset(&act.sa_mask);
    //添加3号信号
    sigaddset(&act.sa_mask,3);
    sigaction(SIGINT,&act,&oldact);
    while(true)
    sleep(1);
    return 0;
}

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

La diferencia con lo anterior es que aquí el proceso sale directamente al final, de hecho es porque después de bloquear la señal N° 2 se vuelve a ejecutar la señal N° 2 y finalmente se ejecuta la señal N° 3 para finalizar el proceso.

El principio del procesamiento de señales de proceso es procesar señales del mismo tipo en serie, no se permite la recursividad.

Supongo que te gusta

Origin blog.csdn.net/qq_68006585/article/details/130574157
Recomendado
Clasificación