【Linux】零基础学习动静态库打包

文章目录

  • 一、软硬链接
  • 二、动静态库
    • 理解动静态库
    • 静态库的打包使用
    • 静态库打包Makefile编写
    • 动态库的打包使用
    • 动态库打包Makefile编写
    • 对于小版本的理解
  • 总结


一、软硬链接


站在先前的讲过的文件系统的角度来看,下图test这个文件有自己独立的inode,在他的inode对应的block数据块当中存放的是test这个可执行程序的路径(包括文件名)。

在这里插入图片描述

并且如果此时将bin1/bin2/bin3/test的这个程序移动到其他位置,软连接则会无效。
在这里插入图片描述

一个文件可以既被软链接,又被硬链接,对文件的修改都会同步到链接的文件当中。



二、动静态库


库的命名:
取消前缀lib,去掉.之后的内容,剩下就是库的名字。

动态链接 – >动态库 libc.so
静态链接 – >静态库 libc.a

感性认知:
动态链接类似于每次要钱就找父母要一次钱。
静态链接类似家里的小金库就是你的,需要自己就可以满足。

gcc编译默认是动态链接的,相同的代码动态链接的程序小于静态链接的程序

//分别生成动静态链接的程序
[ljh@VM-0-11-centos 2.27]$ gcc test.c -o test_dynamic
[ljh@VM-0-11-centos 2.27]$ gcc test.c  -static -o test_static
[ljh@VM-0-11-centos 2.27]$ ll
total 876
-rwxrwxr-x 1 ljh ljh   8360 Feb 27 11:28 a.out
-rw-rw-r-- 1 ljh ljh     27 Feb 27 11:12 log.txt
-rw-rw-r-- 1 ljh ljh     68 Feb 27 11:28 test.c
-rwxrwxr-x 1 ljh ljh   8360 Feb 27 11:29 test_dynamic
-rwxrwxr-x 1 ljh ljh 861288 Feb 27 11:29 test_static

我们通过上面代码可以发现test_static的程序远远大过test_dynamic的程序,他们两个程序用的库一个是libc.so,一个是libc.a

ldd命令可以查看文件依赖的动态库:

[ljh@VM-0-11-centos 2.27]$ ldd test_dynamic 
	linux-vdso.so.1 =>  (0x00007fff813cd000)
	libc.so.6 => /lib64/libc.so.6 (0x00007fa5e59ce000)
	/lib64/ld-linux-x86-64.so.2 (0x00007fa5e5d9c000)

习惯: 一般,为了更好地支持开发,第三方库和语言库,都必须提供两个库,一个是静态库,一个是动态库,方便程序员进行二进制文件的生成。

动态链接vs静态链接
动态链接:体积小,节省资源(磁盘,内存),一旦库丢失,程序就不可以执行了。
静态链接:体积大,浪费资源(磁盘,内存),可能用C语言写10个可执行程序,会出现大量的重复的代码。但库丢失(不安装),不影响。



理解动静态库


专注于打包动静态库与使用动静态库

静态库:
目标:实现一个加减法的静态库。

分析:别人要知道我提供的库是要什么方法,我们当然可以把.c*,.h全部发给别人,但是如果今天我们不想给.c,我们可以将.c全部编译成.o,.o*打包成库,然后和.h放在一个文件夹里放给别人就可以了。

源代码需要私密性保护头文件可以告知有什么方法

步骤:
1. 编写.c和.h文件

[ljh@VM-0-11-centos bin11]$ ll
total 16
-rw-rw-r-- 1 ljh ljh 56 Feb 27 11:55 Add.c
-rw-rw-r-- 1 ljh ljh 53 Feb 27 11:55 Add.h
-rw-rw-r-- 1 ljh ljh 55 Feb 27 11:55 Sub.c
-rw-rw-r-- 1 ljh ljh 53 Feb 27 11:55 Sub.h

2. 采用ar -rc命令打包r:replace替换,create创建

