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)      CPLDPL보다 작거나 같은지를 확인함(커널 모드: 0, 유저 모드: 3, 부를 수 있는지 없는지를 확인하는 것)
-  이 조건을 충족하지 못하면 부를 수 없는 interrupt를 부른 것이라 exception이 발생한다.
3)      만약 target segment selectorPLCPL보다 작으면 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이란 것이고, espss는 푸시되지 않은 상태일 것임.
5)      Clear some bits of eflags register (eflags를 초기화해준다)
6)      cs, eip registersdescriptor에 있는 값으로 설정해준다.
1)~6)을 거쳐서 하드웨어적으로 Kernal Stack에 푸시한 것임.
반복적으로 나오는 descriptortrap vector를 의미하는 듯.(trap vectorIDT에 등록되어 있음.)
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 handlerinterrupt의 일을 하기 보다는 작업을 위한 세팅을 하는 것이다.
실제 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에서 가져올 수 있는 것이다.





댓글

이 블로그의 인기 게시물

C++ int, long int, long long int 어떤 걸 써야할까

C++ fstream 간단한 사용법(파일입출력)