VM ํ†ต๊ณ„ (vmstat)

VM ํ†ต๊ณ„ ์ž๋ฃŒ๊ตฌ์กฐ ๊ด€๊ณ„๋„

๊ฐœ์š” (Overview)

VM ํ†ต๊ณ„๋Š” ๋ฆฌ๋ˆ…์Šค ์ปค๋„์˜ ๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ ์‹œ์Šคํ…œ ๋™์ž‘์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ  ๋ถ„์„ํ•˜๊ธฐ ์œ„ํ•œ ์นด์šดํ„ฐ ์ง‘ํ•ฉ์ž…๋‹ˆ๋‹ค. /proc/vmstat, /proc/meminfo, /proc/buddyinfo, /proc/pagetypeinfo ๋“ฑ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž ๊ณต๊ฐ„์— ๋…ธ์ถœ๋˜๋ฉฐ, ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น, ํšŒ์ˆ˜, ๋‹จํŽธํ™”, ํŽ˜์ด์ง€ ํดํŠธ ๋“ฑ์˜ ์ด๋ฒคํŠธ๋ฅผ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์น˜ ์ž๋™์ฐจ ๊ณ„๊ธฐํŒ์ฒ˜๋Ÿผ ๋ฉ”๋ชจ๋ฆฌ ์‹œ์Šคํ…œ์˜ ๊ฑด๊ฐ• ์ƒํƒœ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ณด์—ฌ์ฃผ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

์ด ์‹œ์Šคํ…œ์€ per-CPU ์นด์šดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฝ ๊ฒฝ์Ÿ์„ ์ตœ์†Œํ™”ํ•˜๊ณ , ์ฃผ๊ธฐ์ ์œผ๋กœ ์ „์—ญ ์นด์šดํ„ฐ๋กœ ๋ณ‘ํ•ฉ(folding)ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. CONFIG_VM_EVENT_COUNTERS์™€ CONFIG_SMP ์„ค์ •์— ๋”ฐ๋ผ ๋™์ž‘ํ•˜๋ฉฐ, NUMA ํ™˜๊ฒฝ์—์„œ๋Š” ์ถ”๊ฐ€์ ์ธ NUMA ์ด๋ฒคํŠธ ์นด์šดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ ์••๋ ฅ์ด ๋ฐœ์ƒํ•  ๋•Œ watermark ๊ธฐ๋ฐ˜์œผ๋กœ ๋“œ๋ฆฌํ”„ํŠธ๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ ์‹œ์Šคํ…œ์ด ์ ์ ˆํžˆ ๋Œ€์‘ํ•˜๋„๋ก ๋•์Šต๋‹ˆ๋‹ค.

์†Œ์Šค ํŒŒ์ผ ๊ฒฝ๋กœ:
โ”œโ”€โ”€ mm/vmstat.c                    โ† ํ•ต์‹ฌ ๊ตฌํ˜„ (์นด์šดํ„ฐ ๊ด€๋ฆฌ, /proc ์ธํ„ฐํŽ˜์ด์Šค)
โ”œโ”€โ”€ include/linux/vmstat.h         โ† vm_event_state, per_cpu_zonestat ์ธํ„ฐํŽ˜์ด์Šค
โ”œโ”€โ”€ include/linux/mmzone.h         โ† per_cpu_zonestat, per_cpu_nodestat ์ •์˜
โ””โ”€โ”€ include/linux/vm_event_item.h  โ† NR_VM_EVENT_ITEMS ๋“ฑ ์—ด๊ฑฐํ˜•

๋น ๋ฅธ ์ ๊ฒ€ ๋ช…๋ น

# /proc/vmstat ์ „์ฒด ๋‚ด์šฉ ํ™•์ธ
cat /proc/vmstat

# ํŠน์ • VM ํ†ต๊ณ„ ํ•ญ๋ชฉ ํ™•์ธ (์˜ˆ: pgfault, pgmajfault)
grep -E "pgfault|pgmajfault|pgpgin|pgpgout|pswpin|pswpout" /proc/vmstat

# ๋ฉ”๋ชจ๋ฆฌ ์••๋ ฅ ๊ด€๋ จ ํ†ต๊ณ„ ํ™•์ธ
grep -E "allocstall|compact_stall|drop_pagecache|drop_slab" /proc/vmstat

# ํŽ˜์ด์ง€ ํšŒ์ˆ˜ ๊ด€๋ จ ํ†ต๊ณ„ ํ™•์ธ
grep -E "pgscan_kswapd|pgsteal_kswapd|pgscan_direct|pgsteal_direct" /proc/vmstat

# NUMA ์ด๋ฒคํŠธ ํ†ต๊ณ„ ํ™•์ธ (NUMA ์‹œ์Šคํ…œ์—์„œ)
grep -E "numa_hit|numa_miss|numa_foreign|numa_local|numa_remote" /proc/vmstat 2>/dev/null || echo "NUMA ํ†ต๊ณ„ ์—†์Œ"

# VM ์ด๋ฒคํŠธ ์นด์šดํ„ฐ ํ™œ์„ฑํ™” ์—ฌ๋ถ€ ํ™•์ธ
zcat /proc/config.gz 2>/dev/null | grep CONFIG_VM_EVENT_COUNTERS || \
grep CONFIG_VM_EVENT_COUNTERS /boot/config-$(uname -r) 2>/dev/null || echo "ํ™•์ธ ๋ถˆ๊ฐ€"

