Linux系统编程-静态库和动态库

静态库:以静态文件的形式在编译的时候和将要运行的程序编译在一块,如果有多个程序需要使用静态库,那么就会在多个程序中就都会多出一个静态库的大小。

动态库:与静态库不同,动态库是在多个程序运行时共享,即多个程序会共享这个库,而不是每个程序都包含这个库,只会共享。

既然动态库比静态库有这么明显的优势,为什么还会存在静态库?

答案就是在调用函数的速度,静态库比动态库的速度快。

静态库在什么方面使用呢?

静态库:对空间要求低,对时间要求高的核心程序。

动态库:对事件要求低,对空间要求高的程序。

静态库的制作:

ar rcs libmylib.a file1.o

注意:必须是lib开头,.a为文件后缀名的规则定义静态文件

步骤:

1. 将 .c 生成 .o文件

        gcc -c add.c -o add.o

2. 使用 ar 工具生成静态库

       ar rcs libxx.a add.o sub.o

3. 编译静态库到可执行文件

      gcc mian.c libxx.a -o main

zhangfengzhou@ubuntu:~/learn/linux_learn/static$ gcc -c add.c -o add.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ gcc -c sub.c -o sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ls
add.c  add.o  sub.c  sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ar rcs libmath.a add.o sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ls
add.c  add.o  libmath.a  sub.c  sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ file libmath.a 
libmath.a: current ar archive
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ 

头文件守卫,防止头文件被重复包含:

  1 #ifndef _MYMATH_H
  2 #define _MYMATH_H
  3 
  4 int add(int, int);
  5 int sub(int, int);
  6 
  7 #endif

比较完整的静态库制作步骤:

zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ls
add.c  add.o  libmymath.a  main  main.c  mymath.h  sub.c  sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ mkdir inc
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ mv *.h inc
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ mkdir lib
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ mv *.a lib
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ls
add.c  add.o  inc  lib  main  main.c  sub.c  sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ gcc main.c ./lib/libmymath.a -o hello -I ./inc
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ls
add.c  add.o  hello  inc  lib  main  main.c  sub.c  sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ./hello
6+2=8
6-2=4
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ 

需要lib, inc 目录,lib目录是存放静态库文件的目录,inc是存放头文件的目录。

动态库制作:

1. 将 .c 生成 .o文件 (生成与位置无关的代码  -fPIC)

     gcc -c add.c -o add.o -fPIC

2.  使用 gcc -shared 制作动态库

      gcc -shared  -fPIC -o lib库名.so add.o sub.o 

3.  编译可执行程序时,指定所使用的动态库。-l: 指定库名   -L:指定路径 

      gcc  main.c -o main -lmymath -L ./lib -I ./inc

注意:

1. mymath 是动态库名称   libmymath.so 去掉lib 和so后缀之后获取库名称

2. ./lib 是存放libmymath.so 动态库的目录

3. ./inc 是存放动态库头文件的目录

4.   运行可执行程序

      ./main

编译时错误:

zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ ls
add.c  add.o  inc  lib  main.c  sub.c  sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ gcc main.c -o main -lmymath -L./lib -I./inc 
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ ls
add.c  add.o  inc  lib  main  main.c  sub.c  sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ ./main
./main: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ 

报错原因分析:

     原因: 

              链接器: 工作于链接阶段 工作时需要 -l 和 -L

              动态链接器:工作于程序运行阶段,工作时需要提供动态库所在目录位置

                     通过环境变量: LD_LIBRARY_PATH=动态路径库  

                     ./main 成功 (临时生效,终端重启环境变量失效)

会去那么几个固定的地方去找动态库,那么这个地方就是环境变量

方法一:

export LD_LIBRARY_PATH=./lib  导出这个环境变量的新值,覆盖旧值,然后就可以找到动态库

解决方式:

zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ export LD_LIBRARY_PATH=./lib
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ ./main
6+2=8
6-2=4
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ 

但是以上有个问题,新开一个窗口,再次执行./main 就会报错,原因是设置的环境变量失效,设置的环境变量只对当前的进程有效。解决方法:

方法二:

永久生效:写入 终端配置文件 .bashrc    (建议是用绝对路径,不要使用 ./lib)

1) vim   ~/.bashrc  

2) 写入 export LD_ LIBRARY_PATH=动态库路径

// ~/.bashrc

export LD_LIBRARY_PATH=./lib

3). .bashrc 或者 source .bashrc 或者重启终端--> 让修改后的 .bashrc 生效

4)./main 顺利执行

为什么c语言从来不用指定搜索路径就可以直接使用,难道是因为c标准库的路径已经是默认搜索的路径,如果确实如此,那么我们是否可以将 libmymath.so 放到标准库的目录下,不用添加动态库搜索路径,也是可以执行的呢?

方法三:

