Linux —— 文件系统概述、软硬链接与动静态库

news2024/11/17 15:32:09

目录

1.文件系统概述

1.1磁盘的基本存储结构

 1.2磁盘的基本逻辑结构

1.3操作系统中的文件系统

 1.4文件系统如何对磁盘进行管理

2.软链接、硬链接

2.1软链接

2.2硬链接

2.3目录的硬链接数

3.静态库和动态库

3.1静态库的制作

3.2静态库的使用

3.3动态库的制作

3.4动态库的使用

 3.5动静态库的加载方式

1.文件系统概述

1.1磁盘的基本存储结构

磁盘是一个机械结构构成的外设,所以访问的速度相对于CPU来说会显得非常慢。

一个磁盘由多个盘片(每个盘片对应多个盘面)和多个磁头(每个盘面对应一个磁头)构成(基本结构)。每一个盘面有很多的同心圆,这些圆圈就是磁道。磁盘寻址的基本单位为扇区(512字节),所以磁盘又被称为块设备文件。每一个磁道的某一段对应一个扇区(即使磁道的周长各不相同,但是数据量却一样)。 

 1.2磁盘的基本逻辑结构

磁盘的物理存储结构并不是线性的,对其进行管理的成本会增加。为了方便,我们把它逻辑抽象为线性结构。

只要知道了扇区的下标,就可以看做成定位了一个扇区。这样的下标操作系统成为逻辑块地址(LBA地址)。 

进行逻辑抽象的原因有两个:

1.便于管理。因为对线性数据结构的管理成本比对非线性数据结构管理的成本低。

2.不想让OS(操作系统)的代码和硬件强耦合。如果没有对磁盘进行逻辑抽象,那么OS的代码可能是只针对磁盘进行管理,但此时将磁盘替换为固态硬盘,那么OS将无法管理固态硬盘。

1.3操作系统中的文件系统

即使磁盘是以扇区(512字节)为IO的基本单位,但是操作系统仍然觉得这样不合适(512字节对于操作系统来说太小了,而且会增加IO次数)。所以OS内的文件系统定制了一套IO方法:每次进行IO时,一次性读取多个扇区,可能一次性读取两个扇区(1kb),也可能读取4个扇区(2kb),主流的一般都是读取8个扇区(4kb)。这样做的原因是为了提高IO效率和局部性原理。

所以此时得出两个结论:

1.操作系统以4kb为单位管理内存,称为页框。

2.磁盘文件以4kb为基本单位分块(特别是可执行文件),称为页帧。

 1.4文件系统如何对磁盘进行管理

 即使把磁盘逻辑抽象成了线性结构,但是直接进行管理的成本还是太高。所以文件系统使用分治思想对磁盘进行管理,即分区、分组管理。

 文件系统对每个分组进行管理。然后将每个分组的管理模式拷贝给其他分组,这样就形成了对分区的管理。最后将每个分区的管理模式拷贝给其他分区,这样就形成对整个磁盘的管理。

在Linux操作系统中,文件=内容+属性,所以Linux在管理文件时,其内容和属性是分批存储的。每一个文件对应一个inode,每一个inode是一个固定大小(128kb或者256kb),每一个inode存储了文件几乎所有的属性(除了文件名),inode为了区分彼此,每一个inode都有一个对应的id([ls -li]查看)。内容是使用data blocks(数据块)存储的,数据块的大小随着应用类型的变化也在变化。

可以看到上图绘制的分组结构,现在来进行解释每一个区域的工作是什么:

  • inode table:保存了分组内部的所有(已用和未用)的inode。
  • data blocks:保存了分组内部所有(已用和未用)的数据块。
  • inode bitmap:分组内部inode对应的位图(比特位为1为已用,0为未用;比特位与当前文件的inode的位置对应:第n个比特位对应第n个inode)。
  • block bitmap:数据块对应的位图结构。
  • GDT(块组描述符表):存储对应分组的宏观数据(有多少个inode、数据块;有多少可用或未用inode、数据块)。

在进行文件的查找时,统一使用inode编号(可以跨组访问但不能跨分区,一个分区对应一套文件系统)。每一个inode都有一个data block blocks数组,存储数据块的编号,其默认大小为15。下标为0~12都是一级索引(一个编号对应一个数据块),下标13~14分别是二级索引和三级索引。二、三级索引和一级索引的区别就在于:一级索引的编号对应一个数据块,二级索引的编号对应一个存储其他数据块编号的数据块,三级索引以此类推。 

