В вычислительной технике сегмент данных (часто обозначаемый как .data ) — это часть объектного файла или соответствующего адресного пространства программы, которая содержит инициализированные статические переменные , то есть глобальные переменные и статические локальные переменные . Размер этого сегмента определяется размером значений в исходном коде программы и не изменяется во время выполнения .
Сегмент данных доступен для чтения/записи, поскольку значения переменных могут быть изменены во время выполнения. Это отличается от сегмента данных, доступного только для чтения (сегмент rodata или.rodata), который содержит статические константы, а не переменные; он также контрастирует ссегментом кода, также известным как текстовый сегмент, который доступен только для чтения на многих архитектурах. Неинициализированные данные, как переменные, так и константы, находятся всегменте BSS.
Исторически сложилось так, что для поддержки адресных пространств памяти, превышающих собственный размер внутреннего адресного регистра, ранние процессоры реализовали систему сегментации, при которой они хранили небольшой набор индексов для использования в качестве смещений для определенных областей. Семейство процессоров Intel 8086 предоставляло четыре сегмента: сегмент кода, сегмент данных, сегмент стека и дополнительный сегмент. Каждый сегмент помещался в определенное место в памяти исполняемым программным обеспечением, и все инструкции, которые работали с данными в этих сегментах, выполнялись относительно начала этого сегмента. Это позволяло 16-битному адресному регистру, который обычно мог бы получить доступ к 64 КБ пространства памяти, получить доступ к 1 МБ пространства памяти.
Такое сегментирование пространства памяти на отдельные блоки с конкретными задачами перешло в языки программирования того времени, и эта концепция до сих пор широко используется в современных языках программирования.
Память компьютерной программы можно в значительной степени разделить на две секции: только для чтения и для чтения/записи. Это различие возникло из ранних систем, хранящих свою основную программу в памяти только для чтения, такой как Mask ROM , EPROM , PROM или EEPROM . По мере того, как системы становились более сложными, и программы загружались с других носителей в RAM вместо выполнения из ROM, идея о том, что некоторые части памяти программы не должны изменяться, сохранялась. Они стали сегментами .text и .rodata программы, а остальная часть, которая могла быть записана, делилась на ряд других сегментов для определенных задач.
Сегмент кода , также известный как текстовый сегмент , содержит исполняемый код и, как правило, доступен только для чтения и имеет фиксированный размер.
Сегмент данных содержит инициализированные статические переменные, т.е. глобальные переменные и локальные статические переменные, которые имеют определенное значение и могут быть изменены. Примеры в C включают:
int i = 3 ; char a [] = "Hello World" ; static int b = 2023 ; // Инициализированная статическая глобальная переменная void foo ( void ) { static int c = 2023 ; // Инициализированная статическая локальная переменная }
Сегмент BSS содержит неинициализированные статические данные, как переменные, так и константы, т.е. глобальные переменные и локальные статические переменные, которые инициализируются нулем или не имеют явной инициализации в исходном коде. Примеры на языке C включают:
статический int i ; статический char a [ 12 ];
Сегмент кучи содержит динамически выделенную память, обычно начинается в конце сегмента BSS и оттуда увеличивается до больших адресов. Он управляется malloc , calloc , realloc и free , которые могут использовать системные вызовы brk и sbrk для регулировки его размера (обратите внимание, что использование brk/sbrk и одного сегмента кучи не требуется для выполнения контракта malloc/calloc/realloc/free; они также могут быть реализованы с помощью mmap /munmap для резервирования/отмены резервирования потенциально несмежных областей виртуальной памяти в виртуальное адресное пространство процесса ). Сегмент кучи совместно используется всеми потоками, разделяемыми библиотеками и динамически загружаемыми модулями в процессе.
Сегмент стека содержит стек вызовов , структуру LIFO , обычно расположенную в верхних частях памяти. Регистр «указатель стека» отслеживает вершину стека; он корректируется каждый раз, когда значение «помещается» в стек. Набор значений, помещаемых для одного вызова функции, называется «фреймом стека». Фрейм стека состоит как минимум из адреса возврата. Автоматические переменные также размещаются в стеке.
Сегмент стека традиционно примыкал к сегменту кучи, и они росли по направлению друг к другу; когда указатель стека встречался с указателем кучи, свободная память исчерпывалась. При больших адресных пространствах и методах виртуальной памяти они, как правило, размещаются более свободно, но они все равно обычно растут в сходящемся направлении. В стандартной архитектуре ПК x86 стек растет по направлению к нулевому адресу, что означает, что более поздние элементы, расположенные глубже в цепочке вызовов, находятся по численно более низким адресам и ближе к куче. В некоторых других архитектурах он растет в противоположном направлении.
Некоторые интерпретируемые языки предлагают схожую возможность для сегмента данных, в частности Perl [1] и Ruby . [2] В этих языках включение строки __DATA__
(Perl) или __END__
(Ruby, старый Perl) отмечает конец сегмента кода и начало сегмента данных. Выполняется только содержимое до этой строки, а содержимое исходного файла после этой строки доступно как файловый объект: PACKAGE::DATA
в Perl (например, main::DATA
) и DATA
в Ruby. Это можно считать формой документа here (файловый литерал).