Почему host statistics64 () возвращает несогласованные результаты?
почему host_statistics64 () в OS X 10.6.8 (я не знаю, есть ли у других версий эта проблема) возвращает количество свободной, активной, неактивной и проводной памяти, которая не складывается с общим объемом ОЗУ? И почему в нем отсутствует непоследовательное количество страниц?
следующий вывод представляет количество страниц, не классифицированных как свободные, активные, неактивные или проводные в течение десяти секунд (выборка примерно один раз в секунду).
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("oopsn");
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("%in", missing);
sleep(1);
}
return 0;
}
2 ответов
TL; DR:
-
host_statistics64()
получить информацию из разных источников, которая может стоить времени и может привести к противоречивым результатам. -
host_statistics64()
получает некоторую информацию по переменным с именами, такими какvm_page_foo_count
. Но не все эти переменные учитываются, например,vm_page_stolen_count
нет. - известный
/usr/bin/top
добавляет украденные страницы в количестве проводной страницы. Это показатель того, что эти страницы следует учитывать при подсчете страниц.
Примечания
- я работаю над macOS 10.12 с ядро Дарвина версии 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НУ NOT UNIX) и " является гибридным ядром объединение ядра 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. И мы получаем информацию о процессоре от макро 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-очень быстрый и непрерывный процесс.
давайте более подробно рассмотрим уже упомянутые переменные 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.
я думаю, что украденные страницы не учитываются при расчете статистики VM a host_statistics64()
делает.
доказательство того, что я прав
лучший способ доказать это - скомпилировать XNU с настроенной версией host_statistics64()
вручную. У меня не было возможности сделать это, но я постараюсь в ближайшее время.
к счастью, мы не единственные, кто заинтересован в правильной статистике VM. Поэтому мы должны посмотреть на реализацию well know /usr/bin/top
(не содержится в XNU), который полностью доступен здесь:топ-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
и добавляет его к
просто заметил, что если добавить compressor_page_count
в смесь вы получаете гораздо ближе к фактическому количеству ОЗУ в машине.
Это наблюдение, а не объяснение, и ссылки на то, где это было должным образом документировано, было бы неплохо иметь!