统计数据

当进程执行内存相关操作时,如分配大页,内核会记录下相关动作。这些数据作为进程生命周期的一部分存在。

mm->rss_stat[]

位置

首先我们来看看mm->rss_stat这个统计数据,在mm_struct中有一个rss_stat[]数组。

类型

这个数组一共就这么些类型。

enum {
	MM_FILEPAGES,	/* Resident file mapping pages */
	MM_ANONPAGES,	/* Resident anonymous pages */
	MM_SWAPENTS,	/* Anonymous swap entries */
	MM_SHMEMPAGES,	/* Resident shared memory pages */
	NR_MM_COUNTERS
};

操作

对操作的方法是

修改类:

  • add_mm_counter()

  • inc_mm_counter()

  • dec_mm_counter()

读取类:

  • get_mm_counter_sum()

这个看上去是统计进程中各种类型内存的数量,以base page为单位。

比如在do_anonymous_page()中,如果分配成功了匿名页,则会 add_mm_counter(vma->vm_mm, MM_ANONPAGES, nr_pages);来增加对应的计数。

而在do_swap_page()中,如果是交换进页面,则会

增加匿名页计数,同时减少swap页计数。

展示

这里统计的值会在文件/proc/self/status中展示。

其中的RssAnon, RssFile, RssShmem和VmSwap。

vm_event_state

位置

这是一个全局的per_cpu数组。

此外,这个还是一个内核配置项。只有配置了CONFIG_VM_EVENT_COUNTERS才会记录相关的事件信息。

类型

这个种类就多很多了, 详见vm_event_item,目测有120+种。

操作

这个事件计数的操作比较简单:

变更:

  • _count_vm_event()

  • count_vm_event()

  • _count_vm_events()

  • count_vm_events()

读取:

  • all_vm_enents()

观察

这个计数是可以观察的,也就是在我们熟知的vmstat文件。

不过vmstat文件中,不仅包含了vm_event_states的信息。所以事件记录是从pgpgin字段开始的。

pgdat->vm_stat[]/vm_node_stat[]

位置

看了下,发现一个有意思的东四,有两个一模一样的统计数组。

  • pgdat->vm_stat[]

  • vm_node_stat[]

两个都是原子变量,只不过一个在pgdat中,一个是全局的。

所以可以看到,最核心的变量更新函数 node_page_state_add()是对这两个变量同时更新的。

而且这个XXX_add()名字好迷惑,实际上只要传入的参数是负数,就是减法操作。

类型

一个枚举型数组,里面约有四十左右类型。

包括了active_anon, active_file等。

操作

目前看到操作这个数组的方式,除了直接通过原子操作atomic_long_read()之外,有下面几个封装。

修改类:

  • 以pgdate为参数,最核心的API,其余都是调用这个

    • node_page_state_add(pgdat, )

    • __inc_node_state(pgdat, )

    • __dec_node_state(pgdat, )

    • __mod_node_page_state(pgdat, )

    • mod_node_page_state(pgdat, ), 可能依赖mod_node_state()

  • 以page为参数

    • __inc_node_page_state(page, )

    • inc_node_page_state(page, )

    • __dec_node_page_state(page, )

    • dec_node_page_state(page, )

  • 以folio为参数

    • __node_stat_mod_folio(folio, )

    • node_stat_mod_folio(folio, )

    • __node_stat_add_folio(folio, )

    • node_stat_add_folio(folio, )

    • __node_stat_sub_folio(folio, )

    • node_stat_sub_folio(folio, )

    • __folio_mod_stat(folio, )

大致看了下,有下划线前缀的是不关中断的版本。偷懒的讲,不确定的时候就有没有下划线的版本比较保险。

基本上以page为参数的API应该是要退出历史舞台了,取而代之的是以folio为参数的。

但是统计数据中有些是和内存页无关的,比如PGPROMOTE_CANDIDATE,就还是用以pgdat为参数的API。

读取类:

  • global_node_page_state()

  • global_node_page_state_pages()

这两个蛮奇怪的,global_node_page_state()调用的是global_node_page_state_pages()。而前者会在某条件下warn一次。

展示

/proc/meminfo中会读取global_node_page_state()来返回当前系统相关信息。

zone->vm_stat[]/vm_zone_stat[]

位置

和vm_node_stat[]一样,有两个一模一样的数组:一个是全局原子变量,一个是在zone里的变量

修改的时候,两者会同时修改。

类型

一个枚举型数组,里面约有四十左右类型。

操作

修改类:

  • 以zone为参数

    • zone_page_state_add(zone, x)

    • __inc_zone_state(zone, )

    • __dec_zone_state(zone, )

    • mod_zone_page_state(), 可能依赖mod_zone_state(zone, )

  • 以page为参数

    • __inc_zone_page_state(page, )

    • inc_zone_page_state(page, )

    • __dec_zone_page_state(page, )

    • dec_zone_page_state(page, )

  • 以folio为参数

    • __zone_stat_mod_folio(folio, )

    • zone_stat_mod_folio(folio, )

    • __zone_stat_add_folio(folio, )

    • zone_stat_add_folio(folio, )

    • __zone_stat_sub_folio(folio, )

    • zone_stat_sub_folio(folio, )

有意思的是,folio的这些api中,目前只用了zone_stat_mod_folio()。

而page的api,目前也只有zsmalloc在用了。

读取类:

  • global_zone_page_state()

展示

/proc/vmstat中会读取global_zone_page_state()来返回当前系统相关信息。

Last updated

Was this helpful?