23.12.13 최초 작성

page fault를 대응하고 page table을 관리page의 종류file-backed page : 디스크에 저장된 파일에서 읽기 전용으로 읽어 와 따로 swap 동작을 하지 않아도 되는 페이지 (code 영역 등)anonymouse page : 디스크에 매핑되지 않은 페이지로 디스크에 저장할 필요 없음 (stack, heap 영역)swap-backed page : 메모리 영역에서 swap-out되어 디스크에 저장된 페이지Virtual Memory Area (VMA)/proc/PID/maps, /proc/PID/smaps에서 확인 가능mm_struct : task_struct에 포함되며 프로세스의 메모리 주소 정보를 저장vm_area_struct : vma를 관리하는 자료구조// include/linux/mm_types.h
struct mm_struct {
struct {
struct vm_area_struct *mmap; //프로세스의 각 영역 정보를 linked list형태로 저장
struct rb_root mm_rb; //메모리 매핑 정보를 저장하는 tree형태의 자료구조
u64 vmacache_seqnum; //최근 참조했던 메모리 주소 공간 정보 저장
atomic_t mm_users; //user 공간에서 얼마나 연결되어 있는지 나타냄
atomic_t mm_count; //mm_struct가 얼마나 참조되고 있는지 나타냄 (0일시 해당 공간 회수)
pgd_t * pgd; //page table 정보를 저장
spinlock_t page_table_lock; //page table 조작 시 page table 보호하는 스핀 락
struct rw_semaphore mmap_lock; //VMA를 read/write할 때 보호하는 semaphore
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack;
unsigned long arg_start, arg_end, env_start, env_end;
//프로세스 메모리 영역 정보
struct mm_rss_stat rss_stat; //mm_struct에 존재하는 page 갯수
struct linux_binfmt *binfmt; //어떤 bianry format이 해당 주소공간에 load 되었는지 나타 냄
struct file *exe_file; //해당 프로세스가 어떤 파일을 실행하는지 나타 냄
...
}
...
}
// include/linux/mm_types.h
struct vm_area_struct {
/* The first cache line has the info for VMA tree walking. */
unsigned long vm_start; //VMA 시작 주소
unsigned long vm_end; //VMA 마지막 주소
/* linked list of VM areas per task, sorted by address */
struct vm_area_struct *vm_next, *vm_prev; //linked list의 이전, 다음 노드 정보
unsigned long vm_flags; /* Flags, see mm.h. */
struct rb_node vm_rb; //메모리 매핑 정보를 저장하는 tree형태의 자료구조
pgprot_t vm_page_prot; //VMA를 접근하는데 필요한 protection 영역 정보
struct list_head anon_vma_chain; //스택, 힙 영역이 연결 된 linked list
struct anon_vma *anon_vma; //메모리를 해제하고 page table을 초기화 할 때 초기화 할 page table의 주소
/* Function pointers to deal with this struct. */
const struct vm_operations_struct *vm_ops; //file을 매핑 한 경우 open, read, write 작업을 할 때 관련된 함수의 포인터
struct file * vm_file; //file을 매핑 한 경우 그 주소
...
}
// /kernel/fork.c
mm_init(); //mm_struct 초기화
allocate_mm(); //mm_struct를 slab cache로 부터 할당 받음
mm_alloc(); //execve()를 호출할 때 발생, mm_struct 새로 할당, 초기화
dup_mm(); //fork()를 CLONE_VM없이 수행할 때 새로운 mm_struct 생성
copy_mm(); //mm_struct를 복사 (thread를 복사할 경우 부모의 mm_struct를 가리킴)
free_mm(); //mm_struct 해제, slab allocator에 반환
get_task_mm(); //프로세스의 mm_struct를 가져오고 referecn count를 증가시킴
mmput(); //mm_struct를 참조하는 프로세스가 사라질 때 referece count감소시킴, 0이 될 때 vm_area해제 및 mmdrop()호출
mmdrop(); //mm_count가 0이 될 때 free_mm() 호출
copy_process()에서 copy_mm() 호출copy_mm()에서 스레드 생성할 경우 CLONE_VM플래그 확인
mm_struct 공유 (mm_user 카운트 증가)프로세스 생성할 경우
dup_mm()을 호출 해 새로운 mm_struct 생성dup_mm()에서 allocate_mm() 호출 해 mm_struct 생성mm_init()을 통해 초기화, mm_alloc_pgd를 호출해 page table 할당dup_mmap()호출 해 기존 mm_struct의 VMA를 새 mm_struct에 복사Copy on Write 사용)do_execveat_common() 호출alloc_bprm()을 통해 새로운 새로운 mm_struct, pgd, VMA할당bprm_execve()을 호출sched_exec()을 통해 로드 밸런싱 수행exce_binprm()을 통해 load_elf_binary(), lod_aout_binary()를 호출, elf파일 로딩vm_mmap_pgoff() 호출do_mmap() 호출 해 get_unmapped_area()을 통해 공간 할당 받고 mmap_region() 수행unmap_vma_region(), vma_merge() : 만약 인접 영역과
vm_area_alloc() : 새 VMA 할당
- call_mmap() : file-backed mapping인 경우 추가적인 동작 연결
vma_link() rb tree에 연결
- `mm_populate()`호출 해 생성된 `VMA`에 실제 `frame`을 할당할지 결정
5 level page table 사용 32 bit : 2 ~ 3 level page table 64 bit : 3 ~ 5 level page table| PGD | PUD | PMD | PTE |
|---|---|---|---|
| Page global directory | Page upper directory | Page middle directory | Page table entry |

