C & C++ Notes
环境配置
- Windows:下载cygwin,安装时勾选gcc(包括gcc-core等一系列,都勾了)、cmake、make、binutils、MinGW、vim、nano等一些你觉得需要用到的。蓝后这里面可以导出exe,但此exe在cygwin外运行报缺少dll,缺少的dll在cygwin的bin目录下,复制过去导出程序的文件夹就行。
- Linux:安装有gcc、cmake等就行了。
头文件
C 函数声明和宏定义。
引用系统头文件:#include <file>
;引用用户头文件:#include "file"
1 |
共享库生成与调用 - Share Lib
静态库:Linux:.a
;Windows: .lib
动态库:Linux:.so
;Windows:.dll
举例子:From https://www.cprogramming.com/tutorial/shared-libraries-linux-gcc.html
在我们开始之前,它可能有助于快速了解从源代码到运行程序发生的所有事情:
- C 预处理器:此阶段处理所有 预处理器指令 。 基本上,任何以# 开头的行,例如#define 和#include。
- 编译正确:一旦源文件被预处理,结果就会被编译。 由于许多人将 整个构建过程 称为编译,因此此阶段通常称为编译正确。 此阶段将 .c 文件转换为 .o(对象)文件。
- 链接:这里是所有目标文件和任何库链接在一起以制作最终程序的地方。 请注意,对于静态库,实际库放置在您的最终程序中,而对于共享库,则只放置对库的引用。 现在您有一个可以运行的完整程序。 你从 shell 启动它,程序被交给加载器。
- 加载:这个阶段发生在你的程序启动时。 扫描您的程序以查找对共享库的引用。 找到的任何引用都会被解析,并将库映射到您的程序中。
第 3 步和第 4 步是共享库的神奇之处(和混淆)。
foo.c:
1 |
|
foo.h:
1 |
|
main.c
1 |
|
静态库生成:
生成对象:gcc -c foo.c
打包:ar crv libfoo.a foo.o
生成内容表:ranlib libfoo.a
动态库生成:
步骤 1:使用位置无关代码进行编译
我们需要将我们的库源代码编译成位置无关代码(PIC): 1
1 | $ gcc -c -Wall -Werror -fpic foo.c |
步骤 2:从目标文件创建共享库
现在我们需要实际将这个目标文件转换成一个共享库。 我们将其称为 libfoo.so:
1 | gcc -shared -o libfoo.so foo.o |
第 3 步:链接共享库
如您所见,这实际上非常简单。 我们有一个共享库。 让我们编译 main.c 并将其与 libfoo 链接。 我们将调用我们的最终程序测试。 请注意,-lfoo 选项不是在寻找 foo.o,而是 libfoo.so。 GCC 假定所有库都以 lib 开头并以 .so 或 .a 结尾(.so 用于共享对象或共享库,而 .a 用于存档或静态链接库)。
1 | $ gcc -Wall -o test main.c -lfoo |
告诉 GCC 在哪里可以找到共享库
哦哦! 链接器不知道在哪里可以找到 libfoo。 默认情况下,GCC 有一个它查找的位置列表,但我们的目录不在该列表中。 2 我们需要告诉 GCC 在哪里可以找到 libfoo.so。 我们将使用 -L 选项来做到这一点。 在这个例子中,我们将使用当前目录,/home/username/foo:
1 | gcc -L/home/username/foo -Wall -o test main.c -lfoo |
第 4 步:使库在运行时可用
很好,没有错误。 现在让我们运行我们的程序:
1 | $ ./test |
1 | $ ./测试 |
不好了! 加载程序找不到共享库。 3 我们没有安装在标准位置,所以需要给loader一点帮助。 我们有几个选择:我们可以为此使用环境变量 LD_LIBRARY_PATH 或 rpath。 我们先来看看 LD_LIBRARY_PATH:
使用 LD_LIBRARY_PATH
1 | echo $LD_LIBRARY_PATH |
里面什么都没有。 让我们通过将我们的工作目录添加到现有的 LD_LIBRARY_PATH 来解决这个问题:
1 | $ LD_LIBRARY_PATH=/home/username/foo:$LD_LIBRARY_PATH |
发生了什么? 我们的目录在 LD_LIBRARY_PATH 中,但我们没有导出它。 在 Linux 中,如果您不将更改导出到环境变量,它们将不会被子进程继承。 加载器和我们的测试程序没有继承我们所做的更改。 幸运的是,修复很简单:
1 | $ export LD_LIBRARY_PATH=/home/username/foo:$LD_LIBRARY_PATH |
很好,成功了! LD_LIBRARY_PATH 非常适合快速测试和您没有管理员权限的系统。 然而,作为一个缺点,导出 LD_LIBRARY_PATH 变量意味着它可能会导致您运行的其他程序出现问题,这些程序也依赖于 LD_LIBRARY_PATH,如果您在完成后没有将其重置为以前的状态。
使用 rpath
现在让我们尝试 rpath(首先我们将清除 LD_LIBRARY_PATH 以确保找到我们的库的是 rpath)。 Rpath 或运行路径是一种将共享库的位置嵌入到可执行文件本身中的方法,而不是依赖于默认位置或环境变量。 我们在链接阶段这样做。 注意冗长的“-Wl,-rpath=/home/username/foo”?? 选项。 -Wl 部分将逗号分隔的选项发送到链接器,因此我们告诉它使用我们的工作目录将 -rpath 选项发送到链接器。
1 | unset LD_LIBRARY_PATH |
太好了,它奏效了。 rpath 方法很棒,因为每个程序都可以独立地列出其共享库位置,因此不同的程序不会像 LD_LIBRARY_PATH 那样在错误的路径中查找问题。
rpath 与 LD_LIBRARY_PATH
然而,rpath 有一些缺点。 首先,它要求将共享库安装在固定位置,以便程序的所有用户都可以访问这些位置中的那些库。 这意味着系统配置的灵活性较低。 其次,如果该库引用 NFS 挂载或其他网络驱动器,您可能会在程序启动时遇到不希望的延迟 - 或者更糟 - 。
使用ldconfig修改ld.so
如果我们想安装我们的库以便系统上的每个人都可以使用它怎么办? 为此,您将需要管理员权限。 您需要这样做有两个原因:首先,将库放在标准位置,可能是 /usr/lib 或 /usr/local/lib,普通用户没有写入权限。 其次,您需要修改 ld.so 配置文件和缓存。 以 root 身份执行以下操作:
1 | $ cp /home/username/foo/libfoo.so /usr/lib |
现在该文件位于标准位置,具有正确的权限,每个人都可以读取。 我们需要告诉加载器它可以使用,所以让我们更新缓存:
1 | $ ldconfig |
这应该创建一个指向我们共享库的链接并更新缓存,以便它可以立即使用。 让我们仔细检查一下:
1 | $ ldconfig -p | grep foo |
现在我们的库已安装。 在我们测试之前,我们必须清理一些东西:
再次清除我们的 LD_LIBRARY_PATH,以防万一:
1 | unset LD_LIBRARY_PATH |
重新链接我们的可执行文件。 请注意,我们不需要 -L 选项,因为我们的库存储在默认位置并且我们没有使用 rpath 选项:
1 | gcc -Wall -o test main.c -lfoo |
让我们确保使用 ldd 使用库的 /usr/lib 实例:
1 | $ ldd test | grep foo |
好,现在让我们运行它:
1 | $ ./test |
就这样结束了。 我们已经介绍了如何构建共享库、如何与其链接,以及如何解决共享库中最常见的加载器问题——以及不同方法的优缺点。
- 它在可执行文件的 DT_RPATH 部分中查找,除非有 DT_RUNPATH 部分。
- 它在 LD_LIBRARY_PATH 中查找。 如果出于安全原因,可执行文件是 setuid/setgid,则跳过此步骤。
- 除非设置了 setuid/setgid 位(出于安全原因),否则它会在可执行文件的 DT_RUNPATH 部分中查找。
- 它在缓存文件 /etc/ld/so/cache 中查找(使用 -z nodeflib 链接器选项禁用)。
它在默认目录 /lib 和 /usr/lib 中查找(使用 -z nodeflib 链接器选项禁用)。
什么是位置无关代码? PIC 是无论放在内存中的哪个位置都可以运行的代码。 因为几个不同的程序都可以使用共享库的一个实例,所以库不能在固定地址存储东西,因为该库在内存中的位置因程序而异。 ↩
- GCC 首先在 /usr/local/lib 中搜索库,然后在 /usr/lib 中搜索。 然后,它按照命令行上指定的顺序在 -L 参数指定的目录中搜索库。 ↩
- 默认的 GNU 加载器 ld.so 按以下顺序查找库: ↩
Cmake 文件,动态库:
1 | cmake_minimum_required(VERSION 2.8) |
If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !