Linux的基础IO:软硬连接 && 动态库 && 静态库

发布于:2024-05-09 ⋅ 阅读:(25) ⋅ 点赞:(0)

目录

软硬连接

硬链接的作用

静态库

制作静态库

安装自定义静态库

动态库

制作动态库

协助OS查找动态库的五种方法

总结 

动态库加载


软硬连接

创建硬链接指令:ln 目标文件 链接者

创建软链接指令:ln -s 目标文件 链接者

删除链接指令:rm 链接文件名

结论:

  1. 软链接是一个独立的文件,有独立的inode number
  2. 硬链接不是一个独立的文件,与目标文件共享inode

结论:软链接包含目标文件所对应的路径字符串(路径具有唯一性,OS进行路径解析找到对应的文件),等价于windows中的快捷方式

结论:软链接不仅可以链接可执行程序,还可以链接普通文件

现象:删除目标文件仍然可以使用硬链接,硬链接的除了硬链接数改变其余相关属性不变

结论1:建立硬链接就是在指定目录下,添加一个新的文件名和inode number的映射关系

结论2:硬链接数就是文件的磁盘级引用计数,即有多少个文件名字符串通过inode number指向同一个的inode(将inode number抽象理解成指针)

注意事项:

1、当只有i_nlink(新创建的硬链接数)及i_count(文件创建初始的计数)都为0的时候,这个文件才会真正被删除(这也是为什么我们在rm file_target1.txt令i_count归零后因为i_link仍为1所以还可以找到inode number对应的文件的原因) 

2、硬链接就是同一个文件使用了多个别名(对引用的目标文件可进行读写操作),它们共同“指向”同一个文件的数据块

3、定位一个文件只有两种方式:

  1. 通过路径,当然最终也是要通过inode number(软链接可以实现)
  2. 直接找到目标文件的inode number(硬链接可以实现)

4、软硬链接存在的目的就是为了让我们快速找到目标文件

硬链接的作用

结论:

  • 任何一个目录刚开始创建的时候,硬链接的引用计数一定为2
  • 在目录中创建一个子目录则该目录的硬链接数自动+1
  • 一个目录中的所有子/孙子...目录数之和 = A的硬链接数 - 2 

作用1:构建Linux的路径结构,让我们可以使用.和..来进行路径定位

作用2:一般用硬链接做文件备份(前面先删除普通文件后删除硬链接的例子可以证明,此外有些资源官方不可能为用户每个人都拷贝一份,只需要为用户提供一个与该资源的硬链接,多个用户就可以通过该硬连接访问,当然实际情况会比这更复杂)

问题:为什么不能创建硬链接?

 解释:如果可以在lesson23目录下建立一个指向root目录的硬链接,当尝试寻找lesson23目录中的某个.txt文件时,搜索过程中找到了该硬链接,就会重新进入root目录寻找,从而造成路径循环,.和..可以循环的原因是Linux涉及时已经做好了处理,而普通用户在使用时没做特殊处理(比如符号链接、联合挂载等方式)就不能为目录建立硬链接

静态库

基本概念:Linux中.a结尾的库是静态库(自己的笔记本电脑单人独享),.so结尾的是动态库(相当于网吧,多个共享);Winodws中的.dll结尾的是动态库,.lib结尾的是静态库

s查找可执行程序依赖的库指令:ldd 可执行程序名

结论:/lib64/libc.so.6是一个软链接 

制作静态库

前提:头文件是一个方法的使用手册,提供函数声明,.o提供二进制的实现,我们只需要补上一个main函数,调用头文件提供的方法,然后由gcc将所有.o进行连接,最后形成可执行

        如果不想把自己写的底层方法的源代码发给室友防止他抄袭,可以将源代码变为二进制的目标文件后与自己写的底层方法的使用手册发给室友,此时室友只需要将自己设计的main.c文件也变为目标文件.o,然后:就可以经gcc将多个.o文件链接从而得到一个可执行程序

gcc main.o mymath.o mystdio.o myexe

        可是如果要将很多的.o文件发给室友,室友在使用时不小心删除了某一个.o文件那么室友在将所有文件进行gcc的时候就会出错,那么为了防止室友在操作过程中不小心将某个.o文件删除,我们可以在发给室友前将所有.o文件进行打包,然后再将打包后的结果传递给室友

打包.o文件指令:ar -rc 包文件 被打包的文件

  • ar是gnu归档工具,rc表示(replace and create)

查看静态库中的目录列表指令:ar -tv 静态库文件

  • t:列出静态库文件
  • v:列出详细信息

结论:库文件的本质就是将多个.o文件进行打包

原因:提高开发效率,用户不需要管底层的方法实现,可以直接使用

安装自定义静态库

开发者将自己的操作手册和方法实现全部放入一个目录中,并由室友”模拟“下载该目录:

        如果室友想不gcc其它.o文件直接gcc自己写的文件,可以直接将这些文件放入系统的文件中,这样直接gcc main.c就可以生成可执行程序了,存入之后就可以将安装包mylib删除了: 

