静态库和动态库系列(2)

这个篇幅主要学习一下如何在linux下去生成.so和.a    学习来源http://blog.chinaunix.net/uid-23592843-id-223539.html

  先说明一点: 尽管在windows下和在linux下都有静态库和动态库的概念,但是由于编译器,汇编器,连接器的不同.windows的动态(lib)静态(dll)库和linux下的动态(.so)静态(.a)库是不兼容的.

  库的产生步骤:

      静态库 .a

      step1:由源文件(.h .cpp) 编译生成一堆.o, 每个.o都包含这个.o对应的编译单元的符号表

      step2:ar命令将很多.o转换成.a,成为静态库

       动态库 .so

      由 gcc加特定参数编译产生

  库的命名:

      在linux下,库一般放在/usr/lib/ 和/lib下.可能是自己的库生成的位置一般放在/usr/lib.系统的一般放在/lib.有待查验

       静态库: 名字一般为 libxxxx.a --->xxxx就是该lib的名字      

      动态库: 名字一般为 libxxxx.so.major.minor--->xxxx是该lib的名字,major是主版本好,minor是副版本号

   如何知道一个可执行程序依赖哪些库

      ldd 命令可以查看一个可执行程序依赖的共享库

      

  可执行程序在执行的时候如何定位动态库(共享库)文件

      当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径.为了知道这个绝对路径就需要系统动态载入器.

      对于elf格式的可执行程序是由ld-linux.so*来完成的,它先后搜索elf文件的DT_RPATH段--->环境变量LD_LIBRARY_PATH---->/etc/ld.so.cache文件列表---->/lib,./usr/lib目录找到库文件后将其载入内存

       例如: export LD_LIBRARY_PATH = ‘pwd‘    ----->将当前文件目录添加为共享目录

  在新安装一个库之后如何让系统能够找到它  新安装的库是不是就是自己刚刚生成的动态库呢?

      如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作;

      如果安装在其它目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下:

          1.编辑/etc/ld.so.conf文件,加入库文件所在目录的路径

          2.运行ldconfig, 该命令会重建/etc/ld.so.cache文件

  用hello.h hello.c main.c 的实际创建过程来讲解如何在linux下创建动态库和静态库

1 //文件名 hello.h
2
3 #ifndef HELLO_H_
4  #define HELLO_H_
5
6 void hello(const char* name);
7
8 #endif

//文件名 hello.c

#include <stdio.h>

void hello(const char* name)
{
      printf("hello %s\n", name);
}

1 //文件名main.c
2
3 #include "hello.h"
4
5 int main()
6 {
7       hello("Lucy");
8       return 0;
9 }

如果我们要生成hello.o的话  用 gcc -c hello.o hello.h hello.c  可以成功,但是因为hello.c之中没有main函数,所以并不是一个完整的程序.因此用gcc -o hello hello.o  来编译并连接它的时候GCC会报错

注意: 无论是静态库还是动态库,都是由.o文件来创建的,因此我们必须先将.c编译成.o文件

这个时候我们有三种思路

   1.编译多个源文件,直接将目标代码合成一个.o文件

2.创建静态链接库libmyhello.a, 使得main函数调用hello函数时可调用静态链接库

3.创建动态链接库libmyhello.so,使得main函数调用hello函数时刻调用动态链接库

      思路一具体过程

      #gcc -c hello.c  ---->会产生出hello.o

      #gcc -c main.c  ---->会产生出main.o

      #gcc -o hello hello.o main.o ---->会产生可执行文件hello

./hello -------->hello lucy

为什么不使用 gcc -o hello hello.o ?是因为上面说的hello.c里面没有main函数,gcc编译会报错的.


这里来详细说明一下gcc -o  和  gcc -c的具体过程:

gcc -o 是将.c源文件编译成为一个可执行的二进制代码,这个过程包括调用作为GCC内的一部分真正的C编译器(ccl),以及 调用GNU C编译器的输出中实际可执行代码的外部GNU汇编器(as) 和连接器工具(ld)

