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

2023-05-16

目录

静态库与动态库

生成 发布动静态库

形成发布静态库

形成发布动态库

一个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 因此在运行期间是被共享的

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

(本篇完)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • Linux操作系统:Firewalld防火墙

    Linux操作系统 xff1a Firewalld 防火墙 课堂引入 xff1a Linux防火墙是通过netfilter来处理的 xff0c 它是内核级别的框架 iptables被作为netfilter的用户态抽象层 xff0c ipta
  • 全景图拼接

    目录 一 图像拼接基本流程 二 RANSAC 随机一致性采样 2 1 RANSAC基本思想 xff1a 数据中包含正确的点和噪声点 xff0c 合理的模型应该能够在描述正确数据点的同时摒弃噪声点 2 2 RANSAC基本流程 xff1a 2
  • 11. JS操作节点续(2) + DOM 重点核心

    关于dom操作 xff0c 我们主要针对于元素的操作 主要有创建 增 删 改 查 属性操作 事件操作 DOM xff1a 我们获取过来的DOM元素是一个对象 xff08 object xff09 xff0c 所以称为 文档对象模型 创建 d
  • Docker-使用篇

    文章目录 一 Docker镜像和容器 镜像命令镜像下载 xff08 pull xff09 查看镜像 xff08 images xff09 镜像搜索 xff08 search xff09 删除镜像 xff08 rmi xff09 容器命令新建
  • Rviz上发布的点位信息机器人不执行(网络配置原因)已解决

    在调试机器人时发现Rviz上发布的点位信息机器人没有执行 xff0c 经检查是由于网络配置原因 xff0c 本文记录解决方法 使用ssh将笔记本与实物机器人连接 xff0c 机器人中控使用的工控机 xff0c 机器人作为显示器和键盘输入 p
  • gazebo小车自主路径规划导航仿真

    gazebo小车自主路径规划仿真 在之前的文章中完成了对地图的构建 xff0c 接下来使用 Ros navgition完成机器人自主导航 首先是Ros navigation的下载安装 ros功能包 xff1a Navigation ros
  • 关于结构体对齐

    首先我们先了解一下各数据类型所占的字节数 x64系统 xff08 64位 xff09 x86系统 xff08 32位 xff09 在这里有一个很有意思的点 xff0c 我使用VS在x86与x64系统下运行出来的long类型所占据的空间都是4
  • 安装docker报出的错误及解决方案

    1 错误一 xff1a 解决方案 xff1a yum erase podman buildan yum install y docker ce allowerasing 重启服务 systemtl restart docker yum源的安
  • make、makefile的使用解释(Linux项目自动化构建工具)

    本文所使用系统为Linux centos 7 makefile是一个文件 里面包含了 xff1a 1 文件与文件的依赖关系 2 文件与文件的依赖方法 为什么需要包含文件与文件的依赖方法和依赖方法呢 xff1f 一个工程中的源文件不计数 xf
  • elementUI中neditor第一次加载无法正常赋值的解决方法

    1 lt neditor v if 61 34 neditorLoad 34 v model 61 34 formFields content 34 class 61 34 wd editor 34 gt 加上v if 61 34 nedi
  • Gazebo仿真中光源的设置

    目录 写在前面官方文档链接point类型directional类型spot类型 写在前面 最近研究在Gazebo中仿真px4 xff0c 使用Apriltag ros包对Apriltag二维码检测的时候发现光源对检测影响非常大 xff0c
  • 使用Java操作Hbase

    目录 修改hosts文件 导入jar包 配置hbase信息 xff0c 连接hbase数据库 创建表 删除表 获取namespace 获取tables 添加数据 查询表中的数据 查询表中所有数据 关闭流 修改hosts文件 位置 xff1a
  • CAN协议国际标准化协议部分总结(一)

    前言 xff1a 本文主要是对CAN协议中ISO11898做一个简短的总结 ISO11898 ISO11519 ISO15765 CAN协议在OSI模型中包含三层 xff0c 物理层 数据链路层和传输层 各层中CAN定义内容可参考下图 其协
  • STM32 串口 DMA 数据读取(详细代码)

    最近重新开始学32 xff0c 搞到串口 DMA 的时候 xff0c 数据读取卡了很长一段时间 xff0c 最终 xff0c 功夫不负有心人终于搞出来了 在此以记录一下 xff0c 方便以后查询使用 在调试的过程中也遇到了很多bug xff
  • 在阿里云购买Linux服务器,配置宝塔环境,全图文,最最详细图解,保姆级教学

    在阿里云购买Linux服务器 xff08 CentOs 7 xff09 xff0c 配置宝塔环境 一 在阿里云购买CentOs服务器 Linux系统 CentOS是免费的 开源的 可以重新分发的开源操作系统 xff0c CentOS xff
  • (Linux)FreeRTOS(任务)

    任务调度器 调度器就是使用相关的调度算法来决定当前需要执行的任务 xff0c 调度器总是在所有处于就绪列表的任务中 xff0c 选择具有最高优先级的任务来执行 抢占式调度 xff1a 主要是针对优先级不同的任务 xff0c 每个任务都有一个
  • 制作一个串口助手 | python + pyqt5

    目录 一 背景 1 1 开发流程图 二 前提 2 1 关于环境 2 2 关于源码 三 步骤 3 1 使用pyqt创建一个 ui界面并生成 py文件 3 2 创建两个 py文件 xff0c 一个用来继承ui界面生成的 py类 xff0c 一个
  • FreeRTOS任务调度与任务切换 | FreeRTOS八

    目录 说明 xff1a 一 任务调度器 1 1 开启任务调度器函数 1 2 任务调度器实现步骤 1 3 xPortStartScheduler 函数 二 启动一个任务 2 1 prvStartFirstTask 函数 2 2 vPortSV
  • 单片机原理简介

    提出问题 xff1a 什么是单片机 xff1f 类似于一台性能相对较弱的微型电脑 xff0c 具有完整的计算机结构和片内外设 xff08 例如串口 xff0c I2C xff0c ADC等硬件 xff09 xff0c 将他们集成封装在一颗芯
  • composer安装第三方库

    用法 xff1a 在thinkphp根目录下执行 composer require wechatpay wechatpay 1 4

随机推荐