此时室友main.c所包含的头文件可以从""变为<>了: 

  • #include <> :表示引用标准库头文件,编译器会从系统配置的库环境中去寻找
  • #include "":一般表示用户自己定义使用的头文件,编译器默认会从当前文件夹中寻找,如果找不到,则到系统默认库环境中去寻找
  • linux下C和C++默认库环境路径:/usr/include

实际运行时,gcc/g++编译器并不认识第三方添加到系统文件中的库文件,需要额外添加一个选项-l:gcc main.c -l libmyc.a(两个l相邻),但是此时仍然会编译报错,这是因为libmyc.a去掉前后缀得到的myc才是编译器可以识别的,所以最终的命令是:gcc main.c -l myc,但实际上我们十分不建议将自己编写的库文件放入系统中,建议趁早删除:

sudo rm /usr/include/mysyio.h
sudo rm /usr/lib/libmyc.a

结论:安装库文件就是将库文件放入对应的系统目录中

如果不下载到系统文件中,可以使用指令指明要参与编译的.h和.o文件:

  • 头文件指明路径不需要指明具体头文件名称,是因为在main.c中就有了#include<头文件>,在-I 选项的帮助下gcc会先去指定路径下寻找头文件再去系统文件中寻找,颠倒寻找顺序
  • 如果连自定义头文件的路径都不想指明,可以将main.c文件中的<>换为" "

补充:有static修饰时,强制gcc全部使用静态链接,无static修饰时有动态链接就用动态链接,没有动态链接就用静态链接

动态库

制作动态库

基本概念:动态库的制作也要将头文件和方法文件变为.o文件,但是在gcc -c main.c生成目标文件前还要加上一个-fPIC选项,该选项的意思是产生位置无关码

打包动态库文件指令:gcc  -shared *.o -o libmyc.so

  • 为了防止gcc在尝试将这些.o文件形成可执行程序时报错,需要加上shared表示生成共享库格式,即我们想要让gcc将这些.o文件生成一个动态库而不是可执行程序

同样的可以用下面的之类指定头文件路径、库文件所在路径、库文件名称后运行程序:

gcc main.c -I mylib/include/ -L mylib/lib -l myc 

虽然这样可以编译链接后生成可执行程序,但是执行可执行程序时会报错:

解释:因为我们告诉的是编译器gcc这些文件的路径以及名称,但是在执行可执行程序是由OS进行的,OS还并不知道这些信息,在程序运行时OS要找到并加载动态库到可执行程序中然后才能运行程序(动态库只是说和可执行程序间建立关联并未加载到可执行程序中),而静态库没有这一问题的原因是,在编译期间已经将库中的代码拷贝到我们的可执行程序中,程序的运行就和库没有关系了(编译时告诉gcc的是可执行程序与动态库的依赖关系)

协助OS查找动态库的五种方法

1、直接将动态库放入系统文件中(不建议)

2、建立软链接

3、命令行导入环境变量

LD_LIBRARY_PATH是一个用于指定动态链接库的搜索路径的环境变量

4、修改.bashrc配置文件,使对环境变量的修改得以永久保存(不推荐)

5、/etc/ld.so.conf.d路径下新增动态库搜索的配置文件:ldconfig

后两个具体实现方法在26、20~30分处,节省时间不做过多叙述

使用外部库的操作在26、50分处

总结 

1、如果我们要使用别人的库,别人应该给我们提供一批头文件和一批库文件(.so和.a),我们在使用第三方库文件生成可执行程序时要参考以下格式写:gcc main.c -l 动态库文件名(记得去掉前后缀)

2、gcc在不使用static选项时,默认使用动态库,如果在多个库文件中提供了一个.a且未使用static选项,则gcc静态链接.a,动态链接其它库

3、为保证使用statci选项时不出下图中的报错,我们链接提供的任何库文件都要包含静态库版本

4、库的加载与静态库无关, 静态库在编译时就已经加载到程序中,后续要考虑的就是程序的加载

动态库加载

基本概念:动态库.so、静态库.a和可执行程序本质是都是磁盘上的文件(使用文件就要提供它们的路径)

补充内容:Linux中形成的可执行程序的格式是ELF格式,程序编译完后形成的二进制文件中的二进制内容是有固定格式的,固定格式中一定要有ELF格式的可执行程序的头部,而头部中又会有很多可执行程序的属性 

问题:在编写代码时起的变量名在程序编译后还存在吗?

解释:不在了,都变成了地址

问题:可执行程序编译成功后,加载运行前,它的二进制代码中有“地址”吗?

解释:

结论:可执行程序编译后会形成很多汇编语句,且每条汇编语句都有它的地址,这些地址都是虚拟地址,在平坦模式 + 绝对编址的情况下虚拟地址等价于逻辑地址

问题:如何对这些汇编语句进行编址呢?

解释: 编址方式分为绝对编址和相对编址:

后续具体内容在26、1:30~2:14处,这里可以提供结论:

1、采用动态链接的进程在创建后,会初始化地址空间,等到进程执行时,CPU就会依据地址信息找到main函数的入口

~over~