PintOS PJT2 - User Program

김수환·2024년 11월 16일

PintOS

목록 보기
6/15

process_fork ()

// process.c
// process_fork 생성
tid_t 
process_fork (const char *name, struct intr_frame *if_ UNUSED) {
    /* Clone current thread to new thread.*/

    thread_t *curr = thread_current();

    struct intr_frame *f = (pg_round_up(rrsp()) - sizeof(struct intr_frame));  // 현재 쓰레드의 if_는 페이지 마지막에 붙어있다.
    memcpy(&curr->parent_if, f, sizeof(struct intr_frame));                    // 1. 부모를 찾기 위해서 2. do_fork에 전달해주기 위해서

    /* 현재 스레드를 새 스레드로 복제합니다.*/
    tid_t tid = thread_create(name, PRI_DEFAULT, __do_fork, curr);

    if (tid == TID_ERROR)
        return TID_ERROR;

    thread_t *child = get_child_process(tid);

    sema_down(&child->fork_sema);  // 생성만 해놓고 자식 프로세스가 __do_fork에서 fork_sema를 sema_up 해줄 때까지 대기

    if (child->exit_status == TID_ERROR)
        return TID_ERROR;

    return tid;  // 부모 프로세스의 리턴값 : 생성한 자식 프로세스의 tid
}

duplicate_pte ()

// process.c

#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

__do_fork ()

// process.c
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 (&current->spt);
    if (!supplemental_page_table_copy (&current->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(&current->fork_sema);  // fork 프로세스가 정상적으로 완료됐으므로 현재 fork용 sema unblock

    process_init ();

    /* Finally, switch to the newly created process. */
    if (succ)
        do_iret (&if_);  // 정상 종료 시 자식 Process를 수행하러 감

error:
    sema_up(&current->fork_sema);  // 복제에 실패했으므로 현재 fork용 sema unblock
    exit(TID_ERROR);
}

profile
juniorDev

0개의 댓글