[ Linux ] 动静态库 手把手教你写一个自己的库

news2025/8/7 13:11:43

目录

静态库与动态库

生成 发布动静态库

形成发布静态库

形成发布动态库

一个makefile同时生成动静态库

如何使用动静态库

使用静态库

正确做法:

使用动态库

运行动态库

为什么动态库运行时有找库的步骤


静态库与动态库

  • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。

生成 发布动静态库

那么我们如何来设计一个静态库呢?我们先写一段最简单的跨文件函数调用的C语言程序

/*main函数*/
#include "mymath.h"

int main()
{
    int from = 1;
    int to = 10;
    int result = addToVal(from,to);
    printf("result:%d\n",result);
    return 0;
}

/*mymath.c*/
#include "mymath.h"

int addToVal(int from,int to)
{
    assert(from<=to);
    int result = 0;
    for(from;from<=to;++from)
    {
        result+=from;
    }
    return result;
}

/*mymath.h*/
#pragma once 
#include <stdio.h>
#include <assert.h>

extern int addToVal(int from,int to);

/*myprint.c*/
#include "myprint.h"

void Print(const char* msg)
{
    printf("%s: %lld\n",msg,(long long)time(NULL));
}

/*myprint.h*/
#pragma once 
#include <stdio.h>
#include <time.h>

extern void Print(const char* msg);

我们以前都是这样写的,现在我们要形成静态库,该怎么形成呢?

形成发布静态库

  • 使用gcc将源文件变成 .o文件并打包成库(关于如何gcc的相关使用大家可看我这篇文章)

链接:不就是把所有的.o链接形成一个可执行程序!如果我把我的所有的.o文件给别人,别人能链接使用吗?

答案当然是可以,因此静态链接的本质就是把.o文件合起来形成一个可执行程序,形成静态库就是把多个.o打包起来形成一个库。那么打包成一个静态库我们使用的命令式ar(ar是gnu归档工具archive files)用的选项是rc(c -- create, r--replace) 后面紧跟库的名字(lib开头,静态库.a结尾)

至此我们就成功的生成了一个静态库,那么静态库的本质是:我们曾经的源文件翻译成.o文件然后打包起来的操作。

  • 发布静态库

当我们把静态库写好之后,如何让别人使用呢?我们在考虑这个问题时我们站在使用者的角度来思考一下,让你在用库的时候,需要什么东西呢?我们都知道需要的是库文件和头文件。因此我们要想发布静态库,我们需要把库文件和头文件给对方。

形成发布动态库

形成动态库和形成静态库的原理基本是一样的,他们的区别是当形成.o文件时需要带-fPIC(position independent code)这一步是需要产生地址无关码。在我们刚刚所写的.o必须存在到进程的地址空间内才能使用。而如果使用fPIC是与地址无关码是把.o加在到内存的任意位置,进程都可以访问使用(采用的是相对地址)。

  • 使用gcc行程.o文件,并且带-shared选项 表示生成共享库格式

  • 动态库的交付(发布)

一个makefile同时生成动静态库

.PHONY:all
all: libmymath.so libmymath.a

libmymath.so:mymath.o myprint.o
	gcc -shared -o libmymath.so mymath.o myprint.o
mymath.o:mymath.c
	gcc -fPIC -c mymath.c -o mymath.o
myprint.o:myprint.c
	gcc -fPIC -c myprint.c -o myprint.o

libmymath.a:mymath_s.o myprint_s.o
	ar -rc libmymath.a mymath.o myprint_s.o
mymath_s.o:mymath.c
	gcc -c mymath.c -o mymath_s.o
myprint_s.o:myprint.c
	gcc -c myprint.c -o myprint_s.o

.PHONY:lib
lib:
	mkdir -p lib-static/lib
	mkdir -p lib-static/include
	cp *.a lib-static/lib
	cp *.h lib-static/include

	mkdir -p lib-dyl/lib
	mkdir -p lib-dyl/include
	cp *.so lib-dyl/lib
	cp *.h lib-dyl/include

.PHONY:clean
clean:
	rm -rf *.o *.a *.so lib*

我们再发布之后查看我们的动静态库

至此我们动静态库就形成了。

如何使用动静态库

当我们形成动静态库之后我们要站在使用库的人的角度来思考一下如何使用我们所形成的动静态库

