2012-05-26 15 views
7

He estado estudiando señales en Linux. Y he hecho un programa de prueba para capturar SIGINT.¿Por qué Linux siempre muestra "^ C" al presionar Ctrl + C?

#include <unistd.h> 
#include <signal.h> 
#include <iostream> 
void signal_handler(int signal_no); 
int main() { 
    signal(SIGINT, signal_handler); 
    for (int i = 0; i < 10; ++i) { 
    std::cout << "I'm sleeping..." << std::endl; 
    unsigned int one_ms = 1000; 
    usleep(200* one_ms); 
    } 
    return 0; 
} 
void signal_handler(int signal_no) { 
    if (signal_no == SIGINT) 
    std::cout << "Oops, you pressed Ctrl+C!\n"; 
    return; 
} 

mientras que la salida se ve así:

I'm sleeping... 
I'm sleeping... 
^COops, you pressed Ctrl+C! 
I'm sleeping... 
I'm sleeping... 
^COops, you pressed Ctrl+C! 
I'm sleeping... 
^COops, you pressed Ctrl+C! 
I'm sleeping... 
^COops, you pressed Ctrl+C! 
I'm sleeping... 
^COops, you pressed Ctrl+C! 
I'm sleeping... 
I'm sleeping... 
I'm sleeping... 

entiendo que al pulsar Ctrl + C, procesa en el grupo de procesos en primer plano todo recibe una SIGINT (si no hay proceso elige ignorarlo).

Entonces, ¿el shell (bash) Y la instancia del programa anterior recibieron la señal? ¿De dónde viene el "^ C" antes de cada "Oops"?

El sistema operativo es CentOS, y el shell es bash.

Respuesta

9

Es el terminal (conductor) que intercepta el^C y la traduce a una señal enviada al proceso adjunto (que es el shell) stty intr ^B daría instrucciones al terminal de controlador para interceptar un^B en su lugar. También es el controlador de terminal que hace eco del^C volver al terminal.

El shell es simplemente un proceso que se encuentra en el otro extremo de la línea, y recibe su stdin de su terminal a través del controlador de terminal (como/dev/ttyX), y su stdout (y stderr) también están conectados a la misma tty.

Tenga en cuenta que (si echoing es en abled) el terminal envía las teclas a ambos el proceso (grupo) y vuelve al terminal. El comando stty es solo envoltorio alrededor del ioctl() s para el controlador tty para los procesos que "controlan" tty.

ACTUALIZACIÓN: para demostrar que el shell no está involucrado, creé el siguiente pequeño programa. Debería ser ejecutado por su shell principal a través del exec ./a.out (parece que un shell interactivo bifurcará un shell hija, de todos modos) El programa establece la clave que genera el SIGINTR en^B, desactiva el eco y luego espera la entrada de stdin.

#include <stdio.h> 
#include <string.h> 
#include <termios.h> 
#include <unistd.h> 
#include <signal.h> 
#include <errno.h> 

int thesignum = 0; 
void handler(int signum); 

void handler(int signum) 
{ thesignum = signum;} 

#define THE_KEY 2 /* ^B */ 

int main(void) 
{ 
int rc; 
struct termios mytermios; 

rc = tcgetattr(0 , &mytermios); 
printf("tcgetattr=%d\n", rc); 

mytermios.c_cc[VINTR] = THE_KEY; /* set intr to ^B */ 
mytermios.c_lflag &= ~ECHO ; /* Dont echo */ 
rc = tcsetattr(0 , TCSANOW, &mytermios); 
printf("tcsetattr(intr,%d) =%d\n", THE_KEY, rc); 

printf("Setting handler()\n"); 
signal(SIGINT, handler); 

printf("entering pause()\n... type something followed by ^%c\n", '@'+THE_KEY); 
rc = pause(); 
printf("Rc=%d: %d(%s), signum=%d\n", rc, errno , strerror(errno), thesignum); 

// mytermios.c_cc[VINTR] = 3; /* reset intr to ^C */ 
mytermios.c_lflag |= ECHO ; /* Do echo */ 
rc = tcsetattr(0 , TCSANOW, &mytermios); 
printf("tcsetattr(intr,%d) =%d\n", THE_KEY, rc); 

return 0; 
} 

