[PintOS] User Program - System Call2 (Project 2, TIL)
KAIST PintOS ๊ฐ์ ๋ฐ Instruction, ํ์๋ PintOS Slides๋ฅผ ์ฐธ๊ณ ํ๋ฉฐ ํ์ตํ ๋ด์ฉ์ ์ ๋ฆฌํ์์ต๋๋ค.
ํ์ต ๋์ค ์์ฑํ ๋ด์ฉ์ด๋ผ ํ๋ฆฐ ๋ด์ฉ์ด ์์ ์ ์์ต๋๋ค.
fork() ํจ์ ๊ตฌํํ๋ ๋ฐ์ ์๊ฐ์ ๋๋ฌด ๋ง์ด ์ผ๋ค. ์ ์งค์ฒ๋ผ ๋ฒ๊ทธ๋ฅผ ๊ณ ์น๋ฉด, ๋ค๋ฅธ ๋ฐ์ ๋ฒ๊ทธ๊ฐ ๋ฌ๊ณ , ๊ทธ ๋ฒ๊ทธ๋ฅผ ๊ณ ์น๋ฉด ์ด์ ํ ์คํธ ์ผ์ด์ค๊ฐ ํต๊ณผ๊ฐ ์ ๋๋ ์ผ์ด ์์ฒญ ๋ง์๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฌดํ ๋ฒ๊ทธ์ ๊ตด๋ ๋์ 'ALL PASSED' ๋ฌธ๊ตฌ๋ฅผ ๋ดค์ ๋์ ๊ฐ๋์ ์์ง๋ ์์ง ๋ชป ํ๊ฒ ๋ค(์ง๋ฆฟํด).
ํ๋ก์ธ์ค ๊ณ์ธต ๊ตฌ์กฐ ๊ฐ์
Process์ ์ ๋ณด์ ๋ถ๋ชจ์ ์์ field๋ฅผ ์ถ๊ฐํ์ฌ ์ด๋ฅผ ๊ด๋ฆฌํ๋ ํจ์๋ฅผ ์ ์ํ๋ค. ์ด๋, project1์์ ์ฌ์ฉ๋์๋ semaphore ๊ฐ๋ ์ด ์ฌ์ฉ๋๋ค. Process ๊ด๋ จ system call์๋ fork(), exit(), exec(), wait()์ด ์๋ค. ํ๋ํ๋ ๋ง๋ค์ด๊ฐ๋ฉฐ ์ ์ํด์ผ ํ ์ , ์์์ผ ํ ์ ์ ์ ๋ฆฌํ๋ค.
exec
์ด ํจ์๋ ํ์ฌ process๋ฅผ ์ ๋ ฅ ๋ฐ์ ์คํ ํ์ผ๋ก ๋ณ๊ฒฝํ๋ ํจ์์ด๋ค. ์๋ก์ด process๋ฅผ ์์ฑํ๋ ๊ฒ์ ์๋๋ฉฐ, process๊ฐ ํ์ฌ ๊ฐ๊ณ ์๋ context๋ฅผ ์คํํ๊ณ ์ ํ๋ ํ์ผ๋ก ๋ณ๊ฒฝํ๋ ๋๋์ด๋ค. fork์ ์์ฃผ ํจ๊ป ์ฐ์ด๋ ์์คํ ์ฝ์ด๋ค.
๊ตฌํ
- ์ธ์๋ก ๋ฐ์ cmd_line์ ์ฃผ์ ์ ํจ์ฑ ํ์ธ(check_address())
- ์ธ์์ ์ ์ฅ๋ ์์น๋ '์ ์ ์์ญ'์ด์ด์ผ๋ง ํจ
- palloc_get_page() ํจ์์ strlcpy() ํจ์๋ฅผ ์ด์ฉํด cmd_line_copy ๋ณ์๋ก ๋ณต์ฌ
- cmd_line_copy ๋ณ์ ์์ฑ ์ด์ : caller vs load์ race condition ๋ฐฉ์งํ๊ธฐ ์ํด
- ์ดํ process_exec() ํจ์๋ฅผ ํธ์ถํ์ฌ cmd_line์ ํ๋ก์ธ์ค๋ฅผ load()
- ์ด๋ stack์ ์์
- ์์ธ ์ฒ๋ฆฌ
- ๋ฉ๋ชจ๋ฆฌ ํ ๋น ์คํจ โก๏ธ exit(-1)
- process_exec() return -1 โก๏ธ exit(-1)
int exec(const char *cmd_line)
{
check_address(cmd_line);
char *cmd_line_copy;
cmd_line_copy = palloc_get_page(0);
if (cmd_line_copy == NULL) // ์์ธ ์ฒ๋ฆฌ
exit(-1);
strlcpy(cmd_line_copy, cmd_line, PGSIZE);
if (process_exec(cmd_line_copy) == -1) // ์์ธ ์ฒ๋ฆฌ
exit(-1);
}
fork
Pintos Instruction์์....
%RBX, %RSP, %RBP, %R12 - %R15๋ฅผ ์ ์ธํ ๋๋จธ์ง ๋ ์ง์คํฐ๊ฐ์ ๋ณต์ ํ ํ์๊ฐ ์๋ค. ๋ฐ๋์ ์์ ํ๋ก์ธ์ค์ process id๋ฅผ ๋ฐํํด๋ผ. ๊ทธ๋ ์ง ์์ผ๋ฉด ์ ํจํ pid๊ฐ ์๋๋ค. ์์ ํ๋ก์ธ์ค์์, ๋ฆฌํด๊ฐ์ ๋ฐ๋์ 0์ด์ด์ผ ํ๋ค. ์์ ํ๋ก์ธ์ค๋ ๋ฐ๋์ ๋ถ๋ชจ ํ๋ก์ธ์ค๋ก๋ถํฐ ํ์ผ ๋์คํฌ๋ฆฝํฐ, ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ ๋ฑ์ ํฌํจํ ์์์ ๋ณต์ฌํด์์ผ ํ๋ค.
๋ถ๋ชจ ํ๋ก์ธ์ค๋ ์์ ํ๋ก์ธ์ค๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๋ณต์ ๋ ๊ฒ์ ์๊ณ ๋์ fork๋ก๋ถํฐ ๋ฆฌํดํด์ผ ํ๋ค. ์ฆ, ๋ง์ฝ ์์ ํ๋ก์ธ์ค๊ฐ ์์์ ๋ณต์ ํ๋๋ฐ ์คํจํ๋ฉด ๋ถ๋ชจ ํ๋ก์ธ์ค์ fork() ํธ์ถ์ ๋ฐ๋์ TID_ERROR๋ฅผ ๋ฐํํด์ผ ํ๋ค. ์ด ํ ํ๋ฆฟ์ threads/mmu.c ๋ด์ ์๋ pml4_for_each() ๋ฅผ ์ฌ์ฉํด ์ ์ฒด user memory ๊ณต๊ฐ์ ๋ณต์ ํ๋๋ฐ, ๋์ํ๋ ํ์ด์ง ํ ์ด๋ธ ๊ตฌ์กฐ์ฒด๋ฅผ ํฌํจํ๋ค. ํ์ง๋ง ๋น ๋ถ๋ถ(pte_for_each_func์์)์ ์ฑ์์ผ ํ๋ค.
fork ์์คํ ์ฝ์ ํ์ฌ process์ ์์ ํ ๋์ผํ process๋ฅผ ํ๋ ๋ ๋ง๋๋ ์์คํ ์ฝ์ด๋ค. ๋์ผํ๊ฒ ๋ง๋ ๋ค๋ ์๋ฏธ๋ฅผ ์ฝ๊ฒ ์๊ฐํ๋ฉด, ๋ถ๋ชจ process์ context๋ฅผ ์์์ด ์๊ณ ์๊ธฐ ๋๋ฌธ์ ์์ process๋ main() ํจ์์์๋ถํฐ ๋ค์ ์์ํด์ผ ํ๋ ๊ฒ์ด ์๋๋ผ ๋ถ๋ชจ process๊ฐ ๋ง์ง๋ง์ผ๋ก ์คํํ ์ฝ๋๋ถํฐ ์คํํ ์ ์๋ค๋ ๊ฒ์ด๋ค. Pintos์์๋ ํ๋ ๋ ๋ง๋ process๋ฅผ '์์ process'๋ผ๊ณ ํ๋ค. ์ด๋ ๊ฒ ์์ ํ ๋์ผํ process๋ฅผ ์ด๋ป๊ฒ ๊ตฌ๋ถํ ์ ์์๊น? fork ํจ์์ return ๊ฐ์ผ๋ก pintos์์๋ ๊ตฌ๋ถํ๋ค.
process_fork() ํจ์์ return value
pid ๊ฐ์ ๋ด๊ธฐ๊ฑฐ๋ TID_ERROR ๊ฐ์ด return ๋๋๋ฐ, ์ด ๊ฐ์ ์๋ ๋ฒ์์ ํด๋นํ ๋ ๋ค์๊ณผ ๊ฐ์ ๋ป์ ๊ฐ๋๋ค.
1. ์์ : fork๊ฐ ์ ์์ ์ผ๋ก ์๋ฃ๋์ง ์์ ์์ process๊ฐ ๋ง๋ค์ด์ง์ง ์์์์ ์๋ฏธ
2. 0 : ์๋ก ๋ง๋ค์ด์ง ์์ process๋ 0์ return ๋ฐ์์ผ๋ก์จ ์์ process์์ ํ์ธ
3. ์์ : ์์ process๊ฐ ์ ์์ ์ผ๋ก ๋ง๋ค์ด์ก์ ๋, ๋ถ๋ชจ process๊ฐ pid ๊ฐ์ return ๋ฐ์
output
linux> ./fork
parent: x=0
child : x=2
๋ง์ฝ ๊ฐ๊ฐ์ process์์ ์ฝ๋๊ฐ ์คํ๋๋ฉด, fork() ํจ์๋ฅผ ๋ง๋ฌ์ ๋, ์์ process์์๋ 0์ด return ๋๊ณ , ์ดํ ๋ถ๋ชจ process์์๋ pid๊ฐ return ๋๋ฉฐ, ๊ฐ๊ฐ์ ์ฝ๋๊ฐ ์คํ๋๋ค.
๋ถ๋ชจ process์ context๋ฅผ ๋ณต์ฌํด์จ๋ค๋ ๊ฒ์ ๋ฌด์์ผ๊น? fork๋ผ๋ ์์คํ ์ฝ์ด ํธ์ถ๋๋ฉด user ํ๋ก๊ทธ๋จ์์ syscall_handler() ๋ก ์ง์ ํ๊ฒ ๋๋ค. thread ๊ตฌ์กฐ์ฒด ๋ด์ ์๋ interrupt frame(if)์ context switching์ด ๋ฐ์ํ ๋๋ง๋ค ๊ฐ์ด ๋ฐ๋๊ฒ ๋๋ค. ์ฆ, syscall_hanlder()๋ก user context(a.k.a ๋ถ๋ชจ process)์ interrupt frame์ ์ ๋ฌํ๋ค. thread_create() ํจ์๋ฅผ ํตํด ์์ process๊ฐ ์์ฑ๋ ๋ ๋ถ๋ชจ context๋ฅผ ๋ณต์ฌํด์ฌ ์ ์๋ ๊ฒ์ด๋ค.
๊ทธ๋ฐ๋ฐ, fork ๊ณผ์ ์ค์ context switching์ผ๋ก ์ธํด ๋ถ๋ชจ process์ interrupt frame์ด ๋ฐ๋๊ฒ ๋๋ฉด ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ์์ process๋ฅผ ์์ฑํ ๋ ํ์ฌ thread์ interrupt frame์ ๊ฐ์ ธ์ค๋๋ก ๊ตฌํํ๋ค๋ฉด context switching์ผ๋ก ์ธํด ๊ฐ์ ธ์์ผ ํ๋ ์์ ์ ๋ถ๋ชจ process interrupt frame์ ๊ฐ์ ธ์ค์ง ๋ชปํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค(๋ฌธ๋งฅ์ด ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ ์์ process ์ฝ๋๋ ์๋ฑํ ๊ณณ์์๋ถํฐ ์คํ๋ ์ ์๋ค!).
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ interrupt frame์ ์ ์ฅํ๋ ์ถ๊ฐ ๋ณ์๋ฅผ ๋ง๋ค์ด syscall_hanlder์ ์ง์ ํ ๋๋ง๋ค user context์ interrupt frame์ ์ ์ฅํด๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋ถ๋ชจ process์ ๋ฌธ๋งฅ์ ๊ฐ์ ธ์์ผ ํ ๋, ์ ์ฅ๋ interrupt frame์ ๊ฐ์ ธ์ค๊ธฐ์ ํ์ฌ thread์ interrupt frame์ ๊ฐ์ ธ์ค๋ ๊ฒ๋ณด๋ค ์ ํํ๋ค. ์ด ๊ณผ์ ์ __do_fork() ํจ์์์ ํ์ธํ ์ ์๋ค.
process_fork() ๊ตฌํ
์ ๋ฆฌํ๋ฉด 1. ๋ถ๋ชจ process๋ ํ์ฌ ์คํ ์ค์ธ user process์ด๊ณ 2. interrupt frame์ system call ํธ์ถ๋ก ์ธํด ๋ฐ๋์๋ค ๋ํ, _if.rsp๋ kernel stack์ ์์น!
์์์ ์ธ๊ธํ ๊ฒ ์ฒ๋ผ thread ๊ตฌ์กฐ์ฒด์ parent_if field๋ฅผ ์ถ๊ฐํ๊ณ , ์์ process๋ฅผ ๋ด์์ค ๋ฆฌ์คํธ field๋ฅผ ์ถ๊ฐํ๋ค.
struct thread {
...
struct list child_list; /* ์์ ํ๋ก์ธ์ค๋ฅผ ๋ด์์ค ๋ฆฌ์คํธ*/
struct list_elem child_elem; /* child_list์ ๋ด์์ค elem */
...
/* Owned by thread.c. */
struct intr_frame tf; /* Information for switching */
unsigned magic; /* Detects stack overflow. */
struct intr_frame parent_if; /* ์ ์ ์คํ์ ์ ๋ณด๋ฅผ ์ธํฐ๋ฝํธ ํ๋ ์ ์์ ๋ฃ์ด์, ์ปค๋ ์คํ์ผ๋ก ๋๊ฒจ์ฃผ๊ธฐ ์ํจ */ //๋ณ๊ฒฝ์ฌํญ - ์์์๊ฒ ๋๊ฒจ์ค intr_frame
};
๋ถ๋ชจ process๊ฐ thread_create() ํจ์๋ฅผ ํตํด ์์ process๋ฅผ ์์ฑํ ํ, ์์ process๊ฐ ์ ์์ ์ผ๋ก ๋ก๋๋ ๋๊น์ง ๊ธฐ๋ค๋ ค์ผ ํ๋ค. ์ด๋, semaphore๊ฐ ์ฌ์ฉ๋๋ค. ์์ process์ fork_sema์ ๋ํด sema_down()์ ํธ์ถํ์ฌ, ์์ process๊ฐ ์ ์ ์์ฑ๋ ๋๊น์ง ๋๊ธฐํ๋ค. ์ ์ ์์ฑ ํ ์์ process๋ sema_up()์ ํธ์ถํด ์์ ์ semaphore๋ฅผ ํด์ ํจ์ผ๋ก์จ ๋ถ๋ชจ process๊ฐ continue๋๋๋ก ํ๋ค.
tid_t process_fork(const char *name, struct intr_frame *if_ UNUSED) {
/* Clone current thread to new thread.*/
struct thread *curr = thread_current();
memcpy(&curr->parent_if, if_, sizeof(struct intr_frame)); // ๋ถ๋ชจ process if_ ๋ณต์ฌ -> ์์ process ์ฌ์ฉ ์์
tid_t pid = thread_create(name, PRI_DEFAULT, __do_fork, curr); // __do_fork ํจ์๋ฅผ ์์ process ์์ฑ ์ ์คํํจ์๋ก ์ง์
if (pid == TID_ERROR)
return TID_ERROR;
struct thread *child = get_child(pid); // ์์ฑ๋ ์์ process์ thread ๊ตฌ์กฐ์ฒด ํ ๋น
sema_down(&child->fork_sema); // ์์ process ๋ก๋ฉ ์๋ฃ ์ ๊น์ง ๋ถ๋ชจ process ๋๊ธฐ
if (child->exit_status == -1)
return TID_ERROR;
return pid;
}
__do_fork()
Pintos ๋ด hint
/* 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์ ์์ฑ ์ดํ ๋ถ๋ชจ process๊ฐ ๊ฐ์ง ๊ฒ๊ณผ ๋์ผํ FDT(File Descriptor Table)๊ฐ ์์ฑ๋์ด์ผ ํ๋ค. ์ฃผ์ด์ง Hint์์ ์ ์ ์๋ ๊ฒ๊ณผ ๊ฐ์ด file_duplicate() ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด file์ ์ฝ๊ฒ ๋ณต์ฌํ ์ ์๋ค.
static void __do_fork(void *aux){
...
/* FDT ๋ณต์ฌ */
for (int i = 0; i < FDT_COUNT_LIMIT; i++) {
struct file *file = parent->fdt[i];
if (file == NULL)
continue;
if (file > 2)
file = file_duplicate(file);
current->fdt[i] = file;
}
current->next_fd = parent->next_fd;
sema_up(¤t->fork_sema); // ๋ก๋๊ฐ ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๊ณ ์๋ ๋ถ๋ชจ ๋๊ธฐ ํด์
process_init();
...
}
๋ง์ง๋ง์ผ๋ก fork_sema๋ฅผ sema_up()ํ์ฌ ๋ถ๋ชจ process์ ๋๊ธฐ๊ฐ ๋๋๋๋ก ํ๋ค.
wait
๋ถ๋ชจ ํ๋ก์ธ์ค๋ ์์ ํ๋ก์ธ์ค๊ฐ ์ข ๋ฃ๋ ๋๊น์ง ๋ฉ์ถฐ์์ด์ผ ํ๋ค. ๋ถ๋ชจ ํ๋ก์ธ์ค๊ฐ wait(์์_ํ๋ก์ธ์ค_id)๋ฅผ ํธ์ถํ๋ฉด wait ํจ์์ ๋ฆฌํด ๊ฐ์ผ๋ก ์์ ํ๋ก์ธ์ค์ exit status๋ฅผ ๋ฐ๊ฒ ๋๋ค(๋น์ ์ ์ข ๋ฃ ์ exit status๋ -1). ๋ถ๋ชจ ํ๋ก์ธ์ค๋ ์ด๋ฅผ ํตํด ์์ ํ๋ก์ธ์ค์ ์ข ๋ฃ ์ฌ๋ถ๋ฅผ ์ ์ ์๊ฒ ๋๋ค. ์ถํ ์์ ํ๋ก์ธ์ค ์ข ๋ฃ ์ ์์ ์์ ๋ฐํ ํ ๋ ์ ํธ๋ก ์ฌ์ฉ๋๋ค.
ํ์ฌ๋ while(), for ๋ฑ์ผ๋ก ๋๊ธฐ ์ํ๋ฅผ ๋ชจ์ฌ ํด๋์์ํ ๋ฐ ์ด๋ ๋น์ฐํ ํ์ ์์ด์ง๋ค. wait() ํจ์๋ ์ฌ์ฉ์์๊ฒ ์ ๊ณต๋๋ ์ธํฐํ์ด์ค(API)์ญํ ์ ํ๋ฉฐ, process_wait() ํจ์๋ ์ค์ ๋ก ์์ ํ๋ก์ธ์ค๊ฐ ์ข ๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ๋ก์ง์ ๊ตฌํํ๋ค. ์ฆ, ๋ ํจ์๋ฅผ ๊ตฌ๋ถ(์บก์ํ)ํ์ฌ์ผ ํ๋ค.
๊ตฌํ
wait ๊ตฌํ
- wait ํจ์์ ์ธ์๋ก ๋ฐ์ pid๋ฅผ process_wait() ํจ์๋ก ๋๊น
process_wait()
- ํ์ฌ ์คํ ์ค์ธ thread๋ฅผ ๊ฐ์ ธ์ด
- ์ธ์๋ก ๋ฐ์ pid์ ํด๋นํ๋ thread๋ฅผ ๊ฐ์ ธ์ด
- ์์ธ ์ฒ๋ฆฌ : ํด๋นํ๋ ์์ ํ๋ก์ธ์ค๊ฐ ์๋ ๊ฒฝ์ฐ -1 ๋ฐํ
- wait_sema๋ฅผ ์ฌ์ฉํด ์์ process์ ์ข
๋ฃ๋ฅผ ๊ธฐ๋ค๋ฆผ
- ์ด ๋์ ํ์ฌ thread(๋ถ๋ชจ)๋ ๋๊ธฐ ์ํ๊ฐ ๋จ
- ์์ process๊ฐ ์ข ๋ฃ๋ ๊ฒฝ์ฐ ๋ถ๋ชจ process์ ์์ ๋ชฉ๋ก์์ ์ ๊ฑฐ
- free_sema๋ฅผ ์ฌ์ฉํด ์์ ํ๋ก์ธ์ค์ ์์์ ํด์ ํ ์ ์๋๋ก semaphore๋ก ์ ํธ๋ฅผ ๋ณด๋
- ์์ ํ๋ก์ธ์ค์ exit_status ๋ฐํ
์ด๋ ๊ฒ process_wait() ํจ์์์ ํ๋ก์ธ์ค ๊ฐ ๋๊ธฐํ๊ฐ ๊ฐ๋ฅํ๋ค. ํ์๋ ํํ ์ค PPT ์๋ฃ์๋ semaphore๋ฅผ ํตํด ๋๊ธฐํ๋ฅผ ๊ตฌํํ๋ผ๊ณ ๋์ด ์์ด์ ์ด๋ ๊ฒ ํ๋๋ฐ, ์กฐ๊ฑด๋ณ์๋ก๋ ๊ฐ๋ฅํ ๊ฒ ๊ฐ๋ค. ๋ค๋ง, ์ ํธ๊ฐ ์์ค๋๋ค๋์ง, thread๊ฐ ์กฐ๊ฑด์ ๊ฒ์ฌํ๊ณ ์กฐ๊ฑด ๋ณ์์์ ๋๊ธฐํ๋ ค๊ณ ํ ๋ ๋ค๋ฅธ thread๊ฐ ์คํ๋๋ค๋ ์ง ํ๋ race condition ์ํฉ์ ์ ์ ํ๊ฒ ์ฒ๋ฆฌํด์ค์ผ ํ๊ธฐ์ ๋์ด๋๊ฐ ํจ์ฌ ๋์ ๊ฒ์ด๋ค.
int wait(int pid)
{
return process_wait(pid);
}
int process_wait(tid_t child_tid UNUSED) {
struct thread *cur = thread_current();
struct thread *child = get_child_process(child_tid);
if (child == NULL)
return -1;
sema_down(&child->wait_sema);
int exit_status = child->exit_status;
list_remove(&child->child_elem);
sema_up(&child->free_sema);
return exit_status;
}
Exit
์ด ์์คํ ์ฝ์ ํ์ฌ ํ๋ก์ธ์ค์ exit status๋ฅผ ํ์ฌ thread์ exit_stauts์ ํ ๋น ํ exit๋ process์ ํ ๋น๋์ด ์๋ ์์์ ๋ชจ๋ ํด์ ํด์ค๋ค. thread_exit() ํจ์๊ฐ ๊ตฌํ๋์ด ์๊ธฐ์ ์ฝ๊ฒ ๋ง๋ค ์ ์๋ค.
void exit(int status) {
struct thread *curr = thread_current();
curr->exit_status = status;
printf("%s: exit(%d)\n", curr->name, status); // process termination message
thread_exit();
}