Linux操作系统基础IO(下)

文章目录

  • 一、标准输出和标准错误
  • 二、文件系统
    • 1.磁盘的物理结构
    • 2.磁盘的存储结构
    • 3.磁盘的逻辑抽象结构
    • 4.磁盘的分区管理
  • 三、软硬链接
    • 1.软链接
    • 2.硬链接
    • 3.删除软硬链接

一、标准输出和标准错误

标准输出和标准错误对应的都是显示器文件,其内容都会打印到显示器上。那么标准输出和标准错误有什么区别呢?标准输出和标准错误都打印到显示器上,我们怎么区分哪些是标准输出打印的信息,哪些是标准错误打印的信息呢?

我们用一段代码来对比一下标准输出和标准错误的区别:
我们分别往stdout文件和stderr文件里写入内容,标记为[1]号的内容是写入到stdout文件里的,标记为[2]号的内容是写入到stderr文件里的。

Linux操作系统基础IO(下)_第1张图片


运行程序查看结果:
我们可以看到,向stdout文件写入的内容和向stderr文件写入的内容都打印到了显示器上。

Linux操作系统基础IO(下)_第2张图片


输入指令:./myfile > stdout.txt
即将内容重定向到stdout.txt文件里,我们会发现结果只有标准输出文件里的内容被写入到了stdout.txt中,而标准错误文件里的内容并没有被写入,依然是打印在显示器上。

Linux操作系统基础IO(下)_第3张图片

这个现象解释起来非常简单,我们的指令./myfile > stdout.txt默认是将标准输出文件里的内容进行重定向,完整的指令应该是:./myfile 1 > stdout.txt.
如果我们想要同时将标准输出文件重定向到一个新的文件,将标准错误文件重定向到另一个新的文件,应该输入这个指令:./myfile > stdout.txt 2>stderr.txt,此时就能将标准输出的内容和标准错误的内容分开到两个不同的文件里了。

Linux操作系统基础IO(下)_第4张图片

如果我们就想要将标准输出和标准错误的内容都重定向到一个文件里,那我们需要使用这个指令:./myfile > all.txt 2>&1

Linux操作系统基础IO(下)_第5张图片

二、文件系统

我们前面介绍过,在内存中的文件都是被打开的文件,我们一直进行的文件操作也是针对被打开文件的,但在磁盘上有大量的没有被打开的文件,这些文件就存储在磁盘里。所以对于文件系统的了解,我们不能还把视角放在内存上,而是应该把视角迁移到硬盘上。

1.磁盘的物理结构

如下是磁盘的内部结构图,磁盘的主轴串联着一摞盘片,每一个盘片都有两面,两面都可以存放数据,我们存储在磁盘上的数据就是在这个地方。传动轴的一端有磁头,磁头是在盘片上方的(并不与盘面接触),一方面盘片在旋转,另一方面磁头在摆动,盘片旋转决定的是磁头访问到的哪一块区域,磁头摆动决定的是访问盘片的内侧还是外侧,这就是磁盘的寻址过程。并且每一个盘面都配备一个磁头(即一个盘片的正反面都有磁头)。

Linux操作系统基础IO(下)_第6张图片

2.磁盘的存储结构

磁盘盘片的盘面会被分成很多组同心圆,这些同心圆称为磁道。每一个磁道(即每一个同心圆)都会被划分为一组扇区,扇区之间由间隔隔开,如下图所示。
磁盘上存储的基本单位是扇区。

Linux操作系统基础IO(下)_第7张图片

Linux操作系统基础IO(下)_第8张图片

所以将来在进行磁盘寻址的时候,磁头找的是某一个面上的某一个磁道上的某一个扇区,这种寻址方式叫做CHS地址。

3.磁盘的逻辑抽象结构

当操作系统要对磁盘进行写数据操作或者读数据操作,操作系统和磁盘之间又是怎么建立联系的呢?
实际上,在操作系统层面,磁盘的盘片就被逻辑抽象成为线性的数组,这个数组的每一个单位就是一个扇区,定义了这样的一个数组,操作系统只需要有数组的下标,就可以实现对磁盘的管理,这也是先描述再组织的思想。这种下标地址我们叫做LBA地址,即Logical Block Address,逻辑块地址。所以如果内存中有数据需要写入到磁盘里,操作系统会根据LBA地址映射转换成CHS地址,然后将内存中要写入到磁盘的数据配合着CHS地址写入到磁盘里,至此就完成了对应的写入操作。

4.磁盘的分区管理

