Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

1148
469
422441


[Kernel] slab object 자료구조 - 페이지, 슬랩 캐시 [Linux][Kernel] MM

이번 시간에는 코어 덤프에서 페이지 디스크립터와 슬랩 오브젝트 자료구조를 알아 봅니다.

우선 다음과 같은 페이지 디스크립터가 있습니다.
(struct page *) (struct page*)0xEC778540 = 0xEC778540 -> (
  (long unsigned int) flags = 128 = 0x80,  //<<--[1]
  (struct address_space *) mapping = 0x0,
  (void *) s_mem = 0x0,
  (long unsigned int) index = 3306744832 = 0xC518EC00,
  (void *) freelist = 0xC518EC00,  //<<--[2]
  (bool) pfmemalloc = FALSE,
  (unsigned int) counters = 2149580809 = 0x80200009,
  (atomic_t) _mapcount = ((int) counter = -2145386487 = 0x80200009),
  (unsigned int:16) inuse = 9 = 0x9,        //<<--[3]
  (unsigned int:15) objects = 32 = 0x20,  //<<--[4]
  (unsigned int:1) frozen = 1 = 0x1,
  (int) units = -2145386487 = 0x80200009,
  (atomic_t) _count = ((int) counter = 1 = 0x1),
  (unsigned int) active = 2149580809 = 0x80200009,
  (struct list_head) lru = ((struct list_head *) next = 0x0100, (struct list_head *) prev = 0x0200
  (struct page *) next = 0x0100,
  (short int) pages = 512 = 0x0200,
  (short int) pobjects = 0 = 0x0,
  (struct slab *) slab_page = 0x0100,
  (struct callback_head) callback_head = ((struct callback_head *) next = 0x0100, (void (*)()) fun
  (long unsigned int) private = 3955232896 = 0xEBC01080,
  (spinlock_t *) ptl = 0xEBC01080,
  (struct kmem_cache *) slab_cache = 0xEBC01080  //<<--[5]
  (struct page *) first_page = 0xEBC01080,
  (long unsigned int) debug_flags = 0 = 0x0,
  (struct task_struct *) tsk_dirty = 0x0,
  (int) order = 0 = 0x0,
  (gfp_t) gfp_mask = 2101968 = 0x002012D0,
  (struct stack_trace) trace = ((unsigned int) nr_entries = 7 = 0x7, (unsigned int) max_entries =
  (long unsigned int [8]) trace_entries = (3234841780 = 0xC0CFC4B4, 3223592064 = 0xC0241C80, 32235 //<<--[6]
 
[1]: struct page.flags 값이 0x80입니다. 이는 슬랩을 관리하는 페이지란 의미죠.
  (long unsigned int) flags = 128 = 0x80
 
pageflag_names 전역 변수에 담겨 있는 페이지 플래그 값을 참고하세요.
  pageflag_names = (
    [0x0] = (mask = 0x1, name = 0xC13D6ADA -> "locked"),
//...
    [0x5] = (mask = 0x20, name = 0xC132D654 -> "lru"),
    [0x6] = (mask = 0x40, name = 0xC150C3E9 -> "active"),
    [0x7] = (mask = 0x80, name = 0xC133136D -> "slab"),
 
[2]: 멤버 변수 이름 그대로 할당할 수 있는 슬랩 오브젝트를 의미합니다.
 (void *) freelist = 0xC518EC00
 
0xC518EC00 주소 메모리 덤프를 열어보니 0x6B6B6B6B 매직값이 담겨 있군요. 0x6b는 이미 해제한 페이지 포이즌 값이라는 점 기억하세요.
_____address|_data________|value_____________|symbol
NSD:C518EBF4| 5A 5A 5A 5A  0x5A5A5A5A
NSD:C518EBF8| 5A 5A 5A 5A  0x5A5A5A5A
NSD:C518EBFC| 5A 5A 5A 5A  0x5A5A5A5A
NSD:C518EC00| 6B 6B 6B 6B  0x6B6B6B6B
NSD:C518EC04| 6B 6B 6B 6B  0x6B6B6B6B
NSD:C518EC08| 6B 6B 6B 6B  0x6B6B6B6B

[3]: 현재 할당되어 쓰고 있는 오브젝트 갯수 입니다.
 (unsigned int:16) inuse = 9 = 0x9,

[4]: 이 슬랩 페이지가 관리하는 슬랩 오브젝트 갯수 입니다. 이 정보로 보아 현재 할당 가능한 슬랩 오브젝트의 갯수는 23개(32-9)입니다.
  (unsigned int:15) objects = 32 = 0x20, 

[5]: 슬랩 캐시입니다. 이 자료 구조는 조금 있다 더 자세히 알아볼게요.
 (struct kmem_cache *) slab_cache = 0xEBC01080

참고로 슬랩 페이지의 struct page->lru는 다음 슬랩 캐시 partial 노드에 등록됩니다.
slab_cache->node[0].partial

[6]: 이 페이지를 할당할 때 콜 스택 정보입니다.
(long unsigned int [8]) trace_entries = (3234841780 = 0xC0CFC4B4, 3223592064 = 0xC0241C80, 

해당 메모리 덤프를 d.v %y.l 0xEC778580  명령어로 확인하니 다음 콜스택이 보이는군요.
_____address|value______|symbol
NSD:EC778580|0xC0CFC4B4  \\vmlinux\socket\sock_alloc_inode+0x34
NSD:EC778584|0xC0241C80  \\vmlinux\fs/inode\alloc_inode+0x1C
NSD:EC778588|0xC024361C  \\vmlinux\fs/inode\new_inode_pseudo+0x8
NSD:EC77858C|0xC0CFBF38  \\vmlinux\socket\sock_alloc+0x14
NSD:EC778590|0xC0CFD5F8  \\vmlinux\socket\sys_accept4+0x54
NSD:EC778594|0xC0106840  \\vmlinux\Global\ret_fast_syscall

0xC0CFC4B4 주소 코드를 열어보니 sock_alloc_inode+0x34 심볼에서 슬랩 오브젝트를 할당하고 있군요.
static struct inode *sock_alloc_inode(struct super_block *sb)
{
struct socket_alloc *ei;
struct socket_wq *wq;

ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL);
if (!ei)
return NULL;
wq = kmalloc(sizeof(*wq), GFP_KERNEL);  //<<--

(struct socket_wq *) 구조체를 확인하니 36(0x24) 바이트 만큼 슬랩 오브젝트를 할당 했군요.
그럼 커널은 "kmalloc-64" 슬랩 오브젝트를 할당했다고 유추할 수 있겠죠?
struct socket_wq {
/* Note: wait MUST be first field of socket_wq */
[0x0] wait_queue_head_t wait;
[0x18] struct fasync_struct *fasync_list;
[0x1c] struct rcu_head rcu;
} ____cacheline_aligned_in_smp;

이번엔 해당 슬랩 페이지에 대응하는 슬랩 캐시 멤버를 알아보겠습니다.
이 자료구조는 struct page->slab_cache에서도 확인할 수 있습니다.
(struct page *) (struct page*)0xEC778540 = 0xEC778540 -> (
//...
  (struct kmem_cache *) slab_cache = 0xEBC01080  

이제 슬랩 캐시 멤버를 볼게요.
(struct kmem_cache *) slab_cache = 0xEBC01080 -> (
  (struct kmem_cache_cpu *) cpu_slab = 0xC17D2CE0,  //<<--[1]
  (long unsigned int) flags = 2147486976 = 0x80000D00,
  (long unsigned int) min_partial = 5 = 0x5,
  (int) size = 128 = 0x80,  //<<--[2]
  (int) object_size = 64 = 0x40,  //<<--[3]
  (int) offset = 68 = 0x44,   //<<--[4]
  (int) cpu_partial = 0 = 0x0,
  (struct kmem_cache_order_objects) oo = ((long unsigned int) x = 32 = 0x20),
  (struct kmem_cache_order_objects) max = ((long unsigned int) x = 32 = 0x20),
  (struct kmem_cache_order_objects) min = ((long unsigned int) x = 32 = 0x20),
  (gfp_t) allocflags = 0 = 0x0,
  (int) refcount = 1 = 0x1,
  (void (*)()) ctor = 0x0,
  (int) inuse = 68 = 0x44,
  (int) align = 64 = 0x40,
  (int) reserved = 0 = 0x0,
  (char *) name = 0xEBC02F80 -> "kmalloc-64",  //<<--[5]
  (struct list_head) list = ((struct list_head *) next = 0xEBC01FC4, (struct list_head *) prev =
  (struct kobject) kobj = ((char *) name = 0xE9C89300 -> "kmalloc-64", (struct list_head) entry
  (struct kmem_cache_node * [1]) node = (
    0xEBC00FC0 -> (
      (spinlock_t) list_lock = ((struct raw_spinlock) rlock = ((arch_spinlock_t) raw_lock = ((u3
      (long unsigned int) nr_partial = 1935 = 0x078F, //<<--[6]
      (struct list_head) partial = (
        (struct list_head *) next = 0xECB97D14,  //<<--[7]
        (struct list_head *) prev = 0xED269774),
      (atomic_long_t) nr_slabs = ((int) counter = 3704 = 0x0E78),
      (atomic_long_t) total_objects = ((int) counter = 118528 = 0x0001CF00), //<<--[8]
      (struct list_head) full = ((struct list_head *) next = 0xEBC00FE4, (struct list_head *) pr

[1]: 각 슬랩 오브젝트는 per-cpu 타입으로 관리됩니다. 이를 percpu 슬랩 캐시라고 합니다.      
  (struct kmem_cache_cpu *) cpu_slab = 0xC17D2CE0

만약 각각 percpu percpu 슬랩 캐시를 보려면 다음 명령어를 쓰면 됩니다.

percpu0:
   (struct kmem_cache_cpu *) (struct kmem_cache_cpu*)(0xC17D2CE0+__per_cpu_offset[0]) = 0xEC494CE0 ->
    (void * *) freelist = 0x0,
    (long unsigned int) tid = 0x0,
    (struct page *) page = 0x0,
    (struct page *) partial = 0x0)

percpu-1: 
  (struct kmem_cache_cpu *) (struct kmem_cache_cpu*)(0xC17D2CE0+__per_cpu_offset[1]) = 0xEC49FCE0 ->
    (void * *) freelist = 0x0,
    (long unsigned int) tid = 0x1,
    (struct page *) page = 0x0,
    (struct page *) partial = 0x0)

percpu2:
  (struct kmem_cache_cpu *) (struct kmem_cache_cpu*)(0xC17D2CE0+__per_cpu_offset[2]) = 0xEC4AACE0 ->
    (void * *) freelist = 0x0,
    (long unsigned int) tid = 0x2,
    (struct page *) page = 0x0,
    (struct page *) partial = 0x0)
 
percpu3:
  (struct kmem_cache_cpu *) (struct kmem_cache_cpu*)(0xC17D2CE0+__per_cpu_offset[3]) = 0xEC4B5CE0 ->
    (void * *) freelist = 0x0,
    (long unsigned int) tid = 0x3,
    (struct page *) page = 0x0,
    (struct page *) partial = 0x0)

percpu4:
  (struct kmem_cache_cpu *) (struct kmem_cache_cpu*)(0xC17D2CE0+__per_cpu_offset[4]) = 0xEC4C0CE0 ->
    (void * *) freelist = 0x0,
    (long unsigned int) tid = 0x4,
    (struct page *) page = 0x0,
    (struct page *) partial = 0x0)

percpu5:
  (struct kmem_cache_cpu *) (struct kmem_cache_cpu*)(0xC17D2CE0+__per_cpu_offset[5]) = 0xEC4CBCE0 ->
    (void * *) freelist = 0x0,
    (long unsigned int) tid = 0x5,
    (struct page *) page = 0xEC778540,
    (struct page *) partial = 0x0)

percpu6:
  (struct kmem_cache_cpu *) (struct kmem_cache_cpu*)(0xC17D2CE0+__per_cpu_offset[6]) = 0xEC4D6CE0 ->
    (void * *) freelist = 0x0,
    (long unsigned int) tid = 0x6,
    (struct page *) page = 0x0,
    (struct page *) partial = 0x0)

percpu7:
  (struct kmem_cache_cpu *) (struct kmem_cache_cpu*)(0xC17D2CE0+__per_cpu_offset[7]) = 0xEC4E1CE0 ->
    (void * *) freelist = 0x0,
    (long unsigned int) tid = 0x7,
    (struct page *) page = 0x0,
    (struct page *) partial = 0x0)

[2]: 메타 데이터가 포함된 슬랩 오브젝트 사이즈입니다.
 (int) size = 128 = 0x80

이 사이즈가 왜 0x80인지는 조금 있다 슬랩 오브젝트 메모리 덤프를 열어보면서 설명드릴께요.

[3]: 실제 이 오브젝트가 쓰는 메모리 사이즈입니다. "kmalloc-64" 슬랩이니 64바이트 사이즈죠.
  (int) object_size = 64 = 0x40,

[4]: 이 오프셋으로 다음 슬랩 오브젝트 주소가 담겨 있는 메모리 위치를 알 수 있습니다.
  (int) offset = 68 = 0x44,  

[5]: 슬랩 종류입니다. "kmalloc-64" 이네요.
  (char *) name = 0xEBC02F80 -> "kmalloc-64"

[6]: 슬랩 캐시에 등록된 슬랩 페이지 갯수를 나타냅니다. 1935개나 되는 슬랩 페이지가 있군요.
  (struct kmem_cache_node * [1]) node = (
    0xEBC00FC0 -> (
      (spinlock_t) list_lock = ((struct raw_spinlock) rlock = ((arch_spinlock_t) raw_lock = ((u3
      (long unsigned int) nr_partial = 1935

[7]: 각각 슬랩 페이지의 struct page->lru는 node[0]->partial.next 에 등록돼 있습니다.
     둘다 (struct list_head *) 구조체입니다.
      (struct list_head) partial = (
        (struct list_head *) next = 0xECB97D14
        (struct list_head *) prev = 0xED269774),  

예를 다음 Trace32 명령어를 입력하면 "kmalloc-64" 슬랩 캐시에 등록된 슬랩 페이지 정보를 확인할 수 있습니다.    
v.v %s %h %t container_of(0xECB97D14,struct page,lru)

  (struct page *) container_of(0xECB97D14,struct page,lru) = 0xECB97D00 -> (
    (long unsigned int) flags = 0x80,
    (struct address_space *) mapping = 0x0,
//...
    (spinlock_t *) ptl = 0xEBC01080,
    (struct kmem_cache *) slab_cache = 0xEBC01080 -> (
      (struct kmem_cache_cpu *) cpu_slab = 0xC17D2CE0,
      (long unsigned int) flags = 0x80000D00,
//...
      (int) reserved = 0x0,
      (char *) name = 0xEBC02F80 -> "kmalloc-64",
      (struct list_head) list = ((struct list_head *) next = 0xEBC01FC4, (struct list_head *) prev =
      (struct kobject) kobj = ((char *) name = 0xE9C89300 -> "kmalloc-64", (struct list_head) entry
      (struct kmem_cache_node * [1]) node = (0xEBC00FC0)),

코어 덤프 소스: 02886369


# Reference: For more information on 'Linux Kernel';

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1

디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2





핑백

덧글

댓글 입력 영역