一、gcc/g++命令中关于库的参数:
-shared: 该选项指定生成动态连接库;
-fPIC:表示编译为位置独立(地址无关)的代码,不用此选项的话,编译后的代码是位置相关的,所以动态载入时,是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
-L:指定链接库的路径,-L. 表示要连接的库在当前目录中
-ltest:指定链接库的名称为test,编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so来确定库的名称
    -Wl,-rpath: 记录以来so文件的路径信息。
LD_LIBRARY_PATH:这个环境变量指示动态连接器可以装载动态库的路径。
当然如果有root权限的话,可以修改/etc/ld.so.conf文件,然后调用 /sbin/ldconfig来达到同样的目的,
     不过如果没有root权限,那么只能采用修改LD_LIBRARY_PATH环境变量的方法了。 
调用动态库的时候,有几个问题会经常碰到:
    1、有时,明明已经将库的头文件所在目录 通过 “-I” include进来了,库所在文件通过 “-L”参数引导,并指定了“-l”的库名,但通过ldd命令察看时,就是死活找不到你指定链接的so文件,这时你要作的就是通过修改 LD_LIBRARY_PATH或者/etc/ld.so.conf文件来指定动态库的目录。通常这样做就可以解决库无法链接的问题了。
 
二、静态库链接时搜索路径的顺序: 
1. ld会去找gcc/g++命令中的参数-L;
2. 再找gcc的环境变量LIBRARY_PATH,它指定程序静态链接库文件搜索路径;
      export LIBRARY_PATH=$LIBRARY_PATH:data/home/billchen/lib
3. 再找默认库目录 /lib /usr/lib /usr/local/lib,这是当初compile gcc时写在程序内的。
   
三、动态链接时、执行时搜索路径顺序: 
1. 编译目标代码时指定的动态库搜索路径;
    2. 环境变量LD_LIBRARY_PATH指定动态库搜索路径,它指定程序动态链接库文件搜索路径;
      export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:data/home/billchen/lib
3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径;
4. 默认的动态库搜索路径/lib;
5. 默认的动态库搜索路径/usr/lib。
 
四、静态库和动态链接库同时存在时,gcc/g++默认链接的是动态库:
    当一个库同时存在静态库和动态库时,比如libmysqlclient.a和libmysqlclient.so同时存在时:
    在Linux下,动态库和静态库同事存在时,gcc/g++的链接程序,默认链接的动态库
    可以使用下面的方法,给连接器ld传递参数,看是否链接动态库还是静态库。

    -Wl,-Bstatic -llibname        //指定让gcc/g++链接静态库
使用:
gcc/g++ test.c -o test -Wl,-Bstatic -llibname -Wl,-Bdynamic -lm -lc -Wl,-Bdynamic -llibname //指定让gcc/g++链接动态库
使用:
gcc/g++ test.c -o test -Wl,-Bdynamic -llibname
   如果要完全静态加在,使用-static参数,即将所有的库以静态的方式链入可执行程序,这样生成的可执行程序,不再依赖任何库,同事出现的问题是,这样编译出来的程序非常大,占用空间。
如果不适用-Wl,-Bdynamic -lm -c会有如下错误:
[chenbaihu@build17 lib]$ ls
libtest.a libtest.so t t.cc test.cc test.h test.o
[chenbaihu@build17 lib]$ g++ -Wall -g t.cc -o t -L./ -Wl,-Bstatic -ltest -Wl,-Bdynamic -lm -lc
[chenbaihu@build17 lib]$ g++ -Wall -g t.cc -o t -L./ -Wl,-Bstatic -ltest
/usr/bin/ld: cannot find -lm
collect2: ld 返回 1
参考:
     http://lists.gnu.org/archive/html/help-gnu-utils/2004-03/msg00009.html

五、有关环境变量:
LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径
LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径
 
六、库的依赖问题:
   比如我们有一个基础库libbase.a,还有一个依赖libbase.a编译的库,叫做libchild.a;在我们编译程序时,一定要先-lchild再-lbase。 如果使用 -lbase -lchild,在编译时将出现一些函数undefined,而这些函数实际上已经在base中已经定义;
   为什么会有库的依赖问题?
一、静态库解析符号引用:
链接器ld是如何使用静态库来解析引用的。在符号解析阶段,链接器从左至右,依次扫描可重定位目标文件(*.o)静态库(*.a)
在这个过程中,链接器将维持三个集合:
集合E:可重定位目标文件(*.o文件)的集合。
集合U:未解析(未定义)的符号集,即符号表中UNDEF的符号。
集合D: 已定义的符号集。
初始情况下,E、U、D均为空。
1、对于每个输入文件f,如果是目标文件(.o),则将f加入E,并用f中的符号表修改U、D(在文件f中定义实现的符号是D,在f中引用的符号是U),然后继续下个文件。
2、如果f是一个静态库(.a),那么链接器将尝试匹配U中未解析符号与静态库成员(静态库的成员就是.o文件)定义的符号。如果静态库中某个成员m(某个.o文件)定义了一个符号来解析U中引用,那么将m加入E中,
   同时使用m的符号表,来更新U、D。对静态库中所有成员目标文件反复进行该过程,直至U和D不再发生变化。此时,静态库f中任何不包含在E中的成员目标文件都将丢弃,链接器将继续下一个文件。
