VM ํต๊ณ (vmstat)
๊ฐ์ (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 Stat | per-CPU + ์ ์ญ | ์ฐจ์ด๊ฐ ๊ธฐ๋ฐ ์๊ณ๊ฐ | ์กด๋ณ ํ์ด์ง ์ํ |
| VM Node Stat | per-CPU + ์ ์ญ | ์ฐจ์ด๊ฐ ๊ธฐ๋ฐ ์๊ณ๊ฐ | ๋ ธ๋๋ณ ํ์ด์ง ์ํ |
| VM Event Stat | per-CPU | ์ฃผ๊ธฐ์ ํด๋ฉ | VM ์ด๋ฒคํธ ๋ฐ์ ํ์ |
| VM NUMA Event | per-CPU + ์กด๋ณ + ์ ์ญ | ์ฐจ์ด๊ฐ ๊ธฐ๋ฐ | NUMA ์ด๋ฒคํธ |
๋๋ฆฌํํธ ๊ด๋ฆฌ ๋ฐฉ์ ๋น๊ต
| ๊ตฌ์กฐ์ฒด | ํ๋ | ์ญํ |
|---|---|---|
| per_cpu_zonestat | vm_stat_diff[] | ์กด๋ณ ์นด์ดํฐ ์ฐจ์ด๊ฐ |
| per_cpu_zonestat | stat_threshold | ๋๋ฆฌํํธ ์๊ณ๊ฐ |
| per_cpu_nodestat | vm_node_stat_diff[] | ๋ ธ๋๋ณ ์นด์ดํฐ ์ฐจ์ด๊ฐ |
| per_cpu_nodestat | stat_threshold | ๋๋ฆฌํํธ ์๊ณ๊ฐ |
/proc ์ธํฐํ์ด์ค ๋น๊ต
| ํ์ผ | ๋ด์ฉ | ํ์ |
|---|---|---|
| /proc/vmstat | VM ํต๊ณ ์นด์ดํฐ | ํ ์คํธ (์ด๋ฆ-๊ฐ ์) |
| /proc/meminfo | ๋ฉ๋ชจ๋ฆฌ ์์ฝ ์ ๋ณด | ํ ์คํธ (์ด๋ฆ-๊ฐ-๋จ์) |
| /proc/buddyinfo | ๋ฒ๋ ํ ๋น์ ์ํ | ํ ์คํธ (๋ ธ๋, ์กด, ์ฃผ๋ฌธ๋ณ) |
| /proc/pagetypeinfo | ํ์ด์ง ํ์ ๋ณ ์ ๋ณด | ํ ์คํธ (์ด๋ ํ์ ๋ณ) |
๊ด๋ จ ๋ฌธ์
์ฐธ๊ณ ์ฌํญ
/proc/sys/vm/stat_refresh๋ฅผ ํตํด ์๋์ผ๋ก ์นด์ดํฐ๋ฅผ ์๋ก๊ณ ์นจํ ์ ์์ต๋๋ค.