Linux下的静态库,共享库的创建和使用

本文详细介绍了在Linux环境下编译程序过程中静态库与共享库的概念及其创建与使用方法。对比了静态库与共享库在空间占用上的差异,并通过实例演示了如何创建这两种类型的库文件及如何在程序中进行静态链接与动态链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

编译程序一般要经过:预处理,编译,汇编和链接这几个步骤。在我们写程序的时候,会存在一些公共的代码。如果为每个程序都要重写这些代码,那么势必会造成太多的麻烦。我们可以将这么公共代码生成库文件,在编译程序的链接步骤中,可以静态地或者动态地将这些公共代码与我们的程序链接,这样就避免了重写这些代码的麻烦了。库文件可以分为静态库和共享库;其使用方式可以分为静态链接和动态链接两种类别。

静态库: 在Linux下静态的格式是.a(archive),它是有一个以上的目标文件(.o)通过ar命令集成的一种库文件。在程序链接静态库时,在编译期间就将所需要的函数和数据从静态库中复制出来。如果有多个程序连接到一个静态库上,那么它们分别需要复制自己需要的函数和数据,这样就导致了大量空间的浪费。

共享库:顾名思义,共享库是可以在多个可执行文件间共享。当多个可执行文件链接到一个共享库时,它们不需要复制自己需要的函数和数据,而是通过一个函数连接表来确定使用哪个函数或数据,这样能有效的避免静态库的空间浪费。但是引入函数连接表会带来额外的开销,现在这个问题已经被prelink和gnu hash等技术比较好的缓解了。

可执行文件:在Linux下加x属性的文件都是可执行文件。上面所指的可执行文件是ELF(Executable and Link Format)格式的文件。可执行文件在编译时链接到静态库,所用到的函数或数据会被复制过来,而在运行期不再依赖静态库。当可执行文件在编译时连接到了共享库,它不拷贝所用的函数或数据,但在运行时会依赖其链接的共享库。

库文件的使用包括:静态加载和动态加载两种方式,从字面意思上我们能稍微了解下它们的不同功能,具体怎么使用将会在下面更详细的描述。


静态库的创建:

如上面静态库的描述中所说:它是一个以上的目标文件(.o)通过ar命令继承的一种库文件。我们先了解下简单的静态库的创建。

首先创建两个源文件:

/*src1.h*/
#ifndef SRC1_H_
#define SRC1_H_
void src1();
#endif

/*src1.c*/
#include <stdio.h>
#include "src1.h"
void print1()
{
	printf("this is src1 func! \n");
}

/*src2.h*/
#ifndef SRC2_H_
#define SRC2_H_
void src2();
#endif

/*src2.c*/
#include <stdio.h>
#include "src2.h"
void print2()
{
	printf("this is src2 func! \n");
}

在终端中分别编译两个源文件到目标文件 

gcc -O -c src1.c src2.c
生成两个目标文件src1.o src2.o。上面我们提到静态库是一个以上的目标文件的集合,借用ar 命令在终端可以实现静态库的继承

ar rsv libtest.a src1.o src2.o

可以生成一个libtest.a的静态库。ar是linux的一个命令,可以通过man ar查看它的不同参数的意义。这里需要注意下,我们生成的静态库的名字期望是这样的一种格式:lib+myname +.a;这样做的目的是为了在编译链接时更好的书写,可以参看下ld命令的几个参数。下面我们就创建一个main的源文件

/*main.c*/
#include <stdio.h>
#include "src1.h"
#include "src2.h"

int main()
{
	print1();
	print2();
	return 0;
}
我们在终端实现mian.c的编译,同时要链接到静态库libtest.a;

gcc -Wall -g main.c -o main -L./ -ltest
这样就实现了main.c的编译,生成的可执行文件为main,通过在终端输入命令./main,我们可以得到:

this is src1 func!
this is src2 func!
其实上面的头文件是不需要的,但是加上也没有问题。静态链接的一个比较经典的例子就是Linux 下的多线程的头文件<pthread.h>;如果我们的程序利用到该头文件中的函数或数据时,那么在编译程序的时候必须要加上-lpthread。在上面编译main.c的命令中,我们用到了参数-L,该参数的目的是指出静态库的具体位置,如果没有-L,那么会在系统环境变量中寻找静态库libtest.a,如果libtest.a的当前位置的路径不在环境变量的定义中,那么它是不会被找到的。因此如果不加-L,我们需要更改环境变量或者将libtest.a拷贝到/etc/lib文件夹中。


共享库的创建

共享库的创建也是利用一个以上的目标文件,不过它的创建命令不是ar。我们以上面的两个源文件为例,继续创建一个共享库。

gcc -Wall -shared -o mytest.so src1.o src2.o
该命令可以实现创建一个mytest.so的共享库。我们还是以上面的main.c源文件为例,创建一个可执行的文件dymain。

gcc -Wall -g -o dymain main.c ./mytest.so
此时就可以生成一个可执行文件dymain,运行./dymain,得到的结果和上面是一致的。比较静态库和共享库的静态链接方式:
gcc -Wall -g main.c -o main -L./ -ltest
gcc -Wall -g -o dymain main.c ./mytest.so


以上提到的是静态链接方式,那么如何实现动态链接呢?首先只有共享库才能实现动态链接,动态链接的实现需要4个函数dlopen(), dlsym(), dyerror()以及dlclose()。它们包含在头文件<dlfcn.h>中。下面我们就创建一个dymain.c的源文件实现动态链接我们之前建立的共享库。

/*dymain.c*/
#include<stdio.h>
#include <stdlib.h>
#include "src1.h"
#include "src2.h"
#include <dlfcn.h>
typedef void (*Func)();
int main()
{
	void * handle = NULL;
	char *error = NULL;
	Func func = NULL;

	handle = dlopen("./mytest.so", RTLD_LAZY);

	if(!handle)
	{
		fputs(dlerror(), sterr);
		exit(1);
	}

	func = dlsym(handle, "print1");

	if((error = dlerror()) != NULL)
	{
		fputs(error, stderr);
		exit(1);
	}

	func();

	dlclose(handle);
	return 0;
}


编译dymain.c

gcc -Wall -o dynamicmain.o dymain.c -ldl
生成可执行文件dynamicmain,它的运行结果是:

this is src1 func!
这就是共享库的动态链接的实现。注意在编译dymain.c是一定要带上-ldl,它表明dymain.c依赖于共享库,是动态加载实现的。






评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值