[PintOS] Project 3 : Virtual Memory - Swap In/Out

chohk10·2023년 5월 30일
0

PintOS

목록 보기
7/8

5. Swap In / Out

디버깅

Sector = 512bytes -> 8개의 섹터가 하나의 페이지를 이룬다.

pass tests/vm/page-linear
pass tests/vm/page-parallel
pass tests/vm/page-merge-seq
pass tests/vm/page-merge-par
pass tests/vm/page-merge-stk
FAIL tests/vm/page-merge-mm
pass tests/vm/page-shuffle
pass tests/vm/mmap-read
pass tests/vm/mmap-close
pass tests/vm/mmap-unmap
pass tests/vm/mmap-overlap
pass tests/vm/mmap-twice
pass tests/vm/mmap-write
pass tests/vm/mmap-ro
pass tests/vm/mmap-exit
pass tests/vm/mmap-shuffle
pass tests/vm/mmap-bad-fd
pass tests/vm/mmap-clean
FAIL tests/vm/mmap-inherit
pass tests/vm/mmap-misalign
pass tests/vm/mmap-null
pass tests/vm/mmap-over-code
pass tests/vm/mmap-over-data
pass tests/vm/mmap-over-stk
pass tests/vm/mmap-remove
pass tests/vm/mmap-zero
pass tests/vm/mmap-bad-fd2
pass tests/vm/mmap-bad-fd3
pass tests/vm/mmap-zero-len
pass tests/vm/mmap-off
pass tests/vm/mmap-bad-off
pass tests/vm/mmap-kernel
pass tests/vm/lazy-file
pass tests/vm/lazy-anon
FAIL tests/vm/swap-file
FAIL tests/vm/swap-anon
FAIL tests/vm/swap-iter
pass tests/vm/swap-fork
FAIL tests/vm/cow/cow-simple
6 of 141 tests failed.

Swap 구현은 우선 했는데, 아직 빠진 부분이 있는지 실패하는 부분들이 있다
그리고 supplemental page table copy에서의 허점을 발견해서 고쳐야 한다.
👉🏻 처음에는 naive 하게 uninit은 uninit 상태로, 그 외 모든 페이지는 그냥 복제를 해주도록 코드를 짰는데, 그러면 page의 멤버들이 정상적으로 값이 세팅되지 않을 것이라는 문제가 있었다. 이 문제를 해결하기 위해서 file-backed는 file-backed init 프로세스에 맞춰 다시 initiate, anonymous는 그 페이지에 맞게, 그리고 swap out 된 페이지는 다시 메모리로 로드하는 방식으로 구현해야한다.
👉🏻 Swap out 된 페이지의 경우 swap disk에서 복제해서 다시 swap disk로 넣어야 하나 하고 생각할 수도 있는데, 메모리에 해당 페이지를 올릴 때 메모리가 부족한 경우 알아서 다른 프로세스의 페이지가 frame_table의 순서에 맞춰서 evict 될 것이기 때문에 해당 프로세스는 모든 페이지를 메모리에 바로 올리면 된다.


Changes :

  • implement frame eviction
  • fixed vm_get_victim()
    • list_pop_back pops an list_elem out, but we were trying to use it as a frame (which will not work)
    • so fixed it so that the victim frame will be list_entry of the popped list_elem

위 사항까지 고치고 나니 swap file 과 swap anon 테스트는 통과하였다!

