TL;DR:
-
host_statistics64()
从不同来源获取信息可能会花费时间并可能产生不一致的结果。
-
host_statistics64()
通过名称类似的变量获取一些信息vm_page_foo_count
。但并非所有这些变量都被考虑在内,例如vm_page_stolen_count
is not.
- 众所周知的
/usr/bin/top
adds 被盗页面到有线页面。这表明在计算页数时应考虑这些页数。
Notes
- 我正在使用 macOS 10.12Darwin内核版本16.5.0 xnu-3789.51.2~3/RELEASE_X86_64 x86_64但所有行为都是完全可重现的。
- 我将链接很多我在我的机器上使用的 XNU 版本的源代码。在这里能找到它:xnu-3789.51.2 https://opensource.apple.com/source/xnu/xnu-3789.51.2/.
- 你写的程序基本上是一样的
/usr/bin/vm_stat
这只是一个包装host_statistics64()
(and host_statistics()
)。相应的源代码可以在这里找到:system_cmds-496/vm_stat.tproj/vm_stat.c https://opensource.apple.com/source/system_cmds/system_cmds-496/vm_stat.tproj/vm_stat.c.auto.html.
如何host_statistics64()
适合 XNU 以及它是如何工作的?
据 widley 所知,OS X 内核被称为XNU (XNU IS NOT UNIX) and “是一个混合内核,将卡内基梅隆大学开发的 Mach 内核与 FreeBSD 和 C++ API 的组件相结合,用于编写名为 IOKit 的驱动程序。” (https://github.com/opensource-apple/xnu/blob/10.12/README.md https://github.com/opensource-apple/xnu/blob/10.12/README.md)
虚拟内存管理(VM)是Mach所以host_statistics64()
位于此处。让我们仔细看看它的实现,它包含在xnu-3789.51.2/osfmk/kern/host.c https://opensource.apple.com/source/xnu/xnu-3789.51.2/osfmk/kern/host.c.auto.html.
函数签名是
kern_return_t
host_statistics64(host_t host, host_flavor_t flavor, host_info64_t info, mach_msg_type_number_t * count);
第一个相关行是
[...]
processor_t processor;
vm_statistics64_t stat;
vm_statistics64_data_t host_vm_stat;
mach_msg_type_number_t original_count;
unsigned int local_q_internal_count;
unsigned int local_q_external_count;
[...]
processor = processor_list;
stat = &PROCESSOR_DATA(processor, vm_stat);
host_vm_stat = *stat;
if (processor_count > 1) {
simple_lock(&processor_list_lock);
while ((processor = processor->processor_list) != NULL) {
stat = &PROCESSOR_DATA(processor, vm_stat);
host_vm_stat.zero_fill_count += stat->zero_fill_count;
host_vm_stat.reactivations += stat->reactivations;
host_vm_stat.pageins += stat->pageins;
host_vm_stat.pageouts += stat->pageouts;
host_vm_stat.faults += stat->faults;
host_vm_stat.cow_faults += stat->cow_faults;
host_vm_stat.lookups += stat->lookups;
host_vm_stat.hits += stat->hits;
host_vm_stat.compressions += stat->compressions;
host_vm_stat.decompressions += stat->decompressions;
host_vm_stat.swapins += stat->swapins;
host_vm_stat.swapouts += stat->swapouts;
}
simple_unlock(&processor_list_lock);
}
[...]
We get host_vm_stat
这是类型vm_statistics64_data_t
。这只是一个typedef struct vm_statistics64
正如你所看到的xnu-3789.51.2/osfmk/mach/vm_statistics.h https://opensource.apple.com/source/xnu/xnu-3789.51.2/osfmk/mach/vm_statistics.h.auto.html。我们从 makro 获取处理器信息PROCESSOR_DATA()
定义于xnu-3789.51.2/osfmk/kern/processor_data.h https://opensource.apple.com/source/xnu/xnu-3789.51.2/osfmk/kern/processor_data.h.auto.html。我们填host_vm_stat
通过简单地将相关数字相加来循环遍历我们所有的处理器。
正如你所看到的,我们发现了一些众所周知的统计数据,例如zero_fill_count
or compressions
但并非全部涵盖host_statistics64()
.
接下来的相关行是:
stat = (vm_statistics64_t)info;
stat->free_count = vm_page_free_count + vm_page_speculative_count;
stat->active_count = vm_page_active_count;
[...]
stat->inactive_count = vm_page_inactive_count;
stat->wire_count = vm_page_wire_count + vm_page_throttled_count + vm_lopage_free_count;
stat->zero_fill_count = host_vm_stat.zero_fill_count;
stat->reactivations = host_vm_stat.reactivations;
stat->pageins = host_vm_stat.pageins;
stat->pageouts = host_vm_stat.pageouts;
stat->faults = host_vm_stat.faults;
stat->cow_faults = host_vm_stat.cow_faults;
stat->lookups = host_vm_stat.lookups;
stat->hits = host_vm_stat.hits;
stat->purgeable_count = vm_page_purgeable_count;
stat->purges = vm_page_purged_count;
stat->speculative_count = vm_page_speculative_count;
我们重复利用stat
并将其设为我们的输出结构。然后我们填写free_count
两个之和unsigned long
called vm_page_free_count
and vm_page_speculative_count
。我们以相同的方式收集其他剩余数据(通过使用名为vm_page_foo_count
)或通过获取统计数据host_vm_stat
我们在上面填写了。
1、结论我们从不同来源收集数据。来自处理器信息或来自称为vm_page_foo_count
。这会花费时间,并且可能会导致一些不一致,事实上虚拟机是一个非常快速且连续的过程。
让我们仔细看看已经提到的变量vm_page_foo_count
。它们定义在xnu-3789.51.2/osfmk/vm/vm_page.h https://opensource.apple.com/source/xnu/xnu-3789.51.2/osfmk/vm/vm_page.h.auto.html如下:
extern
unsigned int vm_page_free_count; /* How many pages are free? (sum of all colors) */
extern
unsigned int vm_page_active_count; /* How many pages are active? */
extern
unsigned int vm_page_inactive_count; /* How many pages are inactive? */
#if CONFIG_SECLUDED_MEMORY
extern
unsigned int vm_page_secluded_count; /* How many pages are secluded? */
extern
unsigned int vm_page_secluded_count_free;
extern
unsigned int vm_page_secluded_count_inuse;
#endif /* CONFIG_SECLUDED_MEMORY */
extern
unsigned int vm_page_cleaned_count; /* How many pages are in the clean queue? */
extern
unsigned int vm_page_throttled_count;/* How many inactives are throttled */
extern
unsigned int vm_page_speculative_count; /* How many speculative pages are unclaimed? */
extern unsigned int vm_page_pageable_internal_count;
extern unsigned int vm_page_pageable_external_count;
extern
unsigned int vm_page_xpmapped_external_count; /* How many pages are mapped executable? */
extern
unsigned int vm_page_external_count; /* How many pages are file-backed? */
extern
unsigned int vm_page_internal_count; /* How many pages are anonymous? */
extern
unsigned int vm_page_wire_count; /* How many pages are wired? */
extern
unsigned int vm_page_wire_count_initial; /* How many pages wired at startup */
extern
unsigned int vm_page_free_target; /* How many do we want free? */
extern
unsigned int vm_page_free_min; /* When to wakeup pageout */
extern
unsigned int vm_page_throttle_limit; /* When to throttle new page creation */
extern
uint32_t vm_page_creation_throttle; /* When to throttle new page creation */
extern
unsigned int vm_page_inactive_target;/* How many do we want inactive? */
#if CONFIG_SECLUDED_MEMORY
extern
unsigned int vm_page_secluded_target;/* How many do we want secluded? */
#endif /* CONFIG_SECLUDED_MEMORY */
extern
unsigned int vm_page_anonymous_min; /* When it's ok to pre-clean */
extern
unsigned int vm_page_inactive_min; /* When to wakeup pageout */
extern
unsigned int vm_page_free_reserved; /* How many pages reserved to do pageout */
extern
unsigned int vm_page_throttle_count; /* Count of page allocations throttled */
extern
unsigned int vm_page_gobble_count;
extern
unsigned int vm_page_stolen_count; /* Count of stolen pages not acccounted in zones */
[...]
extern
unsigned int vm_page_purgeable_count;/* How many pages are purgeable now ? */
extern
unsigned int vm_page_purgeable_wired_count;/* How many purgeable pages are wired now ? */
extern
uint64_t vm_page_purged_count; /* How many pages got purged so far ? */
这是关于我们只能访问非常有限的数量的大量统计数据host_statistics64()
。这些统计数据大部分更新于xnu-3789.51.2/osfmk/vm/vm_resident.c https://opensource.apple.com/source/xnu/xnu-3789.51.2/osfmk/vm/vm_resident.c.auto.html。例如,此函数将页面释放到空闲页面列表:
/*
* vm_page_release:
*
* Return a page to the free list.
*/
void
vm_page_release(
vm_page_t mem,
boolean_t page_queues_locked)
{
[...]
vm_page_free_count++;
[...]
}
非常有趣的是extern unsigned int vm_page_stolen_count; /* Count of stolen pages not acccounted in zones */
。什么是被盗页面?似乎有一些机制可以从某些列表中取出页面,即使它通常不会被调出。这些机制之一是age推测页面列表中的页面的。xnu-3789.51.2/osfmk/vm/vm_page.h https://opensource.apple.com/source/xnu/xnu-3789.51.2/osfmk/vm/vm_page.h.auto.html告诉我们
* VM_PAGE_MAX_SPECULATIVE_AGE_Q * VM_PAGE_SPECULATIVE_Q_AGE_MS
* defines the amount of time a speculative page is normally
* allowed to live in the 'protected' state (i.e. not available
* to be stolen if vm_pageout_scan is running and looking for
* pages)... however, if the total number of speculative pages
* in the protected state exceeds our limit (defined in vm_pageout.c)
* and there are none available in VM_PAGE_SPECULATIVE_AGED_Q, then
* vm_pageout_scan is allowed to steal pages from the protected
* bucket even if they are underage.
*
* vm_pageout_scan is also allowed to pull pages from a protected
* bin if the bin has reached the "age of consent" we've set
真的是void vm_pageout_scan(void)
增加vm_page_stolen_count
。可以在里面找到对应的源码xnu-3789.51.2/osfmk/vm/vm_pageout.c https://opensource.apple.com/source/xnu/xnu-3789.51.2/osfmk/vm/vm_pageout.c.auto.html.
我认为在计算虚拟机统计数据时不会考虑被盗页面host_statistics64()
does.
事实证明我是对的
证明这一点的最好方法是使用自定义版本编译 XNUhost_statistics64()
用手。我没有机会这样做,但很快就会尝试。
幸运的是,我们并不是唯一对正确的虚拟机统计数据感兴趣的人。因此我们应该看看众所周知的实施情况/usr/bin/top
(不包含在 XNU 中)可以在这里完全获得:top-108 https://opensource.apple.com/source/top/top-108/(我刚刚选择了macOS 10.12.4 发布 https://opensource.apple.com/release/macos-10124.html).
我们来看看top-108/libtop.c https://opensource.apple.com/source/top/top-108/libtop.c.auto.html我们发现以下内容:
static int
libtop_tsamp_update_vm_stats(libtop_tsamp_t* tsamp) {
kern_return_t kr;
tsamp->p_vm_stat = tsamp->vm_stat;
mach_msg_type_number_t count = sizeof(tsamp->vm_stat) / sizeof(natural_t);
kr = host_statistics64(libtop_port, HOST_VM_INFO64, (host_info64_t)&tsamp->vm_stat, &count);
if (kr != KERN_SUCCESS) {
return kr;
}
if (tsamp->pages_stolen > 0) {
tsamp->vm_stat.wire_count += tsamp->pages_stolen;
}
[...]
return kr;
}
tsamp
属于类型libtop_tsamp_t
这是一个定义在的结构体top-108/libtop.h https://opensource.apple.com/source/top/top-108/libtop.c.auto.html。它包含除其他事项外vm_statistics64_data_t vm_stat
and uint64_t pages_stolen
.
如你看到的,static int libtop_tsamp_update_vm_stats(libtop_tsamp_t* tsamp)
gets tsamp->vm_stat
填充由host_statistics64()
据我们所知。然后它检查是否tsamp->pages_stolen > 0
并将其加到wire_count
现场tsamp->vm_stat
.
2. 结论如果我们只使用,我们将无法获得这些被盗页面的数量host_statistics64()
as in /usr/bin/vm_stat
或您的示例代码!
Why is host_statistics64()
按原样实施?
老实说,我不知道。寻呼是一个复杂的过程,因此实时观察是一项具有挑战性的任务。我们必须注意到,它的实现似乎没有任何错误。我认为,如果我们能够访问,我们甚至无法获得 100% 准确的页面数vm_page_stolen_count
。实施/usr/bin/top
如果被盗页面的数量不是很大,则不计算被盗页面。
另外一个有趣的事情是函数上方的注释static void update_pages_stolen(libtop_tsamp_t *tsamp)
这是/* This is for <rdar://problem/6410098>. */
. 打开雷达 https://openradar.appspot.com/page/1是 Apple 软件的错误报告网站,通常按照评论中给出的格式对错误进行分类。我无法找到相关的bug;也许是关于缺页的问题。
我希望这些信息可以对您有所帮助。如果我设法在我的机器上编译最新(和定制)版本的 XNU,我会让你知道。也许这会带来有趣的见解。