Linux入职基础-7.6_简单生成动态库(实例讲解)

简单生成动态库(实例讲解)

一、动态库的介绍

Linux下动态库文件的文件名形如 libxxx.so,其中so是Shared Object 的缩写,即可以共享的目标文件。

在链接动态库生成可执行文件时,并不会把动态库的代码复制到执行文件中,而是在执行文件中记录对动态库的引用。

程序执行时,再去加载动态库文件。如果动态库已经加载,则不必重复加载,从而能节省内存空间。

二、动态库生成三步骤

√设计源码文件

√编译位置无关码(PIC)型.o文件

√D链接动态库

编译位置无关码(PIC)型.o文件的方法一般采用C语言编译器的“-KPIC” 或者“fpic”选项。

PIC是 Position Independent Code 的缩写,表示要生成位置无关的代码,这是动态库需要的特性。不同系统之间是有区别的。

三、不同系统编译区别

①Sco和Solaris系列

cc -O  -KPIC –G –o libd1.so  d1.c

cc -O -KPIC –G –o libd2.so  d2.c

编译器选项  -KPIC ,创建动态库的标志-G

cc –O -KPIC –c d1.c

cc –G –o libd1.so d1.o

②HP-UNIX系列

cc +Z –c d1.c

ld –b –o libd1.so d1.o

编译器选项 +Z,创建动态库的连接器标志是-b

③AIX系列

xlc_r4 –c d1.c

ld –G –bnoentry –bexpall –lc d1.o –O libd1.so

④Linux和其它使用gcc编译器系列的UNIX

gcc –fpic –c d1.c

gcc –shared –o libd1.so d1.o

当然。可以把编译和创建合并一起

Gcc –O –fpic –shared –o libd1.so d1.c

编译器选项 –fpic ,创建动态库的连接器标志是–shared

四、动态库的调用

动态库调用分两种方式:隐式和显示

4.1、隐式调用

设计一个调用自建动态库的例子,程序testdl.c调用libmax.so中的函数,相关代码如下:

①调用库函数代码

/*max.c*/

int max(int n1, int n2, int n3)

{

   int max_num = n1;

   max_num = max_num < n2? n2: max_num;

   max_num = max_num < n3? n3: max_num;

   return max_num;

}

编译生成共享库

[root@localhost chap301]# gcc -fPIC -shared -o libmax.so max.c

[root@localhost chap301]# ls

libmax.so max.c  max.h

为动态库编写接口文件:目的是为了让用户知道我们的动态库中有哪些接口可用。

/*max.h*/

#ifndef __MAX_H__

#define __MAX_H__

int max(int n1, int n2, int n3);

#endif

②新建测试代码程序testdl.c,链接动态库生成可执行文件testdl

/*testdl.c*/

#include <stdio.h>

#include "max.h"

int main(int argc, char *argv[])

{

   int a = 80, b = -10, c = 101;

   printf("max number is %d.\n", max(a, b, c));

   return 0;

}

[root@localhost chap301]# gcc -o testdl testdl.c -L. -lmax

[root@localhost chap301]# ls

libmax.so max.c  max.h  testdl  testdl.c

注意,如果同一目录下同时存在同名的动态库和静态库,比如 libmax.so 和 libmax.a 都在当前路径下,则gcc会优先链接动态库。

[root@localhost chap301]# ./testdl

./testdl: error while loading sharedlibraries: libmax.so: cannot open shared object file: No such file or directory

动态库libmax.so确实在当前目录下,但是却说找不到,为何?

③动态库的查找路径分析

如何让执行程序testdl到特定的目录去找动态库呢?介绍三个方法:

编辑/etc/ld.so.conf文件

原来Linux是通过/etc/ld.so.cache 文件搜寻要链接的动态库的。但是,/etc/ld.so.cache 是 ldconfig 程序读取 /etc/ld.so.conf 文件生成的。

现在我们把libmax.so 所在的路径添加到/etc/ld.so.conf 中,再以root权限运行ldconfig 程序,更新 /etc/ld.so.cache ,就可以找到 libmax.so。

[root@localhost chap301]#mv libmax.so /c_fun/dll

[root@localhost chap301]# ls

max.c max.h  testdl  testdl.c

[root@localhost chap301]# vim /etc/ld.so.conf.d/testdl.conf

[root@localhost chap301]# cat /etc/ld.so.conf.d/testdl.conf

/c_fun/dll

[root@localhost chap301]# ldconfig

[root@localhost chap301]# ./testdl

max number is 101.

这样执行程序testdl可以找到动态库testdl.so,得到正确的运行结果。

注意, /etc/ld.so.conf 中并不必包含 /lib 和 /usr/lib,ldconfig程序会自动搜索这两个目录。这也是为何很多程序在安装时直接把dll库直接cp到这两个目录,这样可执行程序即可自动找到dll原因!

使用环境变量来注册动态库

