2021년 06월 19일 15:00 ~ 22:00 스터디 진행 내용을 정리한 글입니다.
40 명
진행: 문영일님
서기: 김현우님, 김성원님
공유: 이민욱님
영상: 최영민님
리눅스 커널 내부구조 (백승재, 최종무 저)
Page frame
) zone
은 자신에 속해 있는 물리 메모리를 관리하는데, 이 물리 메모리의 최소 단위를 페이지 프레임(page frame)
이라 부른다.
리눅스 커널은 메모리 할당을 물리 메모리의 최소 관리 단위인 페이지 프레임 단위(4 KiB
)로 할당한다. 이를 버디 할당자 (Buddy allocator
) 라 부른다.
4 KiB
단위로 할당하면 당연히 내부 단편화(internal fragmentation
) 이 발생한다. 이를 위해 다시 슬랩 할당자 (Slab allocator
) 를 사용한다.
Buddy Allocator
) 버디 할당자는 zone
의 free_area
배열을 통해 구축된다. free_area
는 free_list
와 map
이라는 필드를 가진다. (현재 5.10
버전의 커널에선 사라짐)
struct zone
구조체가 멤버 변수 free_area
를 가지는 것을 볼 수 있다. Order
는 지수를 의미한다. 현재 리눅스 커널(필자의 기준으로 5.10
버전) 에서 MAX_ORDER
가 11
로 정의되어 있다.
free_area
구조체의 멤버 변수는 free_list
와 nr_free
이다. 보다시피 map
은 사라졌다.
free_area
배열은 11 개의 엔트리를 가지며 이 숫자는 해당 free_area
가 관리하는 할당의 크기를 나타낸다. 2 의 n 승 단위 이므로 KiB MiB 가 된다.
free_list
변수는 자신에게 할당된 free
페이지 프레임을 list
로 관리한다.
Lazy Buddy
작은 하나의 페이지 프레임에 대한 할당 요청이 들어오면 큰 페이지를 잘게 쪼개 할당 해주고, 해제 시 다시 작은 페이지를 병합하여 큰 페이지를 만든다.
이러한 과정이 무수히 많이 반복된다면 큰 오버헤드로 작용한다. 따라서 리눅스는 가용 메모리가 충분하다면 병합을 미루고, 가용 메모리가 부족해지면 다시 병합을 진행한다.
Slab allocator
) 버디 할당으로 발생하는 내부 단편화 문제를 막기 위해 리눅스 커널은 슬랩 할당자(Slab allocator
) 를 이용한다. 버디 할당기로 할당받은 연속된 메모리 공간을 지정한 크기로 잘라서 할당한다. 일종의 캐시(cache
) 개념이다.
Slab cache
) cache
의 크기는 리눅스 내부에서 자주 사용하는 자료구조의 크기로 결정(e.g. struct task_struct
)하여 내부 단편화를 최소화시킨다.
각 cache
는 슬랩들로 구성, 슬랩은 다시 객체로 구성된다.
객체는 세 가지 상태로 구분된다:
Full
- 모든 객체가 사용 가능한 상태Free
- 모든 객체가 이미 사용 중Partial
- 일부는 사용 중 일부는 미사용 중리눅스 커널은 이러한 다양한 크기, 다양한 상태의 cache
를 관리하기 위해 kmem_cache
라는 자료구조를 정의했다.
kmem_cache
구조체 역시 리눅스 커널이 관리하는 자료구조로 생성하거나 소멸시킬 수 있다. kmem_cache
를 할당하기 위해서 kmem_cache
구조체 크기의 객체를 담을 수 있는, cache
를 생성하는 cache
, cache_cache
를 생성한다.
슬랩 할당자로부터 객체를 할당받는 저수준 함수는 kmem_cache_alloc()
이며, 반대로 해제하는 함수는 kmem_cache_free()
이다. 슬랩 할당자가 더 이상 할당해줄 공간이 없다면 버디로부터 페이지 프레임을 할당받아야 하고 이를 위한 kmem_cache_grow()
함수를 지원한다.
glibc
의 malloc()
과 free()
는 buddy allocator
를 사용하고, 커널 내부에서 사용하는 kmalloc()
, kfree()
함수는 slab allocator
를 사용한다.
Slab
구조 Slab
은 세 가지의 세부적인 할당기가 존재한다:
slob
- 메모리가 굉장히 적은 시스템에서 사용slub
- 대부분의 데스크탑, 서버에서 사용slab
- 성능이 안 좋아서 요즈음엔 대부분 slub
을 사용 현재 쓰는 slub
에서는 partial
상태만 존재하고 슬라이스한 모든 메모리를 다 사용하면 더 이상 관리하지 않는다.
슬랩의 할당되는 페이지 프레임의 크기는 오브젝트를 슬라이싱하여 저장했을 때 자투리가 가장 적게 남는 order
로 생성된다.
cache
는 최초 생성 시 order
를 저장해서 expand
시에도 같은 order
크기() 페이지 프레임만큼 할당한다.
초기 커널 부팅 시 커널에 사용되는 오브젝트 할당을 위한 cache
들을 쭉 할당한다. 모든 할당이 끝나면 사용자(여기에서 말하는 사용자는 커널 개발자를 의미, 드라이버 개발자 등등.)를 위한 cache
들을 할당 (64
, 128
, 196
, 256
, ...) 한다.
sudo cat /proc/slabinfo
의 결과를 확인한 모습이다. 보는 것처럼 kmalloc
을 위한 cache
를 미리 생성한 것을 볼 수 있다.
[이미지] https://hammertux.github.io/slab-allocator
[책] 리눅스 커널 내부구조 (백승제, 최종무 저)