Вообще говоря, стеков в процессе может быть много и размещаться они могут в разных областях виртуальной памяти. Вот несколько примеров:
Он же стек процесса в однопоточном приложении. Начальный адрес выделяется ядром. Размер стека может быть изменён в командной строке до запуска программы командой ulimit -s <размер в килобайтах>
или вызовом setrlimit
#include <sys/resource.h>
...
struct rlimit rl;
int err;
rl.rlim_cur = 64*1024*1024;
err = setrlimit(RLIMIT_STACK, &rl);
Стек обработчика сигналов может быть расположен в любой области памяти, доступной на чтение и запись. Альтернативный стек создаётся вызовом sigaltstack(new, old), которому передаются указатели на структуры, описывающие стек:
typedef struct {
void *ss_sp; /* Base address of stack */
int ss_flags; /* Flags */
size_t ss_size; /* Number of bytes in stack */
} stack_t;
После создания альтернативного стека можно задавать обработчик сигнала, указав в параметрах функции sigaction() флаг SS_ONSTACK.
При создании новой нити для всегда создаётся новый стек. Функция инициализации нити pthread_attr_init(pthread_attr_t *attr) позволяет вручную задать базовый адрес стека нити и его размер через поля attr.stackaddr и attr.stacksize. В большинстве случаев рекомендуется предоставить выбор адреса и размера стека системе, задав attr.stackaddr=NULL; attr.stacksize=0;.
Динамическое выделение памяти в куче (heap) реализовано на уровне стандартных библиотек C/C++ (функция malloc() и оператор new соответственно). Для распределения памяти из кучи процесс должен сообщить ядру, какой размер виртуальной памяти должен быть отображён на физическую память. Для этого выделяется участок виртуальной памяти, расположенный между адресами start_brk и brk. Величина start_brk фиксирована, а brk может меняться в процессе выполнения программы. Brk (program break - обрыв программы) - граница в виртуальной памяти на которой заканчивается отображение в физическую память. В современном Linux за этой границей могут быть отображения файлов и кода ядра в память процесса, но в оригинальном Unix это был "край" памяти программы. Начальное значение brk - start_brk устанавливается в момент загрузки программы из файла вызовом execve() и указывает на участок после инициализированных (data) и неинициализированных (BSS) глобальных переменных.
Для того, чтобы изменить размер доступной физической памяти, необходимо изменить верхнюю границу области кучи.
#include <unistd.h>
int brk(void *addr); //явное задание адреса
void *sbrk(intptr_t increment); //задание адреса относительно текущего значения
// возвращает предыдущее значение адреса границы
Вызов brk()
устанавливает максимальный адрес виртуальной памяти, для которого в сегменте данных выделяется физическая память. Увеличение адреса равноценно запросу физической памяти, уменьшение - освобождению физической памяти.
Вызов sbrk(0)
позволяет узнать текущую границу сегмента памяти.
В Linux вызов brk() транслируется в вызов функции ядра do_mmap(), изменяющий размер анонимного файла, отображаемого в память.
do_mmap(NULL, oldbrk, newbrk-oldbrk,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FIXED|MAP_PRIVATE, 0)
Отображение содержимого файла в виртуальную память mmap()
задействует кэш файловой системы. Некоторые страницы виртуальной памяти отображаются на физические страницы кэша. В тех ситуациях, когда обычные физические страницы сохраняются в своп, страницы, отображенные в файлы сохраняются в сами файлы. Отображение файлов в память может использоваться, в частности, для отображения секций кода исполняемых файлов и библиотек, в соответствующие сегменты памяти процесса.
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
int msync(void *addr, size_t length, int flags);
int munmap(void *addr, size_t length);
Функция mmap()
отображает length
байтов, начиная со смещения offset
файла, заданного файловым дескриптором fd
, в память, начиная с адреса addr
. Параметр addr является рекомендательным, и обычно бывает выставляется в NULL. mmap()
возвращает фактический адрес отображения или значение MAP_FAILED (равное (void *) -1) в случае ошибки.
Аргумент prot
описывает режим доступа к памяти (не должен конфликтовать с режимом открытия файла).
Параметр flags указывает, видны ли изменения данных в памяти другим процессам, отобразившим этот фрагмент файла в свою память, и влияют ли эти изменения на сам файл.
msync()
или munmap()
.addr
или возвращается ошибка. Не рекомендуется к использованию, так как сильно зависит от архитектуры процессора и конкретной ОС.msync()
сбрасывает изменения сделанные в памяти в отображенный файл. Параметры addr
и length
позволяют синхронизировать только часть отображенной памяти.
munmap()
отменяет отображение файла в память и сохраняет сделанные в памяти изменения в файл.
В подсистеме межпроцессного взаимодействия System V IPC (System five interprocess communication - названо в по имени версии Unix, в которой эта подсистема появилась) для совместной работы с памятью используется резервирование физической памяти на уровне ядра. После резервирования процессы могут отображать зарезервированную память в своё виртуальное адресное пространство используя для идентификации зарезервированного участка идентификатор, генерируемый специальным образом.
Данное резервирование не привязано к какому-либо процессу и сохраняется даже тогда, когда ни один процесс эту физическую память не использует. Таким образом выделение памяти для совместного использования становится "дорогим" с точки зрения использованных ресурсов. Программа, резервирующая области памяти для совместного доступа и не освобождающая их, может либо исчерпать всю доступную память, либо занять максимально доступное в системе количество таких областей.
У областей совместно используемой памяти, как и других объектов System V IPC есть атрибуты "пользователь-владелец", "группа-владельца", "пользователь-создатель", "группа-создателя", а так же права на чтение и запись для владельца, группы-владельца и остальных, аналогичные файловым. Например: rw- r-- ---.
Пример вызовов для работы общей памятью:
// создание ключа на основе inode файла и номера проекта
// файл не ложен удаляться до завершения работы с общей памятью
key_t key=ftok("/etc/hostname", 456);
// получение идентификатора общей памяти на основе ключа
// размером size байт с округлением вверх
// до размера кратного размеру страницы
//
// опция IPC_CREAT - говорит, что если память ещё не зарезервирована
// то должна быть выполнена резервация физической памяти
size=8000
int shmid=shmget(key, size, IPC_CREAT);
// подключение зарезервированной памяти к виртуальному адресному пространству
// второй параметр - желаемый начальный адрес отображения
// третий параметр - флаги, такие как SHM_RDONLY
int *addr=(int *)shmat(shmid, NULL, 0);
// можно работать с памятью по указателю
addr[10]=23;
// отключение разделяемой памяти от виртуального адресного пространства
int err;
err=shmdt(addr);
// освобождение зарезервированной памяти
err=shmctl(shmid, IPC_RMID, NULL);
Список всех зарезервированных областей памяти в системе можно просмотреть командой lspci -m
:
lsipc -m
KEY ID PERMS OWNER SIZE NATTCH STATUS CTIME CPID LPID COMMAND
0xbe130fa1 3112960 rw------- root 1000B 11 May03 7217 9422 /usr/sbin/httpd -DFOREGROUND
0x00000000 557057 rw------- usr74 384K 2 dest Apr28 17752 7476 kdeinit4: konsole [kdeinit]
0x00000000 5898243 rw------- usr92 512K 2 dest 12:05 5265 9678 /usr/bin/geany /home/s0392/1_1.s
0x00000000 4521988 rw------- usr75 384K 2 dest May06 22351 16323 sview
0x00000000 3276805 rw------- usr15 384K 1 dest May05 24835 15236
0x00000000 4587530 rw------- usr75 2M 2 dest May06 19404 16323 metacity
Выделение физической памяти в Linux оптимистично. В момент вызова brk() проверяется лишь то факт, что заказанная виртуальная память не превышает общего объёма физической памяти + размер файла подкачки. Реальное выделение памяти происходит при первой записи. В этом случае может оказаться, что вся доступная физическая память и своп уже распределены между другими процессами.
При нехватке физической памяти Linux запускает алгоритм Out of memory killer (OOM killer) который относительно случайно выбирает один из процессов и завершает его, освобождая тем самым физическую память.