您的位置 首页 新品

学会在Linux下GCC生成和运用静态库和动态库

学会在Linux下GCC生成和使用静态库和动态库-由于windows和linux的平台不同(主要是编译器、汇编器和连接器的不同),因此二者库的二进制是不兼容的。

  一、基本概念1.1什么是库

  在windows渠道和linux渠道下都许多存在着库。

  实质上来说库是一种可履行代码的二进制办法,能够被操作体系载入内存履行。

  由于windows和linux的渠道不同(首要是编译器、汇编器和衔接器的不同),因而二者库的二进制是不兼容的。

  本文仅限于介绍linux下的库。

  1.2库的品种

  linux下的库有两种:静态库和同享库(动态库)。

  二者的不同点在于代码被载入的时间不同。

  静态库的代码在编译进程中现已被载入可履行程序,因而体积较大。

  同享库的代码是在可履行程序运转时才载入内存的,在编译进程中仅简略的引证,因而代码体积较小。

  1.3库存在的含义

  库是他人写好的现有的,老练的,能够复用的代码,你能够运用但要记住恪守答应协议。

  实践中每个程序都要依靠许多根底的底层库,不或许每个人的代码都从零开端,因而库的存在含义非同小可。

  同享库的长处是,不同的应用程序假如调用相同的库,那么在内存里只需求有一份该同享库的实例。

  1.4库文件是怎么发生的在linux下

  静态库的后缀是.a,它的发生分两步

  Step 1.由源文件编译生成一堆.o,每个.o里都包括这个编译单元的符号表

  Step 2.ar指令将许多.o转换成.a,成为静态库

  动态库的后缀是.so,它由gcc加特定参数编译发生。

  详细办法拜见后文实例。

  1.5库文件是怎么命名的,有没有什么规范

  在linux下,库文件一般放在/usr/lib和/lib下,

  静态库的姓名一般为libxxxx.a,其间xxxx是该lib的称号

  动态库的姓名一般为libxxxx.so.major.minor,xxxx是该lib的称号,major是主版别号, minor是副版别号

  1.6怎么知道一个可履行程序依靠哪些库

  ldd指令能够检查一个可履行程序依靠的同享库,

  例如# ldd /bin/lnlibc.so.6

  =》 /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2

  =》 /lib/ld- linux.so.2 (0×40000000)

  能够看到ln指令依靠于libc库和ld-linux库

  1.7可履行程序在履行的时分怎么定位同享库文件

  当体系加载可履行代码时分,能够知道其所依靠的库的姓名,可是还需求知道绝对途径。

  此刻就需求体系动态载入器(dynamic linker/loader)

  关于elf格局的可履行程序,是由ld-linux.so*来完结的,它先后查找elf文件的 DT_RPATH段—环境变量LD_LIBRARY_PATH—/etc/ld.so.cache文件列表—/lib/,/usr/lib目录找到库文件后将其载入内存

  如:export LD_LIBRARY_PATH=’pwd’

  将当时文件目录增加为同享目录

  1.8在新装置一个库之后怎么让体系能够找到他

  假如装置在/lib或许/usr/lib下,那么ld默许能够找到,无需其他操作。

  假如装置在其他目录,需求将其增加到/etc/ld.so.cache文件中,进程如下

  1.修正/etc/ld.so.conf文件,加入库文件地点目录的途径

  2.运转ldconfig,该指令会重建/etc/ld.so.cache文件

  二、用gcc生成静态和动态链接库的示例

  咱们一般把一些共用函数制造成函数库,供其它程序运用。

  函数库分为静态库和动态库两种。

  静态库在程序编译时会被衔接到方针代码中,程序运转时将不再需求该静态库。

  动态库在程序编译时并不会被衔接到方针代码中,而是在程序运转是才被载入,因而在程序运转时还需求动态库存在。

  本文首要经过举例来阐明在Linux中怎么创立静态库和动态库,以及运用它们。

  为了便于论述,咱们先做一部分准备工作。

  2.1准备好测验代码hello.h、hello.c和main.c;

  hello.h(见程序1)为该函数库的头文件。

  hello.c(见程序2)是函数库的源程序,其间包括共用函数hello,该函数将在屏幕上输出“Hello XXX!”。

  main.c(见程序3)为测验库文件的主程序,在主程序中调用了共用函数hello。

  程序1: hello.h

  #ifndef HELLO_H

  #define HELLO_H

  void hello(const char *name);

  #endif

  程序2:hello.c#include

  void hello(const char *name) {

  printf(“Hello %s!\n”, name);

  }

  程序3:main.c#include “hello.h”

  int main()

  {

  hello(“everyone”);

  return 0;

  }2.2问题的提出

  留意:这个时分,咱们编译好的hello.o是无法经过gcc –o 编译的,这个道理十分简略,

  hello.c是一个没有main函数的.c程序,因而不行成一个完好的程序,假如运用gcc –o 编译并衔接它,GCC将报错。

  不管静态库,仍是动态库,都是由.o文件创立的。因而,咱们有必要将源程序hello.c经过gcc先编译成.o文件。

  这个时分咱们有三种思路:

  1) 经过编译多个源文件,直接将方针代码组成一个.o文件。

  2) 经过创立静态链接库libmyhello.a,使得main函数调用hello函数时可调用静态链接库。

  3) 经过创立动态链接库libmyhello.so,使得main函数调用hello函数时可调用静态链接库。

  2.3思路一:编译多个源文件

  在体系提示符下键入以下指令得到hello.o文件。

  # gcc -c hello.c

  为什么不运用gcc–o hello hello.cpp 这个道理咱们之前现已说了,运用-c是什么意思呢?这涉及到gcc 编译选项的知识。

  咱们一般运用的gcc –o 是将.c源文件编译成为一个可履行的二进制代码(-o选项其实是拟定输出文件文件名,假如不加-c选项,gcc默许会编译衔接生成可履行文件,文件的称号有-o选项指定),这包括调用作为GCC内的一部分真实的C编译器(ccl),以及调用GNU C编译器的输出中实践可履行代码的外部GNU汇编器(as)和衔接器东西(ld)。

  而gcc –c是运用GNU汇编器将源文件转化为方针代码之后就完毕,在这种情况下,只调用了C编译器(ccl)和汇编器(as),而衔接器(ld)并没有被履行,所以输出的方针文件不会包括作为Linux程序在被装载和履行时一切必要的包括信息,但它能够在今后被衔接到一个程序。

  咱们运转ls指令看看是否生计了hello.o文件。

  # ls

  hello.c hello.h hello.o main.c

  在ls指令成果中,咱们看到了hello.o文件,本步操作完结。

  同理编译main

  #gcc –c main.c

  将两个文件链接成一个.o文件。

  #gcc –o hello hello.o main.o

  运转

  # 。/hello

  Hello everyone!

  完结^ ^!

  2.4思路二:静态链接库

  下面咱们先来看看怎么创立静态库,以及运用它。

  静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:咱们将创立的静态库名为myhello,则静态库文件名便是libmyhello.a。在创立和运用静态库时,需求留意这点。创立静态库用ar指令。

  在体系提示符下键入以下指令将创立静态库文件libmyhello.a。

  # ar rcs libmyhello.a hello.o

  咱们相同运转ls指令检查成果:

  # ls

  hello.c hello.h hello.o libmyhello.a main.c

  ls指令成果中有libmyhello.a。

  静态库制造完了,怎么运用它内部的函数呢?只需求在运用到这些共用函数的源程序中包括这些共用函数的原型声明,然后在用gcc指令生成方针文件时指明静态库名,gcc将会从静态库中将共用函数衔接到方针文件中。留意,gcc会在静态库名前加上前缀lib,然后追加扩展名.a得到的静态库文件名来查找静态库文件,因而,咱们在写需求衔接的库时,只写姓名就能够,如libmyhello.a的库,只写:-lmyhello

  在程序3:main.c中,咱们包括了静态库的头文件hello.h,然后在主程序main中直接调用共用函数hello。下面先生成方针程序hello,然后运转hello程序看看成果怎么。

  # gcc -o hello main.c -static -L. -lmyhello

  # 。/hello

  Hello everyone!

  咱们删去静态库文件试试共用函数hello是否真的衔接到方针文件 hello中了。

  # rm libmyhello.a

  rm: remove regular file `libmyhello.a‘? y

  # 。/hello

  Hello everyone!

  程序照旧运转,静态库中的共用函数现已衔接到方针文件中了。

  静态链接库的一个缺陷是,假如咱们一起运转了许多程序,而且它们运用了同一个库函数,这样,在内存中会许多复制同一库函数。这样,就会糟蹋许多宝贵的内存和存储空间。运用了同享链接库的Linux就能够防止这个问题。

  同享函数库和静态函数在同一个当地,仅仅后缀有所不同。比方,在一个典型的Linux体系,规范的同享数序函数库是/usr/lib/libm.so。

  当一个程序运用同享函数库时,在衔接阶段并不把函数代码衔接进来,而仅仅链接函数的一个引证。当终究的函数导入内存开端真实履行时,函数引证被解析,同享函数库的代码才真实导入到内存中。这样,同享链接库的函数就能够被许多程序一起同享,而且只需存储一次就能够了。同享函数库的另一个长处是,它能够独立更新,与调用它的函数毫不影响。

  2.5思路三、动态链接库(同享函数库)

  咱们持续看看怎么在Linux中创立动态库。咱们仍是从.o文件开端。

  动态库文件名命名规范和静态库文件名命名规范相似,也是在动态库名增加前缀lib,但其文件扩展名为.so。例如:咱们将创立的动态库名为myhello,则动态库文件名便是libmyhello.so。用gcc来创立动态库。

  在体系提示符下键入以下指令得到动态库文件libmyhello.so。

  # gcc -shared -fPIC -o libmyhello.so hello.o

  “PIC”指令行符号告知GCC发生的代码不要包括对函数和变量详细内存方位的引证,这是由于现在还无法知道运用该音讯代码的应用程序会将它衔接到哪一段内存地址空间。这样编译出的hello.o能够被用于树立同享链接库。树立同享链接库只需求用GCC的”-shared”符号即可。

  咱们照样运用ls指令看看动态库文件是否生成。

  # ls

  hello.cpp hello.h hello.o libmyhello.so main.cpp

  调用动态链接库编译方针文件。

  在程序中运用动态库和运用静态库彻底相同,也是在运用到这些共用函数的源程序中包括这些共用函数的原型声明,然后在用gcc指令生成方针文件时指明动态库名进行编译。咱们先运转gcc指令生成方针文件,再运转它看看成果。

  假如直接用如下办法进行编译,并衔接:

  # gcc -o hello main.c -L. -lmyhello

  (运用”-lmyhello”符号来告知GCC驱动程序在衔接阶段引证同享函数库libmyhello.so。”-L.”符号告知GCC函数库或许坐落当时目录。不然GNU衔接器会查找规范体系函数目录:它先后查找1.elf文件的 DT_RPATH段—2.环境变量LD_LIBRARY_PATH—3./etc/ld.so.cache文件列表—4./lib/,/usr/lib目录找到库文件后将其载入内存,可是咱们生成的同享库在当时文件夹下,并没有加到上述的4个途径的任何一个中,因而,履行后会呈现过错)

  # 。/hello

  。/hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

  #

  过错提示,找不到动态库文件libmyhello.so。程序在运转时,会在/usr/lib和/lib等目录中查找需求的动态库文件。若找到,则载入动态库,不然将提示相似上述过错而停止程序运转。有多种办法能够处理,

  (1)咱们将文件 libmyhello.so复制到目录/usr/lib中,再试试。

  # mv libmyhello.so /usr/lib

  # 。/hello

  成功!

  (2)已然衔接器会查找LD_LIBRARY_PATH所指定的目录,那么咱们能够将这个环境变量设置成当时目录:

  先履行:

  export LD_LIBRARY_PATH=$(pwd)

  再履行:

  。/hello

  成功!

  (3)

  履行:

  ldconfig /usr/zhsoft/lib

  注: 当用户在某个目录下面创立或复制了一个动态链接库,若想使其被体系同享,能够履行一下“ldconfig 目录名”这个指令。此指令的功用在于让ldconfig将指定目录下的动态链接库被体系同享起来,意即:在缓存文件/etc/ld.so.cache中追加进指定目录下的同享库。本例让体系同享了/usr/zhsoft/lib目录下的动态链接库。该指令会重建/etc/ld.so.cache文件

  成功!

  ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥

  下面的这个过错我没有遇到,不过也记录下,给遇到的人:

  { 这步后我没有成功,报错内容如下:/hello: error while loading shared libraries: /usr/lib/libmyhello.so: cannot restore segment prot after reloc: Permission denied

  google了一下,发现是SELinux搞的鬼,处理办法有两个:

  1.

  chcon -t texrel_shlib_t /usr/lib/libmyhello.so

  (chcon -t texrel_shlib_t “你不能share的库的绝对途径”)

  2.

  #vi /etc/sysconfig/selinux file

  或许用

  #gedit /etc/sysconfig/selinux file

  修正SELINUX=disabled

  重启

  }

  #

  ¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥

  这也进一步阐明晰动态库在程序运转时是需求的。

  能够检查程序履行时调用动态库的进程:

  # ldd hello

  履行 test,能够看到它是怎么调用动态库中的函数的。

  [pin@localhost 20090505]$ ldd hello

  linux-gate.so.1 =》 (0x00110000)

  libmyhello.so =》 /usr/lib/libmyhello.so (0x00111000)

  libc.so.6 =》 /lib/libc.so.6 (0x00859000)

  /lib/ld-linux.so.2 (0x0083a000)

  咱们回过头看看,发现运用静态库和运用动态库编译成方针程序运用的gcc指令彻底相同,

  那当静态库和动态库同名时,gcc指令会运用哪个库文件呢?抱着对问题必究究竟的心境,

  来试试看。

  先删去除.c和.h外的一切文件,康复成咱们刚刚修正完举例程序状况。

  # rm -f hello hello.o /usr/lib/libmyhello.so

  # ls

  hello.c hello.h main.c

  #

  在来创立静态库文件libmyhello.a和动态库文件libmyhello.so。

  # gcc -c hello.c

  # ar rcs libmyhello.a hello.o

  # gcc -shared -fPCI -o libmyhello.so hello.o

  # ls

  hello.c hello.h hello.o libmyhello.a libmyhello.so main.c

  #

  经过上述最终一条ls指令,能够发现静态库文件libmyhello.a和动态库文件libmyhello.so都现已生成,并都在当时目录中。然后,咱们运转gcc指令来运用函数库myhello生成方针文件hello,并运转程序 hello。

  # gcc -o hello main.c -L. -lmyhello

  # 。/hello

  。/hello: error while loading shared libraries: libmyhello.so: cannot open shar

  ed object file: No such file or directory

  #

  从程序hello运转的成果中很简略知道,当静态库和动态库同名时, gcc指令将优先运用动态库。

  Note:

  编译参数解析

  最首要的是GCC指令行的一个选项:

  -shared 该选项指定生成动态衔接库(让衔接器生成T类型的导出符号表,有时分也生成弱衔接W类型的导出符号),不必该标志外部程序无法衔接。相当于一个可履行文件

  l -fPIC:标明编译为方位独立的代码,不必此选项的话编译后的代码是方位相关的所以动态载入时是经过代码复制的办法来满意不同进程的需求,而不能到达真实代码段同享的意图。

  l -L.:标明要衔接的库在当时目录中

  l -ltest:编译器查找动态衔接库时有隐含的命名规矩,即在给出的姓名前面加上lib,后边加上.so来确认库的称号

  l LD_LIBRARY_PATH:这个环境变量指示动态衔接器能够装载动态库的途径。

  l 当然假如有root权限的话,能够修正/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来到达相同的意图,不过假如没有root权限,那么只能选用输出LD_LIBRARY_PATH的办法了。

  调用动态库的时分有几个问题会常常碰到,有时,分明现已将库的头文件地点目录 经过 “-I” include进来了,库地点文件经过 “-L”参数引导,并指定了“-l”的库名,但经过ldd指令观察时,便是死活找不到你指定链接的so文件,这时你要作的便是经过修正 LD_LIBRARY_PATH或许/etc/ld.so.conf文件来指定动态库的目录。一般这样做就能够处理库无法链接的问题了。

  静态库链接时查找途径次序:

  1. ld会去找GCC指令中的参数-L

  2. 再找gcc的环境变量LIBRARY_PATH

  3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是最初compile gcc时写在程序内的

  动态链接时、履行时查找途径次序:

  1. 编译方针代码时指定的动态库查找途径;

  2. 环境变量LD_LIBRARY_PATH指定的动态库查找途径;

  3. 配置文件/etc/ld.so.conf中指定的动态库查找途径;

  4. 默许的动态库查找途径/lib;

  5. 默许的动态库查找途径/usr/lib。

  有关环境变量:

  LIBRARY_PATH环境变量:指定程序静态链接库文件查找途径

  LD_LIBRARY_PATH环境变量:指定程序动态链接库文件查找途径

  附注:

  作为Linux程序开发员,最好对开发东西和资源的方位有个开始了解。下面扼要介绍一下首要的文件夹和应用程序。

  1.应用程序(ApplicaTIons)

  应用程序一般都有固定的文件夹,体系通用程序放在/usr/bin,日后体系管理员在本地计算机装置的程序一般放在/usr/local/bin或许/opt文件夹下。除了体系程序外,大部分个人用到的程序都放在/usr/local下,所以坚持/usr的整齐十分重要。当晋级或许重装体系的时分,只要把/usr/local的程序备份一下就能够了。

  一些其他的程序有自己特定的文件夹,比方X Window体系,一般装置在/usr/X11中,或许/usr/X11R6。GNU的编译器GCC,一般放置在/usr/bin或许/usr/local/bin中,不同的Linux版别或许方位稍有不同。

  2.头文件(Head Files)

  在C言语和其他言语中,头文件声明晰体系函数和库函数,而且界说了一些常量。关于C言语,头文件基本上散落于/usr/include和它的子文件夹下。其他的编程言语的库函数散布在编译器界说的当地,比方在一些Linux版别中,X Window体系库函数散布在/usr/include/X11,GNU C++的库函数散布在/usr/include/g++。这些体系库函数的方位关于编译器来说都是“规范方位”,即编译器能够主动查找这些方位。

  假如想引证坐落规范方位之外的头文件,咱们需求在调用编译器的时分加上-I标志,来显式的阐明头文件地点文件夹。比方,

  $ gcc -I/usr/openwin/include hello.c

  会告知编译器除了规范方位外,还要去/usr/openwin/include看看有没有所需的头文件。详细情况见编译器的运用手册(man gcc)。

  库函数(Library Files)

  库函数便是函数的库房,它们都经过编译,重用性不错。一般,库函数相互合作,来完结特定的使命。比方控制屏幕的库函数(cursers和ncursers库函数),数据库读取库函数(dbm库函数)等。

  体系调用的规范库函数一般坐落/lib以及/usr/lib。C编译器(准确点说,衔接器)需求知道库函数的方位。默许情况下,它只查找规范C库函数。

  库函数文件一般最初字母是lib。后边的部分标明库函数的用处(比方C库函数用c标识, 数学库函数用m标明),小数点后的后缀标明库函数的类型:

  .a 指静态链接库

  .so 指动态链接库

  去/usr/lib看一下,你会发现,库函数都有动态和静态两个版别。

  与头文件相同,库函数一般放在规范方位,但咱们也能够经过-L标识符,来增加新的查找文件夹,-l指定特定的库函数文件。比方

  $ gcc -o x11fred -L/usr/openwin/lib x11fred.c -lX11

  上述指令就会在编译期间,链接坐落/usr/openwin/lib文件夹下的libX11函数库,编译生成x11fred。

  静态链接库(StaTIc Libraries)

  最简略的函数库便是一些函数的简略调集。调用库函数中的函数时,需求在调用函数中include界说库函数的头文件。咱们用-l选项增加规范函数库之外的函数库。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/xinpin/89652.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部