Linux——动静态库的制作及使用与动态库原理

目录

一、静态库

1.静态库的制作 

2.静态库的使用 

加载静态库方法一:安装头文件与库文件

加载静态库方法二:指定文件目录

二、动态库

1.动态库的制作

2.动态库的使用  

方法一:安装到系统中

方法二:软链接

方法三:配置环境变量

方法四:更改系统关于动态库的配置文件

三、安装ncurses库

四、 动态库加载原理


一、静态库

静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。

1.静态库的制作 

站在库的制作者的角度,如果我想写一个库文件,肯定是不能写main函数的,并且要将所有的方法打包好给用户。

比如我们现在创建好了四个.h头文件进行声明,还有四个.c文件进行定义(这四个文件只有加减乘除四个函数)。

我们写一个主函数include一下这四个头文件

gcc编译一下然后执行,没啥问题

但是一般情况下,对于多文件的编译,我们不会直接gcc,因为这样每一次编译,所有的文件都要进行预处理、编译、汇编、链接。一般建议先把源文件编译成 .o 文件。最后将.o文件进行链接形成可执行文件

我们写一个makefile来帮助编译。其中 %. 是省略写法,只要后缀相同,前面的会自动遍历,$<也是省略写法,会将依赖文件一个个展开。

这样确实可以,但依然不够优雅,我想要讲所有的 .o 文件打包成一个文件

使用以下命令将.o文件打包成静态库

ar -rc libmymath.a Add.o Div.o Mul.o Sub.o

那么现在有了这个静态库文件,我们应该如何使用呢? 

我们模拟一下真实情况,目前只有.h文件(因为要include,因此.h是必须的)、静态库文件、还有我们写的主函数。

使用以下命令即可链接静态库编译成可执行文件

gcc main.c -lmymath -L.

 gcc默认动态链接,但个别库只提供了静态库,gcc只能局部性的用我们指定的静态库进行静态链接,其他库正常动态链接。如果-static,就必须要全部使用静态库。

但是这样还是有点戳,说好的打包,你是打包了,但一看这么全,把库文件和.h文件都放在一起,很难看的,我想将.h放在一个文件夹中,库文件放在一个文件夹中。

使用Makefile来帮我们部署。

现在形成了mymath_lib文件夹,现在就打包好了 

以后我们要发送给其他用户,就将文件夹压缩发给其他用户使用即可。 

2.静态库的使用 

其他用户收到了这个包,先创建c文件

加载静态库方法一:安装头文件与库文件

系统搜索头文件默认是在/usr/include文件夹中,那么我们将新下载的库的头文件,拷贝到该文件夹中,这个行为就叫做安装。

库文件默认在/lib64文件夹中,依然是将库文件拷贝到该文件夹中,后续直接gcc 编译程序就可以了。

加载静态库方法二:指定文件目录

文件中include指定相关目录即可在编译时找到库的头文件

再使用如下代码即可编译可执行文件,-I(大写i)后接头文件目录,代表需要去这里找头文件,-l(小写L)后接库名,-L后接库目录,代表这里找库文件。

gcc main.c -I mymath_lib/include -lmymath -L mymath_lib/lib

二、动态库

1.动态库的制作

依然是将.h文件和.c文件先放在一起,并写一个Makefile帮我们部署。

这里形成动态库的命令如下,是使用的gcc(gcc内置了动态链接,证明动态库更重要一点)shared: 表示生成共享库格式

gcc -shared -o libmymath.so Add.o Div.o Mul.o Sub.o

后面的 fPIC 为位置无关码,后续我们会讲到。

现在我们make编译后,再make output进行打包,就有一个mymath_lib这个打包好的动态库文件夹

里面存放的文件如下 

2.动态库的使用  

用户收到别人写的动态库,再写上自己的主程序,就可以开始编译运行了

使用如下代码编译,编译上跟静态库没有区别 

gcc main.c -I mymath_lib/include/ -lmymath -L mymath_lib/lib/

 但是我们执行的时候报错了,他找不到libmymaty.so文件

