-
XV6 - 이론 정리_2CS/OS 2022. 5. 13. 18:33
이제부터는 XV6가 어떻게 동작하는 지 실제 부팅부터 알아볼 겁니다.
Step1 - Booting
PC powers on -> load bootloader(bootasm.S, bootmain.c) -> load kernel -> execute kernel entry
컴퓨터는 하드웨어를 초기화 및 부트로더라는 프로그램(bootasm.S)을 실행시킨다.
부트로더(xv6에서 bootasm.S, bootmain.c의 코드로 구성된다.)는 항상 디스크의 첫번째 섹터에 위치하며 커널 이미지를 메모리로 로드한다. 부트로더는 항상 xv6 kernel을 물리메모리 0x100000 번지에 위치시킨다. 더 낮은 번지의 물리메모리는 I/O device에 관해 쓰인다. 부트로더가 xv6의 entry(entry.S)에 진입함으로써 xv6 커널의 실행이 시작된다.
Step2 - Creating First user-level Proess ( Main.c )
부팅이 완료되면, OS는 하나의 Process를 실행시켜주어야 한다. 이를 위하여 처음에 Main.c의 함수들을 하나씩 호출하게 된다.
kinit1() 함수부터 kinit2()함수까지는 H/W 자원과 xv6 kernel에 대한 초기화 작업이다.
우리가 관심있는 것은 첫번째 user-process의 생성이기 때문에 생략한다.
흐름은 아래와 같다.
userinit() Function
-> allocproc() Function (proc structure를 init 및 proc 구조체를 allocate 및 page table 생성 프로세스를 실행 가능한 형태로 )
-> mpmain() function
-> scheduler() function(프로세스의 scheduling)
-> scheduler
-> execute process
-> 첫번째 프로세스가 생성된다.
Step3. Userinit Function
First Process를 위해 Proc Structure와 Kernel Stack 할당 및 초기화 및 실행할 Program을 Load 함으로써 실행 될 수 있는 준비를 한다.
xv6 kernel은 모든 프로세스를 proc 구조체를 통해 관리한다. proc structure는 ptable이라는 배열로 관리되며 xv6에서 최대로 생성될 수 있는 프로세스는 64개이다.userinit() 함수는 제일 먼저 allocproc()함수를 호출하여 할당할 수 있는 proc 구조체가 있는지 확인하고 있으면 할당받는다. proc 구조체를 할당받았으면 pid와 kernel stack을 할당받는다. 이후 할당받은 kernel stack을 아래 그림과 같이 구성한다.
위처럼 kernel stack을 구성하는 이유는 새로 생성된 프로세스가 스케쥴링될 때 실행될 함수와 레지스터를 미리 세팅해놓기 위해서이다. trapframe과 context에 대해 간단히 설명하면 trapframe은 kernel mode에서 user mode로 돌아가기 위해 user mode의 레지스터를 저장해놓은 것이다. 유저 영역에서 커널영역으로 진입할 때 다시 유저 영역으로 돌아 갈 수 있게 레지스터들을 kernel stack에 저장해 놓아야 하는데 그것이 바로 trapframe이다.
결국 Kernel Stack에 있는 Trapframe , Context를 표현한 것이 Proc.h에 proc structure에 저장되어 있다.
context는 프로세스 간의 스케쥴링에 필요한 레지스터들을 저장해놓은 것이다. 프로세스 간에 scheduling은 커널 영역에서 이루어진다. CPU 소유권을 포기할 프로세스가 다시 rescheduling 되기 위에 현재 커널 영역에서 사용하는 레지스터를 저장해 놓은 것이 context이다.
-> 프로세스 간 scheduling 되는 현상을 context switching
실제 코드에서는 swtch Function을 통하여 Scheduler <-> process context로 표현된다.
context의 eip레지스터(extension instruction pointer; 실행할 함수 주소)를 forkret() 함수 주소로 저장함으로써 생성된 프로세스가 스케쥴링되면 forkret() 함수가 실행된다. 또 eip 레지스터 다음에 trapret 함수의 주소를 저장함으로써 forkret() 함수가 return된 후 바로 trapret() 함수가 실행될 수 있도록 구성한다.
forkret()과 trapret() 함수에 대해서 간단히 설명하면 forkret()은 fork return의 약자로 fork(새로운 프로세스를 생성하는 함수)하여 새로운 프로세스가 생성하는 작업이 끝났으니 ptable lock을 해제한다. trapret()은 trap retrun의 약자로 trap을 통해 kernel에 진입하여 작업을 완료했으니 다시 user mode로 전화하기 위해 kernel stack의 trapframe에 저장되어있던 user mode에서의 register를 복구시킴으로써 user mode로 전환하는 함수이다.
지금까지 첫 번째 프로세스를 생성하였고 커널 스텍을 위의 그림과 같이 구성하였다. 아직 빼놓은게 하나가 있다. 실행할 프로그램을 메모리로 load하지 않았다. userinit() 함수의 setupkvm() inituvm() 함수를 통해 이루어진다. setupkvm() 함수는 생성될 process를 위한 kernel page table을 생성한다. inituvm() 함수는 할당받은 페이지 테이블에 실행할 프로그램을 mapping하는 함수이다.
Step4. Schedulling
Mpmain() 함수가 Scehduler를 실행함으로써, 시작된다.
특이한 점은 scheduler()함수 return하지 않는다. 즉, 지속적으로 스케쥴링을 담당한다. 즉 다른말로 xv6 kernel이 scheduler() 함수로 진입한 후에는 지속해서 스케쥴링을 담당한다.
후에 eip가 0이 되어 initcode.S 를 호출한다. exec(init, argv)를 하기위한 함수이다.
'CS > OS' 카테고리의 다른 글
7.Posix thread(Pthread) (0) 2022.05.25 5. Process_Scheduling (0) 2022.05.19 XV6 - 이론 정리_1 (0) 2022.05.13 3. Dual Mode / System Call / OS Service (0) 2022.05.11 2.OS 역사 (0) 2022.05.06