我们下面创建一个库名为mymath的静态库,打包成lib发送给对端即可。注意是将.o文件打包。

[ljh@VM-0-11-centos bin11]$ gcc -c *.c

[ljh@VM-0-11-centos bin11]$ ar -rc libmymath.a *.o

[ljh@VM-0-11-centos bin11]$ ll
total 36
-rw-rw-r-- 1 ljh ljh   56 Feb 27 11:55 Add.c
-rw-rw-r-- 1 ljh ljh   53 Feb 27 11:55 Add.h
-rw-rw-r-- 1 ljh ljh 1240 Feb 27 12:31 Add.o
drwxrwxr-x 2 ljh ljh 4096 Feb 27 12:01 lib
-rw-rw-r-- 1 ljh ljh 2920 Feb 27 12:31 libmymath.a
-rw-rw-r-- 1 ljh ljh   55 Feb 27 11:55 Sub.c
-rw-rw-r-- 1 ljh ljh   53 Feb 27 11:55 Sub.h
-rw-rw-r-- 1 ljh ljh 1240 Feb 27 12:31 Sub.o
drwxrwxr-x 3 ljh ljh 4096 Feb 27 12:33 test

[ljh@VM-0-11-centos bin11]$ mkdir lib

[ljh@VM-0-11-centos bin11]$ cp *.h *.a lib

[ljh@VM-0-11-centos bin11]$ tree lib
lib
|-- Add.h
|-- libmymath.a
`-- Sub.h

0 directories, 3 files

有了lib,我们把lib发送给别人就可以,我们这里在本机创建多一个文件夹做测试。

3. 测试lib

[ljh@VM-0-11-centos bin11]$  mkdir test
[ljh@VM-0-11-centos bin11]$ cd test
[ljh@VM-0-11-centos test]$ cp ../lib . -rf



静态库的打包使用


李四今日拿到张三发的lib文件,看了看里面的.h文件,发现实现了Add,Sub,想拿来使用,于是他写了下面这段代码,但是却报错了。

[ljh@VM-0-11-centos test]$ vim main.c

[ljh@VM-0-11-centos test]$ cat main.c
#include
#include"Add.h"
#include"Sub.h"
int main()
{
  printf("%d\n",Add(10,20));
  printf("%d\n",Sub(10,20));
}

[ljh@VM-0-11-centos test]$ gcc main.c -o main
main.c:2:16: fatal error: Add.h: No such file or directory
 #include"Add.h"
                ^
compilation terminated.

[ljh@VM-0-11-centos test]$ pwd
/home/ljh/2.27/bin11/test

No such file or directory,李四查阅了原因,头文件只会在默认路径或main.c的同路径下查找/home/ljh/2.27/bin11/test,也就是这个路径,而.h放在了/home/ljh/2.27/bin11/test/lib下,李四这个时候理解了,查阅了方法,发现gcc带选项 -I ./lib即可.
随即他写下这段代码:李四看到报出了链接错误,那么肯定是找到了文件,但是却没有链接上,李四冥思苦想,上网查找资料。

[ljh@VM-0-11-centos test]$ gcc main.c -o main -I ./lib/
/tmp/ccBm9pNY.o: In function `main':
main.c:(.text+0xf): undefined reference to `Add'
main.c:(.text+0x2f): undefined reference to `Sub'
collect2: error: ld returned 1 exit status

李四最终发现库文件的路径没有指定,也就是.h他能找到了,但是.c找不到就是库没有找到,而gcc下 -L ./lib就能指定这个库,李四随即把代码改成下面,但是还是报了错误。

