Второе издание
Скачать 3.09 Mb.
|
Таблица 11.2. Низкоуровневые средства выделения памяти Функция Описание a l l o c _ p a g e (gfp_mask) Выделяет одну страницу памяти и возвращает указа- тель на соответствующую ей структуру page alloc_pages (gfp_mask, order) Выделяет 2 o r d e r страниц памяти и возвращает указа- тель на структуру page первой страницы _ _ g e t _ f r e e _ p a g e (gfp_mask) Выделяет одну страницу памяти и возвращает указа- тель на ее логический адрес __get_free_pages (gfp_mask, order) Выделяет 2 o r d e r страниц памяти и возвращает указа- тель на логический адрес первой страницы get_zeroed_page (gfp_mask) Выделяет одну страницу памяти, обнуляет ее содержи- мое и возвращает указатель на ее логический адрес Значение GFP_KERNEL, которое передается в качестве параметра, — это пример флага gfp_mask, который скоро будет рассмотрен детально. Обратите внимание на проверку ошибок после вызова функции __get_free_ pages () . Выделение памяти в ядре может быть неудачным, поэтому код должен проверить и при необходимости обработать соответствующую ошибку. Это может означать, что придется пересмотреть все операции, которые были до этого сделаны. В связи с этим, часто имеет смысл выделять память в самом начале подпрограммы, чтобы упростить обработку ошибок. В противном случае после попытки выделения памяти отмена ранее выполненных действий может оказаться сложной. Низкоуровневые функции выделения памяти полезны, когда необходимы участки памяти, которые находятся в смежных физических страницах, особенно если необ- ходима одна страница или очень большое количество страниц. Для более общего случая, когда необходимо выделить заданное количество байтов памяти, ядро предо- ставляет функцию kmalloc(). Функция kmalloc () Функция kmalloc () работает аналогично функции mallос () пространства поль- зователя, за исключением того, что добавляется еще один параметр flags. Функция kmalloc () — это простой интерфейс для выделения в ядре участков памяти разме- ром в заданное количество байтов. Если необходимы смежные страницы физиче- ской памяти, особенно когда их количество близко целой степени двойки, то ранее рассмотренные интерфейсы могут оказаться лучшим выбором. Однако для большин- ства операций выделения памяти в ядре функция kmalloc () — наиболее предпочти- тельный интерфейс. Рассматриваемая функция определена в файле void * kmalloc(size_t size, int flags) Данная функция возвращает указатель на участок памяти, который имеет размер хотя бы size байт 3 . Выделенный участок памяти содержит физически смежные стра- ницы. В случае ошибки функция возвращает значение NULL. Выделение памяти в ядре заканчивается успешно, только если доступно достаточное количество памяти. Поэтому после вызова функции kmalloc () всегда необходимо проверять возвраща- емое значение на равенство значению NULL и соответственным образом обрабаты- вать ошибку. Рассмотрим пример. Допустим, нам необходимо выделить достаточно памяти для того, чтобы в ней можно было разместить некоторую воображаемую структуру dog. struct dog *ptr; ptr = kmalloc (sizeof (struct dog), GFP_KERNEL); if (!ptr) /* здесь обработать ошибку ... */ 3 Данная функция может выделить памяти больше, чем указано, и нет никакой возможности узнать, на сколько больше! Поскольку в своей основе система выделения памяти в ядре базируется на страницах, некоторые запросы на выделение памяти могут округляться, чтобы хорошо вписывать- ся е области доступной памяти. Ядро никогда не выделит меньше памяти, чем необходимо. Если ядро не v, состоянии найти хотя бы указанное количество байтов, то операция завершится неудач- но и функции возвратит значение NULL. 240 Глава 11 Если вызов функции kmalloc () завершится успешно, то переменная p t r будет указывать на область памяти, размер которой больше указанного значения или ра- вен ему. Флаг GFP_KERNEL определяет тип поведения системы выделения памяти, когда она пытается выделить необходимую память при вызове функции kmalloc (). Флаги gfp_mask Выше были показаны различные примеры использования флагов, которые моди- фицируют работу системы выделения памяти, как при вызове низкоуровневых функ- ций, работающих на уровне страниц, так и при использовании функции kmalloc {) . Теперь давайте рассмотрим их более детально. Флаги разбиты на три категории: модификаторы операций, модификаторы зон и флаги типов. Модификаторы операций указывают, каким образом ядро должно вы- делять указанную память. В некоторых ситуациях только некоторые методы могут использоваться для выделения памяти. Например, обработчики прерываний могут потребовать от ядра, что нельзя переходить в состояние ожидания при выделе- нии памяти (поскольку обработчики прерывания не могут быть перепланированы), Модификаторы зоны указывают, откуда нужно выделять память. Как было рассказа- но, ядро делит физическую память на несколько зон, каждая из которых служит для различных целей. Модификаторы зоны указывают, из какой зоны выделять память. Флаги типов представляют собой различные комбинации модификаторов операций и зон, которые необходимы для определенного типа выделения памяти. Флаги ти- пов содержат в себе различные модификаторы, вместо которых можно просто ис- пользовать один флаг типа. Флаг GFP_KERNEL — это флаг типа, который использу- ется кодом, выполняющимся в ядре в контексте процесса. Рассмотрим все флаги отдельно. Модификаторы операций Все флаги, включая модификаторы операций, определены в заголовочном файле Описанные модификаторы можно указывать вместе, как показано в следующем примере. ptr = kmalioc(size, __GFP_WAIT | __GFP_IO | __GFP_FS); Этот код дает инструкцию ядру (а именно функции alloc_pages ()), что опера- ция выделения памяти может быть блокирующей, выполнять операции ввода-вывода и операции файловой системы, если это необходимо. В данном случае ядру предо- ставляется большая свобода в отношении того, где оно будет искать необходимую память, чтобы удовлетворить запрос. Большинство запросов на выделение памяти указывают эти модификаторы, но это делается косвенным образом с помощью флагов типа, которые скоро будут рас- смотрены. Не нужно волноваться, у вас не будет необходимости каждый раз разби- раться, какие из этих ужасных флагов использовать при выделении памяти! Управление памятью 241 Таблица 11.3. Модификаторы операций Флаг Описание __GFP_WAIT __GFP_HIGH __GFP_IO __GFP_FS __GFP_COLD __GFP_NOWARN __GFP_REPEAT __GFP_NOFAIL __GFP_NORETRY __GFP_NO_GROW __GFP_COMP Модификаторы зоны Модификаторы зоны указывают, из какой зоны должна выделяться память. Обыч- но выделение может происходить из любой зоны. Однако ядро предпочитает зону ZONE_NORMAL, чтобы гарантировать, что в других зонах, когда это необходимо, есть свободные страницы. Существует всего два модификатора зоны, поскольку, кроме зоны ZONE_N0RMAL (из которой по умолчанию идет выделение памяти), существует всего две зоны. В табл. 11.4 приведен список модификаторов зоны. Таблица 11.4. Модификаторы зоны Указание одного из этих флагов изменяет зону, из которой ядро пытается выде- лить память. Флаг __GFP_DMA требует, чтобы ядро выделило память только из зоны ZONE_DMA. Этот флаг эквивалентен следующему высказыванию в форме жесткого требования: "Мне абсолютно необходима память, в которой можно выполнять операции прямого доступа к памяти". Флаг __GFP_HIGHMEM, наоборот, требует, чтобы выделе- ние памяти было из зон ZONE_NORMAL и ZOHE_HIGHMEM (вторая более предпочти- тельна). Этот флаг эквивалентен запросу: "Я могу использовать верхнюю память, но мне на самом деле, все равно, и делайте, что хотите, обычная память тоже подойдет". 2 4 2 Глава 11 Флаг Описание __GFP_DMA __GFP_HIGHMEM Выделять память только из зоны ZONE_ DMA Выделять память только из зон ZONE_HIGHMEM и ZONE_NORMAL Операция выделения памяти может переводить текущий процесс в состояние ожидания Операция выделения памяти может обращаться к аварийным запасам Операция выделения памяти может использовать дисковые операции ввода-вывода Операция выделения памяти может использовать операции ввода- вывода файловой системы Операция выделения памяти должна использовать страницы памяти, содержимое которых не находится в кэше процессора (cache cold) Операция выделения памяти не будет печатать сообщения об ошибках Операция выделения памяти повторит попытку выделения в случае ошибки Операция выделения памяти будет повторять попытки выделения неопределенное количество раз Операция выделения памяти никогда не будет повторять попытку выделения памяти Используется внутри слябового распределителя памяти (slab layer) Добавить метаданные составной (compound) страницы памяти. Используется кодом поддержки больших страниц памяти (hugetlb) Если не указан ни один из флагов, то ядро пытается выделять память из зон ZONE_NORMAL и ZONE_DMA, отдавая значительное предпочтение зоне ZONE_NORMAL. Флаг __GFP_HIGHMEM нельзя укалывать при вызове функций __get_free_pages () или k m a l l o c (> . Это связано с тем, что они возвращают логический адрес, а не структуру page, и появляется возможность, что эти функции выделят память, кото- рая в данный момент не отображается п виртуальное адресное пространство ядра и поэтому не имеет логического адреса. Только функция a l l o c _ p a g e a () может вы- делять страницы в верхней памяти. Однако в большинстве случаев в запросах на вы- деление памяти не нужно указывать модификаторы зоны, так как достаточно того, что используется зона ZONE_NORMAL. Флаги типов Флаги типов указывают модификаторы операций и зон, которые необходимы для выполнения запросов определенных типов. В связи с этим, в коде ядра стара- ются использовать правильный флаг типа и не использовать больших наборов мо- дификаторов. Это одновременно и проще и при этом меньше шансов ошибиться. В табл. 11.5 приведен список возможных флагов типов, а в табл. 11.6 показано, какие модификаторы соответствуют какому флагу. Таблица 11.5. Флаги типов Флаг Описание G F P _ A T O M I C Запрос на выделение памяти высокоприоритетный и в состояние ожидания переходить нельзя. Этот флаг предназначен для использования в обработчиках прерываний, нижних половин и в других ситуациях, когда нельзя переходить в состояние ожидания G F P _ N O I O Запрос на выделение памяти может блокироваться, но при его выполнении нельзя выполнять операции дискового ввода-вывода. Этот флаг предназначен для использования в коде блочного ввода-вывода, когда нельзя инициировать новые операции ввода-вывода GFP_NOFS Запрос на выделение памяти может блокироваться и выполнять дисковые опе- рации ввода-вывода, но запрещено выполнять операции, связанные с файло- выми системами. Этот флаг предназначен для использования в коде файловых систем, когда нельзя начинать выполнение новых файловых операций G F P _ K E R N E L Обычный запрос на выделение памяти, который может блокироваться. Этот флаг предназначен для использования в коде, который выполняется в контек- сте процесса, когда безопасно переходить в состояние ожидания G F P _ U S E R Обычный запрос на выделение памяти, который может блокироваться. Этот флаг используется для выделения памяти процессам пространства пользователя GFP_HIGHUSER Запрос на выделение памяти из зоны ZONE_HIGHMEM, который может блоки- роваться. Этот флаг используется для выделения памяти процессам простран- ства пользователя GFP_DMA Запрос на выделение памяти из зоны ZONE_DMA. Драйверам устройств, кото- рым нужна память для выполнения операций по ПДП, необходимо использо- вать этот флаг обычно в комбинации с одним из описанных выше флагов Управление памятью 243 Таблица 11.6. Список модификаторов, соответствующих каждому флагу типа GFP_ATOMIC GFP_NOIO GFP_NOFS GFP_KERNEL GFP_USER GFP_HIGHUSER GFP_DMA Рассмотрим наиболее часто используемые флаги и для чего они могут быть нуж- ны. Подавляющее большинство операций выделения памяти в ядре используют флаг GFP_KERNEL. В результате операция выделения памяти имеет обычный приоритет и может переводить процесс в состояние ожидания. Поскольку этот вызов может бло- кироваться, его можно использовать только в контексте процесса, выполнение ко- торого может быть безопасно перепланировано (т.е. нет удерживаемых блокировок и т.д.). При использовании этого флага нет никаких оговорок по поводу того, каким образом ядро может получить необходимую память, поэтому операция выделения памяти имеет большой шанс выполниться успешно. Можно сказать, что свойства флага GFP_ATOMIC лежат на противоположном кон- це спектра. Так как этот флаг указывает, что операция выделения памяти не может переходить в состояние ожидания, то такая операция очень ограничена в том, ка- кую память можно использовать для выделения. Если нет доступного участка памя- ти заданного размера, то ядро, скорее всего, не будет пытаться освободить память, поскольку вызывающий код не может переходить в состояние ожидания. При ис- пользовании флага GFP_KERNEL, наоборот, ядро может перевести вызывающий код в состояние ожидания, чтобы во время ожидания вытеснить страницы на диск (swap out), очистить измененные страницы памяти путем записи их в дисковый файл (flush dirty pages) и т.д. Поскольку при использовании флага GFP_ATOMIC нет возможности выполнить ни одну из этих операций, то и шансов успешно выполнить выделение памяти тоже меньше (по крайней мере, когда в системе недостаточно памяти). Тем не менее использование флага GFP_ATOMIC— это единственная возможность, когда вызывающий код не может переходить в состояние ожидания, как в случае обработ- чиков прерываний и нижних половин. По своим свойствам между рассмотренными флагами находятся флаги GFP_NOIC и GFP_NOFS. Операции пыделения памяти, которые запущены с этими флагами, могут блокироваться, но они воздерживаются от выполнения некоторых действий. Выделение памяти с флагом GFP_NOIO не будет запускать никаких операций дис- кового ввода-вывода. С другой стороны, при использовании флага GFP_NOFS могут запускаться операции дискового ввода-вывода, но не могут запускаться операции файловых систем. Когда эти флаги могут быть полезны? Они соответственно необ- ходимы для определенного низкоуровневого кода блочного ввода-вывода или кода файловых систем. Представьте себе, что в некотором часто используемом участке кода файловых систем используется выделение памяти без указания флага GFP_NOFS. Если выделение памяти требует выполнения операций файловой системы, то вы- 244 Глава 11 Флаг Модификаторы __GFP_HIGH __GFP_WAIT (__GFP_WAIT | __GFP_IO) (__GFP_WAIT | __GFP_IO | __GFP_FS) (__GFP_WAIT | __GFP_IO | __GFP_FS) (__GFP_WAIT | __GFP_IO | __GFP_FS | __GFP_HIGHMEM) __GFP_DMA деление памяти приведет к еще большему количеству операций файловой системы, которые потребуют дополнительного выделения памяти и еще большего количества файловых операций! При разработке кода, который использует выделение памяти, необходимо гарантировать, что операции выделения памяти не будут использовать этот код, как в рассмотренном случае, иначе может возникнуть самоблокировка. Не удивительно, что и ядре рассматриваемые флаги используются только в небольшом количестве мест. Флаг GFP_DMA применяется для указания, что система выделения памяти долж- на при выполнении запроса предоставить память из зоны ZONE_DMA. Этот флаг ис- пользуется драйверами устройств, для которых необходимо выполнение операций прямого доступа к памяти. Обычно этот флаг должен комбинироваться с флагами CFP_ATOMIC или GFP_KERNEL. В подавляющем большинстве случаев при разработке кода вам будет необходимо использовать флаги GFP_ATOMIC или GFP_KERNEL. В табл. 11.7 показано какие флаги и в каких ситуациях необходимо использовать. Независимо от типа операции выде- ления памяти, необходимо проверять результат и обрабатывать ошибки. Таблица 11.7. Какой флаг и когда необходимо использовать Функция kfree() Обратной к функции kmalloc () является функция k f r e e (), которая определена в файле < l i n u x / s l a b . h > следующим образом. void kfree(const void *ptr) Функция k f r e e () позволяет освободить память, ранее выделенную с помощью функции kmalloc (). Вызов этой функции для памяти, которая ранее не была выде- лена с помощью функции kmalloc () или уже была освобождена, приводит к очень плохим последствиям, таким как освобождение памяти, которая принадлежит другим частям ядра. Так же как и в пространстве пользователя, количество операций выде- ления памяти должно быть равно количеству операций освобождения, чтобы предот- вратить утечку памяти и другие проблемы. Следует обратить внимание, что случай вызова kfree (NULL) специально проверяется и поэтому является безопасным. Управление памятью 2 4 5 Ситуация Решение Контекст процесса, можно переходить в со- Используется флаг GFP_KERNEL стояние ожидания Контекст процесса, нельзя переходить в со- Используется флаг G F P _ A T O M I C или память вы- стояние ожидания деляется с использованием флага G F P _ K E R N E L но в более ранний или поздний момент, когда можно переходить в состояние ожидания Обработчик прерывания Используется флаг G F P _ A T O M I C Обработка нижней половины Используется флаг G F P _ A T O M I C Необходима память для выполнения операций Используются флаги ПДП, можно переходить в состояние ожидания (GFP_DMA | GFP_KERNEL) Необходима память для выполнения операций Используются флаги (GFP_DMA | GFP_ATOMIC) ПДП, нельзя переходить в состояние ожидания или выделение выполняется в более поздний или более ранний момент времени Рассмотрим пример выделения памяти в обработчике прерывания. В этом при- мере обработчику прерывания необходимо выделить буфер памяти для хранения входных данных. С помощью препроцессора определяется константа. BUF_SIZE, как размер буфера памяти в байтах, который, скорее всего, должен быть больше, чем несколько байт. char *buf; buf = kmalloc(BUF_SIZE, GFP_ATOMIC); if (!buf) /* ошибка выделения памяти! */ Позже, когда память больше не нужна, нужно не забыть освободить ее с помо- щью вызова функции kfree(buf); Функция vmalloc () Функция vmalloc () работает аналогично функции kmalloc (), за исключением того, что она выделяет страницы памяти, которые только виртуально смежные и необязательно смежные физически. Точно так же работают и функции выделения памяти в пространстве пользователя: страницы памяти, которые выделяются с по- мощью функции malloc (), япляются смежными в виртуальном адресном простран- стве процесса, но нет никакой гарантии, что они смежные в физической оператив- ной памяти. Функция k m a l l o c () отличается тем, что гарантирует, что страницы памяти будут физически (и виртуально) смежными. Функция vmalloc () гарантирует только, что страницы будут смежными в виртуальном адресном пространстве ядра. Это реализуется путем выделения потенциально несмежных участков физической памяти и "исправления" таблиц страниц, чтобы отобразить эту физическую память в непрерывный участок логического адресного пространства. Большей частью, только аппаратным устройствам необходимо выделение физи- чески непрерывных участков памяти. Аппаратные устройства существуют по дру- гую сторону модуля управления памятью и не "понимают" виртуальной адресации. Следовательно, все области памяти, с которыми работают аппаратные устройства, должны состоять из физически смежных блоков, а не из виртуально непрерывных участков. Для участков памяти, которые используются только программным обеспе- чением, например буферы памяти, связанные с процессами, прекрасно подходят области памяти, которые только виртуально непрерывны. При программировании заметить разницу невозможно. Это связано с тем, что память ядром воспринимается как логически непрерывная. Несмотря на то что физически смежные страницы памяти необходимы только в определенных случаях, большая часть кода ядра использует для выделения памяти функцию k m a l l o c () , а не v m a l l o c () . Это, в основном, делается из соображений производительности. Для того чтобы физически несмежные страницы памяти сде- лать смежными в виртуальном адресном пространстве, функция vmalloc () должна соответствующим образом заполнить таблицы страниц. Хуже того, страницы памя- ти, которые получаются с помощью функции vmalloc () , должны отображаться по- средством страниц памяти, которые принадлежат к таблицам страниц (потому что 246 Глава 11 выделяемые страницы памяти физически несмежные). Это приводит к значитель- но менее эффективному использованию буфера TLB 4 , чем в случае, когда страницы памяти отображаются напрямую. Исходя из этих соображений функция vmalloc () используется только тогда, когда она абсолютно необходима, обычно это делается для выделения очень больших областей памяти. Например, при динамической за- грузке модулей ядра, модули загружаются в память, которая выделяется с помощью функции vmalloc (). Функция v m a l l o c () объявлена в файле < l i n u x / v m a l l o c . h > и определена в файле mm/vmalloc.с. Использование этой функции аналогично функции malloc () пространства пользователя. void * vmalloc(unsigned long size) Функция возвращает указатель на виртуально непрерывную область памяти раз- мером по крайней мере s i z e байт. В случае ошибки эта функция возвращает значе- ние NULL. Данная функция может переводить процесс в состояние ожидания и со- ответственно не может вызываться в контексте прерывания или в других ситуациях, когда блокирование недопустимо. Для освобождения памяти, выделенной с помощью функции vmalloc () , необхо- димо использовать функцию void vfree(void *addr) Эта функция освобождает участок памяти, который начинается с адреса addr и был ранее выделен с помощью функции v m a l l o c (). Данная функция также может переводить процесс в состояние ожидания и поэтому не может вызываться из кон- текста прерывания. Функция не возвращает никаких значений. Использовать рассмотренные функции очень просто. Например, следующим об- разом. char *buf; buf = vmalloc (16 * PAGE_SIZE); /* получить 16 страниц памяти */ if (!buf) /* ошибка! Не удалось выделить память */ /* * переменная buf теперь указывает на область памяти * размером, по крайней мере, 16*PAGE_SIZE байт, которая состоит * из виртуально смежных блоков памяти */ После того как память больше не нужна, необходимо убедиться, что она освобож- дается с помощью следующего вызова. vfree ( b u f ) ; 4 Буфер TLB (translation lookside buffer или буфер быстрого преобразования адреса) — это аппарат- ный буфер памяти, который используется в большинстве аппаратных платформ для кэширования отображений виртуальных адресов памяти в физические адреса. Этот буфер позволяет существен- но повысить производительность системы, так как большинство операций доступа к памяти вы- полняются с использованием виртуальной адресации. Управление памятью 247 Уровень слябового распределителя памяти Выделение и освобождение структур данных — это одна из наиболее частых опе- раций, которые выполняются в любом ядре. Для того чтобы облегчить процедуру частого выделения и освобождения данных при программировании, вводятся списки свободных ресурсов (free list). Список свободных ресурсов содержит некоторый набор уже выделенных структур данных. Когда коду необходим новый экземпляр структу- ры данных, он может взять одну структуру из списка свободных ресурсов, вместо того чтобы выделять необходимый объем памяти и заполнять его данными соот- ветствующей структуры. Позже, когда структура данных больше не нужна, она снова возвращается в список свободных ресурсов, вместо того чтобы освобождать память. В этом смысле список свободных ресурсов представляет собой кэш объектов, в кото- ром хранятся объекты одного определенного, часто используемого типа. Одна из наибольших проблем, связанных со списком свободных ресурсов в ядре, это то, что над ними нет никакого централизованного контроля. Когда ощущается недостаток свободной памяти, нет никакой возможности взаимодействовать меж- ду ядром и всеми списками свободных ресурсов, которые в такой ситуации долж- ны уменьшить размер своего кэша, чтобы освободить память. В ядре нет никакой информации о случайно созданных списках свободных ресурсов. Для исправления положения и для универсальности кода ядро предоставляет уровень слябового рас- пределения памяти (slab layer), который также называется просто слябовым распре- делителем памяти (slab allocator). Уровень слябового распределения памяти выпол- няет функции общего уровня кэширования структур данных. Концепции слябового распределения памяти впервые были реализованы в опе- рационной системе SunOS 5.4 фирмы Sun Microsystems'. Для уровня кэширования структур данных в операционной системе Linux используется такое же название и похожие особенности реализации. Уровень слябового распределения памяти служит для достижения следующих целей. • Часто используемые структуры данных, скорее всего, будут часто выделяться и освобождаться, поэтому их следует кэшировать. • Частые операции выделения и освобождения памяти могут привести к фраг- ментации памяти (к невозможности найти большие участки физически одно- родной памяти). Для предотвращения этого, кэшированные списки свободных ресурсов организованы непрерывным образом в физической памяти. Так как структуры данных возвращаются снова в список свободных ресурсов, то в ре- зультате никакой фрагментации не возникает. • Список свободных ресурсов обеспечивает улучшенную производительность при частых выделениях и освобождениях объектов, так как освобожденные объекты сразу же готовы для нового выделения. • Если распределитель памяти может использовать дополнительную инфор- мацию, такую как размер объекта, размер страницы памяти и общий размер кэша, то появляется возможность принимать в критических ситуациях более интеллектуальные решения. 5 И позже документированы в работе Bonwirk J."The Slab Allocator: An Object-Caching Kernel Memory Allocator," USENIX, 1994. у 248 Глава 11 • Если кэш организован, как связанный с определенным процессором (т.е. для каждого процессора в системе используется свой уникальный отдельный кэш), то выделение и освобождение структур данных может выполняться без исполь- зования SMP-блокиропок. • Если распределитель памяти рассчитан на доступ к неоднородной памяти (Non-Uniform Memory Access NUMA), то появляется возможность выделения памяти с того же узла (node), на котором эта память запрашивается. • Хранимые объекты могут быть "окрашены', чтобы предотвратить отображение разных объектов на одни и те же строки системного кэша. Уровень слябового распределения памяти в ОС Linux был реализован с учетом указанных принципов. Устройство слябового распределителя памяти Уровень слябового распределения памяти делит объекты па группы, которые на- зываются кэшами (cache). Разные кэши используются для хранения объектов различ- ных типов. Для каждого типа объектов существует свой уникальный кэш. Например, один кэш используется для дескрипторов процессов (список свободных структур s t r u c t task_struct), а другой— для индексов файловых систем ( s t r u c t inode). Интересно, что интерфейс krnalloc () построен на базе уровня слябового распреде- ления памяти и использует семейство кэшей общего назначения. Далее кэши делятся на слябы (буквально slab — однородная плитка, отсюда и на- звание всей подсистемы). Слябы занимают одну или несколько физически смежных страниц памяти. Обычно сляб занимает только одну страницу памяти. Каждый кэш может содержать несколько слябов. Каждый сляб содержит некоторое количество объектов, которые представляют со- бой кэшируемые структуры данных. Каждый сляб может быть в одном из трех состо- яний: полный (full), частично заполненный (partial) и пустой (empty). Полный сляб не содержит свободных объектов (все объекты сляба выделены для использования). Частично заполненный сляб содержит часть выделенных и часть свободных объ- ектов. Когда некоторая часть ядра запрашивает новый объект, то этот запрос удо- влетворяется из частично заполненного сляба, если такой существует. В противном случае запрос выполняется из пустого сляба. Если пустого сляба не существует, то он создается. Очевидно, что полный сляб никогда не может удовлетворить запрос, поскольку в нем нет свободных объектов. Такая политика уменьшает фрагментацию памяти. В качестве примера рассмотрим структуры inode, которые являются представле- нием в оперативной памяти индексов дисковых файлов (см. главу 12). Эти структуры часто создаются и удаляются, поэтому есть смысл управлять ими с помощью слябо- вого распределителя памяти. Структуры s t r u c t inode выделяются из кэша inode_ cachep (такое соглашение по присваиванию названий является стандартом). Этот кэш состоит из одного или более слябов, скорее всего слябов много, поскольку мно- го объектов. Каждый сляб содержит максимально возможное количество объектов типа s t r u c t inode. Когда ядро выделяет новую структуру типа s t r u c t inode, воз- вращается указатель на уже выделенную, но не используемую структуру из частично заполненного сляба или, если такого нет, из пустого сляба. Когда ядру больше не ну- жен объект типа inode, то слябовый распределитель памяти помечает этот объект Управление памятью 249 |