Кажется, довольно ясно, что он должен все настроить.
- Когда именно он запускается?
- Почему есть две скобки?
- Это
__attribute__
функция? Макрос? Синтаксис? - Это работает в C? C ++?
- Должна ли функция, с которой она работает, быть статичной?
- Когда
__attribute__((destructor))
бежит?
__attribute__((constructor))
static void initialize_navigationBarImages() {
navigationBarImages = [[NSMutableDictionary alloc] init];
}
__attribute__((destructor))
static void destroy_navigationBarImages() {
[navigationBarImages release];
}
источник
#define __attribute__(x)
). Если у вас есть несколько атрибутов, например,__attribute__((noreturn, weak))
было бы трудно "вычеркнуть" макрос, если бы был только один набор скобок..init/.fini
. (Вы можете действительно иметь несколько конструкторов и деструкторов в одном модуле перевода, не обращая внимания на несколько в одной библиотеке - как это будет работать?) Вместо этого на платформах, использующих двоичный формат ELF (Linux и т. Д.), На конструкторы и деструкторы ссылаются в.ctors
и.dtors
секциях заголовка. Правда, в прежние времена функции с именамиinit
иfini
выполнялись при динамической загрузке и выгрузке библиотеки, если бы они существовали, но сейчас это устарело, заменив этот более совершенный механизм.__attribute__
- это то, что вы не используете gcc, поскольку это тоже расширение gcc..init
/.fini
не считается устаревшим Это все еще часть стандарта ELF, и я бы сказал, что так будет всегда. Код в.init
/.fini
запускается загрузчиком / компоновщиком времени выполнения, когда код загружается / выгружается. Т.е. при каждом загрузке ELF (например, совместно используемой библиотеки).init
будет выполняться код . Все еще возможно использовать этот механизм для достижения того же, что и с__attribute__((constructor))/((destructor))
. Это старая школа, но у нее есть некоторые преимущества..ctors
/.dtors
Механизм, например, требует поддержки system-rtl / loader / linker-script. Это далеко не обязательно будет доступно во всех системах, например, глубоко встроенных системах, где код выполняется на голом железе. Т.е., даже если__attribute__((constructor))/((destructor))
GCC поддерживается, он не уверен, что он будет работать, поскольку компоновщик может его организовать, а загрузчик (или, в некоторых случаях, загрузочный код) его запустит. Чтобы использовать.init
/.fini
вместо этого, самый простой способ - использовать флаги компоновщика: -init & -fini (то есть из командной строки GCC синтаксис будет-Wl -init my_init -fini my_fini
).В системе, поддерживающей оба метода, одним из возможных преимуществ является то, что код in
.init
запускается до,.ctors
а код -.fini
после.dtors
. Если порядок важен, это как минимум один грубый, но простой способ различить функции инициализации / выхода.Основным недостатком является то, что вы не можете легко иметь более одной
_init
и одной_fini
функции на каждый загружаемый модуль и, вероятно, придется фрагментировать код более.so
чем мотивированным. Другое состоит в том, что при использовании метода компоновщика, описанного выше, один заменяет оригинальный _init и_fini
функции по умолчанию (предоставленныеcrti.o
). Это где все виды инициализации обычно происходят (в Linux это где глобальное назначение переменных инициализируется). Обход, который описан здесьОбратите внимание на ссылку выше, что каскадирование к оригиналу
_init()
не нужно, так как он все еще на месте. Однакоcall
встроенная сборка является x86-мнемонической, и вызов функции из сборки выглядел бы совершенно иначе для многих других архитектур (например, для ARM). Т.е. код не прозрачный..init
/.fini
и.ctors
/.detors
механизмы похожи, но не совсем. Код в.init
/.fini
работает "как есть". Т.е. у вас может быть несколько функций в.init
/.fini
, но синтаксически сложно AFAIK поместить их там полностью прозрачно в чистом C, не разбивая код на множество маленьких.so
файлов..ctors
/.dtors
организованы иначе, чем.init
/.fini
. Разделы.ctors
/.dtors
являются просто таблицами с указателями на функции, а «вызывающий» - это системный цикл, который косвенно вызывает каждую функцию. Т.е. вызывающий цикл может быть специфичным для архитектуры, но, поскольку он является частью системы (если он вообще существует, т.е.), это не имеет значения.Следующий фрагмент добавляет новые указатели
.ctors
функций в массив функций, в основном так же, как__attribute__((constructor))
и (метод может сосуществовать с__attribute__((constructor)))
.Можно также добавить указатели на функции в совершенно другой раздел, придуманный самим собой. В таком случае требуется модифицированный скрипт компоновщика и дополнительная функция, имитирующая загрузчик
.ctors
/.dtors
цикл. Но с его помощью можно лучше контролировать порядок выполнения, добавлять аргументы в аргументах и обрабатывать код возврата (например, в проекте C ++ это было бы полезно, если нужно что-то запустить до или после глобальных конструкторов).Я бы предпочел,
__attribute__((constructor))/((destructor))
где это возможно, это простое и элегантное решение, даже если оно кажется обманом. Для таких программистов, как я, это не всегда вариант.Несколько хороших ссылок в книге « Линкеры и загрузчики» .
источник
__attribute__((constructor))/((destructor))
деструктор, не запускается. Я пробовал несколько вещей, таких как добавление записи в .dtor, как показано выше. Но безуспешно. Эту проблему легко продублировать, запустив код с помощью numactl. Например, предположим, что test_code содержит деструктор (добавьте printf к функциям конструктора и desctructor для устранения проблемы). Тогда бегиLD_PRELOAD=./test_code numactl -N 0 sleep 1
. Вы увидите, что конструктор вызывается дважды, а деструктор - только один раз.Эта страница дает глубокое понимание реализации
constructor
иdestructor
атрибутов и разделов внутри ELF, которые позволяют им работать. После усвоения информации, представленной здесь, я собрал немного дополнительной информации и (заимствуя пример раздела у Майкла Амбруса выше) создал пример, чтобы проиллюстрировать концепции и помочь моему обучению. Эти результаты представлены ниже вместе с примером источника.Как поясняется в этой теме, то
constructor
иdestructor
атрибуты создания записей в.ctors
и.dtors
секции объектного файла. Вы можете разместить ссылки на функции в любом разделе одним из трех способов. (1) используя любойsection
атрибут; (2)constructor
иdestructor
атрибуты или (3) с вызовом встроенной сборки (как указано в ссылке в ответе Амбруса).Использование
constructor
иdestructor
атрибуты позволяют дополнительно назначить приоритет конструктора / деструктора контролировать свой порядок выполнения передmain()
вызывается или после того как он возвращается. Чем ниже заданное значение приоритета, тем выше приоритет выполнения (более низкие приоритеты выполняются до более высоких приоритетов перед main () - и после более высоких приоритетов после main ()). Заданные вами значения приоритета должны быть больше, чем100
когда компилятор резервирует значения приоритета от 0 до 100 для реализации. Aconstructor
илиdestructor
указанный с приоритетом выполняется доconstructor
илиdestructor
указанный без приоритета.С помощью атрибута «раздела» или рядных сборки, вы можете также ссылки на функцию места в
.init
и.fini
секции ELF коды , который будет выполняться до любого конструктора и после любого деструктора, соответственно. Любые функции, вызываемые ссылкой на функцию, размещенную в.init
разделе, будут выполняться перед самой ссылкой на функцию (как обычно).Я попытался проиллюстрировать каждый из них в следующем примере:
вывод:
Этот пример помог закрепить поведение конструктора / деструктора, надеюсь, он будет полезен и другим.
источник
MAX_RESERVED_INIT_PRIORITY
), и то, что они были такими же, как C ++ (init_priority
) 7.7 C ++ - Определенные атрибуты переменных, функций и типов . Тогда я попробовал это99
:warning: constructor priorities from 0 to 100 are reserved for the implementation [enabled by default] void construct0 () __attribute__ ((constructor (99)));
.Вот «конкретный» (и, возможно, полезный ) пример того, как, почему и когда использовать эти удобные, но неприглядные конструкции ...
Xcode использует «глобальное» «пользовательское значение по умолчанию», чтобы решить, какой
XCTestObserver
класс извергает свое сердце для осажденной консоли.В этом примере ... когда я неявно загружаю эту псевдо-библиотеку, давайте назовем ее ...
libdemure.a
через флаг в моем тестовом объекте.Я бы хотел..
При загрузке (т.
XCTest
Е. Когда загружается мой тестовый пакет) переопределитеXCTest
класс «по умолчанию» «наблюдатель» ... (черезconstructor
функцию) PS: Насколько я могу сказать ... все, что сделано здесь, может быть сделано с эквивалентным эффектом внутри моего+ (void) load { ... }
метод класса .запустить мои тесты .... в этом случае с меньшим количеством пустых подробностей в журналах (реализация по запросу)
Верните «глобальный»
XCTestObserver
класс в его первозданное состояние, чтобы не запутывать другиеXCTest
пробеги, которые не попали в подножку (он же связан сlibdemure.a
). Я думаю, это исторически было сделано вdealloc
... но я не собираюсь начинать возиться с этой старой каргой.Так...
Без флага компоновщика ... (Модно-полицейский рой Купертино требует возмездия , но здесь, по желанию, здесь преобладает дефолт Apple )
С
-ldemure.a
флагом компоновщика ... (приемлемые результаты, удушье ... "спасибоconstructor
/destructor
" ... толпа ура )источник
Вот еще один конкретный пример. Это для общей библиотеки. Основная функция общей библиотеки - связь с устройством чтения смарт-карт. Но он также может получать «информацию о конфигурации» во время выполнения через udp. UDP обрабатывается потоком, который ДОЛЖЕН быть запущен во время инициализации.
Библиотека была написана в c.
источник