Системный вызов mlock() блокирует все страницы вызывающего процесса в диапазоне виртуальных адресов длиной length, начинающегося с addr. В отличие от аналогичного аргумента в ряде других вызовов, связанных с памятью, addr не обязательно выравнивать по странице; ядро автоматически выбирает ближайшую страницу, адрес начала которой не превышает addr. Однако стандарт SUSv3 разрешает реализации требовать, чтобы аргумент addr был кратным размеру страницы в системе, и портируемые приложения, использующие вызовы mlock() и munlock(), должны следовать этому требованию.
Поскольку единицей блокирования является целая страница, конец блокируемого участка совпадает с концом следующей страницы, адрес которой больше length плюс addr. Например, в системе, где размер страницы равен 4096 байтам, вызов mlock(2000, 4000) заблокирует диапазон байтов с 0 по 8191.
Чтобы узнать, сколько всего памяти заблокировал текущий процесс, можно прочитать поле VmLck в файле /proc/PID/status.
После успешного вызова mlock() все страницы в заданном диапазоне гарантированно запираются в физической памяти. Вызов mlock() дает сбой, если для блокирования всех запрашиваемых страниц не хватает физической памяти или запрос нарушает ограничение на ресурсы RLIMIT_MEMLOCK.
Пример использования вызова mlock() показан в разделе 46.2.
Системный вызов munlock() выполняет обратную операцию, удаляя из памяти блокировку, установленную ранее вызывающим процессом. Аргументы addr и length интерпретируются так же, как и в mlock(). Разблокировка набора страниц не гарантирует, что они перестанут храниться в физическом памяти, — они будут удалены оттуда только в том случае, если память потребуется другим процессам.
Блокировки могут удаляться не только вручную, благодаря вызову munlock(), но и автоматически. Это происходит в следующих ситуациях:
• при завершении процесса;
• при удалении заблокированных страниц из отображения с помощью вызова munmap();
• если заблокированные страницы перекрываются в результате вызова mmap() с флагом MAP_FIXED.
Ниже мы рассмотрим некоторые особенности семантики блокирования памяти.
Блокировки памяти не наследуются потомком, созданным путем вызова fork(), и не сохраняются на протяжении работы exec().
Если несколько процессов разделяют какой-то набор страниц (например, отображение типа MAP_SHARED), то эти страницы остаются запертыми в физической памяти, пока хотя бы один процесс удерживает соответствующую блокировку.
Блокировки памяти не накапливаются в рамках одного процесса. Если процесс многократно вызовет mlock() для определенного диапазона виртуальных адресов, то это приведет к установке всего лишь одной блокировки, которую можно будет убрать с помощью единственного вызова munlock(). С другой стороны, при отображении одних и тех же страниц (то есть того же файла) на разные участки памяти того же процесса и блокировке каждого из этих отображений страницы будут оставаться запертыми в физической памяти до тех пор, пока все отображения не будут разблокированы.
Тот факт, что единицей измерения блокировок памяти являются страницы, а также то, что блокировки не могут накапливаться, означает следующее: независимое применение вызовов mlock() и munlock() к разным структурам данных на одной виртуальной странице логически неверно. Представьте, к примеру, что у нас есть две структуры данных внутри одной страницы виртуальной памяти и на них ссылаются указатели p1 и p2. Теперь выполним следующие вызовы:
mlock(*p1, len1);
mlock(*p2, len2); /* На самом деле ни на что не влияет */
munlock(*p1, len1);
Все вышеприведенные вызовы завершатся успешно, но в результате будет разблокирована вся страница; таким образом, структура данных, на которую указывает p2, не запирается в физической памяти.
Обратите внимание на то, что семантика операции shmctl() SHM_LOCK отличается от семантики вызовов mlock() и mlockall():
• после операции SHM_LOCK страницы блокируются, только если последующие обращения к ним приводят к отказу. Вызовы mlock() и mlockall() возвращаются только после того, как проверят запертые в физической памяти страницы на отказы;
• операция SHM_LOCK работает скорее с разделяемым сегментом памяти, чем с процессом (по данной причине значение поля VmLck в файле /proc/PID/status не учитывает размер любых подключенных сегментов памяти типа System V, которые были заблокированы операцией SHM_LOCK). Это значит, что после отказа страница остается в физической памяти, даже если все процессы отключатся от соответствующего сегмента памяти. Участок, запертый в физической памяти с помощью вызова mlock() (или mlockall()), остается там только до тех пор, пока хотя бы один процесс удерживает блокировку данного участка.
Для выполнения этих операций процесс может использовать вызовы mlockall() и munlockall().
#include
int mlockall(int
int munlockall(void);