【Linux】基础IO —— 深入理解文件系统 | 软硬链接

news2025/4/16 14:19:39

🌈欢迎来到Linux专栏~~ 深入理解文件系统


  • (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort
  • 目前状态:大三非科班啃C++中
  • 🌍博客主页:张小姐的猫~江湖背景
  • 快上车🚘,握好方向盘跟我有一起打天下嘞!
  • 送给自己的一句鸡汤🤔:
  • 🔥真正的大师永远怀着一颗学徒的心
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏
  • 🎉🎉欢迎持续关注!
    在这里插入图片描述

请添加图片描述

深入理解文件系统

  • 🌈欢迎来到Linux专栏~~ 深入理解文件系统
    • 一. 答疑解惑
      • 🌈close关闭fd之后文件内部没有数据
      • 🌈stdout 和 stderr 区别
    • 二 . 理解文件系统
      • 🎨科普背景知识
      • 🎨了解磁盘结构
        • 🔵物理结构
        • 🟡磁盘的存储结构
        • 🟢磁盘的虚拟结构
      • 🎨文件系统与inode
    • 三 . 软硬链接
      • 🌏软链接
      • 🌏硬链接
  • 📢写在最后

请添加图片描述

一. 答疑解惑

🌈close关闭fd之后文件内部没有数据

代码展示:

#include<stdio.h>                                                           
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
 
int main()
{
	close(1);
	int fd  = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
	if(fd < 0)
	{
	   perror("open");
	   return 0;
	}

	printf("hello world: %d\n", fd);//stdout -> 1
                                                                                            
 	close(fd);
	return 0;
 }

在这里插入图片描述
发现运行和打印都没有打印出结果,而当我们fflush的时候才在文件中打印出来

在这里插入图片描述
看了上两篇博客的都应该游刃有余了

因为close(1),而printf()只向1中打印,此时的fd = 1。此时数据会暂存在stdout的缓冲区中,此时普通文件是全缓冲,遇到\n不会及时刷新。对应 的fd先关闭, 数据无法及时刷新了,而加了fflush会把数据直接刷新

🌈stdout 和 stderr 区别

#include<iostream>                                                                           
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>

 int main()
 {
    //stdout -> 1
    printf("hello printf 1\n");
    fprintf(stdout, "hello fprintf 1\n");                                                        

    // stderr -> 2
    perror("hello perror 2"); //stderr

	const char *s1 = "hello write 1\n";
	write(1, s1, strlen(s1));

    const char *s2 = "hello write 2\n";
	write(1, s2, strlen(s1));

    //cout -> 1
	std::cout << "hello cout 1" << std::endl;
	//cout -> 2
	std::cerr << "hello cerr 2" << std::endl;

	return 0}

结果如下~

在这里插入图片描述

  • 我们发现,重定向只对1号文件描述符有用;也就是正确信息被重定向
  • 文件描述符 1 和 2 对应的都是显示器文件,但是题目两个是不同的,如同认为同一个显示器文件,被打开了两次

在这里插入图片描述

一般而言,如果程序运行有可能有问题的话,建议使用stderr,或者cerr来打印
如果是常规的文本内容,我们建议进行cout 或 stdout 进行打印

💦举例:常规信息和报错信息分开打印
正确的打印到ok.txt ,错误的打印到err.txt 也称错误重定向

./myfile 1 > log.txt 2 > err.txt

在这里插入图片描述

💦但如果我们像都打印在一个文件中呢?

./myfile > log.txt 2>&1

💢先重定向,指向log.txt;把1的内容给2拷贝一份,最后1和2指向同一个文件

在这里插入图片描述结果还真的是
在这里插入图片描述
还有一个骚操作也可以实现

cat < log.txt > back.txt//相当于拷贝

我们一个都见过这个errno错误码)的吧

  #include <errno.h>
  
  RETURN VALUE
       open() and creat() return the new file descriptor, or -1 if an error occurred (in which
       case, errno is set appropriately).