intr.sh:

#!/bin/sh 
echo $$ 
exec ./a.out 
echo I am back. 
+0

Lo curioso es que la otra respuesta (que está completamente equivocada) todavía recibe votos explícitos, solo porque contiene enlaces a los Libros Correctos. – wildplasser

+1

@WiSaGaN ¿Cómo puede ser la misma respuesta "casi correcta" (comentar) y "completamente incorrecta" (comentar OP). ** Por supuesto ** los controladores del dispositivo están involucrados cuando se comunica con un sistema a través de periféricos (¿cómo es que no mencionó el controlador del teclado?) Proporcionar más detalles no hace que una respuesta "casi correcta" sea "completamente errónea". Si fuera al nivel del lenguaje de máquina y las tablas de búsqueda, ¿eso haría que tu respuesta fuera incorrecta? Apenas. Proporcioné enlaces a libros para que OP pueda buscar más detalles según sea necesario. OP sintió que mi respuesta fue útil. Tu comentario actual es contradictorio y poco sincero en el mejor de los casos. – Levon

+0

Lo incorrecto es que * el shell no está involucrado *. La conversión de^C a INTR tiene lugar en el controlador del terminal. El caparazón es solo un receptor (como cualquier otro proceso). El astuto no apunta al caparazón, sino al tty. – wildplasser

6

El shell hace eco de todo lo que escribe, por lo que cuando escribe ^C, también se hace eco (y en su caso interceptado por su manejador de señal). El comando stty -echo puede o no serle útil según sus necesidades/limitaciones, consulte man page for stty para obtener más información.

Por supuesto mucho más continúa en un nivel inferior, cualquier momento se comunican con un sistema a través de los controladores de dispositivos periféricos (como los de teclado que se utiliza para generar la señal^C, y el conductor terminal que muestra todo) estan involucrados. Puede profundizar aún más en el nivel del lenguaje ensamblador/máquina, registros, tablas de búsqueda, etc. Si desea un nivel de comprensión más detallado y profundo, los libros a continuación son un buen lugar para comenzar:

The Design of the Unix OS es un buena referencia para este tipo de cosas. Otras dos referencias clásicas: Unix Programming Environment y Advanced Programming in the UNIX Environment

buen resumen aquí en esta cuestión de forma How does Ctrl-C terminate a child process?

"cuando estás ejecutar un programa, por ejemplo find, la cáscara:

  • el propio tenedor cáscara
  • y para el niño establece la gestión de señal predeterminada
  • reemplazar el niño con el comando dado (por ejemplo, con aleta) d)
  • cuando presiona CTRL-C, el shell primario maneja esta señal pero el niño lo recibirá, con la acción predeterminada, finalizará. (El niño puede poner en práctica el manejo de señales también)"
+0

¿Quiere decir que aquí es fiesta en el grupo de procesos en primer plano, así, por lo que recibió la señal enviada por el núcleo? – WiSaGaN

+1

@WiSaGaN Quiero decir es que usa el teclado para comunicarse con su programa, y ​​el shell está entre usted y el programa (es decir, la * interfaz *) para que intercepte esta acción. Lo que su manejador de señal hace es determinar la acción * subsiguiente * que ocurre después de que se ve el '^ C'. Entonces, en lugar de finalizar el programa, puede hacer que haga otra cosa, como imprimir un mensaje en su caso. – Levon

+0

Por lo tanto, es en realidad el shell que envía la señal al grupo de procesos en primer plano (kernel realmente lo implementa). Y entonces los eventos subyacentes son, el teclado presiona Ctrl + C y señala a la CPU. Luego, una interrupción de hardware invocó al controlador de interrupciones, que le dice al shell que Ctrl + C ha presionado. Entonces, una señal es enviada por el caparazón. Por lo tanto, la conversión "Ctrl + C" a la "señal SIGINT" se realiza mediante el shell. ¿Estoy en lo correcto? – WiSaGaN

Cuestiones relacionadas