动静态库的创建 | 使用 | 加载

动静态库的介绍

静态库( .a ):在程序编译的时候,把库的代码链接(拷贝)到可执行程序。

动态库(.so):在程序的执行时,链接动态库的代码。多个程序同时共享代码。

创建一个静态库:
创建计算器,带有加减乘除功能。

声明和定义分离。创建文件

所有文件:

头文件 .h

动静态库的创建 | 使用 | 加载_第1张图片

源文件.c

动静态库的创建 | 使用 | 加载_第2张图片

静态库的打包

一:生成 .o文件

利用指令gcc -c 

gcc -c Add.c Sub.c Div.c Mul.c

为了简化操作,我们只测试加法功能

创建testadd目录 将Add.o文件移动到testadd目录,创建一个简单的test代码

包含上层目录的Add.h头文件

 1 #include"../Add.h"
  2 
  3 int main()
  4 {
  5   int ret=add(3,5);
  6   printf("%d\n",ret);
  7   return 0;
  8 }

将test.c文件编译成test.o文件 

因此用户可以通过包含头文件的形式 再编译成可执行文件 

动静态库的创建 | 使用 | 加载_第3张图片

因此通过将.o文件和.h文件打包给别人,别人就能使用库。

这一个将.o文件拷贝编译生成可执行可执行程序的过程就是静态链接。

但是有果有许多的.o文件,打包起来就很繁琐,用户使用也不舒服。

介绍第二种方式

二、借助ar指令简化

1)生成静态库

ar是gun归档工具,常用于目标文件打包成静态库

r-c表示replace-create

指令:

ar -rc libmymath.a add.o sub.o

生成静态库libmymath.a


利用自动化工具(Makefile)

 动静态库的创建 | 使用 | 加载_第4张图片

这一的make操作,我们希望将所有的.c文件(不包含main函数)打包成libmymath.a文件

%.o :%.c

        gcc -c $<                       

.o 文件依赖 .c

依赖方法是gcc -c    

$< 是将所有的.c文件一个一个指定方法

ar -rc将所有.o生成libmymath.a

动静态库的创建 | 使用 | 加载_第5张图片

自动化构建

通过 ar - tv指令查看静态文件中的目录列表

ar -tv libmy_math.a

2)将头文件和库打包(发布)

将我们的库给别人实际上是给出俩个文件。一个文件包含库,一个文件包含头文件

使用者只需要包含头文件就能使用我们的库文件

下面修改我们的Makefile自动化建构文件

创建mymath_lib目录

底下包含俩个子目录

include (用来放头文件)

lib(用来放库文件)

  1 static-lib=libmy_math.a
  2 
  3 $(static-lib):Add.o Div.o Mul.o Sub.o
  4   ar -rc $@ $^
  5 %.o:%.c
  6   gcc -c $<
  7 .PHONY:output
  8 output:
  9   mkdir -p mymath_lib/include
 10   mkdir -p mymath_lib/lib 
 11   cp -f *.h mymath_lib/include 
 12   cp -f *.a mymath_lib/lib 
 13                                                                                                                                                                               
 14 .PHONY:clean
 15 clean:
 16   rm -rf *.o *.a
 17 

make构造

动静态库的创建 | 使用 | 加载_第6张图片

tree查看目录mymath_lib

动静态库的创建 | 使用 | 加载_第7张图片

这样就完成对静态库的创建,如果我们想发送出去。我们还可以进行cxz压缩文件


静态库的使用

我们有一份静态库mymath_lib

在使用时,必须指明链接哪一个库,哪个路径

如果直接gcc编译会出错

原因是找不到库     " "会再当前目录下寻找文件,找不到再全系统目录下寻找

动静态库的创建 | 使用 | 加载_第8张图片


方法一:(不推荐)

将mymath_lib的头文件和库文件分别拷贝到系统目录中

系统头文件一般在/user/include/路径下

系统库文件一般放在/lib64/路径下

然后编译的时候通过  gcc 文件名 +l指明要链接的第三方库的名称

不推荐的原因操作相对繁琐,会污染系统库,不想使用了需要自行删除

方法二:(推荐)

在gcc编译时候 ,通过-I(小写i) 和 L(大写L)指定路径和库

  • -I(小写i指定路径)  搜索头文件
gcc test.c -I./mymath_lib/include
  • 发生链接错误,需要指定库文件的路径  -L
gcc test.c -I./mymath_lib/include -L./mymath_lib/lib/

  • 没有链接到库 需要指定库 去掉lib和.a
gcc test.c -I./mymath_lib/include -L./mymath_lib/lib/ -lmymath

最后在指定路径和库后终于完成编译,就产生了可执行程序


动态库的打包

动态库的创建,大致与静态库相同,有些许差异

下面新建一个目录,存放计算器的四个.c和.h文件

第一步:生成.o文件并打包

生成动态库需要添加与位置无关码 -fPIC

说明:

  • fPIC选项是在编译过程告诉编译器生成与绝对路径无关的相对存储方式。保证代码被加载到内存的任意位置,都能通过位置无关码(相对路径)找到/访问。
  • 我们总是以fPIC产生.so文件,如果不带fPIC也可以编译动态库。如果不带,在内存加载数据时,总是会被重新定位,没有固定的位置。
  • 因此-fPIC能够实现共享。后续将会做详细的解释。

