Linux动态库和静态库

news2025/3/20 9:13:14

Linux动态库和静态库

  • Linux动态库和静态库
    • 动静态库的基本原理
      • 可执行程序的生成过程
      • 动静态库的本质
    • 认识动静态库
      • 背后的库支持
      • 动静态库的命名
      • 静态链接示例
    • 动静态库各自的特征
      • 静态库
      • 动态库
    • 静态库的打包与使用
      • 示例文件
      • 打包
        • 1. 生成目标文件
        • 2. 打包静态库
        • 3. 组织文件
        • 使用 Makefile
      • 使用
        • 方法一:使用选项
        • 方法二:拷贝到系统路径
    • 动态库的打包与使用
      • 打包
        • 1. 生成目标文件
        • 2. 打包动态库
        • 3. 组织文件
        • 使用 Makefile
      • 使用
        • 编译
        • 运行时问题
        • 解决方法
    • 总结


Linux动态库和静态库

在Linux开发中,动态库和静态库是代码复用和程序构建的重要工具。无论是编写小型工具还是大型项目,理解动静态库的原理和使用方法都能极大提高开发效率。本文将从基本原理出发,逐步带您认识动静态库,分析它们的特征,并通过具体示例演示静态库和动态库的打包与使用过程。让我们开始吧!


动静态库的基本原理

可执行程序的生成过程

要理解动静态库的本质,首先需要了解源代码如何变成可执行程序。在Linux下,这一过程分为四个步骤:

  1. 预处理:处理头文件展开、去注释、宏替换和条件编译,生成 .i 文件。例如,#include 会被替换为头文件内容。
  2. 编译:进行词法分析、语法分析、语义分析和符号汇总,将代码翻译成汇编指令,生成 .s 文件。
  3. 汇编:将汇编指令转换为二进制机器码,生成目标文件 .o
  4. 链接:将多个 .o 文件链接起来,生成最终的可执行程序。

假设有五个文件:test1.ctest2.ctest3.ctest4.cmain1.c,要生成可执行程序,我们需要:

  • 分别编译生成 test1.otest2.otest3.otest4.omain1.o
  • 将这些目标文件链接,生成最终程序。

如果另一个项目需要用 test1.ctest4.cmain2.c 生成新程序,过程相同。但如果这些文件频繁复用,每次都重新编译会很麻烦。这时,我们可以将 test1.otest4.o 打包成一个“库”,供不同项目直接链接使用。

动静态库的本质

动静态库本质上是目标文件(.o)的集合,是可执行程序的“半成品”。它们不包含主函数(main),只提供函数或方法的实现,供其他程序调用。库的出现解决了代码复用问题,避免了重复编译的繁琐。


认识动静态库

让我们通过一个简单程序认识动静态库:

#include <stdio.h>

int main()
{
    printf("hello world\n"); // 调用库函数
    return 0;
}

编译并运行:

gcc -o mytest mytest.c
./mytest

输出:hello world

背后的库支持

这个程序能输出 hello world,是因为 gcc 在链接时自动引入了 C 标准库。可以用 ldd 查看依赖:

ldd mytest

输出示例:

linux-vdso.so.1 =>  (0x00007fff5f5ff000)
libc.so.6 => /lib64/libc.so.6 (0x00007f9c8e5b0000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9c8e9b4000)

其中,libc.so.6 是 C 标准动态库的软链接。查看其真实文件:

ls -l /lib64/libc.so.6

输出:libc.so.6 -> libc-2.17.so

进一步检查文件类型:

file /lib64/libc-2.17.so

输出:ELF 64-bit LSB shared object, x86-64, ...

这表明 libc-2.17.so 是一个动态共享库(.so 后缀)。

动静态库的命名

  • Linux
    • 动态库:.so(shared object),如 libc.so.6
    • 静态库:.a(archive),如 libm.a
  • Windows
    • 动态库:.dll(dynamic link library)。
    • 静态库:.lib

库名规则:去掉前缀 lib 和后缀(.so.a)及版本号,剩下的是库名。例如,libc.so.6 的库名是 c

静态链接示例

默认情况下,gcc 使用动态链接。若要静态链接,添加 -static

gcc -o mytest-s mytest.c -static

检查依赖:

ldd mytest-s

输出:not a dynamic executable

静态链接的可执行文件不依赖动态库,体积明显更大:

ls -lh mytest mytest-s