这是因为可执行程序和静态库被打包在一起,编译成可执行程序,便和静态库没关系了,因此运行时直接运行就可以了。但动态库和可执行程序是分离的,并不是同一文件,在运行时,可执行程序和动态库都要加载到内存中,虽然我们之前告诉编译器,库文件在哪里,但是执行程序跟编译是两码事,因此还是找不到。

方法一:安装到系统中

拷贝头文件

拷贝库

之后执行a.out就没问题了 

并且,由于我们库文件已经安装了,后面编译也可以简单一点,告诉编译器我们使用了哪个库文件就好了,不需要在 -I(include路径) -L(lib路径)

gcc main.c -lmymath

同时可以使用 ldd a.out 查看可执行文件依赖了那些动态库。

方法二:软链接

使用如下代码进行软链接

ln -s mymath_lib/lib/libmymath.so libmymath.so

这句代码就是将文件夹中的libmymath.so软链接到当前目录下,因此当前路径就多了一个libmymath.so

执行可执行程序时,虽然他不会进入当前目录的文件夹里面找,但是会在当前目录下查找是否有动态库,因此我们软链接到当前目录下,就可以执行了。 

当然,我们也可以将mymath库的软链接安装到系统中,也是可以的。

ln -s ~/centos_test/109/240314_lib/dytest/test/mymath_lib/lib/libmymath.so /lib64/libmymath.so

方法三:配置环境变量

Linux下有一个环境变量名为  LD_LIBRARY_PATH  。系统还会去该环境变量中搜索动静态库。因此我们将目录添加到该环境变量中,就可以了,代码如下

LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/centos_test/109/240314_lib/dytest/test/mymath_lib/lib/

方法四:更改系统关于动态库的配置文件

系统中有一个目录  /etc/ld.so.conf.d/   ,ld(加载),so(动态库后缀),conf(配置文件),d(目录)

 这些系统文件管理者动态库的加载,配置文件中只需要写一个路径就可以了

再sudo ldconfig 进行刷新,就能找到了 


如果别人给我的库文件,同时包含静态库和动态库,那么我们编译时默认使用的是动态库,要使用静态库依然需要加上-static。

三、安装ncurses库

我们学习了动静态库的制作和使用,现在我们要来实战一下,安装ncurses库。

sudo yum install ncurses-devel.x86_64

 有了之前的理解,我们知道安装库的本质就是将头文件拷贝到/usr/include 文件夹下,将库文件拷贝到 /lib64/ 文件夹下。

下载好我们创建ncurses.c文件,并写一个贪吃蛇代码测试一下。

#include <ncurses.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>#define DELAY 100000int main() {int x, y, maxX, maxY; //蛇头的位置和终端窗口的大小int direction = KEY_RIGHT; //方向int snakeLength = 5; //蛇的长度int snakeX[100], snakeY[100]; //蛇身的位置int foodX, foodY; //食物的位置int score = 0; //得分int gameOver = 0; //游戏结束标志// 初始化ncurses库initscr();noecho();curs_set(0);keypad(stdscr, TRUE);timeout(0);// 获取终端窗口的大小getmaxyx(stdscr, maxY, maxX);// 初始化蛇的初始位置和长度x = maxX / 2;y = maxY / 2;for (int i = 0; i < snakeLength; i++) {snakeX[i] = x - i;snakeY[i] = y;}// 生成食物的初始位置srand(time(NULL));foodX = rand() % maxX;foodY = rand() % maxY;// 游戏循环while (!gameOver) {clear();// 绘制蛇for (int i = 0; i < snakeLength; i++) {mvprintw(snakeY[i], snakeX[i], "O");}// 绘制食物mvprintw(foodY, foodX, "*");// 显示分数mvprintw(0, 0, "Score: %d", score);// 移动蛇的位置int nextX = snakeX[0];int nextY = snakeY[0];switch (direction) {case KEY_UP:nextY--;break;case KEY_DOWN:nextY++;break;case KEY_LEFT:nextX--;break;case KEY_RIGHT:nextX++;break;}// 检查是否吃到食物if (nextX == foodX && nextY == foodY) {score++;snakeLength++;foodX = rand() % maxX;foodY = rand() % maxY;} // 移动蛇的身体for (int i = snakeLength - 1; i > 0; i--) { //后一节移动到前一节的位置snakeX[i] = snakeX[i - 1];snakeY[i] = snakeY[i - 1];}// 更新蛇头位置snakeX[0] = nextX;snakeY[0] = nextY;// 检查游戏结束条件//检查是否越界if (nextX < 0 || nextX >= maxX || nextY < 0 || nextY >= maxY) {gameOver = 1;}//检查是否撞到自己的身体for (int i = 1; i < snakeLength; i++) {if (snakeX[i] == nextX && snakeY[i] == nextY) {gameOver = 1;}}// 刷新屏幕refresh();// 延迟一段时间usleep(DELAY);// 获取用户输入int key = getch();switch (key) {case KEY_UP:case KEY_DOWN:case KEY_LEFT:case KEY_RIGHT:direction = key;break;case 'q':gameOver = 1;break;}}// 清理并退出ncurses库endwin();printf("Game Over! Your score: %d\n", score);return 0;
}

 使用c99标准编译成test可执行文件,注意一定要加 -lncurses 代表你使用了这个库