# SMP ๊ด€๋ จ ์„ค์ • ํ™•์ธ
zcat /proc/config.gz 2>/dev/null | grep CONFIG_SMP || \
grep CONFIG_SMP /boot/config-$(uname -r) 2>/dev/null || echo "ํ™•์ธ ๋ถˆ๊ฐ€"

# vmstat ๋ช…๋ น์–ด๋กœ ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง
vmstat 1 5

# vmstat ๋ช…๋ น์–ด๋กœ ํŠน์ • ์นด์šดํ„ฐ๋งŒ ํ™•์ธ
vmstat -s | head -20

# ํ†ต๊ณ„ ์ƒˆ๋กœ๊ณ ์นจ (root ํ•„์š”)
echo 1 > /proc/sys/vm/stat_refresh 2>/dev/null || echo "stat_refresh ์ง€์› ์•ˆ ํ•จ"

# ๊ตฌ์กฐ์ฒด ํฌ๊ธฐ ํ™•์ธ (์ปค๋„ ์†Œ์Šค ๊ธฐ์ค€)
grep -n "struct vm_event_state" /home/ryotta205/Chamber_linux/linux-7.0-source/include/linux/vmstat.h

# per_cpu_zonestat ๊ตฌ์กฐ์ฒด ํ™•์ธ
grep -n "struct per_cpu_zonestat" /home/ryotta205/Chamber_linux/linux-7.0-source/include/linux/mmzone.h

# per_cpu_nodestat ๊ตฌ์กฐ์ฒด ํ™•์ธ
grep -n "struct per_cpu_nodestat" /home/ryotta205/Chamber_linux/linux-7.0-source/include/linux/mmzone.h

# /proc/meminfo์—์„œ ๊ธฐ๋ณธ ๋ฉ”๋ชจ๋ฆฌ ์ •๋ณด ํ™•์ธ
cat /proc/meminfo | head -20

ํ•ต์‹ฌ ์ž๋ฃŒ๊ตฌ์กฐ

1. `vm_event_state` (include/linux/vmstat.h:51)

/* per-CPU VM ์ด๋ฒคํŠธ ์นด์šดํ„ฐ ๊ตฌ์กฐ์ฒด */
struct vm_event_state {
unsigned long event[NR_VM_EVENT_ITEMS];  // ์ด๋ฒคํŠธ๋ณ„ ์นด์šดํ„ฐ ๋ฐฐ์—ด
};

์„ค๋ช…: ๊ฐ CPU๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ ์กด์žฌํ•˜๋Š” VM ์ด๋ฒคํŠธ ์นด์šดํ„ฐ์ž…๋‹ˆ๋‹ค. NR_VM_EVENT_ITEMS ํฌ๊ธฐ์˜ ๋ฐฐ์—ด์„ ๊ฐ€์ง€๋ฉฐ, ํŽ˜์ด์ง€ ํดํŠธ, ์Šค์™‘ ์ธ/์•„์›ƒ, IO ์ด๋ฒคํŠธ ๋“ฑ์˜ ์นด์šดํ„ฐ๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. per-CPU๋กœ ๊ด€๋ฆฌ๋˜์–ด ๋ฝ ๊ฒฝ์Ÿ์ด ์—†์Šต๋‹ˆ๋‹ค.

2. `per_cpu_zonestat` (include/linux/mmzone.h:762)

/* per-CPU ์กด๋ณ„ ํ†ต๊ณ„ ๊ตฌ์กฐ์ฒด */
struct per_cpu_zonestat {
#ifdef CONFIG_SMP
s8 vm_stat_diff[NR_VM_ZONE_STAT_ITEMS];  // ์กด๋ณ„ ์นด์šดํ„ฐ ์ฐจ์ด๊ฐ’ (drift)
s8 stat_threshold;                         // ๋“œ๋ฆฌํ”„ํŠธ ์ž„๊ณ„๊ฐ’
#endif
#ifdef CONFIG_NUMA
/* NUMA ์ด๋ฒคํŠธ ์นด์šดํ„ฐ (์ •ํ™•ํ•˜์ง€ ์•Š์Œ, ํ•„์š” ์‹œ ํด๋”ฉ) */
unsigned long vm_numa_event[NR_VM_NUMA_EVENT_ITEMS];
#endif
};

์„ค๋ช…: ๊ฐ CPU๋ณ„, ์กด๋ณ„๋กœ ์กด์žฌํ•˜๋Š” ํ†ต๊ณ„ ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค. SMP ํ™˜๊ฒฝ์—์„œ๋Š” per-CPU ์นด์šดํ„ฐ์™€ ์ „์—ญ ์นด์šดํ„ฐ ์‚ฌ์ด์˜ ์ฐจ์ด๊ฐ’(vm_stat_diff)์„ ์ €์žฅํ•˜์—ฌ ๋“œ๋ฆฌํ”„ํŠธ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. NUMA ํ™˜๊ฒฝ์—์„œ๋Š” NUMA ์ด๋ฒคํŠธ ์นด์šดํ„ฐ๋„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

3. `per_cpu_nodestat` (include/linux/mmzone.h:777)

