시스템 콜 핸들러 및 시스템 콜 구현
시스템 콜을 구현하기 전, 64bit system call table을 먼저, 알아볼 필요가 있다.
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 and optionally project 4. */
SYS_MMAP, /* Map a file into memory. */
SYS_MUNMAP, /* Remove a memory mapping. */
/* Project 4 only. */
SYS_CHDIR, /* Change the current directory. */
SYS_MKDIR, /* Create a directory. */
SYS_READDIR, /* Reads a directory entry. */
SYS_ISDIR, /* Tests if a fd represents a directory. */
SYS_INUMBER, /* Returns the inode number for a fd. */
SYS_SYMLINK, /* Returns the inode number for a fd. */
/* Extra for Project 2 */
SYS_DUP2, /* Duplicate the file descriptor */
SYS_MOUNT,
SYS_UMOUNT,
};
쉬운 것(?).. 쉬운 것은 없다... 그나마 간단한 것 부터 해보겠다.
// userprog/syscall.c
void
syscall_handler (struct intr_frame *f UNUSED) {
switch (f->R.rax)
{
case SYS_HALT:
halt();
break;
case SYS_EXIT:
exit(f->R.rdi);
break;
case SYS_CREATE:
f->R.rax = create(f->R.rdi, f->R.rsi);
break;
case SYS_REMOVE:
f->R.rax = remove(f->R.rdi);
break;
default:
exit(-1);
break;
}
}
물론 구현해야할 함수가 더 많지만, 이번 글에서 다룰 것들만 case에 추가해서 구현해보겠다.
struct intr_frame
안에 struct gp_registers R;
로 선언되어있다. 구조체 안의 구조체로 f->R.
형식을 사용한다.
시스템 호출 핸들러 syscall_handler()
가 제어권을 얻으면 시스템 호출 번호는 rax
에 있고 인수는 %rdi
, %rsi
, %rdx
, %r10
, %r8
및 %r9
순서로 전달된다.
// userprog/syscall.c
#include threas/init.h // power_off를 include하기 위해
void halt (void)
{
power_off(); //
}
pintos를 종료시키는 함수, power_off()를 한다.
// userprog/syscall.c
void exit(int status)
{
/* 실행중인 스레드 구조체를 가져옴 */
/* 프로세스 종료 메시지 출력, 출력 양식: “프로세스이름: exit(종료상태)” */
/* 스레드 종료 */
struct thread *curr = thread_current ();
curr -> exit_status = status;
printf("%s: exit(%d)\n", thread_name (), status);
thread_exit ();
}
현재 thread(process)를 종료시키는 명령이다. status가 0인 경우가 정상종료 되는 경우다.
// userprog/syscall.c
bool create(const char *file, unsigned initial_size)
{
/* 파일 이름과 크기에 해당하는 파일 생성 */
/* 파일 생성 성공 시 true 반환, 실패 시 false 반환 */
check_address (file);
return filesys_create(file, initial_size);
}
check_address로 file을 확인한 후, filesys_create()를 통해서 새로운 file을 만든다.
// userprog/syscall.c
bool remove(const char *file)
{
/* 파일 이름에 해당하는 파일을 제거 */
/* 파일 제거 성공 시 true 반환, 실패 시 false 반환 */
check_address (file);
return filesys_remove (file);
}
check_address로 file을 확인한 후, filesys_remove를 한다.
// userprog/syscall.c
void check_address(const uint64_t *uaddr)
{
struct thread *curr = thread_current ();
if (uaddr == NULL || !(is_user_vaddr(uaddr)) || pml4_get_page(curr->pml4, uaddr) == NULL) {
exit(-1);
}
}
주소 값이 유저 영역에서 사용하는 주소 값인지 확인 하는 함수.
Pintos에서는 시스템 콜이 접근할 수 있는 주소를 0x8048000~0xc0000000으로 제한함.
유저 영역을 벗어난 영역일 경우 프로세스 종료(exit(-1))
mmu.c
It's short for Page Map Level 4. A bit of explanation can be found here. Basically it's just the way AMD decided to label page tables.
pml4_create
:pml4_get_page
pml4e_walk
pml4_destroy
yeah ~
pintos -v -k -T 60 -m 20 --fs-disk=10 -p tests/userprog/halt:halt -- -q -f run halt < /dev/null 2> tests/userprog/halt.errors > tests/userprog/halt.output
perl -I../.. ../../tests/userprog/halt.ck tests/userprog/halt tests/userprog/halt.result
pass tests/userprog/halt
pintos -v -k -T 60 -m 20 --fs-disk=10 -p tests/userprog/exit:exit -- -q -f run exit < /dev/null 2> tests/userprog/exit.errors > tests/userprog/exit.output
perl -I../.. ../../tests/userprog/exit.ck tests/userprog/exit tests/userprog/exit.result
pass tests/userprog/exit