[ljh@VM-0-11-centos test]$ gcc main.c -o main -I ./lib/ -L ./lib/
/tmp/ccHZnoAe.o: In function `main':
main.c:(.text+0xf): undefined reference to `Add'
main.c:(.text+0x2f): undefined reference to `Sub'
collect2: error: ld returned 1 exit status

李四想了想,引入的库文件路径已经有了,头文件也已经能找到了,是不是要给出具体的库文件名呢?gcc下 -l mymath 后加库名称,代码改了之后,李四发现就能够正常运行了。

[ljh@VM-0-11-centos test]$ gcc main.c -o main -I ./lib/ -L ./lib/  -lmymath
[ljh@VM-0-11-centos test]$ ./main 
30
-10

注意1:
若发生以下错误,则是打包的时候错误的将.c文件给打包了。
[ljh@VM-0-11-centos test]$ gcc main.c -o main -I ./lib/ -L ./lib/ -lmymath
./lib//libmymath.a: error adding symbols: Archive has no index; run ranlib to add one
collect2: error: ld returned 1 exit status

注意2:
上面我们提供的静态库但是编译的时候是默认动态 链接,链接会变成静态链接。虽然file显示静态链接,但是ldd没有显示我们的staticlib库,说明对这个库采用的是静态链接的方式。
file 命令文件是否是链接状态

[ljh@VM-0-11-centos zhangsan]$ ls
main  main.c  stlib
[ljh@VM-0-11-centos zhangsan]$ file main
main: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=fc61dd09c0e83aa9d34292bb5dfcfd2322b5337f, not stripped 这里显示动态

但是ldd当中没有,说明staticlib库没有被动态链接。
[ljh@VM-0-11-centos zhangsan]$ ldd main
	linux-vdso.so.1 =>  (0x00007ffec1fd9000)
	libc.so.6 => /lib64/libc.so.6 (0x00007ff39e814000)
	/lib64/ld-linux-x86-64.so.2 (0x00007ff39ebe2000)

总结:
gcc选项:
-I + path 给定路径或默认路径当中查找,默认路径一般是 /usr/include。
-L + path:告诉gcc除了默认路径和当前路径,也要到我们的指定路径下找库文件,系统默认的默认库在/lib或/lib64当中。
-l +库名称 例如ls/lib64,我们能看到很多库,但是具体是要哪一个库我们是要去指定的。

为什么在C语言编译的时候,从来没有明显的使用-L,-I,-i等选项呢?
因为库文件和头文件在默认路径下gcc能找到,并且gcc是编译C代码的,默认就链接了libc,所以我们一个选项都不用带就可以编译程序。

我们的程序如何不使用这些选项也能编译程序
头文件和库文件分别拷贝到默认路径下(库的安装)。但是通常第三方库要调用的时候还是要带-l表明要链接哪一个库。

静态库打包Makefile编写

进行ar打包并创建stlib文件夹,只需要把stlib发送给别人就可以了。
代码:

[ljh@VM-0-11-centos dystlib]$ cat Makefile 
lib:Add.o Sub.o
	ar -rc libstaticlib.a *.o 
	mkdir stlib
	cp *.h stlib 
	cp libstaticlib.a stlib
Add.o:Add.c
	gcc -c $^
Sub.o:Sub.c
	gcc -c $^

结果:

[ljh@VM-0-11-centos dystlib]$ cd zhangsan
[ljh@VM-0-11-centos zhangsan]$ cp ../stlib/ .
[ljh@VM-0-11-centos zhangsan]$ tree stlib
stlib
|-- Add.h
|-- libstaticlib.a
`-- Sub.h

0 directories, 3 files
[ljh@VM-0-11-centos zhangsan]$ gcc main.c -o main -I ./stlib/ -L ./stlib/  -lstaticlib
[ljh@VM-0-11-centos zhangsan]$ ./main 
30
-10

动态库的打包使用


动态库的打包:
生成一个加减法的动态库dy_lib

  • 生成动态库 shared: 表示生成共享库格式 fPIC:产生位置无关码(position independent code)
    库名规则:libxxx.so
    调用动态库本质上是进程跑起来后由进程在共享区当中执行库文件的代码。
    进程执行代码区的代码的时候需要动态库的内容就可以去共享区当中执行。
    【Linux】零基础学习动静态库打包_第1张图片

