Pintos project 1~3까지 진행이 됐다.
project 2 - system call을 할땐 system call이 제일 힘든건줄 알았는데 project 3- vm을 하니 vm이 가장 힘든거라는걸 알게 됐다.
project 4 는 더 힘들 것인가...
일단 결과를 먼저 공유한다.

VM을 시작하기 전에 syn-read 혹은 multi-oom 등을 수백번 돌려보면서 lock이 정상적으로 동작하는지 꼭 확인해보길 바란다.
나는 중간에 lock이 정상동작하지 않아 fail case 가 있었다. lock을 수정한 후에 pass하였으니 꼭 lock을 수정해야한다.
나의 경우
fork_sema가 __do_fork 가 끝난 후에도 되고, process_exit에서도 진행이 되어서 sema_up 이 두번이 중복적으로 일어나 fail이 된 경우였다. (문제는 당시에 발견되지 않고 VM을 한창 진행중에 발견됐다는 것이다.)
__do_fork 안에 있는 // sema_up (¤t->fork_sema); 로 주석처리 하였더니 정상 동작하는 것을 확인하였다.
혹시 모르니 __do_fork 함수를 아래 다시 올려놓겠다.
코드를 gitbook의 순서에 맞춰 올리도록 하겠다.
// 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 (¤t->spt);
if (!supplemental_page_table_copy (¤t->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 (¤t->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(¤t->fork_sema);
free (fork_argv);
exit_handler (TID_ERROR);
}
앞선 project에서 사용했던 check_add를 수정하고 buffer를 check하는 것을 추가해줌
check_add,check_buff 를 추가함
struct page *check_add (void *add) {
if (is_kernel_vaddr (add) || add == NULL || spt_find_page(&thread_current()->spt,add) == NULL || !(&thread_current()->pml4))
{
exit_handler(-1);
}
return spt_find_page(&thread_current()->spt, add);
}
void
check_buff (void * buffer, unsigned size, void *rsp, bool to_write){
for(int i=0; i<size; i++){
struct page *page = check_add(buffer + i);
if(page ==NULL)
exit_handler(-1);
if(to_write == true && page->writable == false)
exit_handler(-1);
}
}
SYS_READ와 SYS_WRITE는 buffer check가 필요함 - buffer 단위로 읽고 쓰고 할거니
그래서 해당 systemcall을 받으면 점검을 해줌
case SYS_READ:
check_buff(f->R.rsi, f->R.rdx, f->rsp, 1);
f->R.rax = read_handler (a1, a2, a3);
break;
case SYS_WRITE:
check_buff(f->R.rsi, f->R.rdx, f->rsp, 0);
f->R.rax = write_handler (a1, a2, a3);
break;