08_week_C_pintos_systemcall-4

신치우·2022년 11월 28일
0

data_structure_and_Pintos

목록 보기
24/36

마지막 구현인 fork이다
systemcall 구현중 가장 복잡한 부분이다
fork를 구현할때 가장 중요한 부분은

  1. 부모의 모든 process를 복사해서 저장한다
  2. 부모의 process에서 열려있던 file에 대한 정보도 전부 상속 받는다

관련된 함수
(process_fork, __do_fork, duplicate_pte)

//syscall.c
tid_t
fork_handler (const char *thread_name, struct intr_frame *f) {
  return process_fork (thread_name, f);
}

아래 함수에서는 임시적으로 malloc을 이용하여 공간을 할당해준다
parent 의 정보를 임시적으로 저장하기 위한 공간

// process.c
struct argv {   // process fork를 할때 현재 Thread의 정보와 interrupt frame의
                // 정보를 저장하기 위한 공간
  struct thread *fork_thread;
  struct intr_frame *fork_if;
};
//process.c
tid_t
process_fork (const char *name, struct intr_frame *if_) {
  /* Clone current thread to new thread.*/
  struct thread *parent = thread_current ();

  struct argv *fork_argv = (struct argv *) malloc (sizeof (struct argv)); // thread와 intr_fram을 기억하기 위한 공간을 만들어줌
  fork_argv->fork_if = if_; // 각 구조안에 현재 정보를 넣어줌
  fork_argv->fork_thread = thread_current ();

  tid_t tid = thread_create (name, PRI_DEFAULT, __do_fork, fork_argv); // thread가 create되면서 fork에 성공했다면 tid를 return 하지만 실패했다면 TID_ERROR를 return 함
  if (tid == TID_ERROR)
    return TID_ERROR;

  struct thread *child = get_child (tid); // child list에서 tid가 일치하는 child를 찾아주고 (thread가 create 되면서 추가가 됐을거니깐)

  sema_down (&child->fork_sema); // 해당 child의 fork를 sema_down 함 --> fork가 완료되기전에 process가 종료되지 않게 방지함
  return tid;
}

__do_fork의 역할은
부모 process의 모든 정보를 current process로 복사해준다

//process.c
static void
__do_fork (void *aux) {
  struct intr_frame if_;
  // struct thread *parent = (struct thread *) aux;

  struct argv *fork_argv = (struct argv *) aux;
  struct thread *parent = fork_argv->fork_thread;
  struct thread *current = thread_current ();
  /* TODO: somehow pass the parent_if. (i.e. process_fork()'s if_) */
  struct intr_frame *parent_if;
  parent_if = fork_argv->fork_if;
  bool succ = true;

  /* 1. Read the cpu context to local stack. */
  memcpy (&if_, parent_if, sizeof (struct intr_frame));
  if_.R.rax = 0; // 자식 process는 항상 0을 return 하기 때문에 if_R.rax = 0을 넣어줌 --> git book 내용과 함께 작성

  /* 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
  if (!pml4_for_each (parent->pml4, duplicate_pte, fork_argv)) // pte_entry table에 duplicate_pte 함수를 적용함 --> 실패하면 error로 가고 아니면 진행
    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.*/
  if (parent->fd_idx >= FD_COUNT_LIMT) // 만일 할당한 fd_idx가 FD_COUNT_LIMIT 보다 크다면 error로 감 (공간 초과)
    goto error;


  // 부모 process가 가지고 있는 열린 파일의 정보는 자식 process에게 상속됨
  current->fd_table[0] = parent->fd_table[0]; // 처음 0과 1은 정해져있으니 그 둘을 그대로 복사해옴
  current->fd_table[1] = parent->fd_table[1];

  for (int i = 2; i < FD_COUNT_LIMT; i++) { // 남은 부분들도 복사
    struct file *temp_file = parent->fd_table[i];
    if (temp_file == NULL)
      continue;
    current->fd_table[i] = file_duplicate (temp_file); // temp file의 속성을 포함한 개체를 복사하고 File과 동일한 inode에 대한 새 파일을 반환 --> 실패하면 NULL을 반환
  }

  current->fd_idx = parent->fd_idx; // 위 정보를 상속 받기 때문에 열린 파일의 개수도 같이 넘겨줌
  sema_up (&current->fork_sema); // 그리고 sema_up으로 fork_sema는 풀어줌

  process_init (); // process를 초기화

  /* Finally, switch to the newly created process. */
  free (fork_argv); // fork_argv의 역할은 끝났으니깐 free 해줌

  /*
  do_iret을 통해서 interrupt frame에 있는 정보를 register로 보내줌
  if_.rip 의 시작주소를 따로 저장해주는 작업이 없는 이유는 (process_exe의 load에서 하는 작업)
  duplicate_pte 와 file_duplicate 등을 통해서 interrupt frame의 모든 정보를 자식 process에 복사를 해주었기 때문임
  그래서 그냥 register에 올려주기만 하면됨
  */
  if (succ)
    do_iret (&if_);

error:
  current->exit_status = TID_ERROR;
  sema_up (&current->fork_sema);
  free (fork_argv);
  exit_handler (TID_ERROR);
}

duplicate 의 주 역할
부모의 주소 공간을 복사(속성 및 메모리를 복사한다)
읽기형인지 읽기형 && 쓰기형인지 속성을 복사해서 자식 process에 적용해준다

/* Duplicate the parent's address space by passing this function to the
 * pml4_for_each. This is only for the project 2. */
static bool
duplicate_pte (uint64_t *pte, void *va, void *aux) {
  struct thread *current = thread_current ();
  struct argv *argv_fork = (struct argv *) aux;
  // struct thread *parent = (struct thread *) aux;
  struct thread *parent = argv_fork->fork_thread;
  void *parent_page;
  void *newpage;
  bool writable;

  /* 1. TODO: If the parent_page is kernel page, then return immediately. */
  /*만약 va가 kernel address 라면 --> true를 return */
  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_USER);
  if (newpage == NULL) {
    printf ("duplicate_pte page fault\n");
    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). */
  /*input 으로 들어온 parent -> pml4 와 is_writable 함수를 이용하여 쓰기가 가능한지 아닌지 판단한다*/
  memcpy (newpage, parent_page, PGSIZE);
  writable = is_writable (pte);

  /* 5. Add new page to child's page table at address VA with WRITABLE
   *    permission. */
  /* fork 하려는 것이 쓰기가 가능하면 read/write가 다되는걸 return 하고
    read만 가능하면 read 전용으로 return함

    위 두가지 모두 true를 return 함
    하지만 메모리를 할당하지 못했다면 false를 return 함
   */
  if (!pml4_set_page (current->pml4, va, newpage, writable)) {
    /* 6. TODO: if fail to insert page, do error handling. */
    printf (" duplicate_pte pmlt_set_page fault \n");
    return false;
  }
  return true;
}
profile
하루에 집중을

0개의 댓글

관련 채용 정보