gcc -c 是使用GNU汇编器将源文件转换为目标代码之后就结束,在这个情况下,只调用了c编译器(ccl)和汇编器(as),而连接器(ld)没被执行,所以输出的目标文件*.o中不会包含作为linux程序在被装载和执行时所必须的包含信息,但它可以在以后被连接到一个程序

      思路二:静态链接库

静态库的命名规则libxxx.a-->xxx是库的名字 .例如我们将创建名字叫hello的静态库 那么静态库文件名libhello.a. 这一点很容易搞错的

创建静态库用ar命令

#gcc -c hello.c

# ar rsc libhello.a hello.o

这样静态库就创建成功了

静态库制作完成后,如何使用静态库的内部的函数呢.? 只需要在使用到这些内部函数的源程序中包含这些内部函数的原型声明,然后用gcc -o 命令生成目标文件时候指明静态库名就可以了.gcc将会从静态库中将公用函数连接到目标文件中.注意:gcc会在静态库名前自动加上lib,然后追加.a后缀,因此我们在写需要连接的库时,只写名字就可以了 例如如果要连接libhello.a ,那么gcc语句只要写 -lhello 

#gcc -o hello main.c -static -L. -lhello  --->用main.c和静态库生成可执行文件hello

#./hello -------->hello lucy

在生成了可执行hello后即使我们删除掉libhello.a也没关系,hello也可以执行.因为hello函数以及被生成到可执行文件中了.


静态库的一个缺点是,如果我们同时运行了许多程序,并且这些程序都使用了同一个库函数,那么在内存中这个库函数将会被拷贝很多份,这样会浪费很多内存空间.使用共享链接库(动态库)可以避免这个问题.

静态库和动态库是在一个地方的,只不过后缀有所不同.在典型的linux系统下,动态库的路径是/usr/lib/libxxx.so

当一个程序在使用动态库时候,在连接阶段并不会把函数代码连接进来,而只是链接函数的一个引用.当需要调用这个函数的时候,函数引用会被解析,此时动态库的代码才真正导入到内存中.这样,动态库的函数可以被许多程序同时共享,并且只需要在内存中存储一次就可以.

  动态库的另一个优点是他可以独立更新,与调用他的函数毫不影响.

    思路三:动态链接库(共享函数库)

动态库的命名libxxx.so 例如动态库名字hello 则动态库的文件名就是libhello.so

#gcc -c hello.c

# gcc -shared -fPIC -o libhello.so hello.o

这样就生成了动态链接库libhello.so

"PIC"命令行标记告诉GCC产生的代码不要包含对函数和变量具体内存位置的引用,这是因为现在还无法知道使用该动态库代码的应用程序将会把动态库连接到哪一段内存地址空间.这样编译出的hello.o 可以被用于建立共享链接库.建立共享链接库只需要使用GCC的-shared标记即可

#gcc -o  hello main.c -L. -lhello


在程序中使用动态库和使用静态库是完全一下的,也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后再用gcc命令生成目标文件时指明动态库名进行编译.     -L. 是告诉GCC,动态库可能在当前目录.否则GNU连接器会查找标准系统函数目录:搜索elf文件的DT_RPATH段, 搜索环境变量LD_LIBRARY_PATH,搜索/etc/ld.so.cache文件类别, 搜索/lib ./usr/lib目录 如果找到了库文件就载入到内存.但是由于我们把动态库生成到了当前文件夹下,那么一直到搜索完/lib和./usr/lib目录都没有找到库,因此会报错.加上-L.就会连当前文件夹也搜索.就会找到了.

或者我们可以在LD_LIBRARY_PATH中加入当前文件夹,这样也会找到 #export LD_LIBRARY_PATH=$(pwd)

如果我们想要让这个动态链接库被系统共享,可以执行"#ldconfig 目录名" 这个命令.此命令的功能是在缓存文件/etc/ld.so.cache中追加进指定目录下的共享库

