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_count
tsamp->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 на моей машине, я дам вам знать. Возможно, это приносит интересную информацию.
'vm_stat', в котором используется тот же код, имеет ту же проблему, что и не совсем сложенная. Таким образом, это не проблема в вашем коде как таковой. – nneonneo
С ОЗУ и ОЗУ 8 ГБ недостающая часть больше (около 2400). – patrix
И посмотрите на http://opensource.apple.com/source/top/top-73/libtop.c, там, кажется, есть некоторые специальные вычисления. – patrix