Ядро Linux в комментариях

       

Sys_shmat


Эта функция реализует системный вызов shmat, с помощью которого вызывающий процесс подключается к области разделяемой памяти.

Дальнейшее нам уже немного знакомо: с учетом настройки функция sys_shmat начинает прорабатывать адрес, по которому эта область разделяемой памяти должна появиться в пространстве памяти вызывающего процесса. Вначале она проверяет адрес, переданный вызывающей программой. Если он равен NULL и флажок SHM_REMAP не установлен (см. строку ), запрос должен быть отвергнут, поскольку чтение или запись по адресу NULL являются недопустимыми.

Вызывающая программа передала NULL в качестве целевого адреса, а это значит, что функция sys_shmat должна выбрать адрес в пространстве памяти процесса. Подходящий для этого адрес предоставляется функцией get_unmapped_area (строка ), которая была мельком упомянута в предыдущей главе. Если она возвращает 0 (значение, эквивалентное NULL во всех архитектурах, поддерживаемых этим ядром), это значит, что не удалось найти ни одной области подходящей величины.

Если подходящий адрес еще не лежит точно на границе страницы, он округляется в большую сторону, к границе следующей страницы, расположенной в направлении старших адресов, а затем вместо него проверяется откорректированный адрес. Функция get_unmapped_area возвращает первый доступный адрес, равный данному или расположенный выше него, поэтому, если доступен адрес, округленный в большую сторону, он и будет использоваться.

Это позволяет понять, почему адрес округляется в большую, а не меньшую сторону (что было бы быстрее и проще): если бы функция sys_shmat округляла в меньшую сторону и адрес, округленный в меньшую сторону, был бы недоступен, то этот код вошел бы в бесконечный цикл. Следующий вызов функции get_unmapped_area приводил бы к просмотру в направлении старших адресов, начиная от адреса, округленного в меньшую сторону, и возвращал бы первоначальное неокругленное подходящее значение, которое было бы снова округлено в меньшую сторону, распознано как непригодное и снова передано функции get_unmapped_area.


Обратите внимание, что здесь для определения пригодности адреса используется значение SHMLBA (строка ), а не PAGE_SIZE (строка ). Однако можно видеть, что значение SHMLBA директивой #define установлено равным PAGE_SIZE, поэтому не имеет значение, какое из них здесь используется.

Но если значения SHMLBA и PAGE_SIZE одинаковы, то почему бы не убрать одно из них? Ответ состоит в том, что значение SHMLBA равно PAGE_SIZE на большинстве платформ, но не на всех. В архитектуре MIPS величина PAGE_SIZE равна 4Кб и в системе Linux значение SHMLBA определено директивой #define равным огромному числу 0x40000 (256 Кб), причем в комментарии указано, что такое большое значение было выбрано в качестве соответствия интерфейсу ABI (Application Binary Interface) компании SGI для компьютеров на основе MIPS. Однако в описании интерфейса ABI версий 2 и 3 для MIPS явно сказано, что значение SHMLBA «может принимать разные значения в соответствующих реализациях», поэтому непонятно, почему разработчики ядра предположили, что от них потребуют придерживаться величины в 256 Кб. Возможно, это значение требовалось для самой первой версии ABI, но автор просмотрел все предыдущие версии ABI, вплоть до 1.2, и не нашел такого требования.

Кроме того, в системе SPARC-64 значение SHMLBA вдвое превышает PAGE_SIZE; к сожалению, в коде нет объяснения причин этого различия.

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

Проверка того, что блок памяти размером len, начинающийся с выбранного адреса, поместится в допустимом пространстве памяти процесса. (Значение len вычисляется на несколько строк раньше, в строке .) Эта проверка явно необходима, если вызывающая программа передает подходящий адрес и, на первый взгляд, она также, видимо, нужна, когда функция sys_shmat выбирает адрес с использованием функции get_unmapped_area. Функция get_unmapped_area выполняет аналогичную проверку, но переданный ей размер области, член shm_segsz объекта struct shmid_ds, не обязательно совпадает со значением len, поскольку len является кратным величине PAGE_SIZE, a shm_segsz может не быть таковым.



Однако, поскольку все адреса, используемые функцией get_unmapped_area, выровнены по границе страницы, на вычисления в этой функции не влияет то, является ли переданный ей размер области кратным размеру страницы.

Как сказано в этом комментарии, в выбранной области нужно оставить немного места для стека процесса. Обязательным требованием является создание буферной зоны из четырех страниц: в этом числе нет ничего магического и его назначение состоит просто в том, чтобы оставить процессу немного места для стека. Напомним сказанное в предыдущей главе, что если задача исчерпывает объем своего стека, она должна быть уничтожена. С учетом всех соображений, вероятно, лучше допустить аварийное завершение одного системного вызова, чем грубое уничтожение целого процесса: процесс может корректно исправить последствия первой ситуации из указанных выше, но не последней из них.

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

Если вызывающая программа не имеет разрешения на использование этой области памяти, данный системный вызов завершается неудачей. Если в качестве параметра передан флажок SHM_RDONLY (только чтение), вызывающая программа нуждается только в разрешении на чтение; в ином случае, вызывающая программа нуждается в разрешении и на чтение, и на запись.

Заполнение новой области VMA. В частности, отметим, что ее член vm_ops инициализируется в значение указателя на shm_vm_ops (строка ), как описано в .

Увеличение числа ссылок на эту область, чтобы она не была преждевременно уничтожена.

Вызов функции shm_map (строка ) для отображения страниц разделяемой памяти на пространство памяти процесса. Если она терпит неудачу, то отменяет выполненные действия, уменьшая число ссылок, уничтожая область, если это была ее первая и единственная ссылка, и освобождая саму область VMA.



Обратите внимание, что область VMA не обязательно будет освобождена, даже если это последняя ссылка; эта область должна быть также отмечена флажком SHM_DEST (строка ). Флажок SHM_DEST может находиться среди флажков, установленных вызывающей программой; он может быть также установлен позже в ветви IPC_RMID функции sys_shmctl — см. строку . Благодаря этому, область разделяемой памяти может пережить все подключенные к ней процессы. Это может оказаться полезным по тем же причинам, по которым иногда может быть полезен неуничтоженный файл контрольной точки, находящийся где-то на диске: может применяться продолжительный процесс, который работает несколько часов каждую ночь, оставляя свои результаты в области разделяемой памяти, которая продолжает существовать даже после того, как текущий фрагмент работы процесса был выполнен. На следующую ночь процесс может продолжить свою работу точно с того места, где он ее прекратил, просто подключившись к этой сохранившейся области разделяемой памяти. (Безусловно, поскольку области разделяемой памяти в отличие от файлов исчезают при выключении компьютера, этот подход не применим для работы, результаты которой нельзя терять.)

Добавление shmd к списку областей VMA, подключенных к этой области, а затем обновление некоторой статистической информации, относящейся к области.

Возвращение адреса, фактически выбранного для области разделяемой памяти в пространстве вызывающей программы, а затем успешный возврат.


Содержание раздела