본문 바로가기
TIL (Today I Learned)/컴퓨터 시스템(CS)

[CS] Kaist PintOS User Programs, System Call #5

by 둥굴프 2022. 12. 27.
이 문서는 카이스트 핀토스 원유집 교수님의 강의를 참고하여 작성하였습니다

 

Pintos Project2 systemcal

 

User program에서 write를 호출하면 syscall.c에 있는 write함수를 호출한다.

write함수는 syscall3 함수를 호출한다.

syscall3함수는 3개의 인자를 받아 User Stack에 쌓고 Interrupt Vector Table의 syscall_handler를 호출한다.

프로젝트에서 구현할 부분은 syscall_handler의 내용이다.

여기 채워져야할 systemcall의 'number'는 syscall_nr.h에 정의되어있다.

 

 System call handler 구현 목표

1. system call handler가 system call number를 사용해 system call을 호출하도록 만들기.

2. ⚠️ 유저 프로그램에서 제공한 주소의 유효성을 검사.

- 포인터(제공한 주소)는 커널 주소공간이 아닌 유저 주소공간을 가리켜야만 한다.

- 만약 유효하지 않은 포인터(주소)라면 Page fault를 일으킨다.

3. 유효성 검사를 통과하면, user stack의 인자를 kernel로 복사한다.

4. eax 레지스터에 system call의 반환값을 저장한다.

 

사용자는 유효한 사용자 주소를 제공해야한다.

커널은 system call을 실행하는 동안 유저 주소 공간에 접근하면 안된다.

대신, 매개변수를 커널 주소 공간에 복사해야한다.

커널이 유저 공간에 접근하지 않고 커널 내부의 프로그램을 실행할 수 있어야한다.

 

 

#1 Address Validation

유저는 유효하지 않은 포인터들을 넘길 수 있다.

- null pointer / pointer to unmapped virtual memory

- pointer to kernel virtual memory address space (above PHYS_BASE)

 

커널은 포인터의 무효성을 감지하고 커널이나 실행 중인 다른 프로세스에 해를 끼치지 않고 프로세스를 종료해야 한다.

 

감지하는 방법

METHOD 1. 유저가 제공한 포인터의 유효성을 확인하기

- 페이지 테이블을 확인하고 주어진 주소들이 모두 매핑 되어있는지 아닌지 확인해야한다.

- 다음에 있는 함수를 사용하라 : 'userprog/pagedir.c' 👉 'thread/mmu.c' / 'thread/vaddr.h'

 

자원 누수를 막는 방법 : 포인터의 유효성이 검증된 다음 page를 할당하거나 lock하기.

 

METHOD 2. 사용자가 PHYS_BASE보다 낮은 주소를 가리키는지 확인하기

- 이 경우 OS는 이 주소가 mapping 되어 있는지 아닌지 확인할 수 없다. 단지 PHYS_BASE보다 낮은지만 확인한다.

- 유저가 유효하지 않은 포인터들을 제공했을 경우 page fault가 발생한다.

- page fault에 대한 코드를 수정하여 처리할 수 있다.

- 위 방법은 빠르다. 다른 것을 확인할 필요가 없고, 하드웨어 MMU에 의존하여 검증하기 때문이다.

 

자원 누수를 막는 방법 : 메모리 접근에서 에러코드를 반환할 방법이 없다.

이런 경우를 처리하기 위해서는 exception.c:page_fault()함수를 수정해야한다.

추가적으로, exception.c:page_fault()함수에서 rax를 0xffffffff로 설정하고, 이것의 이전 값을 rip에 복사해야한다.

 

#2 Add system calls

lib/user/syscall.c

 

halt(void) : 핀토스를 종료하는 system call.

Use void shutdown_power_off(void)

exit(int status) : 프로세스 종료(exit). 'Name of process : exit(status)' 메세지를 출력해야한다.

Use void thread_exit(void)

exec (const char *file) : 자식 프로세스를 생성하고 cmd_line에 해당하는 프로그램을 실행합니다.

This exec is not exec in Unix. Exec in pintos is combination of fork and exec.

wait (pid_t pid) : 프로세스 ID가 pid인 자식 프로세스가 종료될 때까지 기다린다.

 

1. wait (pid_t pid) 을 어떻게 구현해야 하는가?

1. 스레드 구조체에 "wait"하기 위한 세마포어를 추가한다.

2 .스레드가 처음 생성되었을 때 세마포어는 0으로 초기화된다.

3. wait(tid)는 tid의 세마포어에 대해 sema_down을 호출한다. 이순간 호출한 프로세스는 blocked상태가 된다.

4. tid 프로세스가 exit()을 호출할 때 sema_up을 호출한다.

5. sema_down과 sema_up 호출 어디서 할까?

 

추가로, 스레드 구조체 exit status를 나타내는 feild를 추가해야한다.

스레드 구조에 종료 상태를 나타내는 필드를 추가

 

카이스트 원유집 교수님의 강의자료 中

 

2. exec (const *cmd_line)

부모는 자식 프로세스가 성공적으로 생성되고 바이너리 파일이 성공적으로 불러와질 때까지 기다려야한다.

 

1.스레드 구조체에 "exec()"에 대한 세마포어를 추가합니다.

이는 스레드 구조체의 두번째 세마포어가 될 것이다.

2. 스레드가 처음 생성되었을 때 세마포어는 0으로 초기화된다.

3. sema_down을 호출하여 하위 프로세스의 실행 파일이 성공적으로 로드될 때까지 기다린다.

4.실행 파일이 성공적으로 로드되면 sema_up을 호출한다.

5. sema_down과 sema_up 호출 어디서 할까?

 

추가로, 로드 상태를 나타내는 또 다른 변수 load status가 필요하다.

스레드 구조에서 파일이 성공적으로 로드되었는지 여부를 나타내는 필드가 필요하다.

 

카이스트 원유집 교수님의 강의자료 中

 

 

댓글