一、动态库、静态库简介,每次都能学到些东西

  • 栏目:软件 时间:2020-03-28 11:55
<返回列表

上一篇介绍了Linux 静态库生成指南,有兴趣的朋友可以前往阅读。

做开发快3年了,在linux下编译安装软件算是家常便饭了。就拿gcc来说,都有不下10次了,可基本每次都会碰到些奇奇怪怪的问题。看来还是像vs、codeblocks这样的ide把人弄蠢了。便下定决心一定要好好学习下如何在linux下纯手工gcc编译c项目。今天学了2点,一个是库文件处理,另一个是makefile编写。

linux下生成静态库和动态库

Linux下动态库文件的文件名形如 libxxx.so,其中so是 Shared Object 的缩写,即可以共享的目标文件。

学习的系统是centos6.6,编译升级的gcc4.8.2,明天写个博客总结下这回gcc安装的过程,每次都能学到些东西。

一、动态库、静态库简介

在链接动态库生成可执行文件时,并不会把动态库的代码复制到执行文件中,而是在执行文件中记录对动态库的引用。

gcc的编译过程

首先需要清楚gcc编译做了些什么

源文件----预处理---->预处理文件(*.i)----编译---->汇编文件(*.s)----汇编编译---->目标文件(*.o)----链接---->可执行文件

基本与windows下的类似。通过给gcc加一些选项,可以控制编译工作进行到指定的阶段,下面试常用的一些gcc编译选项

学习过c和c++的都知道,c或c++程序存在各种各样的库文件,就是已经编译好的包含数据和执行代码的二进制文件。windows就是dll文件,linux下有.a和.so文件。如果需要使用这些库文件,在ide环境下勾个选项就把事给办了,在手工编译的情况下就麻烦了点。

库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:

程序执行时,再去加载动态库文件。如果动态库已经加载,则不必重复加载,从而能节省内存空间。

gcc创建和使用静态库

静态库.a(win 系统下是lib)和动态库.so(win 系统下是.dll)。所谓静态、动态是指链接。回顾一下,将一个程序编译成可执行程序的步骤:

Linux下生成和使用动态库的步骤如下:

编写static_lib.c文件

韦德体育 1

韦德体育 2

  1. 编写源文件。
  2. 将一个或几个源文件编译链接,生成共享库。
  3. 通过 -L<path> -lxxx 的gcc选项链接生成的libxxx.so。
  4. 把libxxx.so放入链接库的标准路径,或指定 LD_LIBRARY_PATH,才能运行链接了libxxx.so的程序。

创建静态库

1 gcc -c static_lib.c
2 ar rcs static_lib.a static_lib.o

上面的命令会在当前目录下生产 static_lib.a 静态库文件

静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。

下面通过实例详细讲解。

使用链接静态库

编写 static_lib.h文件

韦德体育 3

编写main3.c文件,使用静态库中的方法

韦德体育 4

编译main3.c并链接静态库文件

执行

1 gcc main3.c -lstatic_lib.a -o app3

但却出现链接器ld找不到库的问题,把-l参数去掉就正常了

1 gcc main3.c static_lib.a -o app3

最后会生成可执行文件app3。静态库的特点是将库里的代码放到了执行文件里,如果修改了静态库的代码,要重新编译依赖它执行文件才能升级

韦德体育 5

编写源文件

建立一个源文件: max.c,代码如下:

int max(int n1, int n2, int n3)
{
    int max_num = n1;
    max_num = max_num < n2? n2: max_num;
    max_num = max_num < n3? n3: max_num;
    return max_num;
}

编译生成共享库:

gcc -fPIC -shared -o libmax.so max.c

我们会得到libmax.so。

实际上上述过程分为编译和链接两步, -fPIC是编译选项,PIC是 Position Independent Code 的缩写,表示要生成位置无关的代码,这是动态库需要的特性; -shared是链接选项,告诉gcc生成动态库而不是可执行文件。

上述的一行命令等同于:

gcc -c -fPIC max.c
gcc -shared -o libmax.so max.o

gcc创建和使用动态库

动态库就是在有执行文件需要使用这个库时,动态加载到执行的库文件。

韦德体育 6

为动态库编写接口文件

为了让用户知道我们的动态库中有哪些接口可用,我们需要编写对应的头文件。

建立 max.h ,输入以下代码:

#ifndef __MAX_H__
#define __MAX_H__