/* per-CPU ๋…ธ๋“œ๋ณ„ ํ†ต๊ณ„ ๊ตฌ์กฐ์ฒด */
struct per_cpu_nodestat {
s8 stat_threshold;                      // ๋“œ๋ฆฌํ”„ํŠธ ์ž„๊ณ„๊ฐ’
s8 vm_node_stat_diff[NR_VM_NODE_STAT_ITEMS];  // ๋…ธ๋“œ๋ณ„ ์นด์šดํ„ฐ ์ฐจ์ด๊ฐ’
};

์„ค๋ช…: ๊ฐ CPU๋ณ„, ๋ฉ”๋ชจ๋ฆฌ ๋…ธ๋“œ๋ณ„๋กœ ์กด์žฌํ•˜๋Š” ํ†ต๊ณ„ ๊ตฌ์กฐ์ฒด์ž…๋‹ˆ๋‹ค. ์กด ํ†ต๊ณ„์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ per-CPU ์นด์šดํ„ฐ์™€ ์ „์—ญ ์นด์šดํ„ฐ ์‚ฌ์ด์˜ ์ฐจ์ด๊ฐ’์„ ์ €์žฅํ•˜์—ฌ ๋“œ๋ฆฌํ”„ํŠธ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

4. ์ „์—ญ ์นด์šดํ„ฐ ๋ณ€์ˆ˜๋“ค (mm/vmstat.c:165)

/* ์ „์—ญ ์กด๋ณ„ ํŽ˜์ด์ง€ ์ƒํƒœ ์นด์šดํ„ฐ */
atomic_long_t vm_zone_stat[NR_VM_ZONE_STAT_ITEMS] __cacheline_aligned_in_smp;

/* ์ „์—ญ ๋…ธ๋“œ๋ณ„ ํŽ˜์ด์ง€ ์ƒํƒœ ์นด์šดํ„ฐ */
atomic_long_t vm_node_stat[NR_VM_NODE_STAT_ITEMS] __cacheline_aligned_in_smp;

/* ์ „์—ญ NUMA ์ด๋ฒคํŠธ ์นด์šดํ„ฐ */
atomic_long_t vm_numa_event[NR_VM_NUMA_EVENT_ITEMS] __cacheline_aligned_in_smp;

์„ค๋ช…: ์‹œ์Šคํ…œ ์ „์ฒด์˜ ์กด๋ณ„, ๋…ธ๋“œ๋ณ„, NUMA๋ณ„ ํŽ˜์ด์ง€ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” ์ „์—ญ atomic ์นด์šดํ„ฐ์ž…๋‹ˆ๋‹ค. __cacheline_aligned_in_smp๋กœ ์บ์‹œ ๋ผ์ธ ์ •๋ ฌ๋˜์–ด SMP ํ™˜๊ฒฝ์—์„œ ์„ฑ๋Šฅ์ด ์ตœ์ ํ™”๋ฉ๋‹ˆ๋‹ค.

5. `vmstat_op` ์‹œํ€€์Šค ์—ฐ์‚ฐ (mm/vmstat.c:1956)

/* /proc/vmstat์„ ์œ„ํ•œ seq_file ์—ฐ์‚ฐ */
static const struct seq_operations vmstat_op = {
.start  = vmstat_start,   // ์‹œ์ž‘ ์œ„์น˜ ๋ฐ˜ํ™˜
.next   = vmstat_next,    // ๋‹ค์Œ ํ•ญ๋ชฉ ๋ฐ˜ํ™˜
.stop   = vmstat_stop,    // ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ
.show   = vmstat_show,    // ๊ฐ ํ•ญ๋ชฉ ํ‘œ์‹œ
};

์„ค๋ช…: /proc/vmstat ํŒŒ์ผ ์ฝ๊ธฐ๋ฅผ ์œ„ํ•œ seq_file ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค. vmstat_start์—์„œ ๋ชจ๋“  ์นด์šดํ„ฐ ๊ฐ’์„ ์ˆ˜์ง‘ํ•˜๊ณ , vmstat_show์—์„œ ๊ฐ ํ•ญ๋ชฉ์„ name value ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.


ํ•ต์‹ฌ ํ•จ์ˆ˜

1. `vmstat_start` (mm/vmstat.c:1878)

/* /proc/vmstat ์ฝ๊ธฐ ์‹œ์ž‘ ์‹œ ํ˜ธ์ถœ */
static void *vmstat_start(struct seq_file *m, loff_t *pos)
{
unsigned long *v;
int i;

if (*pos >= NR_VMSTAT_ITEMS)
return NULL;

/* ๋ชจ๋“  ์นด์šดํ„ฐ ๊ฐ’์„ ๋ฐฐ์—ด์— ์ˆ˜์ง‘ */
BUILD_BUG_ON(ARRAY_SIZE(vmstat_text) != NR_VMSTAT_ITEMS);
fold_vm_numa_events();  // NUMA ์ด๋ฒคํŠธ ํด๋”ฉ
v = kmalloc_array(NR_VMSTAT_ITEMS, sizeof(unsigned long), GFP_KERNEL);
m->private = v;
if (!v)
return ERR_PTR(-ENOMEM);

/* ์กด๋ณ„ ์นด์šดํ„ฐ ์ˆ˜์ง‘ */
for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++)
v[i] = global_zone_page_state(i);
v += NR_VM_ZONE_STAT_ITEMS;

