[크래프톤 정글 3기] 12/27(수) TIL

ClassBinu·2023년 12월 27일
0

크래프톤 정글 3기 TIL

목록 보기
71/120

08:56 입실
핀토스 미완이지만.. 오늘 최대한 해결해보고
발표자료 준비

Pintos

mmap 파일 동기화 시점

메모리 매핑된 페이지가 매핑 해제되거나 교체(스왑 아웃)되면 콘텐츠의 모든 변경 사항이 파일에 반영

lazy_load_segment() 분석

처음 호출되면 페이지 폴트가 발생하고, 그때 이 함수 호출.
이 함수는 실제로 디스크의 내용을 메모리에 적재하는 로직이 실행됨.
load_segment로 우선 가짜로 메모리를 할당하고, 페이지 폴트가 발생하면 lozy_load_segment가 동작하면서 실제 할당을 하는 방식.


// 파일을 읽어서 메모리에 로딩하는데 필요한 정보
// 1. 파일 포인터
// 2. 읽어올 위치(오프셋)
// 3. 오프셋부터 어느정도 길이까지 읽어올지(바이트 길이)
// 4. 제로 바이트 수(파일이 페이지보다 작을 경우 나머지 부분은 0으로 채움)
struct lazy_load_arg
{
    struct file *file;
    off_t ofs;
    uint32_t read_bytes;
    uint32_t zero_bytes;
};


bool lazy_load_segment(struct page *page, void *aux)
{
	// aux 포인터를 필요한 정보 구조체로 변환
	struct lazy_load_arg *lazy_load_arg = (struct lazy_load_arg *)aux;

	// aux에서 파일 위치와 오프셋을 기준으로 파일을 찾음.
    // 이후 해당 file의 pos를 조정함.
	file_seek(lazy_load_arg->file, lazy_load_arg->ofs);
    
    // 이 시점에서 file_read를 통해 aux정보를 바탕으로 디스크의 파일을 메모리에 적재한다.
    // 하지만 기대하던 파일 읽기 길이와 일치하지 않으면 페이지를 해제하고 false를 리턴(뭔가 문제가 생겼음)
	if (file_read(lazy_load_arg->file, page->frame->kva, lazy_load_arg->read_bytes) != (int)(lazy_load_arg->read_bytes))
	{
		palloc_free_page(page->frame->kva);
		return false;
	}

	// (파일 읽기가 정상적이면) aux값을 기반으로 나머지 부분을 0으로 초기화
	memset(page->frame->kva + lazy_load_arg->read_bytes, 0, lazy_load_arg->zero_bytes);

	return true;
}

aux에 파일 내용이 올라와 있는 게 아님. aux에는 단순히 길이 정보 등만 담겨있고 실제로 file_read로 파일 내용을 읽어오고, memset을 통해서 나머지 페이지 여백을 0으로 안전하게 세팅함.
memset의 dst에 해당하는 kva는 실제 물리 메모리 위치를 나타냄.(커널 가상 주소)

load_segment()

메모리 페이지를 예약하고 지연 로딩 정보를 설정
이후 페이지 폴트가 발생하면 lazy_load_segment()를 통해 실제 할당 실행

static bool
load_segment(struct file *file, off_t ofs, uint8_t *upage,
			 uint32_t read_bytes, uint32_t zero_bytes, bool writable)
{
	ASSERT((read_bytes + zero_bytes) % PGSIZE == 0);
	ASSERT(pg_ofs(upage) == 0);
	ASSERT(ofs % PGSIZE == 0)
    
    // 주어진 바이트를 설정할 때까지 반복
	while (read_bytes > 0 || zero_bytes > 0)
	{
    	// 로딩할 메타 데이터를 저장할 공간 확보
		struct lazy_load_arg *lazy_load_arg = (struct lazy_load_arg *)malloc(sizeof(struct lazy_load_arg));
		lazy_load_arg->file = file; // 파일 정보 저장
		lazy_load_arg->ofs = ofs; // 오프셋 저장
		lazy_load_arg->read_bytes = page_read_bytes; // 로드할 데이터 길이 저장
		lazy_load_arg->zero_bytes = page_zero_bytes; // 0으로 채울 길이 저장
		// 가상 메모리 페이지 할당 실패 시 false return
		if (!vm_alloc_page_with_initializer(VM_ANON, upage,
											writable, lazy_load_segment, lazy_load_arg))
			return false;

		// 남은 작업 현황 최신화
		read_bytes -= page_read_bytes;
		zero_bytes -= page_zero_bytes;
		upage += PGSIZE;
		ofs += page_read_bytes;
	}
	return true;
}

잘못 생각한 점.
왜 매번 arg를 계속 생성하지? 마지막에 한 번만 생성하면 되는거 아닌가?
아님, 왜냐면 페이지끼리는 사실상 독립적으로 할당되고 작동됨. 그래서 페이지 마다 고유의 aux 정보가 있어야 함.

