
syscall_handler[구현 2-1] userprog/syscall.c 내 syscall_handler 함수 구현
switch~case문 쓰면 되는데, 각 case 밑에 break 두는 거 잊지 말기rax 레지스터에 저장됨 -> f -> R -> rax로 확인include/lib/syscall_nr.h에서 확인 가능 (열거형임에 유의)f -> R의 rdi, rsi, rdx, r10, r8, r9 멤버로 확인 가능rax 레지스터에 저장해야 함printf("system call!\n")과 thread_exit()은 기본값// userprog/syscall.c
syscall_handler (struct intr_frame *f UNUSED) {
// [구현 2-1] 시스템 콜 번호에 따라, 올바른 함수가 실행되도록 구현한다.
// 인가는 rdi rsi rdx
int number = f -> R.rax;
switch(number){
case SYS_HALT:
/* Halt the operating system. */
// 코드 넣기
break;
case SYS_EXIT:
/* Terminate this process. */
// 코드 넣기
break;
case SYS_WRITE:
/* Write to a file. */
// 코드 넣기
break;
// 이하 생략...
}
// 얘네는 없애줘야 함
//printf ("system call!\n");
// thread_exit ();
}
// include/lib/syscall_nr.h
/* System call numbers. */
enum {
/* Projects 2 and later. */
SYS_HALT, /* Halt the operating system. */
SYS_EXIT, /* Terminate this process. */
SYS_FORK, /* Clone current process. */
SYS_EXEC, /* Switch current process. */
SYS_WAIT, /* Wait for a child process to die. */
SYS_CREATE, /* Create a file. */
SYS_REMOVE, /* Delete a file. */
SYS_OPEN, /* Open a file. */
SYS_FILESIZE, /* Obtain a file's size. */
SYS_READ, /* Read from a file. */
SYS_WRITE, /* Write to a file. */
SYS_SEEK, /* Change position in a file. */
SYS_TELL, /* Report current position in a file. */
SYS_CLOSE, /* Close a file. */
// 뒤에서부턴 Project 3 이후 내용이라 생략
};
[구현 2-2] Address validation 수행 함수 만들기
0인지 확인 -> 빡꾸threads/vaddr.h -> is_kernel_vaddr(주소)가 true면 커널영역의 주소 -> 빡꾸threads/mmu.h -> pml4_get_page(thread_current() -> pml4, 주소)가 널 포인터를 반환하면 매핑되지 않은 주소 -> 빡꾸// userprog/syscall.c
// [구현 2-2] 포인터가 valid한지 확인하는 함수를 만든다.
bool valid_pointer (void *p){
// 널 주소-/ 커널 영역의 주소 / 매핑되지 않은 페이지
if (p == NULL || is_kernel_vaddr(p) || pml4_get_page(thread_current() -> pml4, p) == NULL){
thread_current() -> exit_code = -1;
thread_exit();
};
}
f -> R의 rdi, rsi, rdx, r10, r8, r9 멤버에서 차례로 확인한다.f -> R의 rax 멤버에 저장한다.halt - 핀토스 종료halt 구현하기src/include/threads/init.h에 선언된 power_off()를 호출하면 해결됨. 참 쉽죠?// 사용자 측 (lib/user/syscall.c`)
void halt (void) {
syscall0 (SYS_HALT);
NOT_REACHED ();
}
syscallX 계열 함수는, 인자가 X개인 syscall을 보낸다고 생각하면 됨syscallX의 맨 처음 인수는 시스템 콜 번호 (SYS_HALT가 %rax에 저장됨)%rdi부터 6개 레지스터에 저장됨)syscall 명령으로 시스템콜 호출// 커널 측 (userprog/syscall.c)
switch(number) {
case SYS_HALT:
/* Halt the operating system. */
// [구현 3] 핀토스를 종료. 매우 쉽죠?
power_off();
break;
}
exit - 프로세스, 쓰레드의 종료exit 구현하기process.c의 process_wait()을 수정해야 할 것 같긴 한데, 일단 임시 방편으로...status로 설정.// 사용자 측 (lib/user/syscall.c`)
void exit (int status) {
syscall1 (SYS_EXIT, status);
NOT_REACHED ();
}
exit() 함수를 사용하면 됨status에 대응되는 f -> R.rdi를 보내주기case SYS_EXIT:
/* Terminate this process. */
// [구현 4] 쓰레드/프로세스 종료 후, 종료코드 반환
exit((int)(f -> R.rdi));
break;
write 약간 - 콘솔 출력만 구현// 사용자
int write (int fd, const void *buffer, unsigned size) {
return syscall3 (SYS_WRITE, fd, buffer, size);
}
buffer의 내용을 size만큼 파일 명시자 fd에 출력한다.write 시스템 콜은 fd == 1일 때는 파일이 아닌 콘솔에 출력되게끔 구현해야 함fd == 1일 때만 먼저 구현할 수 있음// 커널
case SYS_WRITE:
/* Write to a file. */
// [구현 5] 콘솔 창에다 출력하는 부분만 일단 우선 구현 가능.
valid_pointer(f -> R.rsi);
if ((int)(f -> R.rdi) == 1){
putbuf((char *)(f -> R.rsi), (size_t)(f -> R.rdx));
}
break;
valid_pointer(f -> R.rsi)로 buffer가 valid pointer인지 확인fd(RDI)가 1인 경우, putbuf을 이용해 buffer(RSI)의 내용을 size(RDX)만큼 출력하기fork[구현 4] fork - 자식 프로세스 만들기
// 사용자
pid_t fork (const char *thread_name){
return (pid_t) syscall1 (SYS_FORK, thread_name);
}
thread_name으로 설정rbx, rsp, rbp, r12 ~ r15 7개의 값만 복제하면 됨 (??????)TID_ERROR 반환0 반환threads/mmu.c의 pml4_for_each()를 사용할 것process_fork (userprog/process.c)__do_fork를 호출함./* 현재 프로세스를 clone. 이름은 매개변수 name으로 설정.
성공 시 쓰레드 id를, 실패 시 TID_ERROR를 반환. */
tid_t process_fork (const char *name, struct intr_frame *if_ UNUSED){
/* Clone current thread to new thread.*/
return thread_create (name,
PRI_DEFAULT, __do_fork, thread_current ());
};
__do_fork (userprog/process.c)// 부모의 execution context를 복사함
/* A thread function that copies parent's execution context.
* Hint) parent->tf does not hold the userland context of the process.
* That is, you are required to pass second argument of process_fork to
* this function. */
static void
__do_fork (void *aux) {
struct intr_frame if_;
struct thread *parent = (struct thread *) aux;
struct thread *current = thread_current ();
/* TODO: somehow pass the parent_if. (i.e. process_fork()'s if_) */
struct intr_frame *parent_if;
bool succ = true;
/* 1. Read the cpu context to local stack. */
memcpy (&if_, parent_if, sizeof (struct intr_frame));
/* 2. Duplicate PT */
current->pml4 = pml4_create();
if (current->pml4 == NULL)
goto error;
process_activate (current);
#ifdef VM
supplemental_page_table_init (¤t->spt);
if (!supplemental_page_table_copy (¤t->spt, &parent->spt))
goto error;
#else
if (!pml4_for_each (parent->pml4, duplicate_pte, parent))
goto error;
#endif
/* TODO: Your code goes here.
* TODO: Hint) To duplicate the file object, use `file_duplicate`
* TODO: in include/filesys/file.h. Note that parent should not return
* TODO: from the fork() until this function successfully duplicates
* TODO: the resources of parent.*/
process_init ();
/* Finally, switch to the newly created process. */
if (succ)
do_iret (&if_);
error:
thread_exit ();
}
duplicate_pte (userprog/process.c)pml4_for_each에서 이 함수를 호출해 복사를 진행함/*부모 프로세스의 주소 공간을 복사함 *
/* pml4_for_each에 이 함수를 보내면 됨 */
static bool duplicate_pte (uint64_t *pte, void *va, void *aux)
exec[구현 5] exec - 현재 프로세스에서 file에 주어진 프로그램 실행.
file은 실제로 command line이라, 인자도 포함되어 있을 수 있음echo x y z와 같은 식// 사용자
int exec (const char *file) {
return (pid_t) syscall1 (SYS_EXEC, file);
}
-1을 반환process_exec (userprog/process.c)-1을 반환하고, 성공 시 반환을 하지 않음int process_exec (void *f_name);
wait[구현 6] wait- 자식 프로세스의 종료 대기 구현
// 사용자
int wait (pid_t pid) {
return syscall1 (SYS_WAIT, pid);
}
pid의 종료를 기다리고, 종료 코드를 반환한다.pid가 살아 있으면, 종료 전까지 wait -> 종료될 때 pid가 exit에 전달한 종료 코드 반환pid가 exit을 명시적으로 호출하지 않은 경우, 즉 커널에 의해 강제 terminate된 경우, -1 반환pid가 이미 terminate된 프로세스인 경우, 해당 프로세스의 종료 코드를 바로 반환한다.-1을 반환한다.pid가 부모 프로세스의 직계 자식이 아닌 경우pid에 wait이 호출되어, 대기 상태인 경우process_wait (userprog/process.c)int process_wait (tid_t child_tid UNUSED);
exit[구현 7] - 프로세스 terminate
void exit (int status) {
syscall1 (SYS_EXIT, status);
NOT_REACHED ();
}
thread_exit()를 호출해서 정리시키면 되는데, termination message를 출력해야 한다는 점이 종료,thread_exit()은 process_exit()을 출력하는데, 이 과정에서 termination mesㄹsage 출력 및 프로세스의 종료(process_cleanup())이 이루어짐void
process_exit (void) {
// [구현 7] exit 및 exit 메시지 띄우기 구현
struct thread *curr = thread_current ();
printf("%s: exit(%d)\n", curr -> name, curr -> exit_code);
process_cleanup ();
}
process_exit은 인자를 받지 않아, 바로 status 값을 전달할 순 없음// userprog/syscall.c
case SYS_EXIT:
/* Terminate this process. */
// [구현 7] 쓰레드/프로세스 종료
thread_current() -> exit_code = (int)(f -> R.rdi);
thread_exit();
break;
struct thread에 int exit_code 멤버 추가 -> 시스템 핸들러 측에서 exit_code를 status(RDI)로 바꿔 주고, process_exit 실행하면 됨process_exit에서 현재 쓰레드의 exit_code값을 받아와 사용