“CPU의 MMU가 신호를 보낸다”와 “커널이 문제를 일으키는 프로그램으로 보내서 종료시킨다”는 것 외에는 이것에 관한 정보를 찾을 수없는 것 같습니다.
나는 아마도 신호를 쉘에 보내고 쉘은 문제를 일으키는 프로세스를 종료하고 인쇄하여 신호를 처리한다고 가정했다 "Segmentation fault"
. 그래서 나는 crsh (crap shell) 라고 불리는 매우 작은 쉘을 작성하여 그 가정을 테스트했습니다 . 이 쉘은 사용자 입력을 받아 system()
메소드에 피드하는 것 외에는 아무것도하지 않습니다 .
#include <stdio.h>
#include <stdlib.h>
int main(){
char cmdbuf[1000];
while (1){
printf("Crap Shell> ");
fgets(cmdbuf, 1000, stdin);
system(cmdbuf);
}
}
그래서이 쉘을 맨 터미널에서 ( bash
아래에서 실행 하지 않고) 실행했습니다. 그런 다음 segfault를 생성하는 프로그램을 계속 진행했습니다. 내 가정이 정확하면 a) crash crsh
, xterm 닫기, b) 인쇄 안 함 "Segmentation fault"
또는 c) 둘 다입니다.
braden@system ~/code/crsh/ $ xterm -e ./crsh
Crap Shell> ./segfault
Segmentation fault
Crap Shell> [still running]
정사각형으로 돌아가서 방금이 작업을 수행하는 것이 쉘이 아니라 그 아래 시스템이라는 것을 보여주었습니다. “세그먼트 결함”은 어떻게 인쇄 되나요? “누가”하고 있습니까? 커널? 다른 것? 신호와 모든 부작용은 하드웨어에서 프로그램의 최종 종료로 어떻게 전파됩니까?
답변
모든 최신 CPU는 현재 실행중인 기계 명령 을 중단 할 수 있습니다. 아무 일도 없었던 것처럼 나중에 실행 을 재개 할 수 있도록 충분한 상태 (일반적으로 항상 스택에있는 것은 아님)를 저장 합니다 (중단 된 명령은 처음부터 처음부터 다시 시작됨). 그런 다음 인터럽트 처리기 실행을 시작합니다. 인터럽트 처리기 는 더 많은 머신 코드이지만 CPU가 미리 어디에 있는지 알 수 있도록 특별한 위치에 배치됩니다. 인터럽트 처리기는 항상 운영 체제 커널 의 일부입니다. 가장 큰 권한으로 실행되고 다른 모든 구성 요소의 실행을 감독하는 구성 요소입니다. 1,2
인터럽트는 동기식 일 수 있습니다 . 즉, CPU 자체에서 현재 실행중인 명령에 대한 직접 응답으로 트리거되거나 비동기식 일 수 있습니다. 즉, 네트워크에 도착하는 데이터와 같은 외부 이벤트로 인해 예기치 않은 시간에 발생합니다. 포트. 일부 사람들은 비동기 인터럽트에 대해 “인터럽트”라는 용어를 예약하고 대신에 동기 인터럽트 “트랩”, “오류”또는 “예외”를 호출하지만 이러한 단어는 모두 다른 의미를 가지므로 “동기 인터럽트”를 고수 할 것입니다.
이제 대부분의 최신 운영 체제에는 프로세스 개념이 있습니다 . 기본적으로 이것은 컴퓨터가 동시에 여러 프로그램을 실행할 수있는 메커니즘이지만 운영 체제가 메모리 보호를 구성하는 방법의 주요 측면이기도합니다 . 아직도 모든 ) 현대 CPU를. 그것은 가상 메모리 와 함께 간다메모리 주소와 RAM의 실제 위치 간의 매핑을 변경하는 기능입니다. 메모리 보호를 통해 운영 체제는 각 프로세스에 전용 RAM 덩어리를 제공하여 액세스 할 수 있습니다. 또한 운영 체제 (일부 프로세스를 대신하여 작동)가 RAM 영역을 읽기 전용, 실행 가능, 협력 프로세스 그룹간에 공유 등으로 지정할 수 있습니다. 또한 메모리 만 액세스 할 수있는 메모리 덩어리가 있습니다. 핵심. 삼
각 프로세스가 CPU가 허용하도록 구성된 방식으로 만 메모리에 액세스하는 한 메모리 보호는 보이지 않습니다. 프로세스가 규칙을 어기면 CPU는 동기식 인터럽트를 생성하여 커널에게 항목을 정렬하도록 요청합니다. 프로세스가 실제로 규칙을 위반 하지 않았기 때문에 프로세스가 계속 진행되기 전에 커널 만 약간의 작업을 수행해야합니다. 예를 들어, RAM의 공간을 확보하기 위해 프로세스 메모리의 페이지를 스왑 파일로 “제거”해야하는 경우 커널은 해당 페이지를 액세스 할 수 없음으로 표시합니다. 다음에 프로세스가 프로세스를 사용하려고하면 CPU가 메모리 보호 인터럽트를 생성합니다. 커널은 스왑에서 페이지를 검색하여 원래 위치로 되돌리고 다시 액세스 가능으로 표시 한 후 실행을 재개합니다.
그러나 프로세스가 실제로 규칙을 어겼다 고 가정하십시오. RAM이 매핑되지 않은 페이지에 액세스하려고 시도했거나 머신 코드를 포함하지 않는 것으로 표시된 페이지를 실행하려고했습니다. 일반적으로 “Unix”로 알려진 운영 체제 제품군은 이 상황을 처리 하기 위해 신호 를 사용 합니다. 4 신호는 인터럽트와 유사하지만 하드웨어에 의해 생성되고 커널에 의해 필드 화되는 것이 아니라 커널에 의해 생성되고 프로세스에 의해 필드 화됩니다. 프로세스는 신호 핸들러를 정의 할 수 있습니다그들 자신의 코드로 커널에게 그들이 어디에 있는지 알려주십시오. 그런 다음 해당 신호 처리기가 실행되어 필요한 경우 정상적인 제어 흐름을 방해합니다. 신호는 모두 숫자와 두 개의 이름을 가지며, 하나는 암호 약어이고 다른 하나는 약간 덜 암호 문구입니다. 프로세스가 메모리 보호 규칙을 위반할 때 생성되는 신호는 (일반적으로) 번호 11이며 이름은 SIGSEGV
“세그먼트 폴트”입니다. 5,6
신호와 인터럽트의 중요한 차이점은 모든 신호에 기본 동작 이 있다는 것입니다 . 운영 체제가 모든 인터럽트에 대한 핸들러를 정의하지 못하면 OS의 버그이며 CPU가 누락 된 핸들러를 호출하려고하면 전체 컴퓨터가 충돌합니다. 그러나 프로세스는 모든 신호에 대한 신호 처리기를 정의 할 의무가 없습니다. 커널이 프로세스에 대한 신호를 생성하고 해당 신호가 기본 동작으로 남은 경우 커널은 기본값을 그대로 유지하면서 프로세스를 방해하지 않습니다. 대부분의 신호의 기본 동작은 “아무것도하지 않음”또는 “이 프로세스를 종료하고 코어 덤프를 생성 할 수도 있습니다.” SIGSEGV
후자 중 하나입니다.
요약하자면 메모리 보호 규칙을 위반 한 프로세스가 있습니다. CPU가 프로세스를 일시 중단하고 동기 인터럽트를 생성했습니다. 커널은 해당 인터럽트를 SIGSEGV
처리하여 프로세스에 대한 신호를 생성했습니다 . 의는 않은 과정을 가정 해 봅시다 하지 위한 신호 처리기를 설정을 SIGSEGV
, 그래서 커널은 프로세스를 종료하는 기본 동작을 수행한다. 이것은 _exit
시스템 호출 과 동일한 효과를 갖습니다 . 열린 파일이 닫히거나 메모리가 할당 해제됩니다.
이 시점까지는 사람이 볼 수있는 메시지가 인쇄되지 않았으며 쉘 (또는 더 일반적으로 막 종료 된 프로세스 의 상위 프로세스 )은 전혀 관여하지 않았습니다. 부모가 아닌SIGSEGV
규칙을 어기는 프로세스로 이동합니다 . 그러나 순서 의 다음 단계는 상위 프로세스에 하위 프로세스가 종료되었음을 알리는 것입니다. 이것은 부모가 이미 중 하나를 사용하여,이 통지 될 때까지 대기하는 경우에 간단한이있는 여러 가지 방법에 일어날 수있는 시스템 호출 ( , , , 등). 이 경우 커널은 시스템 호출을 반환하고 상위 프로세스에 종료 상태 라는 코드 번호를 제공 합니다.wait
wait
waitpid
wait4
. 7 종료 상태는 부모에게 자식 프로세스가 종료 된 이유를 알려줍니다 . 이 경우 SIGSEGV
신호 의 기본 동작으로 인해 자식이 종료되었음을 알게됩니다 .
그런 다음 부모 프로세스는 메시지를 인쇄하여 이벤트를 사람에게보고 할 수 있습니다. 쉘 프로그램은 거의 항상 이것을합니다. 당신은 crsh
이 작업을 수행하는 코드를 포함하지 않지만 C 라이브러리 루틴이 있기 때문에, 어쨌든 발생하는 system
모든 기능을 갖춘 쉘 실행 /bin/sh
“후드”를. 이 시나리오 crsh
에서 조부모 입니다. 부모 프로세스 알림은으로 필드 화되어 /bin/sh
일반적인 메시지를 인쇄합니다. 그런 다음 /bin/sh
더 이상 할 일이 없으므로 자체 종료됩니다. C 라이브러리의 구현은 해당 종료 알림 을 system
수신 합니다 . 의 반환 값을 검사하여 코드에서 종료 알림을 볼 수 있습니다.system
; 그러나 손자 프로세스가 segfault에서 죽었다는 것을 알 수는 없습니다. 중간 쉘 프로세스에서 사용 되었기 때문입니다.
각주
-
일부 운영 체제는 커널의 일부로 장치 드라이버 를 구현하지 않습니다 . 그러나 모든 인터럽트 처리기는 여전히 커널의 일부 여야하며, 하드웨어는 커널 이외 의 다른 작업을 허용하지 않기 때문에 메모리 보호를 구성하는 코드도 마찬가지 입니다.
-
커널보다 더 많은 권한을 가진 “하이퍼 바이저”또는 “가상 머신 관리자”라는 프로그램이있을 수 있지만이 답변의 목적 상 하드웨어의 일부로 간주 될 수 있습니다 .
-
커널은 프로그램 이지만 프로세스 는 아닙니다 . 도서관과 비슷합니다. 모든 프로세스는 자체 코드 외에도 때때로 커널 코드의 일부를 실행합니다. 커널 코드 만 실행 하는 많은 “커널 스레드”가 있을 수 있지만 여기서는 신경 쓰지 않습니다.
-
유닉스의 구현으로 간주 될 수없는 더 이상 처리해야 할 유일한 OS 는 물론 Windows입니다. 이 상황에서는 신호를 사용하지 않습니다. (실제로 는 신호 가 없으며, Windows에서는
<signal.h>
인터페이스가 C 라이브러리에 의해 완전히 위조됩니다.) 대신 ” 구조적 예외 처리 ” 라는 것을 사용 합니다. -
일부 메모리 보호 위반은
SIGBUS
대신 ( “버스 오류”)를 생성SIGSEGV
합니다. 둘 사이의 선이 지정되어 있지 않으며 시스템마다 다릅니다. 에 대한 핸들러를 정의하는 프로그램을 작성한 경우에SIGSEGV
대해 동일한 핸들러를 정의하는 것이 좋습니다SIGBUS
. -
“세그먼트 결함”은 원래 Unix 를 실행 한 컴퓨터 중 하나 ( 아마 PDP-11)에 의해 메모리 보호 위반에 대해 생성 된 인터럽트의 이름입니다 . ” 세그먼트 화 “는 메모리 보호의 한 유형 이지만, 현재 “세그먼트 화 오류 ” 라는 용어 는 일반적으로 모든 종류의 메모리 보호 위반을 의미합니다.
-
부모 프로세스에 자식이 종료되었음을 알리는 다른 모든 방법은 결국 부모 호출
wait
및 종료 상태를 수신하게됩니다. 다른 일이 먼저 일어난다는 것입니다.
답변
쉘은 실제로 그 메시지와 관련이 있으며 crsh
간접적으로 쉘을 호출합니다 bash
.
나는 항상 결함을 세분화하는 작은 C 프로그램을 작성했습니다.
#include <stdio.h>
int
main(int ac, char **av)
{
int *i = NULL;
*i = 12;
return 0;
}
기본 쉘에서 실행 zsh
하면 다음과 같이 나타납니다.
4 % ./segv
zsh: 13512 segmentation fault ./segv
에서 실행할 때 bash
귀하의 질문에 언급 한 내용을 얻습니다.
bediger@flq123:csrc % ./segv
Segmentation fault
내 코드에서 신호 처리기를 작성 거라고, 그때는 것을 깨달았다 system()
에서 사용하는 라이브러리 호출 crsh
간부는, 쉘의 /bin/sh
에 따라 man 3 system
. 그것은 /bin/sh
확실히 “세그먼트 결함”을 출력하고 crsh
있습니다.
프로그램을 실행 crsh
하기 위해 execve()
시스템 호출 을 사용하도록 다시 쓰면 “세그먼트 결함”문자열이 표시되지 않습니다. 에 의해 호출 된 쉘에서 온다 system()
.
답변
“CPU의 MMU가 신호를 보낸다”와 “커널이 문제를 일으키는 프로그램으로 보내서 종료시킨다”는 것 외에는 이것에 관한 정보를 찾을 수없는 것 같습니다.
이것은 약간의 요약입니다. 유닉스 신호 메커니즘은 프로세스를 시작하는 CPU 특정 이벤트와 완전히 다릅니다.
일반적으로 잘못된 주소에 액세스하거나 읽기 전용 영역에 쓰거나 실행 불가능한 섹션을 실행하려고 시도하는 경우 CPU는 일부 CPU 관련 이벤트를 생성합니다 (기존의 비 VM 아키텍처에서는 각 “세그먼트”(전통적으로 읽기 전용 실행 파일 “텍스트”, 쓰기 가능 및 가변 길이 “데이터”및 전통적으로 메모리의 반대쪽 끝에있는 스택)에 고정 된 범위의 주소가 있기 때문에 세그먼테이션 위반이라고합니다. 최신 아키텍처에서는 페이지 매핑 (매핑되지 않은 메모리의 경우) 또는 액세스 위반 (읽기, 쓰기 및 실행 권한 문제의 경우) 일 가능성이 높으며 나머지 답변을 위해 이에 중점을 둘 것입니다).
이제이 시점에서 커널은 몇 가지 작업을 수행 할 수 있습니다. 유효하지만로드되지 않은 메모리 (예 : 스왑 아웃 또는 mmapped 파일 등)에 대해 페이지 결함이 생성되며,이 경우 커널은 메모리를 맵핑 한 후 사용자 프로그램을 오류. 그렇지 않으면 신호를 보냅니다. 신호 처리기를 설치하는 프로세스가 다르고 프로그램이 인터럽트 처리기 설치를 시뮬레이트 할 것으로 예상되는 경우와 비교하여 신호 처리기를 설치하는 프로세스가 다르고 대부분 아키텍처에 독립적이기 때문에 이것은 정확하게 “원래 이벤트를 문제의 프로그램으로 전달”하지 않습니다.
사용자 프로그램에 신호 처리기가 설치되어 있으면 스택 프레임을 만들고 사용자 프로그램의 실행 위치를 신호 처리기로 설정하는 것입니다. 모든 신호에 대해 동일한 작업이 수행되지만, 세그먼테이션 위반의 경우에는 일반적으로 신호 처리기가 반환하면 오류를 일으킨 명령이 다시 시작되도록 정렬됩니다. 사용자 프로그램이 오류를 수정했습니다. 예를 들어 메모리를 문제가있는 주소에 매핑하여 가능합니다. 아키텍처에 따라 다릅니다. 신호 처리기는 프로그램의 다른 위치로 이동하여 (일반적으로 longjmp 를 통해 또는 예외를 발생시켜) 잘못된 메모리 액세스를 유발 한 작업을 중단 할 수 있습니다.
사용자 프로그램에 신호 처리기가 설치되어 있지 않으면 종료됩니다. 일부 아키텍처에서는 신호가 무시되면 명령이 반복해서 다시 시작되어 무한 루프가 발생할 수 있습니다.
답변
세그먼테이션 결함은 허용되지 않는 메모리 주소에 대한 액세스입니다 (프로세스의 일부가 아니거나 읽기 전용 데이터를 쓰거나 실행 불가능한 데이터를 실행하려는 경우). MMU (현재 CPU의 일부인 메모리 관리 장치)에 의해 인터럽트가 발생합니다. 인터럽트는 커널에 의해 처리되며, 커널 은 문제를 일으키는 프로세스에 SIGSEGFAULT
신호 ( signal(2)
예 : 참조) 를 보냅니다 . 이 신호의 기본 핸들러는 코어를 덤프하고 (참조 core(5)
) 프로세스를 종료합니다.
껍질에는 절대 손이 없습니다.