2011-12-10 10 views
13

Aquí está mi código,¿Por qué no puedo ignorar la señal SIGSEGV?

#include<signal.h> 
#include<stdio.h> 

int main(int argc,char ** argv) 
    { 
    char *p=NULL; 
    signal(SIGSEGV,SIG_IGN); //Ignoring the Signal 
    printf("%d",*p); 
    printf("Stack Overflow"); //This has to be printed. Right? 
    return 0; 
    } 

Mientras se ejecuta el código, me estoy poniendo fallo de segmentación. Ignoré la señal usando SIG_IGN. Por lo tanto, no debería obtener un error de segmentación. ¿Derecha? Luego, la sentencia printf() después de imprimir el valor '* p' debe ejecutarse también. ¿Derecha?

+11

Habrá un tiempo en el que el código de escritura que traga segfaults será considerado suficiente para poner al programador en la cárcel. – 6502

Respuesta

18

Su código está ignorando SIGSEGV en lugar de atraparlo. Recuerde que la instrucción que activó la señal se reinicia después de manejar la señal. En su caso, manejar la señal no cambió nada, así que la próxima vez que se intente la instrucción ofensiva, falla de la misma manera.

Si tiene la intención de captar la señal de este cambio

signal(SIGSEGV, SIG_IGN); 

a este

signal(SIGSEGV, sighandler); 

Debería probablemente utilizar en lugar de sigaction()signal(). Ver páginas man relevantes.

En su caso, la instrucción ofensiva es la que intenta desreferenciar el puntero NULL.

printf("%d", *p); 

Lo que sigue depende completamente de su plataforma.

Puede usar gdb para establecer qué instrucción de ensamblaje particular desencadena la señal.Si la plataforma es como la mía, se encuentra la instrucción es

movl (%rax), %esi 

con registro rax valor 0 sosteniendo, es decir NULL. Una forma (¡no portátil!) De solucionar esto en su manejador de señal es usar la tercera señal de argumento que obtiene su manejador, es decir, el contexto del usuario. Este es un ejemplo:

#include <signal.h> 
#include <stdio.h> 

#define __USE_GNU 
#include <ucontext.h> 

int *p = NULL; 
int n = 100; 

void sighandler(int signo, siginfo_t *si, ucontext_t* context) 
{ 
    printf("Handler executed for signal %d\n", signo); 
    context->uc_mcontext.gregs[REG_RAX] = &n; 
} 

int main(int argc,char ** argv) 
{ 
    signal(SIGSEGV, sighandler); 
    printf("%d\n", *p); // ... movl (%rax), %esi ... 
    return 0; 
} 

Este programa muestra:

Handler executed for signal 11 
100 

Es primero hace que el controlador para ser ejecutado por el intento de eliminar la referencia a Dirección NULL. A continuación, el controlador soluciona el problema configurando rax en la dirección de la variable n. Una vez que el manejador retorna, el sistema reintenta la instrucción ofensiva y esta vez tiene éxito. printf() recibe 100 como segundo argumento.

No recomiendo usar estas soluciones no portátiles en sus programas.

+0

Gracias por su explicación detallada Adam :) – Dinesh

13

Puede ignorar la señal pero tiene que hacer algo al respecto. Creo que lo que está haciendo en el código publicado (ignorando SIGSEGV a través de SIG_IGN) no funcionará en absoluto por razones que resultarán obvias después de leer la viñeta en negrita.

Cuando haces algo que hace que el núcleo que le envíe un SIGSEGV:

  • Si usted no tiene un manejador de señales, el núcleo mata el proceso y eso es que
  • Si usted tiene una manejador de señales
    • Su manejador es llamado
    • el núcleo se reinicia la operación infractor

De modo que si no hace nada, simplemente se repetirá continuamente. Si haces captura SIGSEGV y no sale, lo que interfiere con el flujo normal, debe:

  • arreglar las cosas de tal manera que la operación infractor no se reinicia o
  • fijar el diseño de memoria, tales que lo que ofende estará bien en la siguiente ejecución
+0

Ok, entonces, ¿qué tengo que hacer exactamente? – Dinesh

+0

@Dinesh, ¿qué es lo que estás tratando de lograr? – ibid

+0

@ibid, en el código, estoy tratando de acceder a una memoria nula de memoria. ASÍ, esto conduce a la creación de la señal SIGSEGV. Pero hice un controlador para eso que simplemente imprimirá "Capturando la señal". Posteriormente, debe ejecutarse la instrucción "return 0" que reside en main(). ¿Derecha? – Dinesh

9

Otra opción es poner entre paréntesis la operación arriesgado con setjmp/longjmp, es decir

#include <setjmp.h> 
#include <signal.h> 

static jmp_buf jbuf; 
static void catch_segv() 
{ 
    longjmp(jbuf, 1); 
} 

int main() 
{ 
    int *p = NULL; 

    signal(SIGSEGV, catch_segv); 
    if (setjmp(jbuf) == 0) { 
     printf("%d\n", *p); 
    } else { 
     printf("Ouch! I crashed!\n"); 
    } 
    return 0; 
} 

El patrón setjmp/longjmp aquí es similar a un bloque try/catch. Sin embargo, es muy arriesgado y no lo salvará si su arriesgada función sobrepasa la pila o asigna recursos, pero se bloquea antes de que se liberen. Es mejor verificar tus punteros y no indirectos a través de los malos.

+1

Por lo que puedo decir, esto no funciona si se encuentra con el segfault varias veces (la segunda vez que el proceso todavía segfaults). AFAIU 'longjmp' /' setjmp' no maneja el contexto de la señal correctamente, y 'sigsetjmp' /' siglongjmp' se debe usar en su lugar. Cf "Notas" en https://linux.die.net/man/2/setcontext – mortenpi

0

ANS: que asignó NULL para el puntero p

char *p = NULL; 

A continuación, se imprimen * valor de p:

printf("%d",*p); 

que significa que desea imprimir valor ubicado en la dirección nulo (es decir; 0 lugar de dirección) que no es posible. Es por eso que está dando la falla de segmentación. La falla de segmentación ocurre en el caso de acceder a la memoria que no está disponible ahora para el programa.

Prueba esto:

char *p = (char*)malloc(sizeof(char)); 
*p = '\0'; 
printf("%d",*p); 

** NOTA: ** Este tipo de error se llama como colgando error de puntero. El puntero colgante es el caso de error/excepción donde la sintaxis es correcta pero el usuario intenta acceder al valor del puntero que ya apunta a NULL.

Cuestiones relacionadas