이제와서 쓰는 핀토스에 기본적으로 구현되어 있는 VM 구현 시 자주 만나는 API ! 를 정리해보자
우리는 지금까지 많은 로직들을 구현해오며 기본으로 제공되는 API들을 사용해왔다.
정리해놓으면 나중에 찾아보기 좋을 것 같아서 정리해본다.
이 친구들은 Project 3 익명 페이지 스왑에서 자주 사용될 것만 같은 친구들이다(아직 구현안함)
하나하나 간단하게 알아보자
bitmap_create(size_t bit_cnt);
swap_disk의 총 섹터 수 / 섹터 당 페이지에 필요한 개수 (예 : 8)만큼 bit_cnt 설정
예 : 디스크에 8192개의 섹터가 있다면 → bitmap_create(8192 / 8) → 1024개의 스왑 슬롯 관리 가능
bitmap_scan_and_flip(bitmap, start, cnt, false);
의미: false (비어있는 슬롯)을 가진 cnt개 연속 비트를 찾고, true로 전환
보통은 cnt = 1 고정 → 한 슬롯만 필요하니까
반환값: 사용 가능한 슬롯의 인덱스, 없으면 BITMAP_ERROR
bitmap_reset(bitmap, slot_index);
해당 슬롯을 다시 비어있는 상태로 되돌림
anon_swap_in()이 끝난 후, 스왑 슬롯을 재사용 가능하게 할 때 사용
bitmap_test(bitmap, index);
true)인지 비어 있는지(false) 확인hash_init(&spt->spt_hash, page_hash, page_less, NULL);
해시 테이블 초기화 API
인자값으로 들어가있는 page_hash, page_less 이 친구들은 커스텀 사용시 직접 구현해줘야 하는 함수들이다.
hash_insert(struct hash *h, struct hash_elem *new);
new 삽입. 동일 키 존재 시 삽입 안 하고 기존 항목 반환hash_find(struct hash *h, struct hash_elem *e);
NULL 반환)hash_delete(struct hash *h, struct hash_elem *e);
hash_clear(struct hash *h, hash_action_func *destructor)
이 다섯개의 함수만 기억하면 SPT나 가상 메모리용 해시 테이블은 충분히 구현 할 수 있다고 한다 (FEAT. GPT)
이 친구들은 MMU와 가상 주소 ↔ 물리 주소 매핑에 직접 관여한다.
주로 페이지를 클레임할 때 vm_do_claim_page()에서 사용된다.
pml4_set_page(pml4, va, pa, writable);
가상 주소 va를 물리 주소 pa에 매핑
writable 플래그로 쓰기 권한 지정
pml4_get_page(pml4, va);
pml4_clear_page(pml4, va);
가상 주소 va의 매핑을 해제
스왑 아웃 이후나 페이지 제거 시 사용된다 !
pml4_is_dirty(pml4, va);
pml4_set_dirty(pml4, va, bool);
pml4_is_accessed(pml4, va);
pml4_set_accessed(pml4, va, bool);
Dirty : 페이지 내용이 수정되었는지 여부
Accessed : 해당 페이지가 최근 접근된 적이 있는지 여부
물리 메모리 프레임을 할당하고 반납할 때 사용된다.
커널 메모리/유저 메모리를 구분해서 플래그로 지정해야 한다.
palloc_get_apge(PAL_USER | PAL_ZERO);
PAL_USER : 사용자 공간에서 할당하겠다 !
PAL_ZERO : 0으로 초기화된 페이지를 !
palloc_free_page(addr);
addr에 해당하는 페이지를 반환할게요 ㅜㅜpalloc_get_multiple(flags, count);
palloc_free_multiple(addr, count);
사실 이 친구들을 가장 먼저 적어야 했을 듯 싶다. 그만큼 광범위하게 사용해서 알면 알수록 좋다 !
list_init(struct list *list);
리스트를 빈 상태로 초기화
구조체 안에 struct list가 들어있으면 이걸 먼저 초기화해야 함
list_empty(struct list *list);
true 반환list_push_front(struct list *list, struct list_elem *e);
list_push_back(struct list *list, struct list_elem *e);
요소 e를 리스트의 앞이나 뒤에 추가한다.
여기까지 구현해왔다면 다들 알겠지만 큐, 스택, 스케줄러 등 구현에 따라 선택적으로 사용된다 !
list_pop_front(struct list *list);
리스트의 맨 앞 요소를 제거하고 반환한다
주로 FIFO 큐처럼 사용할 때 유용함~
list_remove(struct list_elem *e);
리스트 중간에 있는 특정 요소를 제거할 때 사용
SPT, FrameTable, PriorityQueue 등에서 자주 등장
list_begin(struct list *list);
list_end(struct list *list);
list_next(struct list_elem *e);
for문을 통해 리스트를 순회할 때 사용한다
list_begin부터 list_end 전 까지 list_next로 순회
얘도 은근 많이 쓴 것 같다
list_entry(struct list_elem *e, struct your_struct, member);
리스트 안에 있는 요소를 본래 구조체 포인터로 변환할 때 사용
hash_entry() 와 방식이 유사하다
얘도 많이 썼다.
list_insert_ordered(struct list *list, struct list_elem *e, list_less_func *less, void *aux);
less 함수를 기준으로 정렬되도록 삽입
Priority Scheduling 등에서 유~용하게 씀
list_size(struct list *list); // 전체 요소 개수 반환
list_sort(...) // 정렬 (선택적)
각 API 마다 많이 쓰이는 것 같은 친구들만 정리해보았다.
나중에 더 생기면 추가해야 될 것 같다