静态库和动态库的分析

 在Linux操作系统中,普遍使用ELF格式作为可执行程序或者程序生成过程中的中间格式。ELF(Executable and Linking Format,可执行连接格式)。

     ELF文件格式包括三种主要的类型:可执行文件、可重定向文件、共享库:

1、可执行文件(应用程序)
可执行文件包含了代码和数据,是可以直接运行的程序。

2、可重定向文件(*.o)
可重定向文件又称为目标文件,它包含了代码和数据(这些数据是和其他重定位文件和共享的object文件一起连接时使用的)。
*.o文件参与程序的连接(创建一个程序)和程序的执行(运行一个程序),它提供了一个方便有效的方法来用并行的视角看待文件的内容,这些*.o文件的活动可以反映出不同的需要。
Linux下,我们可以用gcc -c编译源文件时可将其编译成*.o格式。

3、共享文件(*.so)
也称为动态库文件,它包含了代码和数据(这些数据是在连接时候被连接器ld和运行时动态连接器使用的)。动态连接器可能称为ld.so.1,libc.so.1或者 ld-linux.so.1。

那么到底什么是库呢?

库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行。库分静态库动态库两种。二者的不同点在于代码被载入的时刻不同。

静态库:这类库的名字一般是libxxx.a,xxx为库的名字。静态库在程序编译时会被连接到目标代码中,利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。

动态库:这类库的名字一般是libxxx.M.N.so,同样的xxx为库的名字,M是库的主版本号,N是库的副版本号。当然也可以不要版本号,但名字必须有。相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。linux系统有几个重要的目录存放相应的函数库,如/lib /usr/lib。

当要使用静态的程序库时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不再需要了。然而,对动态库而言,就不是这样。动态库会在执行程序内留下一个标记指明当程序执行时,首先必须载入这个库。由于动态库节省空间,linux下进行连接的缺省操作是首先连接动态库,也就是说,如果同时存在静态和动态库,不特别指定的话,将与动态库相连接。

一、静态库

        前面已经介绍了静态库的概念,下面我们通过一个实例来看静态库的使用

1、生成静态库

1)这里准备了两个库的源码st1 、st2用它们来制作库libmytest.a,如下

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/stalib$ ls -l  
  2. total 12  
  3. -rw-rw-r-- 1 fs fs 98 Jan  5 19:56 main.c  
  4. -rw-rw-r-- 1 fs fs 69 Jan  5 19:55 st1.c  
  5. -rw-rw-r-- 1 fs fs 68 Jan  5 19:55 st2.c  
  6. fs@ubuntu:~/qiang/stalib$ cat st1.c  
  7. #include <stdio.h>  
  8.   
  9. void print1()  
  10. {  
  11.     printf("Hello!I am st1!\n ");  
  12. }  
  13. fs@ubuntu:~/qiang/stalib$ cat st2.c  
  14. #include <stdio.h>  
  15.   
  16. void print2()  
  17. {  
  18.     printf("Hello!I am st2!\n");  
  19. }  
  20. fs@ubuntu:~/qiang/stalib$ cat main.c  
  21. #include <stdio.h>  
  22.   
  23. int main(int argc, const char *argv[])  
  24. {  
  25.     print1();  
  26.     print2();  
  27.       
  28.     return 0;  
  29. }  
  30. fs@ubuntu:~/qiang/stalib$   

2) 生成libmytest.a文件

创建静态库用ar命令,它将很多.o转换成.a

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/stalib$ gcc -c st1.c st2.c  
  2. fs@ubuntu:~/qiang/stalib$ ar crs libmytest.a st1.o st2.o  
  3. fs@ubuntu:~/qiang/stalib$ ls -l  
  4. total 24  
  5. -rw-rw-r-- 1 fs fs 2262 Jan  5 20:04 libmytest.a  
  6. -rw-rw-r-- 1 fs fs   98 Jan  5 19:56 main.c  
  7. -rw-rw-r-- 1 fs fs   69 Jan  5 20:03 st1.c  
  8. -rw-rw-r-- 1 fs fs 1024 Jan  5 20:04 st1.o  
  9. -rw-rw-r-- 1 fs fs   69 Jan  5 20:03 st2.c  
  10. -rw-rw-r-- 1 fs fs 1024 Jan  5 20:04 st2.o  
  11. fs@ubuntu:~/qiang/stalib$ file libmytest.a   
  12. libmytest.a: current ar archive  
  13. fs@ubuntu:~/qiang/stalib$   

