操作系统文件管理

一、文件系统全家桶

1.1、文件系统的基本组成

文件系统是操作系统中负责管理持久数据的子系统,就是负责把用户的文件存到磁盘硬件中,因为即使计算机断电了,磁盘里的数据并不会丢失,所以可以持久化的保存文件。

文件系统的基本数据单位是文件,它的目的是对磁盘上的文件进行组织管理,那组织的方式不同,就会形成不同的文件系统。Linux 有一句话是”一切皆文件“,不仅普通的文件和目录,就连块设备、管道、socket等也都是统一交给文件系统管理的。

Linux 文件系统会为每个文件分配两个数据结构:索引节点(index node)和目录项(directory entry),他们主要用来记录文件的元信息和目录层次结构。

  • 索引节点,也就是 inode ,用来记录文件的元信息,比如 inode 编号,文件大小,访问权限,创建时间,修改时间,数据在磁盘中的位置等。索引节点是文件的唯一标识,它们之间一一对应,也同样会被存储在硬盘中,所以索引节点同样占用磁盘空间。
  • 目录项,也就是 dentry ,用来记录文件的名字,索引节点指针以及与其他目录项的层级关联关系。多个目录项关联起来,就会形成目录结构,但它与索引节点不同的是,目录项是一个由内核维护的一个数据结构,不存放于磁盘,而是缓存在内存。

由于索引节点唯一标识一个文件,而且目录项记录着文件的名字,所以目录项和索引节点的关系是多对一,也就是说,一个文件可以有多个别名。比如,硬链接的实现就是多个目录项的索引节点指向同一个文件。注意,目录也是文件,也是用索引节点唯一标识,和普通文件的不同是,普通文件在磁盘里面保存的是文件数据,而目录文件在磁盘里面保存子目录或文件。

目录和目录项的区别:

虽然名字很相像,但是却不是一个东西,目录是个文件, 持久化存储在磁盘中,而目录项是内核一个数据结构,缓存在内存。如果查询目录频繁向磁盘中读,效率会很低,所以内核会把已经读过的目录用目录项这个数据结构缓存在内存,下次再次读到相同的目录就只需要从内存中读就可以,大大提高了文件系统的效率。(这个目录项不只是表示目录,也是可以表示文件滴)

数据是如何保存在磁盘的呢?

磁盘读写的最小单位是扇区,扇区的大小只有 512B 大小,如果每次读写都以这么小为单位,那这读写的效率就会非常低,所以文件系统把多个扇区组成了一个逻辑块,每次读写的最小单位就是逻辑块(数据块),Linux 中的逻辑块大小为 4KB ,也就是一次性读写 8 个扇区,这将大大提高了磁盘的读写的效率。

它们之间的关系如下:

操作系统文件管理_第1张图片

索引节点是存储在硬盘上的数据,为了加速文件的访问,通常会把索引节点加载到内存中,此外,硬盘在进行格式的时候,会被分成三个存储区域。分别是超级块,索引节点和数据块区

  • 超级块:用来存储文件系统的详细信息,比如块个数、块大小、空闲块等等
  • 索引节点区:用来存储索引节点
  • 数据块区:用来存储文件或目录数据

我们不可能把超级块和索引节点区全部加载到内存中去,只有在需要使用的时候,才将其加载到内存中,它们加载的时机也不同:

  • 超级块:当文件系统挂载时进入内存
  • 索引节点区:当文件被访问时进入内存

1.2、软链接和硬链接

硬链接是多个目录项中的【索引节点】指向一个文件,也就是指向同一个 inode,但是 inode 是不可能跨越文件系统的,每个文件系统都有各自的 inode 数据结构和列表,所以硬链接是不可用于跨文件系统。由于多个目录项都是指向一个 inode ,那么只有删除文件的所有硬链接以及源文件的时候,系统才会彻底删除该文件。如图:

操作系统文件管理_第2张图片

软文件相当于重新创建一个文件了,这个文件有独立的 inode ,但是这个文件的内容是一个文件的路径,所以访问软链接的时候,实际上相当于访问到了一个文件,所以软链接是可以跨文件系统的,甚至目标文件被删除了,链接文件还是在的,只不过指向的文件找不到了而已。如图:

操作系统文件管理_第3张图片

1.3、文件 IO

文件的读写方式各有千秋,对于文件的 IO 分类也非常多,常见的有:

  • 缓冲与非缓冲 IO
  • 直接与非直接 IO
  • 阻塞与非阻塞 IO  VS  同步与异步 IO

缓冲与非缓冲 IO

文件操作的标准库是可以实现数据的缓存,那么根据【是否可以利用标准库缓冲】,可以把文件 IO 分为缓冲 IO 和非缓冲 IO:

  • 缓冲 IO:利用的是标准库的缓存实现文件的加速访问,而标准库再通过系统调用访问文件
  • 非缓冲 IO:直接通过系统调用访问文件,不经过标准库缓存

直接 IO 与非直接 IO

磁盘 IO 是非常慢的,所以 Linux 内核为了减少磁盘 IO 次数,在系统调用后,会把用户数据拷贝到内核中缓存起来,这个内核缓存空间也就是【页缓存】,只有当缓存满足某些条件时才发起磁盘 IO 的请求。

根据【是否利用操作系统的缓存】,可以把文件 IO 分为直接 IO 与非直接 IO:

  • 直接 IO:不会发生内核缓存和用户程序之间数据复制,而是直接经过文件系统访问磁盘。
  • 非直接 IO :读操作时,数据从内核缓存中拷贝给用户程序,写操作时,数据从用户程序拷贝给内核缓存,再由内核决定什么时候写入数据到磁盘。

