[Linux] Swap Cache

최승혁·2022년 8월 25일
0

작성 중

리눅스가 디스크에서 버퍼링 되는 데이터를 위해 free 메모리를 사용하는 것처럼, 프로세스가 사용하는 페이지에 대한 free 메모리가 필요하다. 디스크 파일로 지원되는 페이지와 다르게 이러한 페이지는 추후에 사용될 수 있기 때문에 삭제되면 안 된다. 그래서 이러한 페이지들은 백업 스토리지에 복사되는 대신에, swap area에 복사되어야 한다.

스왑 공간이 필요한 두 가지 이유가 있다.

  • 프로세스가 사용할 수 있는 메모리 양을 확장한다.
  • 가상 메모리와 스왑 공간을 사용하면 프로세스가 부분적으로 상주하더라도 큰 프로세스를 실행할 수 있다.

페이지 폴트 처리를 통해 주소 지정된 메모리 양이 RAM을 초과할 수 있다. 메모리가 충분하면 스왑이 불필요하겠지만, 프로세스 수명 초기에 참조한 페이지의 상당수는 초기화만 사용된 다음 다시는 사용되지 않을 수 있다. 따라서 해당 페이지를 교체하고 사용하지 않고 상주하는 것보다 더 많은 디스크 버퍼를 만드는 것이 좋다.

디스크는 매우 느린데, 프로세스가 많은 양의 메모리를 자주 다루게 되면 더 많은 RAM만이 합리적인 수행 시간을 수행할 수 있을 것이다. 이것이 올바른 페이지 교체가 중요한 이유이며 관련 페이지를 스왑 공간에 함께 저장하여 프리페칭 하도록 하는 이유이다.

1. Swap Area

각 활성 스왑 영역은 파일이나 파티션에 관계 없이 swap_info_struct를 구성하고 있다. 수행 중인 시스템의 스왑 영역은 MAX_SWAPFILES길이(32)로 static하게 선언된 배열인 swap_info에 저장되며, 이것은 32개의 스왑 영역을 가질 수 있다는 뜻이다. swap_info_struct는 <linux/swap.h>에 다음과 같이 구현되어 있다.

 64 struct swap_info_struct {
 65     unsigned int flags;
 66     kdev_t swap_device;
 67     spinlock_t sdev_lock;
 68     struct dentry * swap_file;
 69     struct vfsmount *swap_vfsmnt;
 70     unsigned short * swap_map;
 71     unsigned int lowest_bit;
 72     unsigned int highest_bit;
 73     unsigned int cluster_next;
 74     unsigned int cluster_nr;
 75     int prio;
 76     int pages;
 77     unsigned long max;
 78     int next;
 79 };
  • flags: 2개의 플래그로 구성 되어있으며, SWP_USED는 활성화 되었다는 뜻이고, SWP_WRITEOK는 write 연산 작업 준비가 완료됐다는 뜻이다.
  • swap_device: 스왑 영역에 사용된 파티션에 해당하는 장치, 파일이면 NULL
  • sdev_loc: swap_map을 주로 보호하는 spin lock, swap_device_lockswap_device_unlock을 사용
  • swap_file: 스왑 영역으로 마운트 된 실제 파일의 denty. 파티션이 마운트된 경우, /dev/ 디렉토리의 파일에 대한 dentry일 수 있다.
  • vfs_mount: 스왑 영역이 저장된 위치에 해당하는 디바이스나 파일의 vfs_mount object
  • swap_map: 모든 스왑 엔트리 또는 스왑 영역의 페이지 크기 슬롯에 대한 하나의 엔트리가 있는 큰 배열, 엔트리는 페이지 슬롯의 사용자 수에 대한 참조 횟수이다. 스왑 캐시는 한 명의 사용자로 계산되고, 슬롯으로 페이지 아웃된 모든 PTE는 사용자로 계산된다. SWAP_MAP_MAX인 경우, 슬롯은 영구적으로 할당된다. SWAP_MAP_BAD인 경우, 슬롯은 사용되지 않는다.
  • lowest_bit: 스왑 영역에서 사용 가능한 가장 낮은 유휴 슬롯이며 검색 공간을 줄이기 위해 선형 스캔할 때 사용된다. 이 비트 아래에는 빈 슬롯이 없음.
  • highest_bit: 스왑 영역에서 사용할 수 있는 가장 높은 유휴 슬롯이며, 이 비트 위에는 유휴 슬롯이 없음.
  • cluster_next: 사용할 다음 블록 클러스터의 오프셋으로 스왑 영역은 관련 페이지가 함께 저장될 가능성을 높이기 위해 클러스터 블록 페이지를 할당하려고 한다.
  • cluster_nr: 클러스터에 할당할 수 있는 남은 페이지 수
  • prio: 각 스왑 영역에는 이 필드에 저장된 우선 순위가 있다. 스왑 영역은 우선 순위에 따라 정렬되고 영역이 사용될 가능성을 결정한다. 기본적으로 우선순위는 할성화 순서대로 정렬되지만 시스템 관리자는 swapon을 사용할 때 -p 플래그를 사용하여 지정할 수도 있다.
  • pages: 스왑 파일의 일부 슬롯을 사용하지 못할 수 있으므로 이 필드는 스왑 영역에서 사용 가능한 페이지 수를 저장한다. SWAP_MAP_BAD로 표시된 슬롯이 계산되지 않는다는 점에서 MAX와 다르다.
  • max: 스왑 영역의 총 슬롯 수.
  • next: 다음 스왑 영역의 swap_info 배열 인덱스.