pass tests/vm/page-linear
pass tests/vm/page-parallel
pass tests/vm/page-merge-seq
pass tests/vm/page-merge-par
pass tests/vm/page-merge-stk
FAIL tests/vm/page-merge-mm
pass tests/vm/page-shuffle
pass tests/vm/mmap-read
pass tests/vm/mmap-close
pass tests/vm/mmap-unmap
pass tests/vm/mmap-overlap
pass tests/vm/mmap-twice
pass tests/vm/mmap-write
pass tests/vm/mmap-ro
FAIL tests/vm/mmap-exit
pass tests/vm/mmap-shuffle
pass tests/vm/mmap-bad-fd
pass tests/vm/mmap-clean
FAIL tests/vm/mmap-inherit
pass tests/vm/mmap-misalign
pass tests/vm/mmap-null
pass tests/vm/mmap-over-code
pass tests/vm/mmap-over-data
pass tests/vm/mmap-over-stk
pass tests/vm/mmap-remove
pass tests/vm/mmap-zero
pass tests/vm/mmap-bad-fd2
pass tests/vm/mmap-bad-fd3
pass tests/vm/mmap-zero-len
pass tests/vm/mmap-off
pass tests/vm/mmap-bad-off
pass tests/vm/mmap-kernel
pass tests/vm/lazy-file
pass tests/vm/lazy-anon
pass tests/vm/swap-file
pass tests/vm/swap-anon
pass tests/vm/swap-iter
FAIL tests/vm/swap-fork
FAIL tests/vm/cow/cow-simple
5 of 141 tests failed.

Changes :

  • anon_initializer : memset frame data to 0
  • mmap : 모든 페이지에 대해 reopen 하도록 수정 -> 오버헤드가 있을 수 있지만.. 페이지 테이블을 모두 정리할 때, mmap된 모든 페이지가 write-back 한 후 해당 파일을 close 해야 하는데, spt 테이블을 돌면서 페이지를 하나하나 정리하다보면 다른 연결된 mmap 페이지들을 기다려줄 수가 없다. 👉🏻 구현할 수 있는 방법은 mmap된 파일을 전체적으로 관리하는 이중 linked list 구조를 만드는 방법과, mmap되는 영역이 여러 페이지에 걸쳐있는 경우, 모든 페이지에 대해 파일을 reopen 해주어 서로 다른 file reference를 가지고 있도록 하는 방법
  • file_backed_destroy : mmap인 경우 mmap이 시작되는 페이지에 대해서만 file_close를 하려고 했는데 위와 같은 이유로 다른 mmap된 페이지들을 관리하기가 어렵기 때문에, 모든 페이지가 reopen을 하도록 했고, 각각의 페이지에 대해 열려있는 file reference를 close 할 수 있도록 수정하였다.
  • anon & file_backed destroy : 둘 다 frame을 완전히 제거할 수 있도록 구현한 destroy_frame 함수를 호출할 수 있도록 수정
  • anon & file_backed swap-out : clear_frame 함수를 사용해서, frame을 얻으려고 하는 함수가 바로 받을 수 있도록 세팅해둘 수 있도록 함

Feat :

  • destroy_frame : Remove frame from frame table, clear frame, remove page from pml4, free frame struct, reset related info
  • clear_frame : Swap out을 하는 경우 frame을 아예 파괴시키면 안되고 frame 구조는 남겨둔 상태에서 get_frame을 하려는 spt entry가 활용할 수 있도록 준비해주어야 한다. 따라서 이에 맞춰서 destroy_frame과 다르게 함수를 만들어 주었다. 그리고, swap out을 하는 경우, vm_get_victim 함수에서 이미 frame table에서 entry를 제거하는 작업은 하고 있기 때문에 해당 함수에서는 frame table에서 entry를 제거해주는 작업은 해줄 필요가 없다.

위와 같은 변화를 줘 본 결과 하나의 테스트케이스를 더 맞긴 했는데, 이전에 맞던게 이번에는 틀리고 있다...

pass tests/vm/mmap-read
pass tests/vm/mmap-close
pass tests/vm/mmap-unmap
pass tests/vm/mmap-overlap
pass tests/vm/mmap-twice
pass tests/vm/mmap-write
pass tests/vm/mmap-ro
pass tests/vm/mmap-exit
pass tests/vm/mmap-shuffle
pass tests/vm/mmap-bad-fd
pass tests/vm/mmap-clean
FAIL tests/vm/mmap-inherit
pass tests/vm/mmap-misalign
pass tests/vm/mmap-null
pass tests/vm/mmap-over-code
pass tests/vm/mmap-over-data
pass tests/vm/mmap-over-stk
pass tests/vm/mmap-remove
pass tests/vm/mmap-zero
pass tests/vm/mmap-bad-fd2
pass tests/vm/mmap-bad-fd3
pass tests/vm/mmap-zero-len
pass tests/vm/mmap-off
pass tests/vm/mmap-bad-off
pass tests/vm/mmap-kernel
pass tests/vm/lazy-file
pass tests/vm/lazy-anon
pass tests/vm/swap-file
FAIL tests/vm/swap-anon
FAIL tests/vm/swap-iter
pass tests/vm/swap-fork
FAIL tests/vm/cow/cow-simple
4 of 141 tests failed.

