사용자 모드에서 커널 모드로 전환해야 하는 경우 3가지
1. System call
2. Exception
-user가 잘못된 행위를 하고 있는 경우(잘못된 메모리 주소 액세스, 0으로 나눌 경우)
3.interrupt
-OS에 이벤트를 알려주세요.
-> 세 가지 모두 인터럽트 핸들링으로 처리
인터럽트란 무엇입니까?
: 하드웨어 또는 소프트웨어가 즉시 주목되어야 할 때 process로 보내는 신호
ISR – 인터럽트 서비스 루틴
Interrupt hadling을 위한 4개의 OS 디자인
1. 현재의 프로세서를 레지스터에 보존 -> 현재 하고 있는 것이 끝난 후에 기존의 instruction 를 실행할 수 있기 때문에
2. 커널 모드에서 실행 준비
3. 어떤 이벤트 정보가 있는지
4. 사용자 프로그램이 커널을 방해하지 않도록 유지
-> 모든 것이 충족되어야 OS가 인터럽트 처리 가능
“int n” instruction
:n번째를 생성하는 instruction
ex) int %T_SYSCALL
eip register -> 다음 실행 명령의 주소가있는 레지스터
esp register -> 하나의 스택 프레임의 끝 주소를 저장하고 다음 스택 포인터 주소를 알고 있습니다.
Interrupt Descriptor
각 인터럽트에는 엔트리 포인터가 있습니다 (엔트리 포인터 : 인터럽트가 실제로 실행되는 명령의 시작 주소).
때때로 발생하는 인터럽트가 실제로 실행되는 명령의 시작 주소가있는 Descriptor table
-tvinit() : trap vector , 트랩 벡터라고 하는 Entry pointer 를 IDT 에 넣는 과정을 실행 (main.c)
-trap vector는 vectors.S 파일에 저장됩니다 (make를 먼저 생성해야 함)
-int n instruction이 종료하면, 전의 처리를 거쳐 eip 레지스터가 n에 대응하는 instruction 주소로 설정되어 명령 실행
*Trap과 Interrupt의 차이
trap과 interrupt는 모두 컴퓨터 시스템에서 발생하는 이벤트이지만, 발생하는 타이밍이나 원인, 처리 방법 등에 차이가 있습니다.
Interrupt : 하드웨어 디바이스나 타이머 등이 CPU에 신호를 보내 처리를 요구하는 것입니다.
Trap: 프로그램에서 예기치 않은 상황이 발생하고 CPU가 이 상황을 감지합니다.
(division by zero, 잘못된 메모리 참조)
-> 인터럽트는 외부 장치 및 타이머와 같은 하드웨어 이벤트처리하고 함정은 소프트웨어에서 예기치 않은 상황을 처리하는 것입니다.
Interrupt Descriptor Table (IDT)
-Interrupt handlers는 IDT에 정의되어 있습니다.
-IDT에는 256개의 항목이 있으며 각 항목은 cs,eip 레지스터 값을 인터럽트 핸들링을 적절하게 만드는 데 사용됩니다.
연습하다
64회 이후 빈 공간을 활용하여 새로운 interrupt를 만들어 봅니다.
Privilege levels in x86
현재 실행중인 프로세스가 얼마나 큰 액세스 권한을 가지고 있는지 보여줍니다.
0:커널 모드
1,2: 사용 x
3: user mode (kernel 보다 작은 권한)
CPL (Current Privilege Level) -> 현재 특권 레벨이 어떻게 되는지
DPL(Descriptor Privilege Level) -> 인터럽트를 실행하기 위한 최소 권한 레벨
=> 항상 CPL<= DPL이 아니면 Service 실행 가능!
“int n” instruction
– 모두 process 자체에 내장된 기능
1. IDT에서 n번째 Descriptor로 가져오기
– 해당 CPU의 CPL과 Gate Descriptor의 DPL을 비교 CPL<=DPL인 경우에만 실행
2. 현재 cs 레지스터CPL이 DPL 이하인지 확인 (권한 확인)
3. target segment selector’s PL (실제로 실행할 때 필요한 권한) < CPL의 경우 권한 조정 필요
(사용자 모드에서 실행 가능한 인터럽트가 있는 경우 이 프로세스가 필요합니다)
<権限調整が必要な場合>
a. 현재 esp 및 ss 레지스터를 임시 저장
b. task segment descriptor에서 두 레지스터의 값을 가져옵니다.
c. 기존 레지스터 값을 스택에 저장
* 커널 모드에서 interrupt가 발생한 경우? -> 커널 모드에서 인터럽트가 발생하면 권한 조정 필요
4. eflag, cs, eip 레지스터를 스택에 저장
5. 여러 플래그에 대한 정보가있는 eflag 레지스터의 일부를 지 웁니다.
6. cs와 eip 레지스터를 disscriptor의 값으로 변경한 후 트랩 벡터 주소로 이동
instruction이 끝나면 alltraps에 JUMP
interrupt의 물리적 주소를 찾기 위해
segement selector에 쓰여진 주소에 따라 offset를 하나 받는다.
이것을 segment descriptor의 base address와 더하여 실제 주소를 알
gate discriptor의 주소는 절대 주소가 아니라 상대 주소이므로, base address와 더불어 찾는 과정을 거치는 것
After “int n”
Jump to Interrupt Service Routine(ISR) 이후에 어떻게 실행됩니까?
1. xv6에서 모든 ISR이 “alltraps”를 호출합니다.
2. 모든 traps 는 CPU register 를 저장한 후 trap 호출 (cpu register 저장은 trap 에 필요한 스택 프레임을 저장한다는 의미)
3. 실제 Interrupt handler가 실행되어야 하는 interrupt 실행
(코드 흐름)
xv6 코드 확인
-xv6 부팅되면 main을 호출하고 main은 trap vector를 init
-trap vector init (256 개의 interrupt 모두에 대해 매일 게이트를 설정하고 초기화)
default는 kernel 모드의 0으로 설정
예외적으로 systemcall 만 user 모드에서 실행되도록 3으로 설정
-> 모든 인터럽트는 Privilege Level을 커널로 설정하고 마지막 systemcall 만 사용자로 변경합니다.
-trap vecotr 가 정의되어 있는 vectors.S (make 를 할 필요가 vectors.pl 파일에 의해 vectors.S 파일을 생성)
int n을 실행 한 후 먼저 푸시 오류 코드에 성공했으므로 0을 설정하고 두 번째는 커널 스택에 자신의 인터럽트 번호를 푸시합니다.
그런 다음 alltrap으로 이동하면 (trapasm.S) 세그먼트 레지스터와 범용 레지스터 저장 -> 프로세스가 커널 C 코드를 실행할 수있는 call trap 함수를 통해 c 파일로 돌아갑니다.
systemcall은 별도로 처리하고 나머지는 switch 문을 통해 해당 인터럽트를 실행합니다.
존재하지 않는 인터럽트가 요구되었을 경우는 kernel, user 를 나누어 예외 처리
struct trapframe이란?
trap이 호출될 때 프로세서 레지스터에 정보가 포함되어 있음
Why?
1. 인터럽트 후 사용자 프로세스로 돌아갈 때 레지스터 복구
2. User Process 정보를 커널로 사용하기 위해(argument 이용)
3. 인터럽트 후에 User Processor가 다른 방식으로 작동하면 반환 주소를 다른 주소로 바꾸어 기존 흐름 대신 새 흐름을 만드는 데 사용해야하기 때문에 (exit 함수)
trapframe은 x86.frame에 정의되어 있습니다.
esp, ss는 pl이 변경될 때만 존재합니다.
arg int 및 arg string
ex) zombie.c
sleep 라는 systemcall 호출 > 실제 sys_sleep 함수는 인수를 별도로 사용하고 인수를 받기 위해 argint라는 함수를 사용합니다.
fetchint를 호출해 저장한 trap frame에 esp값
Modify
-process flow 변경
실습 과제
128번째 interrupt를 만들고 호출되었다는 메시지를 출력
1. 사용자 프로그램에서 128번째 인터럽트 호출
힌트
1. trap.c 코드 수정
2. user program에서 interrupt를 호출하기 때문에 systemcall 부분 확인
3. default 부분 출력되면 잘못 작성한 것으로 재확인