int max(int n1, int n2, int n3);

#endif

编写share_lib.c文件

韦德体育 7

静态库特点总结:

测试,链接动态库生成可执行文件

建立一个使用max函数的test.c,代码如下:

#include <stdio.h>
#include "max.h"

int main(int argc, char *argv[])
{
    int a = 10, b = -2, c = 100;
    printf("max among 10, -2 and 100 is %d.n", max(a, b, c));
    return 0;
}

gcc test.c -L. -lmax 生成a.out,其中-lmax表示要链接libmax.so
-L.表示搜索要链接的库文件时包含当前路径。

注意,如果同一目录下同时存在同名的动态库和静态库,比如 libmax.so 和 libmax.a 都在当前路径下,
则gcc会优先链接动态库。

创建动态库

因为需要与位置无关,所以需要使用-fPIC选项,gcc的选项有上千个,需要查询某个选项可以man gcc然后查找查看

1 gcc -shared -fPIC -o share_lib.so share_lib.c

生成的share_lib.so文件就是动态库文件,在使用这个库的程序使用时被动态加载,并没有被写入到别的执行文件中,所以当库文件修改,不需要去重新编译其他使用这个库的程序

-静态库对函数库的链接是放在编译时期完成的。

韦德体育 ,运行

运行 ./a.out 会得到以下的错误提示。

./a.out: error while loading shared libraries: libmax.so: cannot open shared object file: No such file or directory

找不到libmax.so,原来Linux是通过 /etc/ld.so.cache 文件搜寻要链接的动态库的。
而 /etc/ld.so.cache 是 ldconfig 程序读取 /etc/ld.so.conf 文件生成的。
(注意, /etc/ld.so.conf 中并不必包含 /lib 和 /usr/libldconfig程序会自动搜索这两个目录)

如果我们把 libmax.so 所在的路径添加到 /etc/ld.so.conf 中,再以root权限运行 ldconfig 程序,更新 /etc/ld.so.cache ,a.out运行时,就可以找到libmax.so

但作为一个简单的测试例子,让我们改动系统的东西,似乎不太合适。
还有另一种简单的方法,就是为a.out指定 LD_LIBRARY_PATH

LD_LIBRARY_PATH=. ./a.out

程序就能正常运行了。LD_LIBRARY_PATH=. 是告诉 a.out,先在当前路径寻找链接的动态库。

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

使用动态库

share_lib.h文件声明函数

韦德体育 8

编写main4.c文件,include "share_lib.h" 文件

韦德体育 9

编译main4.c并链接动态库

1 gcc main4.c ./share_lib.so -o app4

生成的app4就是可执行文件.

-程序在运行时与函数库再无瓜葛,移植方便。

makefile让工作自动化

编写makefile,内容如下:

.PHONY: build test clean

build: libmax.so

libmax.so: max.o
    gcc -o $@  -shared $<

max.o: max.c
    gcc -c -fPIC $<

test: a.out

a.out: test.c libmax.so
    gcc test.c -L. -lmax
    LD_LIBRARY_PATH=. ./a.out

clean:
    rm -f *.o *.so a.out

make build就会生成libmax.so, make test就会生成a.out并执行,make clean会清理编译和测试结果。

编写makefile编译

将前面静态库的3个源文件main3.c,static_lib.c,static_lib.h放到一个目录下。

韦德体育 10

编写Makefile文件。

韦德体育 11

其中冒号左边表示目标文件或者命令,命令也叫伪目标。

执行make

韦德体育 12

执行make clean可以清除编译产生的文件

韦德体育 13

当然,这恐怕是最简单的makefile了,Makefile还有很多学的,我也在学习中,以后有收获会继续写博客记录,争取以后看大神写的makefile不要在略懂略懂了

 

-浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。

-是静态库对程序的更新、部署和发布页会带来麻烦。如果静态库liba.lib更新了,所以使用它的应用程序都需要重新编译、发布给用户(对于玩家来说,可能是一个很小的改动,却导致整个程序重新下载,全量更新)。

动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。

韦德体育 14

动态库特点总结:

-动态库把对一些库函数的链接载入推迟到程序运行的时期。

-可以实现进程之间的资源共享。(因此动态库也称为共享库)

-将一些程序升级变得简单。

-甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。

本文主要通过举例来说明在 Linux 中如何创建静态库和动态库,以及使用它们。

