
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
[책] 리눅스 커널 내부구조 (백승제, 최종무 저)