Linux下的动态库和静态库详解

Linux下的动态库和静态库详解动态库和静态库文章目录动态库和静态库静态库与动态库的概念理解动静态库如何打包动静态库与如何使用动静态库如何制作打包动态库为什么我们要使用别人 一般是顶尖的工程师写的 的代码 为了开发效率和鲁棒性 健壮性 如何使用别人的功能 1 库 2 开源代码 3 基本的网络功能调用 各自网络接口 语音识别 库一般分为动态库和静态库 动态库一般的命名为 libc so 静态库一般的命名为 libc a 去掉前缀 lib 去掉 之后的内容 剩下的就是库的名字 这里就是 c 库 生成可执行程序的方式有两种 动态链接和

动态库和静态库

为什么我们要使用别人(一般是顶尖的工程师写的)的代码?

为了开发效率和鲁棒性(健壮性)

如何使用别人的功能?

1、库 2、开源代码 3、基本的网络功能调用(各自网络接口,语音识别)

库一般分为动态库和静态库,动态库一般的命名为libc.so,静态库一般的命名为libc.a,去掉前缀lib,去掉.之后的内容,剩下的就是库的名字,这里就是c库,生成可执行程序的方式有两种:动态链接和静态链接

#include 
      int main() { 
     printf("hello world!\n"); return 0; } 

ldd 可执行程序名字:查看可执行程序的动态链接关系

image-20220228135628014

在linux当中,默认情况下形成的可执行程序是动态链接的,可以看到这里是.so为后缀的

下面我们举个例子来解释一下动态链接和静态链接:

比如你在宿舍写作业,写到某个题突然不会了,学校旁边有个网吧,于是你就去网吧去查找资料解决问题,解决完了然后回到宿舍继续完成作用,还有一种方法就是你告诉你爸让给你买台电脑,你在宿舍有台电脑,于是你就不需要再去网吧去使用电脑去查阅资料,直接在自己买的电脑上面查找资料即可,前面说的那种方法类似于动态链接,后面的那种方法叫做静态链接

当调用库频率增加时或者在上面的例子中说去网吧查阅资料频繁时,就变得很麻烦,就和上面的例子一样我们自己买个电脑,这样就省事了不少。相对应的把库中的我的可执行程序中所需要使用的二进制代码,拷贝进我的可执行程序当中,这就叫做静态链接。

如果想使用静态链接,那就在gcc后面+static选项:一般,为了更好的支持开发,第三方库或者语言库,必须提供两个库,一个叫做静态库,一个叫动态库,方便程序员根据需要进行bin的生成

image-20220228142033129

动态链接的特点:体积小,节省资源(磁盘,内存),但是一旦库丢失,bin不可执行

静态链接的特点:体积大,浪费资源(磁盘,内存),不依赖库,库丢失不影响可执行程序

静态库与动态库的概念

  • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
  • 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

理解动静态库

如何打包动静态库与如何使用动静态库

不想将源代码暴露给别人,就需要打包动静态库,下面我们来看如何打包动静态库:

我们首先写好自己想要打包的程序代码:

add.c

#include"add.h" int myadd(int x,int y) { 
     return x+y; } 

add.h

#pragma once #include 
      int myadd(int x,int y); 

sub.c

#include"sub.h" int mysub(int x,int y) { 
     return x-y; } 

sub.h

#pragma once #include 
      int mysub(int x,int y); 

我们想让别人能使用我的库,前提是别人需要首先知道你的库能给我提供什么方法,通过头文件体现,然后将源文件编译生成目标文件:

gcc -c add.c 

生成.o文件,这个.o文件可以被别人链接

gcc -c sub.c 

然后使用ar -rc命令生成静态库:

ar -rc libmymath.a add.o sub.o 

我们只需要将头文件和这个刚生成的库文件给别人就好了,别人就可以用该库,形成自己的可执行程序

我们将头文件和这个刚生成的库文件都放进lib目录下,自己写个程序使用它:

#include 
      #include"Add.h" #include"sub.h" int main() { 
     printf("%d\n",myadd(10,20)); printf("%d\n",mysub(10,20)); return 0; } 

这里会报错,找不到:image-20220228144018395

因为去当前路径或者默认路径(/usr/include)下找头文件,所以需要告诉gcc在指定路径下去找一下头文件:

-I选项:

gcc test.c -o mytest -I ./lib 

我们发现还会有问题:

image-20220228144100170

我们需要告诉gcc除了默认路径(/lib64/libc)以及当前路径之外,在指定的路径下也找一下库文件:

-L(大写)选项:

gcc test.c -o mytest -I ./lib -L ./lib -lmymath 

-l(小写)库名称:具体你要链接哪一个库

image-20220228144231837

可以看到成功执行了

为什么C语言在编译的时候,从来没有明显的使用过-I -L -l等选项呢?

  1. 库文件和头文件在默认路径下gcc能找到
  2. gcc编译C代码,默认就应该链接libc

如果我们也不想使用这些选项呢?

头文件库文件分别拷贝到默认路径下,这个过程叫做库的安装,对于第三方库例如我们自己写的,一般也要带上-lname

