现在的位置: 首页 > 综合 > 正文

Linux下用gcc生成静态库和动态库

2013年10月20日 ⁄ 综合 ⁄ 共 2688字 ⁄ 字号 评论关闭

一、基本概念

1.1
什么是库


windows
平台和
linux
平台下都大量存在着库。

本质上来说库是 一种可执行代码的二进制形式,可以被操作系统载入内存执行。

由于
windows

linux
的平台不同(主要是编译器、汇编器和连接器 的不同),因此二者库的二进制是不兼容的。

本文仅限于介绍
linux
下的库。

 

 

1.2
库的种类

linux
下的库有两种:静态库和共享库(动态库)。

二者的不同点在于代码被载入的时刻不同。

静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。

共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。

 

 

1.3
库存在的意义

库是别人写好的现有的,成熟的,可以复用的代码,你可以使用但要记得遵守许可协议。

现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻 常。

共享库的好处是,不同的应用程序如果调用相同的库,那么在 内存里只需要有一份该共享库的实例。

 

 

1.4
库文件是如何产生的在
linux

静态库的后缀是
.a
,它的产生分两步

Step 1.
由源文件编译生成一堆
.o
,每个
.o
里都包含这个编译单元的符号表

Step 2.ar
命令将很多
.o
转换成
.a
,成文静态库

动态库的后缀是
.so
,它由
gcc
加特定参数编译产生。

具体方法参见后文实例。

 

1.5
库文件是如何命名的,有没有什么规范


linux
下,库文件一般放在
/usr/lib


/lib

下,

静态库的名字一般为
libxxxx.a
,其中
xxxx
是该
lib
的名称

动态库的名字一般为
libxxxx.so.major.minor

xxxx
是该
lib
的名称,
major

是主版本号,
 minor

是副版本号

 

 

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

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

例如
# ldd /bin/lnlibc.so.6

=> /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2

=> /lib/ld- linux.so.2 (0×40000000)

可以看到
ln
命令依赖于
libc

库和
ld-linux

 

 

1.7
可执行程序在执行的时候如何定位共享库文件

当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路径

此时就需要系统动态载入器
(dynamic linker/loader)

对于
elf
格式的可执行程序,是由
ld-linux.so*
来完成的,它先后搜索
elf
文件的
 DT_RPATH
段—环境变量
LD_LIBRARY_PATH

/etc/ld.so.cache
文件列表—
/lib/,/usr/lib
目录找到库文件后将其载入内存

如:
export LD_LIBRARY_PATH=’pwd’

将当前文件目录添加为共享目录

 

1.8
在新安装一个库之后如何让系统能够找到他

如果安装在
/lib
或者
/usr/lib

下,那么
ld
默认能够找到,无需其他操作。

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

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

2.
运行
ldconfig

,该命令会重建
/etc/ld.so.cache
文件


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

我们通常把一些公用函数制作成函数库,供其它程序使用。

函数库分为
静态库

动态库
两种。

 

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

 

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

 

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

 

为了便于阐述,我们先做一部分准备工 作。

 

2.1
准备好测试代码
hello.h

hello.c

main.c

hello.h(
见程序
1)
为该函数库的头文件。

 

hello.cpp(
见程序
2)
是函数库的源程序,其中包含公用函数
hello
,该函数将在屏幕上输出
"Hello XXX!"

 

main.cpp(
见程序
3)
为测试库文件的主程序,在主程序中调用了公用函数
hello

 

 
程序
1: hello.h

#
ifndef HELLO_H
# define HELLO_H

  
void hello(
const
char * name)
;
  
# endif


程序2:hello.cpp

#
include < stdio. h>

void hello(
const
char * name)
{  

        printf
( "Hello %s!\n"
, name)
;

}

程序3:main.cpp

#
include "hello.h"

 int main(
)

 {
     hello( "everyone"
) ;
     return 0;
 }

2.2
问题的提出

注意:这个时候,我 们编译好的
hello.o
是无法通过
gcc –o 
编译的,这个道理非常简单,

hello.c
是一个没有
main
函数的
.c
程序,因此不够成一个完整的程序,如果使用
gcc –o 
编译并连接它,
GCC
将报错。

无论静态库,还是动态库,都是由
.o
文件创建的。因此,我们必须将源程序
hello.c
通过
gcc
先编译成
.o
文件。

这个时候我们有 三种思路:

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

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

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

2.3
思路一:编译多个源文件

在系统提示符下键入以下命令得到
hello.o
文件。

# g++ -c hello.cpp
 

为什么不适用
g++–o hello hello.cpp 
这个道理 我们之前已经说了,使用
-c
是什么意 思呢?这涉及到
g++ 
编译选项 的常识。

 

我们通常使用的
g++ –o 
是将
.cpp
源文件编 译成为一个可执行的二进制代码,这包括调用作为
GCC
内的一部 分真正的
C
编译器(
ccl
),以及 调用
GNU C
编译器的 输出中实际可执行代码的外部
GNU
汇编器和 连接器工具。


g++ –c
是使用
GNU
汇编器将 源文件转化为目标代码之后就结束,在这种情况下连接器并没有被执行,所以输出的目标文件不会包含作为
Linux
程序在被 装载和执行时所必须的包含信息,但它可以在以后被连接到一个程序。

我们运行
ls
命令看看 是否生存了
hello.o
文件。

# ls

hello.cpp hello.h hello.o main.cpp

 


ls
命令结果 中,我们看到了
hello.o
文件,本 步操作完成。

 

同理编译
main

#g++ –c main.cpp

抱歉!评论已关闭.