/* NUMA ์ด๋ฒคํŠธ ์นด์šดํ„ฐ ์ˆ˜์ง‘ */
for (i = 0; i < NR_VM_NUMA_EVENT_ITEMS; i++)
v[i] = global_numa_event_state(i);
v += NR_VM_NUMA_EVENT_ITEMS;

/* ๋…ธ๋“œ๋ณ„ ์นด์šดํ„ฐ ์ˆ˜์ง‘ */
for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++) {
v[i] = global_node_page_state_pages(i);
if (vmstat_item_print_in_thp(i))
v[i] /= HPAGE_PMD_NR;
}
v += NR_VM_NODE_STAT_ITEMS;

/* ์ „์—ญ ํ†ต๊ณ„ ์ˆ˜์ง‘ */
global_dirty_limits(v + NR_DIRTY_BG_THRESHOLD, v + NR_DIRTY_THRESHOLD);
v[NR_MEMMAP_PAGES] = atomic_long_read(&nr_memmap_pages);
v[NR_MEMMAP_BOOT_PAGES] = atomic_long_read(&nr_memmap_boot_pages);
v += NR_VM_STAT_ITEMS;

/* VM ์ด๋ฒคํŠธ ์นด์šดํ„ฐ ์ˆ˜์ง‘ */
all_vm_events(v);
v[PGPGIN] /= 2;  // ์„นํ„ฐ โ†’ KB ๋ณ€ํ™˜
v[PGPGOUT] /= 2;

return (unsigned long *)m->private + *pos;
}

์—ญํ• : /proc/vmstat ํŒŒ์ผ ์ฝ๊ธฐ ์‹œ์ž‘ ์‹œ ๋ชจ๋“  ์นด์šดํ„ฐ ๊ฐ’์„ ๋ฐฐ์—ด์— ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค. per-CPU ์นด์šดํ„ฐ๋“ค์„ ์ „์—ญ ์นด์šดํ„ฐ๋กœ ๋ณ‘ํ•ฉํ•œ ํ›„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

2. `vmstat_show` (mm/vmstat.c:1931)

/* ๊ฐ VM ํ†ต๊ณ„ ํ•ญ๋ชฉ์„ ์ถœ๋ ฅ */
static int vmstat_show(struct seq_file *m, void *arg)
{
unsigned long *l = arg;
unsigned long off = l - (unsigned long *)m->private;

/* "์ด๋ฆ„ ๊ฐ’" ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅ */
seq_puts(m, vmstat_text[off]);
seq_put_decimal_ull(m, " ", *l);
seq_putc(m, '\n');

/* ๋งˆ์ง€๋ง‰ ํ•ญ๋ชฉ์ธ ๊ฒฝ์šฐ deprecated ์นด์šดํ„ฐ ์ถ”๊ฐ€ */
if (off == NR_VMSTAT_ITEMS - 1) {
seq_puts(m, "nr_unstable 0\n");
}
return 0;
}

์—ญํ• : ์ˆ˜์ง‘๋œ ๊ฐ ์นด์šดํ„ฐ๋ฅผ ์ด๋ฆ„ ๊ฐ’ ํ˜•์‹์œผ๋กœ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. vmstat_text ๋ฐฐ์—ด์—์„œ ํ•ญ๋ชฉ ์ด๋ฆ„์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

3. `all_vm_events` (mm/vmstat.c:133)

/* ๋ชจ๋“  CPU์˜ VM ์ด๋ฒคํŠธ ์นด์šดํ„ฐ๋ฅผ ์ „์—ญ์œผ๋กœ ํ•ฉ์‚ฐ */
void all_vm_events(unsigned long *ret)
{
cpus_read_lock();
sum_vm_events(ret);
cpus_read_unlock();
}

์—ญํ• : ๋ชจ๋“  CPU์˜ per-CPU VM ์ด๋ฒคํŠธ ์นด์šดํ„ฐ๋ฅผ ์ „์—ญ์œผ๋กœ ํ•ฉ์‚ฐํ•ฉ๋‹ˆ๋‹ค. cpus_read_lock()์œผ๋กœ CPU ํ•ซํ”Œ๋Ÿฌ๊ทธ๋ฅผ ๋ณดํ˜ธํ•ฉ๋‹ˆ๋‹ค.

4. `vm_events_fold_cpu` (mm/vmstat.c:147)

/* ํŠน์ • CPU์˜ ์ด๋ฒคํŠธ๋ฅผ ์ „์—ญ ์นด์šดํ„ฐ๋กœ ๋ณ‘ํ•ฉ (fold) */
void vm_events_fold_cpu(int cpu)
{
struct vm_event_state *fold_state = &per_cpu(vm_event_states, cpu);
int i;

for (i = 0; i < NR_VM_EVENT_ITEMS; i++) {
count_vm_events(i, fold_state->event[i]);  // ์ „์—ญ ์นด์šดํ„ฐ์— ์ถ”๊ฐ€
fold_state->event[i] = 0;  // per-CPU ์นด์šดํ„ฐ ์ดˆ๊ธฐํ™”
}
}

