Опубликован: 06.12.2004 | Доступ: свободный | Студентов: 1179 / 142 | Оценка: 4.76 / 4.29 | Длительность: 20:58:00
ISBN: 978-5-9556-0021-5
Лекция 5:

Объекты в памяти

< Лекция 4 || Лекция 5: 1234 || Лекция 6 >

Средства удержания процессов в памяти

Современные системы имеют сложную аппаратную организацию. Отметим следующие особенности таких систем, существенные для приложений реального времени.

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

Подкачка страниц, непопадание в кэш, обращение к более медленной памяти вносят элементы недетерминированности в работу приложений. Чтобы по возможности избавиться от них, в стандарт POSIX введены средства удержания в физической памяти страниц из адресного пространства процессов. Они не ликвидируют недетерминированность полностью (например, ничего не говорится о длительности трансляции логического адреса в физический ), но позволяют обеспечить разумную верхнюю границу времени доступа к командам и данным процессов.

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

Для удержания в физической памяти группы страниц из адресного пространства процесса служит функция mlock() (см. листинг 5.14), воспользоваться которой может только процесс, обладающий соответствующими привилегиями (см. курс [1], где раскрывается понятие "соответствующие привилегии").

#include <sys/mman.h>
int mlock (const void *addr, size_t len);
Листинг 5.14. Описание функции mlock().

После успешного завершения вызова mlock() резидентными в памяти станут все страницы, пересекающиеся с частью адресного пространства процесса, начинающейся со значения addr и имеющей длину len байт. Реализация может требовать, чтобы значение addr указывало на границу страницы.

Если некоторая страница несколько раз отображена в адресные пространства процессов, то для ее удержания в памяти достаточно позаботиться о каком-либо одном отображении.

Удержание отменяется после вызовов fork() и exec(), а также ликвидации по какой-либо причине соответствующей части адресного пространства процесса (отмена отображения, завершение процесса и т.п.). Явная отмена удержания группы страниц реализуется функцией munlock() (см. листинг 5.15).

#include <sys/mman.h>
int munlock (const void *addr, size_t len);
Листинг 5.15. Описание функции munlock().

Более точно, munlock() разрешает больше не удерживать в памяти заданные страницы из адресного пространства вызывающего процесса. Если страница была отображена несколько раз и удерживалась в памяти несколькими вызовами mlock(), то одного обращения к munlock() для отмены удержания недостаточно.

Если нужно удерживать в памяти все адресное пространство процесса (что имеет место для большинства приложений реального времени), целесообразно воспользоваться функциями mlockall() и munlockall() (см. листинг 5.16).

#include <sys/mman.h>

int mlockall (int flags);

int munlockall (void);
Листинг 5.16. Описание функций mlockall() и munlockall().

Поскольку адресное пространство процесса при выполнении, вообще говоря, меняется, необходимо уточнить, что именно должно удерживаться в памяти. Для этого служит аргумент flags функции mlockall(), в значении которого могут фигурировать следующие флаги.

MCL_CURRENT

Удерживать в памяти страницы, присутствующие в адресном пространстве процесса на момент вызова.

MCL_FUTURE

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

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

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

Функция munlockall() отменяет удержание для всех страниц, присутствующих в адресном пространстве процесса на момент вызова или отображенных позднее, если только при вызове mlockall() не был установлен флаг MCL_FUTURE.

Определенную проблему составляет удержание в памяти страниц стека, рост которого не всегда можно точно оценить. (А стек, конечно, нуждается в удержании, иначе, например, функция обработки сигнала может выполниться с неожиданной задержкой.) Одно из возможных решений этой проблемы состоит в установке флага MCL_FUTURE при обращении к mlockall() и последующем вызове вспомогательной функции, в которой продекларирован автоматический массив достаточного размера (см. листинг 5.17).

/* * * * * * * * * * * * * * * * * * * * * * */
/* Программа демонстрирует удержание в памяти*/
/* всего адресного пространства процесса,    */
/* в том числе стека с учетом возможного роста*/
/* * * * * * * * * * * * * * * * * * * * * * */

#include <stdio.h>
#include <sys/mman.h>

#define LOCKED_STACK_SIZE 048576

/* * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Следующая функция нужна, чтобы вызвать рост стека до*/
/* требуемого размера. На всякий случай примем меры для*/
/* защиты от слишком умного оптимизирующего компилятора*/
/* * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int dummy_func (void) {
	char dummy_arr [LOCKED_STACK_SIZE];
	int i;
	int res = 0;

	dummy_arr [0] = 0;
	for (i = 1; i < LOCKED_STACK_SIZE; i++) {
		dummy_arr [i] = dummy_arr [i - 1] + i;
	}

	for (i = 0; i < LOCKED_STACK_SIZE; i++) {
		res += dummy_arr [i];
	}

	return (res);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * */
/* Функция main() вызывает вспомогательную функцию */
/* * * * * * * * * * * * * * * * * * * * * * * * * */
int main (void) {
	if (mlockall (MCL_CURRENT | MCL_FUTURE) != 0) {
		perror ("MLOCKALL");
		return (1);
	}

	fprintf (stderr, "Результат вспомогательной функции:"
		"%d\n", dummy_func ());

	return 0;
}
Листинг 5.17. Пример программы, удерживающей в памяти растущий стек.

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

< Лекция 4 || Лекция 5: 1234 || Лекция 6 >