Linux 动静态库

目录

一、前言

二、设计静态库

1、准备工作

2、设计静态库

3、使用自己写的静态库

4、得到一些结论

三、设计动态库

1、准备工作

2、设计动态库

3、使用自己写的动态库

4、得到的一些结论

四、动态库是怎么被加载的 


一、前言

  • 动态库:libc.so  libc++.so。
  • 静态库:   libc.a   libc++.a。

在命名的前缀lib和后缀so/a去掉就是库名,所以这是C/C++的动静态库。

C/C++ 体系中如何使用别人的功能?

生成可执行的方式有静态链接和动态链接,对应的是静态库和动态库。


比如,张三是一名大一新生,他在写作业时突然有了一个上网的需求,但因为周边环境不熟悉,所以找了学长询问附近网吧的地点,随后就跑去玩了几个小时,然后回来后接着继续写作业,这就叫作动态链接。两年后张三已经是一名大三师兄了,要开始准备找工作了,家里给他买了一台电脑,当他正在学习时想要上网就不用再特意跑去网吧了,而只需要打开自己的电脑就行,也就是说静态链接并没有和外界产生关联。

一般我们写的程序中大部分都是动态链接,因为动态链接中不需要把库中的内容进行过多的拷贝,所以相对而言,它的编译效率较高,这是其一;我们有时候下载一些软件,比如 VS2019,它上面会有一些组件,比如 C++、C# 等,但这些组件并没有在我们的硬盘中,而当我们要安装时,它会帮我们找到这些组件下载,这样有个好处就是我们需要什么就直接下载什么,而不是直接一堆东西直接装在机器上,而自己需要使用的组件却寥寥无几,所以这里就采用动态库的方式实现,这是其二。

当然静态链接也有属于自己的使用场景,一般在服务器上大部分也都是动态链接,不过有时候需要将服务在很多机器上部署,那么单纯的动态链接就可能会出问题,因为动态链接是在程序运行之后才去对应加载到内存中,万一有个库丢失了,那么程序就挂了。所以有时候一些程序它也会采用静态链接,好处就是它不依赖于任何动态库,坏处就是效率比较低。比如静态链接的大小是动态链接的一百倍,要把静态链接这个程序下载下来就只能全部下下来,而动态链接则是边下边用。总的来说,两者各有利弊,没有绝对的好坏之分。

  • 动态链接的生成的可执行程序的体积往往比较小,节省资源(磁盘、内存),但是它依赖第三方库,有一定的风险,一旦库丢失,可执行程序不可执行。
     
  • 静态链接虽然生成的可执行程序的体积较大,浪费资源(磁盘、内存),但是它不依赖第三方库,一旦库丢失,可执行程序依旧可以执行。
  • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码

二、设计静态库

1、准备工作

如果我们想把我们自己写的方法给别人使用

  • 把源文件直接给他。
  • 把源文件想办法打包成库(库内容+.h)

2、设计静态库

静态库是比较简单的,其实我们一开始编译链接生成可执行程序的时候本身就需要将其的.o文件给生成出来,然后再进行链接,我们可以把这些文件生成.o文件,打包形成一个库,再与main.o程序链接生成可执行程序。

gcc -c myadd.c -o myadd.o/gcc -c myprint.c -o myprint.o

ar -rc libmyc.a myadd.o myprint.o

ar (Archive) 是  gnu  归档工具, rc  表示 replace and create,意思就是如果要生成的库中已经包含了对应的 .o 文件,就 replace,否则就 create 。

如果我们想查看这个静态库的目录文件

ar -tv livmyc.a

这里我们用makefile重新写一遍,在上面的基础上我们写一个发布。

这个时候我们可以用make output命令一键发布,并且把对应的.h文件和.a文件cp到lib文件中,这样如果我们想把自己写的库给别人用我们就可以把这个lib文件发给别人。 

3、使用自己写的静态库

创建一个main.c文件

编译发现找不到myadd.h

注意,这里我们需要加上绝对或者相对路径,因为它只会在系统中的库里面和我们当前目录里面去寻找头文件,所以这里必须加上路径

如果我们非要直接用mymath.h,那么我们需要加上-I选项,这个I代表的就是include,即不仅在当前目录中找,如果找不到,就去指定目录下找

 这里头文件找到了,实际错误是链接错误,找不到这个库,gcc只能去系统中,如lib64下去寻找,显然这是找不到的,所以我们要加上-L选项,这里的L就是lib的首字母,然后指定对应的路径

其实这里是需要链接具体的库,那么为什么我们在-I的时候不需要指定头文件呢?这是因为我们的代码里面已经有头文件了,所以只需要告诉路径即可。但是库不行,因为可能有很多个库,到底链接哪个我们不清楚,需要指定。

所以这里我们就还需要一个-l选项,注意这里是库的真实名字,需要去掉lib前缀和.a后缀,且一般这个l紧跟库名称。

这个myc就是我们自己写的库的名称,去掉了前缀lib和后缀.a。

这就是我们设计静态库以及使用自己写的静态库的全过程了。


像我们以前的C/C++代码是不需要这么麻烦的,因为它是可以找到对应的静态库和头文件的,并且gcc是可以认识C/C++的动态库或静态库的。如果我们使用第三方库的话,就必须这样做了

那么我们怎么样可以做到像C/C++里面的库那么方便呢?

首先我们可以直接将这个头文件拷贝到系统对应的include中,静态库拷贝对应的lib64中,这样的话,我们就只需要指明-l就可以编译过去

sudo到我们系统默认路径下的过程本质上就是库的安装!