์—ญํ• : ํŠน์ • CPU๊ฐ€ ์˜คํ”„๋ผ์ธ๋  ๋•Œ ํ•ด๋‹น CPU์˜ per-CPU ์นด์šดํ„ฐ๋ฅผ ์ „์—ญ ์นด์šดํ„ฐ๋กœ ๋ณ‘ํ•ฉํ•˜๊ณ  per-CPU ์นด์šดํ„ฐ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.

5. `__mod_zone_page_state` (mm/vmstat.c:345)

/* ์กด๋ณ„ ํŽ˜์ด์ง€ ์ƒํƒœ ์นด์šดํ„ฐ ์ˆ˜์ • (์ธํ„ฐ๋ŸฝํŠธ ๋น„ํ™œ์„ฑํ™” ์ƒํƒœ์—์„œ ์‚ฌ์šฉ) */
void __mod_zone_page_state(struct zone *zone, enum zone_stat_item item,
long delta)
{
struct per_cpu_zonestat __percpu *pcp = zone->per_cpu_zonestats;
s8 __percpu *p = pcp->vm_stat_diff + item;
long x;
long t;

/* PREEMPT_RT ์ปค๋„์—์„œ๋Š” preemption ๋น„ํ™œ์„ฑํ™”๋กœ ์›์ž์„ฑ ๋ณด์žฅ */
preempt_disable_nested();

x = delta + __this_cpu_read(*p);     // per-CPU ์ฐจ์ด๊ฐ’์— delta ๋”ํ•˜๊ธฐ
t = __this_cpu_read(pcp->stat_threshold);  // ์ž„๊ณ„๊ฐ’ ์ฝ๊ธฐ

/* ์ž„๊ณ„๊ฐ’์˜ ์ ˆ๋Œ€๊ฐ’์„ ์ดˆ๊ณผํ•˜๋ฉด ์ „์—ญ ์นด์šดํ„ฐ์— ๋ฐ˜์˜ */
if (unlikely(abs(x) > t)) {
zone_page_state_add(x, zone, item);
x = 0;
}
__this_cpu_write(*p, x);             // per-CPU ์นด์šดํ„ฐ ์ €์žฅ

preempt_enable_nested();
}

์—ญํ• : ์กด๋ณ„ ํŽ˜์ด์ง€ ์ƒํƒœ ์นด์šดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ per-CPU ์ฐจ์ด๊ฐ’์„ ์—…๋ฐ์ดํŠธํ•˜๊ณ , ์ž„๊ณ„๊ฐ’์˜ ์ ˆ๋Œ€๊ฐ’์„ ์ดˆ๊ณผํ•˜๋ฉด ์ „์—ญ ์นด์šดํ„ฐ์— ๋ฐ˜์˜ํ•ฉ๋‹ˆ๋‹ค. preempt_disable_nested()/preempt_enable_nested()๋กœ ์›์ž์„ฑ์„ ๋ณด์žฅํ•˜๋ฉฐ, ๋ฝ ์—†์ด๋„ ๋น ๋ฅด๊ฒŒ ์นด์šดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

6. `__mod_node_page_state` (mm/vmstat.c:376)

/* ๋…ธ๋“œ๋ณ„ ํŽ˜์ด์ง€ ์ƒํƒœ ์นด์šดํ„ฐ ์ˆ˜์ • */
void __mod_node_page_state(struct pglist_data *pgdat, enum node_stat_item item,
long delta)
{
struct per_cpu_nodestat __percpu *pcp = pgdat->per_cpu_nodestats;
s8 __percpu *p = pcp->vm_node_stat_diff + item;
long x;
long t;

/* ๋ฐ”์ดํŠธ ๋‹จ์œ„ ํ•ญ๋ชฉ์€ ํŽ˜์ด์ง€ ๋‹จ์œ„๋กœ ๋ณ€ํ™˜ */
if (vmstat_item_in_bytes(item)) {
VM_WARN_ON_ONCE(delta & (PAGE_SIZE - 1));
delta >>= PAGE_SHIFT;
}

preempt_disable_nested();

x = delta + __this_cpu_read(*p);
t = __this_cpu_read(pcp->stat_threshold);

if (unlikely(abs(x) > t)) {
node_page_state_add(x, pgdat, item);
x = 0;
}
__this_cpu_write(*p, x);

preempt_enable_nested();
}

์—ญํ• : ๋…ธ๋“œ๋ณ„ ํŽ˜์ด์ง€ ์ƒํƒœ ์นด์šดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ per-CPU ์ฐจ์ด๊ฐ’์„ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. vm_stat_item_in_bytes()๊ฐ€ true์ธ ํ•ญ๋ชฉ(์˜ˆ: NR_SLAB_RECLAIMABLE_B)์€ ๋ฐ”์ดํŠธ๋ฅผ ํŽ˜์ด์ง€ ๋‹จ์œ„๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

7. `vmstat_cpu_dead` (mm/vmstat.c:2204)

/* CPU ์˜คํ”„๋ผ์ธ ์‹œ ํ˜ธ์ถœ๋จ */
static int vmstat_cpu_dead(unsigned int cpu)
{
const struct cpumask *node_cpus;
int node;

node = cpu_to_node(cpu);

refresh_zone_stat_thresholds();   // ์กด๋ณ„ ์ž„๊ณ„๊ฐ’ ์žฌ๊ณ„์‚ฐ
node_cpus = cpumask_of_node(node);
if (!cpumask_empty(node_cpus))
return 0;

node_clear_state(node, N_CPU);    // ๋…ธ๋“œ์— CPU ์—†์œผ๋ฉด N_CPU ํ•ด์ œ

return 0;
}