파라미터 분석

struct file file: 로드할 파일 구조체
off_t ofs: 파일 내에서 읽기 시작할 오프셋
uint8_t
upage: 사용자 공간에 할당될 페이지 시작 주소(데이터 로드 위치)
uint32_t read_bytes: 파일에서 읽어야 하는 바이트 길이
uint32_t zero_bytes: 메모리 페이지 내에서 0으로 채워야 하는 길이
bool writable: 해당 페이지 쓰기 가능 상태 설정(페이지 보호용)


vm_anon_init()

가상 익명 메모리 초기화

void vm_anon_init(void)
{
	// 스왑 디스크를 세팅한다. 익명 페이지는 백업 공간이 필요하다.
	swap_disk = disk_get(1, 1);
	list_init(&swap_table);
	lock_init(&swap_table_lock);

	// 1섹터는 512바이트. 8섹터가 4kb로 1페이지 분량
	for (disk_sector_t i = 0; i < swap_size; i++)
	{
		struct slot *slot = (struct slot *)malloc(sizeof(struct slot));
		slot->page = NULL;
		slot->slot_no = i;
		lock_acquire(&swap_table_lock);
		list_push_back(&swap_table, &slot->swap_elem);
		lock_release(&swap_table_lock);
	}
}

anon_initializer()

가상 익명 메모리 초기화 함수

bool anon_initializer(struct page *page, enum vm_type type, void *kva)
{
    // 페이지의 연산 집합을 익명 페이지용으로 설정. 
    // 이는 익명 페이지 관련 작업을 정의하는 함수 포인터들을 포함한다.
    page->operations = &anon_ops;

    // 익명 페이지에 대한 메타데이터를 페이지 구조체 내에 설정.
    // 이 메타데이터는 익명 페이지의 관리에 필요하다.
    struct anon_page *anon_page = &page->anon;
    
    // 초기 상태에서는 이 익명 페이지가 스왑 영역에 적재되지 않았음을 나타냄.
    // slot_no가 -1이면, 이 페이지는 아직 스왑 영역에 저장되지 않은 상태임을 의미한다.
    // 이는 페이지가 아직 사용되지 않았거나 메모리에 적재되지 않았음을 표시한다.
    anon_page->slot_no = -1;
    return true;
}

anon_swap_in()

static bool
anon_swap_in(struct page *page, void *kva)
{
    // 현재 페이지와 연관된 익명 페이지 구조체에 접근
    struct anon_page *anon_page = &page->anon;
    
    // 현재 페이지가 저장된 스왑 슬롯의 번호를 얻음
    disk_sector_t page_slot_no = anon_page->slot_no;
    
    // 스왑 테이블의 리스트 요소를 순회하기 위한 변수
    struct list_elem *e;
    
    // 스왑 테이블 내의 슬롯을 나타내는 구조체
    struct slot *slot;
    
    // 스왑 테이블에 대한 접근을 동기화하기 위해 락을 획득
    lock_acquire(&swap_table_lock);
    
    // 스왑 테이블 전체를 순회
    for (e = list_begin(&swap_table); e != list_end(&swap_table); e = list_next(e))
    {
        // 현재 요소를 슬롯 구조체로 변환
        slot = list_entry(e, struct slot, swap_elem);
        
        // 현재 슬롯이 페이지에 대응되는 슬롯인지 확인
        if (slot->slot_no == page_slot_no)
        {
            // 페이지에 대응하는 디스크 섹터들을 읽어, kva에 지정된 메모리 주소로 로드
            for (int i = 0; i < 8; i++)
            {
                disk_read(swap_disk, page_slot_no * 8 + i, kva + DISK_SECTOR_SIZE * i);
            }
            
            // 페이지와 슬롯의 연결을 해제
            slot->page = NULL;
            
            // 스왑 슬롯 번호를 초기화하여 페이지가 더 이상 스왑에 있지 않음을 표시
            anon_page->slot_no = -1;
            
            // 작업이 완료되었으므로 락을 해제하고 함수를 종료
            lock_release(&swap_table_lock);
            return true;
        }
    }
    // 스왑 테이블에서 해당 페이지를 찾지 못한 경우 락을 해제하고 false 반환
    lock_release(&swap_table_lock);
    return false;
}

anon_swap_out()

