目的
通过编译和链接一个程序,深入理解链接和编译,掌握静态库和动态库的编写和调用方法。
1)通过 ar 命令将其打包成静态库,并调用自己的静态库编写程序运行, 查看结果,并用 size 命令查看各个段的大小。
2)通过 gcc 产生动态链接库,并运行,用 ldd 命令查看文件的依赖。
相关命令使用
gcc命令:
size 命令:
size 命令基本上就是输出指定输入文件各段及其总和的大小。
ar 命令:
用于建立或修改备存文件,或是从备存文件中抽取文件。
语法:ar[-dmpqrtx][cfosSuvV][a<成员文件>][备存文件][成员文件]
下表是常见命令选项 :
ldd 命令:
可以查看一个可执行程序依赖的共享库
下表是常见命令选项:
readelf 命令:
用于显示读取 ELF 文件中信息
格式:readelf <option(s)> elf-file(s)
下表是常见命令选项:
nm 命令
可以打印出库中的涉及到的所有符号。库既可以是静态的也可以是动态的。
对目标文件和可执行文件而言, 可以获得其中的函数;
另外可以从静态库和动态库中获取到函数名称。
静态库生成的过程
第一步:编辑源文件,test.h,test.c,main.c。其中 main.c 文件中包含 main 函 数,作为程序入口;main.c 中包含 main 函数中需要用到的函数。
第二步:将 main.c 编译成目标文件。gcc -c test.c
,得到 test.o 这个目标文件。
第三步:由.o 文件创建静态库。ar -rcs test.a test.o
创建完成后可以使用nm查看 test.a 中的内容。使用 ar -t test.a
也可以 。
第四步:在程序中使用静态库。gcc main.c -L -l test.a -lpthread -L
指出链接的库在当前目录下,-l 加链接库的名字 因为是静态编译,生成的执行文件可以独立于.a 文件运行。
第五步:执行。
动态库生成的过程
第一步:编辑源文件,与创建静态库相同,代码无变化。
第二步:由 test.c 文件创建动态库文件。
gcc -fPIC -shared -o test.so test.c
这里一定要用-o 重命名选项,不然默认输出文件为 a.out 与编译出的可执行文件重名,到时候编译出来的可执行文件会覆盖掉动态库。
这里也可以使用 nm test.so
来查看动态库中的内容
第三步:在程序中使用动态库。gcc main.c -L -l ./test.so -lpthread
(这里动态库的路径最好使用绝对路径或相对路径,如果只写文件名容易报第四步截图的错误)
第四步:执行。
附使用场景及源码
静态库或静态链接库是一组例程,外部函数和变量,它们在编译时在调用者中解析,并由编译器、链接器或绑定器复制到目标应用程序中,从而生成目标文件和一个独立的可执行文件。动态链接只包括库的地址(而静态链接是浪费空间)动态链接在运行时链接库。
静态库虽然可以在多个程序中重用,但在编译时会被锁定到程序中。另一方面, 动态或共享库作为可执行文件之外的单独文件存在。
使用静态库的缺点是它的代码被锁定到最终的可执行文件中,如果没有重新编译就无法修改。相反,可以修改动态库而无需重新编译。
由于动态库位于可执行文件之外,因此程序只需在编译时制作库文件的一个 副本。而使用静态库意味着程序中的每个文件都必须在编译时拥有它自己的库文件副本。
使用动态库的缺点是程序更容易破坏。例如,如果动态库损坏,则可执行文 件可能不再起作用。但是,静态库是不可触及的,因为它存在于可执行文件中。
使用动态库的好处是多个正在运行的应用程序可以使用相同的库,而无需 每个应用程序拥有自己的副本。
适用范围,如果你有很多文件,静态库的多个副 本意味着可执行文件的大小增加,那就建议使用动态库,可以节省时间。如果执行时间的好处超过节省空间的需要,那么静态库就是最佳选择。
main.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include "test.h"
extern int in;
extern int out;
extern int buff[M];