각 태스크는 task_stuct 라는 자료구조를 통해 관리되고, 고유한 가상 메모리 역시 mm_struct 를 통해 관리되어 진다.
mm_struct 자료구조는 크게 세 부분으로 구분되어진다.
vm_area_structregion, 흔히 말하는 세그먼트)regin 을 vm_area_struct 로 관리한다.pgd (Page Global Directory)start_code, start_data, start_stack 등등.
vm_area_struct 코드
include/linux/mm_types.h 헤더 파일 내의 내용이다. 다음의 코드에서 다음의 필드를 찾을 수 있다:
vm_start : 시작 주소vm_end : 끝 주소vm_flags : 접근 제어 플래그(Read-Only, Writable, etc.) vm_file : 실제 파일 위치vm_offset : 파일 내 세그먼트의 위치에 해당하는 오프셋와 같은 필드를 찾을 수 있다. 4번과 5번은 페이지 폴트 발생 시 사용한다.
리눅스의 가상 주소 메모리 할당의 단위를 페이지(Page) 라 부르며, 크기는 보통 4 KiB 이다.
vm_area_struct 구조 및 동작 방식하나의 태스크는 여러 개의 vm_area_struct 를 가진다. 겹치지 않으며 새로 사용하려는 가상 주소 공간이 인접한 vm_area_struct 와 동일한 속성을 가진다면, 하나의 vm_area_struct 로 합쳐진다.
같은 태스크에 속한 vm_area_struct 는 연결 리스트 + 레드 블랙 트리로 연결되어 있다. 섹션 8 의 그림을 보면 vm_next, vm_prev, vm_rb 가 그것에 해당한다.
효율성 문제 때문인데 탐색에서는 Red black Tree 를 사용하고, 연결 관계 파악에는 Doubly linked List를 사용한다.

각각의 멤버 변수는 가상 주소(Virtual Address) 를 저장한다.
가상 메모리의 각 주소는 elf_loader 가 결정한다. 커널은 전혀 개입하지 않는다.
data, bss 는 컴파일 타임에 그 크기가 결정되지만 다음의 차이가 있다: bss 는 메모리가 초기화되어 있으나, data 는 그러하지 않음.
스택과 힙 사이 공간에는 라이브러리가 올라간다 (ex. glibc)
mm_struct 의 관리 범위mm_struct 는 커널과 유저 영역 모두 관리할까?
1. 커널의 mm_struct 는 init_mm 하나만 존재
2. 커널 스택은 task_struct 의 void *stack 으로 관리됨.
프로그램의 실행은 아래의 순서로 이뤄진다:
실행 파일의 어느 부분을 물리 메모리에 적재하는지는 ELF Header 를 읽어서 결정한다. 가상주소와의 연결은 미리 정해진 규칙(~/include/linux/elf.h) 을 따른다.
Page frame) 페이지(Page): 가상 메모리의 최소 할당 단위
페이지 프레임(Page frame): 물리 메모리의 최소 할당 단위
sys_execve() 시스템 콜은 인자로 전달된 프로그램을 메모리에 적재한다. sys_execv() 는 아래의 순서로 동작한다:
free 한 페이지 프레임들을 할당 받는다.demand paging)파일의 모든 내용을 실행 즉시 물리 메모리에 적재하는 것이 아닌, 페이지 테이블의 NP (Not Present) 상태일 때, 폴트를 발생시켜 그 때 로드한다. 이를 요구 페이징(demand paging) 이라 한다.
요구 페이징을 확인하기 위해 간단한 예제 코드를 작성해보았다:
#include <stdio.h>
#include <stdbool.h>
char array[1024 * 1024 * 1024]; // 1 GiB
int main(void)
{
while (true)
/* do nothing */;
return 0;
}

보는 것처럼 가상 메모리(VIRT)는 크지만 실제 사용하고 있는 메모리(RES)는 그리 많지 않다.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
char array[1024 * 1024 * 1024]; // 1 GiB
int main(void)
{
srand((unsigned long) time(NULL));
for (int i = 0; i < 1024 * 1024 * 1024; i++)
array[i] = rand();
while (true)
/* do nothing */;
return 0;
}

코드를 바꿔서 배열의 각 원소에 임의의 값을 집어 넣도록 했더니 실제 물리 메모리 사용량이 1.0g 로 바뀐 것을 볼 수 있다.

PGD = Page Global Directory
PDM = Page Middle Directory
PTE = Page Table Entry
~/mm/memory.c => follow_page_pte()
대부분의 CPU 는 가상주소를 물리주소로 변환해주는 별도의 하드웨어를 장착하고 있고 이를, MMU (Memory management Unit) 이라 부른다. 지원하는 페이지 레벨은 CPU 제조사 별로 다르다.
64 bit CPU 인 경우 3 단계 페이징 기법으로 표현하기에 무리가 있으므로 4 단계 페이징을 사용한다 > 5.10 버전에선 5 단계까지 사용
버디나 슬랩 할당자는 연속적인 메모리 공간을 할당해주는데, 시스템은 연속적인 물리 메모리가 늘 넉넉하진 않다. 따라서 반드시 물리적으로 연속되지 않아도 되는, 가상적으로만 연속인 메모리를 할당해주는 vmalloc() 과 vfree() 를 통해 할당&해제 받을 수 있다.
demand paging)HAT (Hardware Addresss Translation) 또는 MMU (Memory Management Unit)) 를 필요로 한다.[이미지] https://www.programmersought.com/article/55702605527/
[사이트] https://medium.com/@mxatone/kernel-memory-randomization-and-trampoline-page-tables-9f73827270ab
[책] 리눅스 커널: 내부구조 (백승제, 최종무 저)
운영체제 과제를 하는 도중에 많이 도움을 받았습니다 감사합니다 ㅠㅠ