3、当所有输入文件完成后,如果U非空,链接器则会报错,否则合并和重定位E中目标文件,构建出可执行文件。
到这里,为什么会有库的依赖问题已经得到解答:
 因为libchild.a依赖于libbase.a,但是libbase.a在libchild.a的左边,导致libbase.a中的目标文件(*.o)根本就没有被加载到E中,所以解决方法就是交换两者的顺序。当然也可以使用-lbase -lchild -lbase的方法。
参考文章:http://pananq.com/index.php/page/3/
 
七、动态库升级问题:
   在动态链接库升级时,
不能使用cp newlib.so oldlib.so,这样有可能会使程序core掉;
而应该使用:
rm oldlib.so 然后 cp newlib.so oldlib.so
或者
mv oldlib.so oldlib.so_bak 然后 cp newlib.so oldlib.so

为什么不能用cp newlib.so oldlib.so ?

在替换so文件时,如果在不停程序的情况下,直接用 cp new.so old.so 的方式替换程序使用的动态库文件会导致正在运行中的程序崩溃。

解决方法:

解决的办法是采用“rm+cp” 或“mv+cp” 来替代直接“cp” 的操作方法。

linux系统的动态库有两种使用方法:运行时动态链接库,动态加载库并在程序控制之下使用。

1、为什么在不停程序的情况下,直接用 cp 命令替换程序使用的 so 文件,会使程序崩溃? 很多同学在工作中遇到过这样一个问题,在替换 so 文件时,如果在不停程序的情况下,直接用cp new.so old.so的方式替换程序使用的动态库文件会导致正在运行中的程序崩溃,退出。

这与 cp 命令的实现有关,cp 并不改变目标文件的 inode,cp 的目标文件会继承被覆盖文件的属性而非源文件。实际上它是这样实现的: strace cp libnew.so libold.so 2>&1 |grep open.*lib.*.so open("libnew.so", O_RDONLY|O_LARGEFILE) = 3 open("libold.so", O_WRONLY|O_TRUNC|O_LARGEFILE) = 4 在 cp 使用“O_WRONLY|O_TRUNC” 打开目标文件时,原 so 文件的镜像被意外的破坏了。这样动态链接器 ld.so 不能访问到 so 文件中的函数入口。从而导致 Segmentation fault,程序崩溃。ld.so 加载 so 文件及“再定位”的机制比较复杂。

2、怎样在不停止程序的情况下替换so文件,并且保证程序不会崩溃? 答案是采用“rm+cp” 或“mv+cp” 来替代直接“cp” 的操作方法。

在用新的so文件 libnew.so 替换旧的so文件 libold.so 时,如果采用如下方法: rm libold.so //如果内核正在使用libold.so,那么inode节点不会立刻别删除掉。 cp libnew.so libold.so 采用这种方法,目标文件 libold.so 的 inode 其实已经改变了,原来的 libold.so 文件虽然不能用"ls"查看到,但其inode并没有被真正删除,直到内核释放对它的引用。

(即: rm libold.so,此时,如果ld.so正在加在libold.so,内核就在引用libold.so的inode节点,rm libold.so的inode并没有被真正删除,当ld.so对libold.so的引用结束,inode才会真正删除。这样程序就不会崩溃,因为它还在使用旧的libold.so,当下次再使用libold.so时,已经被替换,就会使用新的libold.so)

同理,mv只是改变了文件名,其 inode 不变,新文件使用了新的 inode。这样动态链接器 ld.so 仍然使用原来文件的 inode 访问旧的 so 文件。因而程序依然能正常运行。

(即: mv libold.so ***后,如果程序使用动态库,还是使用旧的inode节点,当下次再使用libold.so时,就会使用新的libold.so)