如果在使用文件操作类的系统调用函数时,指定了 O_DIRECT 标志,则表示使用直接 IO,如果没有设置过,默认使用的是非直接 IO。

如果使用了非直接 IO 进行写数据操作,内核什么情况下才会把缓存数据写入到磁盘?

  • 在调用 write 的最后,当发现内核缓存的数据太多的时候,内核会把数据写到磁盘上
  • 用户主动调用 sync ,内核缓存会刷到磁盘上
  • 当内存十分紧张,无法再分配页面时,也会把内核缓存的数据刷到磁盘上
  • 内核缓存的数据的缓存时间超过某个时间时,也会把数据刷到磁盘上

阻塞与非阻塞 IO  VS  同步与异步 IO

阻塞 IO,当用户程序执行 read ,线程会被阻塞,一直等到内核数据准备好,并把数据从内核缓冲区拷贝到应用程序的缓冲区中,当拷贝过程完成,read 才会返回。

注意:阻塞等待的是【内核数据准备好】和【数据从内核态拷贝到用户态】这两个过程。

非阻塞 IO,非阻塞的 read 请求在数据未准备好的情况下立即返回,可以继续往下执行,此时应用程序不断轮询内核,直到数据准备好,内核将数据拷贝到应用程序缓冲区,read 调用才可以获得结果。

注意:这里最后一次 read 调用,获取数据的过程是一个同步的过程,是需要等待的过程。这里的同步指的是内核态的数据拷贝到用户程序的缓存区这个过程。

为了解决这种傻乎乎轮询的方式于是 IO 多路复用技术就出来了,当内核数据准备好时,再以事件通知应用程序进行操作。

实际上,无论是 阻塞,非阻塞,还是基于非阻塞 IO 的多路复用都是同步调用。因为它们在 read 调用时,内核将数据从内核空间拷贝到应用程序空间,过程都是需要等待的,也就是说这个过程是同步的,如果内核实现的拷贝效率不高,read 调用就会在这个同步过程中等待比较长的时间。

而真正的异步 IO 是 【内核数据准备好】和【数据从内核态拷贝到用户态】这两个过程都不用等待。

举个:

你好比用户程序,饭堂好比操作系统。

阻塞 I/O 好比,你去饭堂吃饭,但是饭堂的菜还没做好,然后你就一直在那里等啊等,等了好长一段时间终于等到饭堂阿姨把菜端了出来(数据准备的过程),但是你还得继续等阿姨把菜(内核空间)打到你的饭盒里(用户空间),经历完这两个过程,你才可以离开。

非阻塞 I/O 好比,你去了饭堂,问阿姨菜做好了没有,阿姨告诉你没,你就离开了,过几十分钟,你又来饭堂问阿姨,阿姨说做好了,于是阿姨帮你把菜打到你的饭盒里,这个过程你是得等待的。

基于非阻塞的 I/O 多路复用好比,你去饭堂吃饭,发现有一排窗口,饭堂阿姨告诉你这些窗口都还没做好菜,等做好了再通知你,于是等啊等(select 调用中),过了一会阿姨通知你菜做好了,但是不知道哪个窗口的菜做好了,你自己看吧。于是你只能一个一个窗口去确认,后面发现 5 号窗口菜做好了,于是你让 5 号窗口的阿姨帮你打菜到饭盒里,这个打菜的过程你是要等待的,虽然时间不长。打完菜后,你自然就可以离开了。

异步 I/O 好比,你让饭堂阿姨将菜做好并把菜打到饭盒里后,把饭盒送到你面前,整个过程你都不需要任何等待。

二、进程写文件时,进程发生了崩溃,以写入的数据会丢失吗?

大概就是,进程写文件过程中,写一半的时候,进程发生了崩溃,已写入的数据会丢失吗?

是不会的,因为进程在执行 write (使用缓冲)系统调用的时候,实际上是将文件数据写到了内核的 page cache ,它是文件系统中用于缓存文件数据的缓冲,所以即使进程崩溃了,文件数据还是被保留在内核中的 page cache ,我们读数据的时候,也是从内核的 page cache 读取,因此还是依然读的进程崩溃前写入的数据。内核会找个合适的时机,将 page cache 中的数据持久化到磁盘,但是如果 page cache 里的文件数据,在持久化到磁盘之前,系统发生了崩溃,那这部分数据就会丢失了。当然,我们也可以在程序里调用 fsync 函数,在写文件的时候,立刻将文件数据持久化到磁盘,这样就可以解决系统崩溃导致的文件数据丢失的问题。

三、Page 与 Page Cache

page 是内存管理分配的基本单位,Page Cache 由多个 Page 组成。page 在操作系统中通常为 4KB 大小,而 Page Cache 的大小则为 4KB 的整数倍。

另一方面,并不是所有的 page 都被组织为 page cache

Page Cache 的本质是由 Linux 内核管理的内存区域,我们通过 mmap 以及 buffered IO 将文件读取到内存空间实际上都是读取到 Page Cache 中去。

Linux 系统上供用户可访问的内存分为两个类型:

  • File-backed pages:文件备份页也就是 Page Cache 中的 page,对应硬盘上的若干数据块,对于这些页最大的问题就是脏页回盘
  • Anonymous pages:匿名页不对应磁盘上的任何磁盘数据块,它们是进程的运行是内存空间。

你可能感兴趣的:(服务器,linux,unix,centos)