2013-02-09 2 views
14

Почему host_statistics64() в OS X 10.6.8 (я не знаю, есть ли в других версиях эта проблема) возвращает количество отсчетов для свободной, активной, неактивной и проводной памяти, которые не суммируются с общим количеством бара ? И почему у него отсутствует непоследовательное количество страниц?Почему host_statistics64() возвращает непоследовательные результаты?

Следующий результат представляет количество страниц, не классифицированных как свободные, активные, неактивные или проводные в течение десяти секунд (выборочно грубо один раз в секунду).

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

'vm_stat', в котором используется тот же код, имеет ту же проблему, что и не совсем сложенная. Таким образом, это не проблема в вашем коде как таковой. – nneonneo

+0

С ОЗУ и ОЗУ 8 ГБ недостающая часть больше (около 2400). – patrix

+0

И посмотрите на http://opensource.apple.com/source/top/top-73/libtop.c, там, кажется, есть некоторые специальные вычисления. – patrix

ответ

6

TL; DR:

  • host_statistics64() получить информацию из различных источников, которые могут стоить времени и может привести к непредсказуемым результатам.
  • host_statistics64() получает некоторую информацию по переменным с именами, такими как vm_page_foo_count. Но не все эти переменные учитываются, например. vm_page_stolen_count нет.
  • Хорошо известное /usr/bin/top добавляет украденные страницы к числу проводные страницы. Это показатель того, что эти страницы следует учитывать при подсчете страниц.

Примечания

  • я работаю на MacOS 10.12 с Darwin Kernel Version 16.5.0 XNU-3789.51.2 ~ 3/RELEASE_X86_64 x86_64 но все поведение укомплектовать воспроизводимость.
  • Я собираюсь связать много исходного кода версии XNU, которую я использую на своей машине. Его можно найти здесь: xnu-3789.51.2.
  • Программа, которую вы написали, в основном такая же, как /usr/bin/vm_stat, которая является только оберткой для host_statistics64()host_statistics()). Исходный код corressponding можно найти здесь: system_cmds-496/vm_stat.tproj/vm_stat.c.

Как host_statistics64() вписывается в XNU и как он работает?

Как widley знаете OS X ядро ​​называется XNU (X НУ N OT U NIX) и «представляет собой гибрид ядро ​​объединения ядра Mach, разработанное в университете Карнеги-Меллон с компонентами из FreeBSD и C++ API для написания драйверов с именем IOKit." (https://github.com/opensource-apple/xnu/blob/10.12/README.md)

Управление виртуальной памяти (VM) является частью Маха поэтому 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. Это всего лишь typedef struct vm_statistics64, как вы можете видеть в xnu-3789.51.2/osfmk/mach/vm_statistics.h. И мы получаем информацию о процессоре от makro PROCESSOR_DATA(), определенного в xnu-3789.51.2/osfmk/kern/processor_data.h. Мы заполняем 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_count суммой двух unsigned long под названием vm_page_free_count и 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 следующим образом:

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 */. Что такое украденные страницы? Похоже, что есть механизмы вывести страницу из некоторых списков, даже если ее обычно не выгружают. Одним из таких механизмов является возраст страницы в списке спекулятивных страниц. 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.

Я думаю, что украденные страницы не учитываются при расчете статистики ВМ host_statistics64().

Доказательство того, что я прав

Лучший способ доказать, что это было бы составить XNU с настроенной версией host_statistics64() вручную. У меня не было возможности сделать это, но попробую в ближайшее время.

К счастью, мы не единственные, кто интересуется правильной статистикой VM. Поэтому мы должны взглянуть на реализацию хорошо известного /usr/bin/top (не содержащегося в XNU), который полностью доступен здесь: top-108 (я только что выбрал macOS 10.12.4 release).

Давайте посмотрим на 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; 
} 

tsamp имеет тип libtop_tsamp_t, который является структура определена в top-108/libtop.h. Он содержит, среди прочего, vm_statistics64_data_t vm_stat и uint64_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 на моей машине, я дам вам знать. Возможно, это приносит интересную информацию.

1

Просто заметил, что если добавить compressor_page_count в смесь вы получите гораздо ближе к фактическому объему оперативной памяти в машине.

Это замечание, а не объяснение, и ссылки на то, где это было правильно задокументировано, было бы неплохо иметь!

+0

Сжатые страницы вышли в 10.9, как говорится в этом вопросе, это было 10.6. Я обновил код для 10.9, но по-прежнему не хватает примерно такого же количества страниц под 10.9 (с добавленным полем), как это было в 10.6. –

Смежные вопросы