如果打开成功,返回文件描述符,打开失败,errno被设置,所以perror会根据全局的错误码输出对应的错误原因

如果我们自己想设计一个perror

 void myperror(const char *msg)
 {
 	 fprintf(stderr, "%s: %s\n", msg, strerror(errno));
 } 

此前已经删除了log.txt
在这里插入图片描述

二 . 理解文件系统

🎨科普背景知识

🥑有没有没有被打开的文件呢? 在哪里呢?

  • 当然存在,在磁盘上

🥑我们学习磁盘级别的文件,我们的侧重点在哪?

  1. 单个文件角度 —— 这个文件在哪里?这个文件多大?这个文件的其他属性是什么?
  2. 站在系统角度 ——一共有多少个文件?各自属性在哪里?如何快速找到?还可以存储多少个文件

接下来我们来正式了解一下磁盘吧

  1. 内存 - 掉电易失存储介质
  2. 磁盘 - 永久性存储介质 还有: SSD(固态 贵!) 、U盘、flash卡、光盘、磁带

🎨了解磁盘结构

🔵物理结构

磁盘是一个外设 且是我们计算机中唯一的一个机械设备(相对而言很慢)

这个磁盘的盘片就像光盘一样,数据就在盘片上放着,只不过光盘是只读的,磁盘是可读可写的

光说有点抽象的,直接上图

在这里插入图片描述

盘面上要存储数据!(二进制) -> 但计算机只认识二进制 -> 二进制是两态 —> 我们想到磁铁也是两态的

  • 磁盘写入本质就是改变磁盘的正负性(磁头)

🟡磁盘的存储结构

机械硬盘的寻址的工作方式:盘片不断旋转,磁头不断摆动,定位到特定的扇区(面 — 磁道 — 扇区)
通过柱面Cylinder —— 磁头Head —— 扇区Sector 的寻址方法为CHS寻址

扇区的大小:512字节是硬件上的要求(外磁道和内磁道都是一样大小,密度不一样)

在这里插入图片描述

🟢磁盘的虚拟结构

类比磁带,我们可以把磁盘盘片想象成线性结构。

站在OS角度,我们就认为磁盘是线性结构,要访问某一扇区,就要定位数组下标LBA(logic block address);要写到物理磁盘上,就要把LBA地址转化成磁盘的三维地址(磁头,磁道,扇区)。这种关系类似于我们之前的虚拟地址空间和物理内存

在这里插入图片描述

所以:找到特定扇区的位置 ——> 找到数组特定的位置;对磁盘的管理——> 对数组的管理

🎨文件系统与inode

文件在磁盘上是如何被保存的?文件是在磁盘中的,而磁盘现在被我们想象成一个线性结构。

💜 磁盘空间很大,管理成本高。我们采用分治思维,类比管理我们的国家,我们分成好几个省份,再分成好几个市,最后轮到区。因此我们就对大磁盘 ——

1️⃣ 分区:大磁盘 → 小空间,化整为零

​ 2️⃣ 格式化:给每个分区写入文件系统

所以理论上,我能把这100G的小空间管理好,其他空间就复刻我就好啦,因为硬件都是标品,当然了不同分区也可以写入不同的文件系统

在这里插入图片描述

  • boot block 存在于每个分区的开头,备份文件,是与启动相关的,供启动时查找分区
  • 我们再把剩下的空间继续拆解分组,Block group 0 ,Block group 1 … 那么问题就又变成了如果我能管理好Block Group 0,就能管好1~n这些,因此研究文件系统又缩小范围了,就变成研究这一个Block Group 0

话不多说开始吧

虽然磁盘的基本单位是扇区(512字节),但是操作系统(文件系统)和磁盘进行IO的基本单位是4KB(8*512字节),4KBblock大小,所以磁盘被称为块设备