mmap-inherit.result

Kernel panic in run: PANIC at ../../filesys/inode.c:304 in inode_allow_write(): assertion `inode->deny_write_cnt > 0' failed.
Call stack: 0x8004217f8f 0x80042207be 0x800421f615 0x800421f3c2 0x8004222a41 0x8004221b1b 0x8004222072 0x800421a55d 0x800421a5e0 0x8004221f80 0x800421c003 0x800421bdae 0x8004207278 0x800421d6e0 0x800421d35f 0x800421d1bd 0x400e5c
Translation of call stack:
0x0000008004217f8f: debug_panic (lib/kernel/debug.c:32)
0x00000080042207be: inode_allow_write (filesys/inode.c:305)
0x000000800421f615: file_allow_write (filesys/file.c:137)
0x000000800421f3c2: file_close (filesys/file.c:56)
0x0000008004222a41: file_backed_destroy (vm/file.c:100)
0x0000008004221b1b: vm_dealloc_page (vm/vm.c:253)
0x0000008004222072: spt_destroy_func (vm/vm.c:365)
0x000000800421a55d: hash_clear (lib/kernel/hash.c:58)
0x000000800421a5e0: hash_destroy (lib/kernel/hash.c:84)
0x0000008004221f80: supplemental_page_table_kill (vm/vm.c:348)
0x000000800421c003: process_cleanup (userprog/process.c:424)
0x000000800421bdae: process_exit (userprog/process.c:357)
0x0000008004207278: thread_exit (threads/thread.c:339)
0x000000800421d6e0: fork (userprog/syscall.c:206)
0x000000800421d35f: syscall_handler (userprog/syscall.c:100)
0x000000800421d1bd: no_sti (userprog/syscall-entry.o:?)
0x0000000000400e5c: (unknown)

deny_write 한 개수가 0이상이어야 close하면서 해당 개수를 줄여줄 수 있는데, 0 이상이 아니기에 발생하는 assertion fail이라고 이해하였다. file_reopen을 잘못한 건가 싶었는데.. 꼭 그렇지만도 않은 것 같다.. 아니면 과하게 close를 한건가..? 어떨 때 해당 count가 수정되는지 한번 알아봐야할 것 같다.

해당 변수는 deny_write을 호출할 때 +가 되고, deny_write은 load에서 running_file에 대해서 사용하며, file_duplicate에서도 부른다. file_duplicate는 do_fork에서 fdTable을 복제할 때 사용된다. (결국 supplemental_page_table_copy를 수정한 후 해결이 되었다.)


위 문제를 보다 보니까 evict frame을 할 때 evict 당하는 frame은 현재 프로세스의 프레임이 아닌 다른 프로세스의 프레임일 수 있기 때문에 전체적으로 thread_current() 의 pml4를 사용하면 안될 것이라는 걸 깨달았다. 따라서, page 별로 그 페이지가 속해있는 thread를 연결해주는 방식을 적용해보았다.

  • get_victim에서 pml4_accessed를 확인할 때
  • swap_out 에서 pml4_dirty를 확인할 때
  • pml4_clear_page 할 때

적용해도 결과는 다르지 않았고, all pass가 뜨는 팀원의 코드도 해당 부분이 반영되어있지 않아서... 정상 작동이 되지 않을텐데 왜 pass가 뜨는지 의문이다....

👉🏻 팀원의 경우 위에서 언급한 supplemental_page_table_copy의 허점을 고쳐서 구현하는 작업을 먼저 하고 있었는데, 이 부분을 고치면 swap disk가 꽉 차서 터지는 경우가 발생했었다. 그런데 알고보니 결국 위에서 말한 eviction 프로세스에서의 코드 문제 때문이었던 것이다! frame evicton에서 thread_current() 함수를 사용하기 때문에, evict 하려는 프레임이 속해있는 프로세스에서 프레임을 evict 한 것이 아니라, 동일한 가상주소를 가진 현재 프로세스의 프레임을 evict 하고 있었던 것이다.

정말 신기한 것은 spt copy와 evict 시 thread_current를 사용하는 부분을 고치기 이전에도 All pass가 떴었다는 소름 돋는 사실... (카이스트 교수님들은 히든 테스트 케이스를 가지고 있다는 소문이 있던데.. 이런 부분들은 학생들이 스스로 생각해서 구현하도록 하려고 일부러 테스트 케이스를 간단하게 내신게 아닐까 하는 생각이 든다. 😅 )

그래서 팀원의 copy 함수를 내거에 적용한 결과 inherit 도 해결되고 swap-anon, file, iter도 해결이 되었다.
그런데 아직 swap-fork 가 남았다...
너... 뭐가 문젠데...!?

pass tests/vm/mmap-read
pass tests/vm/mmap-close
pass tests/vm/mmap-unmap
pass tests/vm/mmap-overlap
pass tests/vm/mmap-twice
pass tests/vm/mmap-write
pass tests/vm/mmap-ro
pass tests/vm/mmap-exit
pass tests/vm/mmap-shuffle
pass tests/vm/mmap-bad-fd
pass tests/vm/mmap-clean
pass tests/vm/mmap-inherit
pass tests/vm/mmap-misalign
pass tests/vm/mmap-null
pass tests/vm/mmap-over-code
pass tests/vm/mmap-over-data
pass tests/vm/mmap-over-stk
pass tests/vm/mmap-remove
pass tests/vm/mmap-zero
pass tests/vm/mmap-bad-fd2
pass tests/vm/mmap-bad-fd3
pass tests/vm/mmap-zero-len
pass tests/vm/mmap-off
pass tests/vm/mmap-bad-off
pass tests/vm/mmap-kernel
pass tests/vm/lazy-file
pass tests/vm/lazy-anon
pass tests/vm/swap-file
pass tests/vm/swap-anon
pass tests/vm/swap-iter
FAIL tests/vm/swap-fork
FAIL tests/vm/cow/cow-simple
2 of 141 tests failed.

그런데.. swap-fork output을 확인해보니 아무것도 없는 상태로 killed......? 이거 왜이래...?

정말 당황스러웠지만 결국 AWS의 내 ubuntu 서버가 계속 버거워했던 것 같아서 EC2 인스턴스를 재부팅 해주었다.
그랬더니...됐다...!
드디어 ALL PASS !

pass tests/threads/priority-donate-chain
pass tests/userprog/args-none
pass tests/userprog/args-single
pass tests/userprog/args-multiple
pass tests/userprog/args-many
pass tests/userprog/args-dbl-space
pass tests/userprog/halt
pass tests/userprog/exit
pass tests/userprog/create-normal
pass tests/userprog/create-empty
pass tests/userprog/create-null
pass tests/userprog/create-bad-ptr
pass tests/userprog/create-long
pass tests/userprog/create-exists
pass tests/userprog/create-bound
pass tests/userprog/open-normal
pass tests/userprog/open-missing
pass tests/userprog/open-boundary
pass tests/userprog/open-empty
pass tests/userprog/open-null
pass tests/userprog/open-bad-ptr
pass tests/userprog/open-twice
pass tests/userprog/close-normal
pass tests/userprog/close-twice
pass tests/userprog/close-bad-fd
pass tests/userprog/read-normal
pass tests/userprog/read-bad-ptr
pass tests/userprog/read-boundary
pass tests/userprog/read-zero
pass tests/userprog/read-stdout
pass tests/userprog/read-bad-fd
pass tests/userprog/write-normal
pass tests/userprog/write-bad-ptr
pass tests/userprog/write-boundary
pass tests/userprog/write-zero
pass tests/userprog/write-stdin
pass tests/userprog/write-bad-fd
pass tests/userprog/fork-once
pass tests/userprog/fork-multiple
pass tests/userprog/fork-recursive
pass tests/userprog/fork-read
pass tests/userprog/fork-close
pass tests/userprog/fork-boundary
pass tests/userprog/exec-once
pass tests/userprog/exec-arg
pass tests/userprog/exec-boundary
pass tests/userprog/exec-missing
pass tests/userprog/exec-bad-ptr
pass tests/userprog/exec-read
pass tests/userprog/wait-simple
pass tests/userprog/wait-twice
pass tests/userprog/wait-killed
pass tests/userprog/wait-bad-pid
pass tests/userprog/multi-recurse
pass tests/userprog/multi-child-fd
pass tests/userprog/rox-simple
pass tests/userprog/rox-child
pass tests/userprog/rox-multichild
pass tests/userprog/bad-read
pass tests/userprog/bad-write
pass tests/userprog/bad-read2
pass tests/userprog/bad-write2
pass tests/userprog/bad-jump
pass tests/userprog/bad-jump2
pass tests/vm/pt-grow-stack
pass tests/vm/pt-grow-bad
pass tests/vm/pt-big-stk-obj
pass tests/vm/pt-bad-addr
pass tests/vm/pt-bad-read
pass tests/vm/pt-write-code
pass tests/vm/pt-write-code2
pass tests/vm/pt-grow-stk-sc
pass tests/vm/page-linear
pass tests/vm/page-parallel
pass tests/vm/page-merge-seq
pass tests/vm/page-merge-par
pass tests/vm/page-merge-stk
pass tests/vm/page-merge-mm
pass tests/vm/page-shuffle
pass tests/vm/mmap-read
pass tests/vm/mmap-close
pass tests/vm/mmap-unmap
pass tests/vm/mmap-overlap
pass tests/vm/mmap-twice
pass tests/vm/mmap-write
pass tests/vm/mmap-ro
pass tests/vm/mmap-exit
pass tests/vm/mmap-shuffle
pass tests/vm/mmap-bad-fd
pass tests/vm/mmap-clean
pass tests/vm/mmap-inherit
pass tests/vm/mmap-misalign
pass tests/vm/mmap-null
pass tests/vm/mmap-over-code
pass tests/vm/mmap-over-data
pass tests/vm/mmap-over-stk
pass tests/vm/mmap-remove
pass tests/vm/mmap-zero
pass tests/vm/mmap-bad-fd2
pass tests/vm/mmap-bad-fd3
pass tests/vm/mmap-zero-len
pass tests/vm/mmap-off
pass tests/vm/mmap-bad-off
pass tests/vm/mmap-kernel
pass tests/vm/lazy-file
pass tests/vm/lazy-anon
pass tests/vm/swap-file
pass tests/vm/swap-anon
pass tests/vm/swap-iter
pass tests/vm/swap-fork
pass tests/filesys/base/lg-create
pass tests/filesys/base/lg-full
pass tests/filesys/base/lg-random
pass tests/filesys/base/lg-seq-block
pass tests/filesys/base/lg-seq-random
pass tests/filesys/base/sm-create
pass tests/filesys/base/sm-full
pass tests/filesys/base/sm-random
pass tests/filesys/base/sm-seq-block
pass tests/filesys/base/sm-seq-random
pass tests/filesys/base/syn-read
pass tests/filesys/base/syn-remove
pass tests/filesys/base/syn-write
pass tests/threads/alarm-single
pass tests/threads/alarm-multiple
pass tests/threads/alarm-simultaneous
pass tests/threads/alarm-priority
pass tests/threads/alarm-zero
pass tests/threads/alarm-negative
pass tests/threads/priority-change
pass tests/threads/priority-donate-one
pass tests/threads/priority-donate-multiple
pass tests/threads/priority-donate-multiple2
pass tests/threads/priority-donate-nest
pass tests/threads/priority-donate-sema
pass tests/threads/priority-donate-lower
pass tests/threads/priority-fifo
pass tests/threads/priority-preempt
pass tests/threads/priority-sema
pass tests/threads/priority-condvar
pass tests/threads/priority-donate-chain
All 140 tests passed.

이렇게 행복할 수 없다 ㅠㅠ
3일을 더 써서 드디어 Virtual Memory를 끝냈다.
이제 File System 하자!!!

0개의 댓글