Перейти к содержанию

Тэгированный указатель

Статья из Авикипедии. Энциклопедии

Тэгированный указатель — это тип данных, представляющий собой адрес памяти, дополненный связанной с ним информацией, такой как метки управления памятью или счётчики ссылок. Дополнительные данные, называемые тэгами, обычно встраиваются непосредственно в представление указателя, используя особенности адресного пространства. Термин происходит из систем с тэговой архитектурой, где часть битов каждого слова памяти аппаратно зарезервирована для указания его назначения. Хотя строго «тэг» означает данные, определяющие тип, в широком смысле термин «тэгированный указатель» применяется к любым дополнительным данным, ассоциированным с адресом.

    1. Встраивание тэгов в указатель

Существует несколько методов включения тэгов в указатели. Во многих архитектурах наименьшим адресуемым элементом является байт, однако данные часто выравниваются по границам машинного слова или кратных им размеров. Это создаёт неиспользуемые младшие биты в адресе, которые можно задействовать под тэги, обычно в виде битовых полей. При этом код, работающий с указателем, должен применять битовые маски перед обращением к памяти. Например, в 32-битных системах слово занимает 4 байта, поэтому выровненные адреса кратны 4, освобождая два младших бита. В 64-битных архитектурах слово составляет 8 байт, что даёт три свободных бита. Если выравнивание кратно размеру слова, доступно ещё больше битов. В системах с байтовой адресацией, но без выравнивания, свободные биты отсутствуют.

В некоторых операционных системах виртуальные адреса имеют меньшую разрядность, чем шина данных, что оставляет старшие биты для тэгов. Этот подход можно комбинировать с использованием младших битов от выравнивания. Особенно актуально это для 64-битных архитектур, где 64 бита избыточны для большинства приложений. Многие современные 64-битные процессоры работают с адресами уменьшенной ширины. Стоит отметить, что разрядность виртуального адреса может быть меньше физического, который, в свою очередь, может быть уже архитектурного. Некоторые процессоры, например x86-64, запрещают тегированные указатели, требуя канонической формы адреса со старшими битами, равными 0 или 1.

Кроме того, системы виртуальной памяти в современных ОС резервируют область около адреса 0 как неиспользуемую. Это позволяет использовать нулевое значение как специальный указатель null. В отличие от предыдущих методов, здесь доступно только одно специальное значение, а не дополнительные данные для любого указателя.

    1. Примеры использования

Одним из первых коммерческих примеров аппаратной поддержки тэгированных указателей стала IBM System/38. Позднее IBM включила эту функциональность в архитектуру PowerPC для совместимости с ОС IBM i, наследницей System/38.

Значительный пример — среда выполнения Objective-C в iOS 7 на архитектуре AArch64 (например, в iPhone 5S). Здесь виртуальные адреса используют 33 бита, оставляя 31 бит из 64 для тэгов. Указатели на классы Objective-C выровнены по 8 байтам, освобождая ещё 3 бита. Эти битовые поля применяются для хранения счётчиков ссылок и информации о деструкторах объектов.

В ранних версиях macOS использовались тэгированные указатели на объекты данных, называемые Handles. Старшие биты указывали, заблокирован ли объект, выгружен или загружен из ресурсного файла. Это вызвало проблемы совместимости при переходе с 24-битной на 32-битную адресацию в System 7.

    1. Null-указатели и выровненные указатели

Использование нуля для обозначения null-указателя широко распространено, и многие языки программирования, такие как Ада, явно на это опираются. Теоретически, другие значения из зарезервированной области памяти могли бы обозначать особые состояния, но на практике это встречается редко из-за сложностей переносимости. В программной инженерии принято, что если требуется специальное значение, отличное от null, программист должен явно его определить.

Выравнивание указателей предоставляет больше гибкости, чем null-значения, позволяя маркировать указатели информацией о типе данных, условиях доступа или иными метками. Это даёт возможность сопровождать каждый допустимый указатель дополнительными данными. В тэговых архитектурах, таких как Лисп-машины, аппаратно поддерживается интерпретация и обработка тэгированных указателей.

Функция `malloc()` в glibc возвращает адреса, выровненные по 8 байтам для 32-битных платформ и по 16 байтам для 64-битных. Большее выравнивание можно получить с помощью `posix_memalign()`.

    1. Примеры кода
      1. Пример 1

В этом примере на языке C нулевое значение используется для обозначения null-указателя:

```c void optionally_return_a_value (int* optional_return_value_pointer) {

 /* ... */
 int value_to_return = 1;
 /* Проверка на не-NULL (в C NULL, логическое false и ноль эквивалентны) */
 if (optional_return_value_pointer)
   /* Если указатель действителен, передаём значение */
   *optional_return_value_pointer = value_to_return;
 /* В противном случае указатель не разыменовывается */

} ```

      1. Пример 2

Здесь адрес глобальной переменной используется как сторожевой указатель:

```c

  1. define SENTINEL &sentinel_s

node_t sentinel_s;

void do_something_to_a_node (node_t * p) {

 if (NULL == p)
   /* Действие для null-указателя */
 else if (SENTINEL == p)
   /* Действие для сторожевого значения */
 else
   /* Обработка действительного указателя на узел */

} ```

      1. Пример 3

Предположим, структура данных `table_entry` выровнена по 16 байтам, поэтому младшие 4 бита указателей всегда нулевые. Эти биты можно использовать для хранения дополнительной информации: например, бит 0 — «только для чтения», бит 1 — «dirty» (требуется обновление). Для 16-битных указателей: - `0x3421` — указатель «только для чтения» на `table_entry` по адресу `0x3420` - `0xf472` — указатель «dirty» на `table_entry` по адресу `0xf470`

    1. Преимущества

Основное достоинство тэгированных указателей — экономия памяти, так как тэг хранится вместе с указателем, а не в отдельном поле. Это особенно важно, когда указатель является возвращаемым значением функции или при работе с крупными таблицами указателей.

Ещё одно преимущество — возможность атомарного обновления указателя и его тэга без дополнительных механизмов синхронизации, что повышает производительность, особенно в операционных системах.

    1. Недостатки

Тэгированные указатели имеют общие проблемы с такими структурами, как XOR-связные списки. Например, не все отладчики корректно работают с тэгированными указателями, хотя специализированные отладчики лишены этого недостатка.

Использование нуля для null-указателя лишено этих проблем, универсально и поддерживается большинством языков программирования. Исключение — перегрузка функций в C++, где ноль интерпретируется как целое число, а не указатель, поэтому предпочтительнее использовать `nullptr`. В системах с тэгированными указателями ноль обычно не применяется для обозначения null.

    1. Примечания
    1. Литература

Категория:Программные конструкции Категория:Указатели (программирование)

Ссылки[править | править код]