我对程序如何使用共享库有一些疑问。
当我构建一个共享库(使用 -shared -fPIC 开关)时,我会从外部程序中提供一些功能。通常我会做一个 dlopen() 来加载库,然后 dlsym() 将上述函数链接到一些函数指针。此方法不涉及包含任何 .h 文件。有没有办法避免做 dlopen() & dlsym() 并且只包括共享库的 .h ?
我想这可能是 c++ 程序如何使用存储在系统共享库中的代码。即只包括stdlib.h等。
我对程序如何使用共享库有一些疑问。
当我构建一个共享库(使用 -shared -fPIC 开关)时,我会从外部程序中提供一些功能。通常我会做一个 dlopen() 来加载库,然后 dlsym() 将上述函数链接到一些函数指针。此方法不涉及包含任何 .h 文件。有没有办法避免做 dlopen() & dlsym() 并且只包括共享库的 .h ?
我想这可能是 c++ 程序如何使用存储在系统共享库中的代码。即只包括stdlib.h等。
尼克,我认为所有其他答案实际上都是在回答您的问题,即您如何链接库,但是您提出问题的方式表明您对头文件和库之间的区别存在误解。它们是不相同的。你需要两者,他们做的不是同一件事。
构建可执行文件有两个主要阶段,编译(将您的源代码转换为中间形式,包含可执行的二进制指令,但不是可运行的程序)和链接(将这些中间文件组合成单个运行的可执行文件或库)。
当你这样做时gcc -c program.c
,你正在编译,你生成program.o
. 这一步是标题很重要的地方。您需要#include <stdlib.h>
(program.c
例如)使用malloc
and free
。(类似地,您需要#include <dlfcn.h>
and dlopen
。dlsym
)如果您不这样做,编译器会抱怨它不知道这些名称是什么,并因错误而停止。但是,如果您执行#include
标头,编译器不会将您调用的函数的代码插入program.o
. 它只是插入对它们的引用。原因是为了避免代码重复:程序的每个部分只需要访问一次代码,因此如果您需要更多文件 ( module1.c
,module2.c
等等),即使他们都使用过malloc
,您最终也只会引用malloc
. 该单一副本以共享或静态形式(或)存在于标准库中,但您的源代码中未引用这些副本,编译器也不知道它们。libc.so
libc.a
链接器是. 在你做的链接阶段gcc -o program program.o
。然后,链接器将搜索您在命令行上传递的所有库,并找到您调用的所有未在您自己的代码中定义的函数的单一定义。这就是-l
(正如其他人所解释的):告诉链接器您需要使用的库列表。它们的名称通常与您在上一步中使用的标题无关。例如,要使用dlsym
您需要libdl.so
or libdl.a
,因此您的命令行将是gcc -o program program.o -ldl
. 要使用头文件malloc
中的大部分功能,std*.h
您需要libc
,但是因为每个C 程序都使用该库,所以它是自动使用的。链接(好像你已经完成了-lc
)。
对不起,如果我要详细介绍,但如果你不知道你会想要的区别。如果不这样做,就很难理解 C 编译的工作原理。
最后一件事:dlopen
并且dlsym
不是正常的链接方法。它们用于特殊情况,您希望根据信息动态确定您想要的行为,无论出于何种原因,这些信息仅在运行时可用。如果您知道要在编译时调用哪些函数(在 99% 的情况下为真),则不需要使用这些dl*
函数。
您可以链接共享库,例如静态库。然后在启动程序时搜索它们。事实上,默认情况下 -lXXX 会优先选择 libXXX.so 而不是 libXXX.a。
您需要为链接器提供正确的说明来链接您的共享库。
共享库名称类似于 libNAME.so,因此对于链接,您应该使用 -lNAME
将其命名为 libmysharedlib.so,然后将您的主程序链接为:
gcc -o myprogram myprogram.c -lmysharedlib
如果你使用CMake来构建你的项目,你可以使用
TARGET_LINK_LIBRARIES(目标名称库名称)
如:
TARGET_LINK_LIBRARIES(我的程序我的图书馆)
要创建库“mylibrary”,您可以使用
ADD_LIBRARY(目标名称来源列表)
如:
ADD_LIBRARY(mylibrary ${mylibrary_SRCS})
此外,此方法是跨平台的(而简单地将标志传递给 gcc 则不是)。
.so
) 是存储函数/类/...的实际源代码的目标文件(以二进制形式).h
) 是指示(参考)编译器可以在哪里找到.so
主代码所需的函数/类/... (in ) 的文件因此,你需要他们两个。