静态库文件libmytest.a已经生成,用file命令查看其属性,发现它确实是归档压缩文件。用ar -t libmytest.a可以查看一个静态库包含了那些obj文件:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/stalib$ ar -t libmytest.a   
  2. st1.o  
  3. st2.o  
  4. fs@ubuntu:~/qiang/stalib$   

2、使用静态库

前面我们已经写好了main.c,现在测试一下

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/stalib$ gcc -o test main.c -L. -lmytest  
  2. fs@ubuntu:~/qiang/stalib$ ls -l  
  3. total 32  
  4. -rw-rw-r-- 1 fs fs 2262 Jan  5 20:04 libmytest.a  
  5. -rw-rw-r-- 1 fs fs   98 Jan  5 19:56 main.c  
  6. -rw-rw-r-- 1 fs fs   69 Jan  5 20:03 st1.c  
  7. -rw-rw-r-- 1 fs fs 1024 Jan  5 20:04 st1.o  
  8. -rw-rw-r-- 1 fs fs   69 Jan  5 20:03 st2.c  
  9. -rw-rw-r-- 1 fs fs 1024 Jan  5 20:04 st2.o  
  10. -rwxrwxr-x 1 fs fs 7248 Jan  5 20:07 test  
  11. fs@ubuntu:~/qiang/stalib$ ./test   
  12. Hello! I am st1!  
  13. Hello! I am st2!  
  14. fs@ubuntu:~/qiang/stalib$   

这里gcc的参数-L是告诉编译器库文件的路径是当前目录,-l是告诉编译器要使用的库的名字叫mytest。

二、动态库

动态库的基本概念
1、动态链接库是程序运行时加载的库,当动态链接库正确安装后,所有的程序都可以使用动态库来运行程序。动态链接库是目标文件的集合,目标文件在动态链接库中的组织方式是按照特殊方式形成的。库中函数和变量的地址是相对地址,不是绝对地址,其真实地址在调用动态库的程序加载时形成。

2、动态链接库的名称有别名(soname), 真名(realname)和链接名(linker name)。别名由一个前缀lib,然后是库的名字,再加上一个后缀“.so”构成("libxxx.so")。真名是动态链接库真实名称,一般总是在别名的基础加上一个小版本号,发布版本等构成。除此之外,还有一个链接名,即程序链接时使用的库的名字。

3、在动态链接库安装的时候,总是复制文件到某个目录下,然后用一个软连接生成别名,在库文件进行更新的时候,仅仅更新软链接即可。

下面我们通过一个实例来学习如何生成动态库和使用动态库

1、生成动态库

1)当前文件夹下有下面四个文件

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/lib/dylib$ ls -l  
  2. total 16  
  3. -rw-rw-r-- 1 fs fs 73 Jan  5 18:11 dy1.c  
  4. -rw-rw-r-- 1 fs fs 74 Jan  5 18:11 dy2.c  
  5. -rw-rw-r-- 1 fs fs 90 Jan  5 18:11 main.c  
  6. -rw-rw-r-- 1 fs fs 95 Jan  5 18:10 mylib.h  
  7. fs@ubuntu:~/qiang/lib/dylib$   

文件内容分别为:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/lib/dylib$ cat mylib.h   
  2. #ifndef _MYLIB_H_  
  3. #define _MYLIB_H_  
  4.   
  5. #include <stdio.h>  
  6.   
  7. void print1();  
  8. void print2();  
  9.   
  10. #endif  
  11. fs@ubuntu:~/qiang/lib/dylib$ cat dy1.c  
  12. #include "mylib.h"  
  13.   
  14. void print1()  
  15. {  
  16.     printf("My first shared lib!\n");  
  17. }  
  18. fs@ubuntu:~/qiang/lib/dylib$ cat dy2.c  
  19. #include "mylib.h"  
  20.   
  21. void print2()  
  22. {  
  23.     printf("My second shared lib!\n");  
  24. }  
  25. fs@ubuntu:~/qiang/lib/dylib$ cat main.c  
  26. #include "mylib.h"  
  27.   
  28. int main(int argc, char *argv[])  
  29. {  
  30.     print1();  
  31.     print2();  
  32.     return 0;  
  33. }  
  34. fs@ubuntu:~/qiang/lib/dylib$   

