【转】open参数O_DIRECT的学习
free:这里好几个方法我都没测试成功,最后还是用posix_memalign 对齐的方法成功执行了,贴上代码
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 //O_DIRECT 5 #define __USE_GNU 1 6 #include <fcntl.h> 7 #include <unistd.h> 8 #include <stdlib.h> 9 10 int main(void) 11 { 12 long i; 13 const long SIZE = 512*16; 14 int count=512; 15 char *buf; 16 posix_memalign((void*)&buf,512,SIZE*count); 17 int fd = open("/dev/pcissda",O_RDWR|O_DIRECT); 18 for(i=0;i<count;i++) 19 write(fd,buf,SIZE); 20 free(buf); 21 close(fd); 22 return 0; 23 }
open有两个原形:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
这三个参数比较容易看出它们的含义,pathname是文件路径,flags打开文件的标志, mode是打开的模式,返回值应该是打开文件的句柄。
flags标志有下面的定义:
O_RDONLY 以只读的方式打开文件
O_WRONLY 以只写的方式打开文件
O_RDWR 以读写的方式打开文件
O_APPEND 以追加的方式打开文件
O_CREAT 创建一个文件
O_EXEC 如果使用了O_CREAT而且文件已经存在,就会发生一个错误
O_NOBLOCK 以非阻塞的方式打开一个文件
O_TRUNC 如果文件已经存在,则删除文件的内容
O_RDONLY、O_WRONLY、O_RDWR三个标志只能使用任意的一个。
扩展阅读: http://blog.csdn.net/yao_guet/article/details/6460900
open的flags参数中,有一个打开标识:O_DIRECT
网上对于该标志的解释如下:
O_DIRECT (Since Linux 2.4.10)
Try to minimize cache effects of the I/O to and from this file. In general this
will degrade performance, but it is useful in special situations, such as when
applications do their own caching. File I/O is done directly to/from user space
buffers. The I/O is synchronous, that is, at the completion of a read(2) or
write(2), data is guaranteed to have been transferred. See NOTES below for
further discussion.
A semantically similar (but deprecated) interface for block devices is described
in raw(8).
翻译:
该标示是为了写文件或者读文件的I/O高度缓存开销的最小化。一般情况下,该标志会降低性能,但是,在特殊情况下,还是有作用的,例如当应用程序使用自己的高速缓存的时候。文件I/O直接接触到用户内存。I/O操作是同步的,也就是说,一旦read(2)或者write(2)完成,数据可以保证传输完毕。见下面的注释关于更多的讨论。
一个语义相似(但是已经弃用)的块设备处理的接口函数是详见raw(8).
CONFORMING TO
SVr4, 4.3BSD, POSIX.1-2001. The O_DIRECTORY, O_NOATIME, and O_NOFOLLOW flags are
Linux-specific, and one may need to define _GNU_SOURCE to obtain their definitions.
The O_CLOEXEC flag is not specified in POSIX.1-2001, but is specified in POSIX.1-2008.
O_DIRECT is not specified in POSIX; one has to define _GNU_SOURCE to get its defini-
tion.
NOTES
O_DIRECT
The O_DIRECT flag may impose alignment restrictions on the length and address of
userspace buffers and the file offset of I/Os. In Linux alignment restrictions vary by
file system and kernel version and might be absent entirely. However there is cur-
rently no file system-independent interface for an application to discover these
restrictions for a given file or file system. Some file systems provide their own
interfaces for doing so, for example the XFS_IOC_DIOINFO operation in xfsctl(3).
Under Linux 2.4, transfer sizes, and the alignment of the user buffer and the file off-
set must all be multiples of the logical block size of the file system. Under Linux
2.6, alignment to 512-byte boundaries suffices.
The O_DIRECT flag was introduced in SGI IRIX, where it has alignment restrictions simi-
lar to those of Linux 2.4. IRIX has also a fcntl(2) call to query appropriate align-
ments, and sizes. FreeBSD 4.x introduced a flag of the same name, but without align-
ment restrictions.
O_DIRECT support was added under Linux in kernel version 2.4.10. Older Linux kernels
simply ignore this flag. Some file systems may not implement the flag and open() will
fail with EINVAL if it is used.
Applications should avoid mixing O_DIRECT and normal I/O to the same file, and espe-
cially to overlapping byte regions in the same file. Even when the file system cor-
rectly handles the coherency issues in this situation, overall I/O throughput is likely
to be slower than using either mode alone. Likewise, applications should avoid mixing
mmap(2) of files with direct I/O to the same files.
The behaviour of O_DIRECT with NFS will differ from local file systems. Older kernels,
or kernels configured in certain ways, may not support this combination. The NFS pro-
tocol does not support passing the flag to the server, so O_DIRECT I/O will only bypass
the page cache on the client; the server may still cache the I/O. The client asks the
server to make the I/O synchronous to preserve the synchronous semantics of O_DIRECT.
Some servers will perform poorly under these circumstances, especially if the I/O size
is small. Some servers may also be configured to lie to clients about the I/O having
reached stable storage; this will avoid the performance penalty at some risk to data
integrity in the event of server power failure. The Linux NFS client places no align-
ment restrictions on O_DIRECT I/O.
In summary, O_DIRECT is a potentially powerful tool that should be used with caution.
It is recommended that applications treat use of O_DIRECT as a performance option which
is disabled by default.
"The thing that has always disturbed me about O_DIRECT is that the whole inter-
face is just stupid, and was probably designed by a deranged monkey on some
serious mind-controlling substances." — Linus
纵使加上open需要所有的头文件,但是,如果使用该标志的话,还需要在引用头文件“fcntl.h”之前,添加:
//O_DIRECT
#define __USE_GNU 1
#include <fcntl.h>
否则,依旧会出现类似下面的错误:
file.c:28: 错误:‘O_DIRECT’未声明(在此函数内第一次使用)
file.c:28: 错误:(即使在一个函数内多次出现,每个未声明的标识符在其
file.c:28: 错误:所在的函数内也只报告一次。)
扩展阅读: http://bbs.chinaunix.net/thread-683493-1-1.html (O_DIRECT未声明错误解决)
写入数据在用户内存中的首地址要为512的整数倍(如果为块大小的整数倍就更没有问题),所以不能使用数组保存数据,而要使用malloc来申请空间。
const int SIZE = 4096*10(每次写入文件数据块大小)
char *p = malloc(SIZE+512); //多申请512空间
char *q = 0;
q = p&(~511);// 使q是512字节对齐的,q空间的地址范围为SIZE。
int fd = open("文件路径", O_RDWR|O_DIRECT);//省略打开失败判断
write(fd, q, SIZE);//写入
扩展阅读: http://blog.csdn.net/hhtang/article/details/6605951
mmap分配的话,首地址就会是4K对齐。
malloc分配的内存,不管多大,开始地址都不固定。
因此,如果使用malloc,那么,就需要内存首地址的对齐。如果使用mmap的话,则不用考虑这些。
在使用write写入文件的时候:
ssize_t write(int fd, const void *buf, size_t count);
参数count也应该是512的整数倍,因此,如果我希望写count个字节的话,那么就需要在write之前,进行如下的操作:
uint32_t wLen = (pos + 511) & ~511U;
write(fd, buffer, wLen);
如果是512倍数大小写入文件的话,文件的内容不一定是512的整数倍,因此,最后一步对于文件的处理就是:
truncate(fullpath, count);
注意:
1、因为文件的长度是按照512对齐的,而文件内容大小不一定是512的整数倍,因此最后需要进行truncate处理
2、因为写入文件是按照512对齐的,所以,追加文件时,内存开始的地址不一定是512的倍数,因此,该方式不适合文档内容的追加,只适合一次性写入文档。
3、使用“O_DIRECT”参数适合一次性写入文件内容比较大的情况,平常情况下,无需使用该标志。
下面我的一个测试用例,数据只是简单地几十个字节,只是为了说明使用方法罢了。如下:
/****************************************************************** * @file file_O_DIRECT.c * @version v1.0 * @author ymm * @date 2015/01/29 * @brief open函数O_DIRECT的使用 * @history * 1、2015/01/29 author ymm 初步完成 ******************************************************************/ #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> //O_DIRECT #define __USE_GNU 1 #include <fcntl.h> #include <stdint.h> #include <sys/mman.h> #include <errno.h> #define READFILE_BUFFER_SIZE 1 * 1024 * 1024 char fullpath[20]="a.txt"; int Deal() { int fd = open(fullpath, O_DIRECT|O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); if( fd < 0 ){ printf("Fail to open output protocol file: \'%s\'", fullpath); return -1; } uint32_t bufferLen = (READFILE_BUFFER_SIZE) ; char *buffer = (char *)mmap(0, bufferLen, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); if ( MAP_FAILED == buffer ) { printf("mmap error!errno=%d\n",errno); return -1; } //mmap返回地址一直4k对齐 printf("mmap first address:%p\n",buffer); uint32_t address=(uint32_t)buffer; printf("address=%u;address%4096=%u\n",address,address%4096); uint32_t pos = 0, len = 0; len = snprintf((char *)(buffer + pos), 128, "my name is yangmingmingming\r\n"); pos += len; if( pos ){ buffer[pos] = '\0'; printf("pos=%d,buffer=%s\n",pos,buffer); uint32_t wLen = (pos + 4095) & ~4095U; write(fd, buffer, wLen); truncate(fullpath, pos); } if( buffer ){ munmap(buffer, bufferLen); buffer = NULL; } if( fd > 0 ){ close(fd); } return 0; } int main() { Deal(); return 0; }