Сегмент .data
Сегмент .data — это область памяти в исполняемом файле программы, предназначенная для хранения глобальных и статических переменных, которые имеют явно заданное начальное значение, отличное от нуля. В контексте архитектуры компьютерных программ и операционных систем сегмент .data является частью модели памяти процесса, наряду с сегментами кода (.text), стека, кучи и сегментом .bss (для неинициализированных данных). Он относится к статической памяти, то есть его размер и содержимое фиксируются на этапе компиляции и не изменяются в процессе выполнения программы (за исключением случаев, когда переменные объявлены как изменяемые, например, без ключевого слова const в языках C/C++).
История и происхождение
Концепция сегментированной памяти восходит к ранним архитектурам вычислительных машин, таким как IBM System/360 (1964 год), где программа делилась на логические блоки: код, константы и данные. В операционных системах Unix и MS-DOS эта модель была адаптирована для упрощения управления памятью и повышения безопасности. Формат исполняемых файлов (например, COFF, ELF в Unix-подобных системах, PE в Windows) включает в себя секции, каждая из которых имеет определённые атрибуты (доступ на чтение, запись, выполнение). Сегмент .data обычно имеет атрибуты чтения и записи, что позволяет изменять значения переменных во время работы программы, в отличие от сегмента .rodata (только для чтения), где хранятся константы.
В ранних версиях MS-DOS сегмент .data был частью модели памяти с разделением на сегменты кода и данных, что ограничивало размер программы 64 КБ на сегмент. С появлением 32-разрядных процессоров и плоской модели памяти (flat memory model) в Windows и Linux сегментация стала виртуальной: физически все сегменты отображаются в единое адресное пространство процесса, но логически разделяются для защиты.
Структура и расположение
Сегмент .data располагается в адресном пространстве процесса после сегмента кода (.text) и перед сегментом .bss. В исполняемом файле (например, ELF или PE) он представляет собой отдельную секцию, содержимое которой копируется в оперативную память при загрузке программы. Размер сегмента .data определяется на этапе компиляции и линковки и не может быть изменён динамически.
Внутренняя структура сегмента .data представляет собой последовательность байтов, в которой размещаются переменные в порядке их объявления в исходном коде (с учётом выравнивания по границам, кратным размеру типа данных). Каждая переменная занимает фиксированное количество байт (например, 4 байта для int, 8 байт для double). Начальные значения этих переменных хранятся непосредственно в файле и загружаются в память при запуске.
Отличие от сегмента .bss
Сегмент .bss (Block Started by Symbol) предназначен для глобальных и статических переменных, которые не имеют явного начального значения (инициализируются нулём по умолчанию). В отличие от .data, сегмент .bss не занимает места в исполняемом файле — в нём хранится только информация о размере и адресах переменных. При загрузке программы операционная система выделяет память под .bss и обнуляет её. Это позволяет экономить дисковое пространство, особенно для больших массивов, инициализированных нулями.
Примеры использования
В языках программирования C и C++ переменные, объявленные вне функций (глобальные) или с ключевым словом static (статические), помещаются в сегмент .data, если они явно инициализированы ненулевым значением. Например:
``c int global_var = 42; // Помещается в .data static int static_var = 100; // Помещается в .data const int const_var = 7; // Помещается в .rodata (только для чтения) int uninit_var; // Помещается в .bss (инициализируется нулём) ``
В языках ассемблера (например, x86-64) сегмент .data задаётся директивой .data в синтаксисе GNU Assembler (GAS) или DATA SEGMENT в MASM. Пример на GAS:
``assembly .section .data my_var: .long 0x12345678 ``
В операционных системах семейства Linux сегмент .data обычно имеет права доступа rw- (чтение и запись, без выполнения). Это предотвращает случайное или злонамеренное выполнение данных как кода, что является одной из мер защиты от эксплойтов (например, от переполнения буфера).
Применение в различных архитектурах
В операционных системах
В современных ОС (Linux, Windows, macOS) сегмент .data является частью образа процесса. При запуске программы загрузчик (например, ld.so в Linux или загрузчик PE в Windows) считывает секцию .data из файла, выделяет для неё память в виртуальном адресном пространстве процесса и копирует туда начальные значения. В многозадачных системах каждый процесс имеет собственную копию сегмента .data, так как он содержит изменяемые данные, специфичные для данного экземпляра программы.
Во встраиваемых системах
Во встраиваемых системах (например, на микроконтроллерах ARM Cortex-M) сегмент .data часто размещается в оперативной памяти (SRAM), но его начальные значения хранятся во флеш-памяти (в образе прошивки). При старте системы код инициализации (обычно в startup-файле) копирует данные из флеш-памяти в SRAM. Это связано с тем, что флеш-память медленнее и имеет ограниченный ресурс записи, а SRAM энергозависима и теряет данные при выключении питания.
В виртуальных машинах и интерпретаторах
В языках с управляемой памятью (Java, C#, Python) понятие сегмента .data как отдельной области памяти не используется напрямую, так как управление памятью осуществляется сборщиком мусора. Однако статические поля классов (например, static int count = 10; в Java) хранятся в области статической памяти, которая функционально аналогична сегменту .data в нативных программах.
Безопасность и оптимизация
Сегмент .data может быть целью атак, связанных с переполнением буфера или изменением глобальных переменных. Для защиты применяются методы рандомизации адресного пространства (ASLR) и запрет на исполнение данных (NX-бит). В некоторых компиляторах (например, GCC) существуют атрибуты для размещения переменных в определённых секциях, что позволяет разработчикам оптимизировать использование памяти.
Оптимизация размера сегмента .data достигается за счёт:
- Объединения констант: компилятор может объединять одинаковые строковые литералы или числовые константы.
- Выноса инициализированных данных в .rodata: если переменная не изменяется, компилятор может поместить её в сегмент только для чтения.
- Использования .bss: для переменных, инициализированных нулём, предпочтительно использовать неинициализированные данные.
Критика и ограничения
Основным ограничением сегмента .data является его статический размер, определённый на этапе компиляции. Это может приводить к неэффективному использованию памяти, если программа использует большие массивы данных, размер которых неизвестен заранее. В таких случаях предпочтительнее динамическое выделение памяти (куча). Кроме того, в многопоточных программах глобальные переменные в .data требуют синхронизации (мьютексы, атомарные операции), что может снижать производительность.
В некоторых архитектурах (например, в ранних версиях MS-DOS) сегмент .data имел ограничение в 64 КБ из-за 16-разрядной адресации, что вынуждало разработчиков использовать модели памяти с несколькими сегментами данных (например, medium или large модели в компиляторах Borland C++).
Интересные факты
- В формате ELF секция .data может иметь несколько алиасов:
.data.rel.ro(данные, которые после инициализации становятся только для чтения) и.data.rel.local(локальные данные). - В некоторых компиляторах (например, GCC) существует атрибут
__attribute__((section(".data"))), позволяющий явно указать размещение переменной в сегменте .data. - В операционной системе Linux содержимое сегмента .data можно просмотреть с помощью утилиты
readelf -S <исполняемый файл>, которая показывает размер, смещение и права доступа каждой секции.
Источники
- Брайан У. Керниган, Деннис М. Ритчи. «Язык программирования C». 2-е издание. — М.: Вильямс, 2009.
- Рэндал Хайд. «Искусство ассемблера». — СПб.: Питер, 2005.
- Документация GNU Assembler (GAS): раздел «Assembler Directives».
- Спецификация формата ELF (Executable and Linkable Format), версия 1.2, TIS Committee, 1995.
- Документация Microsoft PE Format: раздел «Section Table».
BFOmetr — база данных и аналитика по компаниям России.
На главную BFOmetr →