what is slab ( in-deepth )

해당글을 읽고 와야 한다.

다음 사이트를 참고해서 만들었다.

일단 구조적으로 어떤 방식으로 slab이 작동하는지 알았으면, 이제 실제 커널 코드를 보면서 공부를 해보자

우리가 슬랩캐시 ( ex. kmalloc-4k )도 각각 관리를 하고 있을텐데 이를 커널에서는 구조체로 관리하고 있다.

C
struct kmem_cache {
	struct kmem_cache_cpu __percpu *cpu_slab;
	/* Used for retriving partial slabs etc */
	unsigned long flags;
	unsigned long min_partial;
	int size;		/* The size of an object including meta data */
	int object_size;	/* The size of an object without meta data */
	int offset;		/* Free pointer offset. */
#ifdef CONFIG_SLUB_CPU_PARTIAL
	int cpu_partial;	/* Number of per cpu partial objects to keep around */
#endif
	struct kmem_cache_order_objects oo;

	/* Allocation and freeing of slabs */
	struct kmem_cache_order_objects max;
	struct kmem_cache_order_objects min;
	gfp_t allocflags;	/* gfp flags to use on each alloc */
	int refcount;		/* Refcount for slab cache destroy */
	void (*ctor)(void *);
	int inuse;		/* Offset to metadata */
	int align;		/* Alignment */
	int reserved;		/* Reserved bytes at the end of slabs */
	int red_left_pad;	/* Left redzone padding size */
	const char *name;	/* Name (only for display!) */
	struct list_head list;	/* List of slab caches */
#ifdef CONFIG_SYSFS
	struct kobject kobj;	/* For sysfs */
	struct work_struct kobj_remove_work;
#endif
#ifdef CONFIG_MEMCG
	struct memcg_cache_params memcg_params;
	int max_attr_size; /* for propagation, maximum size of a stored attr */
#ifdef CONFIG_SYSFS
	struct kset *memcg_kset;
#endif
#endif

#ifdef CONFIG_SLAB_FREELIST_HARDENED
	unsigned long random;
#endif

#ifdef CONFIG_NUMA
	/*
	 * Defragmentation by allocating from a remote node.
	 */
	int remote_node_defrag_ratio;
#endif

#ifdef CONFIG_SLAB_FREELIST_RANDOM
	unsigned int *random_seq;
#endif

#ifdef CONFIG_KASAN
	struct kasan_cache kasan_info;
#endif

	struct kmem_cache_node *node[MAX_NUMNODES];
};

다음과 같이 되어 있는데 우리가 흔히 아는 부분은

C
struct kmem_cache {
	struct kmem_cache_cpu __percpu *cpu_slab;

		...

		...

	struct kmem_cache_node *node[MAX_NUMNODES];
}

다음 두 부분일 것이다.

per cpu 부분을 먼저 보면,

C
struct kmem_cache_cpu {
	void **freelist;	/* Pointer to next available object */
	unsigned long tid;	/* Globally unique transaction id */
	struct page *page;	/* The slab from which we are allocating */
#ifdef CONFIG_SLUB_CPU_PARTIAL
	struct page *partial;	/* Partially allocated frozen slabs */
#endif
#ifdef CONFIG_SLUB_STATS
	unsigned stat[NR_SLUB_STAT_ITEMS];
#endif
};

다음과 같은데 바로 익숙한 부분이 나온다. freelist , partial 등등 맞다 바로 전글에서 다루었던 부분이다.

kmem_cache_cpu 구조체는 크게 freelist, page, partial 필드가 존재하고 이 부분을 중점으로 다룬다.

전편에서 다루었듯이 가장 fastpath인 freelist가 있고 이를 그림으로 나타내면,

다음과 같은데 일단 freelist 가 현재 cpu가 애용하는 페이지에 free 상태인 특정 Object를 가리키고 있고 단일 리스트로 해당 object에도 다음 free pointer 를 나타내는 fp가 있으므로 계속 단일로 연결리스트가 된다.

그리고 애용하는 페이지를 나타내는게 바로 page 필드이다.