스왑 영역은 배열에 저장되지만 swap_list라는 pseudo 목록에 유지되며, 이는 <linux/swap.h>에 선언된 매우 간단한 유형이다.

153 struct swap_list_t {
154     int head;    /* head of priority-ordered swapfile list */
155     int next;    /* swapfile to be used next */
156 };
  • head: 사용 중인 스왑 영역의 높은 우선순위를 가지는 영역
  • next: 다음에 사용되어야 할 영역

적절한 영역을 검색할 때는 우선 순위에 따라 영역을 정렬할 수 있지만 필요한 때는 배열에서 빠르게 찾아볼 수 있다.

각 스왑 영역은 디스크의 페이지 사이즈 슬롯으로 나뉘어져 있다. 첫 번째 슬롯은 항상 스왑 영역의 정보를 담으며, 덮어써지지 않는다. 스왑 영역의 첫 1KB는 user-space에서 사용하는 파티션을 위해 디스크의 이름을 저장하는 데 사용한다. 남는 공간은 시스템 프로그램이 mkswap 함수를 통해 스왑 영역을 생성할 때 정보를 저장하는 데 사용된다. 정보는 swap_header로 채워지며, <linux/swap.h>에 구현되어 있다.

 25 union swap_header {
 26     struct 
 27     {
 28         char reserved[PAGE_SIZE - 10];
 29         char magic[10];
 30     } magic;
 31     struct 
 32     {
 33         char     bootbits[1024];
 34         unsigned int version;
 35         unsigned int last_page;
 36         unsigned int nr_badpages;
 37         unsigned int padding[125];
 38         unsigned int badpages[1];
 39     } info;
 40 };
  • magic magic 문자열을 식별하는 데 사용. 스왑 영역이 아닌 파티션이 사용될 지 확인하고, 스왑 영역의 버전을 결정한다. SWAP_SPACE이면 스왑 파일 형식의 버전 1, SWAP_SPACE2이면 버전 2.
  • bootbits: 파티션 정보를 저장하는 비트
  • version: 스왑 영역 레이아웃 버전
  • last_page: 영역에서 마지막으로 사용할 수 있는 페이지
  • nr_badpages: 스왑 영역의 불량 페이지 수
  • padding: 디스크 섹션의 크기는 일반적으로 512B이다. 3개의 필드는 12바이트를 구성하고 패딩은 한 섹터를 커버하기 위해 나머지 500바이트를 채운다.
  • badpages: 페이지의 나머지 부분은 최대 MAX_SWAP_BADPAGE의 인덱스를 저장하는 데 사용.

MAX_SWAP_BADPAGES는 구조가 바뀌면 변하는 컴파일 타임 상수이지만, 단순 방정식에 의해 주어진 현재 형태의 637개 항목이다.

MAX_SWAP_BADPAGES = (PAGE_SIZE - 1024 - 512 - 10) / sizeof(long)

1024는 bootclock이고, 512는 패딩 사이즈이며, 10은 magic string 길이이다.

2. Mapping Page Table Entries to Swap Entries

