Home xv6 백업
Post
Cancel

xv6 백업

2장 systemcall


os의 3가지

  • multiplexing
  • isolation
  • interaction

응용프로그램은 믿지 못하고 버그가 있으며 강한 고립을 원할 수 있다. 직접 자원을 사용하기보다 파일 시스템으로 추상화하는 것이 강한 고립의 도움이 된다.

cpu 전환시 레지스터 값 등이 저장되기 때문에 프로세스는 시분할에 대해 몰라도 된다.

2-2. 모드

os가 다른 응용프로그램이 오류를 일으켜도 잘 돌아가길 원한다.

이를 위해서는 프로세스들이 다른 프로세스에 침범하거나 os를 건들이지 못하게 만들어야한다.

이를 위해 cpu에서 도움을 준다.

  • machine mode
  • supervision mode
  • user mode

xv6는 부팅 시 일부 코드를 머신모드에서 돌리고 감독모드로 전환한다.

2-3. 커널

감독 모드에서 어떤 코드를 돌릴지는 매우 중요하다. 이에 따라 두가지로 나뉜다.

  • monolithic kernel
  • micro kernel

2-5. 프로세스  

( xv6에서 ) 고립의 단위는 프로세스이다.  프로세스 추상화는 다른 프로세스를 망가뜨리거나 접근하지 못하게 막으며 커널 또한 접근하지 못하게 만든다. 이는 커널에서 다음 같은 기술들로 만들어진다.

  • user/supervision mode flag
  • address spaces
  • time-slicing of threads

고립성을 강화하기 위해 프로세스 추상화는

  • 자신만의 머신을 가진다고 착각
  • 고유의 메모리와 주소를 가지며 전체를 사용한다고 착각
  • 자신의 cpu 가 있다고 착각

프로세스는 프로세스의 명령을 실행시키는  실행 스레드를 가지고 있다. 두가지 스택을 가지고 있다.

  • user stack
  • kernel stack

RISC-V의 ecall이 실행되면

  • 하드웨어 특권 레벨이 올라간다.
  • pc를 커널의 엔트리로 욺긴다
  • 엔트리에서 커널 스택으로 바꾸고 커널 명령어를 실행한다.

시스템 콜이 완료되면

  • 유저 스택으로 바꾸고
  • sret명령으로 유저 공간으로 돌아간다
  • 하드웨어 특권 레벨이 내려가고
  • 이전 명령어를 실행한다.

    2-6. booting


3. page table


3.1 paging hw


RISC-V 명령어는 가상 주소를 조작한다.

RISC-V 페이지 테이블 하드웨어가 물리 주소와 가상주소를 매핑해 연결한다.

xv6는 39비트 주소를 사용한다.  

그중 앞의 27비트를 PTE를 찾는데 사용한다.

PTE / PAGE TABLE ENTRIES

  • $ 2^{27}$ 크기의 배열이다.

  • PPN / Physical page number

    - 44bit

  • some flags

가상 주소의 12(39-27) 비트가 남는다. PTE의 PPN 44비트를 56비트 물리주소에 복사하고 남은 12비트에 가상주소의 남은 12비트를 복사한다.

64비트 체계지만 가상 주소는 앞 25비트가 사용되지 않았고, PTE 포맷 또한 10비트 늘려서 (54 + 10) 사용할 수 있다. ( 물리적 페이지 번호를 늘릴 수 있다. )

$2^{39} = 512GB$ 로 프로세스에게 충분한 공간이다. $2^{56}$ 의 크기또한 I/O 디바이스와 DRAM에게 충분하다.

translate

가상 주소는 물리 주소로 3 단계로 변경된다.  

페이지 테이블은 물리 주소에 3 level 트리로 저장된다.

  • 루트는 512개의 PTE를 가지고 있는4096byte 크기의 페이지 테이블이다.

  • $2^9 * 2^9 * 2^9=2^{27}$  ( 전체 페이지 개수를 쪼갠 것이다. )

페이지를 쪼개는 것의 이점은 전체 페이지 디렉토리를 생략할 수 있다는 것이다.

0번 페이지만 사용된다면 1번부터 511번까지의 페이지는 invalid 된다. 그들의 두번째 페이지 테이블과 3번째 테이블은 무시된다.

하지만 cpu가 3 PTE를 메모리에서 불러와야하는 문제가 있다. 이에 PTE 캐시를 사용한다. 이를 TLB라고 부른다.

각각의 페이지 테이블은 flag bits가 있다.  

TLB / translation Look aside Buffer

3.3 CODE


