linux open系统调用的O_DIRECT标记

前言

open系统调用中针对打开的文件描述符,可以增加一个O_DIRECT标记,该标记能够使得针对该文件描述符的写操作绕过操作系统page cache,直接进入通用块设备层,从而减少页缓存对IO效率的影响。

但是针对O_DIRECT标记有一个问题,数据及其元数据尝试落盘但是无法保证完整落盘,如果保证落盘,则需要O_SYNC的标记支持,即IO到达块设备才会返回,来确保用户态的数据安全性。

代码测试

测试代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*打印当前时间*/
void printDatetime()
{
    while(1) {
            time_t now;
            struct tm *tm_now;
            time(&now);
            tm_now = localtime(&now);
            printf("now datetime: %d-%d-%d %d:%d:%d\n", tm_now->tm_year, tm_now->tm_mon, tm_now->tm_mday, tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec);
            fflush(stdout);
            sleep(1);
    }
}

int main(int argc, char* argv[])
{
        char *file_path;
        char *file_path_new;

        if ((argc > 1 && argv[1]) || argv[2] ) {
                file_path = argv[1];
                file_path_new = argv[2];
        } else {
                printf("no file path\n");
                _exit(1);
        }
        printf("%s\n",argv[1]);

        pthread_t t1;
        int err = pthread_create(&t1,NULL,printDatetime,NULL);
        if (err) {
                printf("create thread error \n");
                _exit(-1);
        }

        int fd;
        fd = open(file_path,O_WRONLY |O_CREAT |O_APPEND | O_DIRECT);
        if (fd == -1) {
                printf("open failed\n");
                _exit(-1);
        }

        int fd2;
        fd2 = open(file_path_new,O_RDWR |O_CREAT |O_APPEND | O_DIRECT);
        if (fd2 == -1) {
                printf("open failed\n");
                _exit(-1);
        }


        while(1) {
                int len = write(fd,"something\n",sizeof("something\n"));
                if (len == -1) {
                        printf("write failed\n");
                }
                printf("write_old success\n");

                int len_new = write(fd2,"write to new file something\n",sizeof("write to new file something\n"));
                if (len_new == -1) {
                        printf("write failed\n");
                }
                printf("write_new success\n");
                sleep(1);
        }
        close(fd);
        close(fd2);

        return 0;
}

注意,关于O_DIRECT系统调用除了包含头文件fcntl.h之外,编译时需加入-D_GNU_SOURCE参数
以上代码编译如下:gcc test_write.c -o write -lpthread -D_GNU_SOURCE

  • 用于传递数据的缓冲区,其内存边界必须对齐为逻辑块大小的整数倍
    man 2 open原文描述如下
    Under Linux 2.4, transfer sizes, and the alignment of the user buffer and the file offset must all be multiples of the logical block size of the file system. Under Linux 2.6, alignment to 512-byte boundaries suffices
    在2.4版本及以下 4096B对齐,而2.6以后都是512B
    关于查看逻辑块大小使用如下命令

    [root@node1 ~]# dumpe2fs /dev/hda5|grep -i "block size"
    dumpe2fs 1.42.9 (28-Dec-2013)
    Block size:               4096
    

    如果要写入裸盘文件可以使用如下命令查看逻辑块大小,

    [root@node1 ~]# parted /dev/sdb
    GNU Parted 3.1
    Using /dev/sdb
    Welcome to GNU Parted! Type 'help' to view a list of commands.
    (parted) p                                                                
    Model: ATA WDC WD121PURX-78 (scsi)
    Disk /dev/sdb: 12.0TB
    Sector size (logical/physical): 512B/4096B
    Partition Table: gpt
    Disk Flags: 
    
  • 数据传输的开始点,即文件和设备的偏移量,必须是逻辑块大小的整数倍

  • 待传递数据的长度必须是逻辑块大小的整数倍。

以上代码写入的数据存储引擎已经封装好了4K的边界对齐和块对齐,所以未设置4k边界对齐和写入数据块对齐

dd测试

使用dd对direct标记进行测试

[root@node1 ~]# dd if=/dev/zero of=/nas/cephfs/n1/test_dd bs=1M count=512
512+0 records in
512+0 records out
536870912 bytes (537 MB) copied, 0.284967 s, 1.9 GB/s
[root@node1 ~]# dd if=/dev/zero of=/nas/cephfs/n1/test_dd bs=1M count=512 oflag=direct
512+0 records in
512+0 records out
536870912 bytes (537 MB) copied, 11.519 s, 46.6 MB/s

以上数据可见,如果不加direct标记,则数据会经过页缓存cat /proc/meminfo |grep -i dirty来检测是否写入,因为操作系统页缓存处理速度肯定快,所以dd消耗的时间就少

而使用direct标记之后,会绕过操作系统缓存,数据直接交由磁盘处理,此时磁盘数据速度肯定会慢,所以耗费的时间就久

你可能感兴趣的:(#,编程语言C,编程语言)