推荐阅读:
本文作者读行学孟宁,转载请注明出处!
静态链接。在编译链接时直接将需要的执行代码复制到最终可执行文件中,优点是代码的装载速度快,执行速度也比较快,对外部环境依赖度低。编译时它会把需要的所有代码都链接进去,应用程序相对比较大。缺点是如果多个应用程序使用同一库函数,会被装载多次,浪费内存。
动态链接。在编译时不直接复制可执行代码,而是通过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统。操作系统负责将需要的动态库加载到内存中,然后程序在运行到指定的代码时,去共享执行内存中已经加载的动态库去执行代码,最终达到运行时链接的目的。优点是多个程序可以共享同一段代码,而不需要在磁盘上存储多个副本。缺点是在运行时加载,可能会影响程序的前期执行性能,而且对使用的库依赖性较高,在升级时特别容易出现版本不兼容的问题。
以如下hello.c代码为例,我们分别进行静态链接和动态链接对比一下。
#include<stdio.h>
void main()
{
printf("Hello world!\n");
}
如下编译出的hello就是静态链接的可执行文件。如果在编译时不加“-static”选项,则编译器会默认使用动态链接。如下动态链接的可执行文件hello.dynamic只有7452字节,而静态链接版本hello大小约是其100倍。
$ gcc hello.c -o hello -static
$ gcc hello.c -o hello.dynamic
$ ls -l hello*
-rwxr-xr-x 1 root root 7452 8月 8 16:33 hello.dynamic
-rwxr-xr-x 1 root root 727908 8月 8 08:21 hello
动态链接分为可执行程序装载时动态链接和运行时动态链接,接下来将介绍这两种动态链接。
装载时动态链接
以下实例源码shlibexample.h与shlibexample.c是一个简单动态库的源码,只提供一个函数SharedLibApi()。使用如下指令可能将其编译成libshlibexample.so文件。
$ gcc -shared shlibexample.c -o libshlibexample.so
shlibexample.h的源码如下:
/* FILE NAME : shlibexample.h */
#ifndef _SH_LIB_EXAMPLE_H_
#define _SH_LIB_EXAMPLE_H_
#define SUCCESS 0
#define FAILURE (-1)
#ifdef __cplusplus
extern "C" {
#endif
/*
* Shared Lib API Example
* input : none
* output : none
* return : SUCCESS(0)/FAILURE(-1)
*
*/
int SharedLibApi();
#ifdef __cplusplus
}
#endif
#endif /* _SH_LIB_EXAMPLE_H_ */
shlibexample.c的源码如下:
/* FILE NAME : shlibexample.c */
#include <stdio.h>
#include "shlibexample.h"
/*
* Shared Lib API Example
* input : none
* output : none
* return : SUCCESS(0)/FAILURE(-1)
*
*/
int SharedLibApi()
{
printf("This is a shared libary!\n");
return SUCCESS;
}
只要将以上头文件和生成库文件放置在正确的目录下,就可以像调用printf一样调用SharedLibApi()。
运行时动态链接
运行时动态链接库的源文件为dllibexample.h和dllibexample.c。编译成libdllibexample.so文件的指令如下:
gcc -shared dllibexample.c -o libdllibexample.so
dllibexample.h的源码如下:
#ifndef _DL_LIB_EXAMPLE_H_
#define _DL_LIB_EXAMPLE_H_
#ifdef __cplusplus
extern "C" {
#endif
/*
* Dynamical Loading Lib API Example
* input : none
* output : none
* return : SUCCESS(0)/FAILURE(-1)
*
*/
int DynamicalLoadingLibApi();
#ifdef __cplusplus
}
#endif
#endif /* _DL_LIB_EXAMPLE_H_ */
dllibexample.c的源码如下:
/*
* Revision log:
*
* Created by Mengning,2012/5/3
*
*/
#include <stdio.h>
#include "dllibexample.h"
#define SUCCESS 0
#define FAILURE (-1)
/*
* Dynamical Loading Lib API Example
* input : none
* output : none
* return : SUCCESS(0)/FAILURE(-1)
*
*/
int DynamicalLoadingLibApi()
{
printf("This is a Dynamical Loading libary!\n");
return SUCCESS;
}
运行时动态链接本质上是由程序员自己来控制整个过程的,其基本流程如下:
//先将动态库加载进来
void * handle = dlopen("libdllibexample.so",RTLD_NOW);
//声明一个函数指针
int (*func)(void);
//根据名称找到函数指针
func = dlsym(handle,"DynamicalLoadingLibApi"); //调用已声明函数
func();
如下代码分别以装载时动态链接和运行时动态链接调用了两个动态链接库。从动态链接库的角度是没有差别的,差别只是程序员使用动态链接库的方法不同。
#include <stdio.h>
#include "shlibexample.h"
#include <dlfcn.h>
int main()
{
printf("This is a Main program!\n");
/* 装载时动态链接 */
printf("Calling SharedLibApi() function of libshlibexample.so!\n");
SharedLibApi();
/* 运行时动态链接 */
void * handle = dlopen("libdllibexample.so",RTLD_NOW);
if(handle == NULL)
{
printf("Open Lib libdllibexample.so Error:%s\n",dlerror());
return FAILURE;
}
int (*func)(void);
char * error;
func = dlsym(handle,"DynamicalLoadingLibApi");
if((error = dlerror()) != NULL)
{
printf("DynamicalLoadingLibApi not found:%s\n",error);
return FAILURE;
}
printf("Calling DynamicalLoadingLibApi() function of libdllibexample.so!\n");
func();
dlclose(handle);
return SUCCESS;
}
这里的shlibexample在链接时就需要,所以需要提供其路径,对应的头文件shlibexample.h也需要在编译器能找到位置。使用参数-L指明头文件所在目录,使用-l指明库文件名,如libshlibexample.so去掉lib和.so的部分。dllibexample只在程序运行到相关语句时才会访问,在编译时不需要任何的相关信息,只是用参数-ldl指明其需要使用共享库dlopen等函数。当然在实际运行时,也要确保libdllibexample.so是应用可以查找到的,这也是要修改环境变量LD_LIBRARY_PATH的原因。最终的编译及运行效果如下:
$ gcc main.c -o main -L/path/to/your/dir -lshlibexample -ldl
$ export LD_LIBRARY_PATH=$PWD #将当前目录加入默认路径,否则main找不到依赖的库文件,当然也可以将库文件复制到默认路径下。
$ ./main
This is a Main program!
Calling SharedLibApi() function of libshlibexample.so!
This is a shared libary!
Calling DynamicalLoadingLibApi() function of libdllibexample.so!
This is a Dynamical Loading libary!
推荐阅读:
本文作者读行学孟宁,转载请注明出处!