Direct IO

Posted 我要出家当道士

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Direct IO相关的知识,希望对你有一定的参考价值。

目录

一、基本介绍

二、使用方法与Demo

三、O_DIRECT 与 O_SYNC 


一、基本介绍

         如上图所示,普通的 IO 读写,会先将内容保存在缓冲区中,文件落盘需要调用 fflush 、fsync 等方法。

        而 DirectIO 是无缓冲 IO,,使用无缓冲 IO 对文件进行读写,不会经过 OS Cache,数据直接落盘。

        大多数使用场景中,使用缓冲区可以达到预读取、批量刷盘等目的,可以提高一部分性能。但对于一些应用场景,例如数据库,他们内部设计了一套自己的缓存机制,如果不使用 Direct IO 会存在双重缓存机制,影响性能。而且,数据库的缓存机制更适用于数据库本身。

二、使用方法与Demo

        下面介绍如何使用 Direct IO

/*
    打开文件并返回文件句柄。
    flags 必须包含 O_RDONLY(只读), O_WRONLY(只写), O_RDWR(读写) 中的一种。
    除此之外还有如下 flag:
    O_APPEND : 追加写入,但在 NFS 场景下如果多进程写入文件,会造成文件损毁(NFS不支持追加写入,内核必须模拟)。
    O_ASYNC、O_CLOEXEC、O_CREAT、O_DIRECTORY、O_EXCL、O_LARGEFILE、O_NOATIME、O_NOCTTY、O_NOFOLLOW、O_NONBLOCK、O_NDELAY、O_PATH、O_TRUNC
    O_DIRECT : 绕过系统缓冲区直接读写磁盘文件,大多数情况下会降低性能,但对于应用自身携带缓存机制的情况下很有用。但需要注意,O_DIRECT 不等同于 O_SYNC。
    O_SYNC  : 同步 IO,write 操作在物理落盘之前会一直阻塞。
               
*/
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

         在使用 Direct IO 编码过程中,读写的 buff 需要进行内存对齐,所以不能使用 malloc 进行内存随机分配,需要使用 memalign 、aligned_alloc、 valloc 等函数指定分配的内存起始地址。

#include <malloc.h>
#include <stdlib.h>
/*
    在内存中分配 size 长度的空间,该空间的起始位置为 alignment 的整数倍(必须是2的幂)
*/
void *memalign(size_t alignment, size_t size);

/*
    与 memalign 类似,唯一的不同在于 aligned_alloc 的 size 也必须是 alignment 的整数倍
*/
void *aligned_alloc(size_t alignment, size_t size);

/*
    与 memalign 类似,valloc 分配的内存起始默认为 页 的整数倍,等同于 memalign(sysconf(_SC_PAGESIZE),size)
*/
void * valloc (size_t size)

        在 NFS 场景下,一些特殊配置的内核可能不支持使用 Direct IO。NFS 场景下只有 Client 会越过系统缓存,Server端不会进行改变。客户端会要求服务器 I/O 同步,以保持 O_DIRECT 的同步语义。在这些情况下,某些服务器的性能会很差,尤其是在 I/O 很小的情况下。Linux NFS 客户端对 O_DIRECT I/O 没有对齐限制。

        下面是一个简单的使用 Demo,读取裸设备中的数据并写入指定的文件中。

#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define SECTOR_SIZE 512

int main(int argc, char *argv[]) 
    int offset = 0;
    int length = 5;
    int rc = -1;

    char *sector = aligned_alloc(SECTOR_SIZE, SECTOR_SIZE);
    memset(sector, 0, SECTOR_SIZE);
    int fd = open("/dev/sda1", O_RDWR | O_DIRECT);

    FILE *fp = fopen("/home/tp/test.txt","w");

    lseek(fd, offset, SEEK_SET);
    for (int i = 0; i < length; i++) 
        rc = read(fd, sector, SECTOR_SIZE);
        if (rc < 0)
            printf("sector read error at offset = %d + %d %s", offset, i, strerror(errno));
        printf("Sector: %d", i);
        fwrite(sector, 1, SECTOR_SIZE, fp);
    
    free(sector);
    close(fd);
    fclose(fp);

三、O_DIRECT 与 O_SYNC 

        O_DIRECT 上一章节已经介绍完成,下面简单介绍 O_SYNC。

        O_SYNC 表示打开同步 IO,在进行 write 时,在数据刷入磁盘之前会一直阻塞O_SYNC的同步是通过 fsync 函数实现的。使用 O_SYNC 后会浪费系统资源,会搜索缓存池内的脏页,强制将所有的脏页最终通过 submit_bio 提交给调度器,并等待完成。由于搜索算法的原因,有可能还会导致多余的IO操作。

        而 Direct IO 绕过内存直接将数据写入磁盘,但其只保证所有的 IO 下发到磁盘设备并完成,至于磁盘设备本身是否具备缓存等机制是无法感知的,其实对于 O_SYNC 来说也同样如此。

        还有一点就是 Direct IO针对读写两个操作,SYNC 只针对写操作。

以上是关于Direct IO的主要内容,如果未能解决你的问题,请参考以下文章

MMAP和DIRECT IO区别

MMAP和DIRECT IO区别

Oracle IO是否为Direct IO

Oracle IO是否为Direct IO

ORACLE Direct NFS安装

关于O_DIRECT的那些事儿