动态库的制作和使用

动态库的制作

动态库(在Windows上称为DLL,即Dynamic Link Library,在Unix-like系统上称为SO,即Shared Object)的制作过程涉及几个关键步骤:编写源代码、编译源代码为共享的动态对象,并链接它们生成动态库。以下是在Unix-like系统和Windows系统上创建动态库的一般步骤。

  1. 编写源代码
    创建你的功能代码的源文件,例如 file1.cppfile2.cpp

  2. 编译源代码为位置无关代码(PIC)
    位置无关代码是在内存中可灵活定位的代码,适合用于动态库。

    g++ -fpic -c file1.cpp
    g++ -fpic -c file2.cpp
    
  3. 创建动态库(.so 文件)
    使用 g++gcc 将位置无关的对象文件链接为动态库。

    g++ -shared -o libmydynamic.so file1.o file2.o
    
  4. 设置动态库的路径
    为了让执行文件在运行时能找到动态库,你可能需要设置 LD_LIBRARY_PATH 环境变量或者修改 /etc/ld.so.conf 并运行 ldconfig

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/library
    
daic@daic:~/Linux/lesson06/library$ tree
.
├── include
│   └── head.h
├── lib
├── main.c
└── src├── add.c├── div.c├── mult.c└── sub.c3 directories, 6 files
daic@daic:~/Linux/lesson06/library$ cd src
daic@daic:~/Linux/lesson06/library/src$ gcc -c -fpic *.c
sub.c:2:10: fatal error: head.h: No such file or directory#include "head.h"^~~~~~~~
compilation terminated.
daic@daic:~/Linux/lesson06/library/src$ gcc -c -fpic *.c -I ../include/
daic@daic:~/Linux/lesson06/library/src$ tree
.
├── add.c
├── add.o
├── div.c
├── div.o
├── mult.c
├── mult.o
├── sub.c
└── sub.o0 directories, 8 files
daic@daic:~/Linux/lesson06/library/src$ cd ../lib
daic@daic:~/Linux/lesson06/library/lib$ gcc -shared ../src/*.o -o libcalc.so
daic@daic:~/Linux/lesson06/library/lib$ tree
.
└── libcalc.so

动态库的使用

daic@daic:~/Linux/lesson06$ tree
.
├── calc
│   ├── add.c
│   ├── div.c
│   ├── head.h
│   ├── main.c
│   ├── mult.c
│   └── sub.c
└── library├── include│   └── head.h├── lib│   └── libcalc.so├── main.c└── src├── add.c├── div.c├── mult.c└── sub.c5 directories, 13 files
daic@daic:~/Linux/lesson06$ cd library/
daic@daic:~/Linux/lesson06/library$ gcc main.c -o app -I ./include/ -L ./lib -l calc
daic@daic:~/Linux/lesson06/library$ tree
.
├── app
├── include
│   └── head.h
├── lib
│   └── libcalc.so
├── main.c
└── src├── add.c├── div.c├── mult.c└── sub.c3 directories, 8 files
daic@daic:~/Linux/lesson06/library$ ./app 
./app: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory

这里会遇到动态库加载失败的问题

静态库:gcc进行链接时,会把静态库中代码打包到可执行程序中

动态库:gcc进行链接时,动态库代码不会被打包到可执行程序中

程序启动之后,动态库会被动态加载到内存中,通过ldd(list dynamic depdencies)命令检查动态库依赖关系。

daic@daic:~/Linux/lesson06/library$ ldd main
ldd: ./main: No such file or directory
daic@daic:~/Linux/lesson06/library$ ldd applinux-vdso.so.1 (0x00007ffc19e52000)libcalc.so => not foundlibc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f52bec10000)/lib64/ld-linux-x86-64.so.2 (0x00007f52bf203000)

动态库加载失败原因

ELF(Executable and Linkable Format)

ELF 是一种常用的文件格式,用于定义程序、库文件(如 .so 文件),甚至是核心转储文件在类 Unix 系统(如 Linux)上的结构。ELF 格式是可执行文件和可链接格式的简称,它是最通用的标准文件格式之一,用于可执行程序和库。

ELF 文件包含了程序执行所需的各种信息,包括代码(也称为“文本”段)、数据段、符号表、重定位表等。对于动态链接库和可执行程序而言,ELF 格式提供了存储和运行时所需的结构信息,使得系统加载器可以正确地映射和执行文件。

动态链接过程

ld-linux.so 是 Linux 系统的动态链接器,负责载入 ELF 文件并解析其依赖的共享库文件。动态链接器的工作流程如下:

  1. 读取 ELF 文件的 DT_RPATH 段:这是动态链接时库文件搜索的首选路径,通常在编译时指定。

  2. 环境变量 LD_LIBRARY_PATH:如果 DT_RPATH 未定义或不包含所需的库,链接器会查看 LD_LIBRARY_PATH 环境变量。这是一个由用户或管理员设置的环境变量,用于指定额外的库搜索路径。

  3. /etc/ld.so.cache 文件:这是一个预先编译的库文件路径列表,由 ldconfig 命令根据系统中安装的库更新。它提供了一种快速查找库文件位置的方法。

  4. 标准库目录:如果以上都未找到所需的库,则动态链接器会搜索标准库目录,通常是 /lib/usr/lib

通过这个过程,动态链接器能够找到所有可执行文件所依赖的共享库,并将它们加载到内存中,使程序可以运行。

这个过程中,如果对特定的库或搜索路径有疑问,或者需要调试与库相关的问题,可以使用 ldd 命令来查看可执行文件所依赖的库及其路径,或者使用 strace 来跟踪程序启动时的系统调用,包括动态链接器的活动。这些工具都是分析和解决与 ELF 文件和动态链接相关问题的有力工具。

解决动态库加载问题

DT_RPATH 我们无法改变,不用管

出现这个错误的原因是操作系统在运行 app 程序时,没有找到需要的动态库 libcalc.so。动态库在运行时需要被加载,如果系统的动态链接器没有在配置的库路径中找到这个库,就会抛出这样的错误。

解决方案:

  1. 设置 LD_LIBRARY_PATH 环境变量
    你可以临时通过设置 LD_LIBRARY_PATH 环境变量来指定附加的库搜索路径。这个环境变量包含了一系列目录,系统在这些目录中搜索需要的动态库。

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/your/library/dir
    ./app
    

    在这个例子中,应该将 /path/to/your/library/dir 替换为 libcalc.so 文件的实际路径,例如:

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/daic/Linux/lesson06/library/lib
    ./app
    
  2. 将库文件复制到标准库路径:(不推荐)
    另一个解决方案是将 libcalc.so 复制到系统的标准库路径下,如 /usr/lib/usr/local/lib。这样,库文件就会被自动识别,无需设置环境变量。

    sudo cp /home/daic/Linux/lesson06/library/lib/libcalc.so /usr/local/lib/
    sudo ldconfig  # 更新动态链接器的缓存
    ./app
    
  3. 在编译时指定 rpath
    在编译 app 程序时,你可以使用 -rpath 链接选项来指定一个运行时库搜索路径。这个选项告诉动态链接器在特定的位置查找动态库。

    gcc main.c -o app -I ./include/ -L ./lib -lcalc -Wl,-rpath,/home/daic/Linux/lesson06/library/lib
    ./app
    
  4. 修改 /etc/ld.so.conf 文件
    你也可以将你的库路径添加到 /etc/ld.so.conf 文件或者该文件引用的其他文件中,然后运行 ldconfig。这是一个更为永久的解决方案,适用于多个程序或用户需要访问该库。

    echo "/home/daic/Linux/lesson06/library/lib" | sudo tee -a /etc/ld.so.conf
    sudo ldconfig
    ./app
    

以上任一方法都可以解决动态库未找到的问题。选择哪一种取决于你的具体需求和环境。

临时配置环境变量

如果是为了临时测试,设置 LD_LIBRARY_PATH 可能是最快的方法;如果是为了长期或者系统范围的使用,修改 /etc/ld.so.conf 或使用 rpath 可能更为合适。

daic@daic:~/Linux/lesson06/library/lib$ pwd
/home/daic/Linux/lesson06/library/lib
daic@daic:~/Linux/lesson06/library/lib$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/daic/Linux/lesson06/library/lib
daic@daic:~/Linux/lesson06/library/lib$ echo $LD_LIBRARY_PATH 
:/home/daic/Linux/lesson06/library/lib
daic@daic:~/Linux/lesson06/library/lib$ tree
.
└── libcalc.so0 directories, 1 file
daic@daic:~/Linux/lesson06/library/lib$ cd ..
daic@daic:~/Linux/lesson06/library$ tree
.
├── app
├── include
│   └── head.h
├── lib
│   └── libcalc.so
├── main.c
└── src├── add.c├── div.c├── mult.c└── sub.c3 directories, 8 files
daic@daic:~/Linux/lesson06/library$ ldd applinux-vdso.so.1 (0x00007ffc419de000)libcalc.so => /home/daic/Linux/lesson06/library/lib/libcalc.so (0x00007f648c7a4000)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f648c3b3000)/lib64/ld-linux-x86-64.so.2 (0x00007f648cba8000)
daic@daic:~/Linux/lesson06/library$ ./app
a = 20, b = 12
a + b = 32
a - b = 8
a * b = 240
a / b = 1.666667

但是这样配置的环境变量是临时的,只在当前终端中有用

daic@daic:~/Linux/lesson06/library$ ldd applinux-vdso.so.1 (0x00007ffed4f7c000)libcalc.so => not foundlibc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff495d08000)/lib64/ld-linux-x86-64.so.2 (0x00007ff4962fb000)
daic@daic:~/Linux/lesson06/library$ ./app 
./app: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory

在Unix-like系统中,永久设置环境变量通常涉及编辑用户或系统级的配置文件。这些设置会在每次登录或启动一个新的终端会话时自动应用。以下是几种常见的方法来

永久配置环境变量:

对单个用户设置
  1. 编辑 .bashrc.profile 文件
    对于使用 bash shell 的用户,可以在用户的家目录下编辑 .bashrc 文件;如果是登录shell,比如通过图形界面登录,编辑 .profile 文件会更合适。在文件的末尾添加如下行:

    export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/path/to/your/library"
    

    /path/to/your/library 替换成你的实际库路径。保存文件后,这个变量在下次登录时会自动设置。

  2. 使改动立即生效
    你可以通过运行以下命令使 .bashrc.profile 的更改立即生效,而不需要重新登录:

    source ~/.bashrc
    # 或者
    source ~/.profile
    
对所有用户设置

如果你希望为系统上的所有用户设置环境变量,可以编辑 /etc/profile/etc/environment

  1. 编辑 /etc/profile
    /etc/profile 文件中添加同样的 export 命令。这会影响所有使用 bash shell 的用户:

    export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/path/to/your/library"
    

    这种方式在用户登录时生效,并适用于所有bash用户。

  2. 编辑 /etc/environment
    /etc/environment 中设置环境变量略有不同,因为这个文件不是一个脚本,而是一个存储环境变量的列表,每个变量一行:

    LD_LIBRARY_PATH="/usr/lib:/usr/local/lib:/path/to/your/library"
    

    注意,这里不能使用 $LD_LIBRARY_PATH 来引用已存在的值,因为 /etc/environment 不支持shell命令。这个文件由PAM模块读取,适用于所有用户和登录方式。

注意事项

  • 当你修改环境变量时,确保不要覆盖掉其他重要的系统路径和设置。
  • 对于使用其他shell(如 zshfish)的系统,你需要修改对应的配置文件,比如 ~/.zshrc~/.config/fish/config.fish
  • 对于环境变量的永久配置,确保你了解其对系统安全和稳定性的潜在影响,特别是在全局设置时。

以上方法可以帮助你根据需要为个人或整个系统设置环境变量。选择合适的方法取决于你的具体需求和系统环境。

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

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

相关文章

4*5的矩阵(C语言)

一、N-S流程图&#xff1b; 二、运行结果&#xff1b; 三、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int i 0;int j 0;int result 0;//嵌套循环输出&#xff1b;for (i 1; i < 4; i){//列…

【JavaScript】数组 Array 总结(202404)

🥰 数组 Array (202404) 数组 🔗 ❤️‍🔥 数组是一种有序的多个变量值的集合,可以通过索引来获取元素。 ❤️‍🔥 数组是一种特殊的对象类型。 1、创建数组 字面量 // a. 有元素 let arr = [a, b, c]; console.log(arr); // b. 空数组 let arr1 = []; console.…

L2正则化——解释为什么可以减少模型的复杂度

L2正则化是一种用于机器学习模型的技术&#xff0c;旨在减少模型的复杂度&#xff0c;从而提高其泛化能力。在L2正则化中&#xff0c;通过添加一个惩罚项&#xff0c;模型的权重被迫保持较小的值&#xff0c;这有助于防止过拟合&#xff0c;即模型在训练数据上表现良好但在未见…

【Python】OPC UA模拟服务器实现

目录 服务器模拟1. 环境准备2. 服务器设置3. 服务器初始化4. 节点操作5. 读取CSV文件6. 运行服务器 查看服务器客户端总结 在工业自动化和物联网&#xff08;IoT&#xff09;领域&#xff0c;OPC UA&#xff08;开放平台通信统一架构&#xff09;已经成为一种广泛采用的数据交换…

单链表的基本操作实现:初始化、尾插法、头插法、输出单链表、求表长、按序号查找、按值查找、插入结点、删除结点。

1.参考学习博文&#xff08;写的相当好的文章&#xff09;&#xff1a; http://t.csdnimg.cn/AipNl 2.关于我的总结&#xff1a; 定义单链表&#xff1a; typedef struct LNode {Elemtype data;struct LNode* next; }LNode; data用来存放元素值&#xff0c;next用来指向后…

【算法】反转链表

本题来源---《反转链表》 题目描述&#xff1a; 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输…

前端怎样做权限控制的?

在做系统时&#xff0c;我们常常因为使用该系统或软件的用户不同&#xff0c;要给到不同角色不同的模块权限控制。那前端是怎样做权限控制的&#xff1f;下面我将为你提供一些实际操作的例子&#xff0c;帮助你更具体地理解如何实施系统权限控制。 例子1&#xff1a;基于角色的…

vue2+el-row制作一个无间距网格

:gutter"0"无间距 :span""总为24份&#xff0c;根据自身需要设置每个网格项的宽度 <div class"thirdTabs"><!-- 第一行 --><el-row :gutter"0" class"thirdTabs-row-1"><el-col :span"4" c…

医学图像三维重建与可视化系统 医学图像分割 区域增长

医学图像的三维重建与可视化&#xff0c;这是一个非常有趣且具有挑战性的课题&#xff01;在这样的项目中&#xff0c;可以探索不同的医学图像技术&#xff0c;比如MRI、CT扫描等&#xff0c;然后利用这些图像数据进行三维重建&#xff0c;并将其可视化以供医生或研究人员使用。…

C++中的继承与多态

一、继承&#xff1a; 1.什么是继承&#xff1f; 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许程序员在保持原有类特性的基础上进行扩展&#xff0c;增加功能&#xff0c;这样产生新的类&#xff0c;称派生类。继承呈现了面向对象…

golang map总结

目录 概述 一、哈希表原理 哈希函数 哈希表和哈希函数的关系 哈希表的优势 哈希冲突 什么是哈希冲突 如何处理哈希冲突 链表法 开放寻址法 哈希表常见操作过程 存储数据 检索数据 删除数据 常用的哈希算法 哈希表的应用场景 二、golang map map的内部结构 h…

c++智能指针(4)-- shared_ptr

概述 场景一: 希望指向多个指针管理一片空间 unique_ptr它是不允许两个智能指针管理一片空间的&#xff0c;所以其禁止直接拷贝和赋值(转化为右值可以)。 auto_ptr虽然其允许我们多个智能指针管理一片空间&#xff0c;但是这样的操作对于auto_ptr来说是不安全的&#xff0c;因…

Docker Volume (存储卷)

什么是存储卷? 存储卷就是将宿主机的本地文件系统中存在的某个目录直接与容器内部的文件系统上的某一目录建立绑定关系。这就意味着&#xff0c;当我们在容器中的这个目录下写入数据时&#xff0c;容器会将其内容直接写入到宿主机上与此容器建立了绑定关系的目录。在宿主机上…

选课成绩管理系统

文章目录 员工管理系统一、项目演示二、项目介绍三、系统部分功能截图四、部分代码展示五、底部获取项目&#xff08;9.9&#xffe5;&#xff09; 员工管理系统 一、项目演示 课程管理系统 二、项目介绍 基于springbootvue的前后端分离选课成绩管理系统 该系统可做课程管理…

rpi-ws281x库测试介绍

彩色RGB灯测试-rpi-ws281x库使用 1 rpi-ws281x库介绍 rpi-ws281x 是一个针对 Raspberry Pi 的库&#xff0c;用于控制WS281X 系列 LED 灯带&#xff08;例如 WS2812B&#xff09;的颜色和亮度。它提供了一个 Python 接口&#xff0c;让你可以轻松地通过 Raspberry Pi 控制这些…

基础算法之二分算法

前言 本次博客&#xff0c;将要介绍二分算法的基本原理以及如何使用&#xff0c;深入浅出 二分可以针对整型以及浮点型接下来对其讲解希望对小白有所帮助吧 整型的二分法 一般要在一个数组中猜出一个数是否存在我们可以遍历一遍整个数组&#xff0c;判断是否存在&#xff0…

使用Windows11自带的WSL安装Ubuntu Linux系统教程

WSL介绍 WSL全称Windows Subsystem for Linux&#xff0c;它是Windows10带来的新特性&#xff0c;用于Windows系统上的Linux子系统。也就是说&#xff0c;可以在Windows系统中获取Linux系统&#xff0c;这个过程无需通过虚拟机&#xff0c;而是直连计算机硬件。 简而言之&#…

Linux--进程间的通信-命名管道

前文&#xff1a; Linux–进程间的通信-匿名管道 Linux–进程间的通信–进程池 命名管道的概念 命名管道是一种进程间通信&#xff08;IPC&#xff09;机制&#xff0c;运行不同进程之间进行可靠的、单向或双向的数据通信。 特点和作用&#xff1a; 跨平台性&#xff1a;在W…

ResNet最新变体!性能反超Transformer,模型准确率达98.42%

目前ResNet&#xff08;残差网络&#xff09;有两大主流创新思路&#xff1a;一是与其他技术或模型结合&#xff0c;比如前文讲到的ResNetTransformer&#xff1b;二是在原始设计的基础上进行改进。 尽管ResNet通过残差学习有效改善了深层网络的训练和性能&#xff0c;但同时它…

【LeetCode热题100】【堆】数据流的中位数

题目链接&#xff1a;295. 数据流的中位数 - 力扣&#xff08;LeetCode&#xff09; 不停插入元素要求找到每个状态的中位数&#xff0c;用两个堆&#xff0c;把中位数左边的数记为left&#xff0c;右边的数记为right&#xff0c;一个大顶堆记录小于等于中位数的left&#xff…