mm_struct->pgd의 주소가 가리키는 Page Table에서 PGD 비트를 offset으로 활용 해 참조할 PUD Page Table 탐색
(mm_struct->pgd은 하나의 Frame으로 구성되며 pgd_t로 이루어진 배열임)
(PGD Table의 상위 요소들은 kernel의 Page Table정보를 가지며 swapper_pg_dir를 통해 가상 주소를 변환함)
이후 하위 페이지에 대해 차례대로 위 과정을 반복하면서 참조할 물리 페이지를 탐색
spin lock을 통해 Page Table에 동시에 접근해 수정하는 것을 방지2 level page table일 경우 PUD/PMD Page Table을 찾는 함수는 PGD Page Table에서 작동하는 함수의 결과값을 반환 함 Page Table의 속성을 정의해 놓은 것// /arch/arm64/include/asm64/pgtable-types.h
typedef u64 pteval_t;
typedef u64 pmdval_t;
typedef u64 pudval_t;
typedef u64 p4dval_t;
typedef u64 pgdval_t;
// /arch/x86/include/asm/pgtable_types.h
// arm64의 경우 /arch/arm/include/asm/pgtable.h
#define _PAGE_BIT_PRESENT 0 /* is present */
#define _PAGE_BIT_RW 1 /* writeable */
#define _PAGE_BIT_USER 2 /* userspace addressable */
#define _PAGE_BIT_PWT 3 /* page write through */
#define _PAGE_BIT_PCD 4 /* page cache disabled */
#define _PAGE_BIT_ACCESSED 5 /* was accessed (raised by CPU) */
#define _PAGE_BIT_DIRTY 6 /* was written to (raised by CPU) */
#define _PAGE_BIT_PSE 7 /* 4 MB (or 2MB) page */
#define _PAGE_BIT_PAT 7 /* on 4KB pages */
#define _PAGE_BIT_GLOBAL 8 /* Global TLB entry PPro+ */
#define _PAGE_BIT_SOFTW1 9 /* available for programmer */
#define _PAGE_BIT_SOFTW2 10 /* " */
#define _PAGE_BIT_SOFTW3 11 /* " */
#define _PAGE_BIT_PAT_LARGE 12 /* On 2MB or 1GB pages */
#define _PAGE_BIT_SOFTW4 58 /* available for programmer */
#define _PAGE_BIT_PKEY_BIT0 59 /* Protection Keys, bit 1/4 */
#define _PAGE_BIT_PKEY_BIT1 60 /* Protection Keys, bit 2/4 */
#define _PAGE_BIT_PKEY_BIT2 61 /* Protection Keys, bit 3/4 */
#define _PAGE_BIT_PKEY_BIT3 62 /* Protection Keys, bit 4/4 */
#define _PAGE_BIT_NX 63 /* No execute: only valid after cpuid check */
mk_pte(page, pgprot); //페이지와 protection bit 정보를 조합해 pte 제작
pte_page(pte); //pte를 확인해 대응되는 페이지 찾음
pmd_page(pmd); //pmd를 확인해 대응되는 페이지 찾음
set_pte_at(mm, addr, ptep, pte); //pte값을 ptep가 가르키는 곳에 저장
pte_clear(); //해당 pte 초기화
pmd_clear();
pgd_clear();
ptep_get_and_clear(mm, addr, ptep); //해당 ptep가 가르키는 pte를 찾고 clear
ptep_clear_flush(vma, addr, ptep); //해당 ptep가 가르키는 pte를 clear하고 flush
pte_none(); //해당 entry에 내용이 없을 경우 true 반환
pmd_none();
pgd_none();
pte_present () //해당 entry에 present bit가 세팅된 경우 true 반환
pmd_present ()
pgd_present ()
pte_dirty() //dirty 비트 확인
pte_young() //young 비트(최근 접근되었는지) 확인
pte_mkdirty() //dirty 비트 세팅
pte_mkyoung() //young 비트 세팅
pte_mkclean() //dirty 비트 제거
pte_mkold() //young 비트 제거
pte_alloc() : __pte_alloc() 호출__pte_alloc() : pte_alloc_one() 호출 pte Page Table에 페이지 할당 (HIGHMEN 영역의 페이지 할당) 및 Page Table 플래그 세팅 해 Page Table로 사용된다는 것 표기pmd Page Table의 page에 page table lock을 획득하고 내용 입력pmd_alloc() : __pmd_alloc() 호출__pmd_alloc() : pmd_alloc_one() 호출 pmd Page Table에 페이지 할당 mm_struct->page_table_lock을 통해 보호됨fork() : pgd_alloc() 호출pgd_alloc() : pgd Page Table을 작성Page Table은 Demand Paging에 의해 생성되지만 pgd는 프로세스가 생성될 때만 생성됨)// /include/asm-generic/memory_model.h
#define __pfn_to_page(pfn) (vmemmap + (pfn)) //PFN to Page
#define __page_to_pfn(page) (unsigned long)((page) - vmemmap) //Page to PFN
// /arch/x86/include/asm/page.h
#define __pa(x) __phys_addr((unsigned long)(x)) //물리 메모리 주소로 변환
#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET)) //가상 메모리 주소로 변환
context switching발생 시 TLB Flushing (TLB 교체) 발생, 오버헤드 큼TLB Flush 발생하지 않음 task_struct의 mm_struct active_mm : current context가 실행될 때 참조하는 주소 공간Lazy TLB Flushing : TLB Flushing을 줄이기 위한 방법kernel_thread로 context switching 발생 시 mm-swithing을 막음active_mm 기억context switching발생 시 해당 코어의 active_mm이 TLB Flush 해줘야 하는 mm_struct와 비교해 같다면 TLB Flush, 아니면 그대로 pgd를 사용하다가 커널 공간에 진입하면 pgd의 영역을 늘리고 이 늘어난 offset만큼 주소에 더해 page translation 수행fault-page loading
File backed page : 페이지 캐시를 확인하고 없으면 I/O 수행
Swap-out page : 페이지 캐시를 확인하고 없으면 I/O 수행
Anonymous page : 0으로 초기화된 페이지 할당
주로 read-ahead(Major fault, Minor fault로 나눔) 기법 사용
Major fault : page cache에 없어 실제 I/O를 수행
Minor fault : page cache에 있어 I/O 필요 없음
page 할당이 이루어지지 않은 상태(Anonymous)일 경우 0으로 초기화된 페이지 할당file, swap-backed인데 page/swap cache에 존재하면 cache에서 인출write작업할 때 COW page면 COW수행, 아니면 SIGSEGV 발생page fault는 vmalloc 영역에 처음 접근할 때이므로 적절한 처리 수행Error발생 하면 유저공간 유발인 경우 SIGSEGV발생pte_present(); //해당 프레임이 메모리에 존재할 경우 true 반환
pte_none(); //pte 값이 0임을 확인 (새로 mmap되고 접근되지 않은 경우)
PFN //present인 경우 물리 주소 나타냄
swap-entry //present아니며 none인 경우(swap out된 상태) swap space 어디에 저장된지 확인
PROT //WRITE / READ / EXECUTE권한 확인
ACCESSED //해당 페이지에 접근한지 확인
DIRTY //해당 페이지에 쓰기 작업이 일어났는지 확인
Page cache page단위로 나눠 시작 주소를 cachingcaching, address_space라는 tree 자료구조에 저장됨page fault 발생 시 caching되어 있으면 그 주소의 데이터 가져 옴VFS에서도 사용 되며 dirty 페이지가 일정 시간 경과, 또는 일정 갯수 이상이면 kworker_thread가 write-back 수행Buffer CachecachingI/O block의 크기가 일정 이하일 경우 buffer cache는 VFS에 의해 사용 됨buffer header라는 자료구조로 관리page에 I/O block이 caching될 수 있으며 inode 0에 의해 관리 됨Swap Cacheswap device로 부터 swap-in되는 page를 cachingpage를 공유할 경우 효과적write, single mapped pte일 경우 caching 해제// /include/linux/mm_types.h
struct page {
unsigned long flags; //page의 플래그 설정
union {
struct { /* Page cache and anonymous pages */
struct list_head lru; //lru list에 연결될 때 사용되는 자료구조
struct address_space *mapping; //페이지 종류에 따라 역할 다름
pgoff_t index; //mapping의 offset
unsigned long private; //swap-cache일 경우 swap device의 block number를 저장
...
}
...
}
...
}
mapping 역할
| File-backed | Swap-backed in swap-cache | Swap-backed not in swap-cache | Free |
|---|---|---|---|
Page cache의 주소 | reverse mapping을 위한 anon_vma Page cache 주소 | reverse mapping을 위한 anon_vma | NULL |
Page Table entry에 매핑된 page에서 Page Table entry을 매핑하는 것Swapping 동작 시 기존 Page Table entry를 지우고 새로운 정보를 저장하기 위해 필요page의 anon_vma라는 자료구조로 각 프로세스의 가상 주소를 연결해 해당 Page Table entry를 찾는 방법VMA_interval_tree (rb tree)를 사용해 관리page fault를 처리하는 함수로 __do_page_fault()를 호출 해 fault를 유발하는 주소가 1. kernel address, 2. user address인지 판단do_kern_addr_fault() : 유효한 커널모드에서 접근한건지 판단해 아니면 1.1, 맞으면 1.2
1.1 유저모드에서 접근했다면 SIGSEGV시그널을 발생시켜 시그널 핸들러 호출, 아니면 fixup exception이 가능한지 검사해 가능하면 fixup handler호출하고 return, 불가능하면 종료
1.2 유효한 커널모드에서 접근했고 vamlloc fault면 Page table 갱신하고 return
do_user_addr_fault() : interrupt context || 스레드가 아님을 판단해 맞다면 1.1로 분기, 아니면 2.1로 분기
2.1 VMA를 찾고 유효한 영역이라면 2.2, 아니면 2.1.1
2.1.1 stack영역이 더 늘어날 수 있다면 `2.2` 아니면 `fixup exception` 가능한지 검사해 가능하면 `fixup handler`호출, 불가능하면 종료
2.2 정상접근이라면 handle_mm_fault()를 통해 Demand Paging 수행
handle_mm_fault()p4d, pud, pmd가 없다면 할당Page Table entry 할당handle_pte_fault() 호출handle_pte_fault()pte_none()을 통해 pte가 초기화된 상태 그대로인지 확인, 맞다면 1.1, 아니면 1.2
1.1 vma->vma_ops라는 함수 포인터의 내용이 있는지 확인해 있다면 1.1.1 없다면 1.1.2
1.1.1 do_fault() 호출 해 vma->vma_ops에 등록된 함수 실행 (cold miss)
1.1.2 do_anonymous_page() 호출 해 0으로 초기화 된 페이지 할당해 Page Table과 연결 (cold miss : 해당 메모리 주소를 최초로 호출할 때 발생)
1.2 pte_present()을 통해 존재하고 있다면 1.2.1, 없다면 1.2.2
1.2.1 프레임이 존재하며 접근 권한으로 인한 fault므로 2로 진행해 protection fault handle
1.2.2 해당 frame은 swap-out된 상태이므로 do_swap_page() 호출 해 불러 옴 (cold miss)
pte_protnone()을 확인 해 pte의 protection field가 0인지 판단, 0이라면 2.1, 아니라면 3으로 진행
2.1 numa_balancing을 위해 do_numa_page() 수행
write access인지 판단해 맞다면 3.1, 아니면 3.2
3.1 pte에 write할 수 있는 flag가 없다면 3.1.1 아니면 3.1.2
- 3.1.1 vma에 허용하지만 write할 수 없는 상태이므로 do_wp_page()를 통해 COW 수행하고 `pte_mkyoung()`을 통해 접근했다는 표시 기록
- 3.1.2 pte에 dirty를 세팅하고 `pte_mkyoung()`을 통해 접근했다는 표시 기록
3.2 read, execute일 경우 pte_mkyoung()을 통해 해당 주소에 접근했다는 표시 기록, 4로 이동
update_mmu_cache() 호출 해 바뀐 데이터 갱신
page fault 발생한 페이지가 file-mapped page이거나 pte_none(페이지 생성 but 내용 없음)일 때 Read-fault 발생했을 때 호출 do_fault_around()를 통해 캐시에 있는지 확인, 있다면 인접한 16개의 page를 찾아 pte에 write하고 return, 없다면 2로
do_fault()를 호출 해 vma->vm_ops->fault()의 filemap_fault() 호출
filemap_fault()실행 시 page cache에 페이지 존재하는지 확인, 찾으면 3.1, 아니면 3.2
3.1 do_async_mmap_readahead()호출 해 찾은 페이지 I/O를 수행하지 않고 return
3.2 do_sync_mmap_readahead() 호출 해 I/O 수행, 새 페이지 할당하고 add_to_page_cache_lru()를 통해 page cache와 lru list에 페이지 삽입, - do_mpage_readpage()를 통해 page fault 수행
page fault 발생한 페이지가 file-mapped page이거나 pte_none(페이지 생성 but 내용 없음)일 때 Write-fault 발생했을 때 호출 alloc_page_vma()를 통해 새 페이지 할당
__do_fault()를 호출해 원래 페이지를 불러오고 copy_user_highpage()를 통해 새 페이지에 복사
finish_fault()가 do_set_pte()호출 해 pte에 내용 삽입, dirty, writable세팅, anonymous page 설정
lru_cache_add_inactive_or_unevictable()를 통해 page cache와 INACTIVE_ANON list에 페이지 삽입
page fault 발생한 페이지가 file-mapped page이거나 pte_none(페이지 생성 but 내용 없음)일 때 Write-fault 발생, VM_SHARED 설정되어 있을 때 호출__do_fault()를 호출해 page cache 확인, 없다면 새 페이지 할당해 page cache와 lru list에 페이지 삽입, I/O 수행
vma->vm_ops->page_mkwrite() 호출 해 페이지에 write 수행한다는 정보를 파일 시스템에 알림 (write-back을 위해)
finish_fault()처리
fault_dirty_shared_page() 호출 해 페이지에 dirty 세팅, balance_dirty_pages_ratelimited()를 호출 해 dirty page가 많으면 wirte-back을 통해 줄임
bss/heap/stack영역에서 발생read 접근, shared zero page (0으로 값이 설정된 페이지, 0 register와 역할이 비슷)에 접근 가능한 경우(my_zero_pfn)
1.1 pte에 my_zero_pfn의 주소 입력 (bss/heap/stack영역은 0으로 세팅되어 있으므로)
1.2 set_pte_at()을 통해 pte에 삽입
write 접근, shared zero page에 접근 불가한 경우
2.1 anon_vma_prepare()을 통해 anonymous page 준비 (있다면 가져옴, 없다면 할당)
2.2 alloc_zeroed_user_highpage_movable()을 통해 0으로 초기화, user공간에서 사용할 HIGHMEM의 movable한 페이지 할당
2.3 __SetPageUptodate()을 통해 페이지 정보 업데이트
2.4 mk_pte()을 통해 vma->vm_page_prot정보를 바탕으로 pte 설정, pte_mkwrite()을 통해 write 가능하도록 설정, dirty 세팅
2.5 page_add_new_anon_rmap()을 통해 anonymous mapp에 삽입
2.6 lru_cache_add_inactive_or_unevictable()을 통해 page cache와 INACTIVE_ANON lru list에 페이지 삽입
2.7 set_pte_at()을 통해 pte에 삽입
swapped-out 된 페이지를 디스크에서 찾아올 때 사용pte_present()를 확인해 존재하지 않고 pte_none()을 확인해 초기화된 상태가 아닐 때 발생pte_to_swp_entry()를 통해 swap정보를 pte로 부터 읽어 옴
lookup_swap_cache()를 통해 swap cache를 탐색해 찾으면 해당 페이지를 반환, 아니면 2.1로
2.1 swapin_readahead()를 호출 해 fault가 발생한 entry와 주변 페이지 read
이후 fault종류에 따라 동작
read fault
1. set_pte_at()을 통해 pte 세팅
2. do_page_add_anon_rmap()을 통해 anonymous mapping에 삽입
3. swap_free()을 통해 해당 swap entry가 사용하지 않도록 함 (swap in 되었으므로)
write fault
map_count = 1일 때 (해당 영역을 참조하는 프로세스가 1개일 때)maybe_mkwrite()을 통해 해당 swap cache page 재사용, swap cache에서 페이지 해제set_pte_at()을 통해 pte 세팅 do_page_add_anon_rmap()을 통해 anonymous mapping에 삽입swap_free()을 통해 해당 swap entry가 사용하지 않도록 함 (swap in 되었으므로)map_count > 1일 때set_pte_at()을 통해 pte 세팅 do_page_add_anon_rmap()을 통해 anonymous mapping에 삽입do_wp_page()를 통해 COW 수행swap_free()을 통해 해당 swap entry가 사용하지 않도록 함 (swap in 되었으므로)present page에 write fault발생 시 호출되는 함수COW condition 수행할 때
my_zero_pfn을 확인해 맞다면 alloc_zeroed_user highpage_movable()을 통해 페이지 할당, 아니면 1.1로
1.1 alloc_page_vma()을 통해 페이지 할당, cow_user_page()을 통해 페이지 내용 복사 수행
mk_pte()수행해 pte 만듬
ptep_clear_flush() 수행
page_add_new_anon_rmap()을 통해 anonymous page 트리에 추가
lru_cache_add_inactive_or_unevictable()수행
set_pte_at()수행
update_mmu_cache()을 통해 MMU cache 갱신
mapcount == 1일 때
1. maybe_mkwrite()을 통해 해당 swap cache page 재사용, swap cache에서 페이지 해제
2. set_pte_at()을 통해 pte 세팅
3. do_page_add_anon_rmap()을 통해 anonymous mapping에 삽입
4. swap_free()을 통해 해당 swap entry가 사용하지 않도록 함 (swap in 되었으므로)
VM_WRITE|VM_SHARED 플래그 설정된 경우
do_page_mkwrite()을 통해 파일 시스템에 해당 페이지 쓰기 작업 수행되었다는 것 알림do_page_mkwrite()없을 때 wp_page_reuse()수행