删除文件时,不需要将文件的数据直接清楚掉,而是只要把inode bitmap的比特位由1置0即可(让操作系统认为这个inode无人使用)。所以文件被删除时,其数据还是存在的(是可恢复的),所以误删文件之后,什么也不要做! 

为什么我们在自己操作文件时,并未使用inode编号而是直接操作文件名?其原因在于:我们的文件都是保存在目录下面的,目录也是一个文件(所以目录有其对应的inode),其data block存储所有文件的文件名和inode的映射关系,所以我们操作文件时使用的是文件名而不是inode。也就是说,我们只需要知道文件名,操作系统就会帮我们找到其对应的inode(所以同一目录下不能存在同名文件)。

2.软链接、硬链接

2.1软链接

使用如下命令为某一个文件建立软链接:

ln -s 文件名(默认为当前路径) 软链接名称

软链接有一个独立的inode,说明创建软链接即创建了一个新的文件。这个文件保存被链接文件的路径。所以其作用相当于Windows下的快捷方式。

2.2硬链接

使用如下命令为某一个文件建立硬链接:

ln 文件名(默认为当前路径) 硬链接名称

硬链接没有独立的inode,说明创建硬链接时没有创建新文件。硬链接的本质就是新增一个文件名和inode映射关系。

软硬链接的本质区别就是有无独立的inode

 所以对硬链接的修改,也是对原文件的修改(类似于C++的引用)。

注意使用[ll -i]这个命令时权限与拥有者之间的数字,这个数字便是硬链接数。删除文件时,并不是直接销毁这个文件,而是将其硬连接数减1,当硬链接数为0时,文件才被删除。 

2.3目录的硬链接数

让我们创建一个空目录时,其默认硬链接数为2。

因为目录本身是一个文件,占用一个硬链接数;而目录下有一个隐藏的.目录,这个.目录指的是当前目录,所以又占用一个硬链接数。

 Linux不允许普通用户为目录建立硬链接,其原因在于:硬链接目录时,需要将被链接目录下的所有数据都建立硬链接关系,所以这是一项相当大的工程,Linux不允许我们这么做。而能够为目录建立软链接,是因为目录本身就是一个文件,软连接只需要保存目录所在的位置就行了。

3.静态库和动态库

在C++编程时使用STL就是在使用C++给我们提供的库。如果某一天我们想要给其他人提供我们自己写的库,了解静态库和动态库是非常有必要的。

前面的文章感性介绍了静态链接和动态链接。静态链接直接将方法拷贝至程序中,这样使得访问速度快,但是空间占用率高;动态链接需要编译器主动去程序外部寻找方法,这样使得访问速度慢,但是空间占用率低。

库的思想无非就是将自己的目标文件(如果不想给他人透露我们的具体实现细节)和头文件提供给库的使用者。但是这样的作法是简单、直接并且暴力的。效率更高的做法便是:将我们自己所有的.o文件(目标文件)打包成库文件,配合头文件提供给使用者,而打包工具和打包方式的不同决定了库文件是静态库还是动态库。

3.1静态库的制作

我们实现两个头文件:my_add.h和my_sub.h;实现两个源文件:my_add.c和my_sub.c。代码编写完成后,编写makefile将其目标文件和头文件放入一个文件夹当中。注意库文件的昵称格式,一定要有前缀lib和后缀.a(静态库)或.so(动态库)

生成静态库需要使用如下命名:

ar -rc lib*.a *.o...

//my_add.h
#pragma once
#include <stdio.h>
int add(int x,int y);
//my_add.c
#include "my_add.h"

int add(int x,int y)
{
    printf("%d + %d = %d\n",x,y,x+y);
    return x+y;
}
//my_sub.h
#pragma once
#include <stdio.h>
int sub(int x,int y);
//my_sub.c
#include "my_sub.h"

int sub(int x,int y)
{
    printf("%d - %d = %d",x,y,x-y);
    return x-y;
}
//makefile
libmystatic.a:my_add.o my_sub.o		#静态库需要依赖目标文件
	ar -rc $@ $^
my_add.o:my_add.c
	gcc -c my_add.c -o my_add.o
my_sub.o:my_sub.c
	gcc -c my_sub.c -o my_sub.o