到这里,为什么直接使用“cp new_exec_file old_exec_file”这样的命令时,系统会禁止这样的操作,并且给出这样的提示“cp: cannot create regular file `old': Text file busy”。

这时,我们采用的办法仍然是用“rm+cp”或者“mv+cp”来替代直接“cp”,这跟以上提到的so文件的替换有同样的道理。

但是,为什么系统会阻止cp覆盖可执行程序,而不阻止覆盖so文件呢

这是因为 Linux 有个 Demand Paging 机制,所谓“Demand Paging”,简单的说,就是系统为了节约物理内存开销,并不会程序运行时就将所有页(page)都加载到内存中,而只有在系统有访问需求时才将其加载。“Demand Paging”要求正在运行中的程序镜像(注意,并非文件本身)不被意外修改,因此内核在启动程序后会锁定这个程序镜像的 inode

对于 so 文件,它是靠 ld.so 加载的,而ld.so毕竟也是用户态程序,没有权利去锁定inode,也不应与内核的文件系统底层实现耦合。

Linux下进行程序设计时,关于库的使用:的更多相关文章

  1. linux下编译qt5.6.0静态库——configure配置

    linux下编译qt5.6.0静态库 linux下编译qt5.6.0静态库 configure生成makefile 安装选项 Configure选项 第三方库: 附加选项: QNX/Blackberr ...

  2. [转]Linux下用gcc/g++生成静态库和动态库(Z)

    Linux下用gcc/g++生成静态库和动态库(Z) 2012-07-24 16:45:10|  分类: linux |  标签:链接库  linux  g++  gcc  |举报|字号 订阅     ...

  3. Linux下C程序的编辑,编译和运行以及调试

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html 内部邀请码:C8E245J (不写邀请码,没有现金送) 国 ...

  4. linux下编译qt5.6.0静态库——configure配置(超详细,有每一个模块的说明)(乌合之众)

    linux下编译qt5.6.0静态库 linux下编译qt5.6.0静态库 configure生成makefile 安装选项 Configure选项 第三方库: 附加选项: QNX/Blackberr ...

  5. 位图文件(BMP)格式以及Linux下C程序实现(转)

    源:位图文件(BMP)格式以及Linux下C程序实现 说到图片,位图(Bitmap)当然是最简单的,它是Windows显示图片的基本格式,其文件扩展名为*.BMP.由于没有经过任何的压缩,故BMP图 ...

  6. linux下编译qt5.6.0静态库(使用./configure --help来看看都有哪些参数。超详细,有每一个模块的说明。如果改变了安装的目录,需要到安装目录下的bin目录下创建文件qt.conf)(乌合之众)good

    linux下编译qt5.6.0静态库 linux下编译qt5.6.0静态库 configure生成makefile 安装选项 Configure选项 第三方库: 附加选项: QNX/Blackberr ...

  7. linux下c程序调用reboot函数实现直接重启【转】

    转自:http://www.blog.chinaunix.net/uid-20564848-id-73878.html linux下c程序调用reboot函数实现直接重启 当然你也可以直接调用syst ...

  8. Linux下安装PHP的GD支持库

    Linux下安装PHP的GD支持库   1.安装 zlib  wget ftp://ftp.sunfreeware.com/pub/freeware/SOURCES/zlib-1.2.3.tar.gz ...

  9. Linux下C程序内存泄露检测

    在linux下些C语言程序,最大的问题就是没有一个好的编程IDE,当然想kdevelop等工具都相当的强大,但我还是习惯使用kdevelop工具,由于没有一个习惯的编程IDE,内存检测也就成了在lin ...

随机推荐

  1. Android 各个版本新特性

    一.Android 4.x 新锁屏界面: Android4.0重新设计了锁屏幕UI,下方的解锁虚拟按键向周围发射出微光,轻轻拖动就可以解锁,比原来在UI上确实有很大的进步. 全新Widget排列: 主 ...

  2. VIM初掌握

    Vim 是 Linux 系统上的最著名的文本/代码编辑器,也是早年的 Vi 编辑器的加强版.它的最大特色是完全使用键盘命令进行编辑,脱离了鼠标操作虽然使得入门变得困难,但上手之后键盘的各种巧妙组合操作 ...

  3. vue项目引入FastClick组件解决IOS系统下h5页面中的按钮点击延迟,连续点击无反应的问题

    异常描述: ios系统手机中访问h5页面,按钮点击有延迟,连续点击卡顿.无反应. 异常原因: 这要追溯至 2007 年初.苹果公司在发布首款 iPhone 前夕,遇到一个问题:当时的网站都是为大屏幕设 ...

  4. TZOJ 2018 SPF(连通图割点和分成的连通块)

    描述 Consider the two networks shown below. Assuming that data moves around these networks only betwee ...

  5. 把leveldb嵌入到redis.实现真正的数据持久存储

    目前最新版RedisStorage 是基于 redis 2.6.2基础上,加上 leveldb存储引擎. 这个项目是源于 公司项目的passport 用户认证改造.公司一个项目运行了N年.积累了几千万 ...

  6. crm作业知识点集合[三]

    知识点1 我们要实现一个这样的功能,在学生表中,可以查看每个学生的报名的班级的所有的成绩,就是下图的效果 1.首先我们需要在学生表中自定义一列,这一列的内容就是一个a标签,指向另外一个页面,而我们在另 ...

  7. List<Map<String, String>>和Map<String, List<String>>遍历

    public void TestM() {     List<Map<String, String>> lm = new ArrayList<>();     Ma ...

  8. 13-算法训练 P0505

    算法训练 P0505   时间限制:1.0s   内存限制:256.0MB      一个整数n的阶乘可以写成n!,它表示从1到n这n个整数的乘积.阶乘的增长速度非常快,例如,13!就已经比较大了,已 ...

  9. python 使用内置方法读取注册表

    #coding:utf-8 import _winreg key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,r"键值") #获取该键的 ...

  10. 26.mysql日志

    26.mysql日志mysql日志包括:错误日志.二进制日志.查询日志.慢查询日志. 26.1 错误日志错误日志记录了mysqld启动到停止之间发生的任何严重错误的相关信息.mysql故障时应首先查看 ...