当前位置: 首页 > news >正文

软硬链接与动静态库

1、软硬链接

(1)软链接

①如何建立

代码:ln -s  原文件名  软链接文件名

②特征

可以发现软链接文件与原文件的inode编号是不同的,这说明两者是两个不同的文件。软链接的内容其实是目标文件所对应的路径字符串,类似于windows下的快捷方式。

③使用场景

在项目打包时,一般分为bin、conf、log等目录,bin目录下一般是存放可执行程序。如果我们要执行bin目录下的可执行程序,那么就要如下写代码:

我们的做法是在proc项目目录下,创建一个软链接:

所以说,软链接其实就是一种快捷方式,其内部存的是目标文件的路径字符串。

(2)硬链接

①如何建立

用代码: ln 目标文件 硬链接文件名 

②特征

观察硬链接和目标文件的inode编号,发现其是一样的,说明硬链接和目标文件是同一个inode编号映射的不同文件名。而在权限属性后有一个数字,表示的是硬链接数。当创建一个硬链接后,目标文件和硬链接文件的硬链接数都变为2了。

③目录文件的硬链接数

硬链接数其实是一个inode内部的引用计数,当该引用计数为0时系统则会删除该文件。当创建一个空目录时,其默认硬链接数是2.(示例为newdir)

这是因为在目录内部,默认会有目录的硬链接".";而在创建新目录后,上级目录的硬链接数会加一,因为新目录内部有上级目录的硬链接“..”。根据该特性,我们可以借此数当前目录的子目录个数=硬链接数-2。

系统禁止我们对目录创建硬链接,目的是为了避免形成路径环绕。(系统的. ..会形成路径环绕,但其本身能够处理,所以不会造成影响)

④使用场景

以上的. ..就是使用场景之一,是为了方便用户进行路径切换;

第二个使用场景就是文件备份。例如在某公司的网盘系统,用户A上传一个视频,该视频被很多用户已经上传过了,那么再上传时,系统就不会再将A上传的视频再下载一份,而是直接在A的网盘系统中用一个硬链接将本来已有的视频备份,这就叫快速上传功能。

2、动静态库

(1)静态库

①本质

静态库本质就是将库中的源代码编译成为.o目标文件,并打包成一个库文件。静态库的在linux系统中的后缀是.a(在windows中后缀是.dll)。静态库在编译时直接被复制到文件中。

②实验

先将多个.c文件编译成.o文件:

再将.o文件打包成静态库:

注意这里的名字取得不好,所以后面进行重命名了,命名为libmyc.a;而在编译时只用中间的myc就行了。

我们的做法是,构建一个mylib的目录,该目录的结构如下:

此时编译就可以通过了:

运行a.out:

注意test.c的代码:

#include"mystdio.h" 
#include "mymath.h"
//双引号表示从当前路径下找//#include<mystdio.h>
//#include <mymath.h>
//尖括号表示头文件从系统中找#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
int main()
{myFILE* fd=my_fopen("log.txt","w");const char* buffer="test text:";my_fwrite(fd,buffer,strlen(buffer));my_fflush(fd);my_fclose(fd);int x=1,y=2;fprintf(stdout,"%d + %d = %d\n",x,y,Add(x,y));return 0;
}

③代码解释

a、-I(大写i) 指定用户自定义的头文件路径;头文件是一个手册,告诉用户怎么使用函数,是函数的声明。包含头文件时用<>表示从系统路径中找;用""则表示从当前路径下找,如果-I不写的话,那么一定要写成""的形式,否则在系统路径中找不到自己下载或书写的库。

b、-L指定用户自定义库文件路径;.o文件提供实现。

c、-l(小写L)指定确定的第三方库名称

为什么平时使用gcc编译时,没有加这些选项?

因为默认gcc是可以认识C标准库的,而我们自己写的或者下载别人的库叫第三方库,gcc默认是不认识的。

用ldd查看可执行程序a.out依赖的库,发现并不是自己打包的静态库,而是系统自带的动态库。原因是因为这些依赖的库都只是动态库,而静态库已经把自己的代码拷贝到可执行程序里面了。这里该可执行程序既依赖了动态库,又依赖了静态库,一般来说,可执行程序依赖的库有动态链接的优先动态链接,如果特定的库只提供静态库会把静态库拷贝到可执行程序,也就是说一个可执行程序依赖的库可以是动静态库混杂的现象。gcc默认是动态链接的,但有些库只提供了静态库,那么就只能静态链接(将静态库中内容拷贝到程序中);有静态库有动态库时。默认动态链接,除非在编译时加上-static 强制静态链接。

④作用

静态库的使用是为了提高开发效率。(动态库也是这个作用)

(2)动态库

①本质

动态库本质也是将库中的源代码编译成为.o目标文件,并打包成一个库文件。动态库的在linux系统中的后缀是.so(在windows中后缀是 .lib)。动态库会在程序运行时加载到内存中。

②实验

编译成.o文件:(要带上fPIC)

将.o文件打包成动态库:

将动态库放入mylib目录下

编译、运行,发现a.out正常编译但无法正常运行,且其依赖的动态库找不到:

③代码解释及动态库的使用方式

解释:上述编译的时候,指定了编译器编译时寻找动态库的路径,所以编译成功了;而动态库是在进程启动后动态加载到内存中的,而在运行时,并未指定寻找动态库的路径,系统则会从默认的搜索路径 /lib64 去寻找, 所以找不到所依赖的动态库。