.PHONY:output
output:
	mkdir -p mystaticlib/include	
	mkdir -p mystaticlib/lib
	cp -f *.h mystaticlib/include
	cp -f *.a mystaticlib/lib

.PHONY:clean
clean:
	rm -rf *.o libmystatic.a mystaticlib

箭头所指,就是我们制作好的库了。我们可以使用压缩工具将其放在yum上供人下载,也可以直接将此文件夹发给需要使用此库的使用者。

3.2静态库的使用

当使用者拿到制作好的库时,可以有两种方法使用。一是将此文件夹里面的头文件拷贝至/usr/include路径下(系统存储头文件的目录),再将此文件夹里面的库文件拷贝至/lib64路径下(系统存储库文件的目录),此过程也称为安装库;二是在编译自己写的程序时指明头文件、库文件的搜索路径。

主要介绍第二种方法,这里站在使用者的角度来编写一份代码:

//main.c
#include "my_add.h"
#include "my_sub.h"
#include <stdio.h>

int main()
{
    int ret1 = add(3,5);
    int ret2 = sub(7,4);
    return 0;
}

然后我们使用如下命令进行编译:

gcc main.c -o mytest -I(大写的i) ./mystaticlib/include -L ./mystaticlib/lib -l(小写的L) mystatic

 现在解释命令各选项的作用:

  • -I(大写的i):指定头文件的搜索路径。
  • -L:指定库文件的搜索路径。
  • -l(小写的L):指定库文件昵称(去掉前缀和后缀就是库文件的昵称)。

那么对于第一种方法,各位朋友可以自己去尝试。只不过在编译的时候需要指定库文件昵称,因为对于gcc/g++这两款编译器来说,只能认识自己的库。 

3.3动态库的制作

动态库的生成也需要目标文件,但是生成目标文件时需要多加一个选项(产生与位置无关码)。

产生与位置无关码的目标文件的命令:

gcc -fPIC -c *.c -o *.o

生成动态库的命令:

gcc -shared -o lib*.so  *.o...

makefile可以这样编写: 

//makefile
libmydynamic.so:my_add.o my_sub.o		
###生成动态库(使用gcc编译多加一个-shared选项,阻止生成可执行文件)
	gcc -shared -o libmydynamic.so my_add.o my_sub.o	

###生成目标文件(使用gcc编译多加一个-fPIC选项,生成与位置无关码)
my_add.o:my_add.c
	gcc -fPIC -c my_add.c -o my_add.o
my_sub.o:my_sub.c
	gcc -fPIC -c my_sub.c -o my_sub.o

.PHONY:output
output:
	mkdir -p mydynamiclib/include	
	mkdir -p mydynamiclib/lib
	cp -f *.h mydynamiclib/include
	cp -f *.so mydynamiclib/lib

.PHONY:clean
clean:
	rm -rf *.o libmydynamic.so mydynamiclib

 箭头所指,就是我们制作好的库了。我们可以使用压缩工具将其放在yum上供人下载,也可以直接将此文件夹发给需要使用此库的使用者。

3.4动态库的使用

与静态库的使用一样(一样使用main.c文件的代码为例),使用gcc生成可执行文件时操作一样。但是会发现,生成的可执行文件运行不了。其原因在于,使用的是动态库,可执行文件内部就没有动态库的拷贝,所以我们必须让OS和bash知道动态库在哪里

让OS和bash知道动态库在哪里有四种做法:第一种,将动态库的路径写入LD_LIBRARY_PATH的环境变量中,这种方法的弊端是重启时失效(因为环境变量每次登录时自动配置);第二种,与静态库一样,将制作好的库的头文件、库文件暴力安装至系统目录下;第三种,在/etc/ld.so.conf.d/目录下自定义创建一个后缀为.conf的文件,里面写入动态库的存储路径,然后使用ldconfig命令更新配置;第四种,在系统路径下(/lib64)创建自己制作的动态库的软链接。

第一种方法演示: 

第三种方法演示:

 第四种方法演示:

 3.5动静态库的加载方式

静态库直接加载到程序的代码区(直接将静态库拷贝至程序里),加载到内存后形成进程地址空间,当cpu要处理调用静态库实现的方法的指令时,操作系统只能通过地址空间的代码区再通过页表映射到物理内存的代码区。

 静态库的加载是一种绝对编址方式。因为程序里面包含有静态库,所以静态库在程序当中一定有绝对的地址。所以静态链接对内存空间占用极高(当有100个不同的程序都使用了同一个静态库并加载到内存中,那么内存当中就会有100份这个静态库)。