[ps:在windows平台和linux平台下都大量存在着库。由于windows和linux的平台不同(主要是编译器、汇编器和连接器的不同),因此二者库的二进制是不兼容的。本文仅限于介绍 linux 下的库。]

二、用gcc生成静态和动态链接库的示例

首先,本文主要通过举例来说明在 Linux 中如何创建静态库和动态库,以及使用它们。为了便于阐述,我们先准备下测试代码:hello.h、hello.c、main.c(我将这三个代码放在一个名为‘静态链接’的文件夹中)。如下所示

hello.h

#ifndef HELLO_H
#define HELLO_H
void hello(const char* name);
#endif

hello.c

#include <stdio.h>
void hello(const char* name){
    printf("hello %s! n",name);
}

main.c

#include "hello.h"
int main(){
hello("everyone");
return 0;
}

注意:这个时候,hello.c 是无法通过gcc –o 编译,这个道理非常简单,hello.c 是一个没有main函数的.c程序,因此不够成一个完整的程序,如果使用gcc –o编译并连接它,gcc 将报错。

(ps:gcc -o 与gcc -c 的区别。gcc–o  是将.c源文件编译成为一个可执行的二进制代码,这包括调用作为 GCC 内的一部分真正的 C 编译器( ccl ),以及调用GNU C编译器的输出中实际可执行代码的外部 GNU 汇编器和连接器工具。而g -c是使用GNU汇编器将源文件转化为目标代码之后就结束,在这种情况下连接器并没有被执行,所以输出的目标文件不会包含作为 Linux 程序在被装载和执行时所必须的包含信息,但它可以在以后被连接到一个程序)

其实,我们的问题就是,如何让main.c中能使用hello这个函数。这时候我们有三种思路:

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

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

3、 通过创建动态链接库libmyhello.so ,使得 main 函数调用 hello 函数时可调用静态链接库。

下面分别进行演示。

1.通过编译多个源文件,直接将目标代码合成一个.o 文件.首先利用gcc -c指令将hello.c和main.c编译生成hello.o和main.o文件,再用gcc -o指令将hello.o和main.o文件生成一个可执行文件hello。具体如下图所示:

韦德体育 15

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

下面我们先来看看如何创建静态库,以及使用它。Linux静态库命名规范,必须是"lib[your_library_name].a":lib为前缀,中间

是静态库名,扩展名为.a。例如:我们将创建的静态库名为 myhello ,静态库文件名就是 libmyhello.a。在创建和使用静态库时,

需要注意这点。Linux创建静态库分为两步:

第一步:将代码文件编译成目标文件。即将hello.c文件编译成hello.o文件,使用指令是:gcc -c hello.c

第二步:利用ar工具将目标文件hello.o打包成静态库文件.a(注意命名规则,我的静态库文件名为:libmyhello.a)

具体如下图所示:韦德体育 16

现在我们已经生成了静态库文件libmyhello.a了,下面就是怎么使用它。Linux下使用静态库,只需要在编译的时候,指定静态库的

搜索路径(-L选项)、指定静态库名(不需要lib前缀和.a后缀,-l选项)。

指令:

 gcc -o hello main.c -L/Users/wangxuewei/Desktop/静态链接 -lmyhello

指令可以这么解释:利益gcc -o指令,将main.c编译成可执行文件hello。其中main.c函数中要使用的函数,存储在静态链接库

libmyhello.a 。"-L/Users/wangxuewei/Desktop/静态链接"是该静态库文件所在地址,"-lmyhello"表示的是要使用的静态库文

件的名字,看起来怪怪的哈,因为它不需要制定前缀lib和后缀.a,所以就变成了myhello,-l则是指令标示符号。

[ps:关于gcc指令中的-l和-L参数问题。-l参数就是用来指定程序要链接的库,-l参数紧接着就是库名,那么库名跟真正的库文件名有什么关系呢?就拿数学库来说,他的库名是m,他的库文件名是libm.so(.o),很容易看出,把库文件名的头lib和尾.so/.o去掉就是库名了。放在/lib和/usr/lib和/usr/local/lib里的库直接用-l参数就能链接了,但如果库文件没放在这三个目录里,而是放在其他目录里,这时我们只用-l参数的话,链接还是会出错,出错信息大概是:“/usr/bin/ld: cannot find -lxxx”,也就是链接程序ld在那3个目录里找不到libxxx.so,这时另外一个参数-L就派上用场了,比如常用的X11的库,它在/usr/X11R6/lib目录下,我们编译时就要用-L/usr/X11R6/lib -lX11参数,-L参数跟着的是库文件所在的目录名。再比如我们把libtest.so放在/aaa/bbb/ccc目录下,那链接参数就是-L/aaa/bbb/ccc -ltest]