#export LD_LIBRARY_PATH=${pwd}        /    #ldconfig /usr/Lihong/lib (必须是root权限)

# ./hello {如果没找到库会报的错误:: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory}


这里再说明一些小的细节: 当在一个目录下面,你既生成了静态库又生成了动态库,而且静态库和动态库的名字一样(例如libhello.a libhello.so),那么在程序执行的时候回优先调用动态库.

如果要查看可执行文件调用库的过程和调用哪些库的话用ldd命令(动态库和静态库都是调用这个命令)

可以看看可执行文件hello.先调用了动态库 linux-gate.so 再调用了libmyhello.so  再调用了/lib/ld-linux.so

接下来,基本就把linux下面的创建动态库和静态库就学习完成了.但是我们现在仅仅是学会了怎么用.我们用下面的一个小虚框来对上面一些知识点进行补充学习


上面学习到的编译参数说明

-shared    该选项指定生成动态库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用这个选项的话,生成的可执行程序无法连接动态库

-fPIC         表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的,所以动态载入时是通过代码拷贝的方式来满足不同进程的需求,而不能达到真正代码

      共享的目的

-L.    表示要连接的库在当前目录中

-ltest   编译器查找动态连接库时有隐含的命名规则,即在给出的名字前加上lib,后面加上.so来确定库的名称

LD_LIBRARY_PATH:  这个环境变量指示动态连接器可以装载动态库的路径 (没有root权限也可以修改)

调用动态库的时候有几个问题会经常碰到,有时,明明已经将库的头文件所在目录 通过 “-I” include进来了,库所在文件通过 “-L”参数引导,并指定了“-l”的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是通过修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题了。

静态库的搜索顺序

GCC命令中的参数-L---------->环境变量LIBRARY_PATH------>/lib /usr/lib /usr/local/lib

动态库搜素顺序

GCC命令中的参数-L---------->环境变量LD_LIBRARY_PATH---->/etc/ld.so.conf---->/lib---->/usr/lib

命令行参数讲完之后,还有一些linux的其他杂项,在这里罗列一下

应用程序
系统通用程序---->/usr/bin

在计算机安装的程序--->/usr/local/bin  或者/opt

个人用到的程序----->/usr/local

头文件  
库函数  

好了,以上就是所有的在linux下的动态静态库了.下一章节我们会就windows下的dll和lib进行话题展开.

时间: 2024-10-13 21:34:23

静态库和动态库系列(2)的相关文章

静态库和动态库系列(1)

最近一个哥们问了我一些他们公司的代码的问题,他是得到了别的公司给医院提供的dll,然后调用dll的接口为医院继续写代码..问题是.我也不会啊! 所以趁此机会,好好搞一搞lib和dll.争取能弄明白windows下的lib和dll的一些问题.  一点一点来吧.. 什么是lib,什么是dll.为什么要生成lib和dll 不管是静态库还是动态库,两者都是为了共享程序代码,类比的例子就是你封装了一段代码成为一个函数,别的函数能调用你封装的函数,而不必拷贝粘贴一份.这就叫共享程序代码 库通俗的说就是把一些

静态库和动态库的分析

本质上来说库是一种可执行代码的二进制形式. linux下的库有两种:静态库和共享库(动态库) 静态库在程序编译时会被连接到目标代码中:程序运行时将不再需要该静态库,因此体积较大. 优点:程序运行时将不再需要该静态库 缺点:可执行文件的体积较大. 相同的库可能会需要被多次加载. 静态库: libxxxxx.a 动态库:动态库在程序编译时并不会被连接到目标代码中, 优点: 在程序运行时动态库才会被载入内存,因此代码体积较小. 缺点: 因此在程序运行时还需要动态库存在. 静态库的制作:将功能函数编译成

iOS 中的静态库与动态库,区别、制作和使用

