18

OS X 10.6.8のhost_statistics64()(他のバージョンにこの問題があるかどうかはわかりません)が、RAMの合計量に達しない、空き、アクティブ、非アクティブ、および有線メモリのカウントを返すのはなぜですか?そして、なぜそれは一貫性のないページ数を失っているのですか?

次の出力は、10秒間にフリー、アクティブ、非アクティブ、または有線として分類されなかったページの数を表します(およそ1秒に1回サンプリングされます)。

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;
}
4

2 に答える 2

14

TL; DR:

  • host_statistics64()さまざまなソースから情報を取得します。これには時間がかかり、一貫性のない結果が生じる可能性があります。
  • host_statistics64()のような名前の変数によっていくつかの情報を取得しますvm_page_foo_count。ただし、これらの変数のすべてが考慮されるvm_page_stolen_countわけではありません。たとえば、考慮されません。
  • よく知られているのは、盗まれたページを有線ページの数に/usr/bin/top追加することです。これは、ページをカウントするときにこれらのページを考慮に入れる必要があることを示しています。

ノート

  • Darwinカーネルバージョン16.5.0xnu-3789.51.2〜3 / RELEASE_X86_64x86_64を使用してmacOS10.12で作業していますが、すべての動作を完全に再現できます。
  • 自分のマシンで使用しているXNUバージョンのソースコードをたくさんリンクします。それはここで見つけることができます:xnu-3789.51.2
  • あなたが書いたプログラムは基本的に(と)/usr/bin/vm_statの単なるラッパーと同じです。対応するソースコードはここにあります:system_cmds-496 / vm_stat.tproj/vm_stat.chost_statistics64()host_statistics()

XNUにどのhost_statistics64()ように適合し、どのように機能しますか?

widleyが知っているように、OS XカーネルはXNU(X NU IS N OT U NIX)と呼ばれ、 「カーネギーメロン大学で開発されたMachカーネルと、IOKitと呼ばれるドライバーを作成するためのFreeBSDおよびC++APIのコンポーネントを組み合わせたハイブリッドカーネルです。」https://github.com/opensource-apple/xnu/blob/10.12/README.md

仮想メモリ管理(VM)はMachの一部であるため、host_statistics64()ここにあります。xnu-3789.51.2 / osfmk / kern/host.cに含まれているその実装を詳しく見てみましょう。

関数のシグネチャは

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);
}
[...]

host_vm_statどちらがタイプかを取得しvm_statistics64_data_tます。これは、 xnu-3789.51.2 / osfmk / mach/vm_statistics.htypedef struct vm_statistics64で確認できるとおりです。そして、 xnu-3789.51.2 / osfmk / kern/processor_data.hで定義されたmakroからプロセッサ情報を取得します。関連する数値を合計するだけで、すべてのプロセッサをループしながら入力します。PROCESSOR_DATA()host_vm_stat

ご覧のとおり、のようなzero_fill_count、または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_count2つの合計を入力します。他の残りのデータを同じ方法で(という名前の変数を使用して)収集するか、上記で入力した統計を取得します。unsigned longvm_page_free_countvm_page_speculative_countvm_page_foo_counthost_vm_stat

1.結論私たちはさまざまなソースからデータを収集します。プロセッサ情報またはと呼ばれる変数からvm_page_foo_count。これには時間がかかり、VMが非常に高速で継続的なプロセスであるという事実が問題になる可能性があります。

すでに述べた変数を詳しく見てみましょうvm_page_foo_count。これらは、xnu-3789.51.2 / osfmk / vm/vm_page.hで次のように定義されています。

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で更新されています。たとえば、この関数はページを空きページのリストに解放します。

/*
*   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 */。盗まれたページとは何ですか?通常はページアウトされない場合でも、一部のリストからページを削除するメカニズムがあるようです。これらのメカニズムの1つは、投機的ページリスト内のページの経過時間です。xnu-3789.51.2 / osfmk / vm/vm_page.hから

* 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にあります。

VM統計を計算する際に、盗まれたページは考慮されていないと思いますhost_statistics64()

私が正しいという証拠

これを証明する最良の方法は、XNUをカスタマイズされたバージョンでhost_statistics64()手動でコンパイルすることです。私にはこれをする機会がありませんでしたが、すぐに試みます。

幸い、正しいVM統計に関心があるのは私たちだけではありません。したがって、/usr/bin/topここで完全に利用できる、よく知られている(XNUには含まれていない)実装を確認する必要があります:top-108(私はmacOS 10.12.4リリースを選択しました)。

top-108/libtop.cを見てみましょう。ここで次のことがわかります。

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;
}

tsamptop-108/libtop.hlibtop_tsamp_tで定義されている構造体であるタイプです。とりわけ、が含まれています。vm_statistics64_data_t vm_statuint64_t pages_stolen

ご覧のとおり、私たちが知っているようにstatic int libtop_tsamp_update_vm_stats(libtop_tsamp_t* tsamp)いっぱいtsamp->vm_statになります。host_statistics64()その後、チェックして、のフィールドにtsamp->pages_stolen > 0追加します。wire_counttsamp->vm_stat

2.結論のように、またはサンプルコードを使用host_statistics64()しただけでは、これらの盗まれたページの数はわかりません。/usr/bin/vm_stat

なぜ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>. */Open Radarは、Appleソフトウェアのバグ報告サイトであり、通常、コメントに記載されている形式でバグを分類します。関連するバグを見つけることができませんでした。多分それは行方不明のページについてでした。

これらの情報が少しお役に立てば幸いです。自分のマシンで最新の(そしてカスタマイズされた)バージョンのXNUをコンパイルできたら、お知らせします。多分これは興味深い洞察をもたらします。

于 2017-04-08T20:40:59.783 に答える
1

compressor_page_countミックスに追加すると、マシンのRAMの実際の量にはるかに近づくことに気づきました。

これは説明ではなく観察であり、これが適切に文書化された場所へのリンクがあると便利です。

于 2017-04-01T05:20:45.903 に答える