x86 OS interrupts routine
x86 운영체제에서는 다음과 같은 과정을 거쳐 interrupt를 호출한다
1.
interrupt
n instruction 수행.
2.
IDT에 등록되어있는 ISR(interrupt Service
Routine) 수행.
xv6는 위의 두 과정을 아래와 같이 진행한다.
1. interrupt n instruction - “int n” instruction workflow.
1) IDT(interrupt
descriptor table)에서 n’th
descriptor를 읽어온다.
- 시스템 부팅시 tvinit() 함수로 vertors.S의 번호, DPL을 다 초기화 해줌.
- system call은 DPL이 유저 레벨로 세팅됨
2) CPL이 DPL보다 작거나 같은지를 확인함(커널 모드: 0, 유저 모드: 3,
부를 수 있는지 없는지를 확인하는 것)
- 이 조건을 충족하지 못하면 부를 수 없는 interrupt를
부른 것이라 exception이 발생한다.
3) 만약 target segment selector의 PL이 CPL보다 작으면 PL의 변환이 필요하다(ex PL이 커널모드인 경우). 다음 3단계를 거쳐서 변환이 일어남.
(1) 현재 유저모드니깐, 유저모드의 esp, ss register를 임시로 저장함(임시로 저장하는 이유는
커널 스택에다 푸시하기 위해서)
(2) task segment descriptor에서 esp, ss를 불러와서 CPU-internal registers 에 저장함.
(3) 이제 아까 임시로 저장한 esp, ss를 푸시해줌(유저 모드로 돌아가기 위한 esp, ss)
4) eflags,
cs, eip register를 stack
에 푸시한다.
- 당연히 여기는 Kernal Stack임.
- 만약 3번에서 Control
Transference가 일어나지 않았다면, 현재 kernel
stack이란 것이고, esp와 ss는 푸시되지
않은 상태일 것임.
5) Clear
some bits of eflags register (eflags를 초기화해준다)
6) cs,
eip registers를 descriptor에
있는 값으로 설정해준다.
1)~6)을 거쳐서 하드웨어적으로 Kernal Stack에 푸시한 것임.
반복적으로
나오는 descriptor는 trap vector를 의미하는
듯.(trap vector는 IDT에 등록되어 있음.)
vector에 따라 error
code와 trapno을 푸시해준다.
2. ISR - 이후에는 전부
alltraps로 점프한다.
모든
interrupt는 일단 alltraps를 거치는데, 여기서는 ds, es, fs, gs register를 푸시하고 나머지
범용 레지스터(eax, ecx, edx, ebx, oesp, ebp, esi, edi)를 푸시해준다.
그리고
마지막으로 esp를 푸시해주면 Trapframe이 완성된다.
이 esp 값 덕분에 우리는 kernel stack을 하나의 구조체처럼
관리할 수 있게 된다.
Trapframe이 완성된 다음에는 이제 실제로 interrupt가 하는 일들을 trap 함수에서 하게 된다.
그렇다. 사실 interrupt handler는 interrupt의 일을 하기 보다는 작업을 위한 세팅을 하는 것이다.
실제
interrupt가 하는 일이라고 생각하는 것들은 trap이
하게 된다.
예를
들어서 system call의 한 종류인 argint를 보자.
int
argint(int n, int *ip)
{
{
return fetchint((myproc()->tf->esp + 4 +
4 * n, ip));
// trapframe->esp의 값은 esp를 푸시한 순간의 값이기 때문에,
+ 4를 한 곳부터 n 번째 칸에 원하는 값이 있을 것이다.
}
int
fetchint(uint addr, int* ip)
{
struct proc *curproc = myproc();
// curproc->sz 는 아마 현재 프로세서의 limit address인 듯.
if(addr >= curproc->sz || addr + 4 > curproc->sz)
return -1;
*ip = *(int*)(addr);
return 0;
}
이런
방식으로 우리는 system call 했을 때 넘겨받은 값을 register에서
가져올 수 있는 것이다.
댓글
댓글 쓰기