Windows下内存映射

我想用内存映射和直接读取文件分别实现MD5算法。

众所周知,该算法是线性复杂度,文件的读取可能成为效率的瓶颈。

以前曾写过CRT版本的,效率比我在windows下使用的WinMD5略低。

 

内存映射读取MapViewOfFile

第一步先打开文件,推荐使用CreateFile而不是OpenFile。msdn上OpenFile可以查到:

Note  Only use this function with 16-bit versions of Windows. For newer applications, use the CreateFile function."

 

 

我们只需要读文件

 

文件查找失败会返回 INVALID_HANDLE_VALUE。


第二步是建立文件映射

 

由于不需要别的映射,这里让 lpName = NULL

 

失败会返回NULL。

 

第三步把文件的某个片段映射到内存空间

 

对于小的文件直接一次映射到内存就行了,但是4G大小的文件,你会发现这一步会映射失败,32位地址允许的内存最多是2G,所以我们要分多次映射,每次取的offset必须是系统allocation granularity的整数倍。

 

我的是笔记本,操作系统Vista下的granularity = 65536。大多数的PC机应该都是这个数值。

令uPos 是偏移量,STEP是每次取出映射的长度

 

失败会返回 NULL。

 

获取文件大小,注意如果文件大小超过2G,需要GetFileSize的第2个参数作为文件大小的高位

 

完整代码如下

 

 

直接文件读取ReadFile

大致上差不多,有个优化速度的地方就是不要每次读取64个byte。我是每次读取1024×64个byte,然后分1024次处理

这样速度上快一些,我原来CRT版本的就是这么做的。

完整代码如下

 

 

两种方法的速度比较

都不是很理想,对一个4G的大文件直接读取速度要快一些,大约260s。