我们的磁盘是一块很大的内存空间,管理这么大的一块内存空间成本是比较高的。因此操作系统对磁盘的管理采取的是分而治之的思想,即将磁盘分成几个区,然后分别对单独的一个区进行管理。磁盘分区其实非常常见,我们的电脑上一般会有所谓的C盘、D盘甚至是E盘F盘,这就是磁盘的分区。

Linux操作系统基础IO(下)_第9张图片

将磁盘分成一个个的区以后,我们就可以针对一个区进行管理,其中每个区又可以继续分成若干个组,如下图所示:
一个分区里面最开始一般都有一个区域叫做Boot Block,这里存储的是开机信息,这些开机信息一般包含了分区表、操作系统软件在磁盘的什么位置等,所以在开机的时候硬件就可以通过这里的开机信息找到我们的操作系统。除了这个区域外剩下的区域就被分成了一个个的小组。

Linux操作系统基础IO(下)_第10张图片

到这里,我们就将磁盘的管理转换成了一个组的管理。下面我们可以介绍针对一个组的管理方案,首先我们要明确一点:文件是由文件内容和文件属性组成的,文件内容和文件属性都是数据,它们都要被存储起来,在Linux中采用的是将内容和属性数据分开存储的方案。
其中文件内容被存储在block中,也就是我们所说的磁盘中的块,一个块是4KB大小的空间;文件属性被存储在inode中,inode就是磁盘上的另一份空间,一个inode空间的大小一般是128字节。

有了这两个概念以后,我们可以介绍磁盘分区中一个块组的内容:

Linux操作系统基础IO(下)_第11张图片

  • Data blocks: 这个区域实际上在一个块组中的占比是很大的,一般占一个块组的80%(图中没有画明确),这个区域是以块为单位进行文件内容的保存,一个块的大小是4KB,即使文件没有满4KB,比如文件内容大小只有3KB,照样会分配一个块来保存;或者文件内容大小是6KB,就会分配两个块来保存。
  • inode Table: 这个区域是以128字节为单位进行文件属性的保存,即里面是一个一个的inode,文件属性就存储在inode里面。文件属性里面有一个inode编号,一般而言,一个文件对应一个inode,也就是说一个文件对应一个inode编号。我们可以查看一下文件的inode编号,输入指令:ll -i
    下图中红框框起来的就是文件对应的inode编号。

Linux操作系统基础IO(下)_第12张图片

  • Block bitmap: 这是一个位图结构,用来记录Data blocks的使用情况。比如说Data blocks里面有1000个block,那么Block bitmap就有1000个比特位来记录每一个block是否被占用。
  • inode bitmap: 同理,inode bitmap用来记录inode Table中每一个inode是否被占用。
  • Group Descriptor Table: 简称GDT,它管理的是一个块组,用来记录块组的信息,包含块组中有多少个inode、块组的起始inode编号是多少、块组中有多少个inode被使用、块组中有多少个block被使用、块组的总大小是多少等等信息。
  • Super Block: 这是文件系统的顶层数据结构,它管理的是宏观上的整个分区,它表示的是整个分区的信息,比如整个分区一共有多少个块组、整个分区内每一个块组的inode的使用情况、整个分区内每一个块组的block的使用情况、每一个块组的大小、整个分区在磁盘中对应的位置等等。
    虽然Super Block管理的是整个分区,但Super Block不是像Boot Block一样存在于分区里,而是存在于分区下的每一个块组中,这是因为Super Block存储的信息太重要了,同一个分区下每个块组都存储一个相同Super Block能够起到备份的作用。如果出现了文件系统损坏的情况,可以用备份来修复。

既然文件内容和文件属性是分开存储的,文件内容存储在Data blocks中,文件属性存储在inode Table中,那么文件属性和文件内容是如何关联起来的呢?

在inode Table中,一般情况下每一个文件就对应着inode Table里的一个inode,inode实际上是通过一个结构体struct inode来描述文件的所有属性的,在这个结构体中还有一个blocks[]数组,这个数组存储的就是该文件内容存储在Data blocks中对应的编号,通过blocks[]这个数组就可以找到文件内容所对应的编号,就可以到Data blocks中找到对应的文件内容。

这里还可以引出另一个概念:文件名也属于文件的属性,但是在inode里面并没有保存文件名。原因是在Linux下,底层实际上是通过inode编号来标识文件的,实际上Linux底层并没有文件名的概念。