pagetable_t 자료구조는 말 그대로 root page table page의 포인터이다.

  • walk

    - 가상 주소의 PTE를 찾는다.

  • mappages

    - 새로운 매핑을 위해 PTE를 설치한다.

  • KVM m

    - kernel page table 조작

  • UVM m

    - user page table 조작

  • copyout, copyin

    - 유저 가상 주소로 / 에서 데이터를 복사한다.

    - vm.c에 정의 됨 - 물리 주소 변환때문에

부팅 되면 main에서는 kvminit을 부른다. 커널 페이지를 만들기 위해 kvmmake를 사용한다.

kvmmake는 paging 전에 발생한다. 주소가 직접 매핑된다.

물리적 메모리 페이지를 할당받아 첫번째 루트 페이지 테이블을 잡아준다.

kvmmap을 불러 변환을 설치한다 / 매핑한다.  

( 커널의 명령어, 데이터, 물리 메모리, 장치 메모리 범위 ) 직접 매핑

proc_mapstacks로 각 프로세스들을 위해 커널 스택을 할 당한다. (“proc_mapstacks” (Cox 등, p. 36) )

kvmmap은 mappages를 사용한다.  

mappages는 가상주소와 물리주소를 사이즈 크기만큼 맵핑해서 pte에 집어 넣는다. 이때 페이지 간격으로 이 작업을 진행한다.

가상주소에 대응되는 PTE를 찾는다. 이후 물리 주소로 pte를 초기화한다. pte valid 설정도 해준다.

walk 시 9비트씩 3레벨 페이지 테이블을 탐색한다. PTE가 valid하지 않으면 해당 page가 allocated 되지 않은 것이다. alloc이 설정되어 있으면 페이지 테이블을 만들어주며 진행한다. 마지막에는 3번째 페이지 테이블 엔트리의 주소를 반환한다.

위의 코드는 물리 메모리가 커널 가상 주소와 직접 매핑 되어 있어야 작동한다.

main은 knminithart를 부른다.  

satp에 root page table page를 저장한다.  

> 이후 가사 주소가 활성화 된다.

cpu 가 페이지 테이블을 바꾸면 TLB를 무효화 해야한다.

“sfence.vma” (Cox 등, p. 36) 를 이용해 현재 cpu의 TLB를 flush 한다.

3.6 process address space


4장 trap / systemcall


4.0


cpu에는 3가지 특수한 명령이 있다.

  • systemcall

    - ecall 로 실행

    - 커널이 무엇인가 해야됨

  • exception

    - user나 kernel이 illegal한 행동을 할 때 발생

  • interrupt

    - 디바이스에서 시그널을 보냄

책에서는 trap으로 위 상황들을 이야기한다.

trap은 transparent 해야한다.

  • 이전 실행되던 코드들은 resume 되야한다.

  • special happened를 알 필요가 없다.

이는 특히 코드가 예상하지 못하는 디바이스 인터럽트에 특히 중요하다.

trap은 kernel에서 handling한다.

  • 이는 systemcall에 있어서 자연스럽다.

  • 커널만이 디바이스에 접근 가능하도록 격리

  • 멀티 프로세서간 디바이스를 공유하기 편하다.

  • 잘못된 프로그램을 종료하여 모든 예외에 응답

trap의 4단계

  1. 하드웨어 작업이 cpu에 의해 얻어짐

  2. 어셈블리는 커널 c 코드를 위해 준비

  3. c 함수는 트랩을 어떻게 할지 정함

  4. 시스템 콜이나 드라이버 서비스 루틴 실행

4.1 trap machinery


RISC-V의 레지스터

  • stvec : trap handler의 주소를 담는다.

  • sepc : pc를 저장해 놓는다.

    - sret 명령으로 pc에 sepc를 대입해 복구한다.

  • scause : trap 이유를 담는 번호 저장

  • sscratch : 레지스터 오버라이팅을 피하는데 도움준다.

  • sstatus

    - SIE 비트는 디바이스 인터럽트가  가능한지 나타낸다.

    - clear 하면 인터럽트를 미룬다.

    - SPP 비트는 trap이 어디서 왔는지 나타낸다. ( 유저인지 super인지 )

        - 즉 무슨 모드로 sret 해야하는지 알려준다.

트랩 진행과정 / RISC-V 하드웨어

  1. 만약 trap이 디바이스 인터럽트이고 SIE가 clear하면 기다린다.

  2. SIE 비트를 clear한다.

  3. pc를 sepc에 복사한다.

  4. 현재 모드를 SPP 비트에 저장한다.

  5. scause 설정한다.

  6. supervisor 모드로 설정

  7. stvec -> pc로 복사

  8. 새로운 pc에서 실행