500M左右的文件也是后者快一点点。没有感觉到内存映射的优势:(

内存映射有个小地方要注意的是:如果文件是0 byte的话,CreateFileMapping这步会失败,需要另外处理。

不过实际中不用考虑这个。

<think>好的,用户现在想知道在Windows下如何使用C语言进行内存映射操作,并且可能与之前提到的线程和速率控制相关。之前我回答了一个关于使用乒乓缓冲和多线程同步的问题,现在需要将内存映射结合进去。 首先,我需要回顾Windows下的内存映射相关API。记得Windows使用CreateFileMapping和MapViewOfFile函数来实现内存映射。用户提供的引用中提到了Unix的mmap,但Windows有自己的方法。 用户可能希望看到如何将内存映射与线程同步结合,比如在读取和存储线程之间共享内存映射的区域,同时控制速率。之前的乒乓操作使用了事件对象来同步线程,现在可能需要将缓冲区改为内存映射的区域。 接下来,我需要考虑如何将内存映射集成到之前的示例中。可能需要修改PingPongBuffer结构,使其包含内存映射的指针,或者直接使用内存映射文件作为缓冲区。同时,需要确保线程安全,使用同步机制如事件或互斥量。 用户提供的引用中有关于共享内存和进程通信的内容,但当前问题可能更关注同一进程内的多线程使用内存映射。不过,也可能需要考虑跨进程的情况,但用户没有明确说明,所以先专注于单进程多线程。 需要注意Windows和Unix在内存映射上的区别,比如API的不同和权限设置。例如,在Windows中需要先用CreateFileMapping创建映射对象,然后用MapViewOfFile获取指针。 此外,错误处理也很重要,每个API调用后都应检查返回值,并适当处理错误,如用户之前的示例中使用了错误处理。 用户提到的速率控制可能涉及调整线程的读写速度,确保生产者和消费者速率匹配。在内存映射的情况下,可能不需要显式的速率控制,但如果结合之前的乒乓缓冲,仍需同步机制。 最后,需要提供一个完整的示例代码,展示如何创建内存映射,在多个线程之间共享,并正确同步。同时,解释关键步骤和注意事项,比如资源释放、同步事件的使用等。</think>在Windows环境下实现内存映射文件操作,可以与多线程速率控制结合使用。以下是基于线程同步和乒乓操作改进的内存映射实现方案: --- ### 一、Windows内存映射核心API 1. **关键函数**: ```c HANDLE CreateFileMappingW( HANDLE hFile, // 物理文件句柄 LPSECURITY_ATTRIBUTES lpAttributes, DWORD flProtect, // 保护标志(PAGE_READWRITE) DWORD dwMaximumSizeHigh, // 文件大小高32位 DWORD dwMaximumSizeLow, // 文件大小低32位 LPCWSTR lpName // 映射对象名称 ); LPVOID MapViewOfFile( HANDLE hFileMappingObject, // 映射对象句柄 DWORD dwDesiredAccess, // 访问模式(FILE_MAP_ALL_ACCESS) DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap // 映射字节数 ); ``` --- ### 二、线程安全内存映射实现 #### 1. 改进的乒乓缓冲结构 ```c #define BUFFER_SIZE 4096 typedef struct { HANDLE hMapFile; // 内存映射句柄 LPVOID pBuffers[2]; // 双缓冲指针 DWORD active_buffer; // 当前活动缓冲区索引 HANDLE hMutex; // 互斥锁 HANDLE hSemaphore; // 信号量控制速率 volatile BOOL stop_flag; } ThreadSafeMapping; ``` #### 2. 带速率控制的内存映射初始化 ```c int init_mapping(ThreadSafeMapping* tsm, LPCWSTR file_name) { // 创建内存映射文件 tsm->hMapFile = CreateFileMappingW( INVALID_HANDLE_VALUE, // 使用物理内存 NULL, PAGE_READWRITE, 0, BUFFER_SIZE * 2, // 双缓冲总大小 file_name); if (tsm->hMapFile == NULL) { fprintf(stderr, "CreateFileMapping error: %d\n", GetLastError()); return -1; } // 映射双缓冲视图 for (int i = 0; i < 2; ++i) { tsm->pBuffers[i] = MapViewOfFile( tsm->hMapFile, FILE_MAP_ALL_ACCESS, 0, i * BUFFER_SIZE, // 缓冲区偏移 BUFFER_SIZE); if (tsm->pBuffers[i] == NULL) { fprintf(stderr, "MapViewOfFile error: %d\n", GetLastError()); return -1; } } // 创建同步对象 tsm->hMutex = CreateMutex(NULL, FALSE, NULL); tsm->hSemaphore = CreateSemaphore(NULL, 1, 1, NULL); return 0; } ``` --- ### 三、与线程同步结合的完整示例 #### 1. 生产者线程(数据写入) ```c DWORD WINAPI ProducerThread(LPVOID param) { ThreadSafeMapping* tsm = (ThreadSafeMapping*)param; DWORD bytes_written = 0; while (!tsm->stop_flag) { WaitForSingleObject(tsm->hSemaphore, INFINITE); // 速率控制 // 获取互斥锁保证原子操作 WaitForSingleObject(tsm->hMutex, INFINITE); // 写入当前非活动缓冲区 DWORD target_idx = (tsm->active_buffer + 1) % 2; // 模拟数据生产(示例写入时间戳) SYSTEMTIME st; GetSystemTime(&st); sprintf_s(tsm->pBuffers[target_idx], BUFFER_SIZE, "[%02d:%02d:%02d] Producer Data", st.wHour, st.wMinute, st.wSecond); // 切换活动缓冲区 tsm->active_buffer = target_idx; ReleaseMutex(tsm->hMutex); ReleaseSemaphore(tsm->hSemaphore, 1, NULL); // 释放信号量 Sleep(100); // 控制生产速率(100ms/次) } return 0; } ``` #### 2. 消费者线程(数据处理) ```c DWORD WINAPI ConsumerThread(LPVOID param) { ThreadSafeMapping* tsm = (ThreadSafeMapping*)param; while (!tsm->stop_flag) { WaitForSingleObject(tsm->hSemaphore, INFINITE); WaitForSingleObject(tsm->hMutex, INFINITE); // 读取当前活动缓冲区 printf("Consumer received: %s\n", (char*)tsm->pBuffers[tsm->active_buffer]); ReleaseMutex(tsm->hMutex); ReleaseSemaphore(tsm->hSemaphore, 1, NULL); Sleep(100); // 匹配生产者速率 } return 0; } ``` --- ### 四、性能优化技巧 1. **大页面支持**: ```c // 在CreateFileMapping前设置大页面 if (!SetLargePagesPrivilege()) { // 需要实现权限设置函数 fprintf(stderr, "Large page not supported\n"); } HANDLE hMap = CreateFileMapping(..., SEC_LARGE_PAGES); ``` 2. **非对称速率控制**: ```c // 使用信号量初始值调节生产消费比例 CreateSemaphore(NULL, 3, 3, NULL); // 允许生产者领先3个缓冲 ``` 3. **内存屏障保障数据一致性**: ```c // 在切换active_buffer前插入内存屏障 MemoryBarrier(); // 防止指令重排序 tsm->active_buffer = new_index; ``` --- ### 五、安全注意事项 1. **防御性编程**: ```c // 验证内存映射有效性 if (IsBadWritePtr(tsm->pBuffers[i], BUFFER_SIZE)) { // 错误处理 } ``` 2. **异常处理**: ```c __try { memcpy(tsm->pBuffers[0], data, len); } __except (EXCEPTION_EXECUTE_HANDLER) { fprintf(stderr, "Memory access violation!\n"); } ``` 3. **资源释放**: ```c void cleanup(ThreadSafeMapping* tsm) { for (int i = 0; i < 2; ++i) { if (tsm->pBuffers[i]) UnmapViewOfFile(tsm->pBuffers[i]); } CloseHandle(tsm->hMapFile); CloseHandle(tsm->hMutex); CloseHandle(tsm->hSemaphore); } ``` --- ### 六、与进程间通信结合 可通过命名映射实现跨进程数据共享: ```c // 生产者进程 CreateFileMappingW(INVALID_HANDLE_VALUE, ..., L"Global\\MySharedMemory"); // 消费者进程 HANDLE hMap = OpenFileMappingW(FILE_MAP_READ, FALSE, L"Global\\MySharedMemory"); ``` --- --相关问题--: 1. 如何检测Windows内存映射的页面错误? 2. 内存映射文件与普通文件I/O的性能差异有多大? 3. 如何实现带超时机制的内存映射同步? 4. Windows内存映射能否直接用于GPU计算?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值