페이지가 스왑 아웃 되면, 리눅스는 디스크에 페이지를 위치시키는 정보를 충분히 저장하기 위해 PTE를 사용한다. PTE는 페이지가 위치한 디스크를 저장하는 데에 충분하지 않으나, swap_info 배열에 인덱스를 저장하고 swap_map 내의 오프셋을 저장하는 데 충분하다.

아키텍처와 상관없이, 각 PTEswp_entry_t를 저장하기에 충분하며, 이는 <linux/shmem_fs.h>에 구현되어 있다.

 16 typedef struct {
 17     unsigned long val;
 18 } swp_entry_t;

PTEswap_entry_t로 변환하기 위해 두 가지 매크로가 사용된다. pte_to_swp_entry(), swp_entry_to_pte()

각 아키텍처는 PTE가 스왑아웃 됐는지 결정할 수 있다. 아래 그림에서 x86 시스템에서 이것이 어떻게 구현됐는지 보여준다. swp_entry_t에서 두 비트가 항상 유휴하게 존재한다. x86에서 0 비트는 _PAGE_PRESENT 플래그이고, 7 비트는 _PAGE_PROTNONE이다. 1~6 비트는 swp_type() 매크로에서 반환되는 swap_info 배열 내의 인덱스인 유형에 대한 것이다.

8~31 비트는 swp_entry_tswap_map 내에 오프셋을 저장하는 데 사용한다. x86에서는 24비트를 사용할 수 있으며, 스왑 영역의 크기를 64GB로 제한한다. swp_offset 매크로는 오프셋을 추출하는 데 사용된다. type과 offset을 swp_entry_t로 인코딩하기 위해서는 swp_entry() 매크로를 사용할 수 있으며, 단순히 관련 비트를 이동시키는 연산을 수행한다.

![swp_entry_t](C:\Users\choi\OneDrive - UNIST\바탕 화면\CS\Linux\swp_entry_t.png)

32비트 아키텍처에서 type을 위한 6비트는 MAX_SWAPFILES의 제한인 32비트 대신 최대 64개의 스왑 영역이 존재하도록 허용해야 한다. 이 제한은 vmalloc 주소 공간의 사용으로 인해 발생한다. 스왑 영역이 가능한 최대인 경우, swap_map에 32MB가 필요하다. 각 페이지는 참조 카운트에 대해 하나의 짧은 시간을 사용한다. MAX_SWAPFILES 최대 스왑 영역 수만 존재하려면 사용자/커널 선형 주소 공간 분할로 인해 불가능한 1GB 가상 malloc 공간이 필요하다.

3. Allocating a swap slot

모든 페이지 크기의 슬롯은 unsigned short 타입의 배열인 swap_info_struct->swap_map에 의해 추적된다. 각 엔트리는 공유 페이지 케이스에서 발생하는 슬롯의 사용자 수에 대한 참조 횟수이고 비어있으면 0이다. SWAP_MAP_MAX인 경우 해당 슬롯은 영구적으로 할당된다. 이것은 참조 카운트가 오버플로우 되지 않도록 하기 위해 존재하며, SWAP_MAP_BAD인 경우 슬롯을 사용할 수 없다.

스왑 슬롯을 찾고 할당하는 일은 두 단계로 이루어져 있다.

  • get_swap_page(): high level function에서 수행되며, swap_list->next로 시작하여 적절한 슬롯을 스왑 영역에서 찾는다. 슬롯을 찾으면, 다음에 사용될 스왑 영역을 기록하고 할당된 엔트리를 반환한다.
  • scan_swap_map(): 맵을 찾아보는 일은 이 함수가 수행한다. 유휴 슬롯을 찾기 위해 배열을 선형 탐색하고 반환한다.

리눅스는 SWAPFILE_CLUSTER 크기의 디스크 클러스터로 페이지를 조직화하려 한다. 페이지의 SWAPFILE_CLUSTER 수를 swap_info_struct->cluster_nr에 순차적으로 할당된 페이지의 수를 유지하도록 순차적으로 할당하고, 현재 오프셋을 swap_info_struct->cluster_next에 기록한다.