如果我们有些功能要给别人用,但是又不想公开代码实现,比如高德地图.第三方登录分享等等,这时候我们就要打包成库了.库分静态库和动态库两种: 静态库:以.a 和 .framework为文件后缀名.动态库:以.tbd(之前叫.dylib) 和 .framework 为文件后缀名. 静态库与动态库的区别 静态库:链接时会被完整的复制到可执行文件中,被多次使用就有多份拷贝.动态库:链接时不复制,程序运行时由系统动态加载到内存,系统只加载一次,多个程序共用(如系统的UIKit.framework等),节省内

linux上静态库和动态库的编译和使用(附外部符号错误浅谈)

主要参考博客gcc创建和使用静态库和动态库 对于熟悉windows的同学,linux上的静态库.a相当于win的.lib,动态库.so相当于win的.dll. 首先简要地解释下这两种函数库的区别,参考<Linux程序设计> 1. 静态库也被称为归档文件(archive,因此创建命令是ar),编译器和链接器负责将程序代码和静态库结合在一起组成单独的可执行文件: 但是缺点是许多应用程序同时运行并使用来自同一个静态库的函数时,内存中就会有一个函数的多份副本,而且程序文件自身也有多份同样的副本,这将消

Linux使用静态库和动态库

Linux使用静态库和动态库 (一)库的概念 库是可以复用的代码,在一些大的项目中常常会用到库. 本质上说:库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 一般说库是说两种: 静态库:linux下.a文件.windows下.lib文件 动态库:linux下.so文件.windows下.dll文件 最近花了一些时间把linux下编译.链接等研究了一下,作为一个菜鸟记录并分享一蛤. (二)静态库与动态库 程序的编译运行要经过以下步骤: 1.源文件(.h .cpp等) 2.预编译 3.编

Linux下Gcc生成和使用静态库和动态库详解

参考文章:http://blog.chinaunix.net/uid-23592843-id-223539.html 一.基本概念 1.1什么是库 在windows平台和linux平台下都大量存在着库. 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的平台不同(主要是编译器.汇编器和连接器的不同),因此二者库的二进制是不兼容的. 本文仅限于介绍linux下的库. 1.2库的种类 linux下的库有两种:静态库和共享库(动态库). 二者的不同

静态库和动态库

简介 什么是库? 库是程序代码的集合,是共享程序代码的一种方式 根据源代码的公开情况,库可以分为2种类型 开源库 公开源代码,能看到具体实现 比如SDWebImage.AFNetworking 闭源库 不公开源代码,是经过编译后的二进制文件,看不到具体实现 主要分为:静态库.动态库 为什么制作闭源库 国内的企业,掌握有核心技术,同时是又希望更多的程序员来使用其技术,因此采用"闭源"的方式开发使用 例如:百度地图,友盟,JPush等 在企业开发中,一些核心技术或者常用框架,出于安全性和稳

linux学习 建立静态库,动态库,写简单的makefile

建立静态库 建立四个文件 bin(可执行文件),lib(库),include(头文件),src(放源文件) 这里的起的库明为add 在src文件中执行 1)gcc -c add.c //编译add.c源文件生成add.o目标文件 2)ar crsv ../lib/ libadd.a add.o //对目标文件*.o进行归档,生成lib*.a, 把这个生成文件放在lib里 3)gcc -o main main.c -L(大些的爱偶,放库的路径)../lib   –l(小写的爱偶,库名为add) a

【转】Linux下gcc生成和使用静态库和动态库详解

一.基本概念 1.1 什么是库 在Windows平台和Linux平台下都大量存在着库. 本质上来说,库是一种可执行代码的二进制形式,可以被操作系统载入内存执行. 由于windows和linux的平台不同(主要是编译器.汇编器和连接器的不同),因此二者的库的二进制是不兼容的. 本文仅限于介绍linux下的库. 1.2 库的种类 linux下的库有两种:静态库和共享库(动态库). 二者的不同点在于代码被载入的时刻不同. 静态库的代码在编译过程中已经被载入可执行程序,因此体积较大. 共享库的代码是在可