본문 바로가기

CS

Pintos Project3: Virtual Memory

지금까지 만든 pintos project는 몇가지 한계가 있었다. 먼저 swap을 진행 할 수 없고 demand paging이 사용 불가 했고 가상 메모리가 구현 되지 않았다. 이번 프로젝트에는 이런 점들을 보완할 것이다. 그 전에 먼저 Supplemental page table을 정리하고 넘어가야 한다. 우리가 기존에 아는 페이지 테이블에 좀 더 보충해주는 역할을 하는 테이블이다. 각 페이지에 대한 추가 데이터로 페이지를 보완한다. 여기서는 페이지 테이블과 같게 봐도 될 것 같다. 페이지 폴트가 났을때 추가 되어야 할 데이터를 알 수 있고, 프로세스가 종료 되었을때 해제 되어야할 자원을 알 수 있게 해준다. 우리 프로젝트에서는 hash 를 사용해서 페이지들을 관리 한다. supplemental_page_table 관련 함수들을 작성해 주어야 한다.  페이지를 찾고 삽입하는 함수등을 작성한다.

다음으로는 물리 메모리를 관리하는 구조체를 알아보자 이와 관련된 함수들도 작성해준다.  물리 공간을 가져온 뒤 가상 주소와 매핑하는 함수 까지 작성해야 한다. 메모리가 꽉 차있을 때는 교체될 페이지를 찾는 함수도 작성해 주어야 한다.

페이지와 프레임 매핑하는 함수 이다. 페이지 테이블에 기록해 준다.

다음은 anonymous page 를 작성할 차례다 anon page 란 file-backed 페이지와 다르게 파일 소스가 따로 없다 . 스택과 힙에 할당 된다. 따라서 anon page를 작성하기 위한 구조체와 함수들을 만들어야 한다 . 또한 여기서 lazy loading이라는 개념도 알고 넘어가야 하는데, 실제로 필요하기 전까지 메모리에 올리는 것을 지연하는 것이다. 페이지는 할당 되어 있지만 실제로 물리 프레임이 할당 되지 않은 것이다. 따라서 여기서는 처음에 페이지를 만들때 uninit 형태로 만드는데 여기서 각 페이지 타입에 따라 다르게 initializer를 설정한다. 그뒤 프로그램이 실행 될때 페이지 폴트가 나면 방금 설정해 준 것에 맞춰 실행 시켜 준다. 페이지의 사이클을 보면 initialize -> page fault -> lazy load -> swap in -> swap out 순으로 갖는 것을 알 수 있다. 따라서 vm_alloc_page_with_initializer 함수를 작성할 것이다. 미리 uninit page 형태로 만들어서 위에서 말한 것 처럼 각 타입에 맞게 initializer를 설정 해준다. 그 뒤 처음 page fault가 났을때 설정 해둔 타입에 맞게 처리를 해준다. 자세히 보면 uninit_initialize 를 보면 타입에 맞게 page_initializer를 실행해주고 lazy_load_segment 함수를 실행하면서 위에서 넘긴 인자에 맞게 파일을 읽어준다.

page fault 났을때

 

이 함수는 전에 project 2에서 보았던 load_segment 작성시에도 필요하다.  실행시킬 파일에 맞게 lazy_load를 시켜 놓는다.  anon 함수에서도 initializer를 작성해서 그에 맞게 동작하게 만든다. 다음으로 stack growth 인데 프로젝트 2에서는 크기가 제한 되어 있었지만 여기서는 크기가 커지면 페이지를 더 할당 해준다. 여기서 쓰레드 구조체에 rsp 값을 갖는 변수를 만들어 주는데,  그 이유는 커널에서 페이지 폴트가 났을때의 경우다. 프로세서는 원래 스택포인터를 유저에서 커널 모드로 넘어갈때 예외가 생기면 저장해준다. 따라서 여기서 page fault 가 났을때 핸들링 해주는 함수를 작성해줘야 한다.

페이지 폴트 함수 중간에 보면 stack_growth 부분이 있는데 조건에 맞게 들어온 페이지가 stack 영역이라면 스택영역에 프레임을 할당해준다. 넘어가기 전에  get_frame을 했는데 할당할 frame이 부족할시에 알고리즘을 사용해 교체할 페이지를 찾아 프레임을 확보하는 부분을 구현해야 한다. LRU는 구현하기가 까다로워 clock알고리즘을 구현하면 좋다. 아니면 간단하게 연결 리스트 형식에서 next-fit 형태로 공간을 찾는 방법도 있다. 다음으로 memory mapped file 부분인데, 이것 역시 filebacked memory를 할당하는 함수를 작성해야 한다. 페이지를 처음 만들때는 당연히 lazy한 방식으로 하기 때문에 anon 페이지와 크게 다르지 않다 대신에 initializer와 타입을 명시해줘야 한다.

위의 함수는 실제로 syscall에서 mmap을 호출하면 실행되는 함수이다. 할당해야할 파일과 lazy하게 동작할 함수를 처음 페이지를 uninit으로 설정할때 같이 넘겨준다. 그럼 위에서 말한 동작처럼 page fault가 났을때 메모리를 매핑시켜 줄 것이다. unmap 은 해당 주소에 맞는 페이지를 찾아 더티한지 확인후에 페이지 clear 시켜주면 된다.

마지막으로 swap in/out을 메모리 타입별로 작성해 주면 마무리다.  anon 메모리 같은 경우  swap out할 때 설정해 놓은 스왑 인덱스가 있다. swap in 할때 그 인덱스로 디스크에서 찾아서 kva에 읽어 들입니다. 그리고 해당 섹터는 비트맵에서 지워 줍니다. swap index도 지워 줍니다.

file backed 같은 경우에는 애초에 uninit page 만들때 파일을 같이 인자로 넘겼으니 거기서 파일을 kva 읽어 들입니다. 페이지 크기만큼 읽는데,  그 사이즈를 넘는 나머지 부분은 0으로 채워 줍니다.

 

'CS' 카테고리의 다른 글

입/출력 장치 개념  (2) 2021.10.25
Threads  (0) 2021.10.22
Pintos Project2: User Programs  (0) 2021.10.13
Virtual Memory  (0) 2021.10.07
유저 스택과 커널 스택  (0) 2021.10.05