可能输出:

  • mytest:几十 KB(动态链接)。
  • mytest-s:几 MB(静态链接)。

动静态库各自的特征

静态库

  • 链接方式:编译时将库代码复制到可执行文件中。
  • 运行时:不依赖外部库。
  • 优点:独立性强,可单独运行。
  • 缺点:文件体积大;多个程序加载相同静态库时,内存中会有重复代码,浪费资源。

动态库

  • 链接方式:运行时才加载库代码,可执行文件中只包含函数入口地址表。
  • 运行时:由操作系统从磁盘加载到内存,多个程序共享同一份代码。
  • 优点:节省磁盘和内存空间,多个程序共享库时效率高。
  • 缺点:依赖动态库,若库缺失则无法运行。

静态库的打包与使用

示例文件

我们用以下文件演示:

  • add.h
    #pragma once
    extern int my_add(int x, int y);
    
  • add.c
    #include "add.h"
    int my_add(int x, int y) { return x + y; }
    
  • sub.h
    #pragma once
    extern int my_sub(int x, int y);
    
  • sub.c
    #include "sub.h"
    int my_sub(int x, int y) { return x - y; }
    

打包

1. 生成目标文件
gcc -c add.c -o add.o
gcc -c sub.c -o sub.o
  • -c:只编译,不链接,生成 .o 文件。
2. 打包静态库
ar -rc libcal.a add.o sub.o
  • ar:归档工具。
  • -r:替换已有文件。
  • -c:创建新库。
  • libcal.a:生成静态库。

验证:

ar -tv libcal.a

输出示例:

rw-r--r-- 0/0  1234 Mar 19 12:34 2025 add.o
rw-r--r-- 0/0  1234 Mar 19 12:34 2025 sub.o
3. 组织文件
mkdir -p mathlib/include mathlib/lib
mv add.h sub.h mathlib/include/
mv libcal.a mathlib/lib/

目录结构:

mathlib/
├── include/
│   ├── add.h
│   └── sub.h
└── lib/
    └── libcal.a
使用 Makefile
CC = gcc
AR = ar
CFLAGS = -c
TARGET = libcal.a
OBJS = add.o sub.o

all: $(TARGET)
add.o: add.c add.h
	$(CC) $(CFLAGS) add.c -o add.o
sub.o: sub.c sub.h
	$(CC) $(CFLAGS) sub.c -o sub.o
$(TARGET): $(OBJS)
	$(AR) -rc $(TARGET) $(OBJS)
output:
	mkdir -p mathlib/include mathlib/lib
	cp *.h mathlib/include/
	cp $(TARGET) mathlib/lib/
clean:
	rm -f *.o $(TARGET) mathlib -r
.PHONY: all output clean
  • make:生成库。
  • make output:组织文件。

使用

测试程序 main.c

#include <stdio.h>
#include <add.h>

int main()
{
    int x = 20, y = 10;
    int z = my_add(x, y);
    printf("%d + %d = %d\n", x, y, z);
    return 0;
}
方法一:使用选项
gcc main.c -I./mathlib/include -L./mathlib/lib -lcal -o main
  • -I:头文件路径。
  • -L:库文件路径。
  • -l:指定库名(cal)。

运行:

./main

输出:20 + 10 = 30

