리눅스 커널의 이해 중에서 16장 스와핑 : 메모리 해제 방법 최성자 소프트웨어공학 실험실
목차 Part I : 스와핑 개념 Part II : 스와핑시 고려사항 Part III : 스왑영역 개념 Part IV : 스왑캐시 Part IV : 스왑페이지 전송 Part V : 페이지 스왑아웃 및 스왑인 알고리즘 Part VI : 페이지 프레임 해제 Part VII : 요약
스와핑이란 무엇인가? [정 의] 스왑(Swap) 스와핑(Swapping) [목 적] 메모리 내의 데이터 페이지 또는 세그먼트를 교체하는 것 스와핑(Swapping) 컴퓨터가 주기억장치보다 더 큰 프로그램이나 데이터 파일을 다룰 수 있게 하는 유용한 테크닉 운영체제는 주기억장치 내에 가능한 한 많은 데이터를 복사해 놓고, 나머지는 디스크 상에 저장되어 있음 운영체제가 디스크에 있는 데이터를 필요로할 때, 주기억장치 내의 데이터 일부를 디스크 상의 데이터와 교체 DOS는 스왑핑을 수행하지 않음 윈도우,유닉스 등을 포함한 많은 운영체계들이 스왑핑을 수행 [목 적] 프로세스가 실제 사용가능한 주소 공간 확장 프로세스를 적재할 동적 램 공간 확장 (커널코드와 정적 자료구조 초기화공간 제외)
스와핑의 고려 조건 [스왑 아웃할 페이지 종류] 스와핑 가능한 페이지 종류 [스왑 영역 내 페이지 분산 방법] 프로세스의 익명 메모리 영역에 속한 페이지 프로세스의 비공개 메모리 매핑에 속한 변경 페이지 IPC 공유 메모리 영역에 속한 페이지 [스왑 영역 내 페이지 분산 방법] 스왑영역 단위 슬롯, 슬롯 = 한페이지 디스크 탐색 시간 최소화위해 연속 슬롯에 페이지 저장 -> 효율적인 스왑 알고리즘 필요 하나이상의 스왑영역 사용 경우 높은 우선순위의 스왑영역부터 시작 같은 우선순위 경우 번갈아 가면서 선택 다음 우선순위의 스왑영역 탐색
스와핑의 고려 조건 – 1 [스왑 아웃될 페이지 선택 방법] 스왑아웃의 일반규칙 [스왑 아웃될 페이지 선택 방법] 스왑아웃의 일반규칙 가장 많은 페이지를 소유한 프로세스에서 페이지를 빼앗는 정책 스왑아웃을 위해 어떤 페이지 선택할지 결정 LRU (Least Recently Used) 교체 알고리즘 램의 각 페이지에 페이지 나이(age) 적용 해당 페이지에 마지막 접근한 이후 경과 시간 저장 가장 오래된 페이지 스왑아웃 -- 각 페이지 테이블 엔트리 Accessed 플래그 활용 [페이지 스왑 아웃을 수행할 시기] 스왑아웃 실행 경우 빈 페이지 프레임이 미리 정의한 기준치보다 작아질 때마다 1초 간격으로 수행하는 kswapd라는 커널 쓰레드 통해 버디 시스템에 대한 메모리 요청을 만족하지 못하는경우
페이지 폴트 핸들러는 페이지를 반드시 스왑인해야한다 스왑영역 메모리에서 스왑아웃된 페이지 -> 스왑 영역에 저장 페이지 폴트 핸들러는 페이지를 반드시 스왑인해야한다 페이지를 반드시 스왑아웃해야 한다 do_swap_page() swap_out() swap_in() swap_out_process() swap_out_vma() swapin_readahead() swap_out_pgd() read_swap_cache_async() swap_out_pmd() try_to_swap_out() rw_swap_page() rw_swap_page() rw_swap_page() 저수준 스와핑 함수 brw_page() 블록 장치 드라이버 함수 [그림 16-1]스와핑과 관련한 주요함수
스왑영역 - 1 [스왑 영역 디스크립터] 활성화한 각 스왑 영역 자신의 swap_info_struct 디스크립터를 메모리에 저장 (swap.h 에서 확인) [표 16-1]스왑영역 디스크립터 필드참조 유 형 필 드 설 명 unsigned int kdev_t struct dentry* unsigned short unsigned char* unsigned int unsigned int unsigned int unsigned int int int unsinged long int flags swap_device swap_file swap_map swap_lockmap lowest_bit hightest_bit cluster_next cluster_nr prio pages max next 스왑 영역 플래그 스왑장치의 장치번호 파일 또는 장치 파일의 디엔트리 각 스왑 영역 페이지 슬록마다 하나씩 할당되는 카운터 배열에 대한 포인터 각 스왑 영역 페이지 슬롯마다 하나식 할당되는 비트락의 배열 포인터 빈 슬롯을 찾을 때 살펴볼 처음 페이지 슬롯 빈 슬롯을 찾을 때 살펴볼 마지막 페이지 슬롯 빈 슬롯을 찾을 때 살펴볼 다음 페이지 슬롯 처음부터 다시 시작하기 전에 비어 있는 페이지 슬롯할당 개수 스왑 영역 우선 순위 사용 가능한 페이지 슬롯 개수 스왑 영역 크기(페이지 단위) 다음 스왑 영역 디스크립터에 대한 포인터
swap_device 또는 swap_file swap_map 스왑영역 - 2 [스왑 영역 디스크립터] 결함이 있는 페이지 슬롯 빈 페이지 슬롯 할당된 페이지 슬롯 swap_info 스왑 영역 swap_device 또는 swap_file swap_map 0 1 00 2 1 0 32768 카운터 배열 스왑영역 디스크립터 [그림 16-2] 스왑영역의 자료구조
스왑영역 - 3 [스왑 아웃된 페이지 식별자] 스왑아웃된 페이지 : swap_info배열의 스왑영역 인덱스, 페이지 슬롯 인덱스 지정하여 식별 31 8 7 2 1 0 페이지 슬롯 인덱스 영역번호 0 0 [그림 16-3] 스왑아웃된 페이지 식별자
스왑영역 - 4 [스왑 영역의 활성화와 비활성화] 스왑아웃된 페이지 : swap_info배열의 스왑영역 인덱스, 페이지 슬롯 인덱스 지정하여 식별 /bin/swapon : 스왑 영역을 활성화 /bin/swapoff : 스왑 영역을 비활성화 sys_swapon() 서비스 루틴 { specialfile : 장치 파일(파티션) / 스왑 영역을 구현하기 위해 사용한 일반파일 경로명 포함 swap_flags : SWAP_FLAG_PREFER비트를 1로 설정, 하위 15비트는 스왑 영역의 우선순위 스왑 영역이 생성될 때 첫째 슬롯에 들어간 swap_header 공용체 필드 검사 }
스왑영역 - 5 [스왑 영역의 활성화와 비활성화] sys_swapoff() 서비스 루틴 { specialfile 매개변수가 지정하는 스왑 영역을 비활성화 스왑영역을 탐색하여 모든 페이지 스왑인 더 이상 남아있는 페이지 프레임이 없을 경우, 에러 코드 반환 }
스왑영역 - 6 [빈페이지 슬롯 찾기] 스왑영역 접근시 디스크 탐색시간 최소화 swap_info_struct [빈페이지 슬롯 찾기] 스왑영역 접근시 디스크 탐색시간 최소화 연속하는 페이지 슬롯에 저장 리눅스 마지막 할당된 페이지 슬롯에서 시작 아래의 경우 제외 - 스왑 영역 끝에 도달했을 때 - 스왑 영역의 시작부터 마지막까지 재시작한 후 빈 페이지 슬롯을 SWAPFILE_CLUSTER(256개) 할당 swap_info_struct - cluster_nr 필드 : 빈페이지 슬롯 할당한 수 저장 - cluster_next 필드 : 다음 할당에서 검사할 첫째 페이지 슬롯의 인덱스 저장
스왑영역 - 7 [빈페이지 슬롯 찾기] 빈페이지 슬롯을 찾는 속도를 높이기 위해 [빈페이지 슬롯 찾기] 빈페이지 슬롯을 찾는 속도를 높이기 위해 커널은 각 스왑 영역 디스크립터의 lowest_bit와 highest_bit 필드를 최신상태로 유지 비어있을 수 있는 가장 처음과 마지막 페이지 슬롯 즉, lowest_bit아래와 highest_bit위의 모든 페이지 슬롯 할당 되었음을 의미 Scan_swap_map() { 빈 페이지 슬롯을 찾는 함수 }
페이지 슬롯의 할당과 해제 페이지 슬롯의 할당 절차 1 : get_swap_page()는 새롭게 할당한 페이지 슬롯의 인덱스를 반환하거나 모든 스왑 영역이 꽉 차 있으면 0을 반환 2 : swap_list.next가 가리키는 스왑 영역으로부터 검토 시작 3 : scan_swap_map()을 호출하여 빈 페이지 슬롯을 할당 다음 스왑 영역의 우선순위가 같으면 swap_list.next를 스왑 영역 리스트의 다음 스왑 영역을 가리키도록 변경 해당 페이지 슬롯에 대한 식별자를 반환하면서 종료 4 : 스왑 영역에 쓰기 금지 상태이거나 빈 페이지 슬롯이 없는 경우, 다음 스왑 영역과 현재 스왑 영역의 우선 순위가 같다면 다음 스왑 영역을 현재 영역으로 설정하고 3단계로 이동 5 : 첫째 단계(부분적)인 경우, 리스트 내 첫째 스왑 영역을 대상으로 하고 3단계로 이동 후 둘째 단계 시작 그렇지 않으면, 리스트에 다음 항목이 있는지 검사하고 3단계로 이동 6 : 둘째 단계로 모든 리스트를 검색하였는데 빈 페이지 슬롯을 발견 못한 경우 0을 반환
페이지 슬롯의 할당과 해제 - 1 페이지 슬롯의 해제 절차 1 : entry에서 스왑 영역 인덱스와 페이지 슬롯 인덱스 획득 swap_free() 페이지를 스왑인할 때 swap_map 카운터를 감소시키는 사용 1 : entry에서 스왑 영역 인덱스와 페이지 슬롯 인덱스 획득 2 : 스왑 영역이 활성화 상태인지 검사하여 그렇지 않은 경우 반환 3 : swap_list.next가 가리키는 스왑 영역의 우선 순위보다 스왑 영역이 우선 순위가 높으면 swap_list.next를 swap_list.head로 설정하여 빈 페이지 슬롯을 찾은 다음 탐색을 가장 높은 우선 순위의 스왑 영역서 시작 4 : 해제 중인 페이지 슬롯에 대한 swap_map 카운터가 SWAP_MAP_MAX보다 작으면 카운터를 감소 5 : swap_map 카운터가 0이 되면 nr_swap_pages를 증가시키고 필요하다면 스왑 영역 디스크립터의 lowest_bit과 highest_bit 필드를 변경
스왑캐시 페이지 프레임을 여러 프로세스가 공유 공유 페이지 스왑캐시 사용 목적 커널 각 프로세스 독립적으로 다루기 때문에 프로세스 A와 B가 공유하는 페이지는 A의 주소공간에서 스왑 아웃되었어도 B의 주소공간에 계속 존재하므로 이를 처리 하기위해 스왑영역에 복사한 모든 공유 페이지 프레임을 모아 놓은 스왑캐시 사용 공유 페이지 여러 프로세스가 함께 사용한 프로세스 주소공간에서 스왑아웃 되었더라도 다른 프로세스에서 계속 존재(사용)할 수 있음 스왑캐시 사용 목적 공유페이지를 여러 프로세스들이 공유하기 때문에 일반적인 스와핑으로는 어떤 프로세스들이 공유했었는지를 알 수 없음 최악으로는 모든 프로세스에 있는 페이지 테이블 엔트리를 검색해야 하는 경우도 발생할 수 있음
스왑캐시 - 1 리눅스에서 페이지 프레임을 여러 프로세스가 공유할 수 있는 경우 페이지 프레임이 ‘공유(shared)’ 메모리 매핑과 관련한 경우(15장 메모리 매핑 참고) 새로운 프로세스를 포크(fork)한 경우처럼, Copy On Write로 페이지 프레임을 처리하는 경우(7장 참고) 페이지 프레임을 IPC(Inter-Process Communication) 공유 메모리 자원에 할당한 경우(18장 참고)
공유된 페이지 스와핑 환경 A A B B P P (2) (2) (a) (b) 두 프로세스 A와 B가 페이지 P를 공유 프로세스 A의 주소공간에서 P를 스왑아웃을 수행할 대상으로 선택했다고 가정 스왑영역 스왑영역 (2) A A 스왑 캐시 스왑 캐시 B B P P (2) (2) (a) (b)
공유된 페이지 스와핑-1 A A B B P (1) (c) (d) (3) (2) [그림 16-4] 스왑 캐시의 역할 스왑영역
스왑 캐시 처리 함수 in_swap_cache() lookup_swap_cache() add_to_swap_cache() 페이지가 스왑캐시에 속하는지 결정하기 위하여 페이지의 PG_swap_cache 플래그를 검사 스왑캐시에 페이지가 있으면 offset 필드에 저장된 스왑 아웃된 페이지 식별자를 반환 lookup_swap_cache() 매개변수로 전달한 스왑 아웃된 페이지 식별자에 대하여 페이지 주소를 반환하거나 페이지가 개시에 없으면 0을 반환 add_to_swap_cache() 페이지를 스왑 캐시에 저장 가상적인 swapper_inode 객체 주소와 스왑 아웃된 페이지 식별자로 페이지 디스크립터 inode와 오프셋 필드를 설정
스왑 캐시 처리 함수-1 is_page_shared() delete_from_swap_cache() 여러 프로세스가 페이지를 공유하는지 검사 공유하면 1(참), 그렇지 않으면 0(거짓)을 반환 delete_from_swap_cache() PG_swap_cache 플래그를 0으로 하고 remove_inode_page()를 호출하여 페이지를 스왑 캐시에서 제거 swap_free()를 호출하여 대응하는 페이지 슬롯을 해제 free_page_and_swap_cache() __free_pager()를 호출하여 페이지를 해제 페이지가 스왑캐시에 있는지(PG_swap_chche 플래그 = 1), 그 중 프로세스 하나의 소유하고 있는지(is_page_shared = 0) 검사 두 조건을 만족하면 delete_from_swap_cache()를 호출하여 페이지를 스왑캐시에서 제거하고 대응하는 페이지 슬롯을 비움
스왑페이지전송 스왑 페이지 전송시 주기적으로 검사할 사항 동기화 메커니즘 페이지 프레임과 페이지 슬롯의 락킹 비동기적 스왑 연산이 진행될 수 있는 경우, 동기적 입출력 연산만 시작 어떤 페이지의 스왑 인 또는 스왑 아웃 도중에 페이지를 소유한 프로세스가 종료하는 경우 프로세스가 스왑 아웃을 시도하는 중에 다른 프로세스가 그 페이지를 스왑인 하려는 경우 동기화 메커니즘 같은 페이지 프레임이나 같은 페이지 슬롯에 대해 입출력 연산이 동시에 이루어질 때, 발생할 수 있는 데이터 손상을 방지하기 위한 메커니즘 페이지 프레임과 페이지 슬롯의 락킹 스왑 페이지에 대한 입출력 데이터 전송 : 동기화를 위한 블록킹 연산 필요
페이지 프레임과 페이지 슬롯의 락킹 락킹 개요 페이지 슬롯 상태 Kernel은 같은 페이지 프레임, 같은 페이지 슬롯 또는 둘 모두가 관련된 동시 전송을 허락하지 않음 이를 통하여 페이지 프레임에 대한 경쟁현상(Race Conditions)을 피할 수 있음 페이지 프레임에 대한 입출력 연산을 선조건 wait_on_page()를 호출하여 PG_locked 플래그가 해제될 때까지 기다림 페이지 프레임 락을 획득한 경우, 다른 커널 제어 경로는 입출력 연산동안 이 페이지 프레임의 내용에 접근할 수 없음 페이지 슬롯 상태 입출력 데이터 전송과 관련하여 추적 가능해야 함 스왑 페이지에서 입출력 연산 전 수행 내용 커널은 관련된 페이지 프레임이 스왑캐시에 들어있는지 검사하여, 없는 경우에 페이지 프레임을 스왑 캐시에 추가
페이지 프레임과 페이지 슬롯의 락킹-1 PG_locked 플래그 한계 해결 방법 어떤 페이지의 스왑 인 또는 스왑 아웃 도중에 페이지를 소유한 프로세스가 종료하는 경우 프로세스가 스왑 아웃을 시도하는 중에 다른 프로세스가 그 페이지를 스왑인 하려는 경우 해결 방법 이러한 문제 방지를 위하여 커널은 비트 배열을 사용 비트 배열 스왑 영역의 각 페이지 슬롯에 대한 락(lock) 스왑 영역 디스크립터의 swap_lockmap 필드에 이 배열의 주소를 저장 페이지 슬롯에 대한 입출력 데이터를 활성화하기 전에 이 비트를 설정, 연산 후 해제 scan_swap_map()를 이용하여 ‘빈 페이지 슬롯찾기’를 하는 경우, 락 비트가 ‘1’이면 swap_map 배열이 사용카운터가 ‘0’이라고 하더라도 페이지 슬롯이 비어있다고 생각 안함
스와핑 관련 함수들 – rw_swap_page() 페이지를 스왑 인 또는 스왑 아웃 수행 매개변수 buffer 스왑인 또는 스왑아웃될 페이지를 포함하는 페이지 프레임 시작주소 entry 스왑 아웃 페이지 식별자 rw 데이터 전송방향 READ : 스왑인, WRITE : 스왑아웃 wait 입출력 연산의 종료까지 커널 제어 경로가 블록되어야 하는지를 지정하는 플래그 페이지 폴트로 인한 스왑인은 동기적(wait : 1)이기 때문에 전송이 끝날때까지 프로세스를 보류 스왑 아웃연산의 경우 비동기적(wait : 0) 커널은 동시에 실행할 수 있는 비동기적 스왑 연산수를 pager_daemon 변수인 swap_cluster 필드에 저장
스와핑 관련 함수들 – brw_page() 데이터 전송을 위해 brw_swap_page()에 의해 호출됨 블록 장치 드라이버는 페이지 슬롯의 블록 데이터 전송을 종료할 때마다 대응하는 비동기 버퍼 헤드에서 얻은 b_end_io 메소드를 호출 end_buffer_io_async()는 모든 페이지 블록을 전송한 후 after_unlock_page()를 호출
스와핑 관련 함수들 – ead_swap_cache_async() 스왑 캐시 또는 디스크에서 페이지를 스왑 인하기 위해 호출 매개변수 entry 스왑 아웃된 페이지 식별자 wait 스왑의 입출력 연산을 종료할 때까지 커널이 현재 프로세스를 보류하도록 허가했는지 나타내는 플래그 스왑 연산의 동기/비동기를 결정
스와핑 관련 함수들 – rw_swap_page_nocache() 매개변수로 입출력 연산의 유형(READ, WRITE), 스왑 아웃된 페이지 식별자, 페이지 프레임 주소를 포함 스왑 캐시에 넣지 않고 스왑 영역에서 입출력을 수행하려는 경우 사용 내부적으로 rw_swap_page()를 호출하여 스왑연산 수행
스와핑 관련 함수들 – try_to_swap_out() 주어진 페이지 프레임의 내용을 해제하거나 스왑 아웃함으로써 주어진 페이지 프레임을 비우는 기능 수행 스왑아웃을 성공했거나 블록킹 입출력 연산을 실행한 경우 1 반환 스왑아웃을 수행하지 않기로 결정하면 ‘0’을 반환 매개변수 tsk 프로세스 디스크립터 포인터 vma 메모리 영역 디스크립터 포인터 address 페이지의 최초 선형 주소 page_table address를 매핑한 tsk의 페이지 테이블 엔트리 주소 gtp_mask swap_out() 함수의 gfp_mask 매개변수
페이지 스왑 아웃 프로그램 수행을 위해 스왑 아웃 작업은 마지막 수단 다른 방법을 사용하여 메모리를 제거하는 일반적인 전략 중 일부 swap_out() 함수 priority 0~6까지 정수값으로 스왑할 페이지를 찾는 시간을 설정 작을수록 오래 검색함 gfp_mask 커널이 페이지를 어떻게 취급하는지, 특히 요청이 얼마나 급한지, 커널 제어 경로를 연기할 수 있는지 여부를 나타냄 페이지 아웃 종료 페이지 스왑 아웃 성공 블록킹 연산 실행 정해진 수의 프로세스를 검색하고도 페이지 스왑 아웃에 실패
페이지 스왑 아웃 과정 페이지를 스와핑할 프로세스를 선택 swap_out_process() 호출 프로세스 리스트를 검사하고 프로세스 디스크립터의 swap_cnt 필드가 가장 큰 값이 들어있는 프로세스를 검색 swap_cnt가 모두 ‘0’인 경우, 페이지 프레임 개수를 검색 swap_out_process() 호출 매개변수로 디스크립터 포인터와 gfp_mask를 전달 1을 반환하면 페이지 프레임이 스왑아웃되었거나 현재 프로세스가 보류상태이므로 스왑 아웃을 종료 swap_out_vma() 호출 윗 단계에서 검색한 첫째 메모리 영역의 주소를 프로세스 디스크립터의 swap_address 필드에 저장 swap_out_process()는 1을 반환하거나 메모리 영역 리스트의 끝에 도달할 때까지 swap_out_vma() 계속 호출 메모리 영역이 락킹 상태(VM_LOCKED = 0)인지 검사 페이지 테이블 엔트리를 모두 검사하기 위해 swap_out_pgd()를, swap_out_pgd()는 swap_out_pmd()를, swap_out_pmd()는 try_to_swap_out을 연속적으로 호출하여 스왑 아웃 가능한지 결정
공유 메모리 매핑의 페이지 스왑 아웃 공유 메모리 매핑 내 페이지는 스왑 영역에 저장하지 않고 대응하는 파일을 갱신(15장) filemap_swapout()을 이용하여 공유 메모리 매핑을 한 경우 do_write_page() 함수를 호출하지 않기 때문에 경쟁 현상(Race conditions)이 발생 kpiod 커널 스레드 위의 문제를 해결하기 위해, 커널이 공유 메모리 매핑에 속한 페이지를 스왑 아웃하도록 사용 입력큐에 디스크에 기록할 공유 메모리 영역의 페이지에 대한 새로운 요청을 할 때마다 호출됨 커널은 한번에 여러 페이지를 스왑 아웃할 수 있기 때문에 입력 큐에 여러 요청을 저장 큐의 각 항목은 pio_request 유형 디스크립터이며 필드값을 포함[표16-2 참조]
공유 메모리 매핑의 페이지 스왑 아웃-1 filemap_write_page() 기능 make_pio_request() 기능 filemap_swapout() 호출되면 수행됨 make_pio_request()를 호출하여 kpiod 입력 큐에 요청 추가 자신의 데이터 전송을 수행하는 do_write_page()를 대신 make_pio_request() 기능 기록할 페이지의 사용 카운터를 증가시킴 새로운 pio_request 디스크립터를 할당 pio_request 디스크립터의 필드를 초기화 pio_reqeust 디스크립터의 요청 큐에 삽입 pio_wait 대기큐의 프로세스(kpiod 커널 스레드) 구동시킴
페이지스왑인 >>>>>> do_swap_page() 호출 프로세스가 자신의 주소 공간에서 디스크에 스왑 아웃된 페이지를 참조하려고 할때 스왑인 수행 다음 조건의 경우 ‘페이지 폴트’ 예외 핸들러가 스왑인 연산 페이지가 현재 프로세스의 메모리 영역에 포함되었을 때 페이지 테이블 엔트리의 Present 플래그가 삭제되었을 때 엔트리에 스왑 아웃된 페이지 식별자가 존재할 때 >>>>>> do_swap_page() 호출
스와핑 관련 함수들 – do_swap_page() 매개변수 tsk : 페이지 폴트를 발생시킨 프로세스의 디스크립터 주소 address : 예외를 발생시킨 선형 주소 vma : address를 포함하는 영역의 메모리 영역 디스크립터 주소 page_table : 페이지 테이블 엔트리 주소 entry : 스왑 아웃된 페이지 식별자 write_access : 읽기/쓰기 요청 플래그 기능(IPC 공유 메모리 영역의 전용 swapin 사용) swapin 메소드를 호출 swapin 메소드 반환값을 page_table이 가리키는 테이블 엔트리에 기록 페이지 프레임 사용카운트가 1보다 크고 메모리 영역을 공유하지 않으면 페이지 테이블 엔트리의 Read/Write 플래그를 0으로 함 프로세스의 mm->rss, tss->maj_flt 필드 증가 Kernel_flag 전역 커널 락 해제
스와핑 관련 함수들 – do_swap_page() - 1 범용 swap_in() 사용하는 경우 1 : lookup_swap_cache()를 호출하여 스왑캐시가 이미 entry가 나타내는 페이지를 포함하고 있는지 검사, 그렇다면 4단계로 2 : swap_readahead()를 호출하여 스왑 영역에서 요청한 페이지를 포함하는 2n 페이지 그룹을 읽어들임(n : page_cluster 변수값) >>>>wait을 null로 설정한 경우, 즉 비동기적 스왑 연산의 경우 read_swap_cache_async() 함수를 호출 3 : swap_readahead()가 요청한 페이지를 읽는데 실패하면(비동기적 스왑 연산이 실행중), entry 위에서 read_swap_cache()를 호출 >>>>read_swap_cache()는 동기적 스왑연산 수행 4 : 페이지 테이블이 가리키는 엔트리가 entry와 다른지 검사 5 : swap_free()를 호출하여 entry에 대응하는 페이지 슬롯의 사용 카운터를 감소 6 : 프로세스 mm->rss와 min_flt 필드를 증가 7 : 여러 프로세스가 페이지를 공유하거나 프로세스가 페이지를 읽기만 하는 경우 페이지는 스왑 캐시에 지속 8 : 그렇지 않으면, 페이지를 공유하지 않거나 프로세스가 페이지에 쓰기를 시도하면 스왑캐시에 지속될 필요가 없음
페이지프레임해제 방법 페이지 프레임 해제를 위한 함수들 shrink_mmap() 페이지 캐시, 스왑 캐시, 버퍼 캐시에 사용 shrink_dcache_memory() 디엔트리 캐시에 사용 kmem_cache_reap() 슬랩 캐시에 사용(6장) 프로세스 익명(anonymous) 메모리 영역에 속한 페이지 또는 사적 메모리 매핑에 속하는 변경된 페이지를 스왑아웃 IPC 공유 메모리 영역에 속하는 페이지를 스왑 아웃 >>> 작동 방식을 이해하기 보다는 좋은 성능을 갖도록 하기 위한 매개변수 조정에 초점
페이지프레임해제 - 1 빈 메모리 감시하기 빈 페이지 프레임 수 : nr_free_pages 변수값 /proc/sys/vm/freepages 파일에 기록 min 커널이 중요한 연산을 실행할 수 있도록 예약한 페이지 프레임 최소값 free_area_init()은 2n으로 초기화(n : MB단위이며 10 ~ 25) high nr_free_pages 임계값 3 * freepages.min
페이지프레임해제 - 2 페이지/스왑/버퍼 캐시에서 페이지 회수 디스크캐시에서 페이지 프레임을 회수하기 위해 shrink_mmap() 사용 shrink_mmap() 성공하면 1을 반환, 그렇지 않으면 0을 반환 mem_map 배열을 검색하여 아래의 회수 조건인 페이지를 선택 페이지/스왑/버퍼캐시 중 하나 락킹상태가 아님 어떤 프로세스도 사용하지 않아야 함 매개변수 priority ‘0’을 반환하기 전 검사할 페이지 프레임의 총수 ‘0’(매우 긴급)에서 ‘6’(급하지 않음)까지 범위 gtp_mask 회수할 페이지 프레임의 종류를 나타내는 플래그
페이지프레임해제 - 3 디엔트리와 아이노드 캐시에서 페이지 회수 디엔트리 객체 자체는 크지 않으나 이 객체를 회수하면 연속적인 효과로 많은 메모리를 확보 가능 shrink_dcache_memory() 사용 디엔트리 캐시에서 디엔트리 객체를 제거 몇몇 슬랩을 비워 결과적으로 kmem_cache_leap()이 페이지 프레임들을 회수[6장 참조] 디엔트리 캐시는 아이노드 캐시의 컨트롤러로 동작 >>>> 디엔트리 객체를 해제하면 대응하는 아이노드를 저장한 버퍼도 사용할 수 없고 shrink_mmap()은 대응하는 버퍼 페이지를 해제 shrink_dcache_memory() shrink_mmap()과 같은 매개변수를 입력받음 커널이 입출력 연산을 수행실행하도록 허가를 받았는지 검사(gfp_mask = __GFP_IO)하고, 허가받았다면 prune_dcache() 호출
페이지프레임해제 - 4 prune_dcache() prune_one_dentry() 매개변수 d_nr : 해제될 디엔트리 객체 수 i_nr : 아이노드 객체 수 priority에 의존하는 d_nr 값이 ‘0’ 인 경우 모든 디엔트리 객체 제거 ‘0’이 아닌 경우, d_nr (n/priority) 을 계산 prune_one_dentry() 사용하지 않는 디엔트리 리스트를 탐색하여 해제될 각 객체에 대해 호출 수행 내용 디엔트리 해시 테이블과 부모 디렉토리의 디엔트리 객체 리스트에서 삭제 dentry_iput()을 호출하여 정의된 경우,d_iput 디엔트리 메소드를 사용하고 아닌 경우 iput() 사용하여 디엔트리의 아이노드를 해제 디엔트리의 부모 디렉토리에 대해 dput()을 호출 디엔트리 객체를 슬랩 할당자로 반환
스와핑 관련 함수들 – try_to_free_pages() 기능 kswapd 커널 스레드를 구동 gfp_mask의 __GFP_WAIT을 설정했다면 do_try_to_free_pages()를 호출하면서 gfp_mask 매개변수 전달 매개변수 플래그 집합인 gfp_mask __GFP_IO 플래그 : 입출력 데이터 전송이 허용된 커널 __GFP_WAIT 플래그 : 메모리를 비우기 위해 프레임 내용을 버리도록 허용된 커널 함수가 호출되는 경우 비어있는 페이지 프레임 수가 freepages.min 임계값 이하로 떨어지고 현 프로세스가 PF_MEMALLOC 플래그를 해제한 경우, __get_free_pages()로부터 호출됨 새로운 pio_request 디스크립터 할당에 실패했을 때 make_pio_request() 함수로부터 호출됨
스와핑 관련 함수들 – do_try_to_free_pages() do_try_to_free_pages()와 kswapd 커널 스레드를 통해 호출됨 최소한 SWAP_CLUSTER_MAX개 페이지 프레임을 해제 하려고 시도 리눅스의 경우 보통 32로 swap.h 파일에서 확인 가능 매개변수 gfp_mask 수행 기능 lock_kernel()을 호출하여 전역 커널 락킹을 확보 kmem_cache_reap(gfp_mask)을 호출하여 슬랩 할당자 캐시에서 페이지 프레임을 회수 priority 지역 변수를 6(가장 낮은 수준)으로 설정 각 반복마다 우선순위를 증가시켜 좀더 철저한 반복 검색을 통해 페이지를 회수하려 함 unlock_kernel()을 호출 SWAP_CLUSTER_MAX개 이상의 페이지 프레임을 해제했으면 1을, 그렇지 않으면 0을 반환
kswapd 커널 스레드 메모리 회수를 수행하는 또 다른 커널 메커니즘 필요성 해결책 일부 메모리 할당의 경우 인터럽트나 예외 핸들러로 실행하며 해제할 페이지 프레임을 기다리는 현 프로세스를 블록시키지 못함 중요한 자원에 대해 배타적인 접근을 획득한 상태로 이루어지므로 입출력 데이터 전송을 활성화 할 수 없음 모든 메모리 할당 요청이 이와 같은 커널 제어경로에 따라 수행했을 경우, 커널은 메모리를 회수 못할 가능성이 존재 해결책 kswapd 커널 스레드가 10초마다 활성화되어 kswapd()를 수행 1 : nr_free_pages가 freepages.high 임계값보다 크면 더 이상 메모리를 회수할 필요 없음, 5단계로 이동 2 : gfp_mask를 __GFP_IO로 설정하여 do_try_to_free_pages()를 호출 3 : 현재의 need_resched 필드가 ‘0’이면1단계로 이동 4 : need_resched 필드가 1이면 schedule()을 호출하여 CPU를 다른 프로세스에 양보 5 : 현재 상태를 TALK_INTTERUPTIBLE로 설정 6 : 매개변수로 10 * HZ 값을 전달하여 schedule_timeout()을 호출, 10초 후에 수행하고 1단계로 이동
요약 살펴본 내용 결론 스와핑 개념 및 고려사항, 스왑 영역 개념 스왑 캐시 페이지 스왑 아웃 및 스왑 인 알고리즘 페이지 프레임 해제 기타 스와핑 관련 함수들 결론 스와핑 프로그램 수행에 있어서 마지막 수단 대부분의 프로그램이 대형화됨에 따라 없어서는 안될 중요한 개념 메모리 관리 영역, 스케쥴링, 프로세스 주소공간 등에 대한 지식 필요
Part VI : Q & A