Память, выделяемая каждому процессу, состоит из нескольких частей, которые обычно называют сегментами. К числу таких сегментов относятся следующие:
1. Текстовый сегмент (.text)
Cодержит машинный код, который принадлежит программе, запущенной процессом. Текстовый сегмент создается только для чтения, чтобы процесс не мог случайно изменить свои собственные инструкции (из-за read-only статуса, некоторые компиляторы могут записывать туда константы).
Поскольку многие процессы могут выполнять одну и ту же программу, текстовый сегмент создается с возможностью совместного использования - единственная копия кода программы может быть отображена на виртуальное адресное пространство всех процессов.
2. Сегмент инициализированных данных (.data)
Хранит глобальные и статические переменные, инициализированные явным образом. Значения этих переменных считываются из исполняемого файла при загрузке программы в память.
3. Сегмент неинициализированных данных (.bss - block started by symbol)
Содержит глобальные и статические переменные, не инициализированные явным образом. Перед запуском программы система определяет всю память в этом сегменте значением 0.
Основная причина помещения прошедших инициализацию переменных в отдельный от неинициализированных переменных сегмент заключается в том, что, когда программа сохраняется на диске, нет никакого смысла выделять пространство под неинициализированные данные.
Вместо этого исполняемой программе просто нужно записать местоположение и размер, требуемый для сегмента неинициализированных данных, и это пространство выделяется загрузчиком программы в ходе ее выполнения.
4. Динамически увеличивающийся и уменьшающийся сегмент стека (stack)
Содержит стековые фреймы: для каждой отдельной функции выделяется один стековый фрейм, в котором хранятся ее локальные переменные, аргументы и возвращаемое значение. По мере вызова функций и возврата из них стек расширяется и сжимается.
Текущая вершина стека отслеживается в специально предназначенном для этого регистре - указателе стека. Процесс сжатия стека называется расруткой.
5. Динамическая память (heap)
Область, которая предназначена для динамического выделения памяти в ходе выполнения программы (malloc, calloc, free, realloc - под капотом используют системные вызовы brk и sbrk).
6. Сегмент аргументов командной строки
Сегмент для хранения переменных, которые переданы программе в качестве аргументов (argc и argv[]), где argc хранит количество переданных аргументов, а argv хранит значение фактических значений вместе с именем файла:
~$ ./prog 100 23 43 69
Далее продемонстрированы различные типы переменных в коде и комментарии, указывающие на сегменты их размещения (в случае оптимизаций, часто используемые переменные могут быть помещены в регистры или вообще исключены):
/* Сегмент неинициализированных данных */
char globBuf[65536];
/* Сегмент инициализированных данных */
int primes[] = { 2, 3, 5, 7 };
/* Размещается в фрейме для square() */
static int square(int x)
{
/* Размещается в фрейме для square() */
int result;
result = x * x;
/* Возвращаемое значение передается через регистр */
return result;
}
/* Размещается в фрейме для doCalc() */
static void doCalc(int val)
{
square(val)
if (val < 1000) {
/* Размещается в фрейме для doCalc() */
int t;
t = val * val * val;
}
}
/* Размещается в фрейме для main() */
int main(int argc, char *argv[])
{
printf("File name = %s\n", argv[0]);
printf("Number of arguments = %d\n", argc - 1);
/* Сегмент инициализированных данных */
static int key = 9973;
/* Сегмент неинициализированных данных */
static char mbuf[10240000];
/* Размещается в фрейме для main() */
char *p;
/* Указывает на память в сегменте кучи */
p = malloc(1024);
doCalc(key);
}
В комменты я отправил несколько изображений для визуализации материала.
#linuxcore #linux #cpp #cppcore