Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

27236
1488
262023


[리눅스] /proc/meminfo 메모리 정보 해석하기 14. 메모리 관리

보통 시스템에 메모리가 얼마나 남아 있는지 점검하고 싶을 때가 있어요. 이런 상황에서 메모리를 디버깅할 때 가장 많이 활용되는 파일이 '/proc/meminfo'입니다.

cat "/proc/meminfo" 명령어를 치면 상세한 메모리 정보를 파악할 수 있거든요.
이번에는 "/proc/meminfo"란 노드가 proc 파일 시스템에 메모리 정보를 어떻게 읽어 오는 지 소개합니다. 

/proc/meminfo 해석

'cat /proc/meminfo' 명령어를 입력하면 출력되는 결과와 관련 정보는 다음과 같습니다. 

root@raspberrypi:/home/pi# cat /proc/meminfo
MemTotal:        1986000 kB    // 전체 물리 메모리크기     (1986000 kB/1024 = 1939MB)
MemFree:         1506308 kB    // 사용 가능한 메모리 크기 (1506308 kB/1024 =  1471MB)
MemAvailable:    1694404 kB   
Buffers:           31308 kB       // 디스크 접근용 버퍼 크기(파일 시스템용 메타데이터)
Cached:           257216 kB     // 페이지 캐시 사이즈
SwapCached:            0 kB     // 스왑 중인 페이지 사이즈
SwapTotal:       945532 kB // 전체 스왑 영역 크기
SwapFree:        664176 kB // 사용 가능한 스왑
Active:           203072 kB       // active LRU 크기
Inactive:         171568 kB      // inactive LRU 크기
Active(anon):      86532 kB    // active-anon LRU
Inactive(anon):    41124 kB    // inactive-anon LRU
Active(file):     116540 kB      // active-file LRU
Inactive(file):   130444 kB      // inactive-file LRU
Unevictable:          16 kB      // 회수불가능한페이지
Mlocked:              16 kB      // mlock() 시스템 콜로 lock 걸린 페이지
HighTotal:       1232896 kB   // 전체 High 메모리(유저 스페이스에서 접근) 크기
HighFree:         878004 kB    // 사용 가능한 High 메모리(유저 스페이스에서 접근) 크기
LowTotal:         753104 kB    // 전체 Low 메모리 크기(커널에서 엑세스)
LowFree:          628304 kB    // 사용 가능한 Low 메모리 크기(커널에서 엑세스)
SwapTotal:        102396 kB
SwapFree:         102396 kB
Dirty:               596 kB         // 디스크에 기록해야 할 페이지
Writeback:             0 kB       // 디스크에 기록 중인 페이지
AnonPages:         86152 kB    // 익명 매핑된 사용자 페이지
Mapped:            95420 kB    // 파일 매핑된 사용자 페이지 
Shmem:             41536 kB    // 공유 메모리 페이지
Slab:              55408 kB       // 슬랩 할당자가 관리하는 커널 페이지
SReclaimable:      24300 kB   // 회수 가능한 슬랩 페이지 
SUnreclaim:        31108 kB    // 회수 불가능한 슬랩 페이지
KernelStack:        1856 kB     // 커널 스택으로 사용 중인 페이지
PageTables:         4308 kB    // (하위) 페이지 테이블로 사용 중인 페이지
NFS_Unstable:          0 kB     // NFS에서 사용
Bounce:                0 kB       // 디스크 장치에서 사용 
WritebackTmp:          0 kB    // FUSE 파일시스템에서 사용
CommitLimit:     1095396 kB  // 오버커밋이 허용된 크기(/proc/sys/vm/overcommit_memory 확인 필요)
Committed_AS:     911844 kB  // 실제 오버 커밋된 가상 메모리의 크기
VmallocTotal:     245760 kB    // 전체 vmalloc영역의 크기
VmallocUsed:           0 kB      // 실제로 할당된 vmalloc 영역
VmallocChunk:          0 kB     // 사용 가능한 가장 큰 vmalloc 영역의 크기
Percpu:              640 kB
CmaTotal:         262144 kB
CmaFree:          239556 kB

갑자기 시스템의 메모리 정보를 확인해야 할 때 바로 확인하면 좋은 정보인 것 같습니다.

그런데 이 정보를 보면 한 가지 의문이 생깁니다.

    "이 정보를 출력해 주는 주인공은 누구일까?" 

