Linux下的动态库与静态库

Linux下的动态库与静态库


1动态库的生成

背景问题
(1)printf定义在哪里?
我们#include <stdio.h>里面只有printf的声 明。。。

(2) 你编制了一个有价值的算法、或有价值的 功能模块,但想给别人使用、但又不想公开 代码?如何做到?


在C/C++里,使用库(Library)的技术,可以将编译 好的符号提供给第三方使用。
库分为两种:
(1)共享库Shared Library
(2)静态库Static Library
通常共享库也称为动态库
本章介绍Linux下的动态库和静态库的使用方法

动态库的生成
使用g++命令来生成动态库
编译,生成.o文件 (编译选项 ‐fPIC )
g++ ‐c ‐fPIC example.cpp ‐o example.o
链接,生成目标 .so文件 ( 链接选项 ‐shared)
g++ ‐shared example.o ‐o libexample.so
(PIC:Position Independent Code位置无关代码)

linux下动态库的规范命名:
libxxx.so
前缀lib
后缀.so
其中xxx是库的名称

例如 ,libexample.so

查看符号
使用nm命令查看库中的符号
nm libexample.so

交付物
交付物:
example.h
libexample.so
同时,告之此so文件适用的平台(跨平台使 用时可能会有问题)
例如,目标平台:centos 6.6 32bit

小结:
1,动态库的生成
2,动态库的命名
3,查看符号
4,交付物

2动态库的使用

交付物
example.h
libexample.so
平台:centos6.6 32bit
如何使用这个库呢?

动态库的使用
编辑:
代码:包含头文件,调用里面的函数即可
#include “example.h”
int main()
{
example(1,2); return 0;
}

编译:
g++ ‐c main.cpp ‐o main.o
链接:
g++ main.o ‐o helloworld ‐L. ‐lexample
链接选项:
‐lexample 使用libexample.so这个库文件
‐L. 指定库文件的位置

程序的运行
./helloworld
通常会提示无法运行程序:
libexample.so: …: No such file or directory
问题:libexample.so就在当前目录下,为什么 操作系统会提示找不到该文件呢?

操作系统默认从标准位置寻找相应的库
/lib /usr/lib /usr/local/lib
如果没有找到依赖的库文件,则从
LD_LIBRARY_PATH环境变量里寻找。。。
也就是说,库文件要么放在标准位置,要么放在 LD_LIBRARY_PATH指定的位置,才能被操作系统 找到。。。
libexample.so 放在指定的位置

先使用export命令设置环境变量, 然后再运行 程序。
export LD_LIBRARY_PATH=.
./helloworld

查看依赖的库
拿到一个可执行程序,怎么知道它依赖哪些库呢?
readelf ‐d helloworld
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libexample.so]
0x00000001 (NEEDED) Shared library: [libstdc++.so.6]
0x00000001 (NEEDED) Shared library: [libm.so.6]
0x00000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x00000001 (NEEDED) Shared library:[libc.so.6]

标准C/C++库
至少要知道两个基本的库:
libc.so 标准C库(ANSI C)
libstdc++.so标准c++库(包含STL)
不需要在命令行中指定,g++默认会链接到这 两个库

当程序无法运行的时候,显示No such file or directory的时候,要用readelf ‐d来检查一下它 需要哪些库?这些库在系统上是否存在?

小结:
1,动态库的使用:编辑,编译,链接
2,libexample.so 放在指定的位置
3,查看依赖的库

3生成动态库 - 补充说明

内容提要
(1)在Makefile中创建动态库
(2)在动态库中使用别的动态库
(3)在动态库*享class类型

1.在Makefile中生成动态库
修改标准Makefile Lv1.0
增加编译选项‐fPIC,链接选项‐shared
① EXE=libexample.so
② g++‐shared $(CXX_OBJECTS) ‐o $(EXE)
③ g++ ‐c ‐fPIC ‐MMD $< ‐o [email protected]

Linux下的动态库与静态库
2.动态库中使用别的动态库
在创建动态库时,里面可以调用其他动态库。 默认地,可以调用标准C函数库和标准C++ STL 库。
如果要调用其他第三方库,可以在链接时添 加链接选项。。。
g++ ‐shared $(CXX_OBJECTS) ‐o $(EXE) ‐lxxx