스왑 영역에서 충분히 큰 사용 가능한 클러스터를 찾을 수 없는 경우 swap_info_swap→swap_bit에서 시작하는 간단한 1차 무료 검색이 수행됩니다. 목적은 서로 교환된 페이지들이 서로 연관되어 있다는 전제하에 동시에 서로 교환된 페이지들을 서로 가깝게 하는 것이다. 언뜻 이상해 보이는 이 전제는 페이지 교환 알고리즘이 페이지 교환을 선형적으로 스캔할 때 스왑 공간을 가장 많이 사용한다는 점을 고려할 때 상당히 견고하다. 큰 빈 블록을 검색하여 사용하지 않으면 검색 결과가 첫 번째 빈 검색으로 변질되어 전혀 개선되지 않을 수 있다. 이를 통해 프로세스를 종료하면 큰 블록의 슬롯을 확보할 수 있다.

4. Swap Cache

언급한 바와 같이 페이지를 참조하는 모든 PTE에 매핑할 수 있는 빠른 방법이 없기 때문에 많은 프로세스 간에 공유되는 페이지는 쉽게 스왑할 수 없다. 이로 인해 한 PTE에 대한 페이지가 존재하고 다른 PTE로 스왑 아웃된 페이지가 디스크에 동기화되지 않고 업데이트되어 업데이트가 손실되는 경합 상태가 발생한다.

이 문제를 해결하기 위해 백업 스토리지에 예약된 슬롯이 있는 공유 페이지는 스왑 캐시의 일부로 간주된다. 스왑 캐시는 단순히 페이지 캐시의 전문화이기 때문에 순수하게 개념적이다. 페이지 캐시가 아닌 스왑 캐시에 있는 페이지 간의 첫 번째 주요 차이점은 스왑 캐시에 있는 페이지는 항상 page→message의 address_space로 swapper_space를 사용한다는 것이다. 두 번째 차이점은 페이지가 add_to_page_cache() 대신 add_to_swap_cache()를 사용하여 스왑 캐시에 추가된다는 점이다.

<그림>

익명 페이지는 스왑 아웃을 시도할 때까지 스왑 캐시의 일부가 아니다. 변수 swapper_space는 swap_state.c에서 다음과 같이 선언된다.

 39 struct address_space swapper_space = {
 40     LIST_HEAD_INIT(swapper_space.clean_pages),
 41     LIST_HEAD_INIT(swapper_space.dirty_pages),
 42     LIST_HEAD_INIT(swapper_space.locked_pages),
 43     0,
 44     &swap_aops,
 45 };

PageSwapCache() 매크로에서 테스트되는 page→syspace 필드가 swapper_space로 설정되면 페이지가 스왑 캐시의 일부로 식별됩니다. 리눅스는 스왑과 메모리 사이의 페이지를 동기화하기 위해 파일 백업 페이지와 메모리를 동기화하기 위해 사용하는 것과 정확히 동일한 코드를 사용한다.

스토리지를 백업하기 위한 주소 공간인 swapper_space는 address_space→a_ops에 swap_ops를 사용합니다. 그런 다음 page→index 필드는 일반적인 목적인 파일 오프셋 대신 swp_entry_t 구조를 저장하는 데 사용됩니다. address_space_operations struct swap_aops는 swap_state.c에서 다음과 같이 선언됩니다.

 34 static struct address_space_operations swap_aops = {
 35     writepage: swap_writepage,
 36     sync_page: block_sync_page,
 37 };

페이지를 스왑 캐시에 추가할 때 슬롯은 get_swap_page()로 할당되고 add_to_swap_cache()로 페이지 캐시에 추가된 다음 더티로 표시됩니다. 다음 번에 페이지를 세탁하면 일반 페이지 캐시가 작동하는 것처럼 실제로 디스크의 백업 스토리지에 기록됩니다. 이 프로세스는 그림 11.4에 설명되어 있습니다.

<그림>

공유 PTE에서 페이지를 이후에 스와핑하면 swap_duplicate()로 호출되어 swap_map의 슬롯에 대한 참조가 증가합니다. 쓰기 결과로 PTE가 하드웨어에 의해 더티로 표시되면 비트는 지워지고 구조 페이지는 set_page_dirty()로 더티로 표시되므로 페이지가 삭제되기 전에 디스크 상의 복사본이 동기화됩니다. 이렇게 하면 페이지에 대한 모든 참조가 삭제될 때까지 디스크의 데이터가 페이지 프레임의 데이터와 일치하는지 확인할 수 있습니다.

페이지에 대한 참조 카운트가 마침내 0에 도달하면 페이지를 페이지 캐시에서 삭제할 수 있으며 스왑 맵 카운트는 슬롯이 너무 일찍 해제되지 않도록 디스크 슬롯이 속한 PTE의 수를 갖습니다. 10장에서 설명한 것과 동일한 LRU 에이징 및 로직으로 세척하고 최종적으로 폐기한다.