也可以通过软链接的方式进行编译。

4、得到一些结论

  1. 我们使用第三方库的时候,往后使用时,一定要带上gcc -l选项。
  2. 如果系统中只提供静态库,那么就按照静态链接,否则默认动态链接。也就是说一个可执行程序可能是静态和动态混合链接的(下面解释)
  3. 如果系统要链接多个库,gcc -l能够帮助链接多个库。

三、设计动态库

1、准备工作

创建mylog.c和myprint.c 并组成动态库。

2、设计动态库

动态库和静态库一样,都需要把.c文件形成.o文件。

fPIC:与位置无关码,这个后续会给大家具体介绍。

接下来我们将这两个.o文件打包形成库,ar命令是专门用来打包静态库的,而我们现在需要打包动态库,需要以下操作

我们还是用gcc,因为gcc默认就是动态链接,我们需要加上一个shared共享的选项,代表告诉gcc,我们不要生成可执行程序,而是生成一个库,其他的和直接生成可执行程序的步骤基本是一样的

gcc -shared -o libmymethod.so *.o

 

我们能看到静态库是没有可执行权限x的,而动态库却有,但这不代表这个文件能被执行,这个动态库连main函数都没有。

对于静态库,它的作用就是直接将静态库中的内容拷贝过去。从此以后这个程序的死活就与这个静态库没有任何关系了。更重要的是他是不会被加载到内存当中的。

当我们在运行程序的时候,当要用到动态库的时候,就会跳转到动态库中,从而使得动态库加载到内存中。而要加载就要可执行权限。

所以这个可执行权限就是我们这个文件是否会以可执行程序的方式加载到内存中。库虽然没有main函数,但是它有方法,它以后也要被调用,它也是可执行程序的一种,具有可执行程序的特征的。它不是不能执行,而是不能单独执行,需要别人来用它。

我们还是跟写静态库一样,写一个完整的makefile和一键发布output

make命令直接写成动态库libmyc.so

 

make output命令一键打包成一个文件,当我们想把我们写的库给别人用的话,我们直接把这个libso文件发送给别人即可。

 

3、使用自己写的动态库

 

我们直接编译发现出现跟静态库一样的错误,大致意思也是说找不到这个头文件

 那我们就使用和静态库一样的办法吧,发现运行该此执行文件发生了错误

我们用ldd命令查看该可执行文件的链接方式,发现我们自己写的库没有链接上。

我们不是已经告诉了这个动态库在哪里吗?为什么在运行的时候还是找不到呢?

这里需要注意这里我们的告诉动态库在哪,告诉的是编译器,一旦当程序形成以后,就和编译器没关系了。这里是加载的时候没找到,所以说,动态库在哪里,也得告诉加载器

那么为什么这里找不到呢?更关键的是,我们以前的C语言中它为什么能找到呢?

所以其实加载的时候也要有对应的路径,而之前的C语言中是因为在系统中有一个默认的搜索路径去搜索动态库,而我们现在写的动态库,是找不到的

现在我们有4种办法

把库的路径加载到系统默认路径下 /lib64或者/usr/lib64下。

在系统默认路径下/lib64或/usr/lib64下建立软链接

将自己库所在的路径,添加到系统的环境变量LD_LIBRARY_PATH中

 在/etc/ld.so.conf.d下建立自己的动态库路径的配置文件,然后重新ldconfig即可

我们知道,环境变量每次重新启动的时候都会消失,除非我们写在对应的文件中

所以我们还有一种更简单的办法

ls /etc/ld.so.conf.d/

我们可以注意到这里有很多的对应的配置文件 

这些配置文件里面放的全是路径,只要我们这里建立一个conf文件,然后把我们的动态库路径放上去,系统自然而然就找到了

然后用ldconfig这个命令,最后也能发现代码可以运行了

并且这种方式即便我们将Xshell给关闭了,它依旧是有效的。

4、得到的一些结论

  1. 动态库在进程运行的时候,是要被加载的(静态库没有)
  2. 常见的动态库被所有的可执行程序(动态链接的),都要使用,所以动态库也称共享库

所以,动态库在系统中加载之后,会被所有进程共享。

四、动态库是怎么被加载的 

先看我们以前的这个图,当磁盘中的可执行程序被加载到内存中的时候,那么就会创建task_struct然后里面会有一个进程地址空间,通过页表与物理内存建立映射。在磁盘的这个文件被加载到内存的过程中,会找到对应的inode从而可以读取到文件的属性,同时也会去找到对应的文件的内容,按照一页一页的方式加载到内存当中。

当未来在开启一个进程的时候也是一样的道理。

 

所以此时第一个进程挂掉了,并不会影响第二个进程

我们也知道动态库其实也是一个文件,当我们第一个程序运行的时候,需要执行printf了,需要用到动态库里面的函数了,所以就会将动态库给加载到内存当中。然后在页表上建立映射,这个是在共享区上的。

当运行的时候,就会从代码区跳转到共享区,执行完以后,在跳转回去 

所以这样的话:建立映射了以后,从此我们执行的任何代码,都是在我们的进程地址空间中进行执行的!!!

所以我们还知道一个事实:系统在运行中,一定会存在多个动态库,OS管理起来,先描述,在组织,系统中,所有库的加载情况,OS非常清楚

所以当未来第二个进程,还需要用到这个同样的共享库的时候,就不会再去加载了,而是直接使用了

所以这个库叫做动态库,也叫做共享库,他是通过地址空间加上页表进行完成的,就可以实现对所有进程的共享。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值