我需要编写一个插件系统,它可以在Linux上使用静态链接的模块。 我不希望核心(主函数)显式调用模块的init函数。
对于我想要实现的目标,我能想到的最接近的类比是Linux内核。 在那里可以静态编译和链接未知数量的模块/插件,但模块是在动态加载时启动的。
我有这个
int main(void) { return 0; }
#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
#include "pluginregistrar.h"
void adap1Init(void) { fprintf(stdout, "test1\n"); }
module_init(adap1Init);
#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;
}
并且它没有显示“自适应”的调用,它是给链接器的库列表中的第一个。
答案 0 :(得分:1)
如果您正在使用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
(在这里我不知道你会得到什么样的构造函数。)
答案 1 :(得分:0)
程序中只有一个入口点,默认为main
。如果我理解正确的话,你建议的是有多个入口点。据我所知,这在标准C中是不可能的。
如果静态加载库(意味着它们被传递到链接器中,无论它们是否包含在生成的可执行文件中),那么您必须在头文件中导出它们的初始化程序,并显式调用{{1在每一个上。
如果您动态加载库(使用dlfcn.h中module_init
/ dlopen
之类的机制),那么一旦确定了什么,就可以根据需要调用dlsym
要加载的模块以及何时加载它们。
在任何一种情况下,如果您希望它运行,必须调用 module_init
。你可以重构隐藏它,但在某些时候它必须被调用。