动态链接时:
当我们有若干个进程都需要同一个同一个动态库的内容的时候,我们的内存当中只需要存一份实体。
静态链接时:
每个进程执行程序都会加载到代码区当中,内存当中对一份代码的实现可能存在多份实体。
【Linux】零基础学习动静态库打包_第2张图片
位置无关码:每个进程都有一个共享区,共享区的不同位置可以映射到相同的动态库代码,动态库代码也可以加载到内存当中的任意位置(页表的转换),每个进程就可以在打开的动态库文件里面执行对应的函数。
保证库当中有多少行代码,怎么执行都不会出错,与位置无关,即使有函数调用也能正常运行。总之不影响库的执行。

动态库打包Makefile编写

[ljh@VM-0-11-centos dy_lib]$ cat Makefile 
lib:Add.o Sub.o
	gcc -shared *.o -o libmymath.so 
	mkdir dylib
	cp ./*h dylib 
	cp libmymath.so dylib
Add.o:Add.c
	gcc -fPIC -c $^
Sub.o:Sub.c
	gcc -fPIC -c $^
.PHONY:clean
clean:
	rm -rf dylib

使用上与静态相比略有一点差异,会报错
cannot open shared object file: No such file or directory。

错误示范
[ljh@VM-0-11-centos zhangsan]$ cp -rf ../dylib/ .
[ljh@VM-0-11-centos zhangsan]$ ls
dylib  main.c
[ljh@VM-0-11-centos zhangsan]$ tree dylib
dylib
|-- Add.h
|-- libmymath.so
`-- Sub.h

0 directories, 3 files
[ljh@VM-0-11-centos zhangsan]$ gcc main.c -o main -I ./dylib/ -L ./dylib/  -lmymath
[ljh@VM-0-11-centos zhangsan]$ ./main
./main: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory

分析问题:
当我们运行的时候与编译器gcc已经没有关系了。运行的时候是系统要帮我们找到运行时要使用的动态库!!!
相当于我们给了编译器明确的路径,编译器知道了,并且编译器借助环境变量生成了一个正确的路径,但是系统不会找编译器要,我们还需要自己指明路径给编译器。
【Linux】零基础学习动静态库打包_第3张图片

为什么之前动态连接的其他程序,可以直接运行呢?
因为库在默认路径下,系统可以找到。


解决方法:
第一种解决方案:
环境变量LD_LIBRARY_PATH,我们只需要把库的目录路径导入到环境变量后就可以了。

[ljh@VM-0-11-centos dy_lib]$ cd zhangsan

[ljh@VM-0-11-centos zhangsan]$ ls dylib
Add.h  libmymath.so  Sub.h

[ljh@VM-0-11-centos zhangsan]$ ./main
./main: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory

[ljh@VM-0-11-centos zhangsan]$ pwd  查询当前目录
/home/ljh/2.27/dystlib/dy_lib/zhangsan

[ljh@VM-0-11-centos zhangsan]$ export  LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ljh/2.27/dystlib/dy_lib/zhangsan/dylib    追加导入环境变量
[ljh@VM-0-11-centos zhangsan]$ ./main
30
-10

在这里插入图片描述

然后我们可以把先前写的动静态库放到同一个文件夹(lib)当中,就可以供别人使用。

[ljh@VM-0-11-centos zhangsan]$ ls
lib  main.c

打印文件内容
[ljh@VM-0-11-centos zhangsan]$ tree lib
lib
|-- Add.h
|-- libmymath.a
|-- libmymath.so
`-- Sub.h

0 directories, 4 files

[ljh@VM-0-11-centos zhangsan]$ gcc main.c -o main -I ./lib/ -L ./lib/  -lmymath -static

main就可以是静态编译了
[ljh@VM-0-11-centos zhangsan]$ file main
main: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=02ddaebbe1c007e6bfcd54f5b91e3128a136ebac, not stripped

当然这里的库也可以放在二级目录,三级目录。
【Linux】零基础学习动静态库打包_第4张图片
第二种找到动态库的方法:把自己写的.so文件拷贝到系统的共享库路径下,一般指/usr/lib64。
第三种找到动态库的方法:ldconfig 配置/etc/ld.so.conf.d/,在root用户ldconfig更新
在/etc/ld.so.con.d/路径下创建一个后缀.conf的文件,然后把lib的目录地址写入,有多个可以换行写入即可。
注意要再ldconfig刷新,就可以正常运行动态链接的文件。

编写lib文件的路径
[root@VM-0-11-centos ~]# echo "/home/ljh/2.27/dystlib/dy_lib/zhangsan/lib" > /etc/ld.so.conf.d/ljh_tmp.conf
[root@VM-0-11-centos ~]# logout
刷新
[root@VM-0-11-centos ~]# ldconfig
[root@VM-0-11-centos ~]# logout
[ljh@VM-0-11-centos zhangsan]$ ./main30
-10

在这里插入图片描述

第一种方法只在未关闭会话时有效,第二种只在设置user下有效,而第三种方法可以永久使用。



对于小版本的理解

【Linux】零基础学习动静态库打包_第5张图片

  • 1.因为通常我们的库会有升级,所以我们的库名可能是 libhello.so.a.b.c 之类的,那么如何设置呢?
  • 首先ldd 当中找到名字是可执行文件的soname,而我们gcc的时候定义库名的时候可以添加字段soname

-Wl.option:
此选项传递 option 给连接程序; 如果 option 中间有逗号, 就将 option 分成多个选项, 然 后传递给会连接程序。

gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.1
这样子我们ldd main 的时候显示的就是libhello.so.0 了;
在这里插入图片描述

  • 2.但是此时我们并不能看到libhello.so.0 ,而只有libhello.so.0.0.1,实际上需要使用ldconfig -n . 进行刷新配置。因为配置实际上也有类似缓存的概念,默认我们读的都是缓存,而不是从文件当中进行读取,ldconfig -n .就是让:用此选项时,ldconfig仅扫描命令行指定的目录,不扫描默认目录(/lib、/usr/lib),也不扫描配置文件/etc/ld.so.conf所列的目录。
    【Linux】零基础学习动静态库打包_第6张图片
    1. 但是此时我们的程序要连接的时候不能带上小版本号,gcc main.c -L . -lhello -o main 如这样,那么我们需要用ln -s 再弄一个软连接,ln libhello.so.0 -s libhello.so ,就会生成如下,但是此时我们需要添加环境变量。
 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`

【Linux】零基础学习动静态库打包_第7张图片
这样带来的好处,就是若是小版本的更新,可以不用编译程序,而程序就能够用到最新的动态库。

  • 1.gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.2

【Linux】零基础学习动静态库打包_第8张图片
上述的图有误,实际上再次执行一次ldconfig -n .可以让指向最新的版本。后续运行不需要重新编译源文件。实际上的图类似下面,进行一个简单的更正~
【Linux】零基础学习动静态库打包_第9张图片

  • 2.运行main 函数

【Linux】零基础学习动静态库打包_第10张图片

  • 3.验证发送出去,此时由于 libhello.so的路径 实际上和前面是一样的,也能够从之前的路径,main通过找到找到自己的libhello.so.0 找到libhello.so.0.0.1,但实际上此时已经可以用到最新版本了。
    【Linux】零基础学习动静态库打包_第11张图片

注意:通常环境变量的设置会在/etc/profile.d/ 的路径创建文件,这样能解耦,不需要直接修改 /etc/profile 文件,效果相同。 同理动态库的设置 /etc/ld.so.conf.d/ 底下创建新的文件即可。

库的理解:

当我们使用第三方库,下载别人的库,如果是源文件,我们可以自己编译生成库再使用,如果是别人帮我们编写好了,我们就可以安装实际就帮我们拷贝到了默认路径下。


总结

!_!

你可能感兴趣的:(Linux,linux,学习,运维)