señal de proceso
Primeros pasos con las señales
La señalización se divide en cuatro fases:
- preparación
- producir
- mantener
- 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
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
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 PCB
el PCB
mapa 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);
}
}
La tecla de acceso rápido ctrl+c
puede terminar el proceso, la esencia es que el sistema operativo lo interpreta como la señal No. 2SIGINT
introducir una función
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signum
: número de señalhandler
: 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);
}
}
Aunque signal
la 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);
pid
: del proceso en ejecuciónpid
sig
:sig
Enviar 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;
}
Signal 19 puede detener el proceso
aumentar
int raise(int sig);
Enviar la señal sig
directamente 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;
}
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;
}
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;
}
}
El resultado de la ejecución del programa es desbordamiento. ¿Por qué a/=0
termina el proceso? La terminación del proceso es una señal enviada por el sistema operativo. ¿Cómo se hace?
Realice cálculos en la CPU y almacene los resultados del cálculo en registros. Dado que los a/=0
resultados 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;
}
}
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 alarm
a la función puede configurar un reloj de alarma, es decir, decirle al kernel seconds
que 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;
}
}
¿ Por qué se dice alarm
que una función es información generada bajo condiciones de software? ? ?
El reloj de alarma en realidad está implementado por software. Cualquier proceso puede alarm
configurar 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
reorganizar
El sistema operativo comprueba periódicamente estas alarmas; si expiran, el sistema operativo envía una señal SIGALARM
al proceso, que alarm.p
llama
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;
}
Se produce un error de segmento en el programa, y el proceso core
sale , y hay una señal encima term
de la salida, que se llama salida normal; core
al salir, el programa realizará otras operaciones
core
Al salir del servidor en la nube , no se puede ver ningún fenómeno obvio y está cerrado de forma predeterminadacore file
Puede abrir el servidor en la nube usted mismocore file
ejecutar el programa de nuevo
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
.
Resumir
- Toda la generación de señales anterior la realiza el sistema operativo, porque el sistema operativo administra los recursos de hardware y software.
- El procesamiento de la señal no es inmediato, pero cuando sea apropiado
- La señal no se procesa inmediatamente, la señal se almacenará temporalmente en la PCB
- Antes de que el proceso reciba la señal, ya sabe qué hacer
- 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
- La ejecución real de la acción de procesamiento de señales se denomina entrega de señales.
- El estado entre la generación y la entrega de la señal se denomina señal pendiente.
- Un proceso puede optar por bloquear una señal
- 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.
- 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
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_t
Cada 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_t
ser 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_t
El 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);
- La función
sigempty
inicializaset
el 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. - La función
sigfillset
inicializaset
el 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. - Función
sigaddset
para agregar una señal al conjunto de señales; funciónsigdelset
para eliminar una señal del conjunto de señales; funciónsigismember
para 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 oldset
es un puntero no nulo, lee la máscara de señal actual del proceso y oldset
la envía a través del parámetro.Si es set
un puntero no nulo, cambia la máscara de señal actual del proceso.El parámetro how
indica cómo cambiarlo; si oldset
ambos set
son 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 set
los 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 set
enví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;
}
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:
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:
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);
signum
: número de señal a capturaract
: puntero de estructura
que contiene métodos de procesamiento sa_handler
y 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;
}
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, pending
cambie la posición de la señal en el mapa de bits a 0 y envíe la misma señal nuevamente, y la pending
posició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 pending
la 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_mask
conjunto 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;
}
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.