티스토리 뷰
20241130 4.8 kmalloc 과 vmalloc (p487) ~ 4.9 per-cpu 할당자
clucle 2024. 12. 7. 21:054.8 kmalloc 과 vmalloc (p487)
4.8.1 kmalloc과 vmalloc 의 특징
구분 | kmalloc | vmalloc |
---|---|---|
공통점 | 커널에서 페이지 단위가 아닌 메모리를 할당 | |
할당 | 연속된 물리 메모리를 할당 | 가상 메모리로 연속적인 것처럼 보이게 함 |
4.8.2 GFP(Get Free Page) 플래그
- 할당을 시도할 존을 지정
- 페이지 MOBILITY와 장소 hint 관련 플래그
- 워터마크 관련 플래그
- 페이지 회수 관련 플래그
- 액션 관련 플래그
- 복합 GFP 플래그
4.8.3 kmalloc 할당
- 슬럽을 사용할때
- 큰 크기나 DMA 요청에 대해서는 버디 시스템으로부터 페이지를 할당 (alloc_pages)
- 그렇지 않은 경우, kmalloc kmem 캐시를 통해 메모리 할당 (slab_alloc)
4-151 p491 include/linux/slab.h kmalloc
// size 가 상수이면서 SIZE 가 클때, 버디 시스템으로 부터 페이지 할당
if (__builtin_constant_p(size)) {
if (size > KMALLOC_MAX_CACHE_SIZE)
return kmalloc_large(size, flags);
// dma 가 아닌 경우 & 사이즈 작았던 경우 slab alloc
결국 kmem_cache s 구조체를 찾아서 slab_alloc
4-152 p492 mm/slab_common.c kmalloc_order
큰페이지에 대해 __GFP_COMP 를 사용해 여러 페이지를 버디 시스템에서 할당 받는다.
flags |= __GFP_COMP;
page = alloc_kmem_pages(flags, order);
ret = page ? page_address(page) : null
include/linux/mm.h page_address
- page 구조체로 부터 물리 메모리 페이지의 가상 주소를 획득하기 위함
#if defined(WANT_PAGE_VIRTUAL)
static inline void *page_address(const struct page *page)
{
return page->virtual;
}
static inline void set_page_address(struct page *page, void *address)
{
page->virtual = address;
}
#define page_address_init() do { } while(0)
#endif
#if defined(HASHED_PAGE_VIRTUAL)
void *page_address(const struct page *page);
void set_page_address(struct page *page, void *virtual);
void page_address_init(void);
#endif
#if !defined(HASHED_PAGE_VIRTUAL) && !defined(WANT_PAGE_VIRTUAL)
#define page_address(page) lowmem_page_address(page)
#define set_page_address(page, address) do { } while(0)
#define page_address_init() do { } while(0)
#endif
4-153 mm/page_alloc.c alloc_kmem_pages
- buddy 에서 페이지를 획득해온다.
static inline struct page *
alloc_pages(gfp_t gfp_mask, unsigned int order)
{
return alloc_pages_current(gfp_mask, order);
}
mm mempolicy.c alloc_pages_current
mm page_alloc.c __alloc_pages_nodemask
4-154 p493 include/linux/slab.h kmalloc_index
- kmalloc 시 byte 를 보고 index 를 결정한다. ~64MB 까지 지원
- size_index 여기의 index 가 됨
slab_common.c
static s8 size_index[24] = {
3, /* 8 */
4, /* 16 */
5, /* 24 */
5, /* 32 */
6, /* 40 */
6, /* 48 */
6, /* 56 */
6, /* 64 */
1, /* 72 */
1, /* 80 */
1, /* 88 */
1, /* 96 */
7, /* 104 */
7, /* 112 */
7, /* 120 */
7, /* 128 */
2, /* 136 */
2, /* 144 */
2, /* 152 */
2, /* 160 */
2, /* 168 */
2, /* 176 */
2, /* 184 */
2 /* 192 */
};
static inline int size_index_elem(size_t bytes)
{
return (bytes - 1) / 8;
}
4-155 p495 mm/slab-common.c kmalloc_slab
- 192 보다 작은 경우는 테이블에서 index 획득, 그렇지 않은 경우
- kmalloc_cache[size_index[ 192 보다 작거나 같을때 size_index_elem(size) or 192 초과일 때 fls(size - 1) ]
fls
1024 - 2^10 = 11
2048 - 2^11 = 12
4.8.4 kmallopc 으로 할당한 메모리 해제
4-156 p497 mm/slub.c kfree
4.8.5 vmalloc 초기화
연속된 가상주소 메모리를 할당 받기 위한 메커니즘 초기화
- vmalloc : 연속된 가상 주소를 갖는 메모리 할당
- vfree : vmalloc 으로 할당한 메모리 해제
- vmap : 연속된 가상 주소 매핑
- vunmap : 가상주소 매핑 해제
4-156 p497 vmalloc_init
- vmalloc 초기화, 리스트 헤드 만들기와 필드 초기화 정도만 진행한다.
- per-cpu vmap_block_queue
- per-cpu vfree_deferred
- vmlist : 기존 vmlist 에 등록된 vm_struct 를 읽어 vm_area 를 구성한다
- vmap_block_queue 는 할당이나 free 등 메모리 매핑을 큐로 동작하는 듯하다. 오래 걸리기 떄문에 큐로 지연하는듯 (아직 잘 모름)
#define per_cpu(var, cpu) (*per_cpu_ptr(&(var), cpu))
struct vfree_deferred {
struct llist_head list;
struct work_struct wq;
};
INIT_WORK(&p->wq, free_work);
static void free_work(struct work_struct *w)
{
struct vfree_deferred *p = container_of(w, struct vfree_deferred, wq);
struct llist_node *llnode = llist_del_all(&p->list);
while (llnode) {
void *p = llnode;
llnode = llist_next(llnode);
__vunmap(p, 1);
}
}
vfree 를 queue 에 담아서 처리하기 위해서 지연시키는 용도
4.8.6 vmalloc 을 사용한 메모리 할당
mm/vmalloc.c vmalloc
void *vmalloc(unsigned long size)
4-158 p501 mm/vmalloc.c __vmalloc_node_range
- 요청 노드에서 연속된 가상 메모리를 할당한다. 가상 주소는 지정된 범위 내 빈 공간을 사용한다.
void *__vmalloc_node_range(unsigned long size, unsigned long align,
unsigned long start, unsigned long end, gfp_t gfp_mask,
pgprot_t prot, unsigned long vm_flags, int node,
const void *caller)
__get_vm_area_node // 들어갈 수 있는 vm_area 획득
__vmalloc_area_node // page 구조체와 page 할당 (4-159)
4-159 p503 mm/vmalloc.c __vmalloc_area_node
- page 구조체 테이블을 할당 받고, 싱글 페이지를 할당받아서 연결하고 페이지 테이블에 매핑한다.
// page 구조체를 담기 위한 array 사이즈가 page 보다 작다면 슬럽을, 그렇지 않다면 다시 vmalloc 을 불러 (nestesd) 메모리를 확보한다.
if (array_size > PAGE_SIZE) {
pages = __vmalloc_node(array_size, 1, nested_gfp|__GFP_HIGHMEM,
PAGE_KERNEL, node, area->caller);
} else {
pages = kmalloc_node(array_size, nested_gfp, node);
}
// nr_pages 만큼 돌며 싱글 페이지를 획득
for (i = 0; i < area->nr_pages; i++) {
// pgd pud pmd pte 테이블에 매핑
map_vm_area
// 실패시 vfree 호출 4-161
궁금증 : 그러면 buddy 에서 order 로 크게 페이지 획득하는건 kmalloc 에서만 쓰나?
- gpt : alloc_pages, __get_free_pages 에서도 사용합니다. alloc_pages와 __get_free_pages는 커널 내부에서 다양한 상황에서 사용됩니다. 이들은 메모리 할당 시 더 낮은 수준에서 직접적으로 물리 메모리 페이지를 관리하는 함수로, 특정한 요구 사항이 있는 경우에 사용됩니다.
4.8.7 vmalloc() 으로 할당받은 매모리 해제
4-161 p505 mm/vmallo.c vfree
- vm_area 를 리스트에서 제거하고, vm_area 가 갖고있던 페이지를 모두 해제
What is non-maskable interrupt (NMI)?
A NMI is a hardware interrupt that is exempt from any interrupt-masking enabled by the operating system (e.g. CentOS 8.5). In nearly every situation, it is in response to [non-recoverable hardware errors](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux_for_real_time/7/html/reference_guide/non-maskable_interrupts).
BUG_ON(in_nmi());
4.9 per-cpu 할당자
4.9.1 개요
변수를 cpu 수만큼 할당
별도의 락을 잡을 필요 없어 성능이 빠르고, 캐시 히트율이 높다
per-cpu 의 주요 특징
태스크 선점 & 인터럽트의 보호는 필요하다.
캐시 객체 & 통계 카운터에서 자주 사용된다.
유닛 : cpu 마다 per-cpu 데이터가 저장되는 공간
static 영역 : 부팅시 결정
reserved 영역 : 모듈에서 사용
dynamic : 함수를 통해 동적으로 추가
1개 유닛이 사용하는 페이지수와 바이트수는 변수로 관리된다. pcpu_unit_pages / pcpu_unit_size
- cpu->unit 매핑
- pcpu_unit_map[cpu] ->unit
- pcpu_unit_offsets[unit index] -> offset (node 안에 unit 이 여러개인것 같다. unit index 는 node 와 관계없이 unique index)
- unit->cpu 매핑
- 역으로 unit 도 cpu 에 매핑을 생성
chunk
유닛 전체가 모인 단위
NUMA 시스템에서 1개의 chunk 난 각 노드의 메모리를 사용하여 할당됨 (first chunk)
- first chunk : static, reserved, dynamic
- added chunk : dynamic
그러면 노드의 chunk 는 해당 노드에 연결된 cpu 들 각각에 대해 유닛이 하나씩 있겠구나.
struct pcpu_chunk {
void *base_addr; /* base address of this chunk */
...
}
reserved 영역(모듈) 은 없을 수도 있다.
이에따라 schunk, dchunk 의 매핑이 달라진다. (p514)
'개발 > 코드로 알아보는 ARM 리눅스 커널 TIL' 카테고리의 다른 글
20241214 4.9.3 per-cpu first_chunk 구성 & 초기화 & 동적할당 (1) | 2024.12.21 |
---|---|
20241207 4.9.2 per-cpu (p516) first_chunk 할당 과정 (0) | 2024.12.07 |
20241123 4.7.6 슬랩 객체 할당 (p449) & 4.7.7 슬랩 객체 해제 (0) | 2024.11.30 |
20241116 4.7.5슬랩 페이지와 슬랩 객체 할당 (p442) (1) | 2024.11.21 |
20241109 4.7.4 kmem 캐시 생성 (p426) (0) | 2024.11.21 |
- Total
- Today
- Yesterday
- Obstacle Avoidance
- 영상 픽셀화 하기
- Golang
- SuffixArray
- 봄날에 스케치
- boost
- it's called a vrpit
- red underline
- shared_from_this
- RVO
- Quest2
- C++
- 코어 남기기
- hole-punching
- print shared_ptr class member variable
- set value
- cockroach db
- ad skip
- 잘못된 빨간줄
- Reciprocal n-body Collision Avoidance
- 면접
- vrpit
- 클래스 맴버 변수 출력하기
- chrome-extension
- vr핏
- Visual Studio
- 카카오
- mysql
- 에러 위치 찾기
- 우리는 vr핏이라고 부릅니다
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |