【Linux】静态库和动态库

avatar
作者
猴君
阅读量:0

Linux为什么不允许普通用户给目录建立硬链接呢?

image-20230116092715777

系统层面上有.和…硬链接指向目录。假设我们是超级用户,允许给目录建立硬链接,给根目录建立硬链接,从根目录开始查找,当查找硬链接的时候就是根目录,这时候递归式查找,形成了环路查找,最后导致软件无法正常进行查找工作!所以不允许普通用户给目录建立硬链接。

文章目录

一、动态库和静态库

具体的动态库和静态库的相关内容👉点击跳转

Linux的库一般分为动态库和静态库:

静态库(.a):库文件以.a为后缀,程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
动态库(.so):库文件以.so为后缀,程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。

链接的本质:无非就是我们调用库函数的时候和标准库是如何关联的问题

库的名称:去掉前缀lib去掉后缀.so/.a剩下的就是库名称,比如libc.so就是C库

gcc 在编译时默认使用动态链接,而生成静态链接,我们需要在末尾带上-static:

img

安装静态库:

sudo yum install -y glibc-static sudo yum install -y libstdc++-static 

二、理解库

如果不想给对方我们的源代码,我们可以选择给用户提供我们的.o可重定位目标二进制文件(gcc -c 文件)与.h头文件。让用户用我们提供的.o文件进行链接即可。在编译时,只要把源文件编译成.o文件在将其链接便可形成一个可执行的程序

img

通过gcc -o生成,不出意外,编译运行成功:

image-20230116133214810

难道就这么简单吗?我们可以给对方提供.o(方法的实现),同时还有提供.h(里面包含着都有哪些方法),此时对方是能用的。但是如果存在很多.c文件呢?难道我们要把几千个.c文件全部编译成.o在加上头文件全部一个一个提供吗?那样太过于麻烦,为了让用户更好的使用库,我们就有把所有的.o文件打成一个包,给对方提供一个库文件即可!把多个.o合并成一个文件,这个文件就是库,把包方式的不同就分为了动态库和静态库*。

库的本质就是.o文件的集合。


准备的文件:

image-20230118130259209

三、制作静态库

我们如果想自己写一个库,要不要在这个库里面写main函数呢?答案肯定是不要的,库是被别人用的,自己写的main会和库里的main发生冲突。我们可以站在编写库的角度和使用者的角度来制作:

编写库:创建Makefile:

image-20230117175841393

将文件编译成.o文件

ar命令:把所有的.o打包起来,ar是归档。

-rc代表replace和create;比如:

ar -rc libmymath.a my_add.o my_sub.o 

output:而我们要交付库,实际上要把库文件a.so以及匹配的头文件都交付给用户,而output就是一个发布的过程

image-20230117182852030

使用

image-20230117222131413

image-20230117215933458

此时,我们用户如果需要只要把mylib.tgz拷贝过去:比如:cp mylib.tgz …/test,拷贝过去之后进行解压: tar xzf mylib.tgz

image-20230117222548962

而所谓的安装其实就是在拷贝。直接把安装好的库使用起来:

头文件找不到?

编译器搜索头文件时默认在当前目录下搜索,在系统默认指定路径下搜索。虽然此时的mylib在当前路径下,但是头文件太深了,编译器找不到头文件,所以我们需要给gcc指定路径。带上-I ,指明在当前目录下的mylib目录下查找

image-20230117225023455

问题又来了,找不到库函数的实现。我们在形成可执行程序的时候,库文件要使用的话也要知道库所在的路径在哪里,系统的默认路径是/lib64。所以我们要带上-L。告诉库的路径在哪里。

但是如果要链接第三方的库,必须去指明库的名称(注意去掉前缀和后缀!)!!!也就是说,一定要告知路径下哪一个库,即使只有一个库,也要明确告知gcc要链接哪一个库(虽然我们以前写代码的时候,从来没有指明过库名称,这是因为gcc/g++默认帮我们填了,可以识别C/C++自带的库。自己写的要指明)

image-20230117231655995

最终终于顺利完成!

上面说了那么多,总结一下:

-I:指明头文件的搜索路径

-L:指明库文件的搜索路径

-l:指明要链接哪个库,带上库的名称(去掉前缀和后缀)

gcc默认是动态链接的(建议行为)对于特定的一个库,究竟是动静态库,取决你提供的是动态库还是静态库。

库的安装(把库安装到系统头文件路径下):

把头文件和库文件拷贝进系统的路径下,gcc对于头文件的默认路径是:/usr.include;对于库文件的默认路径是:/lib64:

image-20230117235724073

但是不太推荐这样使用:第三方库并没有经过测试,自己写的会污染库里面的其他文件。


四、制作动态库

首先我们需要把库文件全部编译成.o文件,这里与静态库不同,需要带上选项 -fPIC,形成与位置无关码:

gcc -c -fPIC my_add.c 

什么是与位置的无关码的目标二进制文件:

静态库采用的是绝对编址

动态库采用的是相对编址,动态库中的指定函数的地址通过相对编址(库中的偏移地址+段起始地址):

image-20230118134110242

动态库打包:-shared

gcc -shared -o libmymath.so my_add.o my_sub.o 

image-20230118105431940

image-20230118110420288

使用动态库

image-20230118110927754

但是这样子就可以吗?我们直接运行mymath:

image-20230118111042185

运行不了,这是为什么?找不到库

我们此时已经告诉了库文件,路径和库名称,选项已经给gcc带上了。但是我们当编译完之后,和gcc还有关系吗?答案是无关的,接下来运行是和OS有关的,动态库是运行时才加载的,所以程序运行起来,OS和shell也是需要知道库是在哪里的!而我们自己制作的库并没有在系统路径下,OS无法找到!如何找到动态库:

把库路径添加到环境变量LD_LIBRARY_PATH,比如当前自己制作库的路径是 /home/hwc/dir/test/mylib/lib

image-20230118113229401

直接运行:

image-20230118113256657

但是我们自己定义的环境变量只是本次登录有效,如果想永久有效只能修改环境变量的配置,但是比较麻烦。想永久有效,除了把库拷贝到系统目录下之外,我们还有其他方法:

1.配置文件(/etc/ld.so.conf.d/):动态库进行对应搜索时可以采用自己定义conf文件找到动态库

image-20230118115114624

2.建立软链接直接找到对应的库

image-20230118121302599

把对应的动态库建立在系统的目录下:

image-20230118121733222

总结一下:

运行动态库

1、拷贝.so文件到系统共享库路径下, 一般指/usr/lib

2、更改 LD_LIBRARY_PATH

3、ldconfig 配置/etc/ld.so.conf.d/,ldconfig更新

4.创建软链接


五、动静态库的加载

静态库不需要加载,静态库把代码拷贝到可执行程序里,直接决定了当加载的时候在内存里代码和数据可能存在多份,会比较浪费空间,把静态库中拷贝到程序中的代码区里:

image-20230118135744938

动态库加上fPIC形成位置无关码,采用相对编址方案,在程序链接时对应库当中的偏移量添加到可执行程序,运行时一旦库加载进来,经过地址空间映射,把库映射到地址空间之后,库也就具备了起始地址,通过偏移地址和起始地址这样就可以找到访问的函数:

image-20230118141601439

系统层面上会维护动态库的起始地址,直接建立页表与内存的映射,也就可以跳转访问了,所以动态库加载一次就可以被多个进程共同使用了。而静态库可能有多个程序用了C库,加载到内存时,内存里可能会存在100份重复的代码。而动态链接不会出现重复的代码,减少内存。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!