3.动态库*享class类型
没有什么特别之外,直接按普通方式把class 的头文件和cpp文件写出来就行

小结:
1,Makefile中生成动态库
2,动态库中使用别的动态库
3,动态库*享class类型

4库的标准目录结构

库的目录结构
Linux下面可能会经常使用各种库,有的是系统 自带的库,有的是第三方库。
通常它们的目录结构是:
libxxx/
‐lib/
‐include/
其中,bin下为程序, lib下为库文件, include为头 文件

第三方库的目录结构
系统自带库放在
/usr
‐include /
‐lib /
演示:把example库放在
/home/mytest/example/目录下

头文件:
使用编译选项‐I(大写字母I)参数来指定
g++ ‐c ‐I /home/mytest/example/include …
库文件:
‐L /home/mytest/example ‐lexample
或‐l/home/mytest/example/libexample.so
(全路径也是可以的)

Makefile中使用库
通常,在Makefile里定义两个变量
CXXFLAGS表示C++的编译选项
LDFLAGS表示链接选项

Linux下的动态库与静态库

INCLUDE路径
比较
#include <example.h>
#include “example.h”
<example.h> : 仅从INCLUDE路径里查找此头文件 “example.h” : 先从当前目录下查找,如果不存在,再从 INCLUDE路径里查找。

用‐I选项来指定自定义的INCLUDE路径
标准INCLUDE路径 :/usr/include /usr/local/include

小结:
1,库的目录结构
2,Makefile中使用库

5静态库的创建与使用

静态库
静态库,static library
标准命名:libxxx.a
第一步:编译,得到*.o文件
第二步:打包
ar ‐rcs libxxx.a file1.o file2.o … fileN.o
(注:ar只是将*.o文件打个包而已,并非“链 接”)

静态库
演示:
g++ ‐c test1.cpp ‐o test1.o
g++ ‐c test2.cpp ‐o test2.o
ar ‐rcs libtest.a test1.o test2.o
查看静态库中的符号
nm libtest.a

交付物
静态库的交付物:
‐头文件*.h
‐库文件libxxx.a
另需注明,此静态库适用的平台
如centos6.6 32bit系统

使用静态库
由于libtest.a本质是就是test1.o和test2.o打个包而 已,因此可以像.o文件一样使用
比较:
g++ main.cpp test1.o test2.o ‐o helloworld
g++ main.cpp libtest.a ‐o helloworld
也就是说,在命令行里直接加上libtest.a的全路 径是可以的。

使用静态库
使用‐l选项,指定静态库
g++ main.cpp ‐o helloworld ‐L…/K07_05A ‐ltest
编译器在…/K07_05A下面发现了libtest.a。。。
(考虑:如果在…/K07_05A同时存在libtest.a和 libtest.so,编译器会选哪一个?)

静态库和动态库的区别
使用静态库: 最终的程序(helloworld)里含有函数test1和test2的代 码,所以helloworld在运行的时候不依赖libtest.a的存 在
使用动态库: 最终的程序里没有相应代码,所以程序在运行的时 候会寻找libtest.so
可执行程序 的,体积有差别。。。。
注:使用nm命令,查看是否含有符号的定义

小结:
1,静态库制作
2,静态库交付
3,静态库使用
4,静态库与动态库区别

6静态库与动态库混用

静态库和动态库同时存在
接上节课,如果目录下同时存在libtest.a和 libtest.so。。。 ‐build/
‐libtest.a
‐libtest.so
执行命令:
g++ main.cpp ‐o helloworld ‐Lbuild ‐ltest

静态库和动态库同时存在
查看它倒底用的哪个库。。。
nm helloworld
readelf ‐d helloworld

静态库和动态库同时存在
如果libtest.so 和libtest.a同时存在。。。
则默认优先连接libtest.so

强制使用静态库
当libtest.so 和libtest.a同时存在,以下两种方式可以强制 使用静态库:
① 使用全路径 【推荐】
g++ main.cpp build/libtest.a‐o helloworld
②‐static : 强制所有的库都使用静态库版本【行不通】
g++ main.cpp ‐o helloworld‐static‐Lbuild ‐ltest
缺点:
所有的库(包括libc , libstdc++)都必须提供静态库版本,少 一个都不行。。
注:centos默认安装时不带 libc.a libstdc++.a …

