Блокирование больших объемов памяти, особенно с помощью функции mlockall()
, несет потенциальную угрозу всей системе. Несправедливое распределение оперативной памяти приведет к катастрофическому снижению производительности системы, так как остальным процессам придется сражаться друг с другом за небольшой "клочок" памяти, вследствие чего они будут постоянно выгружаться на диск и загружаться обратно. Может даже возникнуть ситуация, когда оперативная память закончится и система начнет уничтожать процессы. По этой причине функции mlock()
и mlockall()
доступны лишь суперпользователю. Если какой-нибудь другой пользователь попытается вызвать одну из этих функций, она вернёт значение -1, а в переменную errno будет записан код EPERM
.
Функция munlосkall()
разблокирует всю память текущего процесса.
Контролировать использование памяти удобнее всего с помощью команды top
. В колонке SIZE
ее выходных данных показывается размер виртуального адресного пространства каждой программы (общий размер сегментов кода, данных и стека с учетом выгруженных страниц). В колонке RSS
приводится объем резидентной части программы. Сумма значений в столбце RSS
не может превышать имеющийся объем ОЗУ, а суммарный показатель по столбцу SIZE
не может быть больше 2 Гбайт (в 32-разрядных версиях Linux).
Функции семейства mlock()
объявлены в файле .
8.9. Функция mprotect(): задание прав доступа к памяти
В разделе 5.3, "Отображение файлов в памяти", рассказывалось о том, как осуществляется отображение файла в памяти. Вспомните, что третьим аргументом функции mmap()
является битовое объединение флагов доступа: флаги PROT_READ
, PROT_WRITE
и PROT_EXEC
задают права чтения, записи и выполнения файла, а флаг PROT_NONE
означает запрет доступа. Если программа пытается выполнить над отображаемым файлом недопустимую операцию, ей посылается сигнал SIGSEGV
(нарушение сегментации), который приводит к завершению программы.
После того как файл был отображен в памяти, изменить права доступа к нему позволяет функция mprotect()
. Ее аргументами является адрес области памяти, размер области и новый набор флагов доступа. Область должна состоять из целых страниц, т.е. начинаться и заканчиваться на границе между страницами.
Корректное выделение памяти
Учтите, что память, выделяемая функцией malloc()
, обычно не выравнивается по границе страниц, даже если размер области кратен размеру страницы. Если требуется защищать память, выделяемую функцией malloc()
, нужно запросить более крупный блок, а затем найти в нем участок, выровненный по границе страниц.
Кроме того, с помощью функции mmap()
можно обойти функцию malloc()
и запрашивать память непосредственно у ядра Linux.
Предположим, к примеру, что программа выделяет страницу, отображая в памяти файл /dev/zero
. Память инициализируется как для чтения, так и для записи:
int fd = open("/dev/zero", O_RDONLY);
char* memory =
mmap(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE, fd, 0);
close(fd);
Далее программа запрещает запись в эту область памяти, вызывая функцию mprotect()
:
mprotect(memory, page_size, PROT_READ);
Существует оригинальная методика контроля памяти: можно защитить область памяти с помощью функций mmap()
и mprotect()
, а затем обрабатывать сигнал SIGSEGV
, посылаемый при попытке доступа к этой памяти. Эта методика иллюстрируется в листинге 8.7.
Листинг 8.7. ( mprotect.c ) Обнаружение попыток доступа к памяти благодаря функции mprotect()
#include
#include
#include
#include
#include
#include
#include
#include
static int alloc_size;
static char* memory;
void segv_handler(int signal_number) {
printf("memory accessed!\n");
mprotect(memory, alloc_size, PROT_READ | PROT_WRITE);
}
int main() {
int fd;
struct sigaction sa;
/* Назначение функции segv_handler() обработчиком сигнала
SIGSEGV. */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = &segv_handler;
sigaction(SIGSEGV, &sa, NULL);
/* Выделение одной страницы путем отображения в памяти файла
/dev/zero. Сначала память доступна только для записи. */
alloc_size = getpagesize();
fd = open("/dev/zero", O_RDONLY);
memory =
mmap(NULL, alloc_size, PROT_WRITE, MAP_PRIVATE, fd, 0);
close(fd);
/* Запись на страницу для получения ее копии в частное
использование. */
memory[0] = 0;
Читать дальше