2)这里我们将dy1.c与dy2.c用来创建动态库

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/lib/dylib$ gcc -fPIC -Wall -c dy1.c dy2.c  
  2. fs@ubuntu:~/qiang/lib/dylib$ gcc -shared -o libtest.so dy1.o dy2.o  
  3. fs@ubuntu:~/qiang/lib/dylib$   

这里 -fPIC 创建与地址无关的编译程序,-shared指定生成动态链接库。

我们也可以一步到位

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/lib/dylib$ gcc -o libtest.so -fPIC -shared dy1.c dy2.c  
  2. fs@ubuntu:~/qiang/lib/dylib$   

我们可以看到下面已经生成了一个libtest.so

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/lib/dylib$ ls -l  
  2. total 32  
  3. -rw-rw-r-- 1 fs fs   73 Jan  5 18:11 dy1.c  
  4. -rw-rw-r-- 1 fs fs 1332 Jan  5 18:29 dy1.o  
  5. -rw-rw-r-- 1 fs fs   74 Jan  5 18:11 dy2.c  
  6. -rw-rw-r-- 1 fs fs 1336 Jan  5 18:29 dy2.o  
  7. -rwxrwxr-x 1 fs fs 6798 Jan  5 18:29 libtest.so  
  8. -rw-rw-r-- 1 fs fs   90 Jan  5 18:11 main.c  
  9. -rw-rw-r-- 1 fs fs   95 Jan  5 18:10 mylib.h  
  10. fs@ubuntu:~/qiang/lib/dylib$   

2、使用动态链接库
      在编译程序时,使用动态链接库和静态库是一致的,使用”-l库名”的方式,在生成可执行文件的时候会链接库文件。使用如下命令:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/lib/dylib$ gcc -o test main.c -L. -ltest  
  2. fs@ubuntu:~/qiang/lib/dylib$ ls  
  3. dy1.c  dy2.c  libtest.so  main.c  mylib.h  test  

这里 -L 指定动态链接库的路劲,-ldtest链接库函数test 。-ltest是动态库的调用规则。Linux系统下的动态库命名方式是lib*.so,而在链接时表示位-l*,*是自己命名的库名。
我们可以看到这里已经生成了test可执行文件,我们可以执行一下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/lib/dylib$ ./test  
  2. ./test: error while loading shared libraries: libtest.so: cannot open   
  3. shared object file: No such file or directory  
  4. fs@ubuntu:~/qiang/lib/dylib$  

可以发现发生了错误,这是因为程序运行时没有找到动态链接库造成的。程序编译时链接动态库和运行时使用动态链接库的概念是不同的,在运行时,程序链接的动态链接库需要在系统目录下才行。

这就到了动态库的路径问题,有三种方法:

1)把库拷贝到/usr/lib和/lib目录下:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/lib/dylib$ sudo cp libtest.so /lib  
  2. [sudo] password for fs:   

这里要超级用户权限sudo,我们看一下执行结果:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/lib/dylib$ ./test   
  2. My first shared lib!  
  3. My second shared lib!  
  4. fs@ubuntu:~/qiang/lib/dylib$   

这里执行结果正确。

2)在 LD_LIBRARY_PATH 环境变量中加上库所在路径

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/lib/dylib$ export LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH  
  2. fs@ubuntu:~/qiang/lib/dylib$   

我们可以看到:libtest.so 的路径已经存在

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/lib/dylib$ ldd test  
  2.     linux-gate.so.1 =>  (0xb774b000)  
  3.     libtest.so => /home/fs/qiang/lib/dylib/libtest.so (0xb7746000)  
  4.     libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb758d000)  
  5.     /lib/ld-linux.so.2 (0xb774c000)  
  6. fs@ubuntu:~/qiang/lib/dylib$   