生成.o文件之后,不再使用ar打包而是在gcc 选项之后添加shared

下面依旧利用自动化构建工具(先生成.o在整合生成.so)

与静态库生成不同的是,动态库完全由gcc就能完成

 dy-lib=libmymath.so 
  2 
  3 $(dy-lib):Add.o Div.o Mul.o Sub.o
  4   gcc -shared -o $@ $^
  5 %.o:%.c
  6   gcc -fPIC -c $<
  7 
  8 .PHONY:clean
  9 clean:
 10   rm -f *.o *.so 

第二步:发布

与静态库的生成一样,需要将库发布。发布就需要放在同一个目录下的俩个不同目录下

创建库目录my_math_lib

子目录 include放头文件

            lib放库 .so

完善我们的自动化构建

增加output功能

  1 dy-lib=libmymath.so 
  2 
  3 $(dy-lib):Add.o Div.o Mul.o Sub.o
  4   gcc -shared -o $@ $^
  5 %.o:%.c
  6   gcc -fPIC -c $<
  7 
  8 .PHONY:output
  9 output:
 10   mkdir -p my_math_lib/include
 11   mkdir -p my_math_lib/lib 
 12   cp -f *.h my_math_lib/include
 13   cp -f *.so my_math_lib/lib                                                                                                                                                  
 14 .PHONY:clean                              
 15 clean:                                    
 16   rm -f *.o *.so    

 tree一下结构

动静态库的创建 | 使用 | 加载_第9张图片

完成动态库的制作和发布


动态库的使用

在该路径底下只有动态库文件和用户.c文件

依照静态库的方式,

指定头文件路径I(大写i) 

库文件路径(大写L)

指定库名称l

但还是找不到文件

因为我们之前指定的路径和库是为了让gcc完成编译

在运行时,就与编译无关. OS找不到对应的库

ldd指令查看库的链接

动静态库的创建 | 使用 | 加载_第10张图片

系统链接不到.so动态库

解决方法:让系统找到动态库

法一:将动态库拷贝到系统库/lib64路径下(推荐)

对于网上下载的库,最推荐这个做法,简单省事

需要切换到root目录下

动静态库的创建 | 使用 | 加载_第11张图片

sudo cp my_math_lib/lib/libmymath.so /lib64


法二:建立软链接(对于自己的动态库推荐)

sudo ln -sf /home/user_Lian/day16/testdy/my_math_lib/lib/libmymath.so /lib64/libmymath.so

此时在/lib64路径下就存在同名文件 文件的内容指向库

在xshall重启后依旧有效



法三、环境变量(临时方式)

LD_LIBRARY_PATH

使用环境变量让系统找到自己的库

export导入

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/user_Lian/day16/testdy/my_math_lib/lib/

 ldd查询链接情况

动静态库的创建 | 使用 | 加载_第12张图片

链接成功,可以运行程序

但是重新启动Xshall后,环境变量会丢失


法四、配置系统文件

解决了环境变量重启丢失的问题

在系统配置文件 /etc/ld.so.conf.d/下创建文件

sudo vim /etc/ld.so.conf.d/myfile.conf

用vim打开,打入我们的路径

 输入dudo ldconfing

系统会加载配置的文件


动态库的加载(原理)

先来谈一下fPIC位置无关码

是一种起始地址加偏移量的关系;

通过保存起始位置和往起始位置后放偏移的位置就能找到相关数据

不管起始的地址在内存的哪一个位置,都是通过偏移量找到,这就是位置无关码

可执行程序:LEF格式

动态链接的程序,不光光要要加载程序,库也要加载。

动静态库的创建 | 使用 | 加载_第13张图片

程序在编译生成二进制后,没有加载到内存,程序内部也必须有地址!

代码被编译成二进制之后,就再也没有函数名、变量名,被编成地址数字。 是call(xxxx)的形式。

在编译时,对代码的编址,同样遵循虚拟地址空间,是基地址+偏移量的方法。所有代码会全部被放到代码段,是有范围的空间。

再谈一下映射关系

假如在ELF可执行程序中包含俩个库 libc.so  mymath.so

程序加载到物理内存是以kv的形式,物理内存再与地址空间通过页表建立映射关系。

同时库也必须加载到内存,通过页表映射到地址空间的共享区

动静态库的创建 | 使用 | 加载_第14张图片

如果可执行程序调用库函数实际上是一种call(偏移量的形式),由于库映射到共享区,基地址依旧是确定的。再通过调用某一个函数得到的偏移量。基地址+偏移量就能在共享区找到那一个特定的方法。

如果有一个进程,同时使用使用了libc.so库,如果想用库的方法,就不需要再加载库到内存中,通过映射关系到共享区中,修改起始地址,就能使用方法。

这就是动态库,只需要一份库加载到内存就能供任意多可执行程序使用。能够有效节约空间。

你可能感兴趣的:(linux,运维,服务器,动静态库)