+ [二 . 理解文件系统](#___175)
+ - [🎨科普背景知识](#_177)
- [🎨了解磁盘结构](#_195)
- * [🔵物理结构](#_197)
* [🟡磁盘的存储结构](#_213)
* [🟢磁盘的虚拟结构](#_222)
- [🎨文件系统与inode](#inode_235)
+ [三 . 软硬链接](#___345)
+ - [🌏软链接](#_346)
- [🌏硬链接](#_383)
一. 答疑解惑
🌈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
二 . 理解文件系统
🎨科普背景知识
🥑有没有没有被打开的文件呢? 在哪里呢?
- 当然存在,在磁盘上
🥑我们学习磁盘级别的文件,我们的侧重点在哪?
- 单个文件角度 —— 这个文件在哪里?这个文件多大?这个文件的其他属性是什么?
- 站在系统角度 ——一共有多少个文件?各自属性在哪里?如何快速找到?还可以存储多少个文件
接下来我们来正式了解一下磁盘吧
- 内存 - 掉电易失存储介质
- 磁盘 - 永久性存储介质 还有: 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字节),4KB
是block
大小,所以磁盘被称为块设备
哪怕我们只想在磁盘上读取1字节,OS也必须直接读取4KB的数据
原因有两个:
- 512字节太小了,有可能导致多次IO,进而导致效率的降低
- 解耦合:如果操作系统使用和磁盘一样的大小,万一磁盘基本大小改变了,OS的源代码也要不要跟着改呢?硬件(磁盘)和软件(OS)进行解耦
逐个解释:
2. Super Block
:文件系统的属性信息,整个分区属性的属性集(每个块组都有 防止磁盘被刮伤而找不到 文件属性)
3. Data blocks
:多个4KB(扇区*8)大小的集合,保存的都是特定文件的内容
4. inode Table
:inode是一个大小为128字节的空间,保存的是对应文件的属性,该块组内,所有文件的inode空间的集合,需要标识唯一性,每一个inode块都要有一个inode编号!(一般而言,一个文件,一个inode,一个inode编号)
5. BlockBitmap
(位图):统计block
的使用情况。假设有10000+个blocks,就有一一对应的比特位。其中比特位为1,代表该block被占用,否则表示可用
6. inode Bitmap
:统计inode
的使用情况,假设有10000+个inode,就有一一对应的比特位。其中比特位为1,代表该inode被占用,否则表示可用
7. 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 —— 属性 —— 文件
最全的Linux教程,Linux从入门到精通
======================
-
linux从入门到精通(第2版)
-
Linux系统移植
-
Linux驱动开发入门与实战
-
LINUX 系统移植 第2版
-
Linux开源网络全栈详解 从DPDK到OpenFlow
第一份《Linux从入门到精通》466页
====================
内容简介
====
本书是获得了很多读者好评的Linux经典畅销书**《Linux从入门到精通》的第2版**。本书第1版出版后曾经多次印刷,并被51CTO读书频道评为“最受读者喜爱的原创IT技术图书奖”。本书第﹖版以最新的Ubuntu 12.04为版本,循序渐进地向读者介绍了Linux 的基础应用、系统管理、网络应用、娱乐和办公、程序开发、服务器配置、系统安全等。本书附带1张光盘,内容为本书配套多媒体教学视频。另外,本书还为读者提供了大量的Linux学习资料和Ubuntu安装镜像文件,供读者免费下载。
本书适合广大Linux初中级用户、开源软件爱好者和大专院校的学生阅读,同时也非常适合准备从事Linux平台开发的各类人员。
需要《Linux入门到精通》、《linux系统移植》、《Linux驱动开发入门实战》、《Linux开源网络全栈》电子书籍及教程的工程师朋友们劳烦您转发+评论
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!