Linux(七) 动静态库

news2024/11/23 7:52:20

目录

一、动静态库的概念

二、静态库的打包与使用

2.1 静态库的打包

2.2 静态库的使用

三、动态库的打包与使用

3.1 动态库的打包

3.2 动态库的使用

3.3 运行动态库的四种方法

四、总makefile


一、动静态库的概念

静态库: Linux下,以.a为后缀的文件。程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。本质是在编译时把静态库中的代码(不是一次性加载,而是分页加载)复制到了进程的的代码区中。
动态库: Linux下,以.so为后缀的文件。程序在运行的时候才去链接动态库的代码,在可执行程序装载或运行时,由操作系统的装载程序加载库,多个程序共享使用库的代码。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。
静态链接: 将库中的相关代码复制进可执行程序中的过程。
动态链接: 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中。
链接的本质: 使.o文件可以找到要调用的函数的位置
库文件名称: 比如libc.so,去掉前缀lib和后缀.so,剩下的就是库名。libhello.a的库名就是hello。

头文件gcc的默认搜索路径是 /usr/include
库文件的默认搜索路径是 /usr/lib64

把文件拷贝到系统的默认路径下就叫做库的安装,拷贝之后就不用 -I 和 -L了


实例演示: 分别使用静态链接和动态链接编译生成两个可执行程序,比较两个程序的大小
使用gcc静态链接编译时,命令要带上**-static** 选项,如下:

gcc -o test test.c -static

 

可以看到的是,使用静态库静态链接成的可执行程序比动态链接生成的可执行程序要大很多。
我们还可以通过file命令查看文件的链接属性:
 还可以通过ldd 命令查看可执行程序的依赖库,动态链接生成的可执行程序才有依赖库,静态链接升序的可执行程序不依赖任何库文件,因为库文件的代码已经复制进可执行程序了。

因为这里是动态链接,不仅要让编译器动态库的路径,还要让操作系统知道,所以这里我们需要导入一个环境变量LD_LIBRARY_PATH,如下:

export LD_LIBRARY_PATH=/home/dgz/linux/lesson26/lib/uselib/output/lib

执行完之后

总结动静态库的优缺点
静态库

  • 优点: 程序运行的时候将不再需要静态库,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快。
  • 缺点:一是浪费空间,因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,如多个程序中都调用了printf()函数,则这多个程序中都含有printf.o,所以同一个目标文件都在内存存在多个副本;另一方面就是更新比较困难,因为每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。

动态库

  • 优点: 动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间.动态链接的优点显而易见,就是即使需要每个程序都依赖同一个库,但是该库不会像静态链接那样在内存中存在多分,副本,而是这多个程序在执行时共享同一份副本;另一个优点是,更新也比较方便,更新时只需要替换原来的目标文件,而无需将所有的程序再重新链接一遍。当程序下一次运行时,新版本的目标文件会被自动加载到内存并且链接起来,程序就完成了升级的目标
  • 缺点: 程序运行的时候依赖动态库,  据估算,动态链接和静态链接相比,性能损失大约在5%以下。经过实践证明,这点性能损失用来换区程序在空间上的节省和程序构建和升级时的灵活性是值得的。

二、静态库的打包与使用

2.1 静态库的打包

静态库打包: 本质其实就是将代码编译成.o的二进制文件,然后进行打包。
为了更好地演示这个过程,我创建了mymath.cmymath.hmyprint.cmyprint.h四个文件,内容分别如下:
mymath.c
 

#include "mymath.h"
int Add_(int a,int b)
{
    return a+b;
}


mymath.h
 

#pragma once
#include <stdio.h>

extern int Add_(int a,int b);


myprint.c
 

#include "myprint.h"
void printf_(const char* str)
{
    printf("hello %s [%d]\n",str,(int)time(NULL));
}


myprint.h

#pragma once 
#include <stdio.h>
#include <time.h>

extern void printf_(const char* str);

如下: 

打包静态库的步骤

  1. 先将myadd.cmysub.c 变成生成对应的二进制文件
  2. 使用ar 归档工具对两个二进制文件进行打包,同时带上选项**-rc**(r和c分别代表replace和creat),这里的库名是hello。
     

    ar -rc libhello.a *.o

  3. 上面这两个步骤其实就把静态库打包好了,下面我们还有做一个工作就是发布静态库,简单地说,就是把头文件和静态库组织起来,头文件放在include 下,如下:

  4. 这样一个库文件就可以给别人使用了。
    上面的所有步骤我们可以写进Makefile里,利用make指令一键打包和make output发布,如下:

    libhello.a:myprint.o mymath.o
    	ar -rc libhello.a myprint.o mymath.o
    mymath.o:mymath.c
    	gcc -c mymath.c -o mymath.o
    myprint.o:myprint.c
    	gcc -c myprint.c -o myprint.o
    
    .PHONY:output
    output:
    	mkdir -p output/lib 
    	mkdir -p output/include
    	cp -rf *.h output/include
    	cp -rf *.a output/lib
    .PHONY:clean
    clean:
    	rm -rf *.o *.a output

