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 использует подход, известный как
•
•
В результате локальности ссылок появляется возможность выполнять программу, располагая в оперативной памяти лишь часть ее адресного пространства.