动态库如何使用?

方法一:将打包形成的动态库安装到系统中(放到系统的 /lib64/ 下)

此时可正常运行:

方法二:在系统路径 /lib64/ 下建立一个指向自己的动态库的软链接

结果a.out还是运行不了,这是因为命名少了一个.so

方法三:更改环境变量LD_LIBARARY_PATH

这种方法是内存级的,重启系统后会丢失该环境变量信息。

方法四:修改.bashrc配置文件,让环境变量永远生效,这里不演示了。

方法五:新增动态库搜索的配置文件 /etc/ld.so.conf.d 

配置文件查看,并新建一个配置文件:

在配置文件中加入路径(这里是/home/zwh/mylib/mylib/lib):

再加载一下

此时重新进入该系统也能正常运行。

3、虚拟地址空间详述与动态库加载

(1)可执行程序与虚拟地址空间

.c等文件在编译成功后形成可执行程序,是ELF格式的,ELF是可执行程序的头部,涵盖了可执行程序的属性,也就是说该格式可以获取可执行程序的各属性。编译后的可执行程序,其内部有很多行汇编语句,每条汇编语句都有他的地址。

编译时设定地址采用的是平坦模式,也就是按照0000...000~FFFF...FFF,从基地址为0开始编址。而这时的地址,可以理解成虚拟地址,(本质是逻辑地址,但含义类似)。这里的编址方式是相对编址。

当可执行程序运行时,系统为可执行程序创建进程。进程=内核树结构+代码与数据。该创建过程,是先建构内核数据结构,再将代码与数据加载到内存中。内核数据结构有 task_struct,虚拟地址空间mm_struct,以及页表。因为可执行程序的格式是ELF,所以很容易通过ELF+加载器,将程序的各部分起始地址、结束地址拿到,用这些地址来建构虚拟地址空间mm_struct,并将页表的虚拟地址部分填好。与此同时,在CPU内部有一个寄存器pc指针,其中存储的是下一次执行代码的虚拟地址,在这个过程中会将main函数的入口地址放入pc指针。

然后将程序的代码和数据加载到内存中,此时就获取到了他们在内存中的物理地址,然后将这些物理地址填入页表。

当cpu开始运行之后,通过pc中的虚拟地址,结合页表找到物理地址,从而开始执行代码。

以上的过程可以看出,虚拟地址空间的概念,不是OS独有的,而是OS、编译器、加载器共有且统一的。

(2)动态库加载

在上面内存中的代码执行时,遇到动态库中的函数时,则会先判断库有没有被加载,如果没有,则会从磁盘中将动态库加载到内存。动态库是.o文件的集合,其内部是已经编译过的代码,也就有其虚拟地址,这里的地址也是相对地址(相对于某个起始值的偏移)。加载到内存之后,也就有了其物理地址。

此时将其虚拟的相对地址,放入虚拟地址空间mm_struct中的共享区部分,此时其在mm_struct中的虚拟地址是起始位置+相对地址(偏移量)。然后将此时的虚拟地址填入页表左侧,而在内存中的真实物理地址填入右侧。在可执行程序的代码中,调用动态库中的函数,就是通过偏移量call到共享区里去寻找对应的方法,然后当函数执行完返回的时候又会回到代码区执行后续代码。这样的函数调用就是代码区与共享区指令来回执行的过程。

所以在编译动态库时,我们加上-fPIC选项,其中fPIC叫做位置无关码。

系统是如何判断动态库是否加载进来的?

对每个加载在内存中的动态库先描述再组织,形成一个个的结构体,这些结构体彼此相连形成链表;判断需要的动态库是否需要加载只要查询这个链表就可以做到。


http://www.mrgr.cn/news/66303.html

相关文章:

  • 单元测试(Junit)
  • Selective Generation for Language Models 语言模型的选择性生成
  • Python设计模式探究:单例模式实现及应用解析
  • Elasticsearch 实战应用详解!
  • 进度条的实现(配合make和makefile超详细)
  • Css-常用指令大全
  • 无需懂代码!用AI工具Bolt一键生成网站的入门指南!
  • RTX 50很快就能见面!3个月内 全家登场
  • 基于 JAVASSM(Java + Spring + Spring MVC + MyBatis)框架开发一个医院挂号系统
  • 90%会展主办方都会用的6款数字化工具
  • 基于 JAVASSM(Java + Spring + Spring MVC + MyBatis)框架开发一个九宫格日志系统
  • Flutter 正在切换成 Monorepo 和支持 workspaces
  • 【Classifier Guidance/Classifier-free Guidance】理论推导与代码实现
  • 『VUE』19. scope避免组件之间样式互相覆盖(详细图文注释)
  • MATLAB - ROS 2 分析器
  • 欢迎使用Markdown编辑器
  • GaussDB高智能--库内AI引擎:模型管理数据集管理
  • 省级-社会保障水平数据(2007-2022年)
  • 视频编辑学习笔记
  • “大跳水”的全新奥迪A3,精准狙击年轻人的心
  • 【NOIP普及组】明明的随机数
  • 华为HarmonyOS借助AR引擎帮助应用实现虚拟与现实交互的能力3-获取设备位姿
  • 腾讯混元宣布大语言模型和3D模型正式开源
  • 外包干了6年,技术退步明显.......
  • 小张求职记五
  • C++【string类,模拟实现string类】