【发布时间】:2012-12-06 00:09:54
【问题描述】:
我正在尝试用 C 语言构建一个程序,该程序具有许多依赖于各种共享库的可选功能。
在我们的异构计算集群中,并非所有这些库都在所有系统上都可用(或最新)。
示例是来自较新 glibc(sched_getcpu@@GLIBC_2.6、__sched_cpucount@@GLIBC_2.6)或整个共享库的符号,它们可能可用也可能不可用(libnuma、libR、libpbs)。
我知道我可以使用 libdl 来加载带有 dlopen 和 dlsym 的符号,但是对于越来越多的符号(目前大约 30 个)这样做充其量是乏味的。
据我了解,Linux 中的共享库默认是延迟加载的,因此在实际使用之前不需要符号。
但如果我尝试提前检查,那么它会在执行开始时失败:
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
#include <sched.h>
int main() {
void *lib_handle;
int (*fn)(void);
int x;
char *error;
lib_handle = dlopen("libc.so.6", RTLD_LAZY);
if (!lib_handle)
{
fprintf(stderr, "%s\n", dlerror());
exit(1);
}
fn = dlsym(lib_handle, "sched_getcpu");
if ((error = dlerror()) != NULL)
{
fprintf(stderr, "%s\n", error);
exit(1);
}
printf("%d\n", sched_getcpu());
return 0;
}
在包含所有库的编译系统上:
$ icc test.c
$ ./a.out
10
在另一个具有较新版本的 GLIBC 的系统上:
$ ./a.out
./a.out: /lib64/libc.so.6: version `GLIBC_2.6' not found (required by ./a.out)
如果我注释掉实际调用 sched_getcpu 的行,那么我会改为使用较小的系统:
$ ./a.out
/lib64/libc.so.6: undefined symbol: sched_getcpu
那么,有没有办法强制库仅在使用时加载,并在使用它们的块之前进行此类检查?
【问题讨论】:
-
从长远来看,尝试使您的集群更加同质化可能会更安全。或者,最好的解决方案是静态链接必须在这些维护不一致的系统上运行的所有可执行文件,并希望至少它们运行良好兼容的内核。如果您的集群运行的是 BSD,尤其是。 NetBSD 那么这很容易,只要您将应用程序链接到集群中运行的最旧版本的系统上。
-
@GregA.Woods 这是我一直告诉我的系统管理员的话。但问题是:我是一名软件开发人员。我需要编写代码并让它运行。我什至没有对集群的管理员访问权限,所以这不是一个选择。内核都是 Linux,但它是低功耗机器上不同风格的 CentOS 和超级计算机上的 RedHat 的混合体。
-
那么静态链接是您最好的,也许是唯一的选择。不过,您可能必须构建自己的构建机器才能找到最小公分母的内核版本。对于大多数事情来说,这也不是那么难。
-
@GregA.Woods 有些东西可以静态链接,而像
libnuma这样的其他东西很难获得静态版本。我仍然希望在运行时检测丢失的库,因为大多数需要库的功能都是可选的优化。例如没有libnuma的系统不需要它,因为它首先不是 NUMA,所以那些调度/内存优化在那里没有任何意义。幸运的是,我需要的三个必需库是glibc、libpthread、libomp。幸运的是icc提供了一个静态 OpenMP 库。 -
也许如果您从源代码编译所有内容,那么您将更好地处理库的创建方式。我会总是这样做。唯一的另一种选择是在你的包安装中提供每个共享库的显式副本,这样你的包永远不会依赖于 any 非系统库。这甚至可能包括在 GNU/Linux 系统上提供您自己的
glibc,因为我认为 Glibc 不能正确地处理某些形式的向后兼容性。不过,静态链接曾经要容易得多——要破坏的部分要少得多!
标签: c shared-libraries lazy-loading ld