>

내 코드는

#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;
    }

코드를 실행하는 동안 세그먼테이션 오류가 발생합니다. SIG_IGN을 사용하여 신호를 무시했습니다. 따라서 분할 오류가 발생하지 않아야합니다. 권리? 그런 다음 printf()  '* p'인쇄 후 명령문도 실행되어야합니다. 권리?

  • 답변 # 1

    코드가 SIGSEGV를 잡는 대신 무시하고 있습니다. 신호를 트리거 한 명령은 신호를 처리 한 후 다시 시작됩니다. 귀하의 경우 신호를 처리해도 아무런 변화가 없으므로 다음 번 문제를 일으키는 명령을 시도하면 같은 방식으로 실패합니다.

    신호 변경을 잡으려면

    signal(SIGSEGV, SIG_IGN);
    
    

    이것에

    signal(SIGSEGV, sighandler);
    
    

    아마도 sigaction() 를 사용해야합니다   signal() 대신 . 관련 매뉴얼 페이지를 참조하십시오.

    귀하의 경우 문제를 일으키는 명령은 NULL 포인터를 역 참조하려는 명령입니다.

    printf("%d", *p);
    
    

    다음은 플랫폼에 따라 다릅니다.

    gdb 를 사용할 수 있습니다  신호를 트리거하는 특정 어셈블리 명령을 설정합니다. 플랫폼이 내 것과 같은 것이면 지시 사항을 찾을 수 있습니다

    movl    (%rax), %esi
    
    

    랙스 레지스터 보유 값 0, 즉 NULL . 신호 처리기 에서이 문제를 해결하는 한 가지 (휴대 할 수없는!) 방법은 처리기가 얻는 세 번째 인수 신호, 즉 사용자 컨텍스트를 사용하는 것입니다. 예를 들면 다음과 같습니다.

    #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;
    }
    
    

    이 프로그램은 다음을 표시합니다 :

    Handler executed for signal 11
    100
    
    

    먼저 NULL 주소 역 참조를 시도하여 핸들러가 실행되도록합니다. 그런 다음 핸들러는 rax를 변수 n 의 주소로 설정하여 문제를 해결합니다. . 핸들러가 시스템을 반환하면 문제가되는 명령을 다시 시도하고 이번에는 성공합니다. 와이즈 비즈  두 번째 인수로 100을받습니다.

    귀하의 프로그램에서 이식성이없는 솔루션을 사용하지 말 것을 강력히 권장합니다.

  • 답변 # 2

    신호를 무시할 수는 있지만 그것에 대해 무언가를해야합니다. 나는 당신이 게시 한 코드에서 무엇을하고 있는지 믿습니다 ( printf() 무시)   SIGSEGV 를 통해 ) 굵은 글 머리 기호를 읽은 후 명확 해지기 때문에작동하지 않습니다.

    커널이 SIGSEGV를 보내 게하는 무언가를 할 때 :

    시그널 핸들러가없는 경우 커널은 프로세스를 종료하고 그게 바로

    신호 처리기가있는 경우

    핸들러가 호출됩니다

    커널이 문제가있는 작업을 다시 시작합니다

    따라서 아무 것도하지 않으면 계속 반복됩니다. 당신이경우 SIG_IGN 를 잡아  종료하지 않으면 정상적인 흐름을 방해 할 수 있습니다.

    가해 행위가 다시 시작되지 않도록 수정하거나

    불쾌한 내용이 문제가되지 않도록 메모리 레이아웃을 수정하십시오. 다음 실행

  • 답변 # 3

    또 다른 옵션은 setjmp/longjmp (예 :

    SIGSEGV
    
    

    여기서 setjmp/longjmp 패턴은 try/catch 블록과 유사합니다. 그러나 매우 위험하며 위험한 기능이 스택을 초과하거나 리소스를 할당하지만 해제되기 전에 충돌하는 경우 저장하지 않습니다. 포인터를 확인하고 나쁜 포인터를 통해 간접적으로 확인하는 것이 좋습니다.

  • 답변 # 4

    SIGSEGV를 무시하거나 처리하려고 시도하는 것은 잘못된 방법입니다. 프로그램항상에 의해 트리거 된 SIGSEGV는 버그를 나타냅니다. 코드 나 위임 한 코드 중 하나입니다. 버그가 발생하면무엇이든발생할 수 있습니다. 신호 처리기가 수행 할 수있는 조치 또는 수행 할 조치를 알 수 없기 때문에 신호 처리기가 수행 할 수있는 합리적인 "정리"또는 수정 조치가 없습니다. 최선의 방법은 프로그램이 빠르게 실패하도록하는 것이므로 프로그래머는 프로그램이 여전히 즉각적인 실패 상태에있을 때 디버깅 할 기회를 가지게됩니다. 나중에 실패 원인이 발생한 경우 실패 할 가능성이 있습니다. 가려졌습니다. 신호를 무시하거나 처리하지 않으면 프로그램이 빠르게 실패 할 수 있습니다.

    #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; }

관련 자료

  • 이전 c# - UTC 오프셋으로 시간대를 어떻게 확인할 수 있습니까?
  • 다음 javascript - 여러 문자열을 replace () 메서드로 바꾸는 방법은 무엇입니까?