Linux下的IO操作以及ext系列文件系统

文件描述符

0、1和2

系统在开启后,会默认打开三个流,stdin、stdout、stderror,后面打开文件会从文件描述符表中挑选一个最小的空闲描述符来使

用

文件描述符表

在task_struct中,有一个指针,指向一个file_struct,其中最重要的部分就是一个指针数组,每个指针都是指向打开文件的指针,

所谓文件描述符fd,其实就是这个指针在这个数组当中的下标。所以当打开一个文件描述符但是不关闭时,会导致文件描述符泄露

重定向

  • 基于上面对文件描述符和分配方式的介绍,不难发现,其实操作系统就是根据文件描述符表中每个指针对文件进行操作,所以更改
    文件描述符表的内容,就可以实现重定向
cpp 复制代码
close(1);
int fd = open("log.txt", O_RDWR|O_APPEND);
printf("hello world\n");

这时,上述代码不再往屏幕上打印,发现打印到log.txt当中,因为系统默认标准输出的文件描述符是1,而我们用log.txt的文件指

针替换了标准输出的位置,操作系统继续默认往文件描述符表的一号位置操作,就实现了往log.txt输出内容的重定向操作

  • 系统调用dup2
cpp 复制代码
int dup2(int oldfd, int newfd);

oldfd表示原来的文件描述符,newfd表示目的要替换的文件描述符

FILE* 文件流指针

其实就是struct_IO_FILE的重定义,指向了一个struct_IO_FILE结构体,其中包含了file_no,和文件描述符是对应的,换言之,FILE* 是对文件描述符的封装

Linux下一切皆文件

这句话其实在源自于Linux对驱动程序的处理原则。驱动程序+硬件,才让一个硬件能够使用。对于普通文件,单开一个文件后,在内

核中会创建一个struct file,保存着文件的inode元信息,其中保存着一个struct file_operation指针,指向一个struct file_operation结构体,其中包含许多函数指针,他们对于硬件来说,就指向硬件的驱动程序,将对硬件的控制权交给函数处理,所以对于上

层来说,对于硬件、软件的操作,其实都是对于文件的操作,几乎Linux的所有资源,都能够通过操作文件时使用的系统调用read、write来使用

缓冲区

  • 当需要对硬件操作时,需要cpu从用户态切换到内核态,完成上下文切换,频繁的使用系统调用对硬件进行操作,会降低cpu的运行
    效率,所以需要缓冲区集中的对数据进行操作,根据输入输出的不同分为输入缓冲区和输出缓冲区,他们都是内存中的一块区域

缓冲区类型

  1. 全缓冲区:只有打满时才进行io操作,对磁盘文件的操作一般使用全缓冲区
cpp 复制代码
close(1);
int fd = open("log.txt", O_TRUNC | O_CREAT | O_RDWR);
dup2(fd, 1);
printf("hello world\n");

可以发现并没有实际的写入文件,因为原来的对标准输出的操作转换为了对磁盘的操作,采取全缓冲的方式。显然我们并没有写满缓

冲区

  1. 行缓冲区:涉及到终端的操作,比如像屏幕上打印,输入字符串,都是采取的行缓冲区,遇到换行符时刷新缓冲区

  2. 无缓冲区:不缓冲,直接进行输入输出,比如stderr,为了尽快显示错误信息,操作直接输出不缓冲的方式

刷新时机

  1. 进程结束
  2. 系统调用fflush
  3. 换行符

用户级缓冲区和内核级缓冲区

  • 看下面的代码
cpp 复制代码
const char* msg1 = "hello fwrite\n";
const char* msg2 = "hello write\n";
printf("hello printf\n");
fwrite(msg1, strlen(msg1), 1, stdout);
write(1, msg2, strlen(msg2));
fork();

将上面的代码重定向到文件后,可以发现,除了write,每句都打印了两遍

  1. printf和fwrite是标准库对系统调用的封装,即最后还是调用了write,但是他加上了自己的缓冲区,这种语言级的缓冲区不是系
    统的缓冲区。向屏幕输出时,采用行缓冲;但是重定向到文件之后,采取全缓冲,显然我们没有打满缓冲区,所以fork之后的子进程
    同样获得了缓冲区,两者结束时进行刷新,所以printf和fwrite写入了两次
  2. 对于write,是系统调用,使用的是系统的缓冲区,采取行缓冲,在fork之前已经刷新完毕,所以只有一次write,并且write在printf和fwrite之前

