LinuxGod

LinuxGod.net
Linux大神网——精选每一篇高品质的技术干货
  1. 首页
  2. 开源快讯
  3. 正文

【Linux基础知识】Linux下的共享库和动态库

2023年1月7日 185点热度

可以通过运行ldd来看某个程序使用的共享函数库。例如ldd /bin/ls。

查看.so文件使用nm命令,如nm libXXX.so。(注意,nm对于静态的函数库和共享的函数库都起作用)

(6)关于覆盖:

如果想用自己的函数覆盖某个库中的一些函数,同时保留该库中其他的函数的话,可以在/etc/ld.so.preload中加入要替换的库(.o结尾的文件),这些preloading的库函数将有优先加载的权利。

(7)关于更新:

每次新增加动态加载的函数库、删除某个函数库或者修改某个函数库的路径时,都要重新运行ldconfig来更新缓存文件/etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表。

在Linux下,共享库的加载是由/lib/ld.so完成的,ld.so加载共享库时,会从ld.so.cache查找。

创建函数库示例

我们通常把一些公用函数制作成函数库,供其它程序使用。

函数库分为静态库和动态库两种。静态库在程序编译时会被链接到目标代码中,程序运行时将不再需要该静态库。动态库在程序编译时并不会被链接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。本节主要通过举例来说明如何在Linux中创建静态库和动态库,以及使用它们。

在创建函数库前deepin linux,我们先来准备举例用的源程序,并将函数库的源程序编译成.o文件。

step1:创建示例程序

程序1: hello.h

#ifndef HELLO_H
#define HELLO_H

void hello(const char *name);

#endif //HELLO_H

程序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;
}

hello.c(见程序2)是函数库的源程序,其中包含公用函数hello,该函数将在屏幕上输出"Hello XXX!"。hello.h(见程序1)为该函数库的头文件。main.c(见程序3)为测试库文件的主程序,在主程序中调用了公用函数hello。

step2:将hello.c编译成.o文件

无论静态库,还是动态库,都是由.o文件创建的。因此,我们必须将源程序hello.c通过gcc先编译成.o文件。

在命令行中键入以下命令得到hello.o文件:

[zhixa@ess ~test]$ gcc -c hello.c

cmake include头文件_linux include 找不到头文件_linux include 头文件 路径

我们运行ls命令看看是否生存了hello.o文件。

[zhixa@ess ~test]$ ls
hello.c hello.h hello.o main.c

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

下面我们先来看看如何创建静态库,以及使用它。

step3:由.o文件创建静态库

静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名linux include 找不到头文件,扩展名为.a。例如:我们将创建的静态库名为myhello,则静态库文件名就是libmyhello.a。在创建和使用静态库时,需要注意这点。

创建静态库用ar命令。在命令行中键入以下命令将创建静态库文件libmyhello.a。

[zhixa@ess ~test]$ ar -cr libmyhello.a hello.o

我们同样运行ls命令查看结果:

[zhixa@ess ~test]$ ls
hello.c hello.h hello.o libmyhello.a main.c

ls命令结果中有libmyhello.a。

step4:在程序中使用静态库

静态库制作完了,如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明静态库名,gcc将会从静态库中将公用函数连接到目标文件中。注意,gcc会在静态库名前加上前缀lib,然后追加扩展名.a得到的静态库文件名来查找静态库文件。

在程序3即main.c中,我们包含了静态库的头文件hello.h,然后在主程序main中直接调用公用函数hello。下面先生成目标程序hello,然后运行hello程序看看结果如何。

生成目标程序hello有以下三种方法:

生成方法一

[zhixa@ess ~test]$ gcc -o hello main.c -L. –lmyhello
或
[zhixa@ess ~test]$ gcc  main.c -L. –lmyhello -o hello

自定义的库时,main.c还可放在-L.和 –lmyhello之间,但是不能放在它俩之后,否则会提示myhello没定义,但是是系统的库时,如g++ -o main(-L/usr/lib) -lpthread main.cpp就不出错。

生成方法二

[zhixa@ess ~test]$ gcc main.c libmyhello.a -o hello或gcc  -o hello main.c libmyhello.a

生成方法三:

先生成main.o

linux include 头文件 路径_linux include 找不到头文件_cmake include头文件

[zhixa@ess ~test]$ gcc -c main.c 

