답변:
커널은 시작할 때 각 라인의 인터럽트 핸들러를 가리키는 인터럽트 벡터 테이블 ( x86 에서는 인터럽트 디스크립터 테이블 또는 IDT 라고 함)을 초기화합니다 .
80286 이전에는 IDT가 항상 고정 주소에 저장되었습니다. 80286부터 IDT는 LIDT
명령어를 사용하여로드됩니다 .
인터럽트 벡터 테이블은 인터럽트 라인 당 단일 핸들러를 가리 킵니다. 즉, 커널은 예를 들어 여러 다른 인터럽트 루틴을 실행하는 인터럽트 핸들러를 제공하거나 일부 또는 모든 인터럽트를 다루는 단일 핸들러를 제공하도록 선택할 수 있습니다. Linux는 호출 된 인터럽트 라인을 결정하고 호출 할 적절한 다운 스트림 핸들러를 찾는 일반 인터럽트 핸들러를 제공하여 이러한 작업을 수행합니다.
예, 점프 할 코드의 주소를 포함하는 사전 정의 된 위치가 있습니다 : 인터럽트 벡터 . 프로세서에 따라 실제 메모리의 특정 위치 (8088), 가상 메모리의 특정 위치, 프로세서 레지스터, 레지스터로 표시된 메모리의 위치 (ARM, 386) 등이 될 수 있습니다.
세부 사항은 프로세서마다 다르지만 프로세서에서 인터럽트를 처리하는 주요 공통 요소는 다음과 같습니다.
다른 두 가지 답변 (작성 당시)은 인터럽트와 IDT에 대해 이야기합니다. 그러나 최신 Intel-esque CPU에서는 커널을 호출하는 세 가지 방법이 있습니다.
방법 # 1 : 인터럽트.
이것은 위에서 설명했다. 인터럽트 디스크립터 테이블 / 인터럽트 벡터에 항목을 설정 한 다음 소프트웨어 인터럽트를 실행하여 커널에 들어갑니다.
이 방법의 주요 장점은 일반적인 커널이 인터럽트를 처리 할 수 있어야하며, 오래된 하드웨어에서 작동한다는 것입니다.
방법 # 2 : 콜 게이트.
콜 게이트는 특별한 종류의 세그먼트 선택기입니다. 호출 대상은 글로벌 또는 로컬 세그먼트 디스크립터 테이블 (각각 GDT 및 LDT)에로드되어야합니다. 그런 다음 콜 게이트를 세그먼트로 사용하여 원거리 통화 명령을 수행하면 (통화 오프셋은 무시 됨)보다 권한있는 코드를 호출 할 수 있습니다. 콜 게이트는 매우 유연합니다. IA-32 아키텍처에는 네 가지 권한 수준이 있으며 호출 게이트를 사용하면 모든 수준을 호출 할 수 있습니다.
리눅스가 콜 게이트를 사용했다고 믿지는 않지만 Windows 95는 콜 게이트를 사용했습니다. Win95 커널 서비스 ( krnl386.exe
및 kernel.dll
)는 실제로 사용자 모드 (링 3)에서 실행되었습니다. 최고 권한 수준 (링 0)은 프로세스 전환 만 수행 한 드라이버와 마이크로 커널에만 사용되었습니다. 콜 게이트를 사용하여 드라이버 호출이 완료되었습니다. 이를 통해 레거시 16 비트 코드 (많은 부분이있었습니다!)는 항상 그렇듯이 표준 원거리 호출을 사용하는 Win95 드라이버를 사용할 수있었습니다.
글로벌 디스크립터 테이블의 부적절한 보호는 여러 Windows 95 익스플로잇의 원인으로 메모리를 덮어 써서 자체 콜 게이트를 설치했습니다.
방법 # 3 : SYSCALL / SYSRET 및 SYSENTER / SYSEXIT
이들은 AMD와 인텔이 독자적으로 발명 한 두 가지 명령 세트이지만 본질적으로 동일한 기능을 수행합니다. SYSCALL / SYSRET가 처음으로 AMD 전용이었고 SYSENTER / SYSEXIT는 Intel 이었지만 지금은 AMD가 구현합니다. SYSENTER / SYSEXIT에 대해 설명하겠습니다.
콜 게이트와 달리 SYSENTER는 링 0으로 전송하는 데만 사용할 수 있으며 한 위치로만 전송할 수 있습니다. 그러나 호출이나 인터럽트와 달리 스택에 닿지 않기 때문에 대기 시간이 매우 짧다는 이점이 있습니다.
전송 위치는 세 가지 모델 별 레지스터를 사용하여 설정됩니다. 하나는 세그먼트 정보 용이고 다른 하나는 커널 코드의 명령어 포인터 및 스택 포인터입니다. 스택에 "푸시 된"것이 없기 때문에 사용자 모드 코드는 레지스터에 리턴 명령 포인터와 스택 포인터를 전달하여 커널에게 리턴 할 위치를 알려주는 역할을합니다. 커널은 스택 포인터를 복원하고 SYSEXIT 명령은 명령 포인터를 복원합니다.