欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 社会 > Linux动静态库

Linux动静态库

2025/2/25 21:05:12 来源:https://blog.csdn.net/qq_64521581/article/details/145784917  浏览:    关键词:Linux动静态库

铺垫

1.动态库与静态库本质上都是文件。

2.关于动静态链接

动态链接:将库中需要的函数地址,填入可执行程序,建立关联。

优点:节省资源,不会出现太多重复代码。

缺点:对库的依赖性比较强,一旦库丢失,所有使用这个库的程序将无法运行。

静态链接:将库中的方法的实现拷贝到我们可执行程序中。

优点:不依赖库,同类型平台中可以直接使用。

缺点:可执行程序的体积比较大,比较浪费资源。

3.默认编译程序,用的是动态链接的,如果要静态链接 需要加上 -static

4.库的命名一般是libXXX.so(动态库)    libXXX.a(静态库),真实的库名称是XXX的部分。

动静态库的制作和使用

为什么要有库?

1.提高开发效率

2.隐藏源代码

首先写几个简单的.c .h代码

myprint1.h1 #pragma once                                                                                        2 #include<stdio.h>3 4 void print1();
myprint1.c1 #include"myprint1.h"2 3 void print1()4 {5   printf("hello 111\n");                                                                            6 }
myprint2.h1 #pragma once                                                                                        2 #include<stdio.h>3 4 void print2();
 myprint2.c1 #include"myprint2.h"2 3 void print2()4 {5   printf("hello 222\n");                                                                            6 }
main.c1 #include"myprint1.h"                                                                                                                                                                                           2 #include"myprint2.h"3 4 int main()5 {6   print1();7   print2();8   return 0;9 }

以往都是直接进行编译

gcc -c myprint1.c
gcc -c myprint2.c
gcc -c main.c

 生成同名.o

最后链接形成可执行文件

gcc -o test.exe main.o myprint1.o myprint2.o

总结如下图 


静态库文件的制作

首先编译成.o的文件

gcc -c myprint1.c
gcc -c myprint2.c

打包.o的所有文件成为libmyc.a

ar -rc libmyc.a myprint1.o myprint2.o

-c(creat)如果没有libmyc.a则自动创建

-r(replace)如果已经有libmyc.a,且myprint1.o myprint2.o内容发生改动,则重新创建libmyc.a替换原文件

最后生成libmyc.a静态库文件。


静态库文件的使用

进行编译链接main.c文件

gcc main.c -lmyc -L .gcc 要编译的文件 -l库的名字 -L 库所在的路径

这里编译器会在指定的目录下寻找库,不会在当前目录下寻找,所以加上-L选项指定路径让编译器寻找。 

生成a.out可执行文件。


动态库文件的制作

gcc -c -fPIC myprint1.c
gcc -c -fPIC myprint2.c-fPIC:产生位置无关码

形成.o文件。

动态库打包,不需要其他工具,只用gcc。
gcc -shared -o libmyc.so myprint1.o myprint2.o

生成libmyc.so动态库文件。


动态库文件的使用

gcc main.c -L. -lmyc
-L要链接的库在哪个路径下
-l要链接的库

最后产生a.out可执行文件。

注意:

如果头文件不在当前目录,编译时需要使用 -I 选项。

总结来说:

gcc -o mytest main.c  -L链接库所在路径  -l链接库名字  -I头文件的路径

一个小指令:

ldd a.out
查看可执行文件链接的库文件

 编写库的人:未来要给别人(用库的人),交付的是:头文件+库文件,不给源代码。

总结

文件在编译成可执行文件后,

1.如果链接的是静态库,运行时就不需要静态库了。

原因是:静态链接是将库中的方法的实现拷贝到我们可执行程序中。

2.如果链接的是动态库,运行时还需要动态库。

对于动态库:

编译时的库搜索路径 ---- 编译器gcc

运行时的库搜索路径 ---- 操作系统OS

二者可以相同,也可以不同,操作系统通常是去系统默认的搜索路径(Linux中默认为/lib64 )去搜索库文件,编译器也默认去/lib64中搜索(可以通过指令更改 gcc -o mytest main.c  -L链接库所在路径  -l链接库名字 )

3.在编译的预处理阶段已经将头文件展开在目标文件里了,所以运行时不需要头文件。

解决运行时动态库找不到问题的四种方法

1.最普通的方法(库比较官方时推荐)

把库拷贝到操作系统默认的搜索路径(Linux中默认为/lib64 ),既可以支持编译,又可以支持运行。

2.环境变量法

查看LD_LIBRARY_PATH环境变量
echo $LD_LIBRARY_PATH
把运行时需要查找库的目录添加
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:运行时需要查找库的路径

缺点:重启机器后,环境变量会恢复。

解决方法:需要更改配置文件,这样机器每次启动时读取配置文件,都会把目录加上。

在初始目录下,有2个配置文件 .bash_profile  .bashrc

打开 .bashrc文件

看到最后一行,就是每次重启时配置的环境变量,在最后追加上库的路径即可(:冒号作为分隔符)。 

3.软链接方法(库不是官方,个人写的,推荐这种方法)

sudo ln -s /动态库的绝对路径  /lib64/同动态库名