使用静态库

首先我们来看看如何使用静态库。

我们首先按照正常的写一个简单的C语言文件,注意我们不在当前路径下,我们在创建一个Test文件夹,让测试的代码在Test的文件夹下,保证test.c测试文件当前路径下没有这两个库。

#include "mymath.h"
#include "myprint.h"

int main()
{
    int from = 1;
    int to = 10;
    int result = addToVal(from,to);
    printf("result:%d\n",result);
    Print("hello world\n");
    return 0;
}

此时当我们编译这个代码时肯定会报错,因为在当前路径下没有这个头文件,这是理所当然的。

我们之前学习过头文件的搜索路径: "" <>

  1. 在当前路径下查找头文件
  2. 在系统路径下查找头文件

因此既不在当前路径下也不在系统路径下,当然找不到我们刚刚写的库。那怎么办呢?

正确做法:

1.将系统头文件和库文件,拷贝到系统路径下即可。

sudo cp lib-static/include/* /usr/include/ 
sudo cp lib-static/lib/* /lib64/

我们再来编译,发现还是报错了.......但是这次报错信息明显已经不是找不到头文件了因此这个错误已经不是头文件没有了,这是一种链接错误。这是因为这是我们使用了第三方库,我们需要在gcc时带-l+库名

库名:去掉前缀(lib),去掉后缀(.*)的名字 例如:libmymath.a 的库名是mymath

这时候我们发现就可以了。gcc -l(指明我要链接的第三库的名称)

但是现阶段我们不推荐这个做法,污染系统的头文件集和库,因此我们赶紧把刚才加载到头文件集和库文件的内容删除

sudo rm -f /usr/include/mymath.h 
sudo rm -f /usr/include/myprint.h 
sudo rm -f /lib64/libmymath.a

删除后我们再编译又回到了刚才的错误,那么我们介绍第二个方法

2.gcc 指定头文件搜索路径

gcc -I(大i)选项带上头文件所在位置

-L 选项带上所在库的位置

-l 选择带上连接库的名字

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

使用动态库

1.第一种方法和静态库方法一样 拷贝头文件和库文件到系统路径下

2.我们先把动态库拷到当前路径下,然后使用我们使用静态库的第二种方法

cp ../lib-dyl/ . -rf

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

但是当我们成功gcc后以为可以运行了,但是发现还是报错了

我们使用ldd来查看一下程序依赖的库

ldd mytest

我们发现依赖的动态库找不到呢(not found)那我们刚才不是都已经gcc手动连接了吗?这是因为-I -L -l这是gcc的选项,本质是告诉gcc编译器的,但是形成可执行程序后和gcc就没有关系了。而最后我们进程使用的时候没人告诉我们进程,因此动态库在运行起来时进程找不到这个库了。

运行动态库

动态库的运行搜索路径

./mytest: error while loading shared libraries: libmymath.so : cannot open shared object file: No such file or directory

那么静态库的时候没有这个问题呢?因此静态在形成可执行程序后已经把需要的代码拷贝进我的代码了,因此在运行时不依赖任何库,因此不需要在运行时查找了。

那么为什么动态库会有这个问题?程序和动态库是分开加载的

那么如何找到动态库呢?

  • 1. 将库拷贝进系统库文件下 拷贝.so文件到系统共享库路径下, 一般指/usr/lib

这个方法比较好理解,但是不推荐,因为会污染系统库

  • 2. 通过导入环境变量的方式 更改 LD_LIBRARY_PATH

因为程序在运行的时候,会在环境变量中查找自己需要的动态库路径 -- LD_LIBRARY_PATH

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/Lxy/code/linux-cod 导入环境变量 
echo $LD_LIBRARY_PATH -- 查看内容

此时我们再ldd mytest查看发现动态库已经找到了

我们再运行便可以成功

但是如果我们关闭xshell再登录进来我们便又没有了

因此第二种方式只是在当前xshell内有用。

  • 3. 通过系统配置文件 ldconfifig 配置/etc/ld.so.conf.d/,ldconfifig更新

这条命令可以查看我们系统里面,如果我们自定义了动态库,那么此时系统在扫描路径时,不仅在库内扫描,还在当前下扫描。

ls /etc/ld.so.conf.d/

因此我们自己

sudo touch /etc/ld.so.conf.d/libmymath.conf

在这个路径下创建一个文件,把我们动态库的路径写进这个文件中

我们再执行ldconfig让etc文件生效

sudo ldconfig /etc/ld.so.conf.d/libmymath.conf

当我们再次关掉xshell时,我们依然可以运行

  • 4. 其他方式

在系统库文件下创建软连接

sudo ln -s /home/Lxy/code//linux-code/practice/11-14/test/lib-dyl/lib/libmymath.so /lib64/libmymath.so

此时我们来看ldd mymath,已经连接到库了,我们再运行发现也成功了

为什么动态库运行时有找库的步骤

在程序地址空间中,我们谈到过堆栈之间的区域有一个共享区。而我们所写的库会被映射到共享区。因此在自己的地址空间中可以执行所有的代码和库的是因为库的代码被映射到自己的共享区。

因此在物理内存中库只有一份。因此动态库为什么叫shared libs 因此在运行期间是被共享的

因此当我们进程在运行的时候,如果要动态加载他所需要的的库,前提条件是先找到这个库在哪里。而静态库根本无需考虑这个问题。

(本篇完)

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

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

相关文章

聊一聊作为高并发系统基石之一的缓存,会用很简单,用好才是技术活

大家好&#xff0c;又见面了。 在服务端开发中&#xff0c;缓存常常被当做系统性能扛压的不二之选。在实施方案上&#xff0c;缓存使用策略虽有一定普适性&#xff0c;却也并非完全绝对&#xff0c;需要结合实际的项目诉求与场景进行综合权衡与考量&#xff0c;进而得出符合自…

Spring Security认证之用户定义

本文内容来自王松老师的《深入浅出Spring Security》&#xff0c;自己在学习的时候为了加深理解顺手抄录的&#xff0c;有时候还会写一些自己的想法。 在前面的案例中&#xff0c;我们登陆的用户信息是基于配置文件来配置的&#xff0c;其本质上是基于内存来实现的。但是在实际…

(5)多机器人集群编队策略

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 TODO:写完再整理 文章目录系列文章目录前言一、多机器人集群编队策略1、多机器人协同作业冲突问题&#xff08;1&#xff09;介绍&#xff08;2&#xff09;多机器人系统…

学会Spring Cloud微服务架构绝活,渣本也能进大厂

微服务架构是互联网很热门的话题&#xff0c;是互联网技术发展的必然结果。它提倡将单一应用程序划分成一组小的服务&#xff0c;服务之间互相协调、互相配合&#xff0c;为用户提供最终价值。虽然微服务架构没有公认的技术标准和规范或者草案&#xff0c;但业界已经有一些很有…

C++程序设计--第三章内容

提前声明&#xff1a; 本文内容为华北水利水电大学研究生C课程&#xff0c;如有 侵权请告知&#xff0c;作者会予以删除 1.函数 函数作用 —— 任务划分&#xff1b;代码重用定义形式 类型 函数名 &#xff08; 形式参数表&#xff09;{语句序列}调用形式 函数名&#x…

数据结构:树

文章目录一.树的概念二.树的相关概念三.树的表示一.树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下…

『Java』数组中值得说的那些事

文章目录前言一、数组的创建及初始化&#x1f333;1、数组的创建&#x1f333;2、数组的初始化&#x1f351;&#xff08;1&#xff09;动态初始化&#x1f351;&#xff08;2&#xff09;静态初始化二、数组的使用&#x1f333;1、数组中元素访问&#x1f333;2、遍历数组&…

python3 爬虫(初试牛刀)

此文章仅供学习交流使用 在学习爬虫之前&#xff0c;把最近对于 VMware 的网络学习做个总结 接下来&#xff0c;步入正题&#xff01; 分析 Robots 协议 禁止所有爬虫访问任何目录的代码&#xff1a; User-agent: * Disallow:/允许所有爬虫访问任何目录的代码&#xff1a; …

2022年新版Pycharm通过project interpreter国内镜像源设置

2022年新版Pycharm通过project interpreter国内镜像源设置解决方案速览一、国内镜像源列表二、pycharm访问project interpreter解决方案速览 File->Settings->project interpreter-> -> Available Packages将options打勾&#xff0c;并输入-i https://pypi.tuna.…

Gvim显示行号、最大化、字号、主题等常用配置修改

Gvim的设置分两种&#xff1a;1. 临时设置&#xff0c;2. 永久设置&#xff0c;本文只关注永久设置的情况。 配置Gvim只需修改Gvim配置文件即可&#xff0c;Linux中&#xff0c;配置文件的地址是~/.vimrc&#xff0c;若没有该文件则创建即可&#xff0c;我们可以直接输入gvim …

平面设计师怎么找素材?

平面设计素材网站&#xff0c;免费下载&#xff0c;建议收藏&#xff01; 1、菜鸟图库 https://www.sucai999.com/?vNTYwNDUx菜鸟图库是一个素材量非常丰富的网站&#xff0c;网站聚合了平面、UI、淘宝电商、高清背景图、图片、插画等高质量素材&#xff0c;平面模板非常多&am…

数据结构绪论、顺序表课后练习题

文章目录第1章 绪论一 填空题二 选择题三 判断题✅❎第2章 线性表一 判断正误二 单项选择题三 简答题第1章 绪论 一 填空题 数据结构被形式地定义为&#xff08;D, R&#xff09;&#xff0c;其中D是数据元素的有限集合&#xff0c;R是D上的关系有限集合。数据结构按逻辑结构…

idea如何排查jar冲突

工具系列文章目录 idea如何排查jar冲突提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结前言 当遇到jar包冲突的时候常常会出现…

自动驾驶入门:控制

目录 概念 控制流程 PID控制 PID 的优劣势 LQR 线性二次调节器 模型控制预测 总结 概念 控制是驱使车辆前行的策略。对于汽车而言&#xff0c;最基本的控制输入为转向、加速和制动。通常&#xff0c;控制器使用一系列路径点来接收轨迹。 控制器的任务是使用控制输入让…

Flask微服务注册到Nacos

目录一、前言二、手写实现Flask注册到Nacos1. 服务注册2. 心跳检测三、使用nacos-sdk-python完成注册四、SpringBoot远程调用1. 添加 Python 端业务逻辑2. SpringBoot 的远程调用五、SpringGateway网关转发一、前言 最近有一个使用 SpringCloud 的微服务项目&#xff0c;需要使…

[附源码]java毕业设计面向服装集群企业的个性化定制服务系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

C++ 异常处理 重新throw变量时的事件

直接说结论&#xff1a;throw的表达式创建出来的变量会被拷贝下来【通过拷贝构造函数&#xff0c;后面会证实这一点&#xff0c;且是放在堆里的】&#xff0c;然后沿着调用路径去搜索最近匹配异常的catch语句&#xff0c;在沿途&#xff0c;传递给catch语句的是堆中的异常变量的…

2022年数维杯国际大学生数学建模挑战赛D题三重拉尼娜事件下极端气候灾害损失评估与应对策略研究解题过程

2022年数维杯国际大学生数学建模挑战赛 D题 三重拉尼娜事件下极端气候灾害损失评估与应对策略研究 原题再现&#xff1a; 2022年7月至8月&#xff0c;中国南方许多城市经历了多天的炎热天气&#xff0c;而北方部分地区也出现了大面积强降水。此外&#xff0c;许多欧洲国家也经…

GEE开发之Modis_NDVI数据分析获取大总结

GEE开发之Modis_NDVI数据分析获取大总结0. 之前的博客链接1. MYD13Q1(250米/16天)2. MOD09GA_006_NDVI(500米/1天)3. MOD13Q1(250米/16天)4. MOD13A1(500米/16天)5. MOD13A2(1000米/16天)6. 日数据下载(以MYD13Q1为例子)7. 月数据下载(以MYD13Q1为例子)7.1 代码一7.2 代码二&am…

博途1200PLC轴控功能块(脉冲轴)

1200选择晶体管输出的型号,本体支持脉冲发送,利用工艺对象可以组态脉冲轴。利用1200和1500PN总线可以进行总线伺服的控制,具体请参看下面的博客: 博途1200/1500PLC V90 PN通信控制 (FB284功能块)_RXXW_Dor的博客-CSDN博客_fb284功能块先简单说下如何获取FB284,一般有2种方…