gcc nucurses.c -o test -lncurses -std=c99

 ./test 执行一下,代码就跑起来了

妈妈再也不用担心别人给我的静态库或者动态库不会使用啦。 

四、 动态库加载原理

之前我们动态库编译的时候  代码为  gcc -fPIC -c XXX.c

其中fPIC为  与位置无关码 。

  1. 首先我们需要清楚需要动态链接的可执行程序,不光会将他自己的代码加载到内存,还需要将链接的库也加载到内存,这样才可以找得到。
  2. 在程序还没有被加载时,程序内部也是有地址的,C语言的变量名和函数名,在编译为2进制后,就没有了名字的概念,取而代之的是地址。我们代码中gcc -c 形成的.o文件就是二进3.制文件,他只有地址的说法,没有变量的说法。
  3. 那么我们在编译的时候,就需要对代码进行编址,这仍然遵循虚拟地址空间那一套。他不仅仅是操作系统的概念,在编译器编译的时候也要按照这样的规则编译可执行程序。这样在加载的时候,就很好进行数据的对应。
  4. 编址的时候,可以进行绝对编址和相对编址,绝对编址就是实实在在的是什么地址就是什么地址,每一次加载的地址都可能不一样,相对编址就是某一函数,相对于程序入口地址是多少,那么我们可以保证在代码不变的情况下,使用相对编址,他里面的函数或变量的相对地址也不会发生改变。这就叫与位置无关码

我们知道静态库在链接时,会链接上我的代码,他们打包形成了一个可执行程序,因此运行时数据都在可执行程序中,不需要去外部找内容,所以我们执行静态库链接的程序,直接运行就好了。

而动态库链接运行时,我们的代码需要加载到内存,我们代码使用到的库也需要加载到内存,这样才可以找到相关内容,成功运行。动态库被加载之后,映射到了我写的程序task_struct中的指针所指向的虚拟地址空间中的共享区部分。

动态库可以在共享区的任何位置。那我们的代码在运行时需要去找到动态库文件,那就可以直接去共享区查找,再通过页表映射,找到库文件所在的物理内存。当其他的可执行程序,也需要使用到该动态库的时候,也是在他的进程地址空间中的共享区中,用他的页表映射,找到物理内存中的库。我们的意思是动态库在物理内存中只有一份,而在不同的进程的虚拟内存空间中,可以有N多份,只要建立好页表映射就可以了!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/542084.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Light Random Sprays Retinex 传统的图像增强算法LRSR

文章目录 前言1、Light Random Sprays Retinex 概况2、Light Random Sprays Retinex 具体实现2.1、噪声去除2.2、亮度调整2.3、插值技术 3、Light Random Sprays Retinex源码4、Light Random Sprays Retinex效果及结论 前言 Light Random Sprays Retinex, 即“光随机喷雾Retine…

禁止ie自动跳转edge

因为微软对ie已经彻底停止维护了&#xff0c;对于没有升级系统的用户来说&#xff0c;会自动更新edge然后将ie给禁止使用。下面方法有效的解决windows10下&#xff0c;禁止ie自动跳转edge。 方法一&#xff1a;对于2023年10月份前的更新可用 打开控制面板&#xff0c;点击网络…

