인터럽트 후 프로세서는 커널 코드를 어떻게 찾습니까?


13

인터럽트가 발생하면 프로세서는 현재 프로세스를 선점하고 커널 코드를 호출하여 인터럽트를 처리합니다. 프로세서는 커널에 들어갈 위치를 어떻게 알 수 있습니까?

각 인터럽트 라인에 설치할 수있는 인터럽트 핸들러가 있다는 것을 알고 있습니다. 그러나 프로세서는 'hardwired logic'만 실행하기 때문에 인터럽트 처리기 자체 또는 처리기 전에 실행되는 코드를 가리키는 미리 정의 된 위치가 존재해야합니다 (한 인터럽트 행에 대해 여러 개의 처리기가있을 수 있으므로 후자).

답변:


13

커널은 시작할 때 각 라인의 인터럽트 핸들러를 가리키는 인터럽트 벡터 테이블 ( x86 에서는 인터럽트 디스크립터 테이블 또는 IDT 라고 함)을 초기화합니다 .

80286 이전에는 IDT가 항상 고정 주소에 저장되었습니다. 80286부터 IDT는 LIDT명령어를 사용하여로드됩니다 .

인터럽트 벡터 테이블은 인터럽트 라인 당 단일 핸들러를 가리 킵니다. 즉, 커널은 예를 들어 여러 다른 인터럽트 루틴을 실행하는 인터럽트 핸들러를 제공하거나 일부 또는 모든 인터럽트를 다루는 단일 핸들러를 제공하도록 선택할 수 있습니다. Linux는 호출 된 인터럽트 라인을 결정하고 호출 할 적절한 다운 스트림 핸들러를 찾는 일반 인터럽트 핸들러를 제공하여 이러한 작업을 수행합니다.


1
프로세서는 인터럽트 라인을 IDT의 인덱스로 사용하여 PC에 항목을 넣고 실행을 시작합니까? 그러나 모든 인터럽트 핸들러 전에 실행되는 일반 함수가 있습니까? 리눅스의 경우 do_IRQ ()입니다. 이것은 인터럽트 라인에 관계없이 모든 IDT 엔트리가 가리키는 기능입니까?
Philipp Murry

@PhilippMurry 예. 그런 다음 커널은 이전에 실행 된 코드로 돌아 가기 전에 인터럽트를 실제로 처리하기 위해 자체 인터럽트 처리기 (한 줄에 둘 이상이있을 수 있음) 세트를 사용합니다.
Adam Maras

실제로 두 가지 유형의 인터럽트 핸들러가 있습니다 : 프로세서가 호출하는 것 (항상 do_IRQ ())과 커널이 호출하는 것입니다 (하나는 request_irq ()를 통해 등록한 것). 이것을 당신의 대답에 추가 할 수 있습니까? 나는 그때 그것을 받아 들일 것이라고 생각한다 :) 고마워
Philipp Murry

1
@PhilippMurry 저는 Linux가 인터럽트를 처리하는 방법 (및 커널 개발자가 해당 시스템을 활용하는 방법)에 대해서는 전문가가 아니지만 커널이 자체 ISR 관리를 수행 할 수있는 방법에 대한보다 광범위한 정보를 추가했습니다.
Adam Maras

12

예, 점프 할 코드의 주소를 포함하는 사전 정의 된 위치가 있습니다 : 인터럽트 벡터 . 프로세서에 따라 실제 메모리의 특정 위치 (8088), 가상 메모리의 특정 위치, 프로세서 레지스터, 레지스터로 표시된 메모리의 위치 (ARM, 386) 등이 될 수 있습니다.

세부 사항은 프로세서마다 다르지만 프로세서에서 인터럽트를 처리하는 주요 공통 요소는 다음과 같습니다.

  • 마스크 인터럽트 (모든 후속 인터럽트가 대기해야 함)
  • 프로세서 모드를 커널 또는 인터럽트 모드로 설정하십시오 (프로세서에 이러한 모드가있는 경우).
  • 프로그램 카운터의 값을 알려진 위치 (등록 또는 메모리)에 저장하십시오.
  • 다른 레지스터의 값을 저장하거나 레지스터 뱅크간에 전환 할 수 있습니다.
  • 다음 명령어를 실행하십시오 (새 PC 값에서).

1

다른 두 가지 답변 (작성 당시)은 인터럽트와 IDT에 대해 이야기합니다. 그러나 최신 Intel-esque CPU에서는 커널을 호출하는 세 가지 방법이 있습니다.

방법 # 1 : 인터럽트.

이것은 위에서 설명했다. 인터럽트 디스크립터 테이블 / 인터럽트 벡터에 항목을 설정 한 다음 소프트웨어 인터럽트를 실행하여 커널에 들어갑니다.

이 방법의 주요 장점은 일반적인 커널이 인터럽트를 처리 할 수 ​​있어야하며, 오래된 하드웨어에서 작동한다는 것입니다.

방법 # 2 : 콜 게이트.

콜 게이트는 특별한 종류의 세그먼트 선택기입니다. 호출 대상은 글로벌 또는 로컬 세그먼트 디스크립터 테이블 (각각 GDT 및 LDT)에로드되어야합니다. 그런 다음 콜 게이트를 세그먼트로 사용하여 원거리 통화 명령을 수행하면 (통화 오프셋은 무시 됨)보다 권한있는 코드를 호출 할 수 있습니다. 콜 게이트는 매우 유연합니다. IA-32 아키텍처에는 네 가지 권한 수준이 있으며 호출 게이트를 사용하면 모든 수준을 호출 할 수 있습니다.

리눅스가 콜 게이트를 사용했다고 믿지는 않지만 Windows 95는 콜 게이트를 사용했습니다. Win95 커널 서비스 ( krnl386.exekernel.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 명령은 명령 포인터를 복원합니다.

SYSENTER 및 SYSEXIT 명령에 대한 추가 정보

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.