type
status
date
slug
summary
tags
category
icon
password
URL
一个重大的误解
我们通常认为linux是这样写文件内容的:
比如我在终端输入:
几微秒之后,helloworld就写入了磁盘中。真实如此吗?事实上,为了效率,操作系统并没有立即将内容写入磁盘。文件也没有在磁盘上存活,你认为写了文件,就等于写了磁盘,事实上错了,操作系统可能最多要等30秒之后才持久化到磁盘中。发生什么了呢?
是比特”存活”在磁盘上
在操作系统中,一个文件就是一个接口,用户告诉接口我想往文件中写入内容,后面的事情就交给操作系统了。
磁盘只是一组字节的容器而已。当然,这些字节是有结构的。如果没有结构,它们就会是随机的,我们将无法理解它们。磁盘上字节的特定顺序被称为inode(索引节点)。我们最终使用文件来表示inode。
可以将inode看作是处理jpg图片文件的方式,它是按照特定方式排列字节的一种数据结构。
那么,为什么我们不直接与inodes进行交互呢?
为什么我们要使用接口
接口可以定义一组方法,但是没有实现。其目的是使不同的类可以实现相同的方法,但是每个类可以根据自己的需要进行实现。这样,我们可以通过使用接口来实现多态,从而编写更加灵活的代码。此外,接口还可以帮助我们遵循面向对象编程的一些最佳实践,例如解耦和单一职责原则。因此,使用接口可以使我们的代码更加可维护、可扩展和可重用。
你可以直接写入磁盘。你需要知道确切的写入位置,而且还需要直接写入字节。出错的可能性非常高。
相反,请求操作系统来完成它就容易得多。这样,我们可以专注于我们自己的业务逻辑。
所以,我们使用接口的另一个重要原因是效率。通过将磁盘访问委托给操作系统,程序员只用专注于自己的业务逻辑(有点像现在的高级编程语言)。
写磁盘到底有多慢?很慢!
磁盘有多慢?固态硬盘比内存慢1,000倍。机械硬盘比内存慢一百万倍!!
请思考一下问题,在固态硬盘出现之前,磁盘是计算机中最后一个机械设备(除了风扇)。在一个速度极快的电子推动的世界里,我们实际上在推动原子。电子移动的速度(内存)和纯机械移动的速度(磁盘)差异是极其巨大的!!
因此,操作系统会尽力保护我们的应用程序免受这种缓慢的影响。
如何做到这一点的?
写文件的到底发生了什么?
以下就是写入的实际工作方式。
- 你 输入
echo "helloworld" > bar.txt
- 操作系统将“
helloworld
”复制到称为page cache(页面高速缓存)的特殊内存位置中
- (微秒后)操作系统告诉您写入成功
- (异步地,最多30秒后)操作系统将“
helloworld
”写入磁盘
但实际上可能会在30秒后发生!
为什么要异步处理?
假设你做了一个带有“喜欢”按钮的照片分享应用程序,照片的喜欢已经被存储在数据库中。你会选择以下哪种展示方式:
- 点击“喜欢”按钮后立即显示“喜欢”图标?
- 只有当“喜欢”成功保存到磁盘后才显示“喜欢”图标?
当然,你会选择第一种方法,因为用户界面的响应速度非常重要。
计算机会告诉用户它已经完成了,但实际上,它是在你不看的时候完成的。
这被称为非阻塞IO:不要让应用程序等待磁盘响应。
当然,并不仅仅是因为异步写入是非阻塞的,才使用异步写入。
效率和持久性之间的权衡
页面高速缓存在计算机中是如此重要,它们使应用响应更加高效。
尽管异步磁盘写入为我们带来了巨大的速度提升,但有一个问题:异步的代价是什么?最大的代价可能就是数据的丢失。
如果在操作系统将数据写入磁盘之前拔掉计算机电源会发生什么?你会失去数据。
我们如何确保我们的写入数据持久不丢失呢?
O_SYNC
和 sync
: 保证写操作不会丢失
O_SYNC
文件模式可以使用
O_SYNC
模式打开文件。它会将数据先写入page cache,然后等待数据被持久化到磁盘上后才返回。与之相对应的是O_DIRECT,它会直接将数据传输到磁盘而不经过page cache 。同时,O_SYNC仅影响写操作,会阻塞当前写进程 。O_SYNC和O_DIRECT可以一起使用,以实现无缓存I/O和同步写入的效果 。在Linux内核中,O_SYNC通常与completion机制一起使用来实现同步操作 。
以上sync
sync
的系统调用。它的作用是:操作系统立即将页面缓存中的所有内容转储到磁盘!操作系统将执行此操作。可以将此命令放入 shell 中以尝试它。在程序中,您经常会看到一系列写入和
sync
调用交替进行。例如,每 n
次写入后,都会有一个 sync
调用。这使得应用程序可以精细调整持久化和写入吞吐量之间的平衡。演示异步写入
现在进行一些演示。我将写入文件。我将从该文件中读取。我将向您展示,这两个操作将在访问磁盘之前几秒钟完成。
这是我将一次性粘贴到 shell 中的内容。
这里是
bpftrace
的输出,它可以让我们跟踪内核事件。具体地,我们将跟踪当
vfs_read
和 vfs_write
完成时(你只需要知道这些是在读写文件时调用的函数)。我们还将跟踪 block_rq_issue
,它在磁盘驱动程序实际写入磁盘时调用。最后
即使我们读取文件,我们实际上从未读取过磁盘。这是因为读取操作是从页面缓存中进行的! 文件并不直接存储在磁盘上,而是以字节的形式存在。文件只是表示这些字节的方式。
- 作者:ly10247
- 链接:https://tangly1024.com/article/os-knowledge
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。