哪怕我们只想在磁盘上读取1字节,OS也必须直接读取4KB的数据
原因有两个:

  1. 512字节太小了,有可能导致多次IO,进而导致效率的降低
  2. 解耦合:如果操作系统使用和磁盘一样的大小,万一磁盘基本大小改变了,OS的源代码也要不要跟着改呢?硬件(磁盘)和软件(OS)进行解耦

逐个解释:
  1. Super Block:文件系统的属性信息,整个分区属性的属性集(每个块组都有 防止磁盘被刮伤而找不到 文件属性)
  2. Data blocks:多个4KB(扇区*8)大小的集合,保存的都是特定文件的内容
  3. inode Table:inode是一个大小为128字节的空间,保存的是对应文件的属性,该块组内,所有文件的inode空间的集合,需要标识唯一性,每一个inode块都要有一个inode编号!(一般而言,一个文件,一个inode,一个inode编号)
  4. BlockBitmap(位图):统计block的使用情况。假设有10000+个blocks,就有一一对应的比特位。其中比特位为1,代表该block被占用,否则表示可用
  5. inode Bitmap统计inode的使用情况,假设有10000+个inode,就有一一对应的比特位。其中比特位为1,代表该inode被占用,否则表示可用
  6. GDT:块组描述符,已经使用了多少,有多少个inode,已经被占用了多少个,还剩下多少个,使用了多少

💥众所周知,文件 = 文件内容 + 文件属性,其中文件内容放在Data blocks中,属性放在inode Table

inode内部保存了一个数组,保存了对应块的编号,二者关系就联系起来了
在这里插入图片描述Linux中真正标识一个文件,是通过文件的inode编号,一个文件,一个inode(属性集合);一个inode也都有自己的编号。

那么要创建文件就要在inode Table申请一个未被使用的inode,填入属性;文件中还有内容,inode还用数组存储了相关联的blocks块编号,我们可以简单地理解成 ——

struct inode{
	//文件的大小
	//文件的inode编号
	//其他属性
	int block[15];
}

⚡万一这个文件特别大呢? 怎么办?

不是所有的data block 只能存文件数据,也可以存其他块的块号
如果文件特别大,最后的block存的是其他block的块号,所以最后指向的是更多的block来存储

在这里插入图片描述

我们知道要找到文件:inode编号 —— 分区特定的block group —— inode —— 属性 —— 文件

