memalign vs malloc - 使用O_DIRECT参数open一个文件并读写

本文记录了一次使用ODirect参数进行文件读写的实践经历。作者在尝试以扇区单位进行读写时遇到了错误,经过排查发现是由于缓冲区地址未正确对齐导致。文章详细介绍了如何使用memalign及posix_memalign函数确保缓冲区地址为512的倍数,以实现正确的扇区对齐。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

听说使用odirect参数打开文件时能够以扇区的单位进行读写。

于是open了一个块设备文件/dev/sdo,当然还要带上读写参数O_RDWR

然后进行读写时出错了。

 

找了一会发现问题根本在于读写的buf未进行align。

这个align并非是长度进行align。因为我用alloc申请了数倍于512或4096的空间均不行。

 

后来发现是buf的地址需要align,而此操作只能通过memalign系列的函数来完成。

只要buf首地址是512的倍数,至于长度,就无所谓了。但是write/read的时候长度需要是512的整数倍。

 

 

更新:

使用memalign分配较大内存如4MB时出现segmentfault错误,转而使用posix_memalign,很好用。

转载于:https://www.cnblogs.com/bettersky/p/5568431.html

<think>我们正在讨论DirectIO技术,这是一种绕过操作系统缓存直接操作磁盘的技术。根据用户提供的引用内容,我们可以总结如下:引用[1]和[2]介绍了DirectIO项目的目录结构和基本使用,引用[3]强调了DirectIO在特定场景下的性能优势,引用[4]则解释了DirectIO(O_DIRECT)与缓存I/O的区别。用户的问题集中在DirectIO的技术原理及使用方法上。因此,我们需要从原理和实际操作两个层面进行回答。###一、DirectIO技术原理1.**绕过缓存(BypassPageCache)**DirectIO的核心是使用`O_DIRECT`标志(Linux)或`FILE_FLAG_NO_BUFFERING`(Windows),使数据直接在用户空间缓冲区和磁盘之间传输,跳过操作系统的页缓存[^4]。2.**对齐要求(AlignmentConstraints)**-**内存缓冲区对齐**:必须按物理扇区大小对齐(通常512字节或4K)-**文件偏移对齐**:必须是扇区大小的整数倍-**传输长度对齐**:必须是扇区大小的整数倍*违反对齐会导致错误(如Linux的`EINVAL`)[^4]。*3.**性能影响**|操作类型|适用场景|性能表现||----------------|-----------------------------------|-------------------||缓存I/O|通用读写、小文件|高(利用内存缓存)||**DirectIO**|大文件传输、数据库系统|稳定(避免双缓存)|>**双缓存问题**:某些应用(如数据库)已实现自身缓存,使用系统缓存反而导致数据重复拷贝[^3]。4.**数据一致性**DirectIO确保写入完成后数据已落盘(需配合`fsync`等同步机制),避免系统崩溃时缓存丢失[^4]。###二、Linux平台实现方法####1.打开文件时启用`O_DIRECT````c#include<fcntl.h>intfd=open("data.bin",O_WRONLY|O_CREAT|O_DIRECT,0644);//关键标志```####2.分配对齐的内存缓冲区使用`posix_memalign`分配内存:```cchar*buffer;intalign_size=4096;//通常为磁盘扇区大小的整数倍if(posix_memalign((void**)&buffer,align_size,BUFFER_SIZE)!=0){//错误处理}```####3.执行对齐的读写操作```cssize_tret=write(fd,buffer,BUFFER_SIZE);if(ret==-1){perror("writeerror");}```####4.关闭文件```cclose(fd);free(buffer);//释放对齐内存```###三、Windows平台实现方法####1.打开文件时设置`FILE_FLAG_NO_BUFFERING````cHANDLEhFile=CreateFile("data.bin",GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_FLAG_NO_BUFFERING,//关键标志NULL);```####2.使用对齐的内存和参数```c//分配对齐内存(Windows需按扇区对齐)DWORDsectorSize;GetDiskFreeSpace(NULL,NULL,&sectorSize,NULL,NULL);char*buffer=_aligned_malloc(BUFFER_SIZE,sectorSize);//设置写入参数(偏移和长度需对齐)DWORDbytesWritten;OVERLAPPEDoverlapped={0};overlapped.Offset=0;//必须对齐overlapped.OffsetHigh=0;```####3.执行写入```cWriteFile(hFile,buffer,BUFFER_SIZE,&bytesWritten,&overlapped);```####4.清理资源```c_aligned_free(buffer);CloseHandle(hFile);```###四、DirectIO适用场景与限制####✅推荐场景1.**自缓存应用**:如MySQL、PostgreSQL等数据库[^3]2.**大文件传输**:视频处理、科学计算数据集3.**实时系统**:要求写入立即落盘的金融交易系统####⚠️使用限制1.**对齐复杂度**:开发者需手动管理内存/文件偏移对齐2.**性能损失**:小文件随机访问性能可能下降(无缓存优势)3.**平台差异**:Windows/Linux实现机制不同,需条件编译>**关键设计选择**:在Linux中,`O_DIRECT`可能引发同步元数据开销;而Windows的`FILE_FLAG_WRITE_THROUGH`可确保元数据同步[^4]。---###五、DirectIO性能优化建议1.**批量操作**:聚合小写入为大块对齐操作2.**行化**:多线程处理独立文件段3.**结合异步I/O**:如Linux的`io_uring`或Windows重叠I/O```mermaidgraphLRA[应用程序]-->|发起DirectIO请求|B(内核)B-->|直接访问磁盘|C[存储设备]B-->|跳过PageCache|D[避免数据拷贝]```---§§相关问题§§1.如何检测磁盘扇区大小以正确配置DirectIO?2.DirectIO与内存映射文件(mmap)的性能对比?3.在数据库系统中DirectIO的具体应用案例有哪些?4.使用DirectIO时如何确保跨平台兼容性?</think>### DirectIO技术原理及使用方法详解 #### 一、DirectIO技术原理 1. **缓存I/O vs DirectI/O** - **缓存I/O**:常规文件操作(如标准`fwrite`)会经过内核页缓存(Page Cache),数据先写入内存缓存,后由系统异步刷盘[^4] - **DirectI/O**:使用`O_DIRECT`标志(Linux)或`FILE_FLAG_NO_BUFFERING`(Windows),**绕过内核缓存**,数据直接写入磁盘设备[^1][^4] $$ \text{应用层} \xrightarrow{\text{DirectIO}} \text{磁盘设备} \quad (\text{无Page Cache参与}) $$ 2. **技术优势** - 避免**双缓存问题**:对已有自缓存的应用(如数据库)可减少内存拷贝[^3] - 确保**数据持久性**:写入成功即表示数据已落盘(需配合`fsync`) - **性能提升**:大数据量场景减少内存占用和上下文切换[^3] 3. **核心约束条件** | 要素 | 要求 | 原因 | |---|---|----| | 内存缓冲区 | 地址按磁盘扇区对齐(通常512B/4K) | 避免DMA传输失败 | | 文件偏移量 | 扇区大小的整数倍 | 匹配物理扇区边界 | | 数据长度 | 扇区大小的整数倍 | 完整扇区操作 | #### 二、Linux平台实现方法 ```c #include <fcntl.h> #include <stdlib.h> int main() { // 1. 打开文件设置O_DIRECT标志 int fd = open("data.bin", O_WRONLY | O_CREAT | O_DIRECT, 0644); // 2. 分配对齐的内存缓冲区(4K对齐示例) size_t buf_size = 4096; char *buffer; posix_memalign((void**)&buffer, 4096, buf_size); // 3. 准备数据(需确保内容长度对齐) memset(buffer, 'A', buf_size); // 4. 执行DirectIO写入 ssize_t ret = write(fd, buffer, buf_size); // 5. 确保数据物理落盘 fdatasync(fd); // 6. 释放资源 free(buffer); close(fd); } ``` #### 三、Windows平台实现 ```c #include <Windows.h> int main() { // 1. 获取磁盘扇区大小 DWORD sectorSize; GetDiskFreeSpace(NULL, NULL, &sectorSize, NULL, NULL); // 2. 打开文件(关键标志:FILE_FLAG_NO_BUFFERING) HANDLE hFile = CreateFile("data.bin", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_NO_BUFFERING, NULL); // 3. 分配对齐内存 char* buffer = _aligned_malloc(4096, sectorSize); // 4. 设置对齐参数(偏移量和长度需对齐) OVERLAPPED ov = {0}; ov.Offset = 0; // 必须扇区对齐 DWORD bytesWritten; // 5. 执行写入 WriteFile(hFile, buffer, sectorSize, &bytesWritten, &ov); // 6. 强制刷新磁盘缓存 FlushFileBuffers(hFile); // 7. 清理资源 _aligned_free(buffer); CloseHandle(hFile); } ``` #### 四、使用场景与注意事项 **✅ 推荐场景** 1. 数据库系统(MySQL InnoDB, PostgreSQL)的事务日志写入[^3] 2. 高性能计算中的大文件(GB级)顺序写入 3. 需要确保崩溃一致性的关键数据写入 **⚠️ 注意事项** 1. **小文件性能下降**:文件小于缓存块时,DirectIO性能通常差于缓存IO[^4] 2. **对齐违规处理**:未对齐操作将返回`EINVAL`错误(Linux)或`ERROR_INVALID_PARAMETER`(Windows) 3. **组合使用策略**: ```mermaid graph LR A[关键数据] -->|DirectIO| B[确保持久性] C[非关键数据] -->|缓存IO| D[提升吞吐量] ``` #### 五、性能优化技巧 1. **批量聚合写入**:积累多个扇区数据后一次性写入 2. **异步I/O结合**:Linux使用`io_uring`,Windows使用重叠I/O ```c // Linux io_uring示例 struct io_uring ring; io_uring_queue_init(32, &ring, 0); struct io_uring_sqe *sqe = io_uring_get_sqe(&ring); io_uring_prep_write(sqe, fd, buffer, size, offset); ``` 3. **缓冲区复用**:避免频繁分配/释放对齐内存 > **关键设计原则**:DirectIO不是银弹,需根据**数据价值**(重要性)和**访问模式**(大小/频率)决策使用[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值