2.2 静态库的使用

先把静态库放到一个测试目录下:

然后编写一段代码:
 

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

int main()
{
   int ret = Add_(1,2); 
    printf("%d\n",ret);
    printf_("dgz");
    return 0;
}

编写Makefile:
使用gcc编译时,采用静态链接编译,所以要带上选项**-static**,此外,因为我们使用了别人给的静态库,所以我们还有告诉编译器库文件所在路径,头文件所在路径以及库名,所以要用到以下三个选项:

  • -L: 指明库文件所在路径
  • -I: 指明头文件所在路径
  • -l: 指明库文件名称,这里库名就是hello(去掉前缀lib和后缀.a)

这里我们可以使用绝对路径,使用下面的shell命令获取当前所在路径:
 

path=$(shell pwd)

Makefile编写后如下:

path=$(shell pwd)

mytest:test.c
	#-l 指定库目录名称 -L 库目录路径 -I 指定头文件路径
	gcc -o $@ $^ -I $(path)/output/include -L $(path)/output/lib -l hello -static 
.PHONY:clean
clean:
	rm -f mytest

 

使用file指令查看链接属性:

三、动态库的打包与使用

3.1 动态库的打包

我们同样还是使用上面的那四个文件进行演示。
步骤:

  1. 先将mymath.cmyprint.c 变成生成对应的二进制文件。注意这里生成的二进制文件要带上选项**-fPIC**,产生路径无关码,也就是这里使用相对地址,是动态确定的,不存在绝对地址
    gcc -c -fPIC mymath.c -o mymath_d.o
    gcc -c -fPIC myprint.c -o myprint_d.o
  2. 使用gcc带上选项**-shared** (生成共享的库格式)对二进制文件进行打包
    	gcc -shared myprint_d.o mymath_d.o -o libhello.so
  3. 最后一步就是对动态库进行发布,也就是将库文件和头文件进行组织打包
  4. 编写Makefile:
    
    
    libhello.so:myprint_d.o mymath_d.o
    	gcc -shared myprint_d.o mymath_d.o -o libhello.so
    mymath_d.o:mymath.c
    	gcc -c -fPIC mymath.c -o mymath_d.o
    myprint_d.o:myprint.c
    	gcc -c -fPIC myprint.c -o myprint_d.o
    
    
    
    .PHONY:output
    output:
    	mkdir -p output/lib 
    	mkdir -p output/include
    	cp -rf *.h output/include
    	cp -rf *.so output/lib 
    .PHONY:clean
    clean:
    	rm -rf *.o *.so output
    

3.2 动态库的使用

先把动态库放到测试目录下:

然后编写Makefile,和静态库的使用类似:
 

path=$(shell pwd)

mytest_d:test.c
	#-l 指定库目录名称 -L 库目录路径 -I 指定头文件路径
	gcc -o $@ $^ -I $(path)/output/include -L $(path)/output/lib -l hello 
.PHONY:clean
clean:
	rm -f mytest_d

 编译程序: 如果此时直接对程序进行编译,不会报错,但是执行的时候会报错,无法打开共享库里面的文件

 因为这里是动态链接,不仅要让编译器动态库的路径,还要让操作系统知道,所以这里我们需要导入一个环境变量LD_LIBRARY_PATH,如下(上面已经说过,这里重复一次)

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/dgz/linux/lesson26/lib/uselib/output/lib

 此时再执行程序就不会报错了:

使用file指令查看程序的链接属性:

使用ldd指令查看程序依赖的库: 

3.3 运行动态库的四种方法

  1. 将对应到.so和.h文件拷贝到/usr/lib64和/usr/include
  2. 系统在在搜索头文件时会先在系统默认路径下搜索(上面两个路径),如果没找到,但是环境变量LD_LIBRARY_PATH设置了,也会在该环境变量下搜索,这种方法是内存级的,退出就没有了。
  3. 修改配置文件,配置/etc/ld.so.conf.d/
    路径下原有的文件

    我们想要配置它只需要在该路径下创建一个.conf文件

    然后将我们要配置的路径写进该文件

    最后执行一下
    ldconfig
  4. 建立软连接
     建立成功,然后就可以找到了。

