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

news2024/11/16 20:45:23

目录

一、静态库

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 100000

int 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.coloradmin.cn/o/1526638.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c语言文件操作(中)

目录 1. 文件的顺序读写1.1 顺序读写函数1.2 顺序读写函数的原型和介绍 结语 1. 文件的顺序读写 1.1 顺序读写函数 函数名功能适用于fgetc字符输入函数所有输出流fputc字符输出函数所有输出流fgets文本行输入函数所有输出流fputs文本行输出函数所有输出流fscanf格式化输入函数…

刷题DAY24 | LeetCode 77-组合

1 回溯法理论基础 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。回溯是递归的副产品&#xff0c;只要有递归就会有回溯。 所以以下讲解中&#xff0c;回溯函数也就是递归函数&#xff0c;指的都是一个函数。 1.1 回溯法的效率 回溯法的性能如何呢&#xff0…

完整指南:如何使用 Stable Diffusion API

Stable Diffusion 是一个先进的深度学习模型&#xff0c;用于创造和修改图像。这个模型能够基于文本描述来生成图像&#xff0c;让机器理解和实现用户的创意。使用这项技术的关键在于掌握其 API&#xff0c;通过编程来操控图像生成的过程。 在探索 Stable Diffusion API 的世界…

HarmonyOS NEXT应用开发之Web获取相机拍照图片案例

介绍 本示例介绍如何在HTML页面中拉起原生相机进行拍照&#xff0c;并获取返回的图片。 效果预览图 使用说明 点击HTML页面中的选择文件按钮&#xff0c;拉起原生相机进行拍照。完成拍照后&#xff0c;将图片在HTML的img标签中显示。 实现思路 添加Web组件&#xff0c;设置…

Vue.js+SpringBoot开发食品生产管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 加工厂管理模块2.2 客户管理模块2.3 食品管理模块2.4 生产销售订单管理模块2.5 系统管理模块2.6 其他管理模块 三、系统展示四、核心代码4.1 查询食品4.2 查询加工厂4.3 新增生产订单4.4 新增销售订单4.5 查询客户 五、…

生成式人工智能服务安全基本要求实务解析

本文尝试明晰《基本要求》的出台背景与实践定位&#xff0c;梳理《基本要求》所涉的各类安全要求&#xff0c;以便为相关企业遵循执行《基本要求》提供抓手。 引言 自2022年初以来&#xff0c;我国陆续发布算法推荐、深度合成与生成式人工智能服务相关的规范文件&#xff0c;…

阿里云服务器ECS经济型e实例性能如何?

阿里云服务器ECS推出经济型e系列&#xff0c;经济型e实例是阿里云面向个人开发者、学生、小微企业&#xff0c;在中小型网站建设、开发测试、轻量级应用等场景推出的全新入门级云服务器&#xff0c;CPU采用Intel Xeon Platinum架构处理器&#xff0c;支持1:1、1:2、1:4多种处理…

JS第一阶段1

文章目录 1. js组成2. JS三种书写位置JS输出语句 3. 变量4. 数据类型Number字符串型 String布尔型booleanUnddefined和Null 5. 获取变量的数据类型获取检测变量的数据类型 6. 数据转换类型转换为字符串转换为数字型&#xff08;重点&#xff09;转换为布尔型 7.运算符算数运算符…

找不到msvcp110.dll怎么办,msvcp110.dll丢失的5种修复方法

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcp110.dll丢失”。由于msvcp110.dll是Microsoft Visual C Redistributable Package的重要组成部分&#xff0c;它的缺失会导致依赖于该组件的软件无法正常启动或运行&#xff0c;比如某…

从初学者到专家:Java反射的完整指南

一.反射的概念及定义 Java 的反射&#xff08; reflection &#xff09;机制是在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b;对于任意一个对象&#xff0c;都能够调用它的任意方法和属性&#xff0c;既然能拿到那么&#x…

Jenkins + Docker + ASP.NET Core自动化部署