方法二:拷贝到系统路径
sudo cp mathlib/include/* /usr/include/
sudo cp mathlib/lib/libcal.a /lib64/
gcc main.c -lcal -o main
  • 注意:仍需 -lcal 指定库名。

动态库的打包与使用

打包

1. 生成目标文件
gcc -c -fPIC add.c -o add.o
gcc -c -fPIC sub.c -o sub.o
  • -fPIC:生成位置无关码,动态库必需。
2. 打包动态库
gcc -shared -o libcal.so add.o sub.o
  • -shared:生成共享库。
3. 组织文件
mkdir -p mlib/include mlib/lib
mv add.h sub.h mlib/include/
mv libcal.so mlib/lib/
使用 Makefile
CC = gcc
CFLAGS = -c -fPIC
TARGET = libcal.so
OBJS = add.o sub.o

all: $(TARGET)
add.o: add.c add.h
	$(CC) $(CFLAGS) add.c -o add.o
sub.o: sub.c sub.h
	$(CC) $(CFLAGS) sub.c -o sub.o
$(TARGET): $(OBJS)
	$(CC) -shared -o $(TARGET) $(OBJS)
output:
	mkdir -p mlib/include mlib/lib
	cp *.h mlib/include/
	cp $(TARGET) mlib/lib/
clean:
	rm -f *.o $(TARGET) mlib -r
.PHONY: all output clean

使用

编译
gcc main.c -I./mlib/include -L./mlib/lib -lcal -o main
运行时问题

运行 ./main 可能报错:

./main: error while loading shared libraries: libcal.so: cannot open shared object file

检查依赖:

ldd main

输出:libcal.so => not found

解决方法
  1. 拷贝到系统路径

    sudo cp mlib/lib/libcal.so /lib64/
    ./main
    
  2. 设置 LD_LIBRARY_PATH

    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/user/mlib/lib
    ./main
    
  3. 配置 /etc/ld.so.conf.d/

    echo "/home/user/mlib/lib" > mylib.conf
    sudo mv mylib.conf /etc/ld.so.conf.d/
    sudo ldconfig
    ./main
    
    • ldconfig:更新动态库缓存。

总结

  • 静态库:打包用 ar,生成 .a 文件,编译时嵌入代码,独立性强但体积大。
  • 动态库:用 gcc -shared,生成 .so 文件,运行时加载,节省空间但依赖库文件。
  • 使用差异:静态库无需运行时配置,动态库需确保库路径可访问。

通过本文,您应该已经掌握了Linux下动静态库的原理和实践。希望这些知识能在您的开发中派上用场!


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

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

相关文章

13 IO流:字节流、字符流、缓冲流、文件复制(字节/字符/缓冲区)、字符转换流、打印流、IO框架(黑马Java视频笔记)

文章目录 IO流 >> 读写数据的方案1. 认识IO流1&#xff09;IO流的分类2&#xff09;IO流的体系 2. 文件字节输入流2.1 创建文件字节流对象2.2 读取文件1&#xff09;使用read()方法一个一个字节的读取2&#xff09;使用字节数组读取数据:byte[]3&#xff09;使用字节流读…

靶场(十四)---小白心得思路分享---Extplorer

启程&#xff1a; 开始扫描端口服务&#xff0c;发现什么都没有&#xff0c;果断进行下一步目录扫描 PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 3072 98:4e:5d:e1:e6:97:29:6f:…

逆向中常见的加密算法识别

1、base64及换表 base64主要是将输入的每3字节&#xff08;共24bit&#xff09;按照每六比特分成一组&#xff0c;变成4个小于64的索引值&#xff0c;然后通过一个索引表得到4个可见的字符。 索引表为一个64字节的字符串&#xff0c;如果在代码中发现引用了这个索引表“ABCDEF…

Linux上的`i2c-tools`工具集的编译构建和安装

源码复制到Ubuntu系统中并解压 的i2c-tools工具集的源码百度网盘下载链接&#xff1a; https://pan.baidu.com/s/1XNuMuT1auT1dMzYo3LAFmw?pwdi6xe 终端进入源码目录 cd /home/book/mybuild/i2c-tools-4.2执行编译构建命令 运行下面的命令进行编译构建 make CC${CROSS_COM…

langgraph简单Demo(使用langserve实现外部调用)

前言 这个示例是研究如何使用langserve实现外部调用 接入大模型参考文章&#xff1a;接入阿里云百炼 1、安装依赖 pip install langserve fastapi uvicorn pip install sse_starlette 2、代码实现 from fastapi import FastAPI from langchain_core.messages import HumanM…

折叠树报表

折叠树报表中包含了三种信息: 1.树组织信息-可展开、收拢 2.节点的统计信息(汇总求和) 3.每个节点对应的数据信息 一、准备数据 mysql8 数据库中存在两张表 org和store表。 org表和部分数据如下,其中orgname是组织的名称,codepath是完整的组织代码,seq是每个节点的顺序,可…

Python个人学习笔记(16):模块(os)

四、os模块 主要用于文件夹处理 &#xff08;一&#xff09;文件夹相关 os.makedirs(‘dirname1/dirname2’) &#xff1a;创建文件夹目录&#xff0c;不能重复创建&#xff0c;用的多 代码&#xff1a; os.makedirs(a/b/c)结果&#xff1a; os.removedirs(‘dirname1’)&…

虚拟地址空间(下)进程地址空间(上)

一.关于页表组成 1.权限&#xff08;rwx) 作用&#xff1a;如1.让代码区变成只读的 2.写时拷贝的实现&#xff1a;子进程创建时其页表指向的父进程代码和数据权限都是只读的&#xff0c;子进程试图修改&#xff0c;触发错误&#xff0c;系统开始写时拷贝。 来源&#xff1a;…

【数据集分享】青藏高原两次强震玛多地震和漾濞地震的震源过程

2021年5月21日&#xff0c;5小时内在青藏高原不同区域发生了漾濞6.4级和玛多7.4级强烈地震&#xff0c;表明印度板块和欧亚大陆板块的碰撞汇聚作用下青藏高原持续和频繁的 剧烈构造运动和地震活动。本研究利用地震记录和空间对地观测同震位移资料&#xff08;InSAR&#xff09;…

jmeter环境搭建及使用

Meter 是一个开源的性能测试工具&#xff0c;用于测试静态和动态资源的性能。 1、安装 官网下载&#xff1a; 下载地址&#xff1a;Apache JMeter - Download Apache JMeter 网盘下载&#xff1a; 通过百度网盘分享的文件&#xff1a;apache-jmeter-5.6.3.rar 链接&#x…

Python 鼠标轨迹算法 - 防止游戏检测

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…

yum软件包乾坤大挪移(Yum Package Qiankun Great Migration)

yum软件包乾坤大挪移 背景 由于很多的生产环境是无法连接外网的&#xff0c;因此用yum或者dnf命令来安装软件包常常是一个比较麻烦的事情&#xff0c;原因是很多软件的依赖很复杂&#xff0c;如果要一个个下载、拷贝、再安装&#xff0c;这往往是一个非常繁琐冗杂的过程&…

Java:读取中文,read方法

public static void main(String[] args) throws IOException {FileReader fr new FileReader("C:\\aaa\\a.txt");//字符流的底层也是一个字节一个字节读取的&#xff0c;遇到中文就一次读多个&#xff0c;GBK一次读两个&#xff0c;UTF-8一次读三个字节//idea默认U…

英语词性--数词

文章目录 数词概念数词分词基数词序数词 基数与序数词的区别基变序的规律 数词概念 数词&#xff08;Numerals&#xff09; 是英语中用于表示 数量&#xff08;基数&#xff09;或顺序&#xff08;序数&#xff09; 的词类&#xff0c;通常用于描述数字、计数、顺序等。 例如&…

Linux 一步部署DHCP服务

#!/bin/bash #脚本作者和日期 #author: PEI #date: 20250319 #检查root权限 if [ "$USER" ! "root" ]; then echo "错误&#xff1a;非root用户&#xff0c;权限不足&#xff01;" exit 0 fi #防火墙与高级权限 systemctl stop firewa…

如何打造安全稳定的亚马逊采购测评自养号下单系统?

在当今的电商领域&#xff0c;亚马逊作为全球领先的在线购物平台&#xff0c;其商品种类繁多&#xff0c;用户基数庞大&#xff0c;成为了众多商家和消费者的首选。而对于一些需要进行商品测评或市场调研的用户来说&#xff0c;拥有一个稳定、安全的亚马逊账号体系显得尤为重要…

Pytorch中layernorm实现详解

平时我们在编写神经网络时&#xff0c;经常会用到layernorm这个函数来加快网络的收敛速度。那layernorm到底在哪个维度上进行归一化的呢&#xff1f; 一、问题描述 首先借用知乎上的一张图&#xff0c;原文写的也非常好&#xff0c;大家有空可以去阅读一下&#xff0c;链接放…

基于java的ssm+JSP+MYSQL的高校四六级报名管理系统(含LW+PPT+源码+系统演示视频+安装说明)

作者&#xff1a;计算机搬砖家 开发技术&#xff1a;SpringBoot、php、Python、小程序、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;Java精选实战项…

ns3使用入门_基于ns3.44_Part2_配置模块参数的Configuration 和Attributes

前言 事实上ns3的官方手册很全,相关书籍也是有的,官网先贴在这里: ns-3 | a discrete-event network simulator for internet systemsa discrete-event network simulator for internet systemshttps://www.nsnam.org/相关的脚本介绍也都有一些: ns-3.35_wifi-he-networ…

性能测试过程实时监控分析

性能监控 前言一、查看性能测试结果的3大方式1、GUI界面报告插件2、命令行运行 html报告3、后端监听器接入仪表盘 二、influxDB grafana jmeter测试监控大屏1、原理&#xff1a;2、linux环境中influxDB 安装和配置3、jmerer后端监听器连接influxDB4、linux环境总grafana环境搭…