Cache
和Buffer
的区别
磁盘是一个块设备,可以划分为不同的分区;在分区之上再创建文件系统,挂载到某个目录,之后才可以在这个目录中读写文件。Linux
中“一切皆文件”,我们平时查看的“文件”是普通文件,磁盘是块设备文件,我们可以通过执行 “ls -l <路径>
” 查看它们的区别:
$ ls -l /bin/pwd
-rwxr-xr-x 1 root root 31472 3月 3 2017 /bin/pwd
$ ls -l /dev/sda1
brw-rw---- 1 root disk 8, 1 5月 28 14:04 /dev/sda1
第一个符号是“-”
表示普通文件,“b”
表示块设备文件。在读写普通文件时,会经过文件系统,由文件系统负责与磁盘交互,使用的缓存是Cache
(也就是Page Cache
);而读写磁盘或者分区时,就会跳过文件系统,也就是所谓的“裸I/O“,使用的缓存是Buffer
(也就是Block Buffer
)。
同步I/O
:sync
函数和命令
当将数据写入普通文件时,Linux
内核通常先将该数据复制到其中一个Cache
中,如果该Cache
尚未写满,则并不将其排入输出队列,而是等待其写满或者当内核需要重用该Cache
以便存放其他磁盘块数据时,再将该Cache
排入输出队列,然后待其到达队首时,才进行实际的I/O
操作。这种输出方式被称为延迟写(delayed write
)。
延迟写减少了磁盘读写次数,但是却降低了文件内容的更新速度,使得欲写到文件中的数据在一段时间内并没有写到磁盘上。当系统发生故障时,这种延迟可能造成文件更新内容的丢失。为了保证磁盘上实际文件系统与Cache
内容的一致性,Linux
系统提供了sync(2)
函数。通常称为update
的系统守护进程会周期性地(一般每隔30秒)调用sync
函数。这就保证了定期冲洗内核的块缓冲区。命令sync(1)
也调用sync(2)
函数。我们通过man命令来查看下sync(1)
命令:
$ man 1 sync
NAME
sync - Synchronize cached writes to persistent storage
sync(1)
命令的作用是同步Cache
写入到磁盘,接下来我们来查看下改命令调用的sync(2)
函数:
$ man 2 sync
NAME
sync, syncfs - commit buffer cache to disk
BUGS
According to the standard specification (e.g., POSIX.1-2001), sync() schedules the writes, but may return before the actual writing is done. However, since version 1.3.20 Linux does actually wait. (This still does not guarantee data integrity: modern disks have large caches.)
sync(2)
函数当然也是同步Cache
写入到磁盘。不过我们可以看到POSIX.1-2001
规定该函数只是将所有修改过的块缓冲区排入写队列,然后就返回,它并不等待实际写磁盘操作结束。但是在最新的Linux
中,该函数需要等待实际写磁盘操作结束才返回。通常我们往U盘拷贝大文件后,可以通过如下命令查看数据同步到U盘的过程:
$ sync &
$ watch -d grep -e Dirty: /proc/meminfo
一开始Dirty
数字会很大,接下来我们看到在sync(1)
完成的同时接近0。