Читаем Разработка ядра Linux полностью

Еще одна функция — per_cpu_ptr() — возвращает экземпляр данных, связанных с указанным процессором.

per_cpu_ptr(ptr, cpu);

Эта функция не запрещает вытеснение в режиме ядра. Если вы "трогаете" данные, связанные с другим процессором, то, вероятно, необходимо применить блокировки.

<p>Когда лучше использовать данные, связанные с процессорами</p>

Использование данных, связанных с процессорами, позволяет получить ряд преимуществ. Во-первых, это ослабление требований по использованию блокировок. В зависимости от семантики доступа к данным, которые связаны с процессорами, может оказаться, что блокировки вообще не нужны. Следует помнить, что правило "только один процессор может обращаться к этим данным" является всего лишь рекомендацией для программиста. Необходимо специально гарантировать, что каждый процессор работает только со своими данными. Ничто не может помешать нарушению этого правила.

Во-вторых, данные, связанные с процессорами, позволяют существенно уменьшить недостоверность данных, хранящихся в кэше. Это происходит потому, что процессоры поддерживают свои кэши в синхронизированном состоянии. Если один процессор начинает работать с данными, которые находятся в кэше другого процессора, то первый процессор должен обновить содержимое своего кэша. Постоянное аннулирование находящихся в кэше данных, именуемое перегрузкой кэша (cash thrashing), существенно снижает производительность системы. Использование данных, связанных с процессорами, позволяет приблизить эффективность работы с кэшем к максимально возможной, потому что в идеале каждый процессор работает только со своими данными.

Следовательно, использование данных, которые связаны с процессорами, часто избавляет от необходимости использования блокировок (или снижает требования, связанные с блокировками). Единственное требование, предъявляемое к этим данным для безопасной работы, — это запрещение вытеснения кода, который работает в режиме ядра. Запрещение вытеснения — значительно более эффективная операция по сравнению с использованием блокировок, а существующие интерфейсы выполняют запрещение и разрешение вытеснения автоматически. Данные, связанные с процессорами, можно легко использовать как в контексте прерывания, так и в контексте процесса. Тем не менее следует обратить внимание, что при использовании данных, которые связаны с текущим процессором, нельзя переходить в состояние ожидания (в противном случае выполнение может быть продолжено на другом процессоре).

Сейчас нет строгой необходимости где-либо использовать новый интерфейс работы с данными, которые связаны с процессорами. Вполне можно организовать такую работу вручную (на основании массива, как было рассказано ранее), если при этом запрещается вытеснение кода в режиме ядра. Тем не менее новый интерфейс более простой в использовании и, возможно, позволит в будущем выполнять дополнительные оптимизации. Если вы собираетесь использовать в своем коде данные, связанные с процессорами, то лучше использовать новый интерфейс. Единственный недостаток нового интерфейса — он не совместим с более ранними версиями ядер.

<p>Какой способ выделения памяти необходимо использовать</p>

Если необходимы смежные страницы физической памяти, то нужно использовать один из низкоуровневых интерфейсов выделения памяти, или функцию kmalloc(). Это стандартный способ выделения памяти в ядре, и, скорее всего, в большинстве случаев следует использовать именно его. Необходимо вспомнить, что два наиболее часто встречающихся флага, которые передаются этой функции, это флаги GFP_ATOMIC и GFP_KERNEL. Для высокоприоритетных операций выделения памяти, которые не переводят процесс в состояние ожидания, необходимо указывать флаг GFP_ATOMIC. Это обязательно для обработчиков прерываний и других случаев, когда нельзя переходить в состояние ожидания. В коде, который может переходить в состояние ожидания, как, например код, выполняющийся в контексте процесса и не удерживающий спин-блокировку, необходимо использовать флаг GFP_KERNEL. Такой флаг указывает, что должна выполняться операция выделения памяти, которая при необходимости может перейти в состояние ожидания для получения необходимой памяти.

Если есть необходимость выделить страницы верхней памяти, то следует использовать функцию alloc_pages(). Функция alloc_pages() возвращает структуру struct page, а не логический адрес. Поскольку страницы верхней памяти могут не отображаться в адресное пространство ядра, единственный способ доступа к этой памяти — через структуру struct page. Для получения "настоящего" указателя на область памяти необходимо использовать функцию kmap(), которая позволяет отобразить верхнюю память в логическое адресное пространство ядра.

Перейти на страницу:

Похожие книги

C++: базовый курс
C++: базовый курс

В этой книге описаны все основные средства языка С++ - от элементарных понятий до супервозможностей. После рассмотрения основ программирования на C++ (переменных, операторов, инструкций управления, функций, классов и объектов) читатель освоит такие более сложные средства языка, как механизм обработки исключительных ситуаций (исключений), шаблоны, пространства имен, динамическая идентификация типов, стандартная библиотека шаблонов (STL), а также познакомится с расширенным набором ключевых слов, используемым в .NET-программировании. Автор справочника - общепризнанный авторитет в области программирования на языках C и C++, Java и C# - включил в текст своей книги и советы программистам, которые позволят повысить эффективность их работы. Книга рассчитана на широкий круг читателей, желающих изучить язык программирования С++.

Герберт Шилдт

Программирование, программы, базы данных