但是我们在Linux操作系统下看到的文件都是具有文件名的,既然文件是由inode编号来标识的,那么我们又怎么能够通过文件名来找到文件呢?
我们说过在Linux下一切皆文件,那么目录也是属于文件,因此目录也具有文件内容和文件属性,目录的文件内容里存储的就是该目录下所有文件的inode编号和其文件名的映射关系。通过这个映射关系,我们上层看到的文件名就可以和底层的inode编号关联起来。也正是因为这个原因,在同一个目录下不能存在同名文件,因为文件名对应的是inode编号,如果文件名相同意味着inode编号相同,这是不可能的,每一个inode编号都是独一无二的。

接下来我们就可以回答一个问题:当我们创建一个文件的时候,操作系统做了什么?

首先操作系统会在分区内部的一个块组里的inode Bitmap查找没有被使用的inode,找到以后将inode Bitmap里对应的比特位由0置1,然后将创建的文件的属性值写入到inode Table对应的inode里;当我们往文件写入内容时,操作系统会到Block Bitmap里查找没有被使用的块,然后将要写入的数据写入到对应的块中,最后再在inode的blocks[]数组里填上对应的编号。这还没有完,我们创建一个文件一定是在一个目录下创建的,在创建文件的时候用户会给文件指定文件名,操作系统会根据目录的inode找到目录的Data blocks,将文件名和inode编号的映射关系写入到目录的数据块中。

那么当我们删除一个文件时,操作系统又做了什么呢?

操作系统根据目录的inode找到目录的Data blocks,再根据文件名和inode编号的映射关系拿到文件的inode编号,再通过inode编号找到文件的inode Bitmap和Block Bitmap,将inode Bitmap和Block Bitmap对应的比特位由1置0,就完成了删除工作。

三、软硬链接

1.软链接

软链接是通过名字引用另外一个文件,相当于Windows操作系统下的创建快捷方式,软链接创建出来的文件是一个独立文件,它和原来的目标文件不共用同一个inode编号。软链接文件并不是对原来文件的拷贝,而是将原来文件的路径存储到软链接文件的内容当中。我们可以看一个例子理解一下:
我们当前在dir1目录下,写了一个代码生成了一个可执行程序mytest,在当前dir1的目录下我们运行可执行程序只需要输入指令./mytest即可。

Linux操作系统基础IO(下)_第13张图片

但如果我们退出到上一层目录,想要运行刚才的可执行程序mytest就需要带上比较长的路径:./dir1/mytest,假如我们的目录再深一点,运行可执行程序需要带上的路径就会更长,非常的不方便。

Linux操作系统基础IO(下)_第14张图片

因此我们可以在上级目录建立可执行程序mytest的软链接,输入指令:ln -s ./dir1/mytest mytest,这样我们就创建了目标文件的软链接,即在当前目录下的快捷方式,可以直接./运行起来了。

Linux操作系统基础IO(下)_第15张图片

2.硬链接

我们知道了在磁盘上找文件是通过inode编号而不是文件名,文件名和inode是通过映射关系关联起来的,其实在Linux中可以让多个文件名对应同一个inode编号。硬链接就是单纯地在目录下给指定的文件新增“文件名和inode编号的映射关系”。
我们可以测试一下:在当前目录下先创建一个文件叫做my.txt,然后输入指令:ln my.txt my.txt.hard,创建该文件的硬链接,新的文件名设置为my.txt.hard,查看结果我们可以发现,两个文件的名字不同,但是inode编号相同。

Linux操作系统基础IO(下)_第16张图片

我们输入指令ll查看到文件属性,其中红框框起来的这一列数字代表的就是该文件的硬链接数。硬链接数本质上就是该文件属性中的一个计数器,用来标识有几个文件名和该inode编号建立了映射关系。

Linux操作系统基础IO(下)_第17张图片

如果我们创建一个新的空目录和一个新的文件,查看它们的硬链接数会发现,目录的硬链接数是2,文件的硬链接数是1,这是为什么呢?

Linux操作系统基础IO(下)_第18张图片

文件的硬链接数是1非常好理解,因为创建一个新的文件,文件名本来就已经和inode编号建立了映射关系,所以硬链接数当然是1。我们再查看一下目录里面的内容:我们发现空目录里面有一个 “.” 目录,它与dir1目录的inode编号相同,所以dir1的硬链接数是2,这个 “.” 目录就是当前目录。所以我们平时运行可执行程序时输入的 “./” 指令,这个 “.” 就是当前目录下的意思。

Linux操作系统基础IO(下)_第19张图片

3.删除软硬链接

建议使用unlink来删除软硬链接,输入指令:unlink mytestunlink my.txt.hard

Linux操作系统基础IO(下)_第20张图片

你可能感兴趣的:(Linux系统编程,linux,服务器,运维)