动态库的加载比较复杂,它是一种相对编址方式。因为编译生成可执行文件时没有将动态库拷贝至程序里,所以动态库的地址就不确定。那么程序的代码区存储的便是动态库的具体实现方法(函数)相对于动态库起始地址的偏移地址(假设动态库的起始地址为100,函数的地址为120,那么代码区存储的就是20),动态库的起始地址在动态库加载到内存时才能确定(操作系统有办法计算出动态库未加载到内存时函数的偏移地址)。

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

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

相关文章

年薪50k大佬带你五分钟学会接口自动化测试框架

今天&#xff0c;我们来聊聊接口自动化测试是什么&#xff1f;如何开始&#xff1f;接口自动化测试框架怎么做&#xff1f;自动化测试自动化测试&#xff0c;这几年行业内的热词&#xff0c;也是测试人员进阶的必备技能&#xff0c;更是软件测试未来发展的趋势。特别是在敏捷模…

分布式请求链路跟踪-SpringCloud Sleuth

文章目录1.概述1.1.为什么会出现这个技术&#xff1f; 需要解决哪些问题?1.2.是什么?1.3.如何解决问题?2.搭建链路监控步骤2.1.zipkin2.2.服务提供者2.3.服务消费者&#xff08;调用方&#xff09;2.4.测试1.概述 1.1.为什么会出现这个技术&#xff1f; 需要解决哪些问题?…

力扣刷题记录——1108. IP 地址无效化、1281. 整数的各位积和之差 次数 、1295. 统计位数为偶数的数字、1394. 找出数组中的幸运数

本专栏主要记录力扣的刷题记录&#xff0c;备战蓝桥杯&#xff0c;供复盘和优化算法使用&#xff0c;也希望给大家带来帮助&#xff0c;博主是算法小白&#xff0c;希望各位大佬不要见笑&#xff0c;今天要分享的是——《力扣刷题记录——1108. IP 地址无效化、1281. 整数的各位…

手撕排序算法(一)——插入排序

排序的概念及意义本章内容我们采用C语言完成代码。排序的概念我们先来了解一下基础概念&#xff1a;排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。稳定性&#xff1a;假定在待排序…

cin关闭流同步的利弊与cout的endl使用(超时问题)

重要&#xff1a;1&#xff1a;比如print&#xff0c;scanf&#xff0c;gets()&#xff0c;pus()&#xff0c;getchar()不要与cin&#xff0c;cout共用2&#xff1a;cout中不要使用endl每次使用endl&#xff0c;都要flush缓冲区&#xff0c;造成大量时间耗费。推荐cout <<…

springBoot国际化的一种方式

引言&#xff1a; 当我们的应用面向不同国家用户时&#xff0c;根据不同的locale返回不同的语言信息的国际化功能就显得有必要了。一般来说国际化主要表现在前端用户界面上&#xff0c;在现在前后端分离的背景下&#xff0c;前端页面的国际化交由前端代码独立完成&#xff1b;少…

只用两行代码做个表白二维码,赶快送给你心目中那个她吧♥(๑> ₃ <)♥

上一篇&#xff1a;教你一招完美解决 pptx 库安装失败的问题 今天有同事给我说&#xff1a;女朋友生日快到了&#xff0c;想用Python给她写个表白二维码&#xff0c;然后印在买的衣服上送给她。这么特别的生日礼物&#xff0c;博主还是第一次听到&#xff0c;不得不说&#xff…

Linux-make/Makefile

一、了解make/Makefile对于make/Makefile首先我们需要了解make是一条命令&#xff1b;Makefile是一个文件。make是一个命令&#xff0c;可以执行某条指令。这个我们理解&#xff0c;那Makefile是一个文件&#xff0c;那这个文件是干什么用的呢&#xff1f;这个文件内部一共包含…

SAP中新增销售科目配置分析实例

公司有一批呆滞维修用备件需要卖出&#xff0c;对应在系统内就需要做销售处理。但通常情况下&#xff0c;系统设计时没有考虑和配置备件销信也很正常。所以&#xff0c;处理时可能因为缺少配置做不下去。我需要解决配置问题&#xff0c;并做下可行性的测试。 首先考虑到的是末…