ext文件系统

ext2是较早的版本,ext3、ext4均是在ext2的改进,核心设计并没有发生改变。

cpp 复制代码
//MBR Partition1 Partition2 Partition3...
//    /        \
// Boot Block   ext2 File System
//               /             \
//    Block Group1   Block Group2...
//                   /          \
//   Super Block   GDT   Block Bitmap   inode Bitmap   inode table   block table

Boot Block

是一个分区中任何一个文件系统都没有权利修改的分区,存放着系统启动相关信息,一旦破坏,基本上就无法正常启动使用相关分区

或者文件系统的操作系如同了

Block Gruop 块组

ext2文件系统有多个块组组成

Super Block 超级块

是整个文件系统相关信息的描述,包括block大小、inode大小、已使用和未使用的inode数目、已使用和未使用的block数目、最后一

次读写时间等等

可以说,一旦超级块被破坏,这个文件系统就报废了。

所以,文件系统会在第一个块组的开头放置一个超级块的备份(至于其他分区开头是否备份没有硬性要求),以保证在硬盘保存超级

块的区域受损后文件系统依然能够正常使用

GDB 块组描述符表

group desctiption table,描述这个块组的信息,比如inode数目,block bitmap、inode bitmap、inode table、block table的起

止位置等等

块组描述符表的数目,与文件系统中块组的数目相同,每个块组的起始位置也会保存一份GDB的拷贝

两个bitmap和table

bitmap用来表示相应的table中的相应存储位置是否被占用,真正的内容存储在table中,bitmap起到辅助检索的作用

创建一个文件的过程

目录和文件的区别

在linux下,两者没有本质区别,目录其实也是存储在文件系统中的文件,或者换句话说,linux中没有目录只有文件。目录在block

中存储了

其中包含的所有文件和目录的名称,在inode中保存了文件名和inode的映射关系,这样就可以通过文件名获得inode,从而获得访问

目标文件

所需要的信息了

  1. 首先查找到空闲的block和inode,存储数据到block,存储文件信息到inode
  2. 更新所在目录的inode信息,更新文件名和inode的映射关系

软硬连接

硬链接

硬链接其实建立的是相同的inode和文件的映射关系,每建立一个硬链接,硬连接数就+1,当文件删除时,先将硬连接数-1

如果减为零,就将文件删除。所以硬链接可以用于文件的备份

软连接

软连接是通过名字引用另一个文件,软连接文件和被链接文件是两个文件,类似于桌面快捷方式

linux中两个进程打开同一个文件

  1. 文件描述符:对于同一个文件,两个进程产生的文件描述符是两个文件描述符,因为这本质上是指向file_operation结构体的指针在文件描述符表当中的数组下标
  2. 一个进程删除文件时,另一个进程并不会立刻读写失败,删除文件是操作的目录项,而另一个进程已经拥有了这个文件的inode信息,能够找到磁盘上的目标文件,所以不会立刻读写失败
  3. 一个进程修改文件时,另一个进程是可以立刻感知的,因为修改文件是对磁盘文件本身进行的操作
相关推荐
Doraemomo28 分钟前
Linux内核驱动开发——中断与定时器
linux·运维·驱动开发
2301_8084143839 分钟前
Linux中动静态库的理解
linux·运维·服务器
分布式存储与RustFS1 小时前
模型 checkpoint 放对象存储:分片上传、断点续传与残留清理
运维·云原生·开源·对象存储·分布式存储·s3·性能基准
M78佐菲2 小时前
ARM学习笔记(11)
linux·arm开发·笔记·嵌入式硬件·学习
M78佐菲2 小时前
ARM学习笔记(10)
linux·arm开发·笔记·嵌入式硬件·学习
半仙白桑2 小时前
内核篇第十五讲:system-V共享内存
linux·共享内存·shmget·shmat·shmdt·shmctl
一条破秋裤2 小时前
Linux 线程创建:pthread_create 与基本回收
java·linux·运维
码匠许师傅3 小时前
【C++三方组件】cpp-httplib:一个头文件起 HTTP 服务
开发语言·c++·http
可涵不会debug3 小时前
第一次学 LangGraph:用一个快递案例搞懂 State、Node、Edge 和 compile
服务器·数据库·mysql
无名猿3 小时前
new/delete 与 malloc/free:为什么绝对不能混用
c++·内存管理·现代c++·踩坑记录