int result; /* Размещается в фрейме для square() */

result = x * x;

return result; /* Возвращаемое значение передается через регистр */

}

static void

doCalc(int val) /* Размещается в фрейме для doCalc() */

{

printf("The square of %d is %d\n", val, square(val));

if (val < 1000) {

int t; /* Размещается в фрейме для doCalc() */

t = val * val * val;

printf("The cube of %d is %d\n", val, t);

}

}

int

main(int argc, char *argv[]) /* Размещается в фрейме для main() */

{

static int key = 9973; /* Сегмент инициализированных данных */

static char mbuf[10240000]; /* Сегмент неинициализированных данных */

char *p; /* Размещается в фрейме для main() */

p = malloc(1024); /* Указывает на память в сегменте кучи */

doCalc(key);

exit(EXIT_SUCCESS);

}

proc/mem_segments.c

Двоичный интерфейс приложений — Application Binary Interface (ABI) представляет собой набор правил, регулирующих порядок обмена информацией между двоичной исполняемой программой в ходе ее выполнения и каким-либо сервисом (например, ядром или библиотекой). Помимо всего прочего, ABI определяет, какие регистры и места в стеке используются для обмена этой информацией и какой смысл придается обмениваемым значениям. Программа, единожды скомпилированная в соответствии с требованием некоторого ABI, должна запускаться в любой системе, предоставляющей точно такой же ABI. Это отличается от стандартизированного API (например, SUSv3), гарантирующего портируемость только для приложений, скомпилированных из исходного кода.

Хотя это и не описано в SUSv3, среда программы на языке C во многих реализациях UNIX (включая Linux) предоставляет три глобальных идентификатора: etext, edata и end. Они могут использоваться из программы для получения адресов следующего байта соответственно за концом текста программы, за концом сегмента инициализированных данных и за концом сегмента неинициализированных данных. Чтобы воспользоваться этими идентификаторами, их нужно явным образом объявить:

extern char etext, edata, end;

/* К примеру, &etext сообщает адрес первого байта после окончания

текста программы/начала инициализированных данных */

На рис. 6.1 показано расположение различных сегментов памяти в архитектуре x86-32. Пространство с пометкой argv, охватывающее верхнюю часть этой схемы, содержит аргументы командной строки программы (которые в C доступны через аргумент argv функции main()) и список переменных среды процесса (который вскоре будет рассмотрен). Шестнадцатеричные адреса, приведенные в схеме, могут варьироваться в зависимости от конфигурации ядра и ключей компоновки программы. Области, закрашенные серым цветом, представляют собой недопустимые диапазоны в виртуальном адресном пространстве процесса, то есть области, для которых не созданы таблицы страниц (см. далее раздел, посвященный управлению виртуальной памятью).

Рис. 6.1.Типичная структура памяти процесса в Linux/x86-32

6.4. Управление виртуальной памятью

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

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

• Пространственная локальность, которая характеризуется присущей программам тенденцией ссылаться на адреса памяти, близкие к тем, к которым недавно обращались (из-за последовательного характера обработки инструкций и иногда последовательного характера обработки структур данных).

• Локальность по отношению ко времени — характеризуется свойственной программам тенденцией обращаться к тем же адресам памяти в ближайшем будущем, к которым обращение уже было в недавнем прошлом (из-за использования циклов).

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

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

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