C
struct page 
{
...
		struct 
		{	/* slab, slob and slub */
			union 
			{
				struct list_head slab_list;
				struct 
				{	/* Partial pages */
					struct page *next;
					#ifdef CONFIG_64BIT
						int pages;	/* Nr of pages left */
						int pobjects;	/* Approximate count */
					#else
						short int pages;
						short int pobjects;
					#endif
				};
			};
			struct kmem_cache *slab_cache; /* not slob */
			/* Double-word boundary */
			void *freelist;		/* first free object */
			union 
				{
					void *s_mem;	/* slab: first object */
					unsigned long counters;		/* SLUB */
					struct 
					{			/* SLUB */
						unsigned inuse:16;
						unsigned objects:15;
						unsigned frozen:1; // 뒤에서 설명함
					};
				};
			};
...

다음과 같으며,

C
					struct 
					{			/* SLUB */
						unsigned inuse:16;
						unsigned objects:15;
						unsigned frozen:1; // 뒤에서 설명함
					};

각각의 상태를 다음과 같이 관리하고 있고

C
			void *freelist;		/* first free object */

우리가 알듯이 이게 또 다른 freelist이다. slab 쪽 freelist(=active slab에 남아있는 free object들)

즉, c→freelist 와 c→page→freelist 가 있다는 것이다. 이 둘이 동기화가 되지 않아서 문제가 발생했던 것은 전글에서 보면 되고,

우리가 알아야 할것은 현재 cpu가 애용하는 페이지 를 이 page 필드로 옮겨서 사용한다는 것이다.

그러면 이렇게 까지 나아갈 수 있다. 현재 애용하는 page에 더이상 freelist가 없다면, partial 필드를 사용한다고 배웠다. 이 partial 필드는 여유가 존재하는 page들을 역시 단일리스트로 가리키고 있다. 이때 가리키고 있던 페이지를 page 필드로 옮겨서 사용하는 구조인것이다. 즉, 메인메모리 느낌으로 바로 올려서 사용한다. 올리면서 c→freelist 에 c→page→freelist 값을 복사해놓으면 초기 동기화가 완료가 되고 바로 계속 사용할수 있는 상태가 된다.

그럼 각각의 오브젝트들은 어떤 형태를 뛸까? 이것도 구조체로 관리되냐? 라고 한다면 이건 구조체라니 보다는 각각의 바이트 덩어리로 관리된다. 또한 디버깅 옵션에 따라 완전히 다른 형태를 뛰기도 하는데

해당 글에서 자세하게 다루고 있는데

일단 슬랩 Object의 주 구성요소는

  1. FP(Free Pointer)

선두에 있는 free object가 다음 free object를 가리키는 역할을 한다.

데이터 주소 침범 등을 검출하는 경우 FP가 object 뒤로 이동되는 경우가 있다

  1. Poison

데이터 주소 침범 등을 검출하기 위해 사용한다

슬랩 object가 사용 중이지만 초기화되지 않은 경우 0x5a('Z') 값으로 채운다

슬랩 object가 사용되지 않을 때 0x6b('k') 값으로 채운다.

정리하면 poison 은 값으로 에러 검출을 확인하는 용도이다

" slub_debug=P " 옵션 설정으로 poison 디버깅을 할 수 있다.

  1. Red-Zone

데이터 주소 침범 등을 검출하기 위해 사용한다

object-size 좌우에 red-zone 값을 기록한 후 object의 생성과 소멸 시에 이 값이 변경되는 것으로 에러 검출을 한다

" slub_debug=Z " 옵션 설정으로 red-zone 디버깅을 할 수 있다.

  1. Owner(User)-Track

슬랩 object의 생성과 소멸 시에 호출한 함수를 출력하기 위해 사용한다.

" slub_debug=U " 옵션 설정으로 Owner-Track 디버깅을 할 수 있다.

  1. Padding

Align을 위한 부분이다.

패딩 값으로 0x5a=’Z’를 채운다

만약 디버깅으로 아무것도 추가 되지 않았다면, 다음과 같다.

그리고 만약에 다 추가가된다면, 다음과 같다.

실제 데이터(payload)는 object_size 안에 저장이 된다.