리눅스 메모리 관리 2

EEEFFEE·2023년 12월 15일

raspberrypi4-kernel

목록 보기
7/12

23.12.13 최초 작성

1. 프로세스 주소 공간 관리

  • 커널은 프로세스에 각 영역(코드, 데이터, 스택, 힙)을 할당 함
  • 각 영역에 메모리를 할당하거나 해제
  • 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에서 확인 가능

1.1 관련 자료구조

  • 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을 매핑 한 경우 그 주소
    
    ...
    
}

1.2 관련


//	/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() 호출

1.3 프로세스 생성 과정에서 메모리

1.3.1 fork()의 경우

  • 링크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_structVMA를 새 mm_struct에 복사
            (Copy on Write 사용)

1.3.2 execve()의 경우

  • do_execveat_common() 호출
    • alloc_bprm()을 통해 새로운 새로운 mm_struct, pgd, VMA할당
    • bprm_execve()을 호출
      • sched_exec()을 통해 로드 밸런싱 수행
        • exce_binprm()을 통해 load_elf_binary(), lod_aout_binary()를 호출, elf파일 로딩

1.3.3 mmap()의 경우

  • 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`을 할당할지 결정

2. 가상 메모리 주소 변환

2.1 리눅스의 Page Table

  • cpu 구조에 따라 다르며 주로 5 level page table 사용
  • 컴파일 시 Macro에 의해 level이 변경되기도 함
    • 32 bit : 2 ~ 3 level page table
    • 64 bit : 3 ~ 5 level page table
PGDPUDPMDPTE
Page global directoryPage upper directoryPage middle directoryPage table entry

2.1.1 접근 흐름

  1. mm_struct->pgd의 주소가 가리키는 Page Table에서 PGD 비트를 offset으로 활용 해 참조할 PUD Page Table 탐색
    (mm_struct->pgd은 하나의 Frame으로 구성되며 pgd_t로 이루어진 배열임)
    (PGD Table의 상위 요소들은 kernelPage Table정보를 가지며 swapper_pg_dir를 통해 가상 주소를 변환함)

  2. 이후 하위 페이지에 대해 차례대로 위 과정을 반복하면서 참조할 물리 페이지를 탐색

  • 해당 과정에서 spin lock을 통해 Page Table에 동시에 접근해 수정하는 것을 방지
  • 만약 2 level page table일 경우 PUD/PMD Page Table을 찾는 함수는 PGD Page Table에서 작동하는 함수의 결과값을 반환 함

2.2 Page Table Layout

  • 가상 주소의 비트와 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 */

2.3 PTE 조작 함수


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 비트 제거

2.4 Page Table 할당 및 해제 과정

2.4.1 Last-level Page Table (pte)

  • pte_alloc() : __pte_alloc() 호출
  • __pte_alloc() : pte_alloc_one() 호출
    • pte Page Table에 페이지 할당 (HIGHMEN 영역의 페이지 할당) 및 Page Table 플래그 세팅 해 Page Table로 사용된다는 것 표기
    • pmd Page Tablepagepage table lock을 획득하고 내용 입력

2.4.2 Upper-level Page Table (pmd, pud, p4d)

  • pmd_alloc() : __pmd_alloc() 호출
  • __pmd_alloc() : pmd_alloc_one() 호출
    • pmd Page Table에 페이지 할당
    • mm_struct->page_table_lock을 통해 보호됨

2.4.3 First-level Page Table (pgd)

  • fork() : pgd_alloc() 호출
  • pgd_alloc() : pgd Page Table을 작성
    (나머지 Page TableDemand Paging에 의해 생성되지만 pgd는 프로세스가 생성될 때만 생성됨)

2.5 Translation between Page, Page Frame Number, vaddr, paddr


//	/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))	//가상 메모리 주소로 변환

2.6 TLB Flush

  • context switching발생 시 TLB Flushing (TLB 교체) 발생, 오버헤드 큼
  • 만약 교체될 프로세스와 메모리를 공유 중이라면 TLB Flush 발생하지 않음
  • task_structmm_struct
    • active_mm : current context가 실행될 때 참조하는 주소 공간
  • Lazy TLB Flushing : TLB Flushing을 줄이기 위한 방법
    • kernel_threadcontext switching 발생 시 mm-swithing을 막음
    • 각 코어는 현재 실행 중인 프로세스의 active_mm 기억
      • context switching발생 시 해당 코어의 active_mmTLB Flush 해줘야 하는 mm_struct와 비교해 같다면 TLB Flush, 아니면 그대로

2.7 Kernel Page Table Isolation (KPTI)

  • 커널 공간 -> 유저 공간 전환 시 커널의 흔적이 남아 보안상의 취약사항 발생
  • 유저 공간에서 동작할 때 메모리의 커널 영역을 없애는 방법
  • 유저 공간에서 pgd를 사용하다가 커널 공간에 진입하면 pgd의 영역을 늘리고 이 늘어난 offset만큼 주소에 더해 page translation 수행

3. Demand Paging

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 pageCOW수행, 아니면 SIGSEGV 발생
      • 커널에 page faultvmalloc 영역에 처음 접근할 때이므로 적절한 처리 수행
      • Error발생 하면 유저공간 유발인 경우 SIGSEGV발생

3.1 관련 pte flag/filed


pte_present();								//해당 프레임이 메모리에 존재할 경우 true 반환
pte_none();									//pte 값이 0임을 확인 (새로 mmap되고 접근되지 않은 경우)
PFN											//present인 경우 물리 주소 나타냄
swap-entry									//present아니며 none인 경우(swap out된 상태) swap space 어디에 저장된지 확인
PROT										//WRITE / READ / EXECUTE권한 확인
ACCESSED									//해당 페이지에 접근한지 확인
DIRTY										//해당 페이지에 쓰기 작업이 일어났는지 확인

3.2 Kernel Caches

  • Page cache
    • 파일 별로 존재하며 파일을 page단위로 나눠 시작 주소를 caching
    • 각 파일을 메모리에 caching, address_space라는 tree 자료구조에 저장됨
    • page fault 발생 시 caching되어 있으면 그 주소의 데이터 가져 옴
    • VFS에서도 사용 되며 dirty 페이지가 일정 시간 경과, 또는 일정 갯수 이상이면 kworker_threadwrite-back 수행
  • Buffer Cache
    • 파일을 I/O 단위로 입출력 수행할 때 buffer를 caching
    • I/O block의 크기가 일정 이하일 경우 buffer cache는 VFS에 의해 사용 됨
    • buffer header라는 자료구조로 관리
    • pageI/O blockcaching될 수 있으며 inode 0에 의해 관리 됨
  • Swap Cache
    • swap device로 부터 swap-in되는 pagecaching
    • 여러 프로세스가 page를 공유할 경우 효과적
    • 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-backedSwap-backed
in swap-cache
Swap-backed
not in swap-cache
Free
Page cache의 주소reverse mapping을 위한 anon_vma
Page cache 주소
reverse mapping을 위한 anon_vmaNULL

3.3 Reverse Mapping

  • Page Table entry에 매핑된 page에서 Page Table entry을 매핑하는 것
  • Swapping 동작 시 기존 Page Table entry를 지우고 새로운 정보를 저장하기 위해 필요

3.3.1 Anonymous Page

  • pageanon_vma라는 자료구조로 각 프로세스의 가상 주소를 연결해 해당 Page Table entry를 찾는 방법
  • 최근 VMA_interval_tree (rb tree)를 사용해 관리

3.4 Demand Page 처리 흐름

3.4.1 do_page_fault()

  • page fault를 처리하는 함수로 __do_page_fault()를 호출 해 fault를 유발하는 주소가 1. kernel address, 2. user address인지 판단
  1. do_kern_addr_fault() : 유효한 커널모드에서 접근한건지 판단해 아니면 1.1, 맞으면 1.2

    1.1 유저모드에서 접근했다면 SIGSEGV시그널을 발생시켜 시그널 핸들러 호출, 아니면 fixup exception이 가능한지 검사해 가능하면 fixup handler호출하고 return, 불가능하면 종료
    1.2 유효한 커널모드에서 접근했고 vamlloc faultPage table 갱신하고 return

  2. 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 수행

3.4.2 handle_mm_fault()

  • p4d, pud, pmd가 없다면 할당
  • Page Table entry 할당
  • handle_pte_fault() 호출

3.4.3 handle_pte_fault()

  1. 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)
  2. pte_protnone()을 확인 해 pteprotection field가 0인지 판단, 0이라면 2.1, 아니라면 3으로 진행

    2.1 numa_balancing을 위해 do_numa_page() 수행

  3. write access인지 판단해 맞다면 3.1, 아니면 3.2

    3.1 ptewrite할 수 있는 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로 이동

  4. update_mmu_cache() 호출 해 바뀐 데이터 갱신

3.5 do_fault()

3.5.1 do_read_fault()

  • page fault 발생한 페이지가 file-mapped page이거나 pte_none(페이지 생성 but 내용 없음)일 때 Read-fault 발생했을 때 호출
  1. do_fault_around()를 통해 캐시에 있는지 확인, 있다면 인접한 16개의 page를 찾아 pte에 write하고 return, 없다면 2

  2. do_fault()를 호출 해 vma->vm_ops->fault()filemap_fault() 호출

  3. 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 cachelru list에 페이지 삽입, - do_mpage_readpage()를 통해 page fault 수행

3.5.2 do_cow_fault()

  • page fault 발생한 페이지가 file-mapped page이거나 pte_none(페이지 생성 but 내용 없음)일 때 Write-fault 발생했을 때 호출
  1. alloc_page_vma()를 통해 새 페이지 할당

  2. __do_fault()를 호출해 원래 페이지를 불러오고 copy_user_highpage()를 통해 새 페이지에 복사

  3. finish_fault()do_set_pte()호출 해 pte에 내용 삽입, dirty, writable세팅, anonymous page 설정

  4. lru_cache_add_inactive_or_unevictable()를 통해 page cacheINACTIVE_ANON list에 페이지 삽입

3.5.3 do_shared_fault()

  • page fault 발생한 페이지가 file-mapped page이거나 pte_none(페이지 생성 but 내용 없음)일 때 Write-fault 발생, VM_SHARED 설정되어 있을 때 호출
  1. __do_fault()를 호출해 page cache 확인, 없다면 새 페이지 할당해 page cachelru list에 페이지 삽입, I/O 수행

  2. vma->vm_ops->page_mkwrite() 호출 해 페이지에 write 수행한다는 정보를 파일 시스템에 알림 (write-back을 위해)

  3. finish_fault()처리

  4. fault_dirty_shared_page() 호출 해 페이지에 dirty 세팅, balance_dirty_pages_ratelimited()를 호출 해 dirty page가 많으면 wirte-back을 통해 줄임

3.6 do_anonymous_page()

  • bss/heap/stack영역에서 발생
  1. read 접근, shared zero page (0으로 값이 설정된 페이지, 0 register와 역할이 비슷)에 접근 가능한 경우(my_zero_pfn)

    1.1 ptemy_zero_pfn의 주소 입력 (bss/heap/stack영역은 0으로 세팅되어 있으므로)
    1.2 set_pte_at()을 통해 pte에 삽입

  2. write 접근, shared zero page에 접근 불가한 경우

    2.1 anon_vma_prepare()을 통해 anonymous page 준비 (있다면 가져옴, 없다면 할당)

    2.2 alloc_zeroed_user_highpage_movable()을 통해 0으로 초기화, user공간에서 사용할 HIGHMEMmovable한 페이지 할당

    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 cacheINACTIVE_ANON lru list에 페이지 삽입

    2.7 set_pte_at()을 통해 pte에 삽입

3.7 do_swap_page()

  • swapped-out 된 페이지를 디스크에서 찾아올 때 사용
  • pte_present()를 확인해 존재하지 않고 pte_none()을 확인해 초기화된 상태가 아닐 때 발생
  1. pte_to_swp_entry()를 통해 swap정보를 pte로 부터 읽어 옴

  2. lookup_swap_cache()를 통해 swap cache를 탐색해 찾으면 해당 페이지를 반환, 아니면 2.1

    2.1 swapin_readahead()를 호출 해 fault가 발생한 entry와 주변 페이지 read

  3. 이후 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개일 때)
  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 되었으므로)
  • map_count > 1일 때
  1. set_pte_at()을 통해 pte 세팅
  2. do_page_add_anon_rmap()을 통해 anonymous mapping에 삽입
  3. do_wp_page()를 통해 COW 수행
  4. swap_free()을 통해 해당 swap entry가 사용하지 않도록 함 (swap in 되었으므로)

3.7.1 do_wp_page()

  • COW 수행할 때 swap-fault 발생하는 경우 호출
  • present pagewrite fault발생 시 호출되는 함수

COW condition 수행할 때

  1. my_zero_pfn을 확인해 맞다면 alloc_zeroed_user highpage_movable()을 통해 페이지 할당, 아니면 1.1

    1.1 alloc_page_vma()을 통해 페이지 할당, cow_user_page()을 통해 페이지 내용 복사 수행

  2. mk_pte()수행해 pte 만듬

  3. ptep_clear_flush() 수행

  4. page_add_new_anon_rmap()을 통해 anonymous page 트리에 추가

  5. lru_cache_add_inactive_or_unevictable()수행

  6. set_pte_at()수행

  7. 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()수행

0개의 댓글