欢迎各位兄弟 发布技术文章
这里的技术是共享的
下面我的一个测试用例,数据只是简单地几十个字节,只是为了说明使用方法罢了。如下:
- /******************************************************************
- * @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;
- }
来自 https://blog.csdn.net/yang15225094594/article/details/43268133
一般如果在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
春天来了,除了工作学习,大家也要注意锻炼身体,多出去运动运动。
上周末在元大都遗址公园海棠花溪拍的海棠花。
进入正题。
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" );
|
用于传递数据的缓冲区,其内存边界必须对齐为块大小的整数倍
数据传输的开始点,即文件和设备的偏移量,必须是块大小的整数倍
待传递数据的长度必须是块大小的整数倍。
不遵守上述任一限制均将导致EINVAL错误。
功能:强制刷新内核缓冲区到输出文件。这是有必要的,因为为了数据安全,需要确保将数据真正写入磁盘或者磁盘的硬件告诉缓存中。
我们先熟悉一下同步IO相关定义和系统调用。
synchronized IO data integrity completion:确保针对文件的一次更新传递了足够的信息(部分文件元数据)到磁盘,以便于之后对数据的获取。
synchronized IO file integrity completion:确保针对文件的一次更新传递了所有的信息(所有文件元数据)到磁盘,即使有些在后续对文件数据的操作并不需要。
1 fsync
1 2 | #include
int fsync( int fd);
|
0: success
-1: error
1 2 | #include
int fdatasync( int fd);
|
0: success
-1: error
1 2 | #include
void sync( void );
|
1 | fd = open(pathname, O_WRONLY | O_SYNC);
|
采用O_SYNC标志(或者频繁调用fsync(), fdatasync()或sync())对性能影响极大。
性能下降的直接表现为运行总用时大为增加:在缓冲区为1字节的情况下,运行时间相差1000多倍。
以O_SYNC标志执行写操作时运行总用时和CPU时间之间的巨大差异(1030 - 98.8),原因是系统在每个缓冲区中将数据向磁盘传递时会把程序阻塞起来。
首先,通过stdio库将用户数据传递到stdio缓冲区,该缓冲区位于用户态内存区。
当缓冲区填满,stdio库会调用write()系统调用,将数据传递到内核高速缓冲区,该缓冲区位于内核态内存区。
最终,内核发起磁盘操作。
虽然题目还是UNIX高级环境变成(xx),但是打算把所阅读和参考的书换成《Linux/UNIX系统编程手册》。感觉这本书内容更新一点。
工作很忙,周末大部分时间都在外面活动,跑步拍照,虽然只是简单的读书这一篇也是拖了又拖才敲完。
参考:
《Linux/UNIX系统编程手册(上册)》
来自 https://blog.csdn.net/zdy0_2004/article/details/51132307