/* Swap out the page by writing contents to the swap disk. */
static bool
anon_swap_out(struct page *page)
{
    // 페이지가 NULL이면 함수를 종료하고 false 반환
    if (page == NULL)
        return false;

    // 익명 페이지에 대한 메타데이터 접근
    struct anon_page *anon_page = &page->anon;

    // 스왑 테이블의 리스트 요소를 순회하기 위한 변수
    struct list_elem *e;

    // 스왑 테이블 내의 슬롯을 나타내는 구조체
    struct slot *slot;

    // 스왑 테이블에 대한 접근을 동기화하기 위해 락 획득
    lock_acquire(&swap_table_lock);

    // 스왑 테이블을 순회하면서 빈 슬롯을 찾음
    for (e = list_begin(&swap_table); e != list_end(&swap_table); e = list_next(e))
    {
        slot = list_entry(e, struct slot, swap_elem);

        // 빈 슬롯을 찾았을 경우
        if (slot->page == NULL)
        {
            // 페이지의 내용을 스왑 디스크의 해당 슬롯에 저장
            for (int i = 0; i < 8; i++)
            {
                disk_write(swap_disk, slot->slot_no * 8 + i, page->va + DISK_SECTOR_SIZE * i);
            }
            
            // 슬롯 번호 업데이트 및 슬롯에 페이지 연결
            anon_page->slot_no = slot->slot_no;
            slot->page = page;

            // 페이지와 프레임 간의 연결 해제
            page->frame->page = NULL;
            page->frame = NULL;

            // PML4 페이지 테이블에서 페이지 엔트리 제거
            pml4_clear_page(thread_current()->pml4, page->va);

            // 락 해제 후 함수 종료
            lock_release(&swap_table_lock);
            return true;
        }
    }

    // 스왑 테이블에서 빈 슬롯을 찾지 못한 경우, 시스템 패닉 발생
    lock_release(&swap_table_lock);
    PANIC("insufficient swap space");
}

file_backed_initializer()

파일 맵핑 메모리 초기화

bool file_backed_initializer(struct page *page, enum vm_type type, void *kva)
{

	// 파일 백언 관련 연산 정의(파일 기반의 스왑 인, 아웃 연산 연결됨)
	page->operations = &file_ops;

	// 페이지 구조체의 파일 정보 설정
	struct file_page *file_page = &page->file;

	// 지연 로딩할 부가 정보 추출
	struct lazy_load_arg *lazy_load_arg = (struct lazy_load_arg *)page->uninit.aux;
    
    // 파일 구조체 정보 업데이트
	file_page->file = lazy_load_arg->file;
	file_page->ofs = lazy_load_arg->ofs;
	file_page->read_bytes = lazy_load_arg->read_bytes;
	file_page->zero_bytes = lazy_load_arg->zero_bytes;
	return true;
}

이 함수는 실제 파일을 아직 적재한 단계는 아님. 파일을 적재하기 위한 페이지를 생성하고 메타 데이터를 설정하는 것. 실제 스왑 인이 발생하면 물리 메모리(프레임)에 적재되는 것!

매개 변수 분석

struct page page : 초기화할 페이지 정보 구조체
enum vm_type type : 가상메모리 타입(익명, 파일 맵핑)
void
kva : 커널 가상 주소

file_backed_swap_in()

static bool
file_backed_swap_in(struct page *page, void *kva)
{
	struct file_page *file_page = &page->file;
	return lazy_load_segment(page, file_page);
}

해당 페이지 구조체에 명시된 파일로 file_page 구조체 생성
page에 file_page 부가 정보를 바탕으로 프레임 연결하면서 실제 물리 메모리로 적재.

file_backed_swapt_out()

static bool
file_backed_swap_out(struct page *page)
{
	// 파일 기반 페이지 보조 자료
	struct file_page *file_page = &page->file;
    
    // 만약 Pml4가 수정 이력이 있으면(파일 동기화 필요 시)
	if (pml4_is_dirty(thread_current()->pml4, page->va))
	{
    	// 파일 쓰기
		file_write_at(file_page->file, page->va, file_page->read_bytes, file_page->ofs);
        // 더티 값 0으로 바꾸기
		pml4_set_dirty(thread_current()->pml4, page->va, 0);
	}

	// 페이지와 프레임 연결 끊기
	page->frame->page = NULL;
	page->frame = NULL;
    
    // pml4 페이지 테이블에서 현재 메모리 적재 되지 않았음을 표시
	pml4_clear_page(thread_current()->pml4, page->va);
	return true;
}

Project3 주간공유 발표 자료


여담

LLM은 수학 계산은 못할 거라고 생각해서 물어봤는데,
해당 수식을 내부적으로 파이썬으로 계산해서 알려줌;;

즉, 계산하지 못하지만 텍스트를 분석해서 파이썬 코드로 바꾸고, 그걸 바탕으로 계산을 해내는 식으로 작동.
놀랐다..

'함께 자라기' 책 샀음! 오늘은 1시간 일찍 퇴근하고 기숙사에서 책 읽어보기!
https://www.yes24.com/Product/Goods/67350256

0개의 댓글