slab 소스 코드 분석 - slab 초기 화 부터
35801 단어 Linux 커 널 분석
slab 의 초기 화 는 당연히 커 널 시작 부터 시작 되 었 습 니 다.커 널 시작 startkernel () 함수:
// :)
asmlinkage void __init start_kernel(void)
{
...
mem_init(); //
kmem_cache_init(); //slab
...
}
그 중 kmem 호출cache_init () 함수, 이것 이 바로 slab 의 초기 화 입 니 다.
/*
* Initialisation. Called after the page allocator have been initialised and
* before smp_init().
*/
// , kmalloc
void __init kmem_cache_init(void)
{
size_t left_over;
struct cache_sizes *sizes;
struct cache_names *names;
int i;
int order;
int node;
// 1 , shared cache
if (num_possible_nodes() == 1) // , #define num_possible_nodes() 1
use_alien_caches = 0; // linux
// slab , kmalloc ,
// slab , kmalloc !!
// , initkem_list3 ,
//initkmem_list3 ,
for (i = 0; i < NUM_INIT_LISTS; i++) { //FIXME: NUM_INIT_LIST , 2 * MAX_NODES+1 ? 3 * MAX_NODES
kmem_list3_init(&initkmem_list3[i]);
if (i < MAX_NUMNODES)
// cache_cache slab cache kmem_cache , cache_cache
// kmem_cache slab
cache_cache.nodelists[i] = NULL;
}
/*
* Fragmentation resistance on low memory - only use bigger
* page orders on machines with more than 32MB of memory.
*/
// slab_break_gfp_order slab , 。
// : (1) 32MB, ,BREAK_GFP_ORDER_HI 1,
// slab , 3 , 8192K ( 4K, 4096);
//(2) 32MB, BREAK_GFP_ORDER_HI 0, , ,
if (num_physpages > (32 << 20) >> PAGE_SHIFT)
slab_break_gfp_order = BREAK_GFP_ORDER_HI; // slab
/* Bootstrap is tricky, because several objects are allocated
* from caches that do not exist yet:
* 1) initialize the cache_cache cache: it contains the struct
* kmem_cache structures of all caches, except cache_cache itself:
* cache_cache is statically allocated.
* Initially an __init data area is used for the head array and the
* kmem_list3 structures, it's replaced with a kmalloc allocated
* array at the end of the bootstrap.
* 2) Create the first kmalloc cache.
* The struct kmem_cache for the new cache is allocated normally.
* An __init data area is used for the head array.
* 3) Create the remaining kmalloc caches, with minimally sized
* head arrays.
* 4) Replace the __init data head arrays for cache_cache and the first
* kmalloc cache with kmalloc allocated arrays.
* 5) Replace the __init data for kmem_list3 for cache_cache and
* the other cache's with kmalloc allocated memory.
* 6) Resize the head arrays of the kmalloc caches to their final sizes.
*/
node = numa_node_id(); // id, 0, CPU , 0
/* 1) create the cache_cache */
// cache_chain kmem_cache
INIT_LIST_HEAD(&cache_chain);
list_add(&cache_cache.next, &cache_chain);
// cache , L1
cache_cache.colour_off = cache_line_size(); // L1 #define cache_line_size() L1_CACHE_BYTES
// cache_cache per-CPU cache, kmalloc, initarray_cache
cache_cache.array[smp_processor_id()] = &initarray_cache.cache;
// slab , , CACHE_CACHE 0, cache_cache
cache_cache.nodelists[node] = &initkmem_list3[CACHE_CACHE];
/*
* struct kmem_cache size depends on nr_node_ids, which
* can be less than MAX_NUMNODES.
*/
//buffer_size , cache_cache kmem_cache , buffer_size kmem_cache
// ,nodelists kmem_cache , , nodelists UMA , 1 kmem_list3
cache_cache.buffer_size = offsetof(struct kmem_cache, nodelists) +
nr_node_ids * sizeof(struct kmem_list3 *);
#if 0
#if DEBUG
cache_cache.obj_size = cache_cache.buffer_size;
#endif
#endif
// buffer_size , buffer_size
// cache line
cache_cache.buffer_size = ALIGN(cache_cache.buffer_size,
cache_line_size());
// , slab
cache_cache.reciprocal_buffer_size =
reciprocal_value(cache_cache.buffer_size);
// cache_cache slab ,order slab (PAGE_SIZEE<
for (order = 0; order < MAX_ORDER; order++) { //#define MAX_ORDER 11
cache_estimate(order, cache_cache.buffer_size, //buffer_size cache line
cache_line_size(), 0, &left_over, &cache_cache.num); // cache_cache
if (cache_cache.num) //num 0 struct kmem_cache , , order , gfporder
break;
}
BUG_ON(!cache_cache.num); //
cache_cache.gfporder = order; //gfporder slab 2^gfproder , order gfporder
//colour_off L1_CACHE_BYTES, L1 ,
// / L1 , colour , , 0
cache_cache.colour = left_over / cache_cache.colour_off; // colour , colour_off
// slab kmem_bufctl_t
cache_cache.slab_size = ALIGN(cache_cache.num * sizeof(kmem_bufctl_t) +
sizeof(struct slab), cache_line_size());
/* 2+3) create the kmalloc caches */
sizes = malloc_sizes; //malloc_sizes
names = cache_names; //cache_name cache
/*
* Initialize the caches that provide memory for the array cache and the
* kmem_list3 structures first. Without this, further allocations will
* bug.
*/
// struct array_cache struct kmem_list3 general cache,
//INDEX_AC local cache struct arraycache_init kmalloc size , general cache, cache local cache
sizes[INDEX_AC].cs_cachep = kmem_cache_create(names[INDEX_AC].name,
sizes[INDEX_AC].cs_size,
ARCH_KMALLOC_MINALIGN,
ARCH_KMALLOC_FLAGS|SLAB_PANIC, //#define ARCH_KMALLOC_FLAGS SLAB_HWCACHE_ALIGN,
NULL, NULL);
if (INDEX_AC != INDEX_L3) {
// struct kmem_list3 struct arraycache_init kmalloc size , ,
// struct kmem_list3 cache, cache
sizes[INDEX_L3].cs_cachep =
kmem_cache_create(names[INDEX_L3].name,
sizes[INDEX_L3].cs_size,
ARCH_KMALLOC_MINALIGN,
ARCH_KMALLOC_FLAGS|SLAB_PANIC,
NULL, NULL);
}
slab_early_init = 0;// ,slab early init , , slab
//sizes->cs_size malloc_sizes[0], 32
while (sizes->cs_size != ULONG_MAX) { // kmalloc ,ULONG_MAX ,
/*
* For performance, all the general caches are L1 aligned.
* This should be particularly beneficial on SMP boxes, as it
* eliminates( ) "false sharing".
* Note for systems short on memory removing the alignment will
* allow tighter( ) packing of the smaller caches.
*/
if (!sizes->cs_cachep) {
sizes->cs_cachep = kmem_cache_create(names->name,
sizes->cs_size,
ARCH_KMALLOC_MINALIGN,
ARCH_KMALLOC_FLAGS|SLAB_PANIC,
NULL, NULL);
}
#ifdef CONFIG_ZONE_DMA // DMA, kmem_cache , DMA,
sizes->cs_dmacachep = kmem_cache_create(
names->name_dma,
sizes->cs_size,
ARCH_KMALLOC_MINALIGN,
ARCH_KMALLOC_FLAGS|SLAB_CACHE_DMA|
SLAB_PANIC,
NULL, NULL);
#endif
sizes++; // , ++, , general caches, ULONG_MAX
names++;
}
/* 4) Replace the bootstrap head arrays */
{
struct array_cache *ptr;
// arraycache initarray_cache
ptr = kmalloc(sizeof(struct arraycache_init), GFP_KERNEL); //GFP_KERNEL
//
local_irq_disable();
BUG_ON(cpu_cache_get(&cache_cache) != &initarray_cache.cache);
memcpy(ptr, cpu_cache_get(&cache_cache),
sizeof(struct arraycache_init)); // cache_cache per-cpu array_cache ptr
/*
* Do not assume that spinlocks can be initialized via memcpy:
*/
spin_lock_init(&ptr->lock);
cache_cache.array[smp_processor_id()] = ptr; // ptr?
local_irq_enable();
ptr = kmalloc(sizeof(struct arraycache_init), GFP_KERNEL);
local_irq_disable();
BUG_ON(cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep)
!= &initarray_generic.cache);
memcpy(ptr, cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep),
sizeof(struct arraycache_init));
/*
* Do not assume that spinlocks can be initialized via memcpy:
*/
spin_lock_init(&ptr->lock);
malloc_sizes[INDEX_AC].cs_cachep->array[smp_processor_id()] =
ptr;
local_irq_enable();
}
/* 5) Replace the bootstrap kmem_list3's */
{
int nid;
/* Replace the static kmem_list3 structures for the boot cpu */
init_list(&cache_cache, &initkmem_list3[CACHE_CACHE], node);
for_each_online_node(nid) {
init_list(malloc_sizes[INDEX_AC].cs_cachep,
&initkmem_list3[SIZE_AC + nid], nid);
if (INDEX_AC != INDEX_L3) {
init_list(malloc_sizes[INDEX_L3].cs_cachep,
&initkmem_list3[SIZE_L3 + nid], nid);
}
}
}
/* 6) resize the head arrays to their final sizes */
{
struct kmem_cache *cachep;
mutex_lock(&cache_chain_mutex);
list_for_each_entry(cachep, &cache_chain, next)
if (enable_cpucache(cachep)) // ,
BUG();
mutex_unlock(&cache_chain_mutex);
}
/* Annotate slab for lockdep -- annotate the malloc caches */
init_lock_keys();
/* Done! */
g_cpucache_up = FULL;
/*
* Register a cpu startup notifier callback that initializes
* cpu_cache_get for all new cpus
*/
register_cpu_notifier(&cpucache_notifier);
/*
* The reap timers are started later, with a module init call: That part
* of the kernel is not yet operational.
*/
}
이 함수 가 바로 slab 초기 화의 주간 입 니 다.실행 절차:
/* internal cache of cache description objs */
//
static struct kmem_cache cache_cache = {
.batchcount = 1,
.limit = BOOT_CPUCACHE_ENTRIES,
.shared = 1,
.buffer_size = sizeof(struct kmem_cache),
.name = "kmem_cache", // , kmem_cache
};
혹은
static struct arraycache_init initarray_cache /*__initdata*/ =
{ {0, BOOT_CPUCACHE_ENTRIES, 1, 0} };
static struct arraycache_init initarray_generic =
{ {0, BOOT_CPUCACHE_ENTRIES, 1, 0} };
이 함수 의 기타 보조 함 수 는 다음 과 같다. 1. 3 체인 초기 화 함수
static void kmem_list3_init(struct kmem_list3 *parent)
{
INIT_LIST_HEAD(&parent->slabs_full);
INIT_LIST_HEAD(&parent->slabs_partial);
INIT_LIST_HEAD(&parent->slabs_free);
parent->shared = NULL;
parent->alien = NULL;
parent->colour_next = 0;
spin_lock_init(&parent->list_lock);
parent->free_objects = 0;
parent->free_touched = 0;
}
이것 은 별 쓸모 가 없다. 단지 초기 화 일 뿐이다.
#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1)
#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
이 매크로 는 메모리 가 a 의 크기 를 기본 단위 로 정렬 하 는 것 을 제어 하 는 데 사 용 됩 니 다. 0 ~ 7 을 한 그룹 으로 유추 합 니 다.
이 매크로 에 대한 상세 한 설명 은 참조 할 수 있 습 니 다. 커 널 매크로 ALIGN 의 의 미 는 81943 입 니 다. cache estimate () 함수 라 는 함 수 는 모든 slab 의 대상 의 수량 과 공간 을 낭비 하 는 크기 를 계산 하 는 데 사 용 됩 니 다. 주석 중의 관리 대상 은 앞에서 말 한 관리자 입 니 다.
/*
* Calculate the number of objects and left-over bytes for a given buffer size.
*/
static void cache_estimate(unsigned long gfporder, size_t buffer_size,
size_t align, int flags, size_t *left_over,
unsigned int *num)
{
int nr_objs;
size_t mgmt_size;
//slab 2^gfporder
size_t slab_size = PAGE_SIZE << gfporder; //#define PAGE_SIZE 0x400 ( 1024),FIXME: 1K , ?
/*
* The slab management structure can be either off the slab or // off-slab on-slab
* on it. For the latter case, the memory allocated for a
* slab is used for: // :
*
* - The struct slab //slab
* - One kmem_bufctl_t for each object // kmem_bufctl_t
* - Padding to respect alignment of @align //
* - @buffer_size bytes for each object //
*
* If the slab management structure is off the slab, then the
* alignment will already be calculated into the size. Because // off-slab,align
* the slabs are all pages aligned, the objects will be at the // ,
* correct alignment when allocated.
*/
// slab, slab ,
if (flags & CFLGS_OFF_SLAB) {
// slab , slab
mgmt_size = 0;
// = slab /
nr_objs = slab_size / buffer_size; // buffer_size cache line
//
if (nr_objs > SLAB_LIMIT)
nr_objs = SLAB_LIMIT;
} else {
/*
* Ignore padding for the initial guess. The padding
* is at most @align-1 bytes, and @buffer_size is at
* least @align. In the worst case, this result will
* be one greater than the number of objects that fit
* into the memory allocation when taking the padding
* into account.
*/
// slab,slab slab , slab :
// struct slab , kmem_bufctl_t (kmem_bufctl_t slab )
//slab , / ( + sizeof(kmem_bufctl_t)),
nr_objs = (slab_size - sizeof(struct slab)) /
(buffer_size + sizeof(kmem_bufctl_t));
/*
* This calculated number will be either the right
* amount, or one greater than what we want.
*/
// slab ,
if (slab_mgmt_size(nr_objs, align) + nr_objs*buffer_size
> slab_size)
nr_objs--;
//
if (nr_objs > SLAB_LIMIT)
nr_objs = SLAB_LIMIT;
//
mgmt_size = slab_mgmt_size(nr_objs, align);
}
// slab
*num = nr_objs;
// slab ( 0, ), slab
*left_over = slab_size - nr_objs*buffer_size - mgmt_size;
}
5. malloc sizes [] 표 는 kmem cache create () 함 수 를 통 해 유 니 버 설 버퍼 를 만들어 야 합 니 다. 최종 적 으로 파트너 시스템 에 신청 합 니 다. 얼마나 만 들 까요? 재 미 있 는 곳 이 있 습 니 다. 먼저 INDEX AC 를 보 세 요.
#define INDEX_AC index_of(sizeof(struct arraycache_init))
index of 는 다음 과 같 습 니 다:
/*
* This function must be completely optimized away if a constant is passed to
* it. Mostly the same as what is in linux/slab.h except it returns an index.
*/
static __always_inline int index_of(const size_t size) // , mallloc_size
{
extern void __bad_size(void);
if (__builtin_constant_p(size)) {
int i = 0;
#define CACHE(x) \
if (size <=x) \ // size
return i; \
else \
i++; // ,
#include "linux/kmalloc_sizes.h"
#undef CACHE
__bad_size();
} else
__bad_size();
return 0;
}
왜 index of 가 크기 를 정할 수 있다 고 말 하 는 지, 이 건 정말 판 타지 입 니 다. 이 말 에 주의 하 세 요.
#include "linux/kmalloc_sizes.h"
linux / kmalloc sizes. h 의 내용 은 다음 과 같 습 니 다.
#if (PAGE_SIZE == 4096)
CACHE(32)
#endif
CACHE(64)
#if L1_CACHE_BYTES < 64
CACHE(96)
#endif
CACHE(128)
#if L1_CACHE_BYTES < 128
CACHE(192)
#endif
CACHE(256)
CACHE(512)
CACHE(1024)
CACHE(2048)
CACHE(4096)
CACHE(8192)
CACHE(16384)
CACHE(32768)
CACHE(65536)
CACHE(131072)
#if KMALLOC_MAX_SIZE >= 262144
CACHE(262144)
#endif
#if KMALLOC_MAX_SIZE >= 524288
CACHE(524288)
#endif
#if KMALLOC_MAX_SIZE >= 1048576
CACHE(1048576)
#endif
#if KMALLOC_MAX_SIZE >= 2097152
CACHE(2097152)
#endif
#if KMALLOC_MAX_SIZE >= 4194304
CACHE(4194304)
#endif
#if KMALLOC_MAX_SIZE >= 8388608
CACHE(8388608)
#endif
#if KMALLOC_MAX_SIZE >= 16777216
CACHE(16777216)
#endif
#if KMALLOC_MAX_SIZE >= 33554432
CACHE(33554432)
#endif
index of 함수 에서 CACHE (x) 매크로 를 부분 적 으로 정의 하고 이 헤더 파일 을 도입 합 니 다. 이 는 헤더 파일 의 모든 내용 을 index of 함수 의 그 문장 이 있 는 위치 에 추가 한 것 으로 이해 할 수 있 습 니 다. 그러면 프로그램 은 다음 에 끊임없이 실행 자 부분 매크로 CACHE (X) 를 실행 합 니 다.이 매크로 는 함수 적 의미 가 있 습 니 다. 정확 한 size 에 맞 으 면 i 값 을 직접 되 돌려 줍 니 다.
그러면 아래 표 시 된 i 의 값 만 알 면 안 됩 니 다. malloc sizes [] 표 는 언제 초기 화 되 었 습 니까? 정 답 은 정적 으로 초기 화 되 었 습 니 다.
/*
* These are the default caches for kmalloc. Custom caches can have other sizes.
*/
struct cache_sizes malloc_sizes[] = { // malloc_size
#define CACHE(x) { .cs_size = (x) },
#include //
CACHE(ULONG_MAX)
#undef CACHE
};
위의 코드 에서 똑 같이 CACHE (X) 를 정 의 했 지만 이것 은 이전의 그 역할 과 다 릅 니 다. 그들 둘 은 모두 부분 매크로 이 고 사용 한 후에 undef 이 므 로 영향 을 주지 않 습 니 다.
이 매크로 는 "{. cs size = (x)}" 로 정의 되 어 있 습 니 다. 이 쉼표 에 주의 하 시 겠 습 니까? 아래 는 kmalloc size 헤더 파일 에 있 는 많은 CACHE (x) 를 도입 합 니 다. 각각 이 형식 입 니 다. 이것 이 바로 배열 의 초기 화 방식 이 아 닙 니까? 모든 데이터 형식 이 cache size 형식 일 뿐 입 니 다. int array [] = {1}, {2},..} 에 해당 합 니 다., 대충 그렇습니다. cache names [] 표 는 같 습 니 다.
cache sizes 형식 은 다음 과 같 습 니 다.
/* Size description struct for general caches. */
struct cache_sizes {
size_t cs_size; //
struct kmem_cache *cs_cachep; //
#ifdef CONFIG_ZONE_DMA
struct kmem_cache *cs_dmacachep;
#endif
};
따라서 위 와 같은 초기 화 방식 을 통 해 malloc sizes [] 표 에서 작은 것 부터 큰 순 으로 모든 cache sizes 의 cs size 구성원 을 초기 화 합 니 다. 따라서 malloc size [i] 는 크기 의 버퍼 를 설명 하 는 cache sizes 구조 에 대응 합 니 다. cs cachep 를 통 해 해당 하 는 크기 의 유 니 버 설 버퍼 를 얻 을 수 있 습 니 다!
DMA 라면 두 가지 유 니 버 설 버퍼 를 설정 합 니 다. 8. init_list () 함수
/*
* swap the static kmem_list3 with kmalloced memory
*/
static void init_list(struct kmem_cache *cachep, struct kmem_list3 *list,
int nodeid)
{
struct kmem_list3 *ptr;
ptr = kmalloc_node(sizeof(struct kmem_list3), GFP_KERNEL, nodeid);
BUG_ON(!ptr);
local_irq_disable();
memcpy(ptr, list, sizeof(struct kmem_list3));
/*
* Do not assume that spinlocks can be initialized via memcpy:
*/
spin_lock_init(&ptr->list_lock);
MAKE_ALL_LISTS(cachep, ptr, nodeid);
cachep->nodelists[nodeid] = ptr;
local_irq_enable();
}
이것 은 kmalloc 를 이용 하여 3 체인 과 정적 3 체인 교환 을 신청 하 는 함수 입 니 다. 왜 kmalloc 를 이용 하 는 것 이 라 고 말 합 니까? 호출 된 kmalloc 를 보 세 요.node () 함수:
static inline void *kmalloc_node(size_t size, gfp_t flags, int node)
{
return kmalloc(size, flags);
}
바로 이 렇 습 니 다. 우 리 는 '닭 과 알' 문 제 를 해결 하기 위해 먼저 몇 개의 정적 변 수 를 설정 합 니 다. 예 를 들 어 세 개의 체인 initkmemlist 3, 우선 그것 으로 우리 의 cache 를 합성 합 니 다.cache, 그리고 cachecache, 이것 은 버퍼 를 위 한 규칙 적 인 버퍼 입 니 다. 이 를 통 해 우 리 는 크기 의 다른 버퍼 를 마음대로 합성 할 수 있 습 니 다.kmemcache_init () 함수 후반 부 에 kmalloc 할당 대상 을 버퍼 로 사용 할 수 있 습 니 다. 교체 하기 전에 모든 보조 cachecache 의 정적 데이터 구조 (예 를 들 어 initarray cache, initkmem list 3) 는 이후 에 이러한 정적 데이터 구 조 는 더 이상 사용 하지 않 을 것 이다.
cache_cache 는 버퍼 크기 를 위 한 규칙 을 만 들 었 습 니 다. 이 는 모든 버퍼 자체 의 크기 도 같 지만 buffer 에 불과 합 니 다.size 필드 에서 설명 하 는 분배 대상 의 크기 가 다 릅 니 다. 앞으로 서로 다른 크기 의 대상 은 이것 으로 서로 다른 버퍼 를 찾 아 분배 하면 됩 니 다.
참고: