如何将共享 object 库用于 enabling/disabling 功能?
How to use shared object libraries for enabling/disabling features?
- 语言:C
- 操作系统:Red Hat EL
以 "for instance" 开头:
- 假设我有两个库:
libJUMP.so
和 libSIT.so
。
- JUMP 包含函数
jump()
,类似地 SIT 包含函数 sit()
- 我有一个应用程序想提供给不同的人;他们可以获得
jump()
功能和/或 sit()
功能。但是,如果可能的话,我想不要使用#ifdef
。
Header 对于 libJUMP.so
:
#ifndef JUMP_H_
#define JUMP_H_
#define JUMP_ENABLED
void jump();
#endif /* JUMP_H_ */
Header 对于 libSIT.so
:
#ifndef SIT_H_
#define SIT_H_
#define SIT_ENABLED
void sit();
#endif /* SIT_H_ */
我有申请:
#include "jump.h"
#include "sit.h"
int main()
{
// #ifdef JUMP_ENABLED
jump();
// #endif /* JUMP_ENABLED */
// #ifdef SIT_ENABLED
sit();
// #endif /* SIT_ENABLED */
}
所以:
- 有没有不用
#ifdef
的方法?有没有更好的方法?
- 我听说我们可以通过编译两个 SO 库来做到这一点,如果我 运行 目标系统上的应用程序缺少一个,它可能会自动排除该功能(使用一些组合
dlopen()
和 dlsym()
?) 任何简单的例子,如果这确实是正确的?如果可能的话,上面的代码示例:D?
如果这是一个愚蠢的问题,或者根本不可能,请随时告诉我。如果有类似的问题,这将被视为重复,请告诉我,我将删除此 post.
考虑这三个文件。首先,jump.c
:
#include <stdio.h>
int jump(const double height)
{
fflush(stdout);
fprintf(stderr, "Jumping %.3g meters.\n", height);
fflush(stderr);
return 0;
}
其次,sit.c
:
#include <stdio.h>
int sit(void)
{
fflush(stdout);
fprintf(stderr, "Sitting down.\n");
fflush(stderr);
return 0;
}
第三,example.c
使用以上之一或两者,取决于它们(分别为 libjump.so
或 libsit.so
)是否存在于当前工作目录中:
#include <stdio.h>
#include <dlfcn.h>
static const char *jump_lib_path = "./libjump.so";
static int (*jump)(const double) = NULL;
static const char *sit_lib_path = "./libsit.so";
static int (*sit)(void) = NULL;
static void load_dynamic_libraries(void)
{
void *handle;
handle = dlopen(jump_lib_path, RTLD_NOW | RTLD_LOCAL);
if (handle) {
jump = dlsym(handle, "jump");
/* If no jump symbol, we don't need the library at all. */
if (!jump)
dlclose(handle);
}
handle = dlopen(sit_lib_path, RTLD_NOW | RTLD_LOCAL);
if (handle) {
sit = dlsym(handle, "sit");
/* If no sit symbol, the library is useless. */
if (!sit)
dlclose(handle);
}
}
int main(void)
{
int retval;
load_dynamic_libraries();
if (jump) {
printf("Calling 'jump(2.0)':\n");
retval = jump(2.0);
printf("Returned %d.\n\n", retval);
} else
printf("'jump()' is not available.\n\n");
if (sit) {
printf("Calling 'sit()':\n");
retval = sit();
printf("Returned %d.\n\n", retval);
} else
printf("'sit()' is not available.\n\n");
return 0;
}
让我们先编译和运行示例程序:
gcc -Wall -O2 example.c -ldl -o example
./example
程序输出jump() 和sit() 都不可用。让我们把jump.c编译成动态库,libjump.so,然后再运行例子:
gcc -Wall -O2 -fPIC -shared jump.c -Wl,-soname,libjump.so -o libjump.so
./example
现在,jump() 函数起作用了。让我们也编译 sit.c,最后一次 运行 这个例子:
gcc -Wall -O2 -fPIC -shared jump.c -Wl,-soname,libsit.so -o libsit.so
./example
在这里,两个函数都被调用,一切正常。
在example.c
中,jump
和sit
是函数指针。我们将它们初始化为 NULL,这样我们就可以使用 if (jump)
来检查 jump
是否指向一个有效的函数。
load_dynamic_libraries()
函数使用dlopen()
and dlsym()
获取函数指针。请注意,如果动态库成功打开,并且找到了所需的符号,我们不会 dlclose()
它,因为我们希望将动态库保留在内存中。 (如果它看起来不是我们想要的那种库,我们只会 dlclose()
它。)
如果你想避免if (jump)
和if (sit)
子句,你可以使用stubs like
int unsupported_jump(const double height)
{
return ENOTSUP;
}
int unsupported_sit(void)
{
return ENOTSUP;
}
并且在 load_dynamic_libraries()
的末尾,将函数转移到存根而不是 NULL 指针,即
if (!jump)
jump = unsupported_jump;
if (!sit)
sit = unsupported_sit;
请注意,类函数接口最易于使用,因为函数指针充当有效原型。如果您需要对象,我建议使用 getter 函数。只要您记住 dlsym()
returns 指向对象的指针,对象就可以正常工作;使用 getter 函数,在 getter 函数指针类型中是显式的。
插件接口通常只有一个函数(例如,int properties(struct plugin *const props, const int version)
),用于填充函数和对象指针的结构。应用程序提供它使用的结构的版本,插件函数 returns 成功或失败,取决于它是否可以填充结构以适应该版本。
由于插件通常存储在一个目录中(/usr/lib/yourapp/plugins/
很常见),您可以通过使用opendir()
and readdir()
扫描插件中的文件名来轻松加载所有插件一个一个目录,一个一个dlopen()
访问,获取properties()
函数指针,调用它查看插件提供了哪些服务;通常创建插件结构的数组或链接列表。
如您所见,Linux中的所有这些都非常非常简单明了。如果你想要一个特定的插件功能示例,我建议你将其作为一个单独的问题提出,并提供更多关于接口应该公开什么样的功能的细节——确切的数据结构和函数原型在很大程度上取决于什么样的我们手头有应用程序。
有问题吗?评论?
- 语言:C
- 操作系统:Red Hat EL
以 "for instance" 开头:
- 假设我有两个库:
libJUMP.so
和libSIT.so
。 - JUMP 包含函数
jump()
,类似地 SIT 包含函数sit()
- 我有一个应用程序想提供给不同的人;他们可以获得
jump()
功能和/或sit()
功能。但是,如果可能的话,我想不要使用#ifdef
。
Header 对于 libJUMP.so
:
#ifndef JUMP_H_
#define JUMP_H_
#define JUMP_ENABLED
void jump();
#endif /* JUMP_H_ */
Header 对于 libSIT.so
:
#ifndef SIT_H_
#define SIT_H_
#define SIT_ENABLED
void sit();
#endif /* SIT_H_ */
我有申请:
#include "jump.h"
#include "sit.h"
int main()
{
// #ifdef JUMP_ENABLED
jump();
// #endif /* JUMP_ENABLED */
// #ifdef SIT_ENABLED
sit();
// #endif /* SIT_ENABLED */
}
所以:
- 有没有不用
#ifdef
的方法?有没有更好的方法?- 我听说我们可以通过编译两个 SO 库来做到这一点,如果我 运行 目标系统上的应用程序缺少一个,它可能会自动排除该功能(使用一些组合
dlopen()
和dlsym()
?) 任何简单的例子,如果这确实是正确的?如果可能的话,上面的代码示例:D?
- 我听说我们可以通过编译两个 SO 库来做到这一点,如果我 运行 目标系统上的应用程序缺少一个,它可能会自动排除该功能(使用一些组合
如果这是一个愚蠢的问题,或者根本不可能,请随时告诉我。如果有类似的问题,这将被视为重复,请告诉我,我将删除此 post.
考虑这三个文件。首先,jump.c
:
#include <stdio.h>
int jump(const double height)
{
fflush(stdout);
fprintf(stderr, "Jumping %.3g meters.\n", height);
fflush(stderr);
return 0;
}
其次,sit.c
:
#include <stdio.h>
int sit(void)
{
fflush(stdout);
fprintf(stderr, "Sitting down.\n");
fflush(stderr);
return 0;
}
第三,example.c
使用以上之一或两者,取决于它们(分别为 libjump.so
或 libsit.so
)是否存在于当前工作目录中:
#include <stdio.h>
#include <dlfcn.h>
static const char *jump_lib_path = "./libjump.so";
static int (*jump)(const double) = NULL;
static const char *sit_lib_path = "./libsit.so";
static int (*sit)(void) = NULL;
static void load_dynamic_libraries(void)
{
void *handle;
handle = dlopen(jump_lib_path, RTLD_NOW | RTLD_LOCAL);
if (handle) {
jump = dlsym(handle, "jump");
/* If no jump symbol, we don't need the library at all. */
if (!jump)
dlclose(handle);
}
handle = dlopen(sit_lib_path, RTLD_NOW | RTLD_LOCAL);
if (handle) {
sit = dlsym(handle, "sit");
/* If no sit symbol, the library is useless. */
if (!sit)
dlclose(handle);
}
}
int main(void)
{
int retval;
load_dynamic_libraries();
if (jump) {
printf("Calling 'jump(2.0)':\n");
retval = jump(2.0);
printf("Returned %d.\n\n", retval);
} else
printf("'jump()' is not available.\n\n");
if (sit) {
printf("Calling 'sit()':\n");
retval = sit();
printf("Returned %d.\n\n", retval);
} else
printf("'sit()' is not available.\n\n");
return 0;
}
让我们先编译和运行示例程序:
gcc -Wall -O2 example.c -ldl -o example
./example
程序输出jump() 和sit() 都不可用。让我们把jump.c编译成动态库,libjump.so,然后再运行例子:
gcc -Wall -O2 -fPIC -shared jump.c -Wl,-soname,libjump.so -o libjump.so
./example
现在,jump() 函数起作用了。让我们也编译 sit.c,最后一次 运行 这个例子:
gcc -Wall -O2 -fPIC -shared jump.c -Wl,-soname,libsit.so -o libsit.so
./example
在这里,两个函数都被调用,一切正常。
在example.c
中,jump
和sit
是函数指针。我们将它们初始化为 NULL,这样我们就可以使用 if (jump)
来检查 jump
是否指向一个有效的函数。
load_dynamic_libraries()
函数使用dlopen()
and dlsym()
获取函数指针。请注意,如果动态库成功打开,并且找到了所需的符号,我们不会 dlclose()
它,因为我们希望将动态库保留在内存中。 (如果它看起来不是我们想要的那种库,我们只会 dlclose()
它。)
如果你想避免if (jump)
和if (sit)
子句,你可以使用stubs like
int unsupported_jump(const double height)
{
return ENOTSUP;
}
int unsupported_sit(void)
{
return ENOTSUP;
}
并且在 load_dynamic_libraries()
的末尾,将函数转移到存根而不是 NULL 指针,即
if (!jump)
jump = unsupported_jump;
if (!sit)
sit = unsupported_sit;
请注意,类函数接口最易于使用,因为函数指针充当有效原型。如果您需要对象,我建议使用 getter 函数。只要您记住 dlsym()
returns 指向对象的指针,对象就可以正常工作;使用 getter 函数,在 getter 函数指针类型中是显式的。
插件接口通常只有一个函数(例如,int properties(struct plugin *const props, const int version)
),用于填充函数和对象指针的结构。应用程序提供它使用的结构的版本,插件函数 returns 成功或失败,取决于它是否可以填充结构以适应该版本。
由于插件通常存储在一个目录中(/usr/lib/yourapp/plugins/
很常见),您可以通过使用opendir()
and readdir()
扫描插件中的文件名来轻松加载所有插件一个一个目录,一个一个dlopen()
访问,获取properties()
函数指针,调用它查看插件提供了哪些服务;通常创建插件结构的数组或链接列表。
如您所见,Linux中的所有这些都非常非常简单明了。如果你想要一个特定的插件功能示例,我建议你将其作为一个单独的问题提出,并提供更多关于接口应该公开什么样的功能的细节——确切的数据结构和函数原型在很大程度上取决于什么样的我们手头有应用程序。
有问题吗?评论?