四、总makefile

.PHONY:all
all:libhello.so libhello.a

libhello.so:myprint_d.o mymath_d.o
	gcc -shared myprint_d.o mymath_d.o -o libhello.so
mymath_d.o:mymath.c
	gcc -c -fPIC mymath.c -o mymath_d.o
myprint_d.o:myprint.c
	gcc -c -fPIC myprint.c -o myprint_d.o

libhello.a:myprint.o mymath.o
	ar -rc libhello.a myprint.o mymath.o
mymath.o:mymath.c
	gcc -c mymath.c -o mymath.o
myprint.o:myprint.c
	gcc -c myprint.c -o myprint.o
# 发布
.PHONY:output
output:
	mkdir -p output/lib 
	mkdir -p output/include
	cp -rf *.h output/include
	cp -rf *.a output/lib
	cp -rf *.so output/lib 
.PHONY:clean
clean:
	rm -rf *.o *.a *.so output


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

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

相关文章

Python——IO编程

IO在计算机中指Input/Output&#xff0c;也就是输入和输出。由于程序和运行时数据是在内存中驻留&#xff0c;由CPU这个超快的计算核心来执行&#xff0c;涉及到数据交换的地方&#xff0c;通常是磁盘、网络等&#xff0c;就需要IO接口。 比如你打开浏览器&#xff0c;访问新浪…

蓝桥杯 EDA 组 历届国赛真题解析

一、2021年国赛真题 1.1 CN3767 太阳能充电电路 CN3767 是具有太阳能电池最大功率点跟踪功能的 4A&#xff0c;12V 铅酸电池充电管理集成电路。 最大功率点应指的是电池板的输出电压&#xff0c;跟踪电压其做保护。当然 CN3767 也可以直接使用直流充电&#xff0c;具体可以阅读…

污水处理环保设备厂商怎么选

在选择污水处理环保设备厂商时&#xff0c;需要综合考虑多个因素来确保选取的供应商能够提供高质量的设备和服务。以下是一些主要的考虑因素&#xff1a; 企业资质和认证&#xff1a;首先检查供应商是否拥有相关的资质证书和行业认证&#xff0c;例如ISO 9001质量管理体系认证、…

0基础安装 composer

解决&#xff1a; composer 不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件。 php composer.phar可以运行 安装环境&#xff1a;系统w11 官网地址&#xff1a;Composer 1.安装composer 1.1打开命令行窗口 在命令行窗口里&#xff0c;右键是粘贴&#xff0…

Java | Leetcode Java题解之第92题反转链表II

题目&#xff1a; 题解&#xff1a; class Solution {public ListNode reverseBetween(ListNode head, int left, int right) {// 设置 dummyNode 是这一类问题的一般做法ListNode dummyNode new ListNode(-1);dummyNode.next head;ListNode pre dummyNode;for (int i 0; …

【数组的度】leetcode,python

一种很菜的做法&#xff08;暴力&#xff09;&#xff0c;for循环&#xff08;样例能过一大半呢&#xff0c;复杂度的话。。。&#xff09; class Solution:def findShortestSubArray(self, nums: List[int]) -> int:nlen(nums)if n1:return nmx1#出现次数最多的计算for i …

Google:站长移除无效网址

当您的网址不需要呈现在Google站长中时&#xff0c;您可以在站长工具中移除网址 操作步骤&#xff1a;登录Google站长&#xff0c;绑定网站完成后&#xff0c;点击左侧删除 >> 输入网址 如果遇到一些网址&#xff0c;可以找寻网址间的规律&#xff0c;比如说&#xff0…

【oracle】图片转为字节、base64编码等形式批量插入oracle数据库并查询

1.熟悉、梳理、总结下Oracle相关知识体系 2.欢迎批评指正&#xff0c;跪谢一键三连&#xff01; 资源下载&#xff1a; oci.dll、oraocci11.dll、oraociei11.dll3个资源文件资源下载&#xff1a; Instant Client Setup.exe资源下载&#xff1a; oci.dll、oraocci11.dll、oraoc…

java代码混淆工具ProGuard混淆插件

java代码混淆工具ProGuard混淆插件 介绍 ProGuard是一个纯java编写的混淆工具&#xff0c;有客户端跟jar包两种使用方式。可以将程序打包为jar&#xff0c;然后用工具进行混淆&#xff0c;也可以在maven中导入ProGuard的插件&#xff0c;对代码进行混淆。 大家都知道 java代…

