가상 메모리 페이지 관련 요소들
가상 메모리 시스템에서 각 가상 페이지는 다양한 속성을 가짐.
속성들은 페이지 폴트 발생 시 어떤 작업을 해야 하는지 결정하는 데 중요한 역할을 함
1. Virtual Page Number (VPN)
- 가상 주소에서 상위 비트(일반적 20비트)를 사용해 해당 페이지의 번호를 나타냄.
가상 메모리 내에서 페이지의 위치를 결정하는 데 사용됨
2. Read/Write Permission
- 페이지에 대한 접근 권한을 나타냄. 읽기 전용 페이지인지, 쓰기가 가능한지 여부
- 읽기 전용 : 읽기만 가능, 쓰기 시도 시 페이지 폴트
- 읽기/쓰기 : 읽기와 쓰기 모두 가능
3. Type of Virtual Page (가상 페이지의 타입)
- 가상 페이지가 어떤 종류의 데이터에 속하는지 나타냄
- ELF 실행 파일의 페이지 (a page of ELF executable file)
- 실행 파일(ELF)로부터 로드된 페이지
- 일반적으로 코드 또는 상수 데이터가 저장
- 일반 파일의 페이지 (a page of general file)
- 메모리 매핑된 일반 파일에 해당하는 페이지
- 파일의 내용을 메모리에 매핑하여 접근할 수 있는 페이지
- 스왑 영역의 페이지 (a page of swap area)
- 메모리에서 스왑된 페이지로, 메모리 부족 시 디스크의 스왑 영역으로 저장된 페이지
- ELF 실행 파일의 페이지 (a page of ELF executable file)
4. Reference to the File Object and Offset (메모리 맵핑된 파일의 참조 및 오프셋)
- 메모리 맵핑된 파일의 경우, 이 페이지가 어떤 파일의 어느 위치에 해당하는지 나타내는 참조 정보
- 파일 객체 : 이 페이지가 참조하는 파일의 객체
- 오프셋 : 파일 내에서 이 페이지가 시작되는 위치 (바이트 단위)
5. Amount of Data in the Page (페이지 내 데이터 양)
- 해당 페이지에 실제로 들어 있는 데이터의 크기를 나타냄
- 예를 들어, 마지막 파일 페이지의 경우 파일 크기보다 작을 수 있음
6. Location in the Swap Area (스왑 영역에서의 위치)
- 페이지가 스왑되었을 경우, 디스크의 스왑 영역에서 해당 페이지가 저장된 위치를 타나냄
- 스왑 인덱스 : 페이지가 스왑 영역에서 차지하고 있는 블록의 위치를 나타냄
7. In-Memory Flag (메모리 상주 여부)
- 해당 페이지가 물리 메모리에 존재하는지 여부를 나타냄
- True : 페이지가 현재 물리 메모리에 상주
- False : 페이지가 현재 물리 메모리에 존재하지 않으며, 스왑되었거나 파일에 로드되지 않았음
가상 메모리(VM) 시스템을 구현하기 위해 필요한 단계
1. 페이지 테이블 할당
- 페이지 테이블 엔트리를 초기화하여 무효 상태로 설정.
이는 페이지가 아직 메모리에 매핑되지 않았음을 나타냄
2. vm_entry 구조체 할당
- 각 가상 페이지에 대한 vm_entry 구조체를 할당.
이 구조체는 페이지의 상태 및 정보를 저장
3. load_segment() 수정
- load_segment() 함수를 수정하여 ELF 바이너리를 가상 메모리에 로드하는 대신 각 페이지에 대한
vm_entry를 할당하고 초기화 - 데이터와 코드 세그먼트를 로드하는 부분을 제거하고,
페이지에 대한 정보를 설정하는 로직을 추가
4. 가상 주소 공간 관련 구조 초기화
- 가상 주소 공간과 관련된 구조체를 초기화하는 함수를 추가
프로세스의 가상 메모리 관리를 위한 기본 설정을 포함
5. 바이너리 파일 로딩 제거
- 바이너리 파일을 가상 주소 공간으로 로드하는 기존 코드를 제거.
VM 시스템에서 더 이상 필요하지 않게 됨
# vm_entry ?
- 가상 페이지에 대한 정보를 저장하는 구조체
VPN, In-Memory Flag, Read/Write Permission, Type of Virtual Page,
File Reference and Offset, Amount of Data, Location in Swap Area
가상 메모리 시스템을 추가하기 위해서는 기존 스택 페이지 할당 방식 수정하고,
가상 메모리 엔트리(vm_entry) 기반으로 구현해야 함
1. 기존 동작 (Original)
- 페이지 할당 (Allocate a single page)
- palloc_get_page() 함수 등을 사용해 물리 메모리에서 하나의 4KB 페이지를 할당
- 페이지 테이블 설정 (Page table setting)
- 물리 페이지를 가상 주소와 매핑하여 페이지 테이블에 기록.
이 과정에서 페이지 테이블 엔트리를 설정
- 물리 페이지를 가상 주소와 매핑하여 페이지 테이블에 기록.
- 스택 포인터 설정 (Stack pointer (esp) setting)
- 스택의 초기 위치로 스택 포인터(esp) 설정.
스택은 일반적으로 가상 메모리에서 최상위 주소부터 시작하여 아래로 확장
- 스택의 초기 위치로 스택 포인터(esp) 설정.
2. 추가 사항 (Add)
- 4KB 스택에 대한 vm_entry 생성 (Create vm_entry of 4KB stack)
- 4KB 스택에 대한 vm_entry 구조체를 생성. 스택 페이지의 가상 메모리 정보를 저장
- 생성된 vm_entry의 필드 값 초기화 (Initialize created vm_entry field value)
- vm_entry의 각 필드를 스택 페이지에 맞게 초기화함
- vm_entry를 해시 테이블에 삽입 (Insert vm_entry to hash table)
- 스택 페이지에 대한 vm_entry를 가상 메모리 해시 테이블에 삽입하여 관리한다
기존 page_fault()에서 페이지 폴트 발생 시 권한 및 주소 유효성을 검사한 후,
문제가 발생하면 kill(-1)을 호출하여 프로세스를 종료하는 방식이었다.
이 동작을 제거하고, 새로운 페이지 폴트 처리방식을 추가
1. kill(-1) 관련 코드 삭제
- kill(-1) 호출 부분을 제거. 더 이상 프로세스를 종료하지 않고,
handle_mm_fault()를 호출하여 문제를 해결
2. fault_addr의 유효성 검사
- 페이지 폴트가 발생한 주소(fault_addr)가 올바른지 검사.
가상 메모리 범위 내에 존재하는지, 접근 권한이 맞는지를 확인하는 과정
3. 새 페이지 폴트 핸들러 정의 및 호출
- 페이지 폴트가 발생했을 때 handle_mm_fault()를 정의하고 호출
- 페이지 폴트가 발생한 주소에 해당하는 vm_entry 구조체를 찾는다
해당 주소가 어떤 페이지에 속하는지 확인하는 작업 - vm_entry를 기반으로 새로운 페이지를 로드하거나 처리하기 위해
handle_mm_fault() 호출
4. handle_mm_fault() 함수 정의
페이지 폴트 처리 역할. 다음 작업 수행
- 스왑 영역에서 페이지를 불러오거나, 파일에서 데이터를 읽어 페이지를 메모리에 로드
- 스택 페이지의 경우, 스택 확장
- 페이지 테이블을 업데이트하여 새롭게 할당된 페이지를 매핑
handle_mm_fault() 함수 정의
페이지 폴트 시 호출, 필요한 페이지를 메모리에 로드하고 페이지 테이블을 업데이트 하는 과정
1. 물리 메모리 할당
- palloc_get_page() 함수를 이용해 물리 메모리 페이지 할당.
PAL_USER 플래그를 사용하여 사용자 영역의 페이지를 할당
2. 파일로부터 데이터 로드
- 페이지가 파일에 저장된 경우(VM_FILE), 디스크에서 해당 파일을 물리 메모리로 로드.
load_file() 함수 사용.
load_file() 함수는 kpage에 할당된 물리 메모리 주소와 vme를 인자로 받아, vm_entry에 저장된
파일 정보에 따라 디스크에서 데이터를 읽어들임
3. 페이지 테이블 업데이트
- 물리 메모리에 페이지를 로드한 후, 해당 페이지를 가상 주소와 매핑.
이를 위해 install_page() 함수 사용.
이 함수는 가상 주소 upage와 물리 메모리 주소 kpage를 매핑하며, 페이지가 쓰기 가능한지 여부 결정- upage : 가상 페이지의 주소
- kpage : 할당된 물리 페이지의 주소
- writable : 페이지가 쓰기 가능한지 여부
4. vm_entry 상태 업데이트
- 페이지 테이블 매핑이 완료되면, vm_entry의 in_memory 필드르 true로 설정하여
해당 페이지가 메모리에 로드되었음을 나타냄
load_file() 함수는 디스크에서 파일 페이지를 물리 메모리로 로드하는 역할.
이 함수는 vm_entry에 저장된 파일과 오프셋 정보를 이용하여 데이터를 읽어오고,
4KB의 물리 메모리 페이지(kaddr)에 해당 데이터를 저장. 4KB보다 작으면 남은 공간을 0 으로 채워야 함.
- read_bytes : 로드해야 하는 실제 파일 데이터의 크기. vm_entry 구조체에 저장된 값이며,
페이지의 일부만 파일로부터 로드할 수 있는 경우를 처리하기 위함. - zero_bytes : 읽은 데이터 크기 외에, 페이지의 나머지 부분을 0으로 채울 바이트 수.
보통 PGSIZE가 4KB이므로, read_bytes만큼 읽고 남은 공간을 0으로 초기화 - file_seek() : 파일 포인터를 vm_offset 위치로 이동시켜, 파일의 특정 부분에서부터 데이터 읽기
- file_read() : 파일에서 read_bytes만큼의 데이터를 읽어와 물리 메모리의 kaddr에 저장
- memset() : 4KB 중 읽어오지 못한 남은 부분을 0으로 채워 페이지 초기화
Demand paging은 실제로 메모리에 필요할 때에만 페이지를 로드하는 방식.
메모리 사용을 효율적으로 관리하는 기술.
1. userprog/exception.c page_fault()
2. vm/page.c load_file()
3. userprog/process.c handle_mm_fault()