zhangfengzhou@ubuntu:/lib/x86_64-linux-gnu$ pwd
/lib/x86_64-linux-gnu
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ cd lib
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic/lib$ sudo cp libmymath.so /lib/x86_64-linux-gnu/
[sudo] password for zhangfengzhou: 
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic/lib$ cd ..
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ ./main
6+2=8
6-2=4

查看可执行文件在运行时所依赖的动态库以及其所在路径

zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ ldd main
	linux-vdso.so.1 =>  (0x00007fff611f7000)
	libmymath.so => /lib/x86_64-linux-gnu/libmymath.so (0x00007f9f9b5d9000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9f9b20f000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f9f9b7db000)
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ 

5)拷贝自定义动态库 到 /lib (标准C库所在的目录)

方法四:

6)配置文件法:

1. 修改/etc/ld.so.conf

sudo vi /etc/ld.so.conf   添加你的共享库路径 

2.  更新查找共享库的路径

sudo ldconfig -v

3. 测试你的程序可否找到共享库

ldd main     

具体执行步骤:

zhangfengzhou@ubuntu:~$ sudo vim /etc/ld.so.conf
[sudo] password for zhangfengzhou: 
//追加搜索路径
1 include /etc/ld.so.conf.d/*.conf
2 /home/zhangfengzhou/learn/linux_learn/dynamic/lib


zhangfengzhou@ubuntu:~$ sudo ldconfig -v
zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ ldd main
	linux-vdso.so.1 =>  (0x00007ffc13bed000)
	libmymath.so => /home/zhangfengzhou/learn/linux_learn/dynamic/lib/libmymath.so (0x00007f03ef9ed000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f03ef623000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f03efbef000)

objdump -dS hello > out 是反汇编 hello二进制文件生成汇编代码文件,并输出到out文件中

zhangfengzhou@ubuntu:~/learn/linux_learn/static$ objdump -dS hello >out
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ ls
add.c  add.o  hello  inc  lib  main  main.c  out  sub.c  sub.o
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ vim out
zhangfengzhou@ubuntu:~/learn/linux_learn/static$ 
125 0000000000400526 <main>:
126   400526:       55                      push   %rbp
127   400527:       48 89 e5                mov    %rsp,%rbp
128   40052a:       48 83 ec 20             sub    $0x20,%rsp
129   40052e:       89 7d ec                mov    %edi,-0x14(%rbp)
130   400531:       48 89 75 e0             mov    %rsi,-0x20(%rbp)
131   400535:       c7 45 f8 06 00 00 00    movl   $0x6,-0x8(%rbp)
132   40053c:       c7 45 fc 02 00 00 00    movl   $0x2,-0x4(%rbp)
133   400543:       8b 55 fc                mov    -0x4(%rbp),%edx
134   400546:       8b 45 f8                mov    -0x8(%rbp),%eax
135   400549:       89 d6                   mov    %edx,%esi
136   40054b:       89 c7                   mov    %eax,%edi
137   40054d:       e8 48 00 00 00          callq  40059a <add>   # add 函数此处是有具体地址
138   400552:       89 c1                   mov    %eax,%ecx
139   400554:       8b 55 fc                mov    -0x4(%rbp),%edx
140   400557:       8b 45 f8                mov    -0x8(%rbp),%eax
141   40055a:       89 c6                   mov    %eax,%esi
142   40055c:       bf 44 06 40 00          mov    $0x400644,%edi
143   400561:       b8 00 00 00 00          mov    $0x0,%eax
144   400566:       e8 95 fe ff ff          callq  400400 <printf@plt> #printf是动态库函数,无地址
145   40056b:       8b 55 fc                mov    -0x4(%rbp),%edx
146   40056e:       8b 45 f8                mov    -0x8(%rbp),%eax
147   400571:       89 d6                   mov    %edx,%esi
148   400573:       89 c7                   mov    %eax,%edi
149   400575:       e8 34 00 00 00          callq  4005ae <sub>
150   40057a:       89 c1                   mov    %eax,%ecx
151   40057c:       8b 55 fc                mov    -0x4(%rbp),%edx
152   40057f:       8b 45 f8                mov    -0x8(%rbp),%eax
153   400582:       89 c6                   mov    %eax,%esi
154   400584:       bf 4e 06 40 00          mov    $0x40064e,%edi
155   400589:       b8 00 00 00 00          mov    $0x0,%eax
156   40058e:       e8 6d fe ff ff          callq  400400 <printf@plt>
157   400593:       b8 00 00 00 00          mov    $0x0,%eax
158   400598:       c9                      leaveq
159   400599:       c3                      retq

从上面的反汇编的结果来看,动态没有具体地址,在运行的时候才会进行地址回填。

@plt 表示动态库函数

zhangfengzhou@ubuntu:~/learn/linux_learn/dynamic$ file libmymath.so 
libmymath.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=d1b8c90343011313d13b0a4c0642103468b6a216, not stripped

LSB 表示小尾端,与大尾端相对,是一种存储数据的方式。

以上解决动态库加载时出现问题的四种解决方法。

参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值