小结
1,除非你能提供所有库的静态库版本,否 则你无法使用‐static参数
2,当静态库和动态库混用时,通常就是 直接指定静态库的全路径。。。
(静态库就是一堆.o文件打个包而已。。。)

7C函数与C++函数的区别

C的函数与C++的函数
在C语言中,以函数的名字来唯一标识一个函 数,因此,函数名不可以重复。
在C++中,以函数的名字和参数共同标识一个 函数。因此,在C++中的函数可以重名,只要 参数列表有所不同即可。 (称为函数名重载

在目标文件中观察他们的区别
#include <stdio.h>
void example(int a, int b)
{
printf(“a=%d, b=%d \n”, a, b);
}

一、按C++编译, example.cpp
g++ ‐fPIC ‐shared example.cpp ‐o libexample.so
000004d0 T _Z7exampleii

二、按C编译, example.c
gcc ‐fPIC ‐shared example.c ‐o libexample.so
00000430 T example

结论:
在C语言中,函数的符号名就是函数名
example
在C++语言中,函数的符号名中有函数名和 参数列表信息 _Z7exampleii

C++中使用C的库
用gcc生成一个C的库,如何在C++中调用呢?
gcc ‐fPIC ‐shared example.c ‐o libexample.so
在main.cpp中,调用函数example。。
g++ main.cpp ‐o helloworld ‐Lbuild ‐lexample
会报链接错误:
undefined reference to `example(int, int)’
该函数明明存在,然而编译器却说未定义?

C++中使用C的库
实际上,编译器要找的符号是 _Z7exampleii, 而libexample.so里的符号名是example,所以 报告错误。

解决办法:
在C++里想要调用一个C的函数符号,需要添 加extern "C"声明,例如
extern “C” void example(int a, int b);

extern "C"的两种方式:
(1)声明单个函数
extern "C"void example(int a, int b);
(2)声明多个函数
extern "C"
{
void func1(int a, int b); void func2(int a, int b);
}

一般情况下,应该由库的作者来提供一个兼容C/C++的头文件。
//////////// example.h /////////////
#ifdef __cplusplus
extern “C” {
#endif
void example(int a, int b);
#ifdef __cplusplus
}
#endif

Linux下的动态库与静态库

cpp中按C语言编译
在*.cpp中定义的函数,默认都是生成C++的函数符号。
但是,可以强制按C来编译,
extern "C" void example(int a, int b)
{
printf(“a=%d, b=%d \n”, a, b);
}
主要用途:
(1) 在C的项目中,调用一个C++的库(不常见)
(2) 手工加载一个动态库(下一节课)
第一次学,请忽略

注意:
Linux下面的很多库是由C语言写成的,当我们 在C++项目中调用它时,要学会使用extern "C
补充: extern “C”{\ #incl
ude “aaa.h”
}

小结:
1,C与C++库的区别
2,C与C++库之间的调用

8动态库的手工加载

动态库的手工加载

默认情况下,当一个程序运行之前,操作系 统OS会检查它依赖了的so库并加载。。。此 种加载方式称为“自动加载”
动态库*.so还有另一种使用方式:手工加载

手工加载:在程序运行时刻,根据实际需要来加载/卸 载
使用dl库中的函数(dynamic linking loader)
头文件
#include <unistd.h>
#include <dlfcn.h>
链接选项
‐ldl
g++ main.cpp ‐o helloworld ‐ldl

代码里的使用dl的函数:
① 加载库:dlopen
② 找到函数符号: dlsym
③ 调用函数(函数指针的语法)
④ 卸载 库: dlclose

常见问题

  1. dlopen失败?
    检查是不是文件路径给错了
  2. dlsym失败
    用nm检查一下目标so中是否有该符号。注意, 一般只有C的函数符号才适合手工加载。
    (C++的函数符号名称由于规则较为复杂,不 适宜手工加载)

小结:
1,自动加载+手工加载
2,手工加载到步骤
3,常见问题

学习资源 《C语言/C++学习指南》Linux开发篇