Jungle

[TIL] pintos : vm anon 페이지 구현

손가든 2023. 12. 22. 19:56

오늘도 어김없이 vm 코드를 개선해 나갈 예정이다.

 


 

1. hash table 접근 시 동기화 보장

 

The hash table does not do any internal synchronization. It is the caller's responsibility to synchronize calls to hash table functions.

 

깃북에 적힌 해시 테이블에 대한 Synchronization에 관한 주의사항이다.

 

해시 테이블의 값을 찾는 행위는 동시에 진행되어도 문제 없지만

 

supplemental page table 을 삭제할 때, search & delete를 수행할 경우 동기화를 보장해야 한다.

 

따라서 semaphore를 spt 구조체 안에 선언하여 search & delete 시 동기화를 보장해주도록 할 것이다.

 

 


 

2. hash_destroy() 함수와 hash_clear() 함수

 

이 두 함수는 사용 의도가 매우 비슷하다.

 

하지만 hash table을 완전히 비우는 것과 hash table 자체를 삭제한다는 차이가 있다.

 

 

hash_clear() 함수의 내부를 보자.

 

 

해시 함수 내부는 사실 입자로 들어온 hash_action_func함수를 모든 elem에 적용할 수 있도록 친히 순회해주는 코드일 뿐이다.

 

실제로 삭제 함수를 구현해놓지는 않았다.

 

이유는 우리가 원하는 것은 실제 malloc으로 할당한 frame struct와 page struct를 free하는 일이기 때문인데,

 

그것은 코드의 주인인 내가 할 일이기 때문이다.

 

그래서 순회하여 free하는 것을 편하게 해주기 위해 다음과 같이 유동적인 코드만 제공해준 것 같다.

 

 

그래서 다음처럼 구조체만 반환해주고 그안에 물리적으로 할당한 palloc page는 pml4를 반환하면서 삭제되도록 놔두었다.

 

그리고 위 hash_clear()함수는 hash 자체를 삭제하지 않는다.

 

hash table도 하나의 저장체이기 때문에 palloc으로 kernel영역에 할당했는데,

 

프로세스가 종료되면 이 할당받은 table 페이지는 반환되어야 한다.

 

 

따라서 hash_clear도 수행하며 hash자체도 삭제해주는 코드인 hash_destroy를 사용했었다.

 

 

이 hash_destroy를 semaphore와 함께 선언해주었다.

 

 

 

근데 문제가 발생했다.

 

원래 되던 코드가 갑자기 고장나기 시작했다.

 

 

그 이유는 이 supplemental_page_table_kill 함수가 process_exec()에서 사용되기 때문이다.

 

process_exec()는 유저프로그램을 로딩하고 있는 스레드에 존재하는 위해 table과 관련된 모든 데이터들을 삭제하기 위해 process_cleanup()을 수행한다.

 

근데 이 때 process_cleanup()함수에서 위 함수를 호출하기 때문이다.

 

 

process_exec() 함수에서 사용한 cleanup은 테이블 자체를 지우려는 의도가 아니기 때문에 테이블 자체는 삭제하면 안된다.

 

따라서 process_exit()함수에서만 이 hash 자체를 삭제하도록 옮겨주었다.

 

 


 

3. lazy-loading 시 fork 하는 방법

 

lazy-loading 기법을 사용하여 메모리를 가상화 할 때, fork를 어떻게 구현해야 할까?

 

lazy-loading 을 위해서 새로 도입한 spt를 자식 프로세스에도 생성한 뒤 그 안의 페이지들을 복사해주어야 한다.

 

 

이때, 페이지의 유형들이 각각 다르고, 유형별로 페이지들의 프레임을 다르게 할당해야 할 수도 있기 때문에

 

유형별로 처리하기 위해 부모의 spt를 순회하며 switch문으로 처리해주었다.

 

 

 

그리고 또 하나 유의해야 할 점은, ifdef-else 문을 통해 이전 pml4가 복사되는 문단으로 들어가지 않기 때문에

 

복사하면서 하나씩 pml4에 집어넣는 작업도 추가로 처리해야 한다.

 

 

 

위 사진은 fork 시 spt 복사를 위해 구현한 함수이다.

 

깃북에 나와있는 hash 함수 사용법에서 각각 순회하며 해쉬 요소들을 뽑아내 작업을 수행하도록 iterator를 사용했다.

 

그 후 부모의 page를 뽑아낸 뒤 부모의 타입을 받아놓았다.

 

vm.c 파일에 보면 type을 get해주는 함수가 있는데,

 

vm.c get_type 함수

자세히 보면 이 함수는 uninit 타입일 경우 uninit 타입을 반환하는 것이 아니라

 

그 uninit 페이지가 page fault 가 발생했을 경우 어떤 유형으로 변경되어야 하는지를 반환하고 있다.

 

따라서 이 함수는 copy 함수에서는 사용하기 어렵다.

 

우리는 uninit일 경우 uninit 이라는 것을 알고 싶기 때문에 그냥 cp_page->operations->type 로 대입해줬다.

 

 

그리고 spt의 각 페이지의 내용을 복사해서 넣어주기 위해 새로 new_page를 malloc으로 생성해주었다.

 

그리고 페이지의 내용을 memcpy로 복사해 준 뒤,

 

일단 frame은 부모의 진짜 물리 메모리의 정보가 들어있기 때문에 페이지와 연결되있는 프레임을 비운다.

 

그후 자식의 spt에 new_page를 넣어준다.

 

그리고 switch문으로 각 유형별로 처리하는데,

 

spt에 페이지를 넣어주는건 진짜 물리 메모리를 할당하는 것은 아니다.

 

 

따라서 VM_ANON 유형의 페이지라면, 각 개별의 자식 프레임을 할당해주기 위해

 

do_claim_page 함수를 통해 새로 prame을 할당받고 자식의 pml4 테이블에 할당받은 정보를 기록한다.

 

그후 할당받은 프레임의 kva에다가 memcpy로 복사 원본 프레임의 정보를 넣어준다.

 

이렇게 하면 물리 프레임을 가지고 있고 그 안에 내용을 가지고 있는 부모의 페이지가 복사가 완료된다.

 

 

file의 유형도 따로 처리해야 될 예정이지만 아직은 anon에 대한 것만 처리해주는 것으로 마무리했다.