Direct IO

目录

一、基本介绍

二、使用方法与Demo

三、O_DIRECT 与 O_SYNC 

四、Direct IO 与 缓存 IO 写性能 对比


一、基本介绍

Direct IO_第1张图片

         如上图所示,普通的 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 
#include 
/*
    在内存中分配 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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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_第2张图片

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

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

四、Direct IO 与 缓存 IO 写性能 对比

        直接IO和带缓存的IO在写性能方面有很大的区别:

        直接IO是指数据直接从应用程序的缓存区写入到磁盘上,没有经过内核缓存的过程。这种方式可以减少内核缓存的开销,但是需要频繁的访问磁盘,因此会导致更多的磁盘寻道和旋转延迟,从而降低写入性能。

        带缓存的IO是指数据先写入内核缓存,然后再由内核缓存写入磁盘。这种方式可以减少频繁访问磁盘的次数,从而提高写入性能。但是带缓存的IO也会带来一些额外的开销,如缓存管理和同步等,可能会影响性能。

        总体来说,直接IO适用于需要快速写入大量数据的场景,如数据库的日志写入。而带缓存的IO适用于需要频繁读写小量数据的场景,如文件的读写操作。

你可能感兴趣的:(项目demo,操作系统,DIRECT,IO,O_DIRECT,O_SYNC,文件读写,穿透写)