【Linux】——文件(下)

重定向

什么是重定向:

重定向的原理其实就是更改文件描述符中特定下标中的内容!

我们来验证一下这个结论:

输出重定向

如果我们关闭对应标准输出流的文件描述符1

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
    close(1);//关闭标准输出流
    int fd=open("log.txt",O_WRONLY|O_CREAT,0666);
    if(fd<0)
    {
        perror("open fail:");
        return 1;
    }
    //向屏幕打印信息
    printf("hello world!\n");
    printf("hello world!\n");
    printf("hello world!\n");
    printf("hello world!\n");
    printf("hello world!\n");
    fflush(stdout);//刷新缓冲区
    close(fd);
    return 0;
}

在这里插入图片描述
在这里插入图片描述

从上面我们可以看出,本来是向屏幕打印的数据,结果却重定向到了log.txt

输入重定向

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
    close(0);//关闭标准输入流
    int fd=open("log.txt",O_RDONLY);
    if(fd<0)
    {
        perror("open fail:");
        return 1;
    }
    //向屏幕打印信息
    char buf[128]={'\0'};
	while (scanf("%s", buf) != EOF)
    {
		printf("%s\n", buf);
	}
    close(fd);
    return 0;
}

在这里插入图片描述
在这里插入图片描述
从上面我们可以看出,本来是从屏幕上读取数据,结果从log.txt里读取了。

dup2函数

dup2函数是操作系统专门给用户提供的一个系统调用函数,它的作用就是拷贝文件描述符

功能:dup2会将fd_array[oldfd]的内容拷贝到fd_array[newfd]当中,如果newfd已经打开,dup2会先关闭它,然后再进行拷贝。
返回值: 如果调用成功,返回newfd,否则返回-1。

在这里插入图片描述
在使用dup2函数时,需要注意两个点:

oldfd:必须是一个有效的、已打开的文件描述符。如果 oldfd 无效,dup2 会返回 -1 并设置 errno。
如果oldfd是一个有效的文件描述符,但是newfd和oldfd具有相同的值,则dup2不做任何操作,并返回newfd。

#include<stdio.h>
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<fcntl.h>
int main()
{
    int fd=open("log.txt",O_WRONLY|O_CREAT,0666);
    if(fd<0)
    {
        perror("open fail:");
        return 1;
    }
    close(1);
    dup2(fd,1);//进行重定向
    printf("hello printf!\n");
    fprintf(stdout,"hello fprintf!\n");
    close(fd);
    return 0;
}

在这里插入图片描述

缓冲区

语言缓冲区

我们先看一段代码演示:

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

int main()
{
    close(1);//关闭标准输出流
    int fd=open("log.txt",O_WRONLY|O_CREAT,0666);
    if(fd<0)
    {
        perror("open fail:");
        return 1;
    }
    //向屏幕打印信息
    printf("hello world!\n");
    printf("hello world!\n");
    printf("hello world!\n");
    printf("hello world!\n");
    printf("hello world!\n");
    close(fd);
    return 0;
}

在这里插入图片描述
为啥我明明关闭标准输出流了log.txt里还啥都没打印进去呢?
这里就跟缓冲区有关,常见的缓冲区刷新方法有三种:

行缓冲:对显示器进行刷新数据
全缓冲:对磁盘文件写入数据
无缓冲。

C语言的prinft函数,如果加\n就是行缓冲,没加就是全缓冲。
对文件重定向就是让本该打印在屏幕的数据输入进了一个磁盘文件,这时缓冲方法就从行缓冲变为了全缓冲,全缓冲需要程序结束后、缓冲区满时,调用刷新缓冲区的函数才会被刷新。
但是再次之前我们用close关闭了对应的文件描述符(fd),所以在程序结束后就无法找到对应的文件,就不会对文件进行任何的写入,所以我们一般用刷新缓冲区的函数来提前刷新缓冲区(如fflush).

系统缓冲区

我一样用一段代码演示:

#include <stdio.h>
#include <unistd.h>
int main()
{
	//c
	printf("hello printf\n");
	fputs("hello fputs\n", stdout);
	//system
	write(1, "hello write\n", 12);
	fork();
	return 0;
}

在这里插入图片描述
我创建了一个子进程,为啥test.txt里不是两份一样的数据呢?怎么少了一个write

这是因为我们执行可执行程序,打印到屏幕,默认是行缓冲,所以直接打印所有数据。但是如果我们对数据进行重定向的话,向磁盘写入数据,默认为全缓冲,此时数据都会存在语言缓冲区中。这时我们创建了一个子进程,父子进程代码数据共享,进程结束后语言缓冲区刷新,本质就是对数据进行修改,这时为了进程的独立性就会发生写时拷贝,那么因为是语言缓冲区刷新,C语言接口的数据才会打印两份,而write是直接写入系统缓冲区的,所以语言缓冲区和他没关系,他就不会发生写时拷贝,所以这就是为什么write只打印了一份。

磁盘与文件系统

磁盘