本例中,如果把动态库移出其它的目录,执行程序是否还能正常执行呢?现在我们做个实验来分析:

[root@localhost chap301]# mkdir dll

[root@localhost chap301]# mv  /c_fun/dll/libmax.so   ./dll

[root@localhost chap301]# ./testdl

./testdl: error while loading sharedlibraries: ./libmax.so: cannot open shared object file: No such file ordirectory

程序执行失败,无法找到libmax.so位置。

这时我们可以使用Linux的环境变量把动态库进行注册,即把libmax.so动态库的路径注册到环境变量,程序执行时遍历这个变量逐个查找。如下:

 [root@localhostchap301]# LD_LIBRARY_PATH=./dll

[root@localhost chap301]# export LD_LIBRARY_PATH

[root@localhost chap301]# ./testdl

max number is 101.

正常运行,LD_LIBRARY_PATH=./dll 是告诉 testdl先在当前路径下的dll目录中去寻找链接的动态库。

编译时指定路径

即在编译时明确指定动态库的路径

[root@localhost chap301]# mkdir dllpath

[root@localhost chap301]# ls

dll max.c  max.h    testdl.c

[root@localhost chap301]# gcc -O -o testdl testdl.c ./dll/libmax.so

[root@localhost chap301]# ./testdl

max number is 101.

上述例子中我们可以看出,动态库隐式调用与静态库很接近。

4.2、显式调用

通过函数(如dlopen()函数)调用动态库代码,一般会经过打开动态库、获取动态库对象地址、调用动态库对象,错误检查和关闭动态库五个步骤。

打开动态库

void *dlopen(const char *pathname,int mode)

pathname:带路径的动态库名称

mode:动态库加载方式:RTLD_LAZY  RTLD_NOW

获取动态库对象的地址

void dlsym(void *handle,const char *name)

handle:有上面dlopen函数返回的句柄

name:待调用的动态库对象(库中函数名或变量名)

调用动态库对象

函数调用:

定义动态库中函数类型的变量,如本文中libmax.so库中int max()函数。

int (*pFunc)();

pFunc=(int(*)())dlsym(void pHandle,”max”);

if(pFunc)

{

   inta = 80, b = -10, c = 101;

   printf("maxnumber is %d.\n", pFunc(a, b, c));

}

错误检查

char *dlerror(void);

无错误返回NULL

关闭动态库

int dlclose(void *handle);

现在我们通过具体实例来讲解

代码文件max.c、max.h同上范例一样,只需要重新编辑testdl.c文件,使用显示的调用方法来使用libmax.so动态库中函数max()。如下:

[root@localhost chap302]# pwd

/c_fun/mycode/chap302

[root@localhost chap302]# ls

libmax.so max.c  max.h  testdl.c

[root@localhost chap302]# vim testdl.c

/*testdl.c*/

#include<stdio.h>

#include<dlfcn.h>

#include"max.h"

intmain(int argc, char *argv[])

{

    void *pHandle;

    int (*pFunc)();

    char *error;

    pHandle=dlopen("./libmax.so",RTLD_NOW);

    error=dlerror();

    if(error)

    {

      printf("lib loaderror:%s\n",error);

      exit(1);

    }

    if(!pHandle)

    {

       printf("Cann't find libxxx.so filesof DLL \n");

       return 0;

     }

    pFunc=(int(*)())dlsym(pHandle,"max");

    if(pFunc)

    {

        int a = 80, b = -10, c = 101;

        printf("max number is %d.\n",pFunc(a, b, c));

     }

     else

     {

        printf("Cann't find max() func\n");

      }

     dlclose(pHandle);

     return 1;

}

[root@localhost chap302]# gcc -O -o testdl testdl.c -ldl

[root@localhost chap302]# ls

libmax.so max.c  max.h  testdl  testdl.c

[root@localhost chap302]# ./testdl

max number is 101.

五、动态库的Makefile例子

/*Makefile*/

#use make build: build libmax.so

#use make test:  build elf and run

#use make clean: clean all *.o *.so

 

.PHONY: build test clean

build: libmax.so

libmax.so: max.o

       gcc -o $@  -shared $<

max.o: max.c

       gcc -c -fPIC $<

test: testdl

testdl: testdl.c libmax.so

       #gcc -o $@ testdl.c -L. -lmax

       gcc -o $@ testdl.c -ldl

       LD_LIBRARY_PATH=.

       ./testdl

clean:

       rm -f *.o *.so

[root@localhost chap302]# make build

gcc -c -fPIC max.c

gcc -o libmax.so  -shared max.o

[root@localhost chap302]# ls

libmax.so Makefile  max.c  max.h max.o

技巧:对于elf格式的可执行程序,它先后搜索elf文件的 DT_RPATH 段,环境变量 LD_LIBRARY_PATH,/etc/ld.so.cache文件列表, /lib/,/usr/lib目录, 找到库文件后将其载入内存。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值