์—ญํ• : CPU ์˜คํ”„๋ผ์ธ ์‹œ ์กด๋ณ„ ๋“œ๋ฆฌํ”„ํŠธ ์ž„๊ณ„๊ฐ’์„ ์žฌ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ๋…ธ๋“œ์— ์˜จ๋ผ์ธ CPU๊ฐ€ ์—†์œผ๋ฉด N_CPU ์ƒํƒœ๋ฅผ ํ•ด์ œํ•ฉ๋‹ˆ๋‹ค.

8. `refresh_zone_stat_thresholds` (mm/vmstat.c:275)

/* ๋ชจ๋“  ์กด์˜ ๋“œ๋ฆฌํ”„ํŠธ ์ž„๊ณ„๊ฐ’์„ ๊ฐฑ์‹  */
void refresh_zone_stat_thresholds(void)
{
struct pglist_data *pgdat;
struct zone *zone;
int cpu;
int threshold;

/* ๊ธฐ์กด ๋…ธ๋“œ ์ž„๊ณ„๊ฐ’ ์ดˆ๊ธฐํ™” */
for_each_online_pgdat(pgdat) {
for_each_online_cpu(cpu) {
per_cpu_ptr(pgdat->per_cpu_nodestats, cpu)->stat_threshold = 0;
}
}

for_each_populated_zone(zone) {
struct pglist_data *pgdat = zone->zone_pgdat;
unsigned long max_drift, tolerate_drift;

threshold = calculate_normal_threshold(zone);

for_each_online_cpu(cpu) {
per_cpu_ptr(zone->per_cpu_zonestats, cpu)->stat_threshold
= threshold;

/* ๊ฐ€์žฅ ํฐ ์กด์˜ ์ž„๊ณ„๊ฐ’์„ ๋…ธ๋“œ ์ž„๊ณ„๊ฐ’์œผ๋กœ ์„ค์ • */
int pgdat_threshold = per_cpu_ptr(pgdat->per_cpu_nodestats, cpu)->stat_threshold;
per_cpu_ptr(pgdat->per_cpu_nodestats, cpu)->stat_threshold
= max(threshold, pgdat_threshold);
}

/* percpu_drift_mark: min watermark ์œ„๋ฐ˜ ์œ„ํ—˜ ์‹œ ์„ค์ • */
tolerate_drift = low_wmark_pages(zone) - min_wmark_pages(zone);
max_drift = num_online_cpus() * threshold;
if (max_drift > tolerate_drift)
zone->percpu_drift_mark = high_wmark_pages(zone) + max_drift;
}
}

์—ญํ• : ๋ชจ๋“  ์˜จ๋ผ์ธ ์กด์˜ per-CPU ๋“œ๋ฆฌํ”„ํŠธ ์ž„๊ณ„๊ฐ’์„ ์žฌ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. ์ž„๊ณ„๊ฐ’์€ CPU ์ˆ˜์™€ ์กด ํฌ๊ธฐ์— ๋”ฐ๋ผ ๋กœ๊ทธ ์Šค์ผ€์ผ๋กœ ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค. ์ตœ๋Œ€๊ฐ’์€ 125์ž…๋‹ˆ๋‹ค.

9. `calculate_normal_threshold` (mm/vmstat.c:225)

/* ์ผ๋ฐ˜ ์‹œ๋‚˜๋ฆฌ์˜ค ๋“œ๋ฆฌํ”„ํŠธ ์ž„๊ณ„๊ฐ’ ๊ณ„์‚ฐ */
int calculate_normal_threshold(struct zone *zone)
{
int threshold;
int mem;    /* 128MB ๋‹จ์œ„ ๋ฉ”๋ชจ๋ฆฌ */

mem = zone_managed_pages(zone) >> (27 - PAGE_SHIFT);
threshold = 2 * fls(num_online_cpus()) * (1 + fls(mem));
threshold = min(125, threshold);

return threshold;
}

์—ญํ• : CPU ์ˆ˜์™€ ์กด ํฌ๊ธฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋“œ๋ฆฌํ”„ํŠธ ์ž„๊ณ„๊ฐ’์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. fls()๋ฅผ ์‚ฌ์šฉํ•œ ๋กœ๊ทธ ์Šค์ผ€์ผ๋ง์œผ๋กœ CPU ์ˆ˜๊ฐ€ ๋งŽ๊ฑฐ๋‚˜ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํด์ˆ˜๋ก ์ž„๊ณ„๊ฐ’์ด ๋†’์•„์ง‘๋‹ˆ๋‹ค.

10. `calculate_pressure_threshold` (mm/vmstat.c:201)

