-
[SW 정글 68일차] Pintos project 2 - System Calls 구현 1일차기타/SW 사관학교 정글 2021. 10. 10. 02:56
오늘부터 PintOs project 2. System Calls 구현에 들어가기 시작했다.
다른 동료들은 일찍 시작했는데 다들 할 것이 너무 많다고들 말한다.
일단은 목요일 10시 전까지만 완료하면 되니까 우리 조는 우리 조 방식대로 계획을 세워 진행하려고 한다.
1. 내가 해결해야할 것은?
이번에 내가 해야할 것은 System Calls 구현이다.
system call은 프로세스가 user mode일 때에 하지 못하는 일들을 커널에게 요청하는 경우에 쓰는 일종의 커널에서 제공해주는 API이다.
user mode일 경우에 못하는 일들은 대표적으로 I/O 요청(read, write), 네트워크 통신, 메모리 접근 등이 있다.
문제는 정말 간단하게 system call을 구현해달라는 것인데 구현을 위해 전체적인 코드의 구조를 이해해야하는 것이 큰 일인 것 같다.
일단은 pintos에서 기본적으로 제공해주는 system call은 프로세스를 종료시키는 것이다.
system call이 발생하면 system call number를 검색한 후 system call arguments를 검색한 다음에 어떠한 작업을 수행하게 되는 구조이다.
2. System Call Details
첫 번째 프로젝트 때 타이머 인터럽트와 I/O 인터럽트에 의해 유저 모드의 프로세스가 커널모드로 넘어가는 것을 다루었다.
이것은 CPU의 밖인 외부 하드웨어에 의해 발생하는 external interrupt이다.
이러한 excternal interrupt말고 OS는 프로세스 code 안에서 발생하는 software exceuptions을 다루기도 한다.
예를 들어 page fault나 division by zerio 같은 것들이 있다.
기존의 x86 아키텍쳐에서는 시스템 콜이 software exceuptions와 같게 핸들되었다.
하지만 x86-64에서는 syscall을 제시하고 syscall은 system call handler를 call하는 것에 있어 빠른 방법으로 제공한다.
pintos에서도 system call을 하기위해 syscall을 invoke하고 system call number와 추가적인 arguments를 syscall을 invoke하기 전에 보통의 방식대로 레지스터에 올린다.
그러므로 system call handler가 제어권을 얻으면 system call number는 %rax, arguments은 %rdi, %rsi, %rdx, %r10, %r8, %r9에 순서대로 넣어진다.
이제 구현해야할 syscall을 구현해주면 된다.
3. 코드 구현
우리가 구현해야할 syscall은 아래와 같다.
void halt (void) NO_RETURN; void exit (int status) NO_RETURN; pid_t fork (const char *thread_name); int exec (const char *file); int wait (pid_t); bool create (const char *file, unsigned initial_size); bool remove (const char *file); int open (const char *file); int filesize (int fd); int read (int fd, void *buffer, unsigned length); int write (int fd, const void *buffer, unsigned length); void seek (int fd, unsigned position); unsigned tell (int fd); void close (int fd);
순서대로 진행해보자.
3-1) void halt (void) NO_RETURN
halt()는 power_off()라는 함수를 호출함으로써 PintOs를 종료시키는 것이다.
deadlock이 발생하여 정보를 잃을 수 있기 때문에 드물게 사용된다.
void halt (void)는 이미 pintos에서 power_off()를 구현해놨기 때문에 간단하게 구현을 할 수 있다.
power_off()는 다음과 같다.
/* Powers down the machine we're running on, as long as we're running on Bochs or QEMU. */ void power_off (void) { #ifdef FILESYS filesys_done (); #endif print_stats (); printf ("Powering off...\n"); outw (0x604, 0x2000); /* Poweroff command for qemu */ for (;;); } /* Print statistics about Pintos execution. */ static void print_stats (void) { timer_print_stats (); thread_print_stats (); #ifdef FILESYS disk_print_stats (); #endif console_print_stats (); kbd_print_stats (); #ifdef USERPROG exception_print_stats (); #endif }
먼저, print_stats()함수를 호출하는데 print_stat()함수는 timer가 몇ticks를 돌았는지, idle thread, kernel thread, user program이 각각 얼만큼의 ticks만큼 실행됐는지, console에 얼마나 문자들이 쓰였는지, keybobard가 얼마나 눌렸는지 등의 정보를 print해주는 역할을 한다.
그리고 powering off 문자열을 호출하고 outw라는 port I/O하는 linux 명령어로 16bit의 data(2번째 인자)를 port(1번째 인자)에 write해주는 역할을 한다. outw를 통해 qemu에게 power off되었다는 것을 알려준다.
3-2) void exit (int status)
exit()는 현재 실행 중인 process를 종료하고 kernel에게 process가 종료되었다는 status를 반환해준다.
만약에 process의 parent process가 현재 processd의 exit을 기다리는 중이였다면 parent process에게 process가 종료되었다는 status를 반환해준다.
통상적으로 0이라는 status가 성공적으로 exit되었다는 것을 의미하고 nonzero value가 error가 발생했다는 것을 알려준다.
/* End current thread, record exit statusNo return. */ void exit(int status) { struct thread *cur = thread_current(); cur->exit_status = status; printf("%s: exit(%d)\n", thread_name(), status); // Process Termination Message thread_exit(); }
해줘야할 일은 thread 구초체에 exit_status 멤버를 추가해줘야한다.
그 이유는 현재 스레드가 exit되면 해당 사실을 부모 프로세스에게 알려줘야하고 부모 프로세스는 exit_status를 확인하게 된다.
이렇게 현재 스레드의 exit_status를 설정해주고 thread_exit()함수를 실행시켜주면 된다.
3-3) void check_address(const uint64_t *uaddr)
Check_address()는 exec() syscall에 쓰이는 함수로 인자로 들어온 주소 값이 user 영역에 있는 주소 값인지 확인하는 함수다.
PintOS에서는 시스템 콜이 접근할 수 있는 주소를 0cx0000000 ~ 0x8048000으로 제한하기 때문에 유저 영억을 벗어난 영역일 경우 비정상 접근이라고 판단해 exit(-1)로서 프로세스를 종료한다.
pintos에서는 is_kernel_vaddr()함수를 제공해준다.
is_kernel_vaddr()은 인자로 받은 주소가 KERN_BASE(0x8048000)보다 크면 1을 반환하고 그렇지 않으면 0을 반환한다.
즉, 주소 값이 user영역에 있는지 kernel영역에 있는지 확인해주는 함수이다.
[오늘의 나는 어땠을까?]
오늘은 system call구현에 들어갔다.
system call은 이미 OS를 공부하거나 웹 서버를 만들면서 많이 들어본 용어여서 쉽게 이해하면서 구현이 될 줄 알았다.
하지만 구현을 하기 위해서 얽혀있는 코드와 로직이 많았고 그것을 이해하는데만 시간이 부족하다고 느낄 정도였다.
그래도 내가 그냥 '프로그램을 종료해야지'하면서 썼던 exit() system call이 운영체제 입장에서는 어떻게 받아들이고 동작이 되는지를 알 수 있는 좋은 계기가 된 것같다.
이제는 fork나 execve같은 새로운 프로세스를 생성하는 system call을 구현해야할 것이 남아있는데 잘 마무리 짓고 싶다.
그리고 나의 것으로 만들고 이번 project2를 마무리 짓고 싶다.
'기타 > SW 사관학교 정글' 카테고리의 다른 글
[SW 정글 70일차] Pintos project 2 - System Calls 구현 2일차 (0) 2021.10.12 [SW 정글 69일차] 오늘은 Concurrency 되짚어 보기 (0) 2021.10.11 [SW 정글 67일차] Pintos project 2 - Argument Passing 구현 (0) 2021.10.08 [SW 정글 66일차] Pintos project 2 - Argument Passing (0) 2021.10.08 [SW 정글 65일차] Pintos project 2 입문 (0) 2021.10.07