Ядро-подобный модуль инициализации в C - PullRequest
3 голосов
/ 12 ноября 2011

У меня есть проект C / C ++ с большим количеством модулей, которые предназначены для добавления и удаления между установками (они похожи на «плагины»). Я хотел значительно упростить инициализацию модуля, используя технику, аналогичную ядру Linux - всякий раз, когда ядро ​​связано с каким-либо модулем .c, макрос module_init () в файле .c «регистрирует» модуль на каком-то глобальном сайте, поэтому его можно callback'd позже, чтобы быть должным образом инициализирован.

Вопрос: Как мне это сделать без каких-либо неприятных трюков с ссылками?

Я попробовал один подход, примерно следующий:

во всех модулях есть макрос module_init (initfunc), который расширяется в нечто вроде

struct _initializer_ {
  _initializer_() {
    register_init_function(initfunc);
  }
} _init_instance_;

Функция register_init_function хранит указатель initfunc в виде списка, поэтому его можно инициализировать основной программой позже.

Проблема здесь в том, что глобальные переменные C обнуляются перед использованием в неконтролируемом порядке, поэтому случается, что некоторые модули правильно регистрируются, а регистрация других стирается, когда основной модуль «инициализируется» после них. Я также попробовал некоторые трюки со статическим классом и порядком инициализации глобальной переменной, но либо полностью неправильно понял описание, либо они не работают.

Вопрос: существует ли какой-либо метод для обеспечения порядка инициализации или какой-либо другой метод для такой инициализации модуля?

[РЕДАКТИРОВАТЬ] прямое объяснение:

  • main.c имеет глобальную переменную Y
  • main.h описывает эту глобальную переменную или какой-то возможный способ ее изменения (функция X ())
  • module.c вызывает функцию X () (или любую другую Y-модифицирующую вещь), чтобы как можно скорее сохранить что-либо в переменной Y, без ссылки на main.c .
  • main.c может после этого увидеть, что кто-то вызвал X () и сохранил материал в Y.

Пример назначения: gcc main.c modules / common / .c modules / some_specific_purpose / .c

Ответы [ 2 ]

2 голосов
/ 12 ноября 2011

Обычный подход к изменениям поведения во время выполнения программы заключается в использовании динамической загрузки : вы компилируете некоторый код в разделяемую библиотеку (например, с gcc -shared, производя .so файл в Linux и DLL в Windows), а затем используйте функцию API операционной системы для загрузки этой библиотеки в пространство процесса во время выполнения.

Функции Linux называются dlopen(), dlsym() и dlclose().

В Windows у вас есть LoadLibrary(), GetProcAddress() и FreeLibrary().

. Вы также можете использовать библиотеку обертки libtool ltdl, которая абстрагирует загрузку библиотеки для некоторого видакросс-платформенная переносимость.

В любом случае типичный сценарий заключается в том, что вы загружаете библиотеку / общий объект, а затем получаете указатель на функцию на именованную экспортируемую функцию, например int (*initialize)().Затем вы можете реализовать любое динамическое поведение по своему усмотрению.

(Обратите внимание, что это намного проще, чем все, что ядро ​​Linux делает с загрузкой модулей, что является совершенно другой и специализированной процедурой, которая на самом деле непредставляет интерес для программирования в пространстве пользователя.)

1 голос
/ 13 ноября 2011

В: Есть ли какой-нибудь метод, обеспечивающий порядок инициализации

G ++ имеет атрибут init_priority .

Some_Class  A  __attribute__((init_priority (2000)));
Some_Class  B  __attribute__((init_priority (543)));

Заказ будет B, а затем A

Существует также атрибут функции constructor, который может быть полезен. Я вполне уверен, что функции с этим атрибутом не могут полагаться на глобальные объекты.

...