리눅스 시스템 어딘가에서 이 정보를 출력할텐데요. 그 주인공은 리눅스 커널입니다. 이어서 /proc/meminfo 파일을 통해 시스템 메모리 정보를 출력하는 커널 코드를 소개합니다.

메모리 정보를 출력하는 커널 코드 분석 

우선 커널이 부팅하는 과정에서 아래 함수에서 meminfo란 이름으로 proc 파일 시스템으로 등록합니다. 

https://elixir.bootlin.com/linux/v4.19.30/source/fs/proc/meminfo.c
static int __init proc_meminfo_init(void)
{
proc_create("meminfo", 0, NULL, &meminfo_proc_fops);
return 0;
}

static int meminfo_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, meminfo_proc_show, NULL);
}

static const struct file_operations meminfo_proc_fops = {
.open = meminfo_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};

아래 코드를 보면 meminfo_proc_show() 함수에서 메모리 세부 정보를 출력한다는 사실을 알 수 있습니다.

static int meminfo_proc_show(struct seq_file *m, void *v)
{
struct sysinfo i;
unsigned long committed;
long cached;
...
show_val_kb(m, "MemTotal:       ", i.totalram);
show_val_kb(m, "MemFree:        ", i.freeram);
show_val_kb(m, "MemAvailable:   ", available);
show_val_kb(m, "Buffers:        ", i.bufferram);
show_val_kb(m, "Cached:         ", cached);
show_val_kb(m, "SwapCached:     ", total_swapcache_pages());
show_val_kb(m, "Active:         ", pages[LRU_ACTIVE_ANON] +
   pages[LRU_ACTIVE_FILE]);
show_val_kb(m, "Inactive:       ", pages[LRU_INACTIVE_ANON] +
   pages[LRU_INACTIVE_FILE]);
show_val_kb(m, "Active(anon):   ", pages[LRU_ACTIVE_ANON]);
show_val_kb(m, "Inactive(anon): ", pages[LRU_INACTIVE_ANON]);
show_val_kb(m, "Active(file):   ", pages[LRU_ACTIVE_FILE]);
show_val_kb(m, "Inactive(file): ", pages[LRU_INACTIVE_FILE]);

가끔 메모리 leak으로 이슈가 생길 경우 섬세한 디버깅 패치를 작성해서 메모리가 어느 시점에 변동되는지 점검해야 하거든요. 5초 간격으로 호출되는 간단한 타이머 함수를 호출해서 메모리 잔여량을 체크하는 함수를 작성하는거죠.

이 때 proc 파일 시스템으로 등록된 meminfo_proc_show 내에서 호출된 함수 조각을 잘 활용하면 매우 유용할 것 같아요. 아래 함수로 High 메모리 잔여양과 전체 High Memory 양을 확인할 수 있어요.

https://elixir.bootlin.com/linux/v4.19.30/source/mm/page_alloc.c
void si_meminfo(struct sysinfo *val)
{
val->totalram = totalram_pages;
val->sharedram = global_page_state(NR_SHMEM);
val->freeram = global_page_state(NR_FREE_PAGES);
val->bufferram = nr_blockdev_pages();
val->totalhigh = totalhigh_pages;
val->freehigh = nr_free_highpages();
val->mem_unit = PAGE_SIZE;
}

전체 low memory 양은 val->totalram - val->totalhigh,
free low memory 양은 val->freeram -  val->freehigh 으로 계산할 수 있죠.

아래 메모리 정보는 아래 정보로 계산이 되니 아래 API를 참고해서 시간에 따라 메모리가 얼마나 남아 있는지 체크해도 좋을 것 같아요.

"MemTotal:       %8lu kB\n" // val->totalram
"MemFree:        %8lu kB\n" // val->freeram
"MemAvailable:   %8lu kB\n" // val->freeram + 페이지 캐시 + global_page_state(NR_SLAB_RECLAIMABLE)
"Buffers:        %8lu kB\n" // val->bufferram
"Cached:         %8lu kB\n" // global_page_state(NR_FILE_PAGES) - total_swapcache_pages() - val->bufferram
"SwapCached:     %8lu kB\n" // total_swapcache_pages()
"Active:         %8lu kB\n" // global_page_state(NR_ACTIVE_ANON) +  global_page_state(NR_ACTIVE_FILE) 
"Inactive:       %8lu kB\n" // global_page_state(NR_INACTIVE_ANON) +  global_page_state(NR_INACTIVE_FILE)



Written by <디버깅을 통해 배우는 리눅스 커널의 구조와 원리> 저자






 

덧글

댓글 입력 영역