为什么 host_statistics64() 返回不一致的结果?

2024-01-03

为什么 OS X 10.6.8 中的 host_statistics64() (我不知道其他版本是否有这个问题)返回的空闲、活动、非活动和有线内存的计数不等于 RAM 总量?为什么缺少页数不一致?

以下输出表示十秒内未分类为空闲、活动、非活动或有线的页面数(大约每秒采样一次)。

458
243
153
199
357
140
304
93
181
224

产生上述数字的代码是:

#include <stdio.h>
#include <mach/mach.h>
#include <mach/vm_statistics.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char** argv) {
        struct vm_statistics64 stats;
        mach_port_t host    = mach_host_self();
        natural_t   count   = HOST_VM_INFO64_COUNT;
        natural_t   missing = 0;
        int         debug   = argc == 2 ? !strcmp(argv[1], "-v") : 0;
        kern_return_t ret;
        int           mib[2];
        long          ram;
        natural_t     pages;
        size_t        length;
        int           i;

        mib[0] = CTL_HW;
        mib[1] = HW_MEMSIZE;
        length = sizeof(long);
        sysctl(mib, 2, &ram, &length, NULL, 0);
        pages  = ram / getpagesize();

        for (i = 0; i < 10; i++) {
                if ((ret = host_statistics64(host, HOST_VM_INFO64, (host_info64_t)&stats, &count)) != KERN_SUCCESS) {
                        printf("oops\n");
                        return 1;
                }

                /* updated for 10.9 */
                missing = pages - (
                        stats.free_count     +
                        stats.active_count   +
                        stats.inactive_count +
                        stats.wire_count     +
                        stats.compressor_page_count
                );

                if (debug) {
                        printf(
                                "%11d pages (# of pages)\n"
                                "%11d free_count (# of pages free) \n"
                                "%11d active_count (# of pages active) \n"
                                "%11d inactive_count (# of pages inactive) \n"
                                "%11d wire_count (# of pages wired down) \n"
                                "%11lld zero_fill_count (# of zero fill pages) \n"
                                "%11lld reactivations (# of pages reactivated) \n"
                                "%11lld pageins (# of pageins) \n"
                                "%11lld pageouts (# of pageouts) \n"
                                "%11lld faults (# of faults) \n"
                                "%11lld cow_faults (# of copy-on-writes) \n"
                                "%11lld lookups (object cache lookups) \n"
                                "%11lld hits (object cache hits) \n"
                                "%11lld purges (# of pages purged) \n"
                                "%11d purgeable_count (# of pages purgeable) \n"
                                "%11d speculative_count (# of pages speculative (also counted in free_count)) \n"
                                "%11lld decompressions (# of pages decompressed) \n"
                                "%11lld compressions (# of pages compressed) \n"
                                "%11lld swapins (# of pages swapped in (via compression segments)) \n"
                                "%11lld swapouts (# of pages swapped out (via compression segments)) \n"
                                "%11d compressor_page_count (# of pages used by the compressed pager to hold all the compressed data) \n"
                                "%11d throttled_count (# of pages throttled) \n"
                                "%11d external_page_count (# of pages that are file-backed (non-swap)) \n"
                                "%11d internal_page_count (# of pages that are anonymous) \n"
                                "%11lld total_uncompressed_pages_in_compressor (# of pages (uncompressed) held within the compressor.) \n",
                                pages, stats.free_count, stats.active_count, stats.inactive_count,
                                stats.wire_count, stats.zero_fill_count, stats.reactivations,
                                stats.pageins, stats.pageouts, stats.faults, stats.cow_faults,
                                stats.lookups, stats.hits, stats.purges, stats.purgeable_count,
                                stats.speculative_count, stats.decompressions, stats.compressions,
                                stats.swapins, stats.swapouts, stats.compressor_page_count,
                                stats.throttled_count, stats.external_page_count,
                                stats.internal_page_count, stats.total_uncompressed_pages_in_compressor
                        );
                }

                printf("%i\n", missing);
                sleep(1);
        }

        return 0;
}

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,我会让你知道。也许这会带来有趣的见解。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 host_statistics64() 返回不一致的结果? 的相关文章

  • Gcc 4.2 版本缺失

    在我的带有 osx lion 和 XCode 4 1 的新 macbook pro 上 我遇到了一些 gcc 问题 In usr bin我找不到gcc 4 2 我只有以下版本 i686 apple darwin11 llvm gcc 4 2
  • libSDL、CMake 和 Mac OS X Lion

    我正在尝试在我的Mac上编译cmake项目 但它取决于SDL框架 我安装了这个框架 在 cmake 之后向我报告找不到 libSDL 我自己设置了以下导出变量 按照 cmake 的建议 export SDL INCLUDE DIR Libr
  • macOS 更新后 Jenkins 用户消失

    我在 Mac 上运行 Jenkins 作为 CI 服务器 使用用户 jenkins 的典型设置 它在 macOS 10 12 上运行良好 今天我将 macOS 升级到 10 13 High Sierra 升级过程完成后 Jenkins 无法
  • PyCharm:安装包失败

    我需要安装一些软件包 无论我尝试哪个方向 情况只会变得更糟 请帮忙 40 497 执行错误 目录 Users doekewartena Library Caches pip http 或其父目录 不属于当前用户并且缓存已被禁用 请检查该目录
  • 如何在 Xcode 上共同设计并启用第 3 方 CLI 的强化运行时?

    我的项目需要 Ghostscript 来完成很多任务 因此我已将 gs CLI 工具添加到我的项目资源中 然而 当我尝试对项目应用程序进行公证时 Xcode 向我显示了以下内容 我认为这可能是因为 Ghostscript 便携式 CLI 是
  • Cocoa - 在另一个 xib 上显示 xib

    谁能告诉我如何 或指导我有关信息 在另一个 xib 笔尖 上显示 xib 笔尖 我希望如何放置它 以便我可以以编程方式将它移动到主笔尖 就像这样 这显然不起作用 void drawRect NSRect dirtyRect NSRect c
  • 为什么结构中“[0]byte”的位置很重要?

    0 byte在golang中不应该占用任何内存空间 但这两个结构体的大小不同 type bar2 struct A int 0 byte type bar3 struct 0 byte A int 那么为什么这个位置 0 byte这里重要吗
  • 如何在 OS X 版 Chrome 上手动安装扩展程序?

    我已经创建了一个 chrome 扩展 我想将其手动添加到 mac osx 10 7 和 10 8 上的 chrome 我尝试执行以下步骤 在以下 url 下添加扩展文件夹 用户 talmutzafi 库 应用程序支持 Google Chro
  • 如何从 MacOS X Dock 启动脚本?

    我知道我可以将应用程序固定到扩展坞并从那里启动它们 但是 有没有办法将不是 MacOS 意义上的 应用程序 的程序 例如 bash 脚本 固定到扩展坞上 您可以将任何文件拖到 Dock 的右侧栏 垃圾箱和文件夹所在的位置 然后单击它来执行它
  • 每个 CPU 核心处于 C0 电源状态的时间

    任何帮助弄清楚如何做到这一点都会很棒 在过去一秒内 每个 CPU 核心处于 C0 电源状态的时间有多少 这是针对 Mac 应用程序的 因此需要 Objective C cocoa 和 c OS X 没有任何公开 CPU c 状态的 API
  • PHP 对象创建和内存使用

    一个基本的虚拟类 class foo var bar 0 function foo function boo echo memory get usage echo n foo new foo echo memory get usage ec
  • 在 OS X 上以编程方式禁用环境光传感器屏幕调暗

    我已经在内核代码仓库中挖掘了好几天了 但我在任何地方都找不到这个 我想禁用 启用 检测许多高端 Mac 笔记本电脑所具有的基于环境光传感器的屏幕调光的状态 这是通过显示控制面板中的 自动调整亮度 复选框激活的调光 请注意 我是NOT谈论半暗
  • 在C语言中如何清屏? [复制]

    这个问题在这里已经有答案了 我想清除屏幕上的所有文字 我尝试过使用 include
  • 什么是内部类的合成反向引用

    我正在寻找应用程序中的内存泄漏 我正在使用的探查器告诉我寻找这些类型的引用 但我不知道我在寻找什么 有人可以解释一下吗 Thanks Elliott 您可以对 OUTER 类进行合成反向引用 但不能对内部类实例进行合成 e g class
  • 为什么 Mac OS 上的 C 运行时允许预组合和分解的 UTF-8?

    所以我们都知道 Mac OS 上的文件系统具有使用完全分解的 UTF 8 的古怪功能 如果您调用 POSIX API 例如realpath 例如 您将从 Mac OS 返回这样一个完全分解的 UTF 8 字符串 当使用像这样的 API 时f
  • UseCompressedOops JVM 标志有什么作用以及何时应该使用它?

    HotSpot JVM 标志是什么 XX UseCompressedOops我应该做什么以及什么时候使用它 在 64 位 Java 实例上使用它 与不使用它 时 我会看到什么样的性能和内存使用差异 去年大多数 HotSpot JVM 都默认
  • 对 boost 库的依赖项没有完整路径

    我已经成功构建了动态库 依赖于使用自定义前缀构建和安装的 boost 库 b2 install prefix PREFIX 然而 当我跑步时otool L在我的库中 我得到如下输出 libboost regex dylib compatib
  • C 中带有指针的结构的内存开销[重复]

    这个问题在这里已经有答案了 我意识到当我的结构包含指针时 它们会产生内存开销 这里有一个例子 typedef struct int num1 int num2 myStruct1 typedef struct int p int num2
  • sizeof(某个指针)总是等于四吗?

    例如 sizeof char 返回 4 也是如此int long long 我尝试过的一切 这有什么例外吗 您得到的保证是sizeof char 1 没有其他保证 包括不保证sizeof int sizeof double 实际上 在 16
  • 使用 mono/nunit-console/4 在 Mac OS X 控制台上运行测试

    我安装了 Max OS X 10 11 1 上面装有 Xamarin 我编写了简单的测试类 只是为了测试在 Mac OS X 和 Ubuntu 上运行 Nunit 测试 该类实际上有一个返回字符串的方法 using System names

随机推荐