/* ๋ฉ”๋ชจ๋ฆฌ ์••๋ ฅ ์‹œ๋‚˜๋ฆฌ์˜ค ๋“œ๋ฆฌํ”„ํŠธ ์ž„๊ณ„๊ฐ’ ๊ณ„์‚ฐ */
int calculate_pressure_threshold(struct zone *zone)
{
int threshold;
int watermark_distance;

/* low - min ์›Œํ„ฐ๋งˆํฌ ์ฐจ์ด์—์„œ ์ž„๊ณ„๊ฐ’ ์‚ฐ์ถœ */
watermark_distance = low_wmark_pages(zone) - min_wmark_pages(zone);
threshold = max(1, (int)(watermark_distance / num_online_cpus()));
threshold = min(125, threshold);

return threshold;
}

์—ญํ• : ๋ฉ”๋ชจ๋ฆฌ ์••๋ ฅ ์ƒํ™ฉ์—์„œ min watermark ์œ„๋ฐ˜์„ ๋ฐฉ์ง€ํ•˜๋„๋ก ๋ณด์ˆ˜์ ์ธ ์ž„๊ณ„๊ฐ’์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. set_pgdat_percpu_threshold()์—์„œ vmscan์ด ํ˜ธ์ถœ๋  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.


ํ˜ธ์ถœ ํ๋ฆ„

1. /proc/vmstat ์ฝ๊ธฐ ํ๋ฆ„

cat /proc/vmstat
โ†“
seq_read()
โ†“
vmstat_op.start (vmstat_start)
โ”œโ”€โ”€ fold_vm_numa_events()          โ† NUMA ์ด๋ฒคํŠธ ํด๋”ฉ
โ”œโ”€โ”€ global_zone_page_state()       โ† ์กด๋ณ„ ์นด์šดํ„ฐ ์ˆ˜์ง‘
โ”œโ”€โ”€ global_numa_event_state()      โ† NUMA ์ด๋ฒคํŠธ ์ˆ˜์ง‘
โ”œโ”€โ”€ global_node_page_state_pages() โ† ๋…ธ๋“œ๋ณ„ ์นด์šดํ„ฐ ์ˆ˜์ง‘
โ”œโ”€โ”€ global_dirty_limits()          โ† ๋”ํ‹ฐ ํ•œ๋„ ์ˆ˜์ง‘
โ””โ”€โ”€ all_vm_events()                โ† VM ์ด๋ฒคํŠธ ์ˆ˜์ง‘
โ†“
vmstat_op.show (vmstat_show)
โ”œโ”€โ”€ seq_puts(vmstat_text[off])     โ† ํ•ญ๋ชฉ ์ด๋ฆ„ ์ถœ๋ ฅ
โ””โ”€โ”€ seq_put_decimal_ull(value)     โ† ์นด์šดํ„ฐ ๊ฐ’ ์ถœ๋ ฅ
โ†“
vmstat_op.next (vmstat_next)
โ†“
vmstat_op.stop (vmstat_stop)
โ””โ”€โ”€ kfree(m->private)             โ† ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ

2. VM ์นด์šดํ„ฐ ์—…๋ฐ์ดํŠธ ํ๋ฆ„

๋ฉ”๋ชจ๋ฆฌ ์ด๋ฒคํŠธ ๋ฐœ์ƒ (์˜ˆ: ํŽ˜์ด์ง€ ํ• ๋‹น)
โ†“
__mod_zone_page_state(zone, item, delta)  (mm/vmstat.c:345)
โ”œโ”€โ”€ preempt_disable_nested()
โ”œโ”€โ”€ x = delta + __this_cpu_read(*p)   โ† per-CPU ์ฐจ์ด๊ฐ’์— delta ๋”ํ•˜๊ธฐ
โ”œโ”€โ”€ t = __this_cpu_read(stat_threshold) โ† ์ž„๊ณ„๊ฐ’ ์ฝ๊ธฐ
โ”œโ”€โ”€ abs(x) > t ์ด๋ฉด
โ”‚   โ”œโ”€โ”€ zone_page_state_add(x)        โ† ์ „์—ญ ์นด์šดํ„ฐ์— ๋ฐ˜์˜
โ”‚   โ””โ”€โ”€ x = 0
โ””โ”€โ”€ __this_cpu_write(*p, x)           โ† per-CPU ์นด์šดํ„ฐ ์ €์žฅ

3. CPU ์˜คํ”„๋ผ์ธ ์‹œ ์นด์šดํ„ฐ ๋ณ‘ํ•ฉ ํ๋ฆ„

CPU ์˜คํ”„๋ผ์ธ
โ†“
vmstat_cpu_dead(cpu)  (mm/vmstat.c:2204)
โ”œโ”€โ”€ refresh_zone_stat_thresholds() โ† ์กด๋ณ„ ์ž„๊ณ„๊ฐ’ ์žฌ๊ณ„์‚ฐ
โ””โ”€โ”€ (๋…ธ๋“œ์— ๋‚จ์€ CPU ์—†์œผ๋ฉด)
โ””โ”€โ”€ node_clear_state(node, N_CPU)

CPU ์˜คํ”„๋ผ์ธ +์กด ๋น„ํ™œ์„ฑํ™” ์‹œ
โ†“
drain_zonestat(zone, pzstats)  (mm/vmstat.c:948)
โ”œโ”€โ”€ zone_page_state_add()    โ† ์กด๋ณ„ ์ฐจ์ด๊ฐ’ ์ „์—ญ ๋ฐ˜์˜
โ””โ”€โ”€ zone_numa_event_add()    โ† NUMA ์ด๋ฒคํŠธ ์ „์—ญ ๋ฐ˜์˜