磁盘是一种永久性存储介质,在计算机中,磁盘几乎是唯一的机械设备。与磁盘相对应的就是内存,但是内存是掉电易失存储介质,所以目前所有的普通文件都是在磁盘中存储的。
磁盘的物理结构图:
在这里插入图片描述
在这里插入图片描述
我们如果需要找到磁盘的特定区域,只需要找到对应的扇区(sector)磁道(track)柱面(cylinder)就行。
磁盘的逻辑结构类似于磁带:
在这里插入图片描述
磁带也是磁盘的一种,在磁带圈起来时,就类似一个盘面。将其拉直展开,就可以看成是由扇区(512字节)为单位组成的数组。
我们可以将磁盘想象成卷起来的磁带,那么就可以将磁盘的某一个盘片上的某一个磁道抽象为线性结构,类似于数组:
在这里插入图片描述
在计算机中,为实现高效磁盘管理,经常进行分区操作。分区编辑器可在磁盘上划分出多个逻辑部分,如 Windows下常见的C盘和D盘。分区越多,文件管理越精细,不同性质文件可存储于不同分区。这样能更好地组织和管理文件,提升系统运行效率,便于用户快速找到所需文件,优化计算机使用体验。
我们Linux可以使用指令ll /dev/vda*来查看磁盘的分区信息。
在这里插入图片描述

inode

因为文件 = 内容 + 属性,文件的内容和属性又是分开存储的,文件内容放在Data blocks里,那么为了快速定位文件位置,所以保存文件属性的结构inode得有编号以便于快速找到文件。
我们可以通过ls -i,显示当前目录下的各文件编号。
在这里插入图片描述

EXT系列文件系统的分区结构

对于每一个分区来说,分区的头部会包括一个启动块(Boot Block),对于该分区的其余区域,EXT2文件系统会根据分区的大小将其划分为一个个的块组(Block Group)。其中启动块的大小是确定的,而块组的大小是由格式化的时候确定的,并且不可以更改。
组成部分:
在这里插入图片描述

一组中各区域分布:
inode table(节点表):存储了每个inode的详细信息,包括文件的大小、权限、时间戳、数据块位置等
Data blocks(数据区):存放文件内容,全都是划分好的4KB块
inode Bitmap(inode table位图):每个bit位表示inode table中的inode是否被占用。 inode号在inode table中早就设置好了,只是有没有被使用的问题。inode bitmap中的位置与inode table中的位置是一一对应的。
Block Bitmap(块位图):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没有被占用
Group Descriptor Table:块组描述符,描述块组属性信息。例如:每个组的起始inode与block编号。
超级块(Super Block):存放当前分区文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。
Super Block的信息被破坏,可以说整个文件系统结构就被破坏了。Super Block有多份拷贝,可在损坏时进行修复。

值得注意的是:

其他块组当中可能会存在冗余的Super Block,当某一Super Block被破坏后可以通过其他Super Block进行恢复。
磁盘分区并格式化后,每个分区的inode个数就确定了。

对于文件的几个问题,通过上面的学习基本可以回答出:

  1. 如何理解创建一个空文件?
  1. 遍历inode Bitmap,找到比特位为0的位置,申请一个未被使用的inode。
  2. 将inode表中找到对应的inode, 并将文件的属性信息填到inode结构当中。
  3. 将该文件的文件名和inode指针添加到目录文件的数据块当中。
  1. 如何理解向文件写入信息?
  1. 通过文件的inode编号找到对应的inode结构。 通过inode结构找到存储该文件内容的数据块,并将数据写入数据块。
  2. 若不存在数据块或者申请的数据块已经写满了,就需要遍历block
  3. Bitmap找到一个空的块号,并在数据区当中找到对应的空闲块,再把数据写入到数据块当中,最后还需要建立数据块和inode结构的对应关系。
  1. 删除文件做了些什么?
  1. 将该文件对应的inode在Inode map当中设置为无效。
  2. 将该文件申请过的Data Block在Block map当中置为无效。

为啥删除比下载快这么多的原因就在这里,因为文件内容并没有被删除,所以我们可以在对应文件内容被其他文件覆盖之前,通过技术手段复原已删除的文件。

  1. 如何理解目录?

目录也是一种文件,是文件就有对应的文件属性与文件内容,其中对应的文件属性就是我们的inode存储的就是目录的大小,目录的拥有者等。而对应的文件内容存储的就是该目录下的文件名以及对应文件的inode指针。

软硬链接

软链接

软链接就相当于Windows系统的快捷方式,软链接文件有自己的inode号。
建立软链接的方式:

ln -s 被链接的文件名  链接后的文件名

在这里插入图片描述
软链接文件是一个独立的文件,该文件有自己的inode。
在这里插入图片描述
软链接存储的是目标文件或目录的路径,而不是直接指向inode。
删除软链接不会影响目标文件,但如果目标文件被删除或移动,软链接将变为“悬空链接”(dangling link),无法访问目标。
取消软链接可以通过指令unlink 软连接名

硬链接

硬链接文件就是源文件的一个别名,它与源文件之间具有相同的inode,大小。一旦为某个文件建立硬链接,那么对应的硬链接数就会加一。
硬链接没有独立的inode,并不是一个独立的文件, 本质是在特定的目录下,添加一个文件名和inode编号的映射关系。
建立硬链接的方式:

ln 被链接的文件名  链接后的文件名

在这里插入图片描述
取消硬链接的方式和软链接一样都可以用unlink
在这里插入图片描述

我们普通的文件硬链接数量为1,为啥目录的硬链接都是2?
这是因为我们当前目录下还存在一个隐藏文件.指向我们的当前目录,.文件其实就是我们目录的硬链接文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值