Linux中的静态库和共享库

一、库的概念

库是一个二进制文件,包含的代码可被程序调用。例如标准C库、数学库、线程库等等。库有源码,可下载后编译,也可以直接安装二进制包。
库是事先编译好的,可以复用的代码,在OS上运行的程序基本上都要使用库。使用库可以提高开发效率。
Windows和Linux下库文件的格式不兼容。
Linux下包含静态库和共享库。

二、静态库

2.1 静态库的特点
  • 编译(链接)时把静态库中相关代码复制到可执行文件中
  • 程序中包含代码,运行时不再需要静态库
  • 程序运行时无需加载库,运行速度更快
  • 占用更多磁盘和空间
  • 静态库升级后,程序需要重新编译链接
2.2 静态库的创建及链接

第一步:确定库中函数的功能、接口

第二步:编写库源码

/****hello.c****/
#include <stdio.h>
#include "hello.h"

void hello(void){
    
    
	printf("hello Andyxi\n");
}
/****hello.h****/
#ifndef _HELLO_H_
#define _HELLO_H_

void hello(void);

#endif

第三步:编译生成目标文件

linux@linux:~/andy/lib$ ls
hello.c  hello.h
linux@linux:~/andy/lib$ gcc -c hello.c -Wall
linux@linux:~/andy/lib$ ls
hello.c  hello.h  hello.o

第四步:创建静态库

linux@linux:~/andy/lib$ ls
hello.c  hello.h  hello.o
linux@linux:~/andy/lib$ ar crs libhello.a hello.o  //使用 ar crs 命令创建静态库
linux@linux:~/andy/lib$ ls
hello.c  hello.h  hello.o  libhello.a	//注意libhello.a是库文件名,hello是库名
linux@linux:~/andy/lib$ nm libhello.a	//使用 nm 命令可查看库中符号信息

hello.o:
00000000 T hello
         U puts

第五步:编写应用程序

/****test.c****/
#include <stdio.h>
#include "hello.h"

int main(int argc, const char *argv[]){
    
    
	hello();
	return 0;
}

第六步:编译应用程序并链接静态库

linux@linux:~/andy/lib$ ls
hello.c  hello.h  hello.o  libhello.a  test.c
linux@linux:~/andy/lib$ gcc -o test test.c -L. -lhello //使用-L. -l+库名 链接静态库
linux@linux:~/andy/lib$ ls
hello.c  hello.h  hello.o  libhello.a  test  test.c
linux@linux:~/andy/lib$ ./test
hello Andyxi

由于使用的是静态库,编译后相关代码已经复制到可执行文件中。删除静态库,不影响程序执行

linux@linux:~/andy/lib$ ls
hello.c  hello.h  hello.o  libhello.a  test  test.c
linux@linux:~/andy/lib$ rm libhello.a 
linux@linux:~/andy/lib$ ls
hello.c  hello.h  hello.o  test  test.c
linux@linux:~/andy/lib$ ./test 
hello Andyxi

三、共享库

3.1 共享库的特点
  • 编译(链接)时仅记录用到哪个共享库中的哪个符号,不复制共享库中相关代码
  • 程序不包含库中代码,尺寸小
  • 多个程序可共享一个库
  • 程序运行时需要加载库
  • 库升级方便,无需重新编译程序
  • 使用更加广泛
3.2 共享库的创建及链接

第一步:确定库中函数的功能、接口

第二步:编写库源码

/****hello.c****/
#include <stdio.h>

void hello(void){
    
    
	printf("hello world\n");
	return ;
}
/****bye.c****/
#include <stdio.h>

void bye(void){
    
    
	printf("bye!\n");
	return ;
}
/****共享库头文件common.h****/
#ifndef __COMMON_H__
#define __COMMON_H__

void hello(void);
void bye(void);

#endif

第三步:编译生成目标文件

linux@linux:~/andy/lib/share$ ls
bye.c  common.h  hello.c
linux@linux:~/andy/lib/share$ gcc -c -fPIC *.c -Wall
linux@linux:~/andy/lib/share$ ls
bye.c  bye.o  common.h  hello.c  hello.o
  • fPIC选项:告诉编译器生成位置无关代码
  • 位置无关代码:生成的".o文件"文件中的代码可以被加载到任意的地址执行。编译的时候用到了相对寻址而不是绝对寻址

第四步:创建共享库common

linux@linux:~/andy/lib/share$ gcc -shared -o libcommon.so.1 hello.o bye.o
linux@linux:~/andy/lib/share$ ls
bye.c  bye.o  common.h  hello.c  hello.o  libcommon.so.1
  • shared选项:告诉编译器生成一个共享库
  • 生成的共享库的文件名叫"libcommon.so.1",其中".so"表示这是一个共享库,".1"表示这个库的版本是1
  • 符号链接文件命名规则:lib<库名>.so

第五步:编写应用程序

/****test.c****/
#include <stdio.h>
#include "common.h"

int main(int argc, const char *argv[]){
    
    
	hello();
	bye();

	return 0;
}

第六步:编译应用程序并链接共享库

/****为共享库文件创建链接文件****/
linux@linux:~/andy/lib/share$ ls
bye.c  bye.o  common.h  hello.c  hello.o  libcommon.so.1  test.c
linux@linux:~/andy/lib/share$ ln -s libcommon.so.1 libcommon.so	//ln -s创建符号链接
linux@linux:~/andy/lib/share$ ls
bye.c  bye.o  common.h  hello.c  hello.o  libcommon.so  libcommon.so.1  test.c
/****编译应用程序并链接共享库****/
linux@linux:~/andy/lib/share$ gcc -o test test.c -L. -lcommon
linux@linux-:~/andy/lib/share$ ls
bye.c  bye.o  common.h  hello.c  hello.o  libcommon.so  libcommon.so.1  test  test.c
  • gcc -o test test.c -L. -lcommon:可以发现此处共享库和静态库用法相同;GCC在链接时首先找共享库,如果共享库不存在,则链接静态库,如果静态库也找不到,则报错;加"-static"选项后,编译器会直接去找静态库。
3.3 共享库的加载

此时执行程序,会报错

linux@linux:~/andy/lib/share$ ./test
./test: error while loading shared libraries: libcommon.so: cannot open shared object file: No such file or directory
  • 执行时出错原因:因为程序链接的是共享库,并没有复制共享库中的代码,程序在执行时还会去加载用到的共享库,在加载的时候回去缺省的路径(比如"/lib","/usr/lib")下去寻找共享库,但是我们创建的库在当前目录下,并不在系统库的搜索路径里,所以在执行的时候找不到共享库就报错了;
  • 因此创建好共享库后还需要添加共享库加载路径

第七步:加载共享库并执行程序

linux@linux:~/andy/lib/share$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
linux@linux:~/andy/lib/share$ ./test
hello world
bye!
  • export:将原来的环境变量导出
  • “:“前面”$LD_LIBRARY_PATH"是引用原先的值;”:“后面的”.“是追加了当前目录;还可以追加其余共享库的路径,要用”:"隔开
  • 此方法是临时的,只对当前终端有效。当重新打开一个终端再执行改程序时又会报错
3.4 如何找到共享库

为了让系统能找到要加载的共享库,通常由三种方法:

  • 把库拷贝到 /usr/lib 和 /lib 目录下
  • 在 LD_LIBRARY_PATH 环境变量中添加库所在路径
  • 添加 /etc/ld.so.conf.d/*.conf 文件,执行 ldconfig 刷新

猜你喜欢

转载自blog.csdn.net/Chuangke_Andy/article/details/108264126