Система плагинов для статически связанных модулей в C? - PullRequest
2 голосов
/ 04 октября 2011

Мне нужно написать систему плагинов, которая работает со статически связанными модулями в Linux.Я не хочу, чтобы ядро ​​(основная функция) явно вызывало функцию init для модуля.

Самая близкая аналогия, которую я могу придумать, для чего я хочу достичь, это ядро ​​Linux.Там можно иметь неизвестное количество модулей / плагинов, скомпилированных и связанных статически, но модули запускаются так, как если бы они были загружены динамически.

У меня есть

core / main.c:

 int main(void) { return 0; }

core / pluginregistrar.h:

#ifndef PLUGIN_REGISTRAR_H
#define PLUGIN_REGISTRAR_H

#include <stdio.h>

#define module_init(pInitor) \
    static inline funInitor __inittest(void) \
        { fprintf(stdout, "test\n"); return pInitor; }
    int init_module(void) __attribute__((alias(#pInitor)));

typedef void (*funInitor)(void);

#endif

adap1 / main.c:

#include "pluginregistrar.h"

void adap1Init(void) { fprintf(stdout, "test1\n"); }

module_init(adap1Init);

adap2 / main.c:

#include "pluginregistrar.h"

void adap2Init(void) { fprintf(stdout, "test2\n"); }

module_init(adap2Init);

пока, но я понятия не имею, как заставить ядро ​​фактически инициировать модули, которые сделали module_init.

Может кто-нибудь здесь дать мне указатель?(без каламбура)

РЕДАКТИРОВАТЬ: я изменил core / main.c на

extern int init_module(void);
int main(void) {
  init_module();
  return 0;
}

, и он не показывает вызов «адаптации», который был первым в списке библиотек, переданномкомпоновщик.

Ответы [ 2 ]

1 голос
/ 04 октября 2011

Если вы используете GCC, вы можете использовать спецификатор __attribute__((constructor)) для запуска одного фрагмента кода при запуске (до main) для каждого из ваших «модулей». (Также работает с Clang и ICC.)

Например:

$ cat t.c
#include <stdio.h>
#ifdef AAA
static void  __attribute__((constructor)) myinit()
{
    printf("%s\n", AAA);
}
#else
int main()
{
    printf("bye\n");
    return 0;
}
#endif
$ gcc -DAAA=\"hello\" -o  m.o -c t.c 
$ gcc -DAAA=\"there\" -o  n.o -c t.c 
$ gcc -o t.o -c t.c
$ gcc -o foo m.o n.o t.o
$ ./foo
there
hello
bye

(Код предоставлен только для иллюстрации.)

Как только у вас это получится, вы довольно хороши. Пусть эта функция «конструктор» сделает все, что нужно модулю для инициализации, и «зарегистрируется» в вашей структуре плагинов. (Структура со связкой указателей функций, добавленная в связанный список или что-то подобное, будет работать.)

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

Обновление:

Если вам нужно использовать статические библиотеки, а не простые .o файлы, вам понадобится немного дополнительной магии компоновщика.

Предполагается, что вышеприведенное уже выполнено:

$ ar cru libm.a m.o
$ ar cru libn.a n.o
$ gcc -o foo t.c -Wl,-whole-archive libn.a libm.a -Wl,-no-whole-archive
$ ./foo
hello
there
bye

Я не совсем уверен, можете ли вы полагаться на (обратный) порядок ссылок в этом случае.

Или:

$ ar cru libmn.a m.o n.o
$ gcc -o foo t.c -Wl,-whole-archive libmn.a -Wl,-no-whole-archive
$ ./foo 
there
hello
bye

(И здесь я понятия не имею, какой порядок вы получите.)

0 голосов
/ 04 октября 2011

В программе есть только одна точка входа, и по умолчанию это main. То, что вы предлагаете, если я правильно понимаю, это иметь несколько точек входа. Насколько я знаю, это невозможно в стандартном C.

Если вы статически загружаете библиотеки (то есть они передаются в компоновщик независимо от того, включены ли они в полученный исполняемый файл), вам придется экспортировать их инициализаторы в заголовочные файлы и явно вызывать module_init на каждом из них.

Если вы динамически загружаете библиотеки (используя механизм, подобный dlopen / dlsym из dlfcn.h ), тогда вы можете вызвать module_init по требованию, как только вы определили, какие модули загрузить и когда загрузить их.

В любом случае вы должны позвонить module_init, если хотите, чтобы он был запущен. Вы можете сделать рефакторинг, чтобы скрыть его, но в какой-то момент он должен быть вызван.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...