System calls and Handlers
개요
주 목표
- 기존 : system call handler table 비어있음.
- 수정 후
- Pintos의 system call handler 완성
- 유저에게 서비스를 제공하기 위한 시스템 콜 추가
- 프로세스 관련 :
halt,exit,exec,wait - 파일 관련 :
create,remove,open,filesize,read,write,seek,tell,close
- 프로세스 관련 :
수정할 파일
- threads/thread
- userprog/syscall
- userprog/process
System call
운영체제가 제공하는 서비스를 위한 programming interface
유저 모드 프로그램이 커널 기능을 사용하게 해준다.
시스템 콜은 커널 모드에서 실행되고 유저 모드에게 반환한다.
시스템 콜의 주요 포인트는 시스템 콜을 호출하기 위해 하드웨어 interrupts가 발생하여서 실행 모드의 우선순위가 특수(special) 모드로 상승한다는 점이다.
Pintos에서 시스템 콜의 호출 프로세스
그림
system call handler
시스템 콜 number를 사용하여 시스템 콜 handler에서 시스템 콜을 호출한다.
시스템 콜 number : syscall.c에 정의되어 있음
Requirement
- 시스템 콜 handler가 시스템 콜 number를 사용하여 시스템 콜을 호출하도록 해야함
- parameter 리스트에 있는 포인터의 타당함(validation) 확인
- 이 포인터들은 kernel area가 아닌 user area를 가리켜야 한다.
- 이 포인터들이 타당한 주소를 가리키지 않으면, page fault이다.
- user stack의 arguments를 kernel로 복사함
eax레지스터에서 시스템 콜의 반환 값을 저장함
Address Validation
- 유저는 시스템 콜을 통해 잘못된 포인터를 전달할 수 있다.
- null pointer나 mapping되지 않은 가상 메모리를 가리키는 포인터
- 커널 가상 메모리 주소 공간(
PHYS_BASE)을 가리키는 포인터
- 커널은 부적절한 포인터를 감지해야 하고 커널이나 다른 작동중인 프로세스들에게 악영향 없이 프로세스를 종결시켜 한다.
- 감지하는 방법
- 방법 1 : 유저가 제공하는 포인터의 타당성 검사
- 유저 메모리 접근을 다루는 가장 간단한 방식
- userprog/pagedir.c 나 threads/vaddr.h 에 있는 함수 사용
- 포인터의 타당성 확인한 페이지만 lock이나 할당
- 방법 2 : PHYS_BASE 밑만 가리키는 것만 확인
- 잘못된 포인터는 page_fault 야기.
page_fault()코드를 수정하여 해결 가능 - MMU에서 강점이 있어 첫번째 방법보다 빠름
- 보통 실제 kernel에서 사용
- 메모리 접근에서 에러코드를 반환할 방법이 없기 때문에 더 어렵다.
- 후술할 함수들을 사용하여 처리할 수 있음
get_user,put_userpage_fault에서eax를0xffffffff로 설정하고 이전의 값을eip로 복사하는 방법도 있음
- 잘못된 포인터는 page_fault 야기.
- 방법 1 : 유저가 제공하는 포인터의 타당성 검사
추가할 시스템 콜
void halt(void)- Pintos 종료
void shutdown_power_off(void)사용
void exit(int status)- 프로세스를 나간다(exit)
void thread_exit(void)사용- “Name of process: exit(status)“라는 메시지를 출력해야 함
pid_t exec (const char *cmd_line)- 자식 프로세스를 만들고
cmd_line에 상응하는 프로그램을 실행한다.
- 자식 프로세스를 만들고
int wait (pid_t pid)- process id 가
pid인 자식 프로세스의 종결을 기다린다.
- process id 가
Process Hierarchy(계층)
- 존재하는 프로세스를 프로세스 계층에 따라 상승시킨다.
- 부모 자식간 관계를 나타낸다.
- 부모 프로세스를 가리키는 포인터 :
struct thread* - 형제 프로세스를 가리키는 포인터 :
struct list - 자식 프로세스를 가리키는 포인터 :
struct list_elem
- 부모 프로세스를 가리키는 포인터 :
wait() system call
int wait (pid_t pid)
- 자식 프로세스
pid가 exit하길 기다리고 exit status를 되찾아옴 pid가 아직 있다면, 종결되길 기다린다.pid가 exit하며 전달받은 status 반환pid가exit를 호출하지 않았지만 kernel이 종결시켰다면, -1을 반환- 부모 프로세스는 종결된 자식 프로세스를 기다리기 위해 호출할 수 있다.
- 종결된 자식 프로세스의 exit status 반환
- 자식이 종결된 이후, 부모는 자식의 process descriptor을 비할당화 해야한다.
wait이 실패하고 -1을 반환하는 경우pid호출 프로세스의 직접적인 자식을 가리키지 않을 때wait을 호출한 프로세스가 이미pid에wait을 호출했었을
Correct implementation
- process_wait()
- child_tid를 사용하여 자식 프로세스의 descriptor를 찾는다.
- 자식 프로세스가 exit 하기 전까지 호출자를 막는다.
- 자식 프로세스가 exit하면, 자식 프로세스의 descriptor를 비할당화 하고 자식 프로세스의 exit status를 반환한다.
- Semaphore
- 쓰레드 구조체에 “wait"에 관한 세마포어 추가
- 쓰레드가 처음 생성될 때 세마포어는 0으로 초기화 된다.
- wait(
tid)에서,tid의 세마포어에 대한sema_down을 호출한다. - 프로세스
tid의 exit()에서,sema_up을 호출한다. sema_down과sema_up의 올바른 자리가 중요하다
- Exit status
- 쓰레드 구조체에 exit status를 나타내는 field 추가
wait을 호출한 Parent와 child 의 흐름
그림
exec() system call
pid_t exec(const *cmd_line)
cmd_line을 실행하는 프로그램 가동- 쓰레드를 생성하고 가동한다. pintos에서
exec()는 unix에서fork()+exec()와 같음 - arguments를 실행될 프로그램에 전달
- 새로운 자식 프로세스의
pid전달 - 프로그램을 load하거나 프로세스를 생성하는데 실패하면 -1 반환
exec를 호출하는 부모 프로세스는 자식 프로세스가 생성되고 excutable을 완전히 load하기 전까지 기다려야 한다.
Kernel function for exec() : process_execute()
부모는 자식 프로세스가 성공적으로 생성되고 binary file이 완벽히 load 된걸 알 때까지 기다려야 한다.
세마포어
exec()에 대한 세마포어를 쓰레드 구조체에 추가한다.- 쓰레드가 처음 생성될 때 세마포어는 0으로 초기화 된다.
sema_down을 호출하여 자식 프로세스가 excutable file을 잘 load하길 기다린다.- excutable file이 load되면
sema_up을 호출한다. sema_down과sema_up의 위치가 중요
load status
- 쓰레드 구조체에서, 파일이 성공적으로 load되었는지를 나타내는 field가 필요함
exec를 호출한 부모와 자식의 흐름
그림
exit()
- 유저 프로그램을 종결시키고 커널에게 상태 반환
- 프로세스의 부모가 기다린다면, 이 함수가 반환될 status임
| |
Kernenl function for exit() : thread_exit
- Exit status
- 프로세스의 status에 저장한다.
- 세마포어
- 현재 프로세스에
sema_up호출
- 현재 프로세스에
| |