全面解析 Axios 请求库的基本使用方法

Axios 是一个流行的基于 Promise 的 HTTP 请求库&#xff0c;用于在浏览器和 Node.js 中进行 HTTP 请求。它提供了简单易用的 API&#xff0c;可以发送各种类型的请求&#xff08;如 GET、POST、PUT、DELETE等&#xff09;&#xff0c;并处理响应数据&#xff0c;Axios 在前端工…

开启Safari手势支持

在使用Safari 的时候&#xff0c;大家有没有觉得不支持手势使用起来不是很方便&#xff0c; 触摸板只支持少量简单的手势&#xff0c;如缩放&#xff0c;滚动等。如果使用鼠标的用户&#xff0c;则完全无法使用手势。经过折腾研究&#xff0c;使用CirMenu应用可以完美解决这个要…

【矩阵】54. 螺旋矩阵【中等】

螺旋矩阵 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5] 解题思路 1、模拟顺时针螺旋顺序遍历矩阵…

基于openCV实现的单目相机行人和减速带检测

概述 在计算机视觉项目中&#xff0c;相机标定是一项至关重要的任务&#xff0c;因为它可以校正相机内部参数&#xff0c;消除因镜头畸变等因素导致的图像失真&#xff0c;从而提高后续图像处理和分析的精度。在这个项目中&#xff0c;相机标定的核心功能集成在名为calibratio…

音频占用磁盘空间太多 需要把mp3音频转aac音频缩小占用空间 应该怎么操作?

一&#xff1a;什么是aac格式&#xff1f; aac是一种音频压缩格式&#xff0c;它是MPEG-2标准下的一种音频压缩方式&#xff0c;也可以作为HE-AAC&#xff0c;AAC或AAC-LC格式使用&#xff0c;是音频压缩领域中的一种重要格式。与MP3的比较&#xff0c;aac在保证音质的同时可以…

uniapp uview 头像裁剪组件的问题

当切换页面频繁进出头像裁剪组件u-avatar-cropper.vue 获取同一个设备信息时会出现两种不同的高度具体如下 导致 头像裁剪页面高度出现问题&#xff0c;下方按钮被canvas组件遮盖了 解决方法 在进入这个页面前的一个页面做如下代码操作 直接将设备信息提前获取&#xff0c;保…

ZooKeeper命令和监控详解

ZooKeeper监控命令详解 在分布式系统中&#xff0c;ZooKeeper作为一个非常重要的协调服务&#xff0c;它的健康状态直接影响到整个系统的可靠性和稳定性。因此&#xff0c;对ZooKeeper进行有效监控是非常必要的。本文将详细介绍ZooKeeper提供的命令行工具zkCli.sh&#xff0c;…

树莓派与电脑视频实时传输实现

编程环境 1、 树莓派 4B 2、 windows 编程语言 python 应用 tkinter scoket opencv 效果 视频同传 服务端视频初始化 服务端视频读取 windows 客户端接收视频流&#xff0c;队列存储 解析视频&#xff0c;存入队列 ui页面数据刷新 下载链接&#xff1a;https://…

移远通信亮相AWE 2024,以科技力量推动智能家居产业加速发展

科技的飞速发展&#xff0c;为我们的生活带来了诸多便利&#xff0c;从传统的家电产品到智能化的家居设备&#xff0c;我们的居家生活正朝着更智能、更便捷的方向变革。 3月14日&#xff0c;中国家电及消费电子博览会&#xff08;Appliance&electronics World Expo&#xf…

使用 Boot Camp 助理查明您的 Mac 需不需要 Windows 安装介质

使用 Boot Camp 助理查明您的 Mac 需不需要 Windows 安装介质 当前的 Mac 机型无需介质即可安装 Windows&#xff0c;也就是说&#xff0c;您不需要用到外置驱动器。较早的 Mac 机型需要用到 USB 驱动器或光盘驱动器。使用 Boot Camp 助理可查明您需要用到什么。 Boot Camp 助…