Linux---动静态库

动静态库的相关概念

  • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
     

我们以如下的几个头文件和源文件作为测试用例

//add.h
#pragma once
#include<stdio.h>
int add(int,int);//add.c
#include "add.h"
int add(int x,int y)
{return x+y;
}//sub.h
#pragma once 
#include<stdio.h>
int sub(int,int);//sub.c
#include"sub.h"
int sub(int x,int y)
{return x-y;
}//mul.h
#pragma once
#include<stdio.h>
int mul(int,int);//mul.c
#include"mul.h"
int mul(int x,int y)
{return x*y;
}

一、为什么要有库?

如果我们要编译TestMain.c这个文件,我们一般的做法是将TestMain.c和其他源文件一起编译链接成一个可执行文件,具体操作如下

(之所以没有在gcc编译选项里带头文件,是因为编译器会在当前目录和指定目录下查找头文件

但是如果有其他的文件也需要生成可执行文件,并且也都包含了上面的几个头文件,那么我们就又需要将add.c  sub.c mul.c 这几个源文件重新进行编译,这样太浪费时间了,所以我们要先将头文件对应的源文件分别编译成为.o文件,然后我们只需要将我们需要的.o文件和我们需要编译的文件进行链接生成可执行文件即可,省去了源文件重复预处理、编译、汇编的过程

具体操作如下

(Makefile的语法不了解的,可以去了解一下,这里简单说一下这个文件的内容:用 .o文件生成All这个目标文件,但是当前目录没有.o文件,所以它会在文件中能不能找到规则推导出需要的.o文件,也就是第四、五行的作用,最后三行是用来删除生成文件的.o文件)

执行的效果如下

然后我们只要将TestMain.c文件编译成.o文件在和其他的.o文件链接即可生成可执行文件

具体操作如下

上面的操作虽然省略了源文件的重复预处理、编译、汇编的过程,但是这样写还是太费劲了,如果有很多的.o文件,我们手敲也很容易出错,所以我们可以将头文件和它们对应的.o文件打包起来,方便我们操作。也就是形成库。

二、如何生成静态库?

生成静态库
[root@localhost linux]# ar -rc libXXX.a xxx.o xxx.o
ar是gnu归档工具,rc表示(replace and create)


查看静态库中的目录列表
[root@localhost linux]# ar -tv libXXX.a
t:列出静态库中的文件
v:verbose 详细信息

所以静态库本质就是将库中的源代码直接翻译成为.o目标二进制文件,然后打包

注意:静态库文件有前缀lib和后缀.a,静态库的文件名是XXX的部分。

演示如下

现在我们只要有头文件,静态库和.c文件就能生成可执行文件,具体操作如下

有人可能对我们用的C语言的库不需要指明路径和名字感到奇怪,具体原因是因为我们写的叫第三方库,gcc不认识,本质是gcc的默认搜索路径中没有我们的库


我们用ldd命令去查看a.out这个文件依赖的动态库时,会发现没有libmymath.a这个库,为什么?因为libmymath.a是静态库,静态库中的内容会被拷贝到可执行文件中,所以可执行文件不需要找到静态库


这里讲一下gcc编译加不加-static选项的区别,加-static表示依赖的库都需要是静态库,否则报错,所以我们这里不加static选项,gcc默认用动态库,如果没有动态库就会选择用静态库,即遵循动态库优先的原则


当然,我们一般还会将头文件和库文件放到目录中,在需要用的时候就直接找这个目录即可

而我们正常所说的配置环境等,就是将该文件的压缩包下载,然后解压,将文件放到相应的系统目录下(比如头文件放到/usr/include,库文件放到/lib64等)然后我们就能正常使用了

当然我们这个写的是测试样例就不将它放到系统路径下了。那么如果不放到系统中,我们该怎么编译生成可执行文件呢?(我们现在要处理的文件如下)

如果我们直接编译就会报错,gcc找不到我们包含的头文件,因为头文件不在当前目录下(注意当前目录仅仅只有我们看到的文件,头文件在mymath_lib目录中,不属于当前目录!!!)

方法一:可以在包含头文件时,可以直接加上头文件文件所在的目录

方法二:把头文件所在路径告诉给编译器,让gcc也去我们给的路径下去找头文件

但是还是编译不通过,因为gcc找不到库文件,我们也需要把链接的库文件也告诉gcc

(如果我们将头文件和静态库安装到系统中,我们就不需要新增头文件和库的搜索路径了)

三、如何生成动态库?

生成动态库
shared:表示生成共享库格式
fPIC:产生位置无关码(position independent code)
库名规则:libxxx.so
示例:

[root@localhost linux]# gcc -fPIC -c sub.c add.c

[root@localhost linux]# gcc -shared -o libmymath.so *.o

通过指令,我们就能将头文件和动态库放到一个目录下

现在我们要处理的文件如下

看着和用静态库进行链接时一样,但是操作上会有所差异

生成可执行文件的语句和静态库一样,但是当我们运行a.out时,程序报错说找不到库,这就很奇怪了,明明在生成可执行文件的时候已经告诉gcc库文件在哪里了,为什么这里说找不到呢?

在回答这个问题之前,我们来回忆一下,动态库和静态库的区别:

  • 用静态库链接生成可执行文件,本质是将静态库拷贝到可执行文件中
  • 用动态库链接生成可执行文件,本质是让可执行文件在执行时去找动态库,从而调用库函数,也就是说动态库和可执行文件要同时被加载到内存
  • 总的还说,无论是动态库,还是静态库,都需要被加载到内存,程序才能执行,只不过动态库需要单独加载,并且当有多个程序运行时,动态库只需要加载一份,而静态库则是被包含在程序中一起被加载了多份

现在我们再回过头去回答一下上面的问题:我们在运行程序时,需要将链接的动态库也加载到内存,而我们刚刚只是告诉 gcc 动态库的路径,但是现在是shell命令行在运行程序,这两个是独立的进程,也就是说 gcc知道路径 != 命令行知道路径 ,所以我们还需要将动态库的路径告诉命令行。下面的结果也能说明这一点

这里提供四种处理方法:

方法一:将头文件和库文件直接拷贝到系统(推荐使用,但不推荐你自己写的库这么用)

(如果要自己测试,记得把头文件和库从系统中删除)

方法二:通过使用软连接,查找动态库

(在运行程序时,系统会在当前目录下找库,所以直接把动态库/软连接放在当前目录也可以)

方法三:使用往环境变量 LD_LIBRARY_PATH 添加路径的方式,让系统找到动态库,(系统除了会在默认路径找库,还会去LD_LIBRARY_PATH这个环境变量包含的路径中去查找)

(当然这只是内存级的修改,当我们重启Linux时,该环境变量就会恢复原样)

方法四:在/etc/ld.so.conf.d/目录下添加配置文件,向配置文件中写入动态库的路径即可

四、动态库加载原理

1、动态链接的程序在运行时,可执行程序和动态库都要被加载到内存

2、程序没有被加载之前,程序内部有地址吗?有的。当程序被编译成二进制目标文件时,程序中的函数名,变量名就会被换成二进制的地址,因为计算机只认识二进制,程序中的变量名,函数名是方便给人看的,一旦要交给机器也就没必要存在了。

=> 编译时,需要对代码进行编址,如何编址?基本遵循虚拟地址空间的规则,注意:虚拟地址空间,不仅仅是OS中的概念,编译器编译的时候,也要按照这样的规则编译程序,这样才能在加载时,形成从磁盘到内存的对应关系,即编址方式和内存管理进程地址的方式相同,方便映射。

编译时的虚拟地址,又称为逻辑地址(采用基地址+偏移量的方式) 这里基地址为0

3、编址有两种:绝对编址,相对编址,动态库采用相对编址。(我们写的可执行程序是绝对编址,因为在虚拟地址空间中代码区的位置是固定的)

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

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

相关文章

1.27马尔科夫链,抽样蒙特卡洛模拟(逆转化方法,接受拒绝矩阵),马尔科夫链蒙特卡洛MCMC,隐马尔科夫(HMM(V算法剪枝优化),NLP)

马尔科夫链 蒙特卡洛法模拟 抽样&#xff0c;逆转换方法 就是说由系统自带的随机函数RANDOM&#xff0c;通过下面这个方法&#xff0c;可以变为对应的随机模拟函数 就是说要实现蒙特卡洛模拟&#xff0c;是要先有一个概率表达式&#xff0c;然后基于这个概率表达式&#xff0…

Java_简单实现无头单向非循环链表_简单实现LinkedList

文章目录 一、ArrayList的优缺点二、链表1.链表的概念及结构2.链表的分类1、单向或者双向2、带头或者不带头3、循环或者非循环 三、实现无头单向非循环链表1.定义接口2.定义MySingleList3.成员1、节点类&#xff08;定义在MySingList类里&#xff09;2、头节点引用 4.打印链表实…

Docker 容器卷

1、概念介绍 如果是CentOS7安全模块会比之前系统版本加强&#xff0c;不安全的会先禁止&#xff0c;所以目录挂载的情况被默认为不安全的行为&#xff0c;在SELinux里面挂载目录被禁止掉了&#xff0c;如果要开启&#xff0c;我们一般使用--privlegedtrue命令&#xff0c;扩大…

Framework - ActivityThread 应用启动UI渲染流程

一、概念 ActivityThread拥有 main(String[] agrs) 方法&#xff0c;作为程序的入口&#xff0c;是应用程序的初始化类。&#xff08;ActivityThread不是主线程&#xff0c;它在 main() 方法中实例化&#xff0c;是运行在主线程中。&#xff09;ApplicationThread是 ActivityT…

MySQL 中 int(1) 和 int(10) 会影响存储的长度吗

一、MySQL 中 int(1) 和 int(10) 在MySQL数据库设计中&#xff0c;经常会遇到 int 类型的字段&#xff0c;并会习惯性的指定长度&#xff0c;比如&#xff1a; int(1) 和int(10)&#xff0c;而一些新手可能会误解它们之间的关系&#xff0c;认为 int(10) 能够存储更多的数据。…

项目开发 多行编辑

问题 项目开发中&#xff0c;如何进行多行编辑 详细问题 笔者使用IDEA&#xff0c;Android Studio进行项目开发时&#xff0c;由于代码冗余&#xff0c;修改过程中若是逐一删除或编辑&#xff0c;效率相对低&#xff0c;如何进行多行删除或编辑 本文将提供IDEA&#xff0c;A…

【深度学习】基于PyTorch架构神经网络学习总结(基础概念基本网络搭建)

神经网络整体架构 类似于人体的神经元 神经网络工作原来为层次结构&#xff0c;一层一层的变换数据。如上述示例有4层&#xff0c;1层输入层、2层隐藏层、1层输出层神经元&#xff1a;数据的量或矩阵的大小&#xff0c;如上述示例中输入层中有三个神经元代表输入数据有3个特征…

C语言中的指针详解

大家好&#xff0c;今天给大家介绍C语言中的指针详解&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 **指针是C语言中的一个重要概念&#xff0c;它提供了一种直接访问内存地址…

某赛通电子文档安全管理系统 UploadFileToCatalog SQL注入漏洞复现

0x01 产品简介 某赛通电子文档安全管理系统(简称:CDG)是一款电子文档安全加密软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产,对电子文档进行全生命周期防护,系统具有透明加密、主动加密、智能…

Redis——事件

Redis服务器是一个事件驱动程序&#xff0c;服务器需要处理以下两种事件&#xff1a; 文件事件(file event)&#xff1a;Redis服务器通过套接字与客户端(或者其他Redis服务器)进行连接&#xff0c;而文件事件就是服务器对套接字操作的抽象(linux下一切皆文件&#xff0c;返回的…

unity角色触摸转向

1、挂载脚本到角色的父物体A上 2 、以屏幕左边的触摸为移动&#xff0c;右边为转向操作 3、加载角色时&#xff0c;将角色的父物体设置为A&#xff0c;须将角色的位置和角度置0 using System; using System.Collections; using System.Collections.Generic; using UnityEngin…

【C++初阶】--入门基础(二)

目录 一.C输出与输入 二.缺省参数 1.概念 2.缺省参数分类 (1) 全缺省参数 (2)半缺省参数 三.函数重载 1.概念 2.C支持函数重载的原理--名字修饰 四.引用 1.概念 2.语法 3.引用的特性 (1)引用在定义时必须初始化 (2)引用时不能改变指向 (3)一个变量…