cpu는

  • 커널 페이지 테이블로 스위치 하지 않는다.

  • 커널 스택으로 바꾸지 않는다.

  • pc 외 어떤 레지스터도 건들이지 않는다.  

위 작업은 커널이 해줘야한다. 유연성 확보

4.2 Traps from user space


  • uservec

  • usertrap

  • usertrapret

  • userret

xv6 트랩 처리의 제약은 트랩시 페이지 테이블을 전환하지 않는다는 점이다. ( 트랩 처리 코드가 시작되는 지점에서 유저 모드 페이지 테이블이 사용됨 ) 이를 위해서는 trap handler 주소 ( stvec )가  유저 페이지 테이블에 valid mapping 되야한다.

??? 또한 trap handling code는 kernel page table로 교체가 필요할 수 있다. 이 경우에서도 돌아가기 위해서는 커널 페이지 테이블에도 trap handler 주소가 valid mapping 되어야 한다.

두가지 모두를 만족시키기 위해 trampoline page를 사용한다.

trampoline page

  • uservec 을 포함한다.

    - trap handling 코드이다.

  • 모든 프로세서의 TRAMPOLINE에 매핑된다.

    - 이는 가상 주소의 최상단이다.

  • 커널 페이지 테이블에도 TRAMPOLINE 영역에 매핑된다.

    - 같은 주소에 매핑되므로 switch 후에도 계속 실행가능

  • 유저 페이지 테이블에 PTE_U 없이 매핑되어 supervisior에서 실행가능하다.

uservec

  1. uservec 이 실행될때 32개의 레지스터를 저장해야한다.

    1. 메모리 어딘가에 저장된다.

    2. 이를 위해 주소를 담을 레지스터가 필요하다.

  1. sscratch 레지스터를 사용 ( RISC-V )

  2. csrw 명령어로 a0를 sscratch에 저장한다.

  3. 커널은 프로세서마다 trapframe 구조를 만들어 32개의 레지스터를 저장하게 한다.

  4. TRAPFRAME ( 유저 페이지 테이블에 있는 가상주소 ) 에 커널의 trapframe이 매핑된다.

  5. TRAPFRAME의 주소를 a0에 욺기고 레지스터들을 그곳에 저장한다.

    1. 유저의 a0 또한 sscratch에서 가져와 저장한다.

  1. uservec은 초록 정보들을 트랩프레임에 가져오고

    1. satp을 커널 페이지 테이블로 바꾼다.

    2. usertrap을 호출한다.

usertrap

trap의 원인을 결정하고 실행하고, 반환한다.

  1. stvec을 바꾼다.

    1. kernelvec 에 의해 핸들링 되도록

    2. 커널 안에서 실행중이므로 만약 인터럽트가 발생하면 커널 벡터가 핸들링 해야하기 때문이다.

  1. sepc 저장한다.

    1. 다른 프로세서로 스위치되거나, sepc를 수정하고도 유저 공간으로 return할 수 있다.

  1. 원인에 따라…

    1. syscall

        1. add 4 pc

            1. 돌아와서 다음 문장 실행하기 위해

            2. RISC-V는 ecall명령어의 포인터를 pc에 남김

    2. devintr

    3. ex-ception

        1. kill

usertrapret

유저 공간으로 돌아가기 위한 과정이다.

  1. stvec이 uservec을 가르키게 만든다.

  2. sepc 를 저장된 pc 값으로 설정

  3. userret 부른다.

    1. trapoline page에 존재하는

    2. switch page tables

4.3 systemcall 호출 과정


  • a7에 시스템콜 번호가 들어간다.

  • a0와 a1에 인자가 들어간다.

  • ecall을 통해 커널에 trap을 건다.

  • uservec, usertrap, syscall이 실행된다.

  • sys_exec 함수가 실행된다.

  • return 값은 a0에 들어간다.

4.4 인자


시스템 콜의 구현은 user 코드에서 인자를 찾아와야한다.

유저는 시스템콜 wrapper 함수를 부른 것이다. 레지스터에 argument가 저장됨

커널이 찾을 수 있도록, 커널 트랩 코드는 user 레지스터를 현재 프로세스의 트랩 프레임에 저장한다.

exec 예시를 보면 문자열 인수를 지정하는 포인터 배열을 커널 공간으로 넘긴다. 두가지 문제점이 있다.

  1. invalid 포인터를 넘겨 이상한 짓을 할 수 있다.

  2. 커널 페이지 테이블과 유저 테이블이 일치 하지 않는다. 커널은 넘겨 받은 주소에 명령어를 실행할 수 없다.

이를 위해 copyinstr 명령어를 사용한다.

“A” (Cox 등, p. 48)

This post is licensed under CC BY 4.0 by the author.