Память и виртуальная память
В этой главе описываются память выделения и низкоуровневые подпрограммы для изменения карт распределения памяти в ядре. Это также описывает много обычно используемых интерфейсов к системе виртуальной памяти. Это не описывает, как внести изменения в алгоритме замещения страниц или добавить дополнительные пейджеры. OS X не поддерживает внешние пейджеры, несмотря на то, что большая часть функциональности может быть достигнута другими способами, некоторые из которых покрыты на высоком уровне в этой главе. Подробные данные реализации этих интерфейсов подвержены изменениям, однако, и таким образом оставлены недокументированными.
За исключением раздела Allocating Memory в Ядре, эта глава представляет интерес, только если Вы пишете файловые системы или изменяете саму систему виртуальной памяти.
OS X обзор VM
Система VM, используемая в OS X, является потомком Маха VM, создававшийся в Университете Карнеги-Меллон в 1980-х. В большой степени фундаментальный проект является тем же, несмотря на то, что некоторые подробные данные отличаются, особенно при улучшении системы VM. Это действительно, однако, поддерживает возможность запросить определенное поведение разбивки на страницы с помощью универсальных списков страницы (UPLs). См. Универсальные Списки Страницы (UPLs) для получения дополнительной информации.
Проект Маха VM центрируется вокруг понятия физической памяти, являющейся кэшем для виртуальной памяти.
На его высшем уровне Мах VM состоит из адресных пространств и способов управлять содержанием тех адресных пространств извне пространства. Эти адресные пространства редки и имеют понятие мер защиты для ограничения, какие задачи могут получить доступ к своему содержанию.
На более низком уровне, уровне объектов, виртуальная память замечена как набор объектов VM и объектов памяти, каждого с определенным владельцем и мерами защиты. Эти объекты могут быть изменены с вызовами объектов, которые доступны и задаче и (через бэкэнд VM) к пейджерам.
Объект VM является внутренним к системе виртуальной памяти и включает основную информацию о доступе к памяти. Объект памяти, в отличие от этого, предоставлен пейджером. Содержание памяти, связанной с тем объектом памяти, может быть получено от диска или некоторого другого запоминающего устройства путем обмена сообщениями с объектом памяти. Неявно, каждый объект VM связан с данным пейджером через его объект памяти.
Объекты VM кэшируются с системными страницами (RAM), который может быть любым питанием двух многократных из аппаратного размера страницы. В ядре OS X системные страницы являются тем же размером как аппаратные страницы. Каждая системная страница представлена в данном адресном пространстве записью карты. Каждая запись карты имеет свою собственную защиту и наследование. Данная запись карты может иметь наследование shared
, copy
, или none
. Если отмечена страница shared
в данной карте дочерние задачи поделятся этой страницей для чтения и записи. Если отмечена страница copy
, дочерние задачи получают копию этой страницы (использующий копию на записи). Если отмечена страница none
, страницу дочернего элемента оставляют освобожденной.
Объектами VM управляет машинно-независимая система VM с базовым виртуальным к физическим отображениям, обработанным машинно-зависимой pmap системой. pmap
система фактически обрабатывает таблицы страниц, буфера быстрого преобразования адреса, сегменты, и т.д., в зависимости от проекта используемого оборудования.
Когда объект VM дублирован (например, страницы данных от только что вызвавшего процесса fork
), создается теневой объект. Теневой объект первоначально пуст, и содержит ссылку на другой объект. Когда содержание страницы изменяется, страница копируется с родительского объекта в теневой объект и затем изменяется. При чтении данных из страницы, если та страница существует в теневом объекте, используется страница, перечисленная в теневом объекте. Если теневой объект не имеет никакой копии той страницы, с исходным объектом консультируются. Серия теневых объектов, указывающих на теневые объекты или исходные объекты, известна как теневая цепочка.
Если объект в большой степени снова используется способом копии на записи, теневые цепочки могут стать произвольно длинными. Однако с тех пор fork
часто сопровождается exec
, который заменяет весь материал, являющийся затененными, длинными цепочками, редки. Далее, Мах автоматически собирает «мусор» теневые объекты, удаляя любые промежуточные теневые объекты, на страницы которых больше не ссылается никакой (неболее не существующий) теневой объект. Для исходного объекта даже возможно быть выпущенным, если это больше не содержит страницы, относящиеся к цепочке.
Вызовы VM, доступные приложению, включают vm_map
и vm_allocate
, который может использоваться для отображения данных файла или анонимной памяти в адресное пространство. Это возможно только потому, что адресное пространство первоначально редко. В целом приложение может или отобразить файл в свое адресное пространство (через примитивы отображения файла, абстрагированные BSD), или оно может отобразить объект (будучи переданным дескриптор тому объекту). Кроме того, задача может изменить меры защиты объектов в ее адресном пространстве и может совместно использовать те объекты с другими задачами.
В дополнение к аспектам отображения и выделения виртуальной памяти система VM содержит много других подсистем. Они включают бэкэнд (пейджеры) и подсистема общей памяти. Существуют также другие подсистемы, близко связанные к VM, включая сервер общей памяти VM. Они описаны в Другом VM и Подсистемах VM-Related.
Объясненные карты распределения памяти
Каждая задача Маха имеет свою собственную карту распределения памяти. В Махе эта карта распределения памяти принимает форму упорядоченного двунаправленного связанного списка. Как описано в OS X Обзор VM, каждый из этих объектов содержит список страниц и теневых ссылок на другие объекты.
В целом Вы никогда не должны должны быть получать доступ к карте распределения памяти непосредственно, если Вы не изменяете что-то глубоко в системе VM. vm_map_entry
структура содержит специфичную для задачи информацию об отдельном отображении вместе со ссылкой на отступающий объект. В сущности это - связующее звено между объектом VM и картой VM.
В то время как подробные данные этой структуры данных выходят за рамки этого документа, несколько полей имеют особое значение.
Поле is_submap
булево значение, говорящее, является ли эта запись карты нормальным объектом VM или подкартой. Подкарта является набором отображений, который является частью увеличенной карты. Подкарты часто используются для собирания в группу отображений в целях совместного использования их среди многократных задач Маха, но они могут использоваться во многих целях. То, что делает подкарту особенно мощной, - то, что, когда несколько задач отобразили подкарту в свое адресное пространство, они видят изменения друг друга, не только к содержанию объектов в карте, но к самим объектам. Это означает, что, поскольку дополнительные объекты добавлены к или удалены из подкарты, они появляются в или исчезают из адресных пространств всех задач та доля та подкарта.
Поле behavior
управляет ссылочным поведением разбивки на страницы указанного диапазона в данной карте. Это значение изменяется, как кластеризируются pageins. Возможные значения VM_BEHAVIOR_DEFAULT
, VM_BEHAVIOR_RANDOM
, VM_BEHAVIOR_SEQUENTIAL
, и VM_BEHAVIOR_RSEQNTL
, для значения по умолчанию, случайного, последовательного, или обратно-последовательного упорядочивания pagein.
protection
и max_protection
поля управляют полномочиями на объекте. protection
поле указывает, какие права задача в настоящее время имеет для объекта, в то время как max_protection
поле содержит максимальный доступ, который текущая задача может получить для объекта.
Вы могли бы использовать protection
поле при отладке общей памяти. Путем установки защиты, чтобы быть только для чтения, любые непреднамеренные записи к общей памяти вызвали бы исключение. Однако, когда задача фактически должна записать в ту совместно используемую область, она могла увеличить свои полномочия в protection
поле для разрешения записей.
Это была бы дыра в системе безопасности, если задача могла бы увеличиться, ее собственные полномочия на памяти возражают произвольно, как бы то ни было. Для сохранения разумной модели обеспечения безопасности задача, которой принадлежит объект памяти, должна быть в состоянии ограничить права, предоставленные подчиненной задачей. Поэтому задаче не позволяют увеличить ее защиту вне разрешений, данных в max_protection
.
Возможные значения для protection
и max_protection
описаны подробно в xnu/osfmk/mach/vm_prot.h
.
Наконец, use_pmap
поле указывает, должны ли низкоуровневые отображения подкарты быть совместно использованы среди всех задач, в которые отображается подкарта. Если отображения не совместно используются, то структура карты совместно используется среди всех задач, но фактическое содержание страниц не.
Например, совместно используемые библиотеки обрабатываются с двумя подкартами. Совместно используемый раздел кода только для чтения имеет use_pmap
набор к истине. Чтение-запись (несовместно использованный) раздел имеет use_pmap
набор ко лжи, вызывая чистую копию библиотеки DATA
сегмент, который будет отображен в от диска для каждой новой задачи.
Названные записи
OS X система VM обеспечивает абстракцию, известную как именованная запись. Именованная запись является не чем иным как дескриптором к общему объекту или подкарте.
Поддержка общей памяти в OS X достигается путем совместного использования объектов между картами распределения памяти различных задач. Объекты общей памяти должны быть созданы из существующих объектов VM путем вызова vm_allocate
выделить память в Вашем адресном пространстве и затем вызове mach_make_memory_entry_64
получить дескриптор к базовому объекту VM.
Дескриптор, возвращенный mach_make_memory_entry_64
может быть передан vm_map
отобразить тот объект в адресное пространство данной задачи. Дескриптор может также быть передан через IPC или другие средние значения к другим задачам так, чтобы они могли отобразить его в свои адресные пространства. Это предоставляет возможность для совместного использования объектов с задачами, которые не находятся в прямом происхождении, и также позволяет Вам совместно использовать дополнительную память с задачами в Вашем прямом происхождении после того, как создаются те задачи.
Другая форма именованной записи, подкарты, используется для группировки ряда отображений. Наиболее популярный способ использования подкарты должен совместно использовать отображения среди многократных задач Маха. Подкарта может быть создана с vm_region_object_create
.
То, что делает подкарту особенно мощной, - то, что, когда несколько задач отобразили подкарту в свое адресное пространство, они видят изменения друг друга и в данных и в структуре карты. Это означает, что одна задача может отобразить или не отобразить объект VM в адресном пространстве другой задачи просто путем отображения или неотображения того объекта в подкарте.
Универсальные списки страницы (UPLs)
Универсальный список страницы или UPL, является структурой данных, используемой при передаче с системой виртуальной памяти. UPLs может использоваться для изменения поведения страниц относительно кэширования, полномочий, отображения, и т.д. UPLs может также использоваться для продвижения данных в и данных получения по запросу от объектов VM. Термин также часто используется, чтобы относиться к семье подпрограмм, воздействующих на UPLs. Флаги, используемые при контакте с UPLs, описаны в osfmk/mach/memory_object_types.h
.
Жизненный цикл UPL похож на это:
UPL создается на основе содержания объекта VM. Этот UPL включает информацию о страницах в том объекте.
Это UPL изменяется в некотором роде.
Изменения в UPL или фиксируются (пододвинутый обратно системе VM) или прервались, с
ubc_upl_commit
илиubc_upl_abort
, соответственно.
Если у Вас есть дескриптор управления для данного объекта VM (который обычно означает, что Вы в пейджере), можно использовать vm_object_upl_request
получить UPL для того объекта. Иначе, необходимо использовать vm_map_get_upl
вызвать. В любом случае Вас оставляют с дескриптором к UPL.
Когда pagein требуют, пейджер получает список страниц, заблокированных против объекта с определенным набором страниц к не допустимый. Пейджер должен или записать данные в те страницы или должен прервать транзакцию для предотвращения недопустимых данных в ядре. Так же в pageout, ядро должно записать данные в запоминающее устройство или прервать транзакцию для предотвращения потери данных. Пейджер может также выбрать загружать дополнительные страницы в память или бросать дополнительные страницы из памяти по его усмотрению.
Поскольку пейджеры могут использоваться и для виртуальной памяти и для отображения памяти данных файла, когда pageout требуют, данные, возможно, должны быть освобождены из памяти, или может быть желательно сохранить его там и просто сбросить изменения в диске. Поэтому флаг UPL_CLEAN_IN_PLACE
существует, чтобы позволить странице быть сброшенной к диску, но не удаленной из памяти.
Когда пейджер решает разбить на страницы в или дополнительные страницы, он должен определить который страницы переместиться. Пейджер может запросить все грязные страницы путем установки RETURN_ONLY_DIRTY
флаг. Это может также запросить все страницы, которые не находятся в памяти с помощью RETURN_ONLY_ABSENT
флаг.
Существует небольшая проблема, как бы то ни было. Если данная страница отмечена как BUSY
в UPL запрос информации на той странице обычно блокировал бы. Если пейджер делает упреждающую выборку или предварительное сбрасывание, это не желательно, так как это могло бы блокировать на себе или на некотором другом пейджере, блокирующемся, ожидая текущей операции для завершения. Для предотвращения такой мертвой блокировки механизм UPL обеспечивает UPL_NOBLOCK
флаг. Это часто используется в анонимном пейджере для запроса свободной памяти.
Флаг QUERY_OBJECT_TYPE
может использоваться, чтобы определить, физически непрерывен ли объект и получить другие свойства основного объекта.
Флаг UPL_PRECIOUS
средние значения, что должна быть только одна копия данных. Это предотвращает наличие копии и в памяти и в запоминающем устройстве. Однако это повреждает смежность смежных страниц в запоминающем устройстве и таким образом обычно не используется для предотвращения хита производительности.
Флаг SET_INTERNAL
используется подсистемой BSD, чтобы заставить всю информацию о UPL содержаться в единственном объекте памяти так, чтобы это могло быть роздано более легко. Если Ваш код работает в адресном пространстве ядра, это может только использоваться.
Так как этот дескриптор может использоваться для многократных маленьких транзакций (например, при отображении файла в блок памяти блоком), API UPL включает функции для фиксации и прерывания изменений в только части UPL. Эти функции upl_commit_range
и upl_abort_range
, соответственно.
Помочь в использовании UPLs для обработки многослойных транзакций, upl_commit_range
и upl_abort_range
вызовы имеют флаг, заставляющий UPL быть освобожденным, когда нет никаких неизмененных страниц в UPL. При использовании этого флага необходимо очень бояться использовать UPL после того, как все диапазоны фиксировались или прерывались.
Наконец, функция vm_map_get_upl
часто используется в файловых системах. Это получает базовый объект VM, связанный с данным диапазоном в адресном пространстве. Так как это возвращает только первый объект в том диапазоне, это - Ваша ответственность определить, покрыт ли весь диапазон получающимся UPL и, в противном случае для совершения дополнительных вызовов для получения UPLs для других объектов. Обратите внимание на то, что, в то время как vm_map_get_upl
вызов против диапазона адресного пространства, большинство вызовов UPL против a vm_object
.
Используя карты распределения памяти Маха
От контекста ядра (не от KEXT), существует две карты, с которыми необходимо будет, вероятно, иметь дело. Первой является карта ядра. Так как Ваш код выполняется в адресном пространстве ядра, никакое дополнительное усилие не необходимо для использования памяти, на которую ссылаются в карте ядра. Однако Вы, возможно, должны добавить, что дополнительные отображения в ядро отображают и удаляют их, когда они больше не необходимы.
Вторая карта интереса является картой распределения памяти для данной задачи. Это представляет большую часть интереса для кода, принимающего ввод из пользовательских программ, например a sysctl
или Мах обработчик RPC. В почти всех случаях удобные обертки обеспечивают необходимую функциональность, как бы то ни было.
Большинство этих функций базируется вокруг vm_offset_t
введите, который является целым числом размера указателя. В действительности можно думать о них как об указателях с протестом, что они - не обязательно указатели на данные в адресном пространстве ядра, в зависимости от использования.
Низкоуровневые VM отображаются, API включает следующие функции:
kern_return_t vm_map_copyin(vm_map_t src_map, vm_offset_t src_addr, |
vm_size_t len, boolean_t src_destroy, |
vm_map_copy_t *copy_result); |
kern_return_t vm_map_copyout(vm_map_t map, vm_offset_t *addr, /* Out */ |
register vm_map_copy_t copy); |
kern_return_t vm_map_copy_overwrite(vm_map_t dst_map, |
vm_offset_t dst_address,vm_map_copy_t copy, |
boolean_t interruptible, pmap_t pmap); |
void vm_map_copy_discard(vm_map_copy_t copy); |
void vm_map_wire(vm_map_t map, vm_offset_t start, vm_offset_t end, |
vm_prot_t access_type, boolean_t user_wire); |
void vm_map_unwire(vm_map_t map, vm_offset_t start, vm_offset_t end, |
boolean_t user_wire); |
Функция vm_map_copyin
данные копий от произвольного (потенциально неядро) карта распределения памяти в список копии и возвраты указатель списка копии в copy_result
. Если что-то идет не так, как надо, и необходимо выбросить этот промежуточный объект, он должен быть освобожден с vm_map_copy_discard
.
Для фактического получения данных от списка копии необходимо перезаписать объект памяти в адресном пространстве ядра с vm_map_copy_overwrite
. Это перезаписывает объект с содержанием списка копии. В большинстве целей значение передало для interruptible
должен быть FALSE
, и pmap
должен быть NULL
.
Копирование данных от ядра до пространства пользователя является точно тем же как копированием данных от пространства пользователя, за исключением того, что Вы передаете kernel_map
к vm_map_copyin
и передайте пользовательскую карту vm_map_copy_overwrite
. В целом, однако, необходимо избежать делать это, так как Вы могли закончить с памятью задачи, фрагментируемой в большое количество крошечных объектов, которое является нежелательным.
Не использовать vm_map_copyout
при копировании данных в существующую пользовательскую таблицу адресов задачи. Функция vm_map_copyout
используется для заполнения неиспользованной области в таблице адресов. Если область выделяется, то vm_map_copyout
ничего не делает. Поскольку это требует знания текущего состояния карты, это прежде всего используется при создании новой таблицы адресов (например, при ручном создании нового процесса). В большинстве целей Вы не должны использовать vm_map_copyout
.
Функции vm_map_wire
и vm_map_unwire
может использоваться, чтобы соединить проводом и не соединить части проводом таблицы адресов. Если Вы устанавливаете параметр user_wire
к TRUE
, тогда страница может быть неподключенной от пространства пользователя. Это должно быть установлено в FALSE
если Вы собираетесь использовать память для I/O или для некоторой другой работы, которая не может терпеть разбивку на страницы. В vm_map_wire
, параметр access_type
указывает типы доступов, которые не должны быть предоставлены для генерации отсутствия страницы. В целом, однако, необходимо использовать vm_wire
соединять память проводом.
Как отмечалось ранее, эта информация представлена строго для использования в основе ядра. Вы ничего не можете использовать в этом разделе от расширения ядра.
Другой VM и подсистемы VM-Related
Существует две дополнительных подсистемы VM: пейджеры и подсистема обнаружения рабочего набора. Кроме того, подсистема сервера общей памяти VM близко связывается к (но не часть), подсистема VM. В этом разделе описываются эти три VM и подсистемы VM-related.
Пейджеры
OS X имеет три основных пейджера: vnode пейджер, пейджер по умолчанию (или анонимный пейджер) и пейджер устройства. Они используются системой VM для фактического получения данных в объекты VM, лежащие в основе именованных записей. Пейджеры соединяются в систему VM через комбинацию подмножества старого интерфейса пейджера Маха и UPLs.
Пейджер по умолчанию - то, о чем думает большинство людей, когда они думают о системе VM. Это ответственно за перемещение нормальных данных в и из запоминающего устройства. Кроме того, существует средство, известное как динамический пейджер, находящийся поверх пейджера по умолчанию и обрабатывающий создание и удаление файлов запоминающего устройства. Эти файлы пейджера заполнены данными в кластерах (группы страниц).
Когда общее обилие пула страничного файла достигает высшей точки, пейджер по умолчанию просит, чтобы динамический пейджер выделил новый файл хранилища. Когда пул опускается ниже его низкого водяного знака, система VM выбирает файл пейджера, перемещает его содержание в другие файлы пейджера и удаляет его из диска.
vnode пейджер имеет 1:1 (на) отображение между объектами в пространстве VM и открытых файлах (vnodes). Это используется для файлового ввода-вывода с отображенной памятью. vnode пейджер обычно скрыт позади вызовов к файлу BSD APIs.
Пейджер устройства позволяет Вам отображать память необщего назначения с характеристиками кэша, требуемыми для той памяти (WIMG). Память необщего назначения включает физические адреса, отображающиеся на аппаратные средства кроме оперативной памяти — например, память PCI, память кадрового буфера, и т.д. Пейджер устройства обычно скрыт позади вызовов к различным функциям Набора I/O.
Подсистема обнаружения рабочего набора
Для улучшения производительности OS X знали подсистему как подсистему обнаружения рабочего набора. Эту подсистему вызывают на отказе VM; это сохраняет профиль поведения отказа каждой задачи со времени ее начала. Кроме того, как раз перед запросом страницы, код отказа спрашивает эту подсистему, в которую смежные страницы должны быть введены, и затем выполняют единственный большой запрос к пейджеру.
Так как файлы на диске имеют тенденцию иметь довольно хорошую местность, и так как местность адресного пространства в основном сохраняется в запоминающем устройстве, это обеспечивает повышение исполнения всех условий. Кроме того, так как это основано на предыдущем поведении приложения, это имеет тенденцию вытягивать на страницах, которые, вероятно, иначе были бы необходимы позже. Это происходит для всех пейджеров.
Код рабочего набора работает хорошо, как только он установлен. Однако без справки, ее производительность была бы базовой производительностью, пока не был разработан профиль для данного приложения. Для преодоления этого первый раз, когда приложение запускается в данном пользовательском контексте, начальный рабочий набор, требуемый запускать приложение, получен и сохранен в файле. С тех пор, когда приложение запущено, тот файл используется для отбора рабочего набора.
Эти файлы рабочего набора установлены на основе в расчете на пользователя. Они сохранены в /var/vm/app_profile
и только доступны суперпользователем (и ядро).
Подсистема сервера общей памяти VM
Подсистема сервера общей памяти VM является службой BSD, которая близко связывается к VM, но не является частью VM. Этот сервер обеспечивает две подкарты, использующиеся для совместно используемой поддержки библиотеки в OS X. Поскольку совместно используемые библиотеки содержат и части только для чтения (сегмент текста) и (сегмент данных) частей чтения-записи, две части обрабатываются отдельно для максимизации эффективности. Части только для чтения полностью совместно используются задачами, включая базовое pmap
записи. Части чтения-записи совместно используют общую подкарту, но имеют различные базовые объекты данных (достигнутый через копию на записи).
Три функции, экспортируемые подсистемой сервера общей памяти VM, должны только быть вызваны dyld
. Не используйте их в своих программах.
Функция load_shared_file
используется для загрузки новой совместно используемой библиотеки в систему. Как только такой файл загружается, другие задачи могут тогда зависеть от него, таким образом, совместно используемая библиотека не может быть не разделена. Однако новый набор совместно используемых областей может быть создан с new_system_shared_regions
так, чтобы никакие новые задачи не пользовались старыми библиотеками.
Функция reset_shared_file
может использоваться для сброса любых изменений, которые задача, возможно, внесла в ее частную копию раздела данных для файла.
Наконец, функция new_system_shared_regions
может использоваться для создания нового набора совместно используемых областей для будущих задач. Новые области могут использоваться при обновлении предварительно связывающий с новыми совместно используемыми библиотеками для порождения новых задач видеть последние библиотеки в их новых расположениях в памяти. (Пользователи старых совместно используемых библиотек будут все еще работать, но они упадут с предсвязанного пути и выполнят менее эффективно.) Это может также использоваться при контакте с частными библиотеками, которые Вы хотите совместно использовать только с потомками Вашей задачи.
Адресные пространства
Этот раздел объясняет проблемы, которые некоторые разработчики могут видеть при использовании их драйверов у Пантеры или позже. Эти изменения требовались комбинацией аппаратных средств и базовых изменений OS; однако, можно видеть проблемы следовать из изменений даже на существующих аппаратных средствах.
Существует три основных области изменения в OS X v10.3. Это:
IOMemoryDescriptor
измененияСистема VM (
pmap
) измененияИзменения зависимости от ядра
Они описаны подробно в следующих разделах.
Вводная информация на преобразовании адресов PCI
Чтобы позволить существующим драйверам устройств работать с предстоящими 64-разрядными архитектурами системы, много изменений требовались. Для объяснения их в кратком представлении в мосты шины PCI нуждаются.
То, когда устройство PCI должно выполнить транзакцию данных к или от оперативной памяти, драйвер устройства вызывает серию функций, намеревалось подготовить эту память к I/O. В архитектуре, где и драйверы устройств и подсистема памяти используют 32-разрядное обращение, все просто работает, пока память не становится разбитой на страницы во время операции I/O. Поскольку память ядра обычно не листаема, подготовка является в основном лишней.
В системе, подсистема памяти которой использует 64-разрядное обращение, однако, это становится чем-то вроде проблемы. Поскольку устройства на шине PCI могут только обработать 32-разрядные адреса, устройство может только «видеть» апертуру на 4 гигабайта в (потенциально намного больше) оперативная память в любой момент времени.
Существует два возможных решения для этой проблемы. Простое (но медленный) решение должно было бы использовать “буферы возврата”. В таком проекте драйверы устройств скопировали бы данные в память, в частности выделенную в нижней части 4 концерта памяти. Однако это подвергается потере производительности и также помещает дополнительные ограничения на более низкие 4 концерта памяти, вызывая многочисленные проблемы для системы VM.
Другое решение, один выбранный в 64-разрядной реализации Apple, состоит в том, чтобы использовать преобразование адресов для «отображения» блоков памяти в 32-разрядное адресное пространство устройств PCI. В то время как устройство PCI может все еще только видеть апертуру на 4 ГБ, та апертура может тогда быть состоящей из нескольких несмежных участков, и таким образом возвратить буферы, и другие ограничения являются ненужными. Это преобразование адресов сделано с помощью части контроллера памяти, известного как DART, обозначающий Таблицу Разрешения Адреса устройства.
Это представляет много потенциальных проблем, как бы то ни было. Во-первых, физические адреса, как замечено процессором больше карта 1:1 на адреса, как замечено устройствами PCI. Таким образом новый термин, адреса I/O, вводится для описания этого нового представления. Поскольку адреса I/O и физические адреса больше не являются тем же, DART должен сохранить таблицу переводов для использования при отображении между ними. К счастью, если Ваш драйвер записан согласно инструкциям Apple (использующий, только задокументировал APIs), этот процесс обрабатывается прозрачно.
IOMemoryDescriptor
Изменения
Когда Ваш драйвер вызывает IOMemoryDescriptor
::
prepare
, отображение автоматически введено в DART. Когда это вызывает IOMemoryDescriptor
::
release
, отображение удалено. Если Вам не удается сделать это, Ваш драйвер мог бы испытать случайное повреждение данных или панику.
Поскольку DART требует различного кэширования для чтений и записей, направление DMA важно на аппаратных средствах, включающих DART. В то время как можно получить внезапные отказы, если направление будет неправильным в целом (в любой системе), при попытке вызвать WriteBytes на области памяти, направление DMA которой устанавливается для чтения, то Вы вызовете панику ядра на 64-разрядных аппаратных средствах.
Если бы Вы пытаетесь выполнить транзакцию DMA к неподключенному (пользователь) память в предыдущих системах, Вы только получили бы случайные катастрофические отказы, панику и повреждение данных. На машинах с DART Вы, вероятно, не получите данных вообще.
Как побочный эффект изменений в подсистеме памяти, OS X, намного более вероятно, возвратит физически непрерывные диапазоны страницы в областях памяти. Исторически, OS X возвратил многостраничные области памяти в обратном порядке, начиная с последней страницы и движения первой страницы. Результат этого состоял в том, что многостраничные области памяти по существу никогда не имели непрерывный диапазон физических страниц.
Из-за увеличенной вероятности наблюдения физически непрерывных блоков памяти в области памяти это изменение может представить скрытые ошибки в некоторых драйверах, только обнаруживающихся при обработке непрерывных диапазонов физических страниц, которые могли привести к неправильному поведению или панике.
Обратите внимание на то, что упомянутые выше проблемы вызываются ошибками в драйверах и могли привести к проблемам на более старых аппаратных средствах до Пантеры. Эти проблемы, более вероятно, произойдут у Пантеры и более поздних версий OS X, однако, из-за новых аппаратных проектов и изменений OS, внесенных для поддержки тех проектов.
Система VM и pmap
Изменения:
У Пантеры, в результате изменений, описанных подробно в разделе по преобразованию адресов PCI, физические адреса, полученные непосредственно из pmap
уровень не имеет никакой полезной цели вне самой системы VM. Предотвратить их непреднамеренное использование в драйверах устройств, pmap
вызовы больше не доступны от расширений ядра.
Несколько драйверов, записанных до добавления IOMemoryDescriptor
класс все еще использует pmap
вызовы для связывания физических страниц с виртуальным адресом. Кроме того, несколько разработчиков посмотрели на IOMemoryDescriptor
реализация и выбранный для получения адресов непосредственно из pmap
уровень для удаления, что было воспринято как ненужный уровень абстракции.
Даже не удаляя доступ к pmap
вызовы, эти драйверы не функционировали бы в системах с DART (см. раздел PCI выше для получения информации о DARTs). Для лучше подчеркивания этого предстоящего отказа Пантера заставит этим драйверам не удаваться загрузиться с неопределенной ошибкой символа (обычно для pmap_extract
) даже в системах без DART.
Изменения зависимости от ядра
Начинаясь у Пантеры, драйверы устройств, объявляющие зависимость от версии 7 (версия Пантеры) Набора I/O, больше не будут автоматически получать символы от Маха и BSD. Это изменение было внесено, чтобы отговорить разработчиков Набора I/O полагаться на символы, явно не утвержденные для использования в Наборе I/O.
Существующие драйверы незатронуты этим изменением. Это изменение только влияет на Вас при явном изменении драйвера устройства, чтобы объявить, что зависимость от версии 7 Набора I/O использует в своих интересах новые функции I/O Kit.
Сводка
Как описано выше, некоторые драйверы устройств могут потребовать, чтобы незначительные модификации поддерживали Пантеру и выше. Apple приложил все усилия для обеспечения совместимости существующими драйверами устройств до самой большой возможной степени, но могут повредиться несколько драйверов. Если Ваш драйвер повреждается, необходимо сначала проверить, чтобы видеть, включает ли драйвер какую-либо из ошибок, описанных в предыдущих разделах. Если это не делает, свяжитесь с Технической поддержкой Разработчика Apple для дополнительных предложений отладки.
Выделение памяти в ядре
Как с большинством вещей в ядре OS X, существует много способов выделить память. Выбор подпрограмм зависит и от расположения вызывающей подпрограммы и на причине выделения памяти. В целом необходимо использовать подпрограммы Маха для выделения памяти, если Вы не пишете код для использования в Наборе I/O, когда необходимо использовать подпрограммы Набора I/O.
Выделение Памяти От Расширения ядра «Не Набор I/O»
<libkern/OSMalloc.h>
заголовок определяет следующие подпрограммы для выделения памяти ядра:
OSMalloc
— выделяет блок памяти.OSMalloc_noblock
— если запрос блокировал бы, выделяет блок памяти, но сразу возвращает NULL.OSMalloc_nowait
— то же какOSMalloc_noblock
.OSFree
— память выпусков, выделенная с любым изOSMalloc
варианты.OSMalloc_Tagalloc
— позволяет Вам создавать уникальный тег для своих выделений памяти. Выmust
создайте по крайней мере один тег, прежде чем можно будет использовать любой изOSMalloc
функции.OSMalloc_Tagfree
— выпускает тег, выделенный с OSMalloc_Tagalloc. (Необходимо выпустить все выделения, связанные с тем тегом, прежде чем Вы вызовете эту функцию.)
Например, чтобы выделить и освободить страницу соединенной проводом памяти, Вы могли бы записать код как это:
#include <libkern/OSMalloc.h> |
#define MYTAGNAME "com.apple.mytag" |
... |
OSMallocTag mytag = OSMalloc_Tagalloc(MYTAGNAME, OSMT_DEFAULT); |
void *datablock = OSMalloc(PAGE_SIZE_64, mytag); |
... |
OSFree(datablock, PAGE_SIZE_64, mytag); |
Выделить страницу листаемой памяти, передачи OSMT_PAGEABLE
вместо OSMT_DEFAULT
в Вашем вызове к OSMalloc_Tagalloc
.
Выделение памяти от набора I/O
Несмотря на то, что Набор I/O обычно выходит за рамки этого документа, подпрограммы управления памятью Набора I/O представлены здесь для полноты. В целом подпрограммы Набора I/O не должны использоваться вне Набора I/O. Точно так же подпрограммы распределения Маха не должны непосредственно использоваться от Набора I/O, потому что Набор I/O имеет абстракции для тех подпрограмм, соответствующих модели разработки Набора I/O более близко.
Набор I/O включает следующие подпрограммы для выделения памяти ядра:
void *IOMalloc(vm_size_t size); |
void *IOMallocAligned(vm_size_t size, vm_size_t alignment); |
void *IOMallocContiguous(vm_size_t size, vm_size_t alignment, |
IOPhysicalAddress *physicalAddress); |
void *IOMallocPageable(vm_size_t size, vm_size_t alignment); |
void IOFree(void *address, vm_size_t size); |
void IOFreeAligned(void *address, vm_size_t size); |
void IOFreeContiguous(void *address, vm_size_t size); |
void IOFreePageable(void *address, vm_size_t size); |
Большинство этих подпрограмм является относительно прозрачными обертками вокруг функций выделения Маха. Существует два существенных различия, как бы то ни было. Во-первых, вызывающая сторона не должна знать, какая карта распределения памяти изменяется. Во-вторых, у них есть отдельный бесплатный вызов для каждого требования выделения внутренних бухгалтерских причин.
Функции IOMallocContiguous
и IOMallocAligned
отличайтесь несколько от их основ Маха. IOMallocAligned
использование вызывает непосредственно Маху VM, чтобы добавить поддержку произвольного (питание 2) выравнивание данных, вместо того, чтобы выровняться на основе размера объекта. IOMallocContiguous
добавляет дополнительный параметр, PhysicalAddress
. Если этот указатель не NULL
, физический адрес возвращается через этот указатель. Используя функции Маха, получая физический адрес требует отдельного вызова функции.
Выделение памяти в самом ядре
В дополнение к подпрограммам, доступным расширениям ядра, существует много других функций, которые можно вызвать для выделения памяти при изменении самого ядра Маха. Подпрограммы Маха обеспечивают относительно прямой интерфейс для выделения и выпуска памяти. Они - предпочтительный механизм для выделения памяти за пределами Набора I/O. BSD также предлагает _MALLOC
и _FREE
, который может использоваться в частях BSD ядра.
Эти подпрограммы не предусматривают принудительное отображение данного физического адреса к виртуальному адресу. Однако при необходимости в таком отображении Вы, вероятно, пишете драйвер устройства, когда необходимо использовать подпрограммы Набора I/O вместо подпрограмм Маха.
Большинство этих функций базируется вокруг vm_offset_t
введите, который является целым числом размера указателя. В действительности можно думать о них как об указателях с протестом, что они - не обязательно указатели на данные в адресном пространстве ядра, в зависимости от использования.
Это некоторые обычно используемые подпрограммы Маха для выделения памяти:
kern_return_t kmem_alloc(vm_map_t map, vm_offset_t *addrp, vm_size_t size); |
void kmem_free(vm_map_t map, vm_offset_t addr, vm_size_t size); |
kern_return_t mem_alloc_aligned(vm_map_t map, vm_offset_t *addrp, |
vm_size_t size); |
kern_return_t kmem_alloc_wired(vm_map_t map, vm_offset_t *addrp, |
vm_size_t size); |
kern_return_t kmem_alloc_pageable(vm_map_t map, vm_offset_t *addrp, |
vm_size_t size); |
kern_return_t kmem_alloc_contig(vm_map_t map, vm_offset_t *addrp, |
vm_size_t size, vm_offset_t mask, int flags); |
Эти функции все берут карту в качестве первого параметра. Если Вы не должны выделять память в различной карте, необходимо передать kernel_map
для этого параметра.
Весь из kmem_alloc
функции кроме kmem_alloc_pageable
выделите соединенную проводом память. Функция kmem_alloc_pageable
создает надлежащие структуры VM, но не поддерживает область физической памятью. Эта функция могла быть объединена с vm_map_copyout
при создании новой таблицы адресов, например. На практике это редко используется.
Функция kmem_alloc_aligned
выделяет память, выровненную согласно значению size
параметр, который должен быть питанием 2.
Функция kmem_alloc_wired
синонимично с kmem_alloc
и является подходящим для структур данных, которые не могут быть разбиты на страницы. Это не строго необходимо; однако, если Вам явно нужны определенные части данных, которые будут соединены проводом, с помощью kmem_alloc_wired
упрощает находить те части Вашего кода.
Функция kmem_alloc_contig
попытки выделить блок физически непрерывной памяти. Это не всегда возможно, и требует полного вида системы бесплатный список даже для коротких выделений. После запуска этот вид может вызвать длинные задержки, особенно в системах с большим количеством RAM. Вы не должны обычно использовать эту функцию.
Функция kmem_free
используется для освобождения объекта, выделенного с одним из kmem_alloc
функции. В отличие от стандарта C free
функция, kmem_free
требует длины объекта. Если Вы не выделяете объекты фиксированного размера (например, sizeof struct foo
), Вам, вероятно, придется сделать некоторый дополнительный бухгалтерский учет, так как необходимо освободить весь объект, не только часть одной.