我们可以看一下结果:

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/lib/dylib$ ./test   
  2. My first shared lib!  
  3. My second shared lib!  
  4. fs@ubuntu:~/qiang/lib/dylib$   

也能得到正确结果。

3)添加/etc/ld.so.conf.d/*.conf文件。把库所在的路径加到文件末尾,并执行ldconfig刷新。

[cpp] view plaincopy
  1. fs@ubuntu:~/qiang/lib/dylib$ cd /etc/ld.so.conf.d/  
  2. fs@ubuntu:/etc/ld.so.conf.d$ ls  
  3. i386-linux-gnu_GL.conf  libc.conf  
  4. i686-linux-gnu.conf     vmware-tools-libraries.conf  
  5. fs@ubuntu:/etc/ld.so.conf.d$ sudo vi my.conf  
  6. [sudo] password for fs:   
  7. fs@ubuntu:/etc/ld.so.conf.d$ cat my.conf   
  8. /home/fs/qiang/lib/dylib/libtest.so  
  9. fs@ubuntu:/etc/ld.so.conf.d$   

在/etc/ld.so.conf.d/下建立 my.conf  里面只有一句/home/fs/qiang/lib/dylib/libtest.so 即libtest.so的路径,然后执行ldconfig刷新即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402366.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux替换命令

:s/^.*$/\L&/100 &#xff03;&#xff03;将100行内的小写转换成大写 vi/vim 中可以使用 :s 命令来替换字符串。 :s/vivian/sky/ 替换当前行第一个 vivian 为 sky :s/vivian/sky/g 替换当前行所有 vivian 为 sky :n,$s/vivian/sky/ 替换第 n 行开始到最后一行中每一行的第…

bash中时间、日期操作

From: Linux 使用 date 計算時間(昨天、明天) 偶尔要算算时间&#xff0c;转载一下&#xff0c;不用再去man了 昨天 (前一天) date –date’1 days ago’ “%Y-%m-%d”date -d ’1 days ago’ “%Y-%m-%d”date -d yesterday “%Y-%m-%d” 明天 (後一天) date –date’1 days…

如何用Chrome浏览器下载网页音乐视频

打开网页&#xff0c;先让要下载的视频播放&#xff0c;右键单击选择审查元素&#xff08;F12&#xff09;&#xff0c;选择上方的Network选项&#xff0c;按F5刷新&#xff0c;这个时候我们可以看到框架中Size下的不少文件数据数字正在变大&#xff0c;按size降序排列。点击表…

Linux 系统应用编程——标准I/O

标准I/O的由来 标准I/O指的是ANSI C 中定义的用于I/O操作的一系列函数。 只要操作系统安装了C库&#xff0c;标准I/O函数就可以调用。换句话说&#xff0c;如果程序中使用的是标准I/O函数&#xff0c;那么源代码不需要任何修改就可以在其他操作系统下编译运行&#xff0c;具有更…

GitLab五种权限

From: https://blog.csdn.net/chenguanghan123/article/details/100523824

Winfrom实现圆角设计

主要代码 public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Paint(object sender, PaintEventArgs e) { Type(this, 25, 0.1); } private void…

Linux 系统应用编程——进程基础

一、Linux下多任务机制的介绍 Linux有一特性是多任务&#xff0c;多任务处理是指用户可以在同一时间内运行多个应用程序&#xff0c;每个正在执行的应用程序被称为一个任务。 多任务操作系统使用某种调度(shedule)策略&#xff08;由内核来执行&#xff09;支持多个任务并发执行…

[正则] - 学习过程1

1. 判断是否以xxxx开头: 以数字. 开头&#xff0c;如“2. ” if re.match(^\d\. , content):return <h3>%s</h3> %(content) 2. 将内容中以[ dsfda789 df ] 的内容替换成tpl index 0 input {w: 60 # input宽度 }def replaceValue(matched):global i…

【Python文件处理】递归批处理文件夹子目录内所有txt数据

因为有个需求&#xff0c;需要处理文件夹内所有txt文件&#xff0c;将txt里面的数据筛选&#xff0c;重新存储。 虽然手工可以做&#xff0c;但想到了python一直主张的是自动化测试&#xff0c;就想试着写一个自动化处理数据的程序。 一.分析数据格式 需要处理的数据是txt格式存…

Windows Azure 之服务总线中继服务

Windows Azure的服务总线允许在Web服务内部与外部之间做成一个公共的连接点&#xff0c;在无需更改企业防火墙或者其他安全配置的情况下连接内部和外部的服务 而使用Azure云服务的时候由于缩放的原因通过IP来制定连接也是不科学的&#xff0c;而中继服务则可以充当很好的公共连…

C#对char[]的处理

先来看一段简单的C#代码&#xff1a; private void button3_Click(object sender, EventArgs e){char[] a new char[6] { h, e, L, O, \0, \0 }; // 少赋值一个元素都会报错string b new string(a);string result String.Format("b {0}, b.Length {1}",…

Centos7 关闭防火墙(Firewalld ),使用防火墙(iptables)

1、直接关闭防火墙 systemctl stop firewalld.service&#xff1b; #停止firewall systemctl disable firewalld.service&#xff1b; #禁止firewall开机启动 2、安装并启动 iptables service&#xff0c;以及设置开机自启 yum -y install iptables-services&#xff1b;#安装i…

【qt】QT 的信号与槽机制

QT 是一个跨平台的 C GUI 应用构架&#xff0c;它提供了丰富的窗口部件集&#xff0c;具有面向对象、易于扩展、真正的组件编程等特点&#xff0c;更为引人注目的是目前 Linux 上最为流行的 KDE 桌面环境就是建立在 QT 库的基础之上。 QT 支持下列平台&#xff1a;MS/WINDOWS-9…

C# String 前面不足位数补零的方法

using System; using System.Collections.Generic; using System.Linq;namespace ConsoleApp1 {class Program{static void Main(string[] args){//var a 5;var a 24;// 整数前面补N个0以保存对齐Console.WriteLine("{0:D4}", a);Console.WriteLine(a.ToString(&qu…

Linux 系统应用编程——进程间通信(上)

现在再Linux应用较多的进程间通信方式主要有以下几种&#xff1a; 1&#xff09;无名管道&#xff08;pipe&#xff09;及有名管道&#xff08;fifo&#xff09;&#xff1a;无名管道可用于具有亲缘关系进程间的通信&#xff1b;有名管道除具有管道相似的功能外&#xff0c;它还…

通过JDBK操作数据库

一、配置程序——让我们程序能找到数据库的驱动jar包1.把.jar文件复制到项目中去,整合的时候方便。2.在eclipse项目右击“构建路径”--“配置构建路径”--“库”--“添加外部jar”--找到数据库的驱动jar包--点击确定。会在左侧包资源管理器中出现“引用的库”&#xff0c;在里面…

scanf函数详解(下)

问题一如何让scanf()函数正确接受有空格的字符串&#xff1f;如: I love you!#include <stdio.h>int main(){char str[80];scanf("%s",str);printf("%s",str);return 0;}输入&#xff1a;I love you!上述程序并不能达到预期目的&#xff0c;scanf()扫…

77 大道理

1. 鲶鱼效应 以前&#xff0c;沙丁鱼在运输过程中成活率很低。后有人发现&#xff0c;若在沙丁鱼中放一条鲶鱼&#xff0c;情况却有所改观&#xff0c;成活率会大大提高。这是何故呢&#xff1f;原来鲶鱼在到了一个陌生的环境后&#xff0c;就会“性情急燥”&#xff0c;四处乱…

Linux 系统应用编程——网络编程(常用命令解析)

1、telnet Telnet协议是TCP/IP协议族中的一员&#xff0c;是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用telnet程序&#xff0c;用它连接到服务器。终端使用者可以在telnet程序中输入命令&#…

灾难 BZOJ 2815

灾难 【样例输入】 5 0 1 0 1 0 2 3 0 2 0 【样例输出】 4 1 0 0 0 题解&#xff1a; 先跑出拓扑序 我们按拓扑序建立一棵“灭绝树” 灭绝树含义是当一个点灭绝时&#xff0c;它的子树将会全部灭绝 所以答案就是点在灭绝树中的子树大小 一个点如果灭绝&#xff0c;那么需要所有…