🎨可是我们怎么知道inode呢?(Linux中,inode属性里面,没有文件名这样的说法

预备知识:

  • 一个目录下,是不是可以保存很多文件,但这些文件没有重复的文件名!
  • 目录也算是文件,有自己的inode(存的是目录的大小、权限、(链接数)、拥有者、所属组等)、有自己的data block(存的是文件名和inode的映射关系),因为文件名不存在于文件的inode里面

在这里插入图片描述

回答上面的问题:我们是依托目录文件来找到inode编号,因为目录的data block中保存了inode编号和文件名的映射关系,所以能找到文件名进而找到inode编号


💢灵魂多问:

  • 目录下创建文件:遍历inode Bitmap,位图中找0,申请一个未被使用的inode,随后便置1;向inode table中填入属性信息(权限、ACM时间)。并把这个映射关系文件名是用户输入,inode是我们从文件系统申请得到的写到当前目录的Data blocks中

在这里插入图片描述

  • 查看文件,根据目录inode找到与其映射的文件名 以及属性 全部打印
  • 向文件写入:遍历block Map找到若干未被使用的块儿,将该文件的inode与这些blocks建立映射关系,再向blocks中写入内容。

在这里插入图片描述

  • 查看文件内容cat hello.c → 查看当前目录的data Blocks数据块儿 → 找到映射关系:文件名儿对应的inode编号 → 在inode Table中找到inode → 找到对应的blocks[] → 文件内容加在进内存中,在刷新到显示屏。
  • 删除文件 :在目录的data block,用户提供的文件名,以文件名作为key值索引对应的inode,在inode Bitmap中把对应的比特位置中由1置0;再根据属性把使用的数据块儿们也在Bitmap中把它由1置0。最后在目录的data block中,删除inode和文件名的映射关系
  • 所以拷贝一个文件需要一会儿,但是删除很快(因为不需要改文件的属性inode Table和数据data Blocks,标志文件无效即可,并不用覆盖)

( 如果你在Linux系统中,不小心rm -rf误删了文件,还能恢复!前提是inode未被使用,inode和data block没有被占用最好的做法就是什么也不做!找到你在windows下删除文件到回收站,其实只不过是转移了目录,在回收站中删掉才是相当于1置0了)


还有一个问题:位图一开始是不是要被操作系统全清0,那个区域要被分成inode table和super在什么位置 ?GDT是谁写的?inode table的128kb是谁划分的?

  • 以上所有的信息都是:格式化 也就是写入文件系统(写入属性)

【面试题】:
🥗系统里还有空间,为什么创建文件老是失败:

  • 因为inode是固定的,data block也是固定的。可能存在 inode还有,data block不够了;inode没有,data block还有的情况(特别少)

说了一大圈,最终为了引出我们的大boss

三 . 软硬链接

🌏软链接

🍤建立软链接

ln -s testLink.txt soft.link

💛 删除链接,可以rm,但是更建议

unlink soft.link

🔷什么时候会使用到软连接呢?

对于一些执行路径非常深的程序,我们不用每次都输入长长的路径,我们可以通过软链接快速找到它

在这里插入图片描述

现在我不断回退,退到所谓工作目录上来,这时如果我还想运行test.sh就比较麻烦,那么我们就可以通过建立软链接的方式 ——

在这里插入图片描述

这相当于windows下创建的快捷方式

在这里插入图片描述
在系统路径下,也看见了大量了软链接的存在

在这里插入图片描述

🌏硬链接

不带s就是硬链接

ln  testLink1.txt hard.link

我们观察发现,软链接是有自己独立inode的,即软链接是一个独立文件!有自己的inode属性集也有自己的数据块儿 (保存的是它所指向文件的路径 + 文件名)

在这里插入图片描述硬链接自己没有独立的inode,用的是别人的inode;根本就不是一个独立的文件,说白了就起别名,其本质:在特定目录下,添加一个文件名和inode编号的映射关系,仅此而已

在这里插入图片描述

我们还发现在属性中有一个数字,1-> 2 ->1,这个数字就叫做硬链接数

在这里插入图片描述

当我们删除一个文件的时候,并不是把这个文件inode删除,而是把inode引用计数--,当引用计数为0(没有文件名与之关联)的时候,这个文件才真正删除

在这里插入图片描述
🍤那么硬链接的使用场景呢?

为什么创建一个文件的时候,硬链接数是1?

  • 因为自己的文件名就和inode就是一组映射关系

在这里插入图片描述
那为什么dir目录的硬链接数是2呢?

  • 首先目录和自身的inode就是一组映射,第二个是目录内部的. 和inode 也是一组映射
    在这里插入图片描述

在dir下又创建了d1目录,此处为什么dir的硬链接数变成3了?

在这里插入图片描述

因为d1的有..文件指向上级目录dir,所以硬链接数+1

总结:软硬连接的本质:有无独立的inode

📢写在最后

最近作业有点多,更新可能会慢一点

在这里插入图片描述

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

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

相关文章

安全需求和挑战

安全需求和挑战 从风险管理的角度讲&#xff0c;主要就是管理资产、威胁、脆弱性 和防护措施及其相关关系&#xff0c;最终保障云计算平台的持续安全&#xff0c;以及 其所支撑的业务的安全。 云计算平台是在传统 IT技术的基础上&#xff0c;增加了一个虚拟化 层&#xff0c;并…

大数据之——Hive

目录1. Hive 基本概念1.1 什么是 Hive1.2 Hive 的优缺点1.2.1 优点1.2.2 缺点1.3 Hive 架构原理2. Hive 安装2.1 Hive 安装地址2.2Hive 安装部署2.3MySQL 安装2.4 Hive 元数据配置到 MySQL2.4.1 拷贝驱动2.4.2 配置 Metastore 到 MySQL2.4.3 再次启动 Hive2.5 使用元数据服务的…

让学前端不再害怕英语单词(二)

写了本专栏的第一章让学前端不再害怕英语单词&#xff08;一&#xff09;后&#xff0c;反响热度还是比较高的&#xff0c;截止现在已经有20个收藏量了&#xff0c;所以趁热打铁来更第二章 第一章我们简单的介绍了html&#xff0c;css和js的部分高频单词&#xff0c;因为html要…

S32K144的GPIO使用

程序初始化前线使用Components工具对时钟和GPIO进行配置&#xff0c;然后再main函数里面初始化。 时钟配置参考&#xff1a; S32K144之时钟配置 - 明明1109 - 博客园 (cnblogs.com) gpio配置 S32K SDK使用详解之PinSettings组件配置与使用详解(S32K1xx PORT 和GPIO模块)_嵌…

jdk1.8新特性简介

一、引言 jdk1.8出来已经一段时间了&#xff0c;现在1.9也已经出来了&#xff0c;但是很多公司&#xff08;我们公司也一样&#xff09;不太愿意升级到高版本的jdk&#xff0c;主要是有老的项目要维护&#xff0c;还有升级的话配套的框架也要升级&#xff0c;要考虑的细节事情太…

实训素材纯HTML+CSS代码 (教育主题 3页 )

⛵ 源码获取 文末联系 ✈ Web前端开发技术 描述 网页设计题材&#xff0c;DIVCSS 布局制作,HTMLCSS网页设计期末课程大作业 | 家乡旅游景点 | 家乡民生变化 | 介绍自己的家乡 | 我的家乡 | 家乡主题 | HTML期末大学生网页设计作业&#xff0c;Web大学生网页 HTML&#xff1a;结…

SpringMVC具体工作流程(保姆教学)

目录 文章目录[toc]一、SpingMVC的常用组件二、[SpringMVC](https://so.csdn.net/so/search?qSpringMVC&spm1001.2101.3001.7020)的工作流程一、SpingMVC的常用组件 1&#xff09;DispatcherServlet 是一种前端控制器&#xff0c;由框架提供。 作用&#xff1a;统一处理请…

基于Simulink宽带单基地雷达系统仿真(附源码)

目录 一、探索示例 1.1 收发器 1.2 信号处理子系统 1.3 渠道 1.4 目标子系统 二、结果和显示 三、总结 四、程序 本示例演示如何仿真宽带雷达系统。当雷达系统的带宽超过系统中心频率的5%时&#xff0c;通常被认为是宽带的。对于此示例&#xff0c;将使用 10% 的带宽。 …

智慧供应链解决方案-最新全套文件

智慧供应链解决方案-最新全套文件一、建设背景二、建设思路三、建设方案智慧供应链具有以下特点1、高度智能化2、全流程数字化3、信息系统互联互通四、获取 - 智慧供应链全套最新解决方案合集一、建设背景 智慧供应链是结合物联网技术和现代供应链管理的理论、方法和技术&…

ES6 入门教程 16 Reflect 16.1 概述

ES6 入门教程 ECMAScript 6 入门 作者&#xff1a;阮一峰 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录ES6 入门教程16 Reflect16.1 概述16 Reflect 16.1 概述 Reflect对象与Proxy对象一样&#xff0c;也是 ES6 为了操作对象而提供的新 API…

nodejs校园二手交易管理系统vue

本系统的设计主要是为给网上用户提供购物方便&#xff0c;所以应该完成以下目标&#xff1a; (1) 登录、注册。用户要想在交易系统中购买商品&#xff0c;就必须先登录系统。如果不是会员&#xff0c;就必须先注册&#xff0c;然后才能登录系统。 (2) 查找商品。用户可以查找自…

求程序段中++x或者x++的频度,时间复杂度、执行次数

以下程序段中"x"的执行频度&#xff1f; 频度就是执行次数for i:1 to n then;for j:1 to n then;x;i1时 内圈for执行n次...in时 内圈执行n次我的理解&#xff1a;外圈for从1到n&#xff0c;有效循环是n次&#xff0c;外圈每循环一次&#xff0c;内圈循环n次&#…

C语言源代码系列-管理系统之学生籍贯信息

往期文章分享点击跳转>《导航贴》- Unity手册&#xff0c;系统实战学习点击跳转>《导航贴》- Android手册&#xff0c;重温移动开发 &#x1f449;关于作者 众所周知&#xff0c;人生是一个漫长的流程&#xff0c;不断克服困难&#xff0c;不断反思前进的过程。在这个过…

Python 3.11新功能:错误信息回溯

错误信息回溯 长按关注《Python学研大本营》&#xff0c;加入读者群&#xff0c;分享更多精彩 扫码关注《Python学研大本营》&#xff0c;加入读者群&#xff0c;分享更多精彩 Python 3.11于2022 年 10 月 24 日发布。这个最新版本的 Python 速度更快&#xff0c;对用户更友好…

QT源码拾贝0-5(qimage和qpainter)

目录 0 qt源码查看方法 1. qimage.cpp中线程池使用方法 2. qpainter_p.h中SmallStack模板元结构体存放智能指针 3. qpainter.cpp的保存函数&#xff0c;状态对象赋值使用std::exchange函数 4. qpainter.cpp中获得类对象的方法 5. qpainter.cpp中QChar字节操作&…

代码随想录算法训练营三期 day 22 - 二叉树(8)

235. 二叉搜索树的最近公共祖先 原文链接&#xff1a;235. 二叉搜索树的最近公共祖先 题目链接&#xff1a;235. 二叉搜索树的最近公共祖先 在 有序树 里: 从上向下递归遍历&#xff0c;第一次遇到 curcurcur 结点的数值在 p,qp, qp,q 结点对应数值的闭区间中&#xff0c;那么…

内网渗透神器CobaltStrike之Beacon详解(三)

Beacon的种类 HTTP Beacon和HTTPS Beacon 这两个beacon的原理是通过发送http请求与受害主机通信来传达命令, 以此实现控制效果 优点是传输数据快, 缺点时隐蔽性差, 容易被防火墙或内网审计工具拦截 TCP Beacon 自CS4.0版本之后只有反向的TCP Beacon可用, 基于TCP协议的通信…

取证初级案例操作大纲

文章目录**取证初级案例操作大纲**1) 证据文件中有没有存在被删除的Doc文档&#xff1f;如果有的话&#xff0c;请导出并记录文件名及路径&#xff1a;2) 证据文件中有没有存在被删除的图片&#xff1f;如果有的话&#xff0c;请记录文件名及路径&#xff1a;3) 证据文件中哪几…

object类的一些方法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 Object类 输出对象地址 object类里的tostring方法&#xff1a; 正确输出对象里内容 判断俩个对象大小&#xff1a; object类里的equlas方法&#xff1a; 自己实现一…

SpringCloud - 微服务理论基础

文章目录1.微服务架构1.1 什么是微服务1.2 Spring Cloud 简介1.3 Spring Cloud 技术栈2.Boot 和 Cloud 版本选型3.Cloud 服务升级1.微服务架构 1.1 什么是微服务 微服务架构是一种架构模式&#xff0c;它提倡单一应用程序划分成一组小的服务&#xff0c;服务直接相互协调、相互…