文章目录
- 1.创建静态库
- 2.使用静态库
- 3.动态库
1.创建静态库
- 预备知识:
gcc main.c mymath.c myprint.c -o my.exe
/gcc main.o mymath.o myprint.o -o my.exe
三个.c/.o文件可以编译成为可执行程序
- 编写源文件与头文件[库的源文件没有main函数]
- 将所有的源文件编译生成.o文件。(只提供.h和.o,用户也可以编译运行。⇒ 太麻烦,易出错 丢失文件不好找)
- 将所有的.o文件归档生成静态库。
ar -rc libname.a self1.o self2.o
. : r: replace c: creat
- ar: archive files: gnu归档工具,用于归档生成静态库
-
发布静态库:将头文件统一放在include文件夹下 库文件统一放在lib文件夹下 include和lib放在self下
-
利用Makefile自动化生成发布静态库[发布版的库可以打包成压缩文件给other使用]
2.使用静态库
前提: 用户文件夹下有main.c + 发布版的静态库
- 法一: 将头文件和库文件直接拷贝到系统的默认搜索路径下[将库拷贝到系统默认搜索路径下的操作⇒ 库的安装]
gcc头文件的默认搜索路径:/usr/include
gcc库文件的默认搜索路径:/lib64 或者 /usr/lib64
sudo cp -rf self/include/* /usr/include/
.
sudo cp -rf self/lib/libself.a /lib64
.
- C语言默认搜索的库ls /lib64/libc.a
法一下运行main.c:gcc main.c -o test -l self
. -l: 指定所要链接的库
- 法二: 通过-I和-L选项指定头文件和库文件的所在路径 -l指明要链接的库
3.动态库
预备知识
- 使用静态库 是把代码加载到我们要运行的可执行程序中
使用动态库 是让我们的代码和库产生关联 gcc -fPI
C: 形成与位置无关的二进制.o文件
静态库只能存在于我们程序地址空间的特定位置 — 绝对地址 — 与位置有关
动态库在任何位置都能被加载 — 相对地址 — 与位置无关
==> 可在内存中的任意位置加载和执行的代码,不依赖于特定的内存地址。
- readelf -S命令用于显示一个目标文件或者一个二进制可执行文件的节表(section header table)的内容。节表包含了目标文件中的不同节(sections)的详细信息,例如代码段、数据段、符号表等。
- ldd a.out命令用于显示一个可执行文件或者共享库文件所依赖的动态链接库(shared library)。该命令会列出执行文件使用的动态链接库及其相对路径
- gcc 默认使用动态库 没有动态库 也没办法
只有静态库时 针对该库 进行静态链接 ⇒ 拷贝式链接 其余仍使用动态库
带上-static选项 只能使用静态库 ⇒ 摒弃默认优先使用动态库的原则 ldd时:not a dynamic executable
创建动态库
- 编写源文件与头文件
- 将所有的源文件编译生成目标文件[生成动态库的目标文件要加-fPIC选项,生成与位置无关的目标文件]
- 将所有的目标文件归档生成动态库
- 打包发布动态库:为动态库创建目录,并将头文件和库文件分开存放。
- 自动化生成并发布动态库
动态库的加载 文字解释来源: 芥末虾动态库
- 动态库没有被合并到可执行程序中,动态库与可执行程序是被分批加载到物理内存中的。⇒ 动态库是一个独立的库文件 可以和可执行程序分批加载
- 动态库被进程首次调用时,动态库代码被动态库链接器加载到物理内存,通过页表建立与地址空间的映射关系。动态库的全局变量和函数都会被映射到地址空间的共享区。
- 动态库代码采用相对编址的方式,其全局变量和函数在编译和链接时分配到的地址是相对地址(相对库起始地址的偏移量)⇒ 动态库代码与位置无关,每次运行程序,运行不同的实例,都可能会被映射到不同的虚拟地址处。
- 库的运行时虚拟地址(变化的) = 库的起始虚拟地址(变化的) + 库的相对地址(编译链接时分配);
- 如果有多个进程同时调用了同一个动态库,该动态库只需要向内存加载一次。之后凡是调用该动态库的进程只需要计算运行时地址,建立页表映射关系即可。⇒ 动态库也被成为共享库。
静态库的加载
- 静态库代码会同程序代码合并在一起进行编译和编址形成可执行程序,程序运行时可执行程序被程序加载器加载到物理内存,并通过页表映射到进程地址空间,程序的代码会被映射到的代码区,全局变量会被映射到静态区。
- 静态库代码采用绝对编址的方式,其全局变量和函数的虚拟地址在编译和链接时就已经确定,这个地址是固定的线性地址,不管程序运行多少次,运行多少个实例,它的地址始终是确定的,而且是唯一的线性地址。
动态库的使用
- 法一: 将头文件和库文件直接拷贝到系统的默认搜索路径下[不推荐]
- 法二: 通过-I和-L选项指定头文件和库文件的所在路径 -l指明要链接的库
报错: 找不到动态库 ⇒ 动态链接器运行时链接库缓存中没有对应动态库的路径
不是已经指定库了吗?
-l self
那是给gcc命令说的 运行加载时 需要告知OS加载器库的位置 与gcc无关
如何向动态链接器缓存中添加动态库路径?
- 将动态库拷贝到/lib64路径下
或者
在/lib64路径下创建动态库文件的软链接 加载器默认会在该路径下进行搜索[不推荐]ln -s 绝对路径 /lib64/linself.so
- 在环境变量LD_LIBRARY_PATH中添加动态库的所在路径,加载器也会在环境变量中的路径下搜索动态库。[只是修改了bash缓冲区中的内容。只在本次登录中有效]
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH: 路径
. - 在bash的资源配置文件.bashrc/.bash_profile中,添加登录时导入LD_LIBRARY_PATH环境变量的操作**[不推荐]**
- 在/etc/ld.so.conf.d/目录下添加配置文件,永久性添加动态库的搜索路径。ldconfig(加载配置文件 当配置文件发生改变 使用此命令 更新缓存!)是一个用于配置动态链接器运行时的命令。它用于更新动态链接器的运行时链接库缓存,以便在程序运行时能够正确地找到和加载动态库。
sudo vim /etc/ld.so.conf.d/selflib.conf
在selflib.conf中存入/home/lhr/linux/lib/uselib/librelease/lib
库存在的意义
- 使用者的角度:大大减少使用者的开发周期;提高软件本身的质量
- 开发者的角度:使合作开发简单;隐藏源码,提高代码安全性
动静态库总结
- 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
- 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
- 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
- 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
5.动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间
优缺点
静态链接的优点:
- 执行速度快:静态链接的程序在运行时不需要额外的时间来加载和链接库文件,因此执行速度比较快。
- 安全稳定:静态链接的程序不依赖于外部的动态链接库,因此在运行时可以更好地保持程序的稳定性和兼容性,不受外部环境的影响。
- 独立性高:静态链接将所有的代码和库文件都打包在一起,使得程序具有很高的独立性,可以在没有其他依赖的系统上运行。
静态链接的缺点:
- 占用空间大:静态链接将所有的库文件都打包在一起,因此占用的存储空间比较大。
- 更新困难:如果某个库文件发生了更新,需要重新进行静态链接,这样会导致重新编译整个程序,对于大型程序而言更新不方便。
动态链接的优点:
- 空间利用率高:动态链接共享库文件,多个程序可以同时使用一个库文件,减少了存储空间的占用。
- 更新方便:如果某个库文件发生了更新,只需要更新一次库文件即可,不需要重新编译所有使用该库文件的程序。
动态链接的缺点:
- 执行速度相对较慢:动态链接的程序在运行时需要加载和链接库文件,会增加一定的执行时间。
- 对外部环境依赖较高:动态链接的程序需要依赖外部的动态链接库文件,因此在不同的环境中可能会出现兼容性问题
有趣的库
每一个动态库被加载到内存,映射到进程的地址空间,映射的位置可能是不一样的但是因为库里面是相对地址,每一个函数定位采用的是偏移量的方式找的
换句话说,只要知道这个库的相对地址,库的起始地址+函数偏移量 = 就可以在自己的地址空间中访问库中的所有函数[有访问权限]
-
ncurses库是一个用于终端控制和终端用户界面(TUI)开发的C库。它提供了一组函数和数据结构,允许开发人员在文本终端或虚拟终端上创建复杂的用户界面,包括窗口、标签、按钮、菜单等。通过使用ncurses库,可以在终端上创建基于文本的用户界面,实现像图形界面一样的用户交互。[字符的界面库]
-
boost库是一个C++库集合,提供了许多高质量的、被广泛接受的C++库,旨在增强C++语言的功能和功能。boost库包含了众多的组件和模块,提供了字符串处理、容器、算法、并发编程、日期和时间处理、正则表达式、文件系统操作等功能。通过使用boost库,可以简化C++开发,提高代码的可读性和可维护性,同时提供了许多常用的功能模块,能够快速开发高效的C++应用程序。[准标准库]
两个库的主要区别在于应用场景和功能:ncurses库主要用于终端用户界面的开发,而boost库主要用于增强C++语言的功能和提供常用的功能模块。同时,ncurses库是C库,而boost库是C++库。