운영체제에서 부모-자식 관계를 기반으로 프로세스를 나누는 것은 프로세스 관리와 시스템 효율성을 높이는 데 중요한 설계 원칙입니다. 다음은 그 이유를 설명한 내용입니다.
운영체제에서 새 프로세스를 생성할 때, 기존 프로세스(부모)의 구조를 기반으로 복제하여 새로운 프로세스(자식)를 생성합니다.
부모와 자식은 서로 독립적인 실행 흐름을 가질 수 있지만, 필요할 때 협력할 수도 있습니다.
부모와 자식 관계를 설정하면 리소스 관리가 용이합니다.
부모와 자식 프로세스를 분리하면 작업을 병렬적으로 처리할 수 있습니다.
부모-자식 관계는 프로세스 간 메모리 격리를 유지하면서도 제한적인 데이터 공유를 허용합니다.
fork() 시스템 호출: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 = &parent->parent_if;
bool succ = true;
/* 1. Read the cpu context to local stack. */
memcpy (&if_, parent_if, sizeof (struct intr_frame));
if_.R.rax = 0; // 자식 프로세스의 return값 (0)
/* 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
// Page Table 통째로 복제
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.
* TODO: Hint) 파일 객체를 복제하려면 include/filesys/file.h에서 `file_duplicate`를 사용하세요.
이 함수가 부모의 리소스를 성공적으로 복제할 때까지 부모는 fork()에서 반환되어서는 안 됩니다. */
if (parent->fd_idx >= FDCOUNT_LIMIT)
goto error;
/** #Project 2: Extend File Descriptor - fd 복제 */
struct dict_elem dup_file_dict[DICTLEN];
int dup_idx = 0;
current->fd_idx = parent->fd_idx; // fdt 및 idx 복제
struct file *file;
for (int fd = 0; fd < FDCOUNT_LIMIT; fd++) {
file = parent->fdt[fd];
if (file == NULL)
continue;
bool is_exist = false;
for (int i = 0; i <= dup_idx; i++) {
if (dup_file_dict[i].key == file) {
current->fdt[fd] = file_duplicate(file);
is_exist = true;
break;
}
}
if (is_exist)
continue;
if (file > STDERR)
current->fdt[fd] = file_duplicate(file);
else
current->fdt[fd] = file;
if (dup_idx < DICTLEN) {
dup_file_dict[dup_idx].key = file;
dup_file_dict[dup_idx++].value = current->fdt[fd];
}
/** -------------------------------------------------------------- */
}
sema_up(¤t->fork_sema); // fork 프로세스가 정상적으로 완료됐으므로 현재 fork용 sema unblock
process_init ();
/* Finally, switch to the newly created process. */
if (succ)
do_iret (&if_); // 정상 종료 시 자식 Process를 수행하러 감
error:
sema_up(¤t->fork_sema); // 복제에 실패했으므로 현재 fork용 sema unblock
exit(TID_ERROR);
}
#ifndef VM
/* Duplicate the parent's address space by passing this function to the
* pml4_for_each. This is only for the project 2. */
/* 이 함수를 pml4_for_each에 전달하여 상위 주소 공간을 복제합니다.
* 이는 프로젝트 2에만 해당됩니다. */
static bool
duplicate_pte (uint64_t *pte, void *va, void *aux) {
struct thread *current = thread_current ();
struct thread *parent = (struct thread *) aux;
void *parent_page;
void *newpage;
bool writable;
/* 1. TODO: If the parent_page is kernel page, then return immediately. */
if (is_kernel_vaddr(va))
return true;
/* 2. Resolve VA from the parent's page map level 4. */
parent_page = pml4_get_page (parent->pml4, va);
if (parent_page == NULL)
return false;
/* 3. TODO: Allocate new PAL_USER page for the child and set result to
* TODO: NEWPAGE. */
newpage = palloc_get_page(PAL_ZERO);
if (newpage == NULL)
return false;
/* 4. TODO: Duplicate parent's page to the new page and
* TODO: check whether parent's page is writable or not (set WRITABLE
* TODO: according to the result). */
memcpy(newpage, parent_page, PGSIZE);
writable = is_writable(pte);
/* 5. Add new page to child's page table at address VA with WRITABLE
* permission. */
if (!pml4_set_page (current->pml4, va, newpage, writable)) {
/* 6. TODO: if fail to insert page, do error handling. */
return false;
}
return true;
}
#endif