【Linux静态库和动态库】
Linux静态库和动态库
- 1. 编译与ELF格式
- 2. 库的基本概念
- 3.静态库的制作:(假设要将a.c、b.c制作成静态库)
- 4.静态库的常见操作
- 5.静态库的使用
- 6. 多个库的相互依赖
- 举例1.(库文件制作、错误处理)
- 7.静态库和动态库的关系和区别
- 8.动态库的制作
- 软链接 | 硬链接
- 软连接和硬链接区别
1. 编译与ELF格式
预处理:解释并展开源程序当中的所有的预处理指令,此时生成 *.i 文件。(宏替换)
命令:gec@ubuntu:~$ gcc hello.c -o hello.i -E
编译:词法和语法的分析,生成对应硬件平台的汇编语言文件,此时生成 *.s 文件。
命令:gec@ubuntu:~$ gcc hello.i -o hello.s -S
汇编:将汇编语言文件翻译为对应处理器的二进制机器码,此时生成 *.o 文件。
命令:gec@ubuntu:~$ gcc hello.s -o hello.o -c
链接:将多个 *.o 文件合并成一个不带后缀的可执行文件。
命令:gec@ubuntu:~$ gcc hello.o -o hello -lc
重点关注最后一步,库文件的链接:链接实际上是将多个.o文件合并在一起的过程。这些 *.o 文件合并前是 ELF 格式,合并后也是 ELF
格式。ELF全称是 Executable and Linkable Format,即可执行可链接格式
库的本意是library图书馆,库文件就是一个由很多 *.o 文件堆积起来的集合。 ELF需要对各个 *.o
文件中的静态数据(包括常量)、函数入口的地址做统一分配和管理,这个过程就叫做“重定位”,因此未经链接的单独的 *.o
文件又被称为可重定位文件,经过链接处理合并了相同的段的文件称为可执行文件。
2. 库的基本概念
库文件分为两类:静态库和动态库。如:
静态库:libx.a
动态库:liby.so
lib库名.后缀
其中,lib是任何库文件都必须有的前缀,库名就是库文件真正的名称,比如上述例子中两个库文件分别叫x和y,在链接它们的时候写成 -lx 和 -ly ,后缀根据静态库和动态库,可以是 .a 或者 .so:
静态库的后缀:.a (archive,意即档案)
动态库的后缀:.so (share object,意即共享对象)
注意:不管是静态库,还是动态库,都是可重定位文件 *.o 的集合。
3.静态库的制作:(假设要将a.c、b.c制作成静态库)
*第一步,制作 .o 原材料
gec@ubuntu:~$ gcc a.c -o a.o -c
gec@ubuntu:~$ gcc b.c -o b.o -c
*第二步,将 .o 合并成一个静态库
gec@ubuntu:~$ ar crs libx.a a.o b.o
4.静态库的常见操作
#查看 *.o 文件
gec@ubuntu:~$ ar t libx.a #(t意即table,以列表方式列出*.o文件)
a.o
b.o
# 删除 *.o 文件
gec@ubuntu:~$ ar d libx.a b.o #(d意即delte,删除掉指定的*.o文件)
gec@ubuntu:~$ ar t libx.a
a.o
# 增加 *.o 文件
gec@ubuntu:~$ ar r libx.a b.o #(r意即replace,添加或替换(重名时)指定的*.o文件)
gec@ubuntu:~$ ar t libx.a
a.o
b.o
# 提取 *.o 文件
gec@ubuntu:~$ ar x libx.a #(x意即extract,将库中所有的*.o文件释放出来)
gec@ubuntu:~$ ar x libx.a a.o #(指定释放库中的a.o文件)
5.静态库的使用
库文件最大的价值,在于代码复用。假设在上述库文件所包含的*.o文件中,已经包含了若干函数接口,那么只要能链接这个库,就无需再重复编写这些接口,直接链接即可。
假设a.c中包含了如下函数:
// a.c
void func()
{printf("我是a.c中的函数func\n");
}
那么,就可以使用链接库的形式,使用这个接口:
// main.c
void func(); //函数要声明
int main()
{func();return 0;
}
编译并运行的结果是: -L/库路径 -l库名 -i 头文件路径
gec@ubuntu:~$ gcc main.c -L/home/gec -lx -o main
gec@ubuntu:~$ ./main
我是a.c中的函数func
gec@ubuntu:~$
注意:
编译语句中的 -L/home/gec 指明库文件 libx.a 的具体位置,否则系统找不到该库文件。 编译语句中的 -lx
指明要链接的库文件的具体名称,注意不包含前缀后缀。 对于静态库而言,由于编译链接时会将 main.c
所需要的库代码复制一份到最终的执行文件中,这直接导出静态库的如下特性: 执行程序在编译之后与静态库脱离关系,其执行也不依赖于静态库。
执行程序执行时由于不依赖静态库,因此也省去了运行时动态。
6. 多个库的相互依赖
假设有两个库文件:liba.a 和 libb.a,它们分别只包含了 a.o 和 b.o,假设这两个源程序有如下依赖关系:
// a.c
#incldue <stdio.h>
void fa()
{printf("Hey!\n");
}// b.c
#incldue <stdio.h>
void fa(); //fa()在这里被声明了
void fb()
{fa(); // fb() 调用了 fa(),即libb.a依赖于liba.a
}
很明显,b.c中的功能接口是依赖于 a.c 的,换句话说,库文件 libb.a 是依赖于 liba.a 的。
现在再来写一个调用 fb() 的主函数:
void fb();
int main(void)
{fb();
}
编译情况如下:
gec@ubuntu:~$ gcc main.c -o main -L. -lb -la //谁要用我的方法,谁就在我的前面
gec@ubuntu:~$ gcc main.c -o main -L. -la -lb
./libb.a(b.o): In function `fb':
b.c:(.text+0xa): undefined reference to `fa'
collect2: error: ld returned 1 exit status
从以上编译信息来看,得出结论:
当编译链接多个库,且这些库之间有依赖关系时,被依赖的基础库要放在编译语句的后面。 在以上示例中,库 libb.a 依赖于 liba.a,即
liba.a 是被依赖的基础库,因此 -la 要放在 -lb 的后面才能通过编译。 注意:以上结论对于静态库、动态库都适用。
举例1.(库文件制作、错误处理)
【1】将常用的文件IO函数封装成自报错的静态库和动态库,并在要以后的程序中根据需要调这些库文件。read.c
例如:
gcc read.c -o read.o -c 生成连接文件
ar crs libread.a read.o 生成静态库
ssize_t Read(int fd, void *buf, size_t count)
{int total = 0;int n;while(count > 0){while((n=read(fd, (char *)buf+total, count))==-1 &&errno == EINTR);//(char *)buf+total : buf指针,指向每次读取到的数据 加total确保数据不被覆盖 准确定位数据增加后每次的位置if(n == -1) // 遇到了错误{perror("read失败");return -1;}if(n == 0) // 遇到了文件尾时=0break;count -= n; //n每次读到的大小 count 总的大小 count = count-n :减去每次读的数据total += n; //total统计每次读到的数据 total =total+n }// 返回总共读到的字节数return total;
}
7.静态库和动态库的关系和区别
静态库(相当于书店,只卖不借)
原理:编译时,库中的代码将会被复制到每一个程序中
优点:程序不依赖于库、执行效率稍高
缺点:浪费存储空间、无法对用户升级迭代
动态库(相当于图书馆,只借不卖)
原理:编译时,程序仅确认库中功能模块的匹配关系,并未复制
缺点:程序依赖于库、执行效率稍低
优点:节省存储空间、方便对用户升级迭代
8.动态库的制作
lib库名.后缀 对于动态库而言,在后缀后面还经常会带着版本号: lib库名.后缀.版本号 完整的动态库文件名称是:
lib库名.so.主版本号.次版本号.修订版本号,比如: libx.so.1.3.1 动态库一般会用一个只带主版本号的符号链接(软连接ln
-s 生成)来链接程序(方便版本更新换代),
软链接 | 硬链接
1、软链接就是:“ln –s 源文件 目标文件”,只会在选定的位置上生成一个文件的镜像,不会占用磁盘空间,类似与windows的快捷方式。
2、硬链接ln源文件 目标文件,没有参数-s, 会在选定的位置上生成一个和源文件大小相同的文件,无论是软链接还是硬链接,文件都保持同步变化。
2.通过实验加深理解
[oracle@Linux]$ vi test.log
#创建一个测试文件f1
[oracle@Linux]$ ln test.log test1.log
#创建f1的一个硬连接文件test1.log
[oracle@Linux]$ ln -s test.log test2.log
#创建f1的一个符号连接文件test2.log
[oracle@Linux]$ ls -li
# -i参数显示文件的inode节点信息
软连接和硬链接区别
1.软连接有个主体,删除其他一个不会影响,其余的连接,但是删除主体(源文件),所有文件都失效变红
2.硬链接主次不分,只要生成以后,删除谁都不影响。
如:
gec@ubuntu:~$ ls -l
lrwxrwxrwx 1 root root 15 Jan 16 2020 libbsd.so.0 -> libbsd.so.0.8.7
动态库的制作
不管是静态库还是动态库,都是用来被其他程序链接的一个个功能模块。与静态库一致,制作动态库的步骤如下:
将 *.c 编译生成 *.o
将 *.o 编译成动态库
例如:
gec@ubuntu:~$ ls
a.c b.c
# 第一步:将源码编译为 *.o
gec@ubuntu:~$ gcc a.c -o a.o -c -fPIC
gec@ubuntu:~$ gcc b.c -o b.o -c -fPIC
gec@ubuntu:~$ ls
a.c b.c a.o b.o
# 第二步:将 *.o 编译为动态库
gec@ubuntu:~$ gcc -shared -fPIC -o libx.so a.o b.o
gec@ubuntu:~$ ls
a.c b.c a.o b.o libx.so
# 第三步:将连接库生成可执行文件
gec@ubuntu:~$ gcc main.c -o main -L./lib -lx
解释:当前目录lib文件夹下 -l库名 库被放在了/lib下
说明:
-L 选项后面跟着动态库所在的路径。
-l 选项后面跟着动态库的名称
如果程序运行时找不到动态库,运行就会失败,例如:
gec@ubuntu:~$ ./main
报错
出现上述错误的原因,就是因为运行程序 main 时,无法找到其所依赖的动态库 libx.so,解决这个问题,有三种办法:
编译时预告:
gec@ubuntu:~$ gcc main.c -o main -L. -lx -Wl,-rpath=/home/gec/lib
设置环境变量:
gec@ubuntu:~$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/gec/lib
修改系统默认库路径:(不推荐)
gec@ubuntu:~$ sudo vi /etc/ld.so.conf.d/libc.conf
gec@ubuntu:~$ sudo ldconfig
在以上文件中,添加动态库所在路径即可。 注意: 此处要小心编辑,一旦写错可能会导致系统无法启动,这是一种 污染 系统的做法,不推荐。
在gcc中,如果遇到动态库和静态库同名,则会默认优先进行动态链接,如果此时需要用静态链接,可以使用编译选项 -static 达到此目的
gec@ubuntu:~$ ls ./lib # 假设有两个重名的静态、动态库
libx.a libx.so
gec@ubuntu:~$
gec@ubuntu:~$ gcc main.c -o main1 -L./lib -lx
gec@ubuntu:~$ gcc main.c -o main2 -L./lib -lx -static
gec@ubuntu:~$ ls -l
total 868
drwxr-xr-x 2 gec gec 4096 1月 4 17:45 lib/
-rwxr-xr-x 1 gec gec 8288 1月 4 17:46 main1 # 默认动态链接
-rwxr-xr-x 1 gec gec 845120 1月 4 17:46 main2 # 指定静态链接
-rw-r--r-- 1 gec gec 56 1月 4 17:42 main.c
gec@ubuntu:~$
很明显可以看到,静态链接的文件尺寸要大得多。
【Linux静态库和动态库】相关推荐
- 关于Linux静态库和动态库的分析
From: http://hi.baidu.com/bdccutysj/blog/item/5bae7f0202abac7c3912bb15.html 1.什么是库 在windows平台和linux平 ...
- linux 中如何将文件粘贴到usr下的lib内,学会在Linux下GCC生成和使用静态库和动态库...
一.基本概念1.1什么是库 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的平台不同(主 ...
- Linux中gcc的编译、静态库和动态库的制作
欢迎大家关注笔者,你的关注是我持续更博的最大动力 Linux中gcc的编译.静态库.动态库 文章目录: 1 gcc的编译过程 1.1 gcc的编译过程 1.2 gcc的常用参数 2 gcc 静态库的制 ...
- Linux下的静态库、动态库和动态加载库
from: http://www.techug.com/linux-static-lib-dynamic-lib 库的存在极大的提高了C/C++程序的复用性,但是库对于初学者来说有些难以驾驭,本文从L ...
- linux下的共享库(动态库)和静态库
1.什么是库 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的本质不同,因此二者库的二进 ...
- Linux基础——gcc编译、静态库与动态库(共享库)
Linux基础--gcc编译.静态库与动态库(共享库) https://blog.csdn.net/daidaihema/article/details/80902012 Linux基础--gcc编译 ...
- Linux下制作和使用静态库和动态库
写在前面: ldd + 可执行文件 可以查看可执行文件所依赖的库 概述: Linux操作系统支持的函数库分为静态库和动态库,动态库又称共享库.linux系统有几个重要的目录存放相应的函数库,如/lib ...
- Linux 之三 静态库及动态库的编写和使用
最近在整理旧电脑时,发现了一些刚入行时的学习记录,以及最早使用新浪博客 http://blog.sina.com.cn/zcshou 写的一些文章.最近要重拾 Linux,所以把这些 Word 文 ...
- Linux学习:静态库和动态库
1.库是什么? 将"源代码"变为"二进制格式的源代码"(作用:加密,别人可以用,但不知道其中的内容). 2.库制作出来之后,如何给用户使用? 需要给用户:头文件 ...
- linux 动态库建立,浅析linux下静态库和动态库的建立和使用
在粤嵌学了很多知识,我们把常用的公用函数放在一起做成一个函数库,可以供其他程序共同使用,函数库本质上说,是一个可执行代码的二进制形式,可被操作系统载入内存执行.linux下的库分为两种:静态库,后缀名 ...
最新文章
- XPath 详解,总结
- Jakarta Commons Logging学习笔记
- 关于域帐户将计算机加入域登陆上限问题
- 岳翔南京大学计算机,基于组合IIS路径抽取的组合线性混成系统有界可达性分析-中国科学.PDF...
- Educational Codeforces Round 114 (Rated for Div. 2) D. The Strongest Build 暴力 + bfs
- 规范化流程化提交自己代码到远程gitlab服务器
- scipy.sparse.csr_matrix函数和coo_matrix函数
- python docx 表格打印不显示_python-docx 设置 word 文档中表格格式
- 09年全年的case处理总量
- 【hdoj1021】类斐波那契数列的循环节(f[i]能否mod3?找规律)
- maven 手动安装 ojdbc7
- python tk隐藏窗口_显示tkinter消息框时,隐藏root窗口
- 时域采样与频域采样实验【matlab】
- 从windows向vmware中传输压缩包出现的压缩包异常的问题
- 欧拉公式求四面体的体积
- 用matlab产生chu序列和frank序列
- 详解如何在Sbo Add-on开发中使用Folder控件
- ImmunoChemistry艾美捷总细胞毒性试验试剂盒方案
- Android开门动画和关门动画的实现
- JVM-三色标记算法