再生成可执行文件

[zhixa@ess ~test]$ gcc -o hello main.o libmyhello.a
或
[zhixa@ess ~test]$ gcc main.o libmyhello.a -o hello ,

动态库链接时也可以这样做。

运行hello程序,结果如下:

[zhixa@ess ~test]$ ./hello
Hello everyone!

我们删除静态库文件,试试公用函数hello是否真的链接到目标文件 hello中了。

[zhixa@ess ~test]$ rm libmyhello.a
rm: remove regular file `libmyhello.a'? y
[zhixa@ess ~test]$ ./hello
Hello everyone!

程序照常运行,静态库中的公用函数已经链接到目标文件中了。

我们继续看看如何在Linux中创建动态库。我们还是从.o文件开始。

step5:由.o文件创建动态库文件

动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀lib,但其文件扩展名为.so。例如:我们将创建的动态库名为myhello,则动态库文件名就是libmyhello.so。

用gcc来创建动态库。在命令行中键入以下命令得到动态库文件libmyhello.so。

[zhixa@ess ~test]$ gcc -shared -fPIC -o libmyhello.so hello.o 

我们照样使用ls命令看看动态库文件是否生成:

[zhixa@ess ~test]$ ls
hello.c hello.h hello.o libmyhello.so main.c

step6:在程序中使用动态库

在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明动态库名进行编译。我们先运行gcc命令生成目标文件,再运行它看看结果。

在命令行中键入以下命令生成目标文件。

[zhixa@ess ~test]$ gcc -o hello main.c -L. -lmyhello
或
[zhixa@ess ~test]$ gcc main.c libmyhello.so -o hello

这里不会出错(没有libmyhello.so的话,会出错),但是接下来./hello 会提示出错,因为虽然连接时用的是当前目录的动态库,但是运行时linux include 找不到头文件,是到/usr/lib中找库文件的,将文件libmyhello.so复制到目录/usr/lib中就OK了。

在命令行中键入以下命令运行程序查看结果:

[zhixa@ess ~test]$ ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

哦!出错了。快看看错误提示,原来是找不到动态库文件libmyhello.so。程序在运行时,会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提示类似上述错误而终止程序运行。我们将文件libmyhello.so复制到目录/usr/lib中,再试试。

[zhixa@ess ~test]$ mv libmyhello.so /usr/lib
[zhixa@ess ~test]$ ./hello
Hello everyone!

成功了。这也进一步说明了动态库在程序运行时是需要的。

我们回过头看看,发现使用静态库和使用动态库编译成目标程序使用的gcc命令完全一样,那当静态库和动态库同名时,gcc命令会使用哪个库文件呢?抱着对问题必究到底的心情,来试试看。

先删除除.c和.h外的所有文件,恢复成我们刚刚编辑完举例程序状态。

[zhixa@ess ~test]$ rm -f hello hello.o /usr/lib/libmyhello.so
[zhixa@ess ~test]$ ls
hello.c hello.h main.c

再来创建静态库文件libmyhello.a和动态库文件libmyhello.so。

在生成动态库时,需要使用-fPIC,这样才能生成位置无关的代码,达到代码段和数据段共享的目的。

[zhixa@ess ~test]$ gcc -c -fPIC hello.c  
[zhixa@ess ~test]$ ar -cr libmyhello.a hello.o (或-cvr )
[zhixa@ess ~test]$ gcc -shared -fPIC -o libmyhello.so hello.o
[zhixa@ess ~test]$ ls
hello.c hello.h hello.o libmyhello.a libmyhello.so main.c

通过上述最后一条ls命令,可以发现静态库文件libmyhello.a和动态库文件libmyhello.so都已经生成,并都在当前目录中。然后,我们运行gcc命令来使用函数库myhello生成目标文件hello,并运行程序 hello。

[zhixa@ess ~test]$ gcc -o hello main.c -L. –lmyhello  (动态库和静态库同时存在时,优先使用动态库, 当然,直接gcc main.c libmyhello.a -o hello的话,就是指定为静态库了)
[zhixa@ess ~test]$ ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

从程序hello运行的结果中很容易知道,当静态库和动态库同名时,gcc命令将优先使用动态库,默认去链接/usr/lib和/lib等目录中的动态库,将文件libmyhello.so复制到目录/usr/lib中即可。

Note:编译参数解析

最主要的是GCC命令行的选项:

-shared

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

-fPIC

作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code)。那么在产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。

如果不加fPIC,则编译出来的代码在加载时需要根据加载到的位置进行重定位(因为它里面的代码并不是位置无关代码),如果被多个应用程序共同使用,那么它们必须每个程序维护一份so的代码副本了。(因为so被每个程序加载的位置都不同,显然这些重定位后的代码也不同,当然不能共享)。

不用此选项的话编译后的代码是位置相关的linux防火墙设置,所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。

-L.

表示要连接的库在当前目录中。

对于多个库的情况:在编译命令行中,将使用的静态库文件放在源文件后面就可以了。比如:gcc -L/usr/lib myprop.c libtest.a libX11.a libpthread.a -o myprop 其中-L/usr/lib指定库文件的查找路径。编译器默认在当前目录下先查找指定的库文件,如前面的“方法二 #gccmain.c libmyhello.a-o hello”)

-lmyhello

编译器查找动态连接库时有隐含的命名规则,即在给出的名字前面加上lib,后面加上.so或.a来确定库的名称libmyhello.so或libmyhello.a。

LD_LIBRARY_PATH

这个环境变量指示动态连接器可以装载动态库的路径。

当然如果有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(GNU linker)会去找GCC命令中的参数-L。

编译过程是分为四个阶段:预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编 (Assembly)和链接(link) 。

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

有关环境变量

1.PATH是可执行文件路径,是三个中我们最常接触到的,因为我们命令行中的每句能运行的命令,如ls、top、ps等,都是系统通过PATH找到了这个命令执行文件的所在位置,再run这个命令(可执行文件)。

# vim ~/.bash_profile

PATH=$PATH:~/bin

2.LIBRARY_PATH环境变量:指定程序静态链接库文件搜索路径

export  LIBRARY_PATH=LIBRARY_PATH:XXXX

3.LD_LIBRARY_PATH环境变量:指定程序动态链接库文件搜索路径

export  LD_LIBRARY_PATH=LD_LIBRARY_PATH:XXXX

另

从上述可知,如何找到生成的动态库有3种方式:

(1)把库拷贝到/usr/lib和/lib目录下。

(2)在LD_LIBRARY_PATH环境变量中加上库所在路径。

例如动态库libmyhello.so在/home/example/lib目录下:

export LD_LIBRARY_PATH=LD_LIBRARY_PATH:/home/example/lib

(3) 修改/etc/ld.so.conf文件,把库所在的路径加到文件末尾(直接写在文件末尾,不要在路径前加include),并执行ldconfig刷新(ldconfig 命令的用途,主要是在默认搜寻目录(/lib和/usr/lib)以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如前介绍,lib*.so*),进而创建出动态装入程序(ld.so)所需的连接和缓存文件.缓存文件默认为/etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表.)。这样,加入的目录下的所有库文件都可见。

附:

像下面这样指定路径去链接系统的静态库,会报错说要连接的库找不到:

g++ -o main main.cpp -L/usr/lib libpthread.a 

必须下面这样才正确:

g++ -o main main.cpp -L/usr/lib -lpthread 。

自定义的库拷贝到/usr/lib 下时,

g++ -o main main.cpp -L/usr/lib libpthread.a libthread.a libclass.a

会出错,但是下面这样就正确了:

g++ -o main main.cpp -L/usr/lib -lpthread -lthread -lclass

欢迎加入读友交流群

因为群聊已满200人,需扫描下方二维码添加小编微信拉你入群

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: lib文件 linux系统 linux编译 动态库 编译程序
最后更新:2023年1月7日

Linux大神网

每日更新,欢迎收藏♥ 不积跬步无以至千里,加油,共勉。

点赞
< 上一篇
下一篇 >

Linux大神网

每日更新,欢迎收藏♥
不积跬步无以至千里,加油,共勉。

最新 热点 随机
最新 热点 随机
如何在Linux上安装视频播放器installvlc 晚上暴露于蓝光,睡眠质量大打折扣 如何安装便携式WiFi驱动程序?360wifi驱动的教程 CentOS云服务器搭建网站和CentOS搭建DNS解析服务 如何在linux上创建一个用户,减少不必要的沟通成本 如何在Linux系统中查看CPU信息使用lscpu命令行 linux服务器搭建ftp的6下安装vsftpd步骤及步骤 贵州工业职业技术学院求职意向期望工作地--诚聘英才 实验1Linux安装实验掌握虚拟机的使用 Linux系统tar命令的使用方法及使用命令教程 linux 开源nas系统 杰和科技NAS服务器媒体见面会在京召开 Android与Linux开发大不同 Linux系统软件安装包:自己动手,安装不用愁 车市新战局:汽车操作系统会复制智能手机的历史吗? Linux文件系统种类 如何卸载用源码包安装的软件?在线视频教程推荐 「职位」ASP.、PHP、Linux服务器集群开发 Torvalds:Linux内核开发的创新前景充满了热情 Linux文件系统的结构从终端窗口探索Linux目录树结构 卸载软件命令Linux.You
嵌入式Linux操作系统学习规划+LINUX路线,主攻江苏电信天翼校园客户端故障指引及解决办法(101)英特尔GMAGMA950显卡驱动程序/WIN8/8.1电信校园网宽带用USB数据线共享给电脑无线上网国防科大开源操作系统:它只是一个吉祥的象征10个常用Linux文本查看命令及其详细说明和使用示例Linux嵌入式系统内核裁剪与定制方法的介绍情况淘宝教育热卖C语言编程开发C++程序设计零基础入门课程从CPU、内存、硬盘、显卡等这些方面安装Linux系统的最低配置Linux通过chkconfig设置开机启动服务创建的几种常见方式(技术分析)Linux多线程的使用与操作系统的区别通常rar命令由一个主命令加若干选项(可选)构成RedHatLinux中自动运行程序中的应用linux 读写文件 关于Linux内核的神秘面纱,你知道几个?使用wget实用程序的有用命令行工具的使用怎么设置linux开机项自启动?方式是怎样的?嵌入式Linux应用层与驱动层要想学习关于Linux内核的交叉编译步骤和方法:步骤、方法STM32嵌入式linux开发流程及应用程序分析-STMlinux下有哪些文件在介绍lsof命令实用用法介绍?
Linux内核调度器进程的线程状况分析及解决办法 软件介绍deepinlinux最新正版是深度推出的开源操作系统 Linux预置七种运行级别在/inittab目录下的内核文件 PC104板上用Metrowerks的PCS系统实现实时嵌入式Linux操作系统的过程 教程:循环语句删除的命令和命令语句 本周特推1.1语法校对:languagetoolNew高性能的消息 Linux/unixshell-scriptting高级编程课件/linux+nmap实现系统识别 linux扩容知识储备如何对linux系统:history查看linux操作 第一部分ARMLinux内核——分析内核前需要做的准备 什么是Windows系统MicrosoftWindows,linux系统和windows系统的区别 更新原压缩包中的文件这五个是独立的命令,压缩解压都要用到 硬盘说明Linux硬盘分IDE硬盘和SCSI硬盘,前四个主分区 Linux中如何查看SSH的版本? 嵌入式Linux内核移植及根文件系统实验的实验步骤 服务器上安装linux系统的具体操作过程-如何选择系统? 从Windows+Linux(Ubuntu)双系统安装教程说在前面 快速云小编的几种软件是需要安装的。。(一) linux 读写文件 关于Linux内核的神秘面纱,你知道几个? Linux系统如何设置开机默认启动应用程序?教程分享结束 变量解密:提升理解效果!
标签聚合
内核 应用 linux服务器 操作 虚拟机 linux系统 软件 命令 文件目录 文件
书籍
课程
技术群
技术干货大合集↓
  • 2023年9月 / 83篇
  • 2023年8月 / 93篇
  • 2023年7月 / 94篇
  • 2023年6月 / 90篇
  • 2023年5月 / 93篇
  • 2023年4月 / 90篇
  • 2023年3月 / 129篇
  • 2023年2月 / 84篇
  • 2023年1月 / 161篇
  • 2022年12月 / 187篇
  • 2022年11月 / 76篇
友情链接:

Linux书籍 | Linux命令 | Linux系统 | RHCE红帽认证 | Linux软件 | Linux教程 | CentOS系统 | Linux内核 | Linux服务器 | Linux大神 | IT资源

COPYRIGHT © 2023 linuxgod.net ALL RIGHTS RESERVED.