【SQL】SQL常见面试题总结(1)

目录 1、检索数据1.1、从 Customers 表中检索所有的 ID1.2、检索并列出已订购产品的清单1.2、检索所有列 2、排序检索数据2.1、检索顾客名称并且排序2.2、对顾客 ID 和日期排序2.3、按照数量和价格排序2.4、检查 SQL 语句 3、过滤数据3.1、返回固定价格的产品3.2、返回产品并且…

基于Vue和uni-app的增强型单选ccRadioView组件开发

标题&#xff1a;基于Vue和uni-app的增强单选组件ccRadioView的设计与实现 摘要&#xff1a;本文将详细介绍如何使用Vue和uni-app构建一个简单、好用且通用的单选框组件ccRadioView。该组件提供了单选列表的功能&#xff0c;并支持反向传值&#xff0c;方便开发者快速实现单选…

新人学习笔记值(初始JavaScript)

一、Java Script是什么 1.Java Script是世界上最流行的语言之一&#xff0c;是一种运行在客户端的脚本语言&#xff08;script是脚本的意思&#xff09; 2.脚本语言&#xff1a;不需要编译&#xff0c;运行过程中由js解释器&#xff08;js引擎&#xff09;进行解释并运行 3.现在…

如何同步管理1000个设备的VLAN数据?

什么是VLAN&#xff1f; VLAN&#xff0c;也就是虚拟局域网&#xff0c;是通过为子网提供数据链路连接来抽象出局域网的概念。在企业网中&#xff0c;一个企业级交换机一般是24口或者是48口&#xff0c;连接这些接口的终端在物理上形成一个广播域。广播域过大&#xff0c;就会导…

docker镜像中搭建FastDfs

docker镜像中搭建FastDfs 一、搭建过程二、docker端口映射三、映射的方法三、配置Tracker 和 Storage 环境&#xff1a;腾讯云服务器上 ubuntu20.04镜像 一、搭建过程 正常直接在云服务器上搭建过程参考博客&#xff1a; https://blog.csdn.net/qq_38531706/article/details/…

基于51单片机的AD/DA转换的串口通信proteus仿真(附源码)

文章目录 一、前言二、PCF85911.介绍2.原理图3.引脚介绍 三、仿真图1.未仿真时2.仿真时 四、仿真程序main.cIIC.c 五、总结 一、前言 AT89C52是一款经典的8051系列单片机&#xff0c;它通常不包含内置的模数转换器&#xff08;ADC&#xff09;或数字模拟转换器&#xff08;DAC…

模版方法详解

模板方法模式 1 概述 在面向对象程序设计过程中&#xff0c;程序员常常会遇到这种情况&#xff1a;设计一个系统时知道了算法所需的关键步骤&#xff0c;而且确定了这些步骤的执行顺序&#xff0c;但某些步骤的具体实现还未知&#xff0c;或者说某些步骤的实现与具体的环境相…

DS高阶:跳表

一、skiplist 1.1 skiplist的概念 skiplist本质上也是一种查找结构&#xff0c;用于解决算法中的查找问题&#xff0c;跟平衡搜索树和哈希表的价值是一样的&#xff0c;可以作为key或者key/value的查找模型。skiplist是由William Pugh发明的&#xff0c;最早出现于他在1990年发…

SOLIDWORKS科研版的优势

随着科技的不断进步&#xff0c;科研领域对于快捷、准确和可视化的需求也在不断增长。在这个背景下&#xff0c;SOLIDWORKS科研版应运而生&#xff0c;为科研人员提供了一款强大的工具&#xff0c;帮助他们解决复杂的问题&#xff0c;提高研究效率。 首先&#xff0c;SOLIDWOR…

贷款借钱平台 贷款源码 小额贷款系统 卡卡贷源码 小额贷款源码 贷款平台

贷款平台源码/卡卡贷源码/小贷源码/完美版 &#xff0c; 数据库替换application/database.php 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/89268533 更多资源下载&#xff1a;关注我。

镜像抑制和镜像衰减有什么不同

在很多无线产品接收机手册中&#xff0c;我们会看到两个参数&#xff0c;一个是镜像抑制&#xff08;Image Rejection&#xff09;&#xff0c;另一个是镜像衰减&#xff08;Image Attention&#xff09;&#xff0c;但这两者究竟有什么不同&#xff0c;一直比较疑惑&#xff0…