์กฐ๊ฑด๋ณ„ ๋น„๊ต

VM ์นด์šดํ„ฐ ์œ ํ˜• ๋น„๊ต

์นด์šดํ„ฐ ์œ ํ˜•์ €์žฅ ์œ„์น˜์—…๋ฐ์ดํŠธ ๋ฐฉ์‹์šฉ๋„
VM Zone Statper-CPU + ์ „์—ญ์ฐจ์ด๊ฐ’ ๊ธฐ๋ฐ˜ ์ž„๊ณ„๊ฐ’์กด๋ณ„ ํŽ˜์ด์ง€ ์ƒํƒœ
VM Node Statper-CPU + ์ „์—ญ์ฐจ์ด๊ฐ’ ๊ธฐ๋ฐ˜ ์ž„๊ณ„๊ฐ’๋…ธ๋“œ๋ณ„ ํŽ˜์ด์ง€ ์ƒํƒœ
VM Event Statper-CPU์ฃผ๊ธฐ์  ํด๋”ฉVM ์ด๋ฒคํŠธ ๋ฐœ์ƒ ํšŸ์ˆ˜
VM NUMA Eventper-CPU + ์กด๋ณ„ + ์ „์—ญ์ฐจ์ด๊ฐ’ ๊ธฐ๋ฐ˜NUMA ์ด๋ฒคํŠธ

๋“œ๋ฆฌํ”„ํŠธ ๊ด€๋ฆฌ ๋ฐฉ์‹ ๋น„๊ต

๊ตฌ์กฐ์ฒดํ•„๋“œ์—ญํ• 
per_cpu_zonestatvm_stat_diff[]์กด๋ณ„ ์นด์šดํ„ฐ ์ฐจ์ด๊ฐ’
per_cpu_zonestatstat_threshold๋“œ๋ฆฌํ”„ํŠธ ์ž„๊ณ„๊ฐ’
per_cpu_nodestatvm_node_stat_diff[]๋…ธ๋“œ๋ณ„ ์นด์šดํ„ฐ ์ฐจ์ด๊ฐ’
per_cpu_nodestatstat_threshold๋“œ๋ฆฌํ”„ํŠธ ์ž„๊ณ„๊ฐ’

/proc ์ธํ„ฐํŽ˜์ด์Šค ๋น„๊ต

ํŒŒ์ผ๋‚ด์šฉํ˜•์‹
/proc/vmstatVM ํ†ต๊ณ„ ์นด์šดํ„ฐํ…์ŠคํŠธ (์ด๋ฆ„-๊ฐ’ ์Œ)
/proc/meminfo๋ฉ”๋ชจ๋ฆฌ ์š”์•ฝ ์ •๋ณดํ…์ŠคํŠธ (์ด๋ฆ„-๊ฐ’-๋‹จ์œ„)
/proc/buddyinfo๋ฒ„๋”” ํ• ๋‹น์ž ์ƒํƒœํ…์ŠคํŠธ (๋…ธ๋“œ, ์กด, ์ฃผ๋ฌธ๋ณ„)
/proc/pagetypeinfoํŽ˜์ด์ง€ ํƒ€์ž…๋ณ„ ์ •๋ณดํ…์ŠคํŠธ (์ด๋™ ํƒ€์ž…๋ณ„)

๊ด€๋ จ ๋ฌธ์„œ

  • 00-overview.html โ€” ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ฐœ์š”
  • 01-page_alloc.html โ€” ๋ฒ„๋”” ํ• ๋‹น์ž
  • 05-page_reclaim.html โ€” ํŽ˜์ด์ง€ ํšŒ์ˆ˜
  • 12-folio.html โ€” ํด๋ฆฌ์˜ค / ํŽ˜์ด์ง€ ์บ์‹œ
  • 32-workingset.html โ€” ์›Œํ‚น์…‹
  • 39-vmpressure.html โ€” VM ์••๋ ฅ

  • ์ฐธ๊ณ  ์‚ฌํ•ญ

  • VM ํ†ต๊ณ„๋Š” ์‹œ์Šคํ…œ ์„ฑ๋Šฅ ๋ถ„์„๊ณผ ๋ฉ”๋ชจ๋ฆฌ ๋ฌธ์ œ ์ง„๋‹จ์— ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.
  • per-CPU ์นด์šดํ„ฐ ๊ตฌ์กฐ๋กœ SMP ํ™˜๊ฒฝ์—์„œ ๋†’์€ ์„ฑ๋Šฅ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ๋“œ๋ฆฌํ”„ํŠธ ์ž„๊ณ„๊ฐ’์€ CPU ์ˆ˜์™€ ๋ฉ”๋ชจ๋ฆฌ ํฌ๊ธฐ์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ๊ณ„์‚ฐ๋ฉ๋‹ˆ๋‹ค.
  • NUMA ์ด๋ฒคํŠธ๋Š” ์ •ํ™•ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํ•„์š” ์‹œ ์ˆ˜๋™์œผ๋กœ ํด๋”ฉ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • /proc/sys/vm/stat_refresh๋ฅผ ํ†ตํ•ด ์ˆ˜๋™์œผ๋กœ ์นด์šดํ„ฐ๋ฅผ ์ƒˆ๋กœ๊ณ ์นจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.