본 내용은 부산대학교 정보컴퓨터공학부 안성용 교수님의 [운영체제] 수업의 과제 내용을 참고하였습니다.
System call 이란?
User mode 에서 Kernel mode로 접근하게 해주는 interface이다.
Trap Handling Process
아래의 그림을 보자.
Process P는 user mode이므로 자기 자신의 memory만을 볼 수 있다.(자기 자신의 address space)
이 때, Process P가 kill() system call을 호출했을 때를 생각해보자.
xv6 에서는 eax 레지스터를 이용해서 system call number를 전달받게 설계되어있다.
이 때 알아둬야 할 사항은, system call 또한 trap의 한 종류라는 것이다.
우측의 초록색 글자로 trap-table index가 64번이라고 주어졌는데, trap-table의 64번에 바로 syscall-table이 저장되어 있는 것이다.
syscall-table index는 6번을 줬는데, 이는 즉 kill() system call 이 syscall-table의 6번에 위치하고 있다는 말이다.
Process P에서 kill() system call을 요청하면 interrupt가 발생하므로 kernel mode로 진입하고, trap-table을 먼저 참조하고 64번의 syscall-table로 간다. syscall-table의 6번이 가리키는 페이지를 따라가면 sys_kill이 위치하고 있다. 아래의 그림을 참고하자.
User-level System Call API
user.h 라는 헤더파일을 보면 이렇게 기본적인 system call 들이 선언되어 있다.
이 중에 우리가 user mode ( user code라고도 이해 가능) 에서 호출하였던 kill 이라는 시스템 콜이 정의되어 있다.
usys.S 에 들어가보면 여러가지 시스템콜들이 define 되어있는 걸 볼 수 있는데, kill은 오른쪽과 같은 과정을 거쳐 정의된다. 잘 보면 movl $6 으로 바뀐 것을 볼 수 있는데 이건 도대체 어떻게 된 것인지 살펴보자.
syscall.h 헤더파일에 들어가보면 모든 시스템콜들을 이와 같이 숫자로 정의해 놓았다. 따라서 위에서 6이 대치된 이유를 이해할 수 있을 것이다.
syscall.c 에 들어가보면 syscall 함수가 정의되어있다. 먼저 trap.c 에 들어가보면 만약 trap의 number가 64이면 syscall함수를 호출한다고 나와있는데, 아까 위에서 언급했다시피 xv6에서는 eax 레지스터에 syscall의 number를 저장한다. 따라서 num 변수에 6을 저장하고, 6번째 시스템콜인 kill 시스템콜을 호출하게 되는 것이다.
또한 이렇게 sysproc.c 파일에서 정의해놓은 외부 함수들을 이렇게 선언하여주어서, 접근할 수 있게 해놓은 것을 볼 수 있다.
실제로 sysproc.c 에 가보면 모든 system call 함수들이 선언이 되어 있는데 sys_kill 같은 경우도 user code에서 system call 을 호출할 때 전달받았던 인자를 pid변수에 저장한 후, 이를 proc.c에 있는(실제로 kill 동작을 수행하는(?) ) 함수 kill을 호출하게 된다.
따라서 kill system call을 하는 과정을 도식화하면 위와 같다.
user code에서 kill을 호출하게 되면, 먼저 user.h를 통해 이것이 system call 함수라는 것을 확인한 후 usys.S 를 거쳐 kernel mode로 진입을 하게 된다. 과정을 쭉 따라가다가 이제 trap.c에서 system call 의 number인 64를 받아서 system call인 것을 확인하고 syscall.c 파일의 syscall() 함수를 호출하게 되며, syscall 함수에서는 eax에 저장되어 있던 6 이라는 값을 참조하여 6번 system call 인 sys_kill 함수를 호출하게 되고, sys_kill 에서는 proc.c에 있는 실질적으로 kill 동작을 수행하는 kill() 함수를 호출하는 것이다.
How to make my own System call?
이번 과제의 핵심과 과정을 잘 이해해야만 나만의 system call 을 만들 수 있다.
getreadcount라는 시스템 콜을 만드는 것이고, 이는 프로세스들이 read() system call 을 몇 번 호출했는지를 반환하는 system call이다. 실제로 xv6에서 readcount를 입력하면 그 횟수가 출력이 되어야한다.
먼저, 실제로 xv6 환경에서 readcount란 명령어를 만들기 위해서는 "Makefile"에서 내 user code 즉, readcount.c를 정의해줘야 한다.
그렇다면 내 user code를 작성해주자.
readcount.c 를 vi편집기를 통해 작성하자.
내 user code 에서 getreadcont를 호출했다면 실제로 getreadcount라는 system call을 정의해줘야 한다.
이렇게 user.h 에 적어주고,
usys.S에도 추가해준다.
syscall.c에도 이렇게 추가한다.
syscall.h에도 이렇게 추가하고,
sysproc.c에도 이렇게 추가해준다.
proc.c 에도 이렇게 추가해준다. 다만 proc.c 에 이렇게 추가해주려면 아래와 같이 이렇게 defs.h 헤더파일에 proc.c의 getreadcount 함수를 선언을 해줘야한다. 아니면 굳이 proc.c 까지 갈 필요없이 sysproc.c에서 readcount 를 return 하여 주어도 된다.
syscall.c 파일의 상단에 readcount를 선언하고 0으로 초기화해준다.
read 시스템콜이 5번이므로 만약 syscall 함수에서 eax 레지스터에서 5 값을 가져온다면 , read 시스템콜을 호출하는 것이므로 위와 같은 조건문을 추가해준다.
다만 proc.c에서 외부 변수인 readcount를 이렇게 선언을 먼저 해주어야 한다.
모든 준비가 끝났고, make를 한 후에 make qemu-nox를 통해 xv6를 실행해준다.
성공적으로 수행이 되는 것을 확인할 수 있다.
이렇게 이번 과제를 통해서 user mode에서 kernel mode로의 진입, trap과 system call, 또 어떻게 시스템콜을 호출하는지 상세하게 파일을 하나하나 뜯어보면서 살펴보았다.
유익한 과제였고, 재밌었다.
//문제제기 및 피드백 언제든지 감사히 받겠습니다.
'Computer Science > Operating System' 카테고리의 다른 글
[운영체제] Thread(쓰레드) (0) | 2022.05.15 |
---|---|
[운영체제] Swapping (0) | 2022.05.08 |
[운영체제] Smaller Tables -Page Directory (0) | 2022.04.24 |
[운영체제] 제한적 직접 실행(Limited Direct Execution) (0) | 2022.04.23 |
[운영체제] 프로세스(Process)와 가상화(Virtualization) (0) | 2022.04.23 |