Linux программирование в примерах
Рис. 3.1. Адресное пространство Linux/Unix
Хотя перекрывание стека и кучи теоретически возможно, операционная система предотвращает этот случай, и любая программа, пытающаяся это сделать, напрашивается на неприятности. Это особенно верно для современных систем, в которых адресные пространства большие и интервал между верхушкой стека и концом кучи значителен. Различные области памяти могут иметь различную установленную на память аппаратную защиту. Например, сегмент текста может быть помечен «только для исполнения», тогда как у сегментов данных и стека разрешение на исполнение может отсутствовать. Такая практика может предотвратить различные виды атак на безопасность. Подробности, конечно, специфичны для оборудования и операционной системы, и они могут со временем меняться. Стоит заметить, что стандартные как С, так и C++ позволяют размещать элементы с атрибутом
в памяти только для чтения. Сводка взаимоотношений различных сегментов приведена в табл. 3.1.constТаблица 3.1. Сегменты исполняемой программы и их размещение
Память программы Сегмент адресного пространства Секция исполняемого файла Код Text Text Инициализированные данные Data Data BSS Data BSS Куча Data Стек Stack Программа
распечатывает размеры в байтах каждой из секций text, data и BSS вместе с общим размером в десятичном и шестнадцатеричном виде. (Программаsizeпоказана далее в этой главе; см. раздел 3.2.5 «Исследование адресного пространства».)ch03-memaddr.с$ <b>cc -o ch03-memaddr.с -о ch03-memaddr</b> /* Компилировать программу */$ <b>ls -l ch03-memaddr</b> /* Показать общий размер */-rwxr-xr-x 1 arnold devel 12320 Nov 24 16:45 ch03-memaddr$ <b>size ch03-memaddr</b> /* Показать размеры компонентов */text data bss dec hex filename1458 276 8 1742 6ce ch03-memaddr$ <b>strip ch03-memaddr</b> /* Удалить символы */$ <b>ls -l ch03-memaddr</b> /* Снова показать общий размер */-rwxr-xr-x 1 arnold devel 3480 Nov 24 16:45 ch03-memaddr$ <b>size ch03-memaddr</b> /* Размеры компонентов не изменились */text data bss dec hex filename1458 276 8 1742 6ce ch03-memaddrОбщий размер загруженного в память из файла в 12 320 байтов всего лишь 1742 байта. Большую часть этого места занимают символы (symbols), список имен переменных и функций программы. (Символы не загружаются в память при запуске программы.) Программа
удаляет символы из объектного файла. Для большой программы это может сохранить значительное дисковое пространство ценой невозможности отладки дампа ядра [40], если таковой появится (На современных системах об этом не стоит беспокоиться, не используйтеstrip.) Даже после удаления символов файл все еще больше, чем загруженный в память образ, поскольку формат объектного файла содержат дополнительные данные о программе, такие, как использованные разделяемые библиотеки, если они есть. [41]stripНаконец, упомянем потоки (threads), которые представляют несколько цепочек исполнения в рамках единственного адресного пространства. Обычно у каждого потока имеется свой собственный стек, а также способ получения локальных данных потока, т.е. динамически выделяемых данных для персонального использования этим потоком. Мы больше не будем рассматривать в данной книге потоки, поскольку это является продвинутой темой.
3.2. Выделение памяти
Четыре библиотечные функции образуют основу управления динамической памятью С Мы опишем сначала их, затем последуют описания двух системных вызовов, поверх которых построены эти библиотечные функции. Библиотечные функции С, в свою очередь, обычно используются для реализации других выделяющих память библиотечных функций и операторов C++
иnew.deleteНаконец, мы обсудим функцию, которую часто используют, но которую мы не рекомендуем использовать.
3.2.1. Библиотечные вызовы:
,malloc(),calloc(),realloc()free()Динамическую память выделяют с помощью функций
илиmalloc(). Эти функции возвращают указатели на выделенную память. Когда у вас есть блок памяти определенного первоначального размера, вы можете изменить его размер с помощью функцииcalloc(). Динамическая память освобождается функциейrealloc().free()Отладка использования динамической памяти сама по себе является важной темой. Инструменты для этой цели мы обсудим в разделе 15.5.2 «Отладчики выделения памяти».
3.2.1.1. Исследование подробностей на языке С
Вот объявления функций из темы справки GNU/Linux malloc(3):
#include <stdlib.h> /* ISO С */void *calloc(size_t nmemb, size_t size);/* Выделить и инициализировать нулями */void *malloc(size_t size);/* Выделить без инициализации */void free(void *ptr);/* Освободить память */void *realloc(void *ptr, size_t size);/* Изменить размер выделенной памяти */Функции выделения памяти возвращают тип
. Это бестиповый или общий указатель, все, что с ним можно делать — это привести его к другому типу и назначить типизированному указателю. Примеры впереди.void*Тип
является беззнаковым целым типом, который представляет размер памяти. Он используется для динамического выделения памяти, и далее в книге мы увидим множество примеров его использования. На большинстве современных системsize_t_t являетсяsize, но лучше явно использоватьunsigned longвместо простого целого типаsize_t.unsigned
