direct io/buffer io

打个比方:正常情况下磁盘上有个文件,如何操作它呢?

读取:硬盘->内核缓冲区->用户缓冲区
       写回:用户缓冲区->内核缓冲区->硬盘

       这里的内核缓冲区指的是page cache,说白了,也就是是DRAM。主要作用是它用于缓存文件内容,从而加快对磁盘上映像和数据的访问。

       正常的系统调用read/write的流程是怎样的呢?
       - 读取:硬盘->内核缓冲区->用户缓冲区;
     - 写回:数据会从用户地址空间拷贝到操作系统内核地址空间的页缓存中去,这是write就会直接返回,操作系统会在恰当的时机写入磁盘,这就是传说中的Buffered IO。

       然而对于[自缓存应用程序] [2]来说,缓存 I/O 明显不是一个好的选择。因此出现了DIRECT IO。然而想象一下,不经内核缓冲区,直接写磁盘,必然会引起阻塞。所以通常DIRECT IO与AIO会一起出现。Linux 异步 I/O 是 Linux 2.6 中的一个标准特性,其本质思想就是进程发出数据传输请求之后,进程不会被阻塞,也不用等待任何操作完成,进程可以在数据传输的时候继续执行其他的操作。相对于同步访问文件的方式来说,异步访问文件的方式可以提高应用程序的效率,并且提高系统资源利用率。直接 I/O 经常会和异步访问文件的方式结合在一起使用。

        对于nginx来说,是否开启AIO要看具体使用场景:
        -  正常写入文件往往是写入内存就直接返回,因此目前nginx仅支持在读取文件时使用异步IO。
       - 由于linux AIO仅支持DIRECT IO,AIO一定回去从磁盘上读取文件。所以从阻塞worker进程的角度上来说有一定的好处,但是对于单个请求来说速度是降低了的。
>异步文件I/O是把“双刃剑”,关键要看使用场景,如果大部分用户的请求落到文件缓冲中,那么不要使用异步 I/O,反之则可以试着使用异步I/O,看一下是否会为服务带来并发能力上的提升。 -- [<<深入理解NGINX>> 陶辉著] [1]
 
mmap  
mmap系统调用是将硬盘文件映射到用内存中,其实就是将page cache中的页直接映射到用户进程地址空间中,从而进程可以直接访问自身地址空间的虚拟地址来访问page cache中的页,从而省去了内核空间到用户空间的copy。



Page cache实际上是针对文件系统的,是文件的缓存,在文件层面上的数据会缓存到page cache。文件的逻辑层需要映射到实际的物理磁盘,这种映射关系由文件系统来完成。当page cache的数据需要刷新时,page cache中的数据交给buffer cache,但是这种处理在2.6版本的内核之后就变的很简单了,没有真正意义上的cache操作。

Linux 2.6的内核中Page cacheBuffer cache进一步结合,Buffer pages其实也是Page cache里面的页。从Linux算法实现的角度,Page cacheBuffer cache目前是一样的,只是多了一层抽象,通过buffer_head来进行一些访问管理。可以理解为只有Page cache概念亦可。在Linux 2.6的内核中Page cacheBuffer cache进一步结合,Buffer pages其实也是Page cache里面的页。从Linux算法实现的角度,

wps_clip_image-4293

标准IO

在 Linux 中,这种访问文件的方式是通过两个系统调用实现的:read() 和 write()。当应用程序调用 read() 系统调用读取一块数据的时候,如果该块数据已经在内存中了,那么就直接从内存中读出该数据并返回给应用程序;如果该块数据不在内存中,那么数据会被从磁盘上读到页高缓存中去,然后再从页缓存中拷贝到用户地址空间中去。如果一个进程读取某个文件,那么其他进程就都不可以读取或者更改该文件;对于写数据操作来说,当一个进程调用了 write() 系统调用往某个文件中写数据的时候,数据会先从用户地址空间拷贝到操作系统内核地址空间的页缓存中去,然后才被写到磁盘上。但是对于这种标准的访问文件的方式来说,在数据被写到页缓存中的时候,write() 系统调用就算执行完成,并不会等数据完全写入到磁盘上。Linux 在这里采用的是我们前边提到的延迟写机制( deferred writes )。如果用户采用的是延迟写机制( deferred writes ),那么应用程序就完全不需要等到数据全部被写回到磁盘,数据只要被写到页缓存中去就可以了。在延迟写机制的情况下,操作系统会定期地将放在页缓存中的数据刷到磁盘上。

wps_clip_image-21828

直接IO

凡是通过直接 I/O 方式进行数据传输,数据均直接在用户地址空间的缓冲区和磁盘之间直接进行传输,完全不需要页缓存的支持。操作系统层提供的缓存往往会使应用程序在读写数据的时候获得更好的性能,但是对于某些特殊的应用程序,比如说数据库管理系统这类应用,他们更倾向于选择他们自己的缓存机制,因为数据库管理系统往往比操作系统更了解数据库中存放的数据,数据库管理系统可以提供一种更加有效的缓存机制来提高数据库中数据的存取性能。

简单说来,page cache用来缓存文件数据,buffer cache用来缓存磁盘数据。在有文件系统的情况下,对文件操作,那么数据会缓存到page cache,如果直接采用dd等工具对磁盘进行读写,那么数据会缓存到buffer cache

补充一点,在文件系统层每个设备都会分配一个def_blk_ops的文件操作方法,这是设备的操作方法,在每个设备的inode下面会存在一个radix tree,这个radix tree下面将会放置缓存数据的page页。这个page的数量将会在top程序的buffer一栏中显示。如果设备做了文件系统,那么会生成一个inode,这个inode会分配ext3_ops之类的操作方法,这些方法是文件系统的方法,在这个inode下面同样存在一个radix tree,这里会缓存文件的page页,缓存页的数量在top程序的cache一栏进行统计。从上面的分析可以看出,2.6内核中的buffer cachepage cache在处理上是保持一致的,但是存在概念上的差别,page cache针对文件的cachebuffer是针对磁盘块数据的cache,仅此而已。


举例来说,数据刚写入缓冲区而尚未写回磁盘,此时如果发出读取请求,此请求可从缓冲区得到满足,而且不会因此而读取到地盘上的旧数据。此行为会实际提高性能,因为读取请求可从内存中的缓冲区得到满足,而不必从磁盘。当读取和写入请求如预期般交替出现时,结果也和预期一样,也就是说,数据被写回磁盘之前系统不会崩溃!即使应用程序相信写入请求已经成功完成了,但事实上数据尚未写回磁盘。


库函数的fwrite是在应用层建立一个缓冲区,通过setvbuf函数设置,fwrite的数据先写到这个缓冲区,缓冲区写满后调用write拷贝到buffer cache;也就是说,glibc的缓冲区的目的是为了减少write等系统调用的次数。

unix最强大的系统调用:posix_fadvise()
通过此调用,应用告诉内核想访问文件的方式,以便内核做出优化;
POSIX_FADV_NORMAL:默认的选项,linux中预读窗口为128K;
POSIX_FADV_SEQUENTIAL:linux中预读窗口为256K;通过预读,明显加快了应用读取文件的速度;
POSIX_FADV_RANDOM:不预读;

绕过内核缓冲区:打开文件时,指定标志为O_DIRECT
数据库的实现,就使用此方式,数据库会有自己的文件读取和缓存机制,不需要内核再重复做这个事情;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值