欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

open参数O_DIRECT的学习 有大用

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”参数适合一次性写入文件内容比较大的情况,平常情况下,无需使用该标志。


   

下面我的一个测试用例,数据只是简单地几十个字节,只是为了说明使用方法罢了。如下:


   


   

  1. /******************************************************************
  2. *  @file       file_O_DIRECT.c
  3. *  @version    v1.0
  4. *  @author     ymm
  5. *  @date       2015/01/29
  6. *  @brief      open函数O_DIRECT的使用
  7. *  @history      
  8. *  1、2015/01/29  author ymm    初步完成
  9. ******************************************************************/
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include <sys/types.h>
  13. #include <sys/stat.h>
  14. //O_DIRECT
  15. #define __USE_GNU 1
  16. #include <fcntl.h>
  17. #include <stdint.h>
  18. #include <sys/mman.h>
  19. #include <errno.h>
  20. #define READFILE_BUFFER_SIZE 1 * 1024 * 1024
  21. char fullpath[20]="a.txt";
  22. int Deal()
  23. {
  24. int fd = open(fullpath, O_DIRECT|O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
  25. if( fd < 0 ){
  26. printf("Fail to open output protocol file: \'%s\'", fullpath);
  27. return -1;
  28. }
  29. uint32_t bufferLen = (READFILE_BUFFER_SIZE) ;
  30. char *buffer = (char *)mmap(0, bufferLen, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
  31. if ( MAP_FAILED == buffer )
  32. {
  33. printf("mmap error!errno=%d\n",errno);
  34. return -1;
  35. }
  36. //mmap返回地址一直4k对齐
  37. printf("mmap first address:%p\n",buffer);
  38. uint32_t address=(uint32_t)buffer;
  39. printf("address=%u;address%4096=%u\n",address,address%4096);
  40. uint32_t pos = 0, len = 0;
  41. len = snprintf((char *)(buffer + pos), 128, "my name is yangmingmingming\r\n");
  42. pos += len;
  43. if( pos ){
  44. buffer[pos] = '\0';
  45. printf("pos=%d,buffer=%s\n",pos,buffer);
  46. uint32_t wLen = (pos + 4095) & ~4095U;
  47. write(fd, buffer, wLen);
  48. truncate(fullpath, pos);
  49. }
  50. if( buffer ){
  51. munmap(buffer, bufferLen);
  52. buffer = NULL;
  53. }
  54. if( fd > 0 ){
  55. close(fd);
  56. }
  57. return 0;
  58. }
  59. int main()
  60. {
  61. Deal();
  62. return 0;
  63. }

   

来自 https://blog.csdn.net/yang15225094594/article/details/43268133    


   

文件操作O_DIRECT使用

           
字数 724阅读 253                


一般如果在Linux内核中读写一个文件,其IO流程都需要经过Kernel内的page cache层次,若想要使用自己开发的缓存系统,那么就可以在打开这个文件的时候,对该文件加以O_DIRECT的标志位,这样一来就可以让程序对该文件的IO直接在磁盘上进行,从而避开了Kernel的page cache,进而对IO流程里的块数据进行拦截,让其流入到自己开发的缓存系统内。
O_DIRECT参数适用要求:

O_DIRECT
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, i.e., at the completion of a read(2) or write(2), data is guaranteed to have been transferred. Under Linux 2.4 transfer sizes, and the alignment of user buffer and file offset must all be multiples of the logical block size of the file system.

比较重要的地方:
将IO数据写到自己的cache的时候,cache所分配的内存的大小和首地址必须是块对齐的,毕竟是直接和磁盘打交道,数据都是一块一块的,地址映射什么的都没了。如果用普通的malloc的方法去分配就无法做到了,否则会出现数据无法正确写入到磁盘的囧状,
可以用valloc 或者memalign来分配空间对其的内存,其中valloc分配的是页对齐的内存。
引用:

OPEN(2) Linux Programmer's Manual OPEN(2)
NAME top
open, openat, creat - open and possibly create a file        

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 O_DIRECT flag on its own makes an effort
to transfer data synchronously, but does not give the
guarantees of the O_SYNC flag that data and necessary metadata
are transferred. To guarantee synchronous I/O, O_SYNC must be
used in addition to O_DIRECT. See NOTES below for further
discussion.A semantically similar (but deprecated) interface for block
devices is described in raw(8).
O_DIRECTORY
If pathname is not a directory, cause the open to fail. This
flag was added in kernel version 2.1.126, to avoid denial-of-
service problems if opendir(3) is called on a FIFO or tape
device.
O_DSYNC
Write operations on the file will complete according to the
requirements of synchronized I/O data integrity completion.
By the time write(2) (and similar) return, the output data has
been transferred to the underlying hardware, along with any
file metadata that would be required to retrieve that data
(i.e., as though each write(2) was followed by a call to
fdatasync(2)). See NOTES below.

来自 http://man7.org/linux/man-pages/man2/open.2.html        

WRITE(2) Linux Programmer's Manual WRITE(2)
NAME top
write - write to a file deaaaaaor        

EINVAL fd is attached to an object which is unsuitable for writing;
or the file was opened with the O_DIRECT flag, and either the
address specified in buf, the value specified in count, or the
file offset is not suitably aligned.

来自 http://man7.org/linux/man-pages/man2/write.2.html        

关于地址对齐可以看之前写的文章 linux memalign、valloc函数        


来自  https://www.jianshu.com/p/7c891a002a4e    


   

UNIX高级环境编程(14)文件IO - O_DIRECT和O_SYNC详解

                   
zdy0_2004 2016-04-12 12:57:02  4996  收藏 3                    
分类专栏: Linux 文章标签: unix                    


http://www.cnblogs.com/suzhou/p/5381738.html
               

春天来了,除了工作学习,大家也要注意锻炼身体,多出去运动运动。 

上周末在元大都遗址公园海棠花溪拍的海棠花。

DSC 2639 01                        

 

进入正题。

O_DIRECT和O_SYNC是系统调用open的flag参数。通过指定open的flag参数,以特定的文件描述符打开某一文件。

这两个flag会对写盘的性能有很大的影响,因此对这两个flag做一些详细的了解。

先看一个open函数的使用例子.

1
2
3
4
5
6
/* Open new or existing file for reading and wrting,                                                            
     sync io and no buffer io; file permissions read+                                                            
     write for owner, nothing for all others */                                                            
fd = open( "myfile" , O_RDWR | O_CREAT | O_SYNC | O_DIRECT, S_IRUSR | S_IWUSR);                                                            
if  (fd == -1)                                                            
     errExit( "open" );                                                            
O_DIRECT: 无缓冲的输入、输出。
O_SYNC:以同步IO方式打开文件。
下面对这两个flag做一些详细的说明。
 

一,O_DIRECT,绕过缓冲区高速缓存,直接IO

直接IO:Linux允许应用程序在执行磁盘IO时绕过缓冲区高速缓存,从用户空间直接将数据传递到文件或磁盘设备,称为直接IO(direct IO)或者裸IO(raw IO)。
应用场景:数据库系统,其高速缓存和IO优化机制均自成一体,无需内核消耗CPU时间和内存去完成相同的任务。
使用直接IO的弊端:可能会大大降低性能,内核对缓冲区告诉缓存做了不少优化,包括:按顺序预读取,在成簇磁盘块上执行IO,允许访问同一文件的多个进程共享高速缓存的缓冲区。
使用方法:在调用open函数打开文件或设备时指定O_DIRECT标志。
注意可能发生的不一致性:若一进程以O_DIRECT标志打开某文件,而另一进程以普通(即使用了高速缓存缓冲区)打开同一文件,则由直接IO所读写的数据与缓冲区高速缓存中内容之间不存在一致性,应尽量避免这一场景。
 
使用直接IO需要遵守的一些限制:
  • 用于传递数据的缓冲区,其内存边界必须对齐为块大小的整数倍

  • 数据传输的开始点,即文件和设备的偏移量,必须是块大小的整数倍

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

不遵守上述任一限制均将导致EINVAL错误。

 

二,O_SYNC,以同步方式写入文件

功能:强制刷新内核缓冲区到输出文件。这是有必要的,因为为了数据安全,需要确保将数据真正写入磁盘或者磁盘的硬件告诉缓存中。

我们先熟悉一下同步IO相关定义和系统调用。

同步IO数据完整性和同步IO文件完整性

同步IO的定义:某一IO操作,要么已成功完成到磁盘的数据传递,要么被诊断为不成功。
SUSv3定义的两种同步IO完成类型(此处用英文,因为译者也忍无可忍用了原文…)
  • synchronized IO data integrity completion:确保针对文件的一次更新传递了足够的信息(部分文件元数据)到磁盘,以便于之后对数据的获取。

  • synchronized IO file integrity completion:确保针对文件的一次更新传递了所有的信息(所有文件元数据)到磁盘,即使有些在后续对文件数据的操作并不需要。

用于控制文件IO内核缓冲的系统调用

1 fsync

作用:fsync()系统调用将使缓冲数据和fd相关的所有元数据都刷新到磁盘上。调用fsync会强制使文件处于Synchronized IO file integrity completion状态。
函数声明:
1
2
#include                                                                    
int  fsync( int  fd);                                                                    

函数返回值:
  • 0: success

  • -1: error

返回时间:仅在对磁盘设备(或者至少是其高速缓存)的传递完成后,fsync()调用才会返回。
 
2 fdatasync
作用:fdatasync()系统调用的作用类似fsync(),只是强制文件处于synchronized IO data integrity compeletion状态。
函数声明:
1
2
#include                                                                        
int  fdatasync( int  fd);                                                                        

函数返回值:
  • 0: success

  • -1: error

与fsync的区别:fdatasync()可能会减少磁盘操作的次数,由fsync()调用请求的两次变成一次。例如,修改了文件的数据,而文件大小不变,那么调用fdatasync调用请求只强制进行了数据更新,相比之下,fsync()调用会强制将元数据传递到磁盘上,而元数据和文件数据通常驻留在磁盘的不同区域,更新这些数据需要反复在整个磁盘上执行寻道操作。
 
3 sync系统调用
作用:sync()系统调用会使包含更新文件信息的所有内核缓冲区(即数据块、指针块、元数据等)刷新到磁盘上。
函数声明:
1
2
#include                                                                        
void  sync( void );                                                                        

细节:若内容发生变化的内核缓冲区在30s内未经显式方式同步到磁盘上,则一条长期运行的内核线程会确保将其刷新到磁盘上。这一做法是为了规避缓冲区与相关磁盘文件内容长期处于不一致状态。
 
4 使所有写入同步:O_SYNC
调用open()函数时,如制定O_SYNC标志,则会使所有后续输出同步。
1
fd = open(pathname, O_WRONLY | O_SYNC);                                                                        

作用:调用open后,每个write调用会自动将文件数据和元数据刷新到磁盘上,即按照Synchronized IO file integrity completion的要求执行写操作。
 
5 有无O_SYNC性能对比
场景:将一百万字节写入一个ext2文件系统上的新创建文件,比较写入时间。
对比结果:
NewImage                            
从结果中可以得到的结论:
  • 采用O_SYNC标志(或者频繁调用fsync(), fdatasync()或sync())对性能影响极大。

  • 性能下降的直接表现为运行总用时大为增加:在缓冲区为1字节的情况下,运行时间相差1000多倍。

  • 以O_SYNC标志执行写操作时运行总用时和CPU时间之间的巨大差异(1030 - 98.8),原因是系统在每个缓冲区中将数据向磁盘传递时会把程序阻塞起来。

 

 三,IO缓冲层次关系

先总结一下stdio函数库和内核采用的缓冲这两级缓冲,然后用图说明两层缓冲机制和各种缓冲类型的控制机制。
  • 首先,通过stdio库将用户数据传递到stdio缓冲区,该缓冲区位于用户态内存区。

  • 当缓冲区填满,stdio库会调用write()系统调用,将数据传递到内核高速缓冲区,该缓冲区位于内核态内存区。

  • 最终,内核发起磁盘操作。

该层次结构如下图所示
 
NewImage                            

 

上图中,左侧虚线方框中为可于任何时刻显式强制刷新各类缓冲区的调用。
右侧所示为促使刷新自动化的调用:通过禁用stdio的缓冲,和在文件输出类的系统调用中启用同步,从而使每个write()调用立刻刷新到磁盘。
 

四,小结

输入输出数据的缓冲由内核和stdio库完成。有时可能希望阻止缓冲,但这需要了解其对应用程序性能的影响。
可以使用各种系统调用和库函数来控制内核和stdio缓冲,并执行一次性的缓冲区刷新。
在Linux环境下,open()所特有的O_DIRECT标识允许特定应用跳过缓冲区高速缓存。
  

虽然题目还是UNIX高级环境变成(xx),但是打算把所阅读和参考的书换成《Linux/UNIX系统编程手册》。感觉这本书内容更新一点。

工作很忙,周末大部分时间都在外面活动,跑步拍照,虽然只是简单的读书这一篇也是拖了又拖才敲完。

 

参考:

《Linux/UNIX系统编程手册(上册)》  



   

               

来自 https://blog.csdn.net/zdy0_2004/article/details/51132307    


   


   

普通分类: