首页 > linux > open 的同步标记

open 的同步标记

open 是 Linux 下打开文件的标准 API, 这个 API 同时定义了很多文件操作的参数, 不同的参数对性能影响很大. 事实上, 对同步的参数来讲(O_SYNC 系列), 默认的参数 很快, 但是会损失一些功能, 比如 cache 的存在并没有真正的把修改的内容写入文件, 如果是异常关机可能导致磁盘数据和内存没有同步, 对某些应用(如虚拟机)意味着磁盘 可能损坏.

O_SYNC

同步 I/O 的标记, 执行 write 时, 保证数据被写入硬件才返回, 也就是说调用 write 写数据的时候, write 将被阻塞, 直到所有数据(包括文件内容和文件属性) 都写入了底层硬件.

O_DSYNC

和 O_SYNC 类似, 只不过这个标记保证的只是文件的内容被写入底层硬件, 并不保证 文件属性的同步.

O_DIRECT

POSIX 并没有包含这个标志, 在 Linux 上使用的时候必须要定义 _GNU_SOURCE 这个宏. 如果使用这个标志, 数据的传输将不会通过内核空间, 而是使用 DMA 直接在用户空间 到存储设备之间传送, 不保证数据是否同步. 所以 O_DIRECT 常和 O_SYNC 一起 保证数据的同步和效率.

测试

我写了一个简单的测试程序来测试以上参数对数据读写的影响.

测试结果
Flags Used Time
Default 3ms
O_SYNC 90000ms
O_DSYNC 30000ms
O_DIRECT 1ms
O_SYNC and O_DIRECT 1ms

测试代码:

#define _GNU_SOURCE
 
#include <sys/types.h>
#include <sys/stat.h>
#include 
#include 
#include 
#include 
 
/* #define USE_SYNC 1 */
/* #define USE_DSYNC 1 */
/* #define USE_DIRECT 1 */
 
void test()
{
    int fd;
    int i;
    int offset = 0;
    unsigned char buf[512] = "1234567890";
    int flags = 0;
 
#ifdef USE_DIRECT
    void *align_buf = NULL;
    if (posix_memalign(&align_buf, 512, sizeof(buf)) != 0) {
        perror("memalign failed");
        return;
    }
#endif
 
    flags = O_WRONLY | O_CREAT | O_TRUNC;
#ifdef USE_SYNC
    printf("USE O_SYNC flag\n");
    flags |= O_SYNC;
#endif
#ifdef USE_DSYNC
    printf("USE O_DSYNC flag\n");
    flags |= O_DSYNC;
#endif
#ifdef USE_DIRECT
    printf("USE O_DIRECT flag\n");
    flags |= O_DIRECT;
#endif
    fd = open("/tmp/test.bin", flags, 0644);
 
    if (fd == -1) {
        perror("Create file failed");
        return;
    }
    if (ftruncate(fd, 10 * 1024 * 1024)) {
        goto cleanup;
    }
 
    for (i = 0; i < 2048; ++i) {
#ifdef USE_DIRECT
        int len = pwrite(fd, align_buf, sizeof(buf), offset);
#else
        int len = pwrite(fd, buf, sizeof(buf), offset);
#endif
    if (len < 0) {
            perror("failed to write");
            break;
    } else if (len == 0) {
            break;
            /* ? */
        }
        offset += len;
    }
 
cleanup:
    if (fd != -1) {
        close(fd);
    }
#ifdef USE_DIRECT
    if (align_buf) {
        free(align_buf);
    }
#endif
}
 
int main(int argc, char *argv[])
{
    test();
    return 0;
}
分类: linux 标签:
  1. 2013年6月13日19:55 | #1

    I love what you guys tend to be up too. This sort of clever work and reporting! Keep up the awesome works guys I’ve added you guys to my blogroll.

  2. jasonli
    2014年4月26日16:33 | #2

    这个测试显然是错误的,不可能有这么大的性能差别。
    使用O_DIRECT打开文件后,write操作的buffer的起始地址和长度都必须按512字节对齐,否则write系统调用会返回-1,errno为EINVAL
    显然你并没有判断 pwrite的返回值

    BTW,测试结果的时间单位应该是微秒(us)吧?怎么可能是ms

  3. mathslinux
    2014年4月30日09:06 | #3

    @jasonli 谢谢你的提示, O_DIRECT 的用法的确是错误了, 没有对齐, 但是测试结果没有问题, 我更新了一下代码(O_DIRECT部分的), 把原始测试结果发给你看看:

    # time ./test

    real 0m0.004s
    user 0m0.000s
    sys 0m0.004s
    # ls -lh /tmp/test.bin; du -h /tmp/test.bin
    -rw-r–r– 1 root root 10M Apr 30 04:46 /tmp/test.bin
    1.0M /tmp/test.bin

    # time ./test
    USE O_SYNC flag

    real 1m17.341s
    user 0m0.000s
    sys 0m0.305s
    # ls -lh /tmp/test.bin; du -h /tmp/test.bin
    -rw-r–r– 1 root root 10M Apr 30 04:49 /tmp/test.bin
    1.0M /tmp/test.bin

    # time ./test
    USE O_DSYNC flag

    real 0m32.782s
    user 0m0.002s
    sys 0m0.149s
    # ls -lh /tmp/test.bin; du -h /tmp/test.bin
    -rw-r–r– 1 root root 10M Apr 30 04:50 /tmp/test.bin
    1.0M /tmp/test.bin

    # time ./test
    USE O_DIRECT flag

    real 0m0.464s
    user 0m0.000s
    sys 0m0.032s
    # ls -lh /tmp/test.bin; du -h /tmp/test.bin
    -rw-r–r– 1 root root 10M Apr 30 04:51 /tmp/test.bin
    1.0M /tmp/test.bin

  1. 本文目前尚无任何 trackbacks 和 pingbacks.