내 세그멘테이션 오류는 어디로 사라졌나?

1 hour ago 1
  • entr로 C 프로그램을 반복 컴파일·실행하자 hello가 비정상 종료됐지만, 컴파일 출력 뒤에 Segmentation fault 메시지가 나타나지 않음
  • 이 메시지는 충돌한 프로그램이 아니라, 자식 프로세스를 회수하며 SIGSEGV 종료를 확인한 부모 셸이 출력함
  • bash -c를 명시해도 남은 명령이 사실상 하나뿐이면 Bash가 새 프로세스를 만들지 않고 execve로 자신을 교체할 수 있어 메시지를 출력할 부모 셸이 사라짐
  • 별도 스크립트에서는 shebang으로 시작된 Bash가 ./hello를 자식으로 실행하고 기다리므로, SIGSEGV를 확인해 Segmentation fault (core dumped) 를 출력함
  • ./hello를 서브셸 (./hello)에서 실행하거나 뒤에 ; true를 추가해 후속 작업을 남기면 exec 최적화를 막고 오류 메시지를 확인할 수 있음

메시지가 사라진 실행 경로

  • 다음 entr 명령으로 hello.c가 바뀔 때마다 컴파일과 실행을 반복함

    ls hello.c | entr -s "gcc -o hello hello.c && ./hello"
  • hello가 세그멘테이션 오류로 종료됐지만 Segmentation fault 메시지나 눈에 보이는 0이 아닌 종료 상태 없이 아무 출력도 남지 않음

  • bash -c로 명령을 감싸도 결과는 같았음

    ls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello'"
  • 반면 같은 명령을 run.sh로 옮겨 실행하면 오류가 표시됨

    #!/bin/bash gcc -o hello hello.c && ./hello ls hello.c | entr -s ./run.sh ./run.sh: line 2: 104465 Segmentation fault (core dumped) ./hello
  • Segmentation fault를 출력하는 주체

    • 충돌한 프로그램은 이미 종료된 상태이며, 부모 셸이 자식 프로세스를 회수하면서 SIGSEGV로 죽었다는 사실을 확인한 뒤 메시지를 출력함
    • 따라서 메시지를 출력할 부모 셸이 남아 있지 않으면 아무것도 나타나지 않음
    • Bash는 bash -c "some_command"에서 실행할 일이 사실상 해당 명령뿐이면 새 프로세스를 포크하지 않고 execve로 자신을 교체할 수 있음
    • 이 최적화는 대부분의 실행에서는 외부로 드러나지 않음
    • 스크립트 방식에서는 entr가 ./run.sh를 자식으로 실행하고, shebang이 새 Bash를 시작함
    • 새 Bash는 gcc 실행 후 ./hello를 포크하고 기다리므로, 자식의 SIGSEGV 종료를 확인해 오류 메시지를 남길 수 있음

부모 셸을 남기는 두 가지 방법

  • 충돌하는 명령을 서브셸에서 실행하면 메시지가 다시 나타남 ls hello.c | entr -s "gcc -o hello hello.c && (./hello)" hello.c: line 1: 106595 Segmentation fault (core dumped) ( ./hello )
    • 괄호는 ./hello가 포크된 서브셸에서 실행되도록 강제함
    • Bash가 부모 역할을 유지하므로 서브셸의 SIGSEGV 종료를 회수해 메시지를 출력할 수 있음
  • 충돌 명령 뒤에 실행할 작업을 남겨도 exec 최적화를 막을 수 있음 ls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello; true'" bash: line 1: 109516 Segmentation fault (core dumped) ./hello
    • ; true 때문에 Bash는 ./hello가 끝난 뒤에도 할 일이 남아 있어 자신을 해당 프로그램으로 교체하지 않음
    • 별도 래퍼 스크립트 없이도 세그멘테이션 오류 메시지를 확인할 수 있음
Read Entire Article