[PintOS] User Program - System Call (Project 2, TIL)
KAIST PintOS ๊ฐ์ ๋ฐ Instruction, ํ์๋ PintOS Slides๋ฅผ ์ฐธ๊ณ ํ๋ฉฐ ํ์ตํ ๋ด์ฉ์ ์ ๋ฆฌํ์์ต๋๋ค.
ํ์ต ๋์ค ์์ฑํ ๋ด์ฉ์ด๋ผ ํ๋ฆฐ ๋ด์ฉ์ด ์์ ์ ์์ต๋๋ค.
๋ค์ด๊ฐ๊ธฐ ์ ์
๋ ์ง์คํฐ ์ญํ ์ ๋ฆฌ
- %rax : system call number ์ ์ฅ โถ๏ธ ์ด์์ฒด์ ๊ฐ ์ด๋ค ์์คํ ํธ์ถ์ ์ํํ ์ง ๊ฒฐ์ ํ๋ ๋ฐ ์ฌ์ฉ๋จ
- %rdi, %rsi, %r10, % r8, %r9 : system call arguments ์ ๋ฌ์ ์ฌ์ฉ(ํธ์ถ์ ๋ฐ๋ผ ์ผ๋ถ๋ง ์ฌ์ฉ๋ ์ ์์ผ๋ฉฐ, ์์๋๋ก ํ ๋น๋จ)
- %rcx : syscall ๋ช ๋ น์ด ์ฌ์ฉ ์ %rcx ๋ ์ง์คํฐ์ ๋ณต๊ท ์ฃผ์๊ฐ ์ ์ฅ๋จ(์ผ๋ฐ์ ์ธ ํจ์ ํธ์ถ ์์๋ ์คํ์ ์ ์ฅ!) โฌ ๏ธ ์ด์์ฒด์ ์์ ์๋ ์ฒ๋ฆฌ
- %r11 : syscall ๋ช ๋ น์ด ์ฌ์ฉ ์ ํ์ฌ process state๋ฅผ ๋ํ๋ด๋ ๋ฆฌํ๋๊ทธ(RFLAGS ๋ ์ง์คํฐ ๊ฐ)๊ฐ ์ ์ฅ๋จ. ์์คํ ํธ์ถ ๊ฐ process state๋ฅผ ๋ณด์กดํ๋ ๋ชฉ์ ์ผ๋ก ์ฌ์ฉ๋จ โฌ ๏ธ ์ด์์ฒด์ ์์ ์๋ ์ฒ๋ฆฌ
Exception ์ฒ๋ฆฌ
Exception(์์ธ)์ด๋ ์ฝ๋ ์คํ ์ค์ ์ด๋ค ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ ๋งํ๋ค. ์ฆ, ์ ์ ํ๋ฆ์ ๋ฐฉํดํ๋ ๊ฒ์ผ๋ก page fault ํน์ 0์ผ๋ก ๋๋๋ ํ์(divide by zero)์ ๊ฐ์ ์ค๋ฅ(๋ด๋ถ ์ธํฐ๋ฝํธ)๋ฅผ ๋ค ์ ์๋ค. ์ด๋ฌํ ์์ธ ์ฒ๋ฆฌ๋ ์ด์ ์ฒด์ ์์ ๋ด๋นํ๋ค. Project1์์๋ ์ธ๋ถ ์ธํฐ๋ฝํธ๋ฅผ ์ด์ ์ฒด์ ๊ฐ ํ์ด๋จธ์ I/O interrupts๋ก ์ฒ๋ฆฌํ๋ ๊ฒ์ ๋ค๋ฃจ์๋ค. Project2์์๋ user program์ด ์์ธ ์ฒ๋ฆฌ ๋ฉ์ปค๋์ฆ์ผ๋ก ์ด์ ์ฒด์ ์ ์๋น์ค(halt, exit, open ๋ฑ)๋ฅผ ์์ฒญํ๋ ๊ฒ์ ๊ตฌํํ๋ฉฐ System Call์ด๋ผ๊ณ ํ๋ค.
x86-64 ์ํคํ ์ฒ์์์ System Call
x86-64 ์ํคํ ์ฒ์์ ์์คํ ํธ์ถ์ ์ํ ๋ช ๋ น์ด์ธ syscall์ด ๋์ ๋์๋ค. ์ด ๋ช ๋ น์ด๋ฅผ ํตํด system call handler๋ฅผ ๋น ๋ฅด๊ฒ ํธ์ถํ ์ ์๋ค. syscall ๋ช ๋ น์ด ์ฌ์ฉ ์ system call number์ ์ถ๊ฐ arguments๋ ๋ ์ง์คํฐ์ ์ค์ ๋์ด ์์ด์ผ ํ๋ค(์ฐธ๊ณ : Linux system call table ์ ๋ฆฌ).
System Call Handling
syscall_handler()๊ฐ ํธ์ถ๋๋ฉด, system call number๋ %rax, ์ฌํ arguments๋ ์์๋๋ก %rdi, %rsi, %rdx, %r10, %r8, %r9 ์์๋๋ก ํ ๋น๋๋ค. x86-64 convention์ ๋ฐ๋ผ ํจ์ ๋ฐํ ๊ฐ์ RAX ๋ ์ง์คํฐ์ ์์น๋๋ฉฐ, return ๊ฐ์ด ์๋ ์์คํ ํธ์ถ์ intr_frame์ rax ๋ฉค๋ฒ(%rax)๋ฅผ ์์ ํ๋ ๊ฒ์ ์ ์ ์๋ค.
๐ก x86-64 ์ํคํ ์ณ์์๋ syscall ๋ช ๋ น์ด๋ฅผ ํตํด system call์ ์ํ syscall_handler๋ฅผ ๋น ๋ฅด๊ฒ ํธ์ถํ ์ ์๊ณ , system call number์ arguments๋ ์ ํด์ง ๋ ์ง์คํฐ์ ์ค์ ๋์ด ์ ๋ฌ๋๋ค. ์์คํ ํธ์ถ ๊ฒฐ๊ณผ๋ RAX ๋ ์ง์คํฐ์ return ๋๋ฉฐ ์ด๋ intr_frame์ %rax ์์ ์ผ๋ก ์ด๋ฃจ์ด์ง๋ค.
์ฝ๋๋ก ์ดํด๋ณด๊ธฐ
main() ํจ์์ msg()์์ vmsg() ๋ด ์ด๋ค system call์ ํธ์ถํ๋ฉด(halt() ๊ฐ์ )
1๏ธโฃ syscall.c์์ halt() ํจ์ ํธ์ถ
void
halt (void) {
syscall0 (SYS_HALT);
NOT_REACHED ();
}
2๏ธโฃ syscall0 (SYS_HALT);
/* Invokes syscall NUMBER, passing no arguments, and returns the
return value as an `int'. */
#define syscall0(NUMBER) ( \
syscall(((uint64_t) NUMBER), 0, 0, 0, 0, 0, 0))
/* 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. */
...
}
syscall0 (SYS_HALT); ๋ syscall0 (3); ์ด๋ผ๋ ๊ฒ์ผ๋ก ๊ฒฐ๊ตญ #define์ ๋ฐ๋ผ syscall(3, 0, 0, 0, 0, 0, 0)์ด ํธ์ถ๋๋ค.
3๏ธโฃ syscall()
__attribute__((always_inline))
static __inline int64_t syscall (uint64_t num_, uint64_t a1_, uint64_t a2_,
uint64_t a3_, uint64_t a4_, uint64_t a5_, uint64_t a6_) {
int64_t ret;
/* system call arguments๋ฅผ binding ํจ */
register uint64_t *num asm ("rax") = (uint64_t *) num_;
register uint64_t *a1 asm ("rdi") = (uint64_t *) a1_;
register uint64_t *a2 asm ("rsi") = (uint64_t *) a2_;
register uint64_t *a3 asm ("rdx") = (uint64_t *) a3_;
register uint64_t *a4 asm ("r10") = (uint64_t *) a4_;
register uint64_t *a5 asm ("r8") = (uint64_t *) a5_;
register uint64_t *a6 asm ("r9") = (uint64_t *) a6_;
__asm __volatile(
"mov %1, %%rax\n"
"mov %2, %%rdi\n"
"mov %3, %%rsi\n"
"mov %4, %%rdx\n"
"mov %5, %%r10\n"
"mov %6, %%r8\n"
"mov %7, %%r9\n"
"syscall\n"
: "=a" (ret)
: "g" (num), "g" (a1), "g" (a2), "g" (a3), "g" (a4), "g" (a5), "g" (a6)
: "cc", "memory");
return ret;
}
- system call arguments ์ค ์ธ์๋ก ์ฌ์ฉ๋ ๊ฐ ์ค ์ฒซ ๋ฒ์งธ ๊ฐ(num_)์ด 'rax'์ binding ๋๋ ๊ฒ์ ํ์ธํ ์ ์์
- ๋๋จธ์ง ์ธ์๋ ์์๋๋ก rdi, rsi, rdx ,..., r9์ binding ๋จ
- __asm __volatile ๋ก ํญ์ ๋ช ์๋ ์์๋๋ก ์๋ํ๋๋ก ํจ
- mov ๋ช ๋ น์ด๋ก ๋ฏธ๋ฆฌ ์ ์๋ ๋ ์ง์คํฐ์ ํ ๋น
- syscall ๋ช ๋ น์ด๋ก ์ค์ system call์ด ์ํ๋จ(์ด์ ์ผ CPU๋ ์ปค๋ ๋ชจ๋ ์ ํ)
- syscall ๊ฒฐ๊ณผ๋ ret ๋ณ์์ ๋ด๊ฒจ caller์ ๋ฐํ๋จ
4๏ธโฃ "syscall\n"
#include "threads/loader.h"
.text
.globl syscall_entry
.type syscall_entry, @function
syscall_entry:
movq %rbx, temp1(%rip)
movq %r12, temp2(%rip) /* callee saved registers */
movq %rsp, %rbx /* Store userland rsp */
movabs $tss, %r12
movq (%r12), %r12
movq 4(%r12), %rsp /* Kernel Stack ์ง์
! */
/* Now we are in the kernel stack */
push $(SEL_UDSEG) /* if->ss */
push %rbx /* if->rsp */
push %r11 /* if->eflags */
push $(SEL_UCSEG) /* if->cs */
push %rcx /* if->rip */
subq $16, %rsp /* skip error_code, vec_no */
push $(SEL_UDSEG) /* if->ds */
push $(SEL_UDSEG) /* if->es */
movq ๋ช ๋ น์ ํตํด %rsp๊ฐ kernel stack์ผ๋ก ์ด๋ํ๋ค. ๋๋์ด user stack์์ kernel stack์ผ๋ก ์ง์ ํ๊ฒ ๋๋ค! ์ด ๋ช ๋ น์ด๋ syscall-entry.S์ ์๋ค. '.S' ํ์ผ์ C์ธ์ด๋ก ์์ฑ๋ ํ๋ก๊ทธ๋จ์์ ์ด์ ๋ธ๋ฆฌ ์ธ์ด ์ฝ๋๋ฅผ ํฌํจํ๋ ํ์ผ์ด๋ค. ์ ์ฒ๋ฆฌ๊ธฐ์์ ์ฒ๋ฆฌ๋๋ฉฐ C์ธ์ด์ ์ ์ฒ๋ฆฌ๊ธฐ ์ง์๋ฌธ(#include, #define ๋ฑ)์ ์ฌ์ฉํ ์ ์๋ค.
- movq 4(%r12), %rsp : r12์์ 4 byte๋ฅผ ๋ํ ์ฃผ์๋ก rsp๋ฅผ ์ด๋์ํด(x86-64 ์ํคํ ์ฒ์ด๋ฏ๋ก 8๋ฐ์ดํธ ๋จ์๊ฐ ๋์ด์ผ ํ ๊ฒ ๊ฐ์๋ฐ.. ์ถํ์ ์ดํด๋ณผ ์์ ์ด๋ค ๐ค)
์ ๊ณผ์ ์ ๊ฑฐ์ณ์ syscall_handler() ํจ์๋ก ๋์ด๊ฐ๊ฒ ๋๋ฉฐ, kernel ๋จ์ ์์ ์ ์ํํ ์ ์๊ฒ ๋๋ค.
System Call Handler
- User stack pointer(rsp)์ system call arguments๊ฐ ์ ํจ ์ฃผ์ ์์ญ(user stack)์ธ์ง ํ์ธํ๋๋ก ๊ตฌํ
- System call number๋ฅผ ์ด์ฉํ์ฌ ํด๋น system call์ ์๋น์ค ๋ฃจํด์ ํธ์ถํ๋๋ก ๊ตฌํ
- User stack interrupt frame์ arguments๋ฅผ kernel stack์ ๋ณต์ฌ๋๋๋ก ๊ตฌํ
- System call return ๊ฐ์ Interrupt frame์ rax์ ์ ์ฅ๋๋๋ก ๊ตฌํ
void check_address (void *addr)
System Call Handler 1๋ฒ์ ํด๋นํ๋ ํจ์๋ฅผ ๊ตฌํํ๋ค.
- ์ฃผ์ ๊ฐ์ด ์ ์ ์์ญ ์ฃผ์ ๊ฐ์ธ์ง ํ์ธ
- ์ ์ ์์ญ์ด ์๋๋ผ๋ฉด process exit
void
check_address (void *addr){
if (addr != NULL && is_user_vaddr(addr)){
return; // ์ ํจํ ์ฃผ์
}
exit (-1); // ์ ํจํ์ง ์์ ์ฃผ์์ผ ๋ ์ฒ๋ฆฌ
}
- ์ฒ์ ์ฝ๋๋ฅผ ์์ฑ ์ exit() ํจ์ ๋์ thread_exit() ํจ์๋ฅผ ์ฌ์ฉํ์์ ์๋ ์์
- pintos๋ฅผ ์์ ํ ์ข ๋ฃํ ๋ ์กฐ๊ฑด์ด ๋ถ๋ ๊ฒ ์๊ธฐ ๋๋ฌธ์ ์ถํ exit () ํจ์๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๊ฒ ๋จ
ํ์๋ Pintos PPT์ ํ์ํ ํจ์๋ก ๋์ ์๋ 'void get_argument()' ํจ์๋ ๊ตฌํํ ํ์๊ฐ ์๋ค(system call arguments๋ฅผ ์ด๋ฏธ kernel์ ๋ณต์ฌํ๊ธฐ ๋๋ฌธ).
# ์ฐธ๊ณ
System Call ๊ฐ๊ฐ์ด ์ ๊ตฌํ๋์๋์ง ํ์ธ('make check'๋ฅผ ํตํ PASS, FAIL ํ์ธ)์ ์ํด์๋ main.c ์ msg๊ฐ ์ถ๋ ฅ๋์ด์ผ ํ๋ค. ์ ๊ทธ๋ฆผ์์ ์ฒ๋ผ output ๋น๊ต๊ฐ ๋๊ธฐ ๋๋ฌธ์ธ๋ฐ, ํ์ฌ ๋ถ๋ชจ process๋ ์์ process๋ฅผ ์์ฑํ๊ณ ๊ณง๋ฐ๋ก ์ข ๋ฃ๋๋ฏ๋ก msg๊ฐ ์ถ๋ ฅ๋ ์ ์๋ค. ๋น์ฐํ๊ฒ๋ FAIL์ด ๊ณ์ ๋์ค๊ฒ ๋๋ค.
halt()
System Call Handler 2, 3, 4๋ฒ์ ํด๋นํ๋ ๊ตฌํ๋ด์ฉ์ด๋ค.
syscall_handler (struct intr_frame *f UNUSED) {
// TODO: Your implementation goes here.
int syscall_number = f->R.rax; // system call number ๊ฐ์ ธ์ค๊ธฐ
switch (syscall_number){
case SYS_HALT:
halt();
break;
...
case SYS_WAIT:
f->R.rax = wait(f->R.rdi); // return ๊ฐ ํ์ฌ rax์ ์ ์ฅ
break;
}
- f->R.rax์ system call number๋ฅผ ๊ฐ์ ธ์ค๊ณ , ์ด number์ ๋ง๋ ํจ์๊ฐ ์คํ๋๋๋ก ์๋น์ค ๋ฃจํด ๊ตฌ์ฑ
- System call return ๊ฐ์ด ์๋ ๊ฒฝ์ฐ ํ์ฌ rax์ ์ ์ฅ
halt() ํจ์๋ pintos๋ผ๋ ์ด์์ฒด์ ๋ฅผ ์ข ๋ฃ์ํค๋ ํจ์์ด๋ค.
void
halt (void){
power_off (); // pintos ์์ ํ ์ข
๋ฃ
}
์ด๋ ๊ฒ ๋งค์ฐ ๊ฐ๋จํ๊ฒ ๊ตฌํ๋๋ค.
exit()
ํ์ฌ ํ๋ก์ธ์ค๋ฅผ ์ข ๋ฃ์ํค๋ system call์ด๋ค.
- Process Termination Message : "ํ๋ก์ธ์ค ์ด๋ฆ: exit(status)"
- ์ ์ ์ข ๋ฃ ์ status๋ 0
void
exit (int status){
struct thread *curr = thread_current();
curr->status = status;
printf("%s(ํ๋ก์ธ์ค ์ด๋ฆ): exit(%d)\n",curr->name, status); // process termination message
thread_exit();
}
check_address()์์ ์ ํจํ์ง ์์ ์ฃผ์์ธ ๊ฒฝ์ฐ exit (-1)์ผ๋ก process๋ฅผ exit ํด์ผํ๋ ์ด์ ๋ฅผ ์ ์ ์๋ค.
create()
์ ๋ ฅ ๋ฐ์ file ์์น์ initial_size ๋งํผ ํ ๋นํ๋ system call์ด๋ค.
bool
create (const char *file, unsigned initial_size){ // ํ์ผ ์์คํ
์์ฑ ์์คํ
์ฝ
check_address(file); // ์ ํจํ ์ฃผ์์ธ ๊ฒฝ์ฐ
return filesys_create(file, initial_size);
}
*๋ง์ฐฌ๊ฐ์ง๋ก create ํธ์ถ ์ syscall_handler์ switch ๋ฌธ์์ ๊ฑธ๋ฆฌ๋๋ก case ์ถ๊ฐ ํ์
Hierarchical Process Structure ์ ๊น์ง ์๋ฃํ์๋ค. ์ดํ์๋ ํ์ฌ ํ๋ก์ธ์ค๊ฐ ์ข ๋ฃ๋๋ ๊ฒ์ semaphore๋ฅผ ์ด์ฉํด ํด๊ฒฐํ์ฌ, Pintos๋ฅผ ๋ฐ์ ์ํจ๋ค.
์๊ฐ์ด ๋ น๋๋ค HURRY UP ๐ซ