本文解析 Go 在 Windows 平台调用 CreateFileMapping 时如何正确指定内存保护标志(如只读、读写、可执行等),阐明其与 Unix mmap 标志的本质差异,并提供可运行的代码示例与关键注意事项。
本文解析 go 在 windows 平台调用 `createfilemapping` 时如何正确指定内存保护标志(如只读、读写、可执行等),阐明其与 unix `mmap` 标志的本质差异,并提供可运行的代码示例与关键注意事项。
在 Windows 系统中,Go 的内存映射机制与 Unix/Linux 截然不同:它不使用类似 MAP_SHARED | MAP_PRIVATE 的“映射行为标志”,而是通过 保护属性(protection flags) 控制映射视图的访问权限,并由 文件句柄类型 和 后续 MapViewOfFile 调用 共同决定共享语义。
核心区别:Unix vs Windows 内存映射模型
- ✅ Unix mmap:flags 参数(如 MAP_SHARED)明确指定映射的同步行为(是否回写到文件/共享内存);
- ✅ Windows CreateFileMapping:flProtect 参数(即第三个参数)仅指定内存页保护级别(如 PAGE_READONLY、PAGE_READWRITE、PAGE_EXECUTE_READ),不控制共享逻辑;
- ? 共享性由文件句柄和映射视图决定:只要多个进程使用相同命名映射对象(或同一文件句柄创建的映射),再通过 MapViewOfFile 映射为可读写视图,即可实现跨进程共享——这是 Windows 的默认行为,无需额外“共享标志”。
常用保护标志(对应 flProtect 参数)
Go 的 syscall 包已预定义常用常量(位于 syscall/ztypes_windows.go):
| Windows 常量 | 含义 | Go 中使用方式 |
|---|---|---|
| syscall.PAGE_READONLY | 只读映射(不可写) | syscall.PAGE_READONLY |
| syscall.PAGE_READWRITE | 可读可写 | syscall.PAGE_READWRITE |
| syscall.PAGE_WRITECOPY | 写时复制(COW) | syscall.PAGE_WRITECOPY |
| syscall.PAGE_EXECUTE_READ | 可读 + 可执行(常用于 JIT) | syscall.PAGE_EXECUTE_READ |
| syscall.PAGE_EXECUTE_READWRITE | 可读、可写、可执行 | syscall.PAGE_EXECUTE_READWRITE |
⚠️ 注意:PAGE_EXECUTE_* 类标志需配合 SEC_IMAGE 或启用 DEP 绕过策略(生产环境慎用);普通数据映射推荐 PAGE_READWRITE。
完整示例:创建并映射共享内存(Windows)
package main
import (
"fmt"
"syscall"
"unsafe"
)
func main() {
const mappingName = "Global\MySharedMemory"
const size = 4096
// 1. 创建文件映射对象(使用 INVALID_HANDLE_VALUE 表示匿名映射)
hMap, err := syscall.CreateFileMapping(
syscall.InvalidHandle, // hFile: INVALID_HANDLE → 匿名共享内存
nil, // lpSecurityAttributes: nil → 默认安全
syscall.PAGE_READWRITE, // flProtect: 关键!指定访问权限
0, // dwMaximumSizeHigh
uint32(size), // dwMaximumSizeLow
syscall.StringToUTF16Ptr(mappingName),
)
if err != nil {
panic(fmt.Sprintf("CreateFileMapping failed: %v", err))
}
defer syscall.CloseHandle(hMap)
// 2. 映射视图(指定访问模式,必须与 flProtect 兼容)
addr, err := syscall.MapViewOfFile(
hMap,
syscall.FILE_MAP_ALL_ACCESS, // dwDesiredAccess: 权限需兼容 flProtect
0, 0, uint32(size),
)
if err != nil {
panic(fmt.Sprintf("MapViewOfFile failed: %v", err))
}
defer syscall.UnmapViewOfFile(addr)
// 3. 操作内存(转换为 []byte)
data := (*[4096]byte)(unsafe.Pointer(addr))[:size:size]
copy(data[:12], "Hello Windows")
fmt.Printf("Written: %s
", data[:12])
}关键注意事项
- ✅ flProtect 必须与 MapViewOfFile 的 dwDesiredAccess 匹配:例如 PAGE_READONLY 映射后不能传 FILE_MAP_WRITE;
- ✅ 匿名映射(INVALID_HANDLE_VALUE)即为进程间共享内存,无需额外标志——命名(如 "Global\xxx")可跨会话共享;
- ❌ 不存在 MAP_SHARED 等效标志:Windows 不区分“共享”与“私有”映射,而是由 MapViewOfFileEx 的 dwAllocationType(如 MEM_RESERVE)及 flProtect 共同体现;
- ? 若需模拟 MAP_PRIVATE(写时复制),应使用 PAGE_WRITECOPY + FILE_MAP_COPY 访问模式;
- ? 推荐始终检查 CreateFileMapping 和 MapViewOfFile 的返回错误,Windows 错误码需用 syscall.GetLastError() 解析。
掌握这些差异与约定,你就能在 Go 中稳健地实现 Windows 平台的高性能内存共享,无需强行套用 Unix 模型。


















