본문 바로가기

정글캠프-WIL/핀토스

Pintos - interrupt 비교 (#PF(page fault), Timer Interrupt)

이전 thread를 학습하며 timer interrupt의 발생 과정

timer_init 부터 해서 intr_register_ext 부터 intr_handler까지 학습을 해본 기억이 있다. 물론 완벽하게 이해하지 못하고 대충 넘어갔는데,

이번 vm을 학습하며 #PF라는 인터럽트가 중요해졌는데, 이전에 학습한 timer_interrupt와 비교해서 차이가 무엇인지 정리를 해보고자 한다. 중점은 #PF를 중심으로 둘 예정

 

External hardware interrupt
ex. timer, keyboard, disk - 외부 장치가 CPU에게 비동기적으로 알림
Internal interrupt (CPU exception)
ex. page fault(#PF), divide error(#DE), general protection(#GP) - 현재 실행 중인 명령어 때문에 동기적으로 발생

#PF란?

1. 어떤 유저 프로그램이 어떤 가상주소에 접근
                  ↓
2. CPU가 현재 CR3의 PML4부터 page table을 따라갔는데, PTE가 없거나 present bit가 없거나 권한 위반임
                  ↓
3. CPU가 "이 명령어는 계속 실행할 수 없다" 판단하고 vector 14로 예외 발생
                  ↓
4. 예외가 발생한 주소를 CR2가 기록

즉, 현재 물리메모리가 존재하지 않거나 권한 위반인 경우를 뜻함

접은글 : 코드 위치 [PF 등록]

접은글을 펼쳐보면, 실제로 main에서부터 exception을 초기화 하는 exception_init이 있다.

이 코드를 따라가 보면 제일 아래에

intr_register_init (14, 0, INTR_OFF, page_fault, "#PF Page-Fault Exception");

이 등록되어 있고 들어가 보면

조건 vector 번호 < 32  && vec_no > 47, 핸들러 등록 함수가 있다.

여기서 궁금증 [추가적으로 dpl, level 같은 거는 다음 함수를 파보면서 설명 예정]

①. 왜 vector 번호가 32보다 작거나 47보다 커야하지?

②. 핸들러 등록함수가 뭐지?

①. 내부 인터럽트 vector 번호가 32보다 작거나 47보다 큰 이유

32~47번 interrupt vector는 CPU exception이 아니라 외부 하드웨어 interrupt용으로 예약해 둔 구간

IDT vector 0 ~ 31 32 ~ 47 48 이상
개념 CPU가 직접 발생시키는 exception PIC가 매핑한 외부 interrupt, IRQ 0~ 15 syscall 같은 software interrupt나 OS가 따로 쓰는 interrupt 기능

따라서 32~47번은 외부 인터럽트니까 제외하고, 나머지는 internal interrupt 처럼 다루자는 의미

②. register_handler

Handler 등록
make_trap_gate : struct gate의 type 멤버변수의 값을 14
make_intr_gate : struct gate의 type 멤버변수의 값을 15
type 14 : trap gate
type 15 : interrupt gate

둘 다 이 interrupt vector가 발생하면 어디 코드로 점프할지를 IDT에 등록

차이는 CPU가 handler로 진입할 때 IF 플래그를 어떻게 다루느냐임

Interrupt Gate :
handler 진입 시 CPU가 자동으로 IF = 0으로 만듦
-> maskable external interrupt가 꺼짐
  [외부 하드웨어 인터럽트를 잠시 받지 않게 만든다는 의미 - CPU가 무시할 수 있는 외부 인터럽트]
  대표적으로
    - 타이머 인터럽트(IRQ0)
    - 키보드 입력
    - 마우스 입력
    - 디스크 완료 신호
    - 네트워크 카드 인터럽트
-> handler 실행 중 timer/keyboard 같은 외부 interrupt가 중첩되지 않음

Trap Gate :
handler 진입 시 IF 값을 그대로 둠
-> 원래 interrupt가 켜져 있었다면 handler 안에서도 interrupt 허용

②-1. struct gate

IDT(Interrupt Descriptor Table) 엔트리 하나를 x86-64 descriptor 형식메 맞춰 쪼갠 구조체
- off_15_0, off_31_16, off_32_63
실제 handler 주소, intr_stubs[vec_no]의 주소를 3조각으로 나눠 저장

- ss
segment selector. 보통 kernel code segment selector.

- ist
Interrupt Stack Table 인덱스. 0이면 특별한 IST stack을 쓰지 않음.

- type
gate 종류
  0xE = interrupt gate
  0xF = trap gate

- s
system descriptor 여부. IDT gate는 system descriptor라서 0이어야 함.

- dpl
Descriptor Privilege Level.
이 interrupt를 `int n` 명령으로 누가 호출할 수 있는지 결정
dpl = 0 이면 커널만 (커널모드), dpl = 3 이면 유저도 가능 (유저모드)
syscall interrupt가 보통 dpl = 3인 이유가 이것

- p
present bit : 1이면 유효한 descriptor

- rsv1, rsv2
reserved : 0이어야 한다(무조건?)

Timer Interrupt와의 비교

접은글 : 코드 위치 [Timer Interrupt 등록]

접은글을 펼쳐보면 실제로 PF interrupt와 다른 부분이 존재한다.

intr_register_ext (0x20, timer_interrupt, "8254 Timer");

들어가보면

타이머 인터럽트는 intr_register_ext()를 통해 등록되고, 내부적으로 register_handler(vec_no, 0, INTR_OFF, handler, name)을 호출한다.

여기서 dpl = 0은 이 인터럽트가 커널 권한에서만 직접 다룰 수 있음을 의미하고, INTR_OFF는 이 핸들러가 실행되는 동안 CPU의 외부 인터럽트 허용 플래그(IF)를 꺼서 다른 maskable external interrupt가 중첩되어 들어오지 못하게 한다.

즉, 타이머 인터럽트 핸들러는 커널 모드에서 실행되며, 실행 중에는 다른 외부 하드웨어 인터럽트가 끼어들 수 없도록 보호된다.
참고
여기서 0x20은 timer IRQ0[컴퓨터 하드웨어가 Interrupt Request 하는 번호중 0]가 재매핑된 vector
CPU exception들이 vector 0x00 ~ 0x1f까지 쓰기 때문에, Pintos는 PIC를 초기화하면서 하드웨어 IRQ들을 0x20부터 0x2f 쪽으로 밀어놓는다. 그래서 timer는 IRQ0이지만 CPU입장에서는 vector 0x20으로 들어온다.

INTR_REGISTER_INT vs INTR_REGISTER_EXT

intr_register_int()는 CPU 내부 예외나 소프트웨어 interrupt 용

특징
vector 범위 : 0x00 ~0x1f의 CPU exception
DPL을 직접 지정 가능 (DPL 0[커널모드] or 3[사용자모드도 가능])
INTR_ON, INTR_OFF 선택 가능
PIC EOI(pic end_of_interrupt) 필요 없음 - 끝냈다는 신호
현재 instruction과 관련된 동기적 이벤트가 많음

dpl 관련 예시

dpl은 유저 모드가 int n 명령으로 직접 이 interrupt gate를 호출할 수 있는지와 관련 있음. 예를 들어 breakpoint #BP는 intr_register_int(3, 3, ...)로 DPL 3이라 유저가 의도적으로 발생시킬 수 있음.

하지만 page fault는 DPL 0이라 유저가 int $14로 직접 page fault handler를 호출하는 건 막고, CPU가 실제 page fault를 감지해서 발생시키는 건 DPL과 무관하게 허용된다.

intr_register_ext()는 외부 하드웨어용 interrupt 용

특징
DPL은 항상 0
interrupts off 상태로 진입
handler가 끝나면 PIC에 EOI 필요
비동기 이벤트
interrupt context로 취급
정리
page fault
  원인: 현재 실행 중인 명령어가 잘못된/미매핑 주소 접근
  vector: 14, 0x0e
  등록: intr_register_int
  동기/비동기: 동기적
  추가 정보: CR2에 fault address, error_code에 present/write/user 정보
  처리 후: lazy loading 성공하면 같은 instruction 재시도

timer interrupt
  원인: 8254 PIT 하드웨어가 주기적으로 IRQ 발생
  vector: 0x20
  등록: intr_register_ext
  동기/비동기: 비동기적
  추가 정보: 특정 fault address 없음
  처리 후: ticks 증가, 필요하면 스케줄링/yield