O_DIRECT lseek() read()

O_DIRECT + lseek(), read() 报错

问题描述

使用lseek()函数设置所需要的偏移量,使用read() O_DIRECT之后,read返回值是-1。

解决方案

O_DIRECT之后,读取就只能是以页大小(我这里页大小是4K)为单位读取,也必须以页大小对齐(因此注意设置buf为memalign方式分配内存空间)。lseek()的偏移量,也必须以4K对齐,才能运行,否则int ret = read(); 则有ret = -1。

具体背景 + 代码

程序十分简单,就是测试read 和 write使用 O_DIRECT(不经过缓冲区,直接读写磁盘)的时间。

以下C++代码粘贴到test_direct_io.cpp中,用如下命令运行:

g++ -o exec_test_direct_io test_direct_io.cpp

C++代码。

// g++ -o exec_test_direct_io test_direct_io.cpp
#include <errno.h>
#include <fcntl.h>
#include <malloc.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/io.h>
#include <sys/time.h>
#include <sys/unistd.h>
#include <iostream>
#define _GNU_SOURCE  //测试宏
using namespace std;

void rawIO() {
  int fd;
  size_t length;
  char *buf;
  length = getpagesize() * 1024 * 16 * 15;
  cout << "page size:" << getpagesize() << endl;
  cout << "length:" << (float)length / 1024 / 1024 << "MB" << endl;
  string file_path = "/data/helo.txt";
  fd = open(file_path.c_str(), O_RDWR | O_DIRECT);
  if (fd == -1) {
    printf("failed to open file %s\n", file_path.c_str());
    exit(-1);
  }
  //函数:void * memalign (size_t boundary, size_t size)
  //函数memalign将分配一个由size指定大小,地址是boundary的倍数的内存块。参数boundary必须是2的幂!
  // 函数memalign可以分配较大的内存块,并且可以为返回的地址指定粒度。
  buf = (char *)memalign(getpagesize(), length);
  //   buf = (char *)malloc(sizeof(uint8_t) * length);
  if (buf == NULL) {
    printf("buf = null\n");
    exit(-1);
  }
  //   for (int i = 0; i < length; i++) {
  //     buf[i] = 'a';
  //   }
  //   int ret = write(fd, (void *)buf, length);
  //   lseek(fd, 2, SEEK_SET);
  int ret = read(fd, buf, length);
  if (ret == -1) {
    std::cout << "write fail" << std::endl;
    printf("err: %s \n", strerror(errno));
    exit(0);
  } else {
    cout << "write over!" << endl;
  }
  free(buf);
}

// void buffered_IO() {}

int main() {
  struct timeval t_s, t_e;
  gettimeofday(&t_s, 0);
  rawIO();
  gettimeofday(&t_e, 0);
  float total_t = 0;
  total_t +=
      t_e.tv_sec - t_s.tv_sec + (float)(t_e.tv_usec - t_s.tv_usec) / 1000000;
  cout << "total time = " << total_t << endl;
}

其中的这个部分是用来生成数据集(并测试写入速度)的,使用的时候可以将read部分注释,这部分↓取消注释。

  //   for (int i = 0; i < length; i++) {
  //     buf[i] = 'a';
  //   }
  //   int ret = write(fd, (void *)buf, length);

其中lseek(fd, 2, SEEK_SET);报错的原因是,使用O_DIRECT后必须使用页大小(4KB)对齐,所以**“文件头 + 2”的偏移肯定不行,只有“文件头 + 4KB整数倍”的偏移才能运行**。

…被这个问题困扰了一晚上,难顶…

蔚天灿雨的Debug实录 2022-05-07 HITSZ LEVI

后记

原本主要是为了解决系统主动缓存带来的HDD速度过快的问题,后面发现使用direct_io运行速度过慢,改用运行时清除磁盘缓存,每读一个文件之前,都清理一次缓存,这样保证缓存中总有(且仅有)一个文件存在。具体内容可以看我的下一篇博客Linux用代码清理磁盘缓存(运行时清理磁盘缓存)
蔚天灿雨 HITSZ LEVI 2022-05-09

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值