本来没想着要写这篇博客&#xff0c;但是在实操过程中&#xff0c;一个是被网络问题搞炸了心态&#xff08;真心感觉网络能把人搞疯&#xff0c;别人下个包、下个镜像几秒钟搞定&#xff0c;我看着我的几KB小水管真是有苦说不出&#xff09;&#xff0c;另一个就是这里面坑还是…

C语言---指针的两个运算符:点和箭头

目录 点&#xff08;.&#xff09;运算符箭头&#xff08;->&#xff09;运算符需要注意实际例子 C语言中的指针是一种特殊的变量&#xff0c;它存储了一个内存地址。点&#xff08;.&#xff09;和箭头&#xff08;->&#xff09;是用于访问结构体和联合体成员的运算符。…

手撕算法-二叉树的镜像

题目描述 操作给定的二叉树&#xff0c;将其变换为源二叉树的镜像。数据范围&#xff1a;二叉树的节点数 0≤_n_≤1000 &#xff0c; 二叉树每个节点的值 0≤_val_≤1000要求&#xff1a; 空间复杂度 O(n) 。本题也有原地操作&#xff0c;即空间复杂度 O(1) 的解法&#xff0c…

【设计模式】-工厂模式

工厂模式是一种创建型设计模式&#xff0c;它提供了一种在不指定具体类的情况下创建对象的方法。工厂模式的核心思想是将对象的创建与使用分离&#xff0c;降低系统的耦合度&#xff0c;使系统更加灵活、可扩展。 工厂模式主要分为三种类型&#xff1a;简单工厂模式、工厂方法…

苍穹外卖-day06:HttpClient、微信小程序开发、微信登录(业务流程)、导入商品浏览功能代码(业务逻辑)

苍穹外卖-day06 课程内容 HttpClient微信小程序开发微信登录导入商品浏览功能代码 功能实现&#xff1a;微信登录、商品浏览 微信登录效果图&#xff1a; 商品浏览效果图&#xff1a; 1. HttpClient 1.1 介绍 HttpClient 是Apache Jakarta Common 下的子项目&#xff0c;…

苍穹外卖-day04:项目实战-套餐管理(新增套餐,分页查询套餐,删除套餐,修改套餐,起售停售套餐)业务类似于菜品模块

苍穹外卖-day04 课程内容 新增套餐套餐分页查询删除套餐修改套餐起售停售套餐 要求&#xff1a; 根据产品原型进行需求分析&#xff0c;分析出业务规则设计接口梳理表之间的关系&#xff08;分类表、菜品表、套餐表、口味表、套餐菜品关系表&#xff09;根据接口设计进行代…

C#重新认识笔记_ 点乘,叉乘

C#重新认识笔记_ 点积&#xff0c;叉乘 一、Dot Product 点乘&#xff1a; &#xff08;Ax*Bx&#xff09;&#xff08;Ay*By&#xff09;&#xff08;Az*Bz&#xff09;Dot Product 点积 利用点积&#xff0c;可以了解&#xff0c;两个向量(vector)的相关信息&#xff0c; …

el-form 的表单校验,如何验证某一项或者多项;validateField 的使用

通常对form表单的校验都是整体校验&#xff1a; this.$refs.form.validate( valid > {if (valid) {// 校验通过&#xff0c;业务逻辑代码...} }); 如果需要对表单里的特定一项或几项进行校验&#xff0c;应该如何实现&#xff1f; 业务场景&#xff1a;下图点探测按钮时…

CSS 零基础入门教程

目录 1. div 和 span2. 什么是CSS&#xff1f;3. CSS 引入方式3.1 内部样式表3.2 外部样式表3.3 行内样式 4. 选择器4.1 标签选择器4.2 类选择器4.3 id 选择器4.4 通配符选择器 5. CSS 基础属性6. 谷歌浏览器调试工具 正文开始。 1. div 和 span 在学习 CSS 之前&#xff0c;…

Redis 八种常用数据类型详解

夯实基础&#xff0c;这篇文章带着大家回顾一下 Redis 中的 8 种常用数据类型&#xff1a; 5 种基础数据类型&#xff1a;String&#xff08;字符串&#xff09;、List&#xff08;列表&#xff09;、Set&#xff08;集合&#xff09;、Hash&#xff08;散列&#xff09;、Zse…