커널 스터디(iamroot 18기) 4주차 내용 정리 #1, 메모리 할당기

문연수·2021년 7월 16일
0

iamroot (Linux Internal)

목록 보기
8/24

2021년 06월 19일 15:00 ~ 22:00 스터디 진행 내용을 정리한 글입니다.

0. 스터디 공지사항

참석자

40 명

서기 및 공유

진행: 문영일님
서기: 김현우님, 김성원님
공유: 이민욱님
영상: 최영민님

진도

리눅스 커널 내부구조 (백승재, 최종무 저)

1. 페이지 프레임 (Page frame)

zone 은 자신에 속해 있는 물리 메모리를 관리하는데, 이 물리 메모리의 최소 단위를 페이지 프레임(page frame) 이라 부른다.

리눅스 커널은 메모리 할당을 물리 메모리의 최소 관리 단위인 페이지 프레임 단위(4 KiB)로 할당한다. 이를 버디 할당자 (Buddy allocator) 라 부른다.

4 KiB 단위로 할당하면 당연히 내부 단편화(internal fragmentation) 이 발생한다. 이를 위해 다시 슬랩 할당자 (Slab allocator) 를 사용한다.

2. 버디 할당자 (Buddy Allocator)

버디 할당자는 zonefree_area 배열을 통해 구축된다. free_areafree_listmap 이라는 필드를 가진다. (현재 5.10 버전의 커널에선 사라짐)

struct zone 구조체가 멤버 변수 free_area 를 가지는 것을 볼 수 있다. Order 는 지수를 의미한다. 현재 리눅스 커널(필자의 기준으로 5.10 버전) 에서 MAX_ORDER11 로 정의되어 있다.

free_area 구조체의 멤버 변수는 free_listnr_free 이다. 보다시피 map 은 사라졌다.

free_area 배열은 11 개의 엔트리를 가지며 이 숫자는 해당 free_area 가 관리하는 할당의 크기를 나타낸다. 2 의 n 승 단위 이므로 2111×42^{11 - 1} {\times} 4 KiB =4= 4 MiB 가 된다.

free_list 변수는 자신에게 할당된 free 페이지 프레임을 list 로 관리한다.

3. Lazy Buddy

작은 하나의 페이지 프레임에 대한 할당 요청이 들어오면 큰 페이지를 잘게 쪼개 할당 해주고, 해제 시 다시 작은 페이지를 병합하여 큰 페이지를 만든다.

이러한 과정이 무수히 많이 반복된다면 큰 오버헤드로 작용한다. 따라서 리눅스는 가용 메모리가 충분하다면 병합을 미루고, 가용 메모리가 부족해지면 다시 병합을 진행한다.

4. 슬랩 할당자 (Slab allocator)

버디 할당으로 발생하는 내부 단편화 문제를 막기 위해 리눅스 커널은 슬랩 할당자(Slab allocator) 를 이용한다. 버디 할당기로 할당받은 연속된 메모리 공간을 지정한 크기로 잘라서 할당한다. 일종의 캐시(cache) 개념이다.

5. 슬랩 캐시 (Slab cache)

cache 의 크기는 리눅스 내부에서 자주 사용하는 자료구조의 크기로 결정(e.g. struct task_struct)하여 내부 단편화를 최소화시킨다.
cache 는 슬랩들로 구성, 슬랩은 다시 객체로 구성된다.

객체는 세 가지 상태로 구분된다:

  1. Full - 모든 객체가 사용 가능한 상태
  2. Free - 모든 객체가 이미 사용 중
  3. Partial - 일부는 사용 중 일부는 미사용 중

리눅스 커널은 이러한 다양한 크기, 다양한 상태의 cache 를 관리하기 위해 kmem_cache 라는 자료구조를 정의했다.

kmem_cache 구조체 역시 리눅스 커널이 관리하는 자료구조로 생성하거나 소멸시킬 수 있다. kmem_cache 를 할당하기 위해서 kmem_cache 구조체 크기의 객체를 담을 수 있는, cache 를 생성하는 cache, cache_cache 를 생성한다.

슬랩 할당자로부터 객체를 할당받는 저수준 함수는 kmem_cache_alloc() 이며, 반대로 해제하는 함수는 kmem_cache_free() 이다. 슬랩 할당자가 더 이상 할당해줄 공간이 없다면 버디로부터 페이지 프레임을 할당받아야 하고 이를 위한 kmem_cache_grow() 함수를 지원한다.

glibcmalloc()free()buddy allocator 를 사용하고, 커널 내부에서 사용하는 kmalloc(), kfree() 함수는 slab allocator 를 사용한다.

6. Slab 구조

Slab 은 세 가지의 세부적인 할당기가 존재한다:

  1. slob - 메모리가 굉장히 적은 시스템에서 사용
  2. slub - 대부분의 데스크탑, 서버에서 사용
  3. slab - 성능이 안 좋아서 요즈음엔 대부분 slub 을 사용

현재 쓰는 slub 에서는 partial 상태만 존재하고 슬라이스한 모든 메모리를 다 사용하면 더 이상 관리하지 않는다.

슬랩의 할당되는 페이지 프레임의 크기는 오브젝트를 슬라이싱하여 저장했을 때 자투리가 가장 적게 남는 order 로 생성된다.

cache 는 최초 생성 시 order 를 저장해서 expand 시에도 같은 order 크기(2n2^n) 페이지 프레임만큼 할당한다.

초기 커널 부팅 시 커널에 사용되는 오브젝트 할당을 위한 cache 들을 쭉 할당한다. 모든 할당이 끝나면 사용자(여기에서 말하는 사용자는 커널 개발자를 의미, 드라이버 개발자 등등.)를 위한 cache 들을 할당 (64, 128, 196, 256, ...) 한다.

sudo cat /proc/slabinfo

의 결과를 확인한 모습이다. 보는 것처럼 kmalloc 을 위한 cache 를 미리 생성한 것을 볼 수 있다.

출처

[이미지] https://hammertux.github.io/slab-allocator
[책] 리눅스 커널 내부구조 (백승제, 최종무 저)

profile
2000.11.30

0개의 댓글