반면에 "swap out" 페이지에 대해 페이지 장애가 발생하면 do_swap_page()의 로직은 lookup_swap_cache()를 호출하여 페이지가 스왑 캐시에 존재하는지 확인합니다. 이 경우 PTE가 페이지 프레임을 가리키도록 업데이트되고 페이지 참조 카운트가 증가하고 스왑 슬롯이 swap_free()로 감소합니다.

swp_entry_t get_swap_page()
This function allocates a slot in a swap_map by searching active swap areas. This is covered in greater detail in Section [11.3](https://www.kernel.org/doc/gorman/html/understand/understand014.html#sec: Allocating a swap slot) but included here as it is principally used in conjunction with the swap cache
int add_to_swap_cache(struct page *page, swp_entry_t entry)
This function adds a page to the swap cache. It first checks if it already exists by calling swap_duplicate() and if not, is adds it to the swap cache via the normal page cache interface function add_to_page_cache_unique()
struct page * lookup_swap_cache(swp_entry_t entry)
This searches the swap cache and returns the struct page corresponding to the supplied entry. It works by searching the normal page cache based on swapper_space and the swap_map offset
int swap_duplicate(swp_entry_t entry)
This function verifies a swap entry is valid and if so, increments its swap map count
void swap_free(swp_entry_t entry)
The complement function to swap_duplicate(). It decrements the relevant counter in the swap_map. When the count reaches zero, the slot is effectively free

5. Reading Pages from Backing Storage

페이지에서 읽을 때 사용되는 주요 함수는 read_swap_cache_async()이며 주로 페이지 장애 시 호출됩니다. 함수는 find_get_page()를 사용하여 스왑 캐시 검색을 시작합니다. 일반적으로 스왑 캐시 검색은 lookup_swap_cache()에 의해 수행되지만 이 함수는 수행된 검색 수에 대한 통계를 업데이트하며 캐시를 여러 번 검색해야 할 수 있으므로 대신 find_get_page()가 사용됩니다.

<그림>

다른 프로세스가 동일한 페이지를 매핑했거나 동시에 여러 프로세스가 동일한 페이지에서 오류를 발생시키는 경우 페이지가 스왑 캐시에 이미 존재할 수 있습니다. 페이지가 스왑 캐시에 없으면 페이지를 할당하고 백업 스토리지의 데이터로 채워야 합니다.

페이지가 alloc_page()로 할당되면 스왑 캐시의 페이지에서만 스왑 캐시 작업을 수행할 수 있으므로 add_to_swap_cache()로 스왑 캐시에 추가됩니다. 페이지를 스왑 캐시에 추가할 수 없는 경우 스왑 캐시를 다시 검색하여 다른 프로세스가 데이터를 스왑 캐시에 아직 넣지 않았는지 확인합니다.

백업 스토리지에서 정보를 읽기 위해 rw_swap_page()가 호출되며, 이는 섹션 11.7에서 논의됩니다. 함수가 완료되면 page_cache_release()를 호출하여 find_get_page()에서 가져온 페이지에 대한 참조를 삭제합니다.

6. Writing Pages to Backing Storage

디스크에 페이지를 쓸 때 address_space→a_ops를 참조하여 적절한 쓰기 기능을 찾습니다. 스토리지를 백업하는 경우 address_spaceswapper_space이고 스왑 작업은 swap_aops에 포함됩니다. swap_aops 구조체는 쓰기 함수로 swap_writepage()를 등록한다.

<그림>

swap_writepage() 함수는 쓰기 프로세스가 스왑 캐시 페이지의 마지막 사용자인지 여부에 따라 다르게 동작합니다. remove_exclusive_swap_page()를 호출하여 페이지를 사용하는 다른 프로세스가 있는지 확인합니다. 이것은 페이지 cache_lock을 누른 상태에서 페이지 수를 검사하는 간단한 경우입니다. 다른 프로세스가 페이지를 매핑하지 않으면 스왑 캐시에서 제거되고 해제됩니다.

remove_exclusive_swap_page()가 스왑 캐시에서 페이지를 제거하고 해제하면 swap_writepage()가 더 이상 사용되지 않으므로 페이지의 잠금을 해제합니다. 스왑 캐시에 여전히 존재하는 경우 rw_swap_page()를 호출하여 데이터를 백업 스토리지에 씁니다.

7. Reading/Writing Swap Area Blocks

스왑 영역에 대한 읽기 및 쓰기를 위한 최상위 함수는 rw_swap_page()입니다. 이 기능은 모든 작업이 스왑 캐시를 통해 수행되어 업데이트 손실을 방지합니다. rw_page_base는 실제 작업을 수행하는 핵심 함수입니다.

작업이 읽기인지 확인하는 것으로 시작합니다. 데이터 입력에 IO가 필요한 경우 페이지가 최신이 아니므로 ClearPageUpdate()를 사용하여 최신 플래그를 지웁니다. 디스크에서 페이지를 성공적으로 읽으면 이 플래그가 다시 설정됩니다. 그런 다음 get_swaphandle_info()를 호출하여 스왑 파일을 위한 아이노드의 스왑 파티션을 위한 장치를 획득합니다. 이것들은 실제 IO를 수행할 블록 계층에서 필요합니다.

코어 함수는 블록 계층 함수 brw_page()를 사용하여 실제 디스크 IO를 수행하므로 스왑 파티션 또는 파일과 함께 작동할 수 있습니다. 스왑 영역이 파일인 경우 bmap()은 페이지 데이터를 포함하는 파일 시스템의 모든 블록 목록으로 로컬 어레이를 채우는 데 사용됩니다. 파일 시스템은 파일 및 디스크를 저장하는 고유한 방법을 사용할 수 있으며 디스크에 직접 정보를 쓸 수 있는 스왑 파티션만큼 간단하지 않습니다. 백업 스토리지가 파티션인 경우 한 페이지 크기의 블록만 IO를 필요로 하며 관련된 파일 시스템이 없으므로 bmap()은 필요하지 않습니다.

읽거나 써야 하는 블록이 알려지면 brw_page()를 사용하여 일반적인 블록 IO 작업이 수행됩니다. 수행되는 IO는 모두 비동기이므로 기능이 빠르게 돌아갑니다. IO가 완료되면 블록 계층이 페이지를 잠금 해제하고 대기 프로세스가 시작됩니다.

8. Activating a Swap Area

이제 스왑 영역이 무엇인지, 스왑 영역이 어떻게 표현되고 페이지가 어떻게 추적되는지 살펴보았으므로, 영역을 활성화하기 위해 모든 영역이 어떻게 연계되는지 살펴봐야 할 때입니다. 영역을 활성화하는 것은 개념적으로 매우 간단합니다. 파일을 열고 디스크에서 헤더 정보를 로드한 후 swap_info_struct를 채우고 스왑 목록에 추가합니다.

  • 스왑 영역의 활성화를 담당하는 함수는 sys_swapon()이며, 두 개의 매개 변수, 즉 스왑 영역의 특수 파일에 대한 경로와 플래그 집합이 필요합니다. 스왑이 활성화된 동안에는 BKL(Big Kernel Lock)이 유지되어 이 작업이 수행되는 동안 응용 프로그램이 커널 공간에 들어가는 것을 방지합니다. 이 기능은 매우 크지만 다음과 같은 간단한 단계로 나눌 수 있습니다.

  • swap_info 어레이에서 사용 가능한 swap_info_struct를 찾아 기본값으로 초기화합니다.
    제공된 특수 파일에 대한 디렉토리 트리를 가로지르는 user_path_walk()를 호출하고 파일의 사용 가능한 데이터(예: 항목 및 저장 위치에 대한 파일 시스템 정보)로 namidata 구조를 채웁니다(vfsmount).

  • 스왑 영역의 치수 및 찾는 방법과 관련된 swap_info_struct 필드를 채웁니다. 스왑 영역이 파티션인 경우 크기를 계산하기 전에 블록 크기가 PAGE_SIZE로 구성됩니다. 파일일 경우, 정보는 inode에서 직접 가져옵니다.

  • 영역이 아직 활성화되지 않았는지 확인합니다. 그렇지 않은 경우 메모리에서 페이지를 할당하고 스왑 영역에서 첫 번째 페이지 크기 슬롯을 읽습니다. 이 페이지에는 양호한 슬롯 수 및 swap_info_slots→slots_map을 잘못된 항목으로 채우는 방법과 같은 정보가 포함되어 있습니다.

  • swap_info_swap→swap_map에 대해 vmalloc를 사용하여 메모리를 할당하고 양호한 슬롯에 대해서는 각 항목을 0으로 초기화하며 그렇지 않으면 SWAP_MAP_BAD를 초기화합니다. x863과 같이 페이지 크기가 4KiB인 아키텍처의 경우 버전 1이 128MiB 미만의 영역만 스왑할 수 있었기 때문에 헤더 정보는 버전 2 파일 형식이 되는 것이 이상적입니다.

  • 헤더에 표시된 정보가 실제 스왑 영역과 일치하는지 확인한 후 swap_info_struct에 최대 페이지 수 및 사용 가능한 정상 페이지와 같은 나머지 정보를 입력합니다. nr_swap_pages 및 total_swap_pages에 대한 글로벌 통계 업데이트

  • 이제 스왑 영역이 완전히 활성화되고 초기화되므로 새로 활성화된 영역의 우선 순위에 따라 올바른 위치에 스왑 목록에 삽입됩니다.

기능이 끝나면 BKL이 해제되고 시스템은 페이징에 사용할 수 있는 새 스왑 영역을 갖게 됩니다.

9. Deactivating a Swap Area

스왑 영역을 활성화하는 것과 비교할 때 비활성화에는 엄청난 비용이 듭니다. 주요 문제는 영역을 간단히 제거할 수 없다는 것입니다. 이제 스왑 아웃된 모든 페이지를 다시 스왑 인해야 합니다. 구조 페이지를 참조하는 모든 PTE에 신속하게 매핑할 수 없는 것처럼 스왑 항목을 PTE에 매핑할 수 있는 빠른 방법도 없습니다. 이렇게 하려면 비활성화할 스왑 영역을 참조하고 스왑 인하는 PTE를 찾기 위해 모든 프로세스 페이지 테이블을 이동해야 합니다. 이는 물론 물리적 메모리를 사용할 수 없는 경우 스왑 비활성화 작업이 실패한다는 것을 의미합니다.

영역을 비활성화하는 함수는 충분히 예측 가능한 sys_swapoff()라고 불린다. 이 함수는 주로 swap_info_struct 업데이트와 관련이 있습니다. 페이지 아웃된 각 페이지에서 페이징하는 주요 작업은 매우 비싼 try_to_unuse()의 책임이다. swap_map에 사용된 각 슬롯에 대해 프로세스의 페이지 테이블을 탐색하여 검색해야 합니다. 최악의 경우, 모든 mm_structs에 속하는 모든 페이지 테이블을 넘어야 할 수도 있다. 따라서 지역을 비활성화하기 위해 취해야 할 과제는 대체로 다음과 같다.

  • Call user_path_walk() to acquire the information about the special file to be deactivated and then take the BKL
  • Remove the swap_info_struct from the swap list and update the global statistics on the number of swap pages available (nr_swap_pages) and the total number of swap entries (total_swap_pages. Once this is acquired, the BKL can be released again
  • Call try_to_unuse() which will page in all pages from the swap area to be deactivated. This function loops through the swap map using find_next_to_unuse() to locate the next used swap slot. For each used slot it finds, it performs the following;
    • Call read_swap_cache_async() to allocate a page for the slot saved on disk. Ideally it exists in the swap cache already but the page allocator will be called if it is not
    • Wait on the page to be fully paged in and lock it. Once locked, call unuse_process() for every process that has a PTE referencing the page. This function traverses the page table searching for the relevant PTE and then updates it to point to the struct page. If the page is a shared memory page with no remaining reference, shmem_unuse() is called instead
    • Free all slots that were permanently mapped. It is believed that slots will never become permanently reserved so the risk is taken.
    • Delete the page from the swap cache to prevent try_to_swap_out() referencing a page in the event it still somehow has a reference in swap map
  • If there was not enough available memory to page in all the entries, the swap area is reinserted back into the running system as it cannot be simply dropped. If it succeeded, the swap_info_struct is placed into an uninitialised state and the swap_map memory freed with vfree()
각주

페이징과 스와핑

  • 페이징: 물리 메모리를 페이지 단위로 나누어 사용하는 것.
  • 스와핑: 메모리 상의 프로세스 주소 공간 전체를 디스크의 스왑 영역으로 일시적으로 내려 놓는 것. 그런 다음 사용하는 페이지에 대해서 페이징을 통해 메모리 상에 다시 올려 사용한다.
profile
그냥 기록하는 블로그

0개의 댓글