再议O_DIRECT, O_DIRECT与logical_block_size
2014-12-05 15:16
295 查看
引言
之前我们说过一篇关于O_DIRECT问题的文章http://blog.csdn.net/zr_lang/article/details/40982663,那篇是在编译的时候需要注意的问题,编译之后就要运行,关于DIRECT I/O的使用可能很少有人会关注,特别是在C语言里直接使用。如果不是遍写很底层的I/O代码可能不会用到direct io来做什么操作,大部分也都是用默认的buffer io。前不久我们在测试的时候发现LTP也有使用DIRECT I/O的错误,在某些体系结构上会造成运行错误,当然那个问题已经被我旁边的同事发patch修了。但是我前不久在写一个程序的时候也遇到了DIRECTI/O读写失败的问题,一段在x86_64等机器上运行良好的程序放在s390上时偶尔会出错,经过调试发现问题就是出现DIRECT I/O的使用上。下面让我们来详细说一下这个问题。
首先我们来看一段在Buffer I/O中最常见的程序:
#define BUF_SIZE 1024*1024 char *buf = NULL; buf = malloc(BUF_SIZE); memset(buf, 0, BUF_SIZE); fd = open(filename, O_RDONLY); if (fd < 0) { perror("open"); exit(1); } ret = read(fd, buf, count); if (ret < 0) { perror("read"); exit(1); }
这可能是Buffer I/O中很常见的写法,但是仅仅这样是不能满足Direct I/O的需要的。
DIRECT I/O操作
条件
Direct I/O至少需要先满足两个条件:1、 buf必须是一个以一倍或几倍于logical_block_size的大小对齐。
2、 每一次read的count必须是logical_block_size的倍数。
根据这两条规则我们需要对上面的程序做修改。
得到logical_block_size
首先我们需要知道logical_block_size的大小,logical_block_size跟设备本身相关,所以需要通过kernel得到device的logical_block_size。从用户空间有这样几种方式:1、 blockdev --getss $devname。
2、 cat /sys/block/$(basename $devname)/queue/logical_block_size。
3、 通过ioctl系统调用的BLKSSZGET命令。
目前我知道这三种方式,但是应该不限于此。这三个方法算比较通用的,写脚本可以使用前两种,写C程序可以读/sys/下的文件,也可以用ioctl系统调用。我们用ioctl来举例,代码如下:
#include <sys/ioctl.h> #include <linux/fs.h> int lbs = 0; fd = open(devname, O_RDONLY); ioctl(fd, BLKSSZGET, &lbs);
ioctl的BLKSSZGET命令读取devname的logical_block_size存入lbs中。BLKSSZGET定义在#include <linux/fs.h>中。
内存对齐
有了logical_block_size的数值,我们需要按照其大小对齐内存,我们借助posix_memalign来分配一段以logical_block_size对齐的内存空间,代码如下:char *buf; posix_memalign((void **)&buf, lbs, BUF_SIZE);
posix_memalign将分配BUF_SIZE大小的空间给buf,并且以lbs大小对齐。
DIRECT I/O的改动
#define BUF_SIZE 1024*1024 char *buf = NULL; int lbs = 0; int fd; fd = open(devname, O_RDONLY); if (fd < 0) { perror("open devname"); exit(1); } if (ioctl(fd, BLKSSZGET, &lbs) < 0) { perro("ioctl"); exit(1); } close(fd); posix_memalign((void **)&buf, lbs, BUF_SIZE); memset(buf, 0, BUF_SIZE); fd = open(filename, O_RDONLY); if (fd < 0) { perror("open filename"); exit(1); } ret = read(fd, buf, lbs); if (ret < 0) { perror("read"); exit(1); }
对齐不一定非要以一倍lbs的大小对齐,也可以是几倍,read的时候也是如此。而且read可以加上循环,我这里只是示意一下代码。
结尾
以上以read为例简单解释了一下DIRECT I/O的操作,write也请参考以上的叙述。 另外还有一个问题,我在ppc64和s390上测试发现如果在ioctl的时候传入的最后一个变量是long类型的,那么得到的logical_block_size的数值将存在long类型变量的高32位上。目前我还不知道这是否是一个bug,后续我将确认此事,如果后续的kernel不再存在这一问题也请知道的人告诉我。相关文章推荐
- 关于Oracle asmlib配置文件中的ORACLEASM_USE_LOGICAL_BLOCK_SIZE参数:
- ORA-27046: file size is not a multiple of logical block size
- Linux中的physical_block_size和logical_block_size
- error:unable to get logical block size for spfile
- ORA-27046: file size is not a multiple of logical block size
- 关于innodb_flush_log_at_trx_commit、innodb_flush_method、innodb_log_block_size和fsync()、O_DIRECT、iops、云盘的关系与总结
- Oracle中实现透视表 根据ERN的修改后的在Block_size 16K下我实现了56万行数据透视.
- stripcslashes stripslashes mcrypt_get_block_size
- Log Block Size
- 获得Linux文件系统的block size
- 使用font-size:0 来去掉inline-block元素之间的空隙
- 将list按blockSize大小等分,最后多余的单独一份
- oracle 数据文件最大容量由block_size决定
- 遇到502错误,invalid request block size 解决方法
- Java_JVM参数-XX:MaxDirectMemorySize 与 两种 ByteBuffer: heap,direct ByteBuffer
- Physical read + Logical read(DB block gets ,consistent gets )
- 查询dba_data_files遇到ORA-00379: no free buffers available in buffer pool DEFAULT for block size 2K
- ORA-00379: no free buffers available in buffer pool DEFAULT for block size 32K
- invalid request block size: 21573 (max 4096)...skip错误原因
- uwsgi invalid request block size: 的解决办法