进一步可以删除静态函数libmyhello.a,然后运行hello函数,看看其是否真的链接到了目标函数.

韦德体育 17

3、 通过创建动态链接库libmyhello.so ,使得 main 函数调用 hello 函数时可调用静态链接库。

linux动态库的命名规则

动态链接库的名字形式为 libxxx.so,前缀是lib,后缀名为“.so”。

l  针对于实际库文件,每个共享库都有个特殊的名字“soname”。在程序启动后,程序通过这个名字来告诉动态加载器该载入哪个共享库。

l  在文件系统中,soname仅是一个链接到实际动态库的链接。对于动态库而言,每个库实际上都有另一个名字给编译器来用。它是一个指向实际库镜像文件的链接文件(lib+soname+.so)。

创建动态库(.so)可以分为两步:

第一步:生成目标文件,此时要加编译器选项-fPIC。[ps:-fPIC是创建与地质无关的编译程序,是为了能够在多个应用程序间共享]

gcc -fPIC -c hello.c

第二步:生成动态库,此时要加链接器选项-shared。[ps:-shared是指定生成动态链接库]

gcc -shared -o libmyhello.so hello.o

以上两步也可以写成一条指令:

gcc -shared -fPCI -o libmyhello.so hello.c

运行该指令后会生成一个libmyhello.so文件,如下图:

韦德体育 18

使用动态链接库
引用动态链接库,和静态库一样,在编译时指定动态链接库就好

gcc main.c -L. -lmyhello

-L.  代表动态库在当前路径下;-l后面跟的是动态链接库的名字(与静态一样,可以舍去前缀lib和后缀.so)

执行该指令后,会生成一个a.out的可执行文件,可以执行下看看:

韦德体育 19
可以看到,是可以执行的!!但是,在网上看,很多博客说这样是不可以的,因为:库文件在连接(静态库和动态库)和运行(仅限于使用动态库的程序)时被使用,其搜索路径是在系统中进行设置的。一般Linux系统把/lib 和 /usr/lib(也可能是/usr/local/lib)三个目录作为默认的库搜索路径,所以使用这两个目录中的库时不需要进行设置路径即可直接使用。对于处于默认库搜索路径之外的库,需要将库的位置添加到搜索路径之中。但是,我电脑中,当动态库与执行程序在同一目录下时,也可以运行,可能是因为我用的mac吧,暂时没弄清楚。。。不过可以确定,当将动态库libmyhello.so移动到/usr/local/lib时,a.out仍可正常运行。

韦德体育 20

更一般的,对于处于默认路径之外的库,如何让系统找到它呢?一般有两种方法:

1.将动态库复制到/lib 或 /usr/lib 或 /usr/local/lib中。就像我上面做的一样,将libmyhello.so复制到/usr/local/lib中,这是系统是可以找到的。

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

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

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

[ps:第二种方法我没有试,因为mac系统里好像跟这个不太一样,还在研究中。。。具体可以参考下面几篇文章,解释的比较详细]

参考:

http://blog.163.com/hitperson@126/blog/static/130245975201151552938133

http://blog.163.com/redhumor@126/blog/static/19554784201154101229272/

http://blog.csdn.net/jaylong35/article/details/6132087

上一篇:负载均衡集群配置(1),在server.xml文件里面未注释掉的有两个 下一篇:没有了

更多阅读

一、动态库、静态库简介,每次都能学到

软件 2020-03-28
上一篇介绍了Linux静态库生成指南,有兴趣的朋友可以前往阅读。 做开发快3年了,在linux下编...
查看全文

韦德体育命令中各选项的含义为,把ind

软件 2020-03-27
在Linux中要修改一个文件夹或文件的权限我们需要用到linux chmod命令来做,下面我写了几个简单...
查看全文

负载均衡集群配置(1),在server.xml文件

软件 2020-03-27
这篇文章主要讲解关于如何在Linux下搭建Apache+tomcat负载均衡服务器集群的过程。 Linux下搭建A...
查看全文

友情链接: 网站地图

Copyright © 2015-2019 http://www.koi-bumi.com. 韦德体育有限公司 版权所有