如何制作打包动态库

生成动态库:

Makefile的编写

libmymath.so add.o sub.o gcc -shared -o $@ $^ add.o:add.c gcc -fPIC -c $^ sub.o:sub.c gcc -fPIC -c $^ 

gcc选项 -fPIC:产生与位置无关码

在进程地址空间中有共享区,一般动态库的代码是映射在共享区的,我们将库代码加载到内存,通过页表映射到进程地址空间的共享区,当其他程序想要相同的库代码时,可以通过页表映射到内存中相同库代码的部分,这样就大大减少了空间的使用情况,库有可能加载到物理内存的任意位置,也可能被映射到进程地址空间中共享区的任意区域,所以必须保证库当中的代码怎么执行都不会错,为了保证这个库当中的代码地址不随着加载到内存的位置发生变化而变化,需要告诉gcc,我们进行fPIC选项产生与位置无关码进行编译

我们通过静态库的那套方式进行编译链接:

gcc test.c -o mytest -I ./dy_lib -L ./dy_lib -lmymath 

我们运行可执行程序,发现无法正常运行:

image-20220228153922235

此时和编译器gcc还有关系吗?毫无关系,是运行时出现的问题,属于运行问题,也要能够让系统帮我们找到运行时需要使用的动态库。

为什么之前动态链接的其他程序可以直接运行呢?因为库可以找到

环境变量LD_LIBRARY_PATH,需要将库的路径导入到环境变量LD_LIBRARY_PATH中:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/whb/101/lesson18/dy_lib/test/lib/ 

image-20220228154358535

可以看到此时就可以正常执行了

image-20220228154453152


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/228731.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午6:20
下一篇 2026年3月16日 下午6:20


相关推荐

  • JSP提交表单

    JSP提交表单nbsp 设计表单页面 它是静态页面 使用 HTML 编写 而且使用了 JavaScript 脚本语言来验证填写表单数据 表单页面为 form htm 代码如下 htmlheadtitl 教师信息登记表 titleheadscr nbsp language JavaScript function nbsp checkit nbsp nbsp nbsp nbsp var nbsp theForm nbsp nbsp nbsp nbsp theForm document

    2026年3月18日
    2
  • 【Hibernate】uniqueResult方法「建议收藏」

    【Hibernate】uniqueResult方法「建议收藏」数据库中根据你的查询条件只会返回唯一结果,就可以用uniqueResult这个方法!否则就用list();其返回类型为Object uniqueResult()方法通常是在业务方法查询语句中用到的,比如(UsersRoles)getSession().createQuery(“selecturfromUsersRolesurwhereur.role.id=?andur.

    2025年10月22日
    4
  • Mysql中用SQL增加、删除字段,修改字段名、字段类型、注释,调整字段顺序总结

    Mysql中用SQL增加、删除字段,修改字段名、字段类型、注释,调整字段顺序总结1.增加一个字段 代码如下 复制代码 //增加一个字段,默认为空altertableuseraddCOLUMNnew1VARCHAR(20)DEFAULTNULL; //增加一个字段,默认不能为空altertableuseraddCOLUMNnew2VARCHAR(20)NOTNULL; 2….

    2022年6月1日
    56
  • 谈谈构建单页布局网站的创意技术——附优秀案例

    谈谈构建单页布局网站的创意技术——附优秀案例在过去的几年中 不断涌现出新的自定义网站设计技术 一个非常流行的想法是把所有的主要内容放在一个页面 使用动态滚动动画来定位内容 这种单页布局在只需要显示相关信息的一小部分的目标网页和移动应用程序制作网站经常使用 在这篇文章中 我想向大家介绍单页网站中一些有趣的技术 希望这些想法能够给正在寻找这方面内容的年轻设计师提供灵感 视差滑动效果 可能是因为实现这种效果比较复杂 视差

    2026年3月17日
    1
  • access h3c交换机光口_h3c光纤交换机_H3C交换机光口设置

    access h3c交换机光口_h3c光纤交换机_H3C交换机光口设置H3C 交换机光口设置有些路由器和交换机的端口是光电复用的 即 combo 口 默认情况下 开的是电口 我们需要打光口的开关打开 interg0 0 1comboenable 交换的操作更简单了 只需要把那个电口用关掉 则对应的光口就起来了 interg1 0 22shutdown 假始 22 口对应的光口是 25 那么此时 25 号光口就可以用了 产品系列核心路由交换机 H3CS9500E

    2026年3月26日
    1
  • USB大容量存储设备_usb网卡变成大容量存储设备

    USB大容量存储设备_usb网卡变成大容量存储设备[USB开发]USB大容量存储设备的开发cxl84发表于2008-3-1723:24:00USB是目前在打印机,数字存储设备,输入/输出设备,数码像机,MP3播放器等其他周边设备中得到广泛应用的连接方式。USB设备具有使用方便,速度快,连接灵活,即插即用,总线供电等优点。基于USB接口的大容量存储设备(USBMassStorage)应运而生,目前市场上的这类设备主要有:USB移

    2026年4月17日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号