UniRx之ReactiveCommand

前言 UniRx中ReactiveCommand和AsyncReactiveCommand是一种基于IObservable的可控命令机制&#xff0c;用于控制是否允许进程运行 很难用文字说明&#xff0c;下面我们直接看代码吧 ReactiveCommand void Start() {//创建IObservable<bool>ReactiveProperty<bool&g…

分布式事务2种协议 及 4种模式

分布式事务协议 解决分布式事务&#xff0c;也有相应的规范和协议。分布式事务相关的协议有2PC、3PC。 由于三阶段提交协议3PC非常难实现&#xff0c;目前市面主流的分布式事务解决方案都是2PC协议。 2PC&#xff1a;两阶段提交协议 两阶段提交协议&#xff1a;事务管理器分…

如何为客户创建一个简单好用的帮助文档?

产品售后服务难&#xff0c;客服人员压力大&#xff0c;客户不满意。相信这是很多企业都面临的问题&#xff0c;产品是卖出去了&#xff0c;但是做不完的售后&#xff0c;回答不完的重复问题&#xff0c;电话、微信响个不停&#xff0c;售后服务一直都是企业的一个痛点&#xf…

JSR303数据校验和@ControllerAdvice统一异常处理

1.引入依赖&#xff08;springboot2.3之后需要引入&#xff09; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId><version>2.6.6</version> </dependency>…

scrapy总结

一、scrapy是什么&#xff1f;*结构性数据&#xff1a;即同一类型的数据如&#xff1a;某一网页上的同一类型的标签二、scrapy安装pip install scrapy出错提示to update pip&#xff0c;请升级pippython -m pip install --upgrade pip三、scrapy的基本使用&#xff08;爬虫项目…

Python __del__()方法:销毁对象

Python 通过调用 __init__() 方法构造当前类的实例化对象&#xff0c;而本节要学的 __del__() 方法&#xff0c;功能正好和 __init__() 相反&#xff0c;其用来销毁实例化对象。事实上在编写程序时&#xff0c;如果之前创建的类实例化对象后续不再使用&#xff0c;最好在适当位…

Python爬虫-某懂车平台之汽车销量排行榜

前言 本文是该专栏的第33篇,后面会持续分享python爬虫干货知识,记得关注。 之前笔者在本专栏有详细介绍过该平台二手车数据,感兴趣的同学可以在本专栏往前翻阅查找。而本文要介绍的内容,是汽车销量排行数据。 地址:aHR0cHM6Ly93d3cuZG9uZ2NoZWRpLmNvbS9zYWxlcw== (注:地…

哪个牌子台灯对孩子视力好?精选不同价位的学生护眼台灯

在我国&#xff0c;由于科技水平的提高和电子产品的普及&#xff0c;儿童青少年的近视率正逐年攀升&#xff0c;且出现低龄化现象。2020年&#xff0c;我国儿童青少年总体近视率竟高达52.7%&#xff0c;其中6岁儿童已达14.3%&#xff0c;小学生为35.6%&#xff0c;初中生为71.1…

Hue(1): Apache Hue 介绍

1 Hue 是什么 HUEHadoop User Experience Hue 是一个开源的 Apache Hadoop UI 系统&#xff0c;由 Cloudera Desktop 演化而来&#xff0c;最后 Cloudera 公司将其贡献给 Apache 基金会的 Hadoop 社区&#xff0c;它是基于Python Web 框架 Django 实现的。 通过使用 Hue&am…

GAMES101笔记:BRDF和渲染方程

BRDF : 双向反射分布函数(Bidirectional Reflectance Distribution Function) 描述从某个方向入射的能量反射到不同的方向上的能量的分布。 理解反射 从能量的角度理解反射。上图中&#xff0c;ωi\omega_iωi​方向入射的光线具有的Radiance&#xff0c;累积在微小面积dAdAd…

沉浸式 3D 场景下的多视点视频 增强算法研究

沉浸式 3D 场景下的多视点视频 增强算法研究研究内容图像质量增强为什么进行图像质量增强图像有损压缩技术多视点视频中的深度图像特点视点数目增强虚拟视点合成技术视点外推为什么进行视点数目增强主要贡献基于自适应残差网络的多视点压缩深度图像增强算法基于多约束编解码网络…