这时在/lib64目录下有一个动态库的同名文件指向原路径下的动态库。 

4.配置文件法

/etc/ld.so.conf.d 路径下建立一个文件 文件名.conf   在该文件下放置动态链接库的路径即可。

/etc/ld.so.conf.d 的含义:ld:load加载  .so:动态库  .conf:配置文件  .d:目录

最后要让刚才的配置文件生效使用如下命令。

sudo ldconfig

关于同时存在动静态库时链接问题

1.如果同时提供动态库和静态库,gcc默认使用动态库

2.如果非要使用静态库,就必须使用static选项

3.如果只提供静态库,则只能对该库进行静态链接,但是程序不一定整体是静态链接的。

4.如果只提供动态库,默认是只能动态链接,若强行静态链接,会报错!

理解动态库加载

1.系统角度理解动态库加载

动态库加载后会被映射到进程的共享区中。

当进程1的代码mytest1需要用到myc动态库时,OS将该库加载到物理空间,进而通过页表映射到进程1的虚拟地址空间的共享区中,以使用。


添加一个进程,当进程2的代码mytest2再次需要用到 myc动态库时,直接将物理空间中该动态库的代码和数据,通过页表映射到进程2的虚拟地址空间的共享区中即可。

库只需要被加载一次,然后在不同的进程地址空间中进行映射,就可以使用该库,这就是为什么叫做动态库,共享库。


一些问题:

1.谁来决定,哪些库加载了,哪些库没加载?OS会自动完成。

2.系统中可不可以同时存在非常多已经加载的库?肯定的。

操作系统要对这些库进行管理,通过一个结构体struct loadlib进行描述已经加载的库,再通过链表进行增删查改管理。

struct loadlib
{char* libname;void* addr;……struct loadlib* next; 
}

2.关于编址

可执行程序本身是有自己的格式信息的。 

1.如果我们的可执行程序,没有加载到内存中,我们的可执行程序有没有地址(每行代码生成的指令的地址) ?有,程序通过编译后就有了地址。我们来验证。

objdump -S a.out > test.s在 Linux 中,使用 objdump -S a.out > test.s 命令可以将可执行文件 a.out 的反汇编代码(-S选项:混合源代码和汇编代码)输出到 test.s 文件中。

生成的 test.s 文件包含:

  • 机器指令的汇编表示(如 movcall)。

  • 虚拟地址(逻辑地址):每条指令在进程虚拟地址空间中的位置。

可以看到可执行程序在没有加载进内存时就已经有了地址(每行代码生成的指令的地址) 

其实我们的可执行程序,在没有加载之前,也已经基本上被按照类别(比如权限,访问属性等)已经将可执行程序划分成为各个区域了。如下:

size a.out

2.我们进程地址空间里面的很多地址数据属性数据,是从可执行程序来的。

程序执行的起始地址,存储在可执行文件头(如ELF的e_entry字段)。操作系统加载程序后,CPU从此地址开始执行。

编址的两种方式:绝对编址,相对编址

虚拟地址(线性地址:通常指分页机制中的地址(虚拟地址空间中的地址),即虚拟地址通过页表转换后的地址称为物理地址

平坦模式:Linux将代码段、数据段等所有段的基址(Base Address)设为0,段限长(Limit)设为最大值。

绝对编址:是指在编译和链接中确定所有代码和数据的固定地址,这意味着程序中的每个指令和数据项都使用固定的、绝对的内存地址进行引用。这种编址方式也称为平坦模式

绝对编址的范围:32位平台下 [0,4GB]

对于代码和数据的每一个地址都是绝对确定的,这就是未来的虚拟地址,可以对应到虚拟地址空间,填充到页表。

虚拟地址空间本身不仅是OS要遵守,编译器编译程序时也要遵守。

相对编址(逻辑地址:是指地址是相对于某个段的偏移量,通常用于表示程序中的变量、函数等的位置。逻辑地址在编译时生成,并且与具体的内存布局无关。通俗来说,如果我们将数据段与代码段分开,代码段从0开始,数据段从0开始,通过偏移量来描述地址的方式,称为相对编址。

Linux通过平坦模式(段基址为0),使逻辑地址的偏移量直接等于线性地址(虚拟地址),后续仅需分页机制转换为物理地址,从而简化内存管理。

总结:Linux逻辑地址 = 虚拟地址(线性地址)

3.理解动态库动态链接和加载的问题

a.一般程序加载

几个问题:

1.地址空间既然是一个数据结构的对象,那么谁来用什么数据初始化?

可执行程序加载时,会用可执行程序头部相关字段,来把地址空间这个结构体对象进行初始化。

所以不同程序就会有不同的正文区域,初始化区域,未初始化区域的大小。

2.代码是数据吗?是的。

所谓的地址空间,本质是由操作系统+编译器+CPU,三者共同配合完成的! 


b.动态库的加载

静态库没有这一说法,一旦静态库被使用,那么静态库文件以绝对编址的方式写入代码中,随代码加载进物理地址。

对于动态库加载:

库的数据和方法的访问,都是通过库所在地址空间的起始地址+程序内部的偏移量即可。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词