最直接可靠的方式是调用CreateFile并设dwShareMode=0和OPEN_EXISTING;若返回INVALID_HANDLE_VALUE且GetLastError()为ERROR_SHARING_VIOLATION,说明文件被独占打开。

Windows 下用 CreateFile 尝试以独占方式打开文件
最直接可靠的方式是调用 Win32 API 的 CreateFile,指定 dwShareMode = 0(即不共享读/写/删除),并使用 OPEN_EXISTING。如果返回 INVALID_HANDLE_VALUE,且 GetLastError() 是 ERROR_SHARING_VIOLATION,说明该文件正被另一个进程以独占或非兼容共享模式打开。
注意:这会触发系统级的共享检查,比单纯查句柄更准确;但需确保调用进程有读取权限(哪怕只是尝试打开,不实际读内容)。
- 必须关闭返回的句柄(即使失败也要调用
CloseHandle,避免资源泄漏) - 不要用
FILE_ATTRIBUTE_NORMAL以外的标志干扰判断,比如FILE_FLAG_NO_BUFFERING可能导致误报 - 对只读文件,若对方以
GENERIC_WRITE打开但未设FILE_SHARE_WRITE,你用只读方式仍会失败
Linux/macOS 下用 open() 配合 O_EXCL | O_RDWR 不可靠
Unix-like 系统没有“独占打开”语义,O_EXCL 仅对 O_CREAT 有效(防止竞态创建),对已存在文件无效。所以不能靠 open(path, O_EXCL | O_RDWR) 判断占用。
真正可用的是 flock() 或 fcntl(F_SETLK) 尝试加写锁——但前提是目标进程也主动参与文件锁协议。很多程序(如数据库、编辑器)不用 POSIX 锁,这类检测就会漏判。
-
flock(fd, LOCK_EX | LOCK_NB)失败且errno == EWOULDBLOCK,仅表示有其他 flock 持有者,不是通用解法 -
/proc/*/fd/(Linux)或lsof是诊断手段,不是程序化判断接口,依赖外部工具和权限 - 没有内核级“谁在用这个文件”的原子查询,所有用户态方案都有竞态窗口
跨平台程序中应避免依赖“实时占用检测”
无论 Windows 还是 Unix,所谓“当前是否被独占打开”本质是个瞬时快照,且受权限、锁策略、文件系统类型(如 NFS、FAT32 不支持某些锁)影响极大。生产环境里更稳妥的做法是:设计时让关键文件自带租约机制(如写入临时锁文件 + PID + 时间戳),或用 IPC 协调访问。
强行轮询 CreateFile 或 flock 容易引发性能抖动和误判,尤其在高并发或网络文件系统上。
- Windows 上
ERROR_SHARING_VIOLATION可能来自防病毒软件临时扫描,不一定是业务进程 - Linux 上
lsof -t -w -n -P -F p /path/to/file输出 PID 列表,但需 root 权限才能看到所有进程 - 即使检测到占用,你也无法安全地“强制释放”,只能提示用户或退避重试
Python 示例:Windows 快速探测(使用 ctypes)
以下片段可嵌入脚本做轻量检查,不依赖第三方包:
import ctypes from ctypes import wintypeskernel32 = ctypes.WinDLL('kernel32', use_last_error=True) kernel32.CreateFileW.argtypes = ( wintypes.LPCWSTR, wintypes.DWORD, wintypes.DWORD, wintypes.LPVOID, wintypes.DWORD, wintypes.DWORD, wintypes.HANDLE ) kernel32.CreateFileW.restype = wintypes.HANDLE
def is_file_exclusively_opened(path): handle = kernel32.CreateFileW( path, 0x80000000 | 0x40000000, 0, None, 3, 0x80, None ) if handle == wintypes.HANDLE(-1).value: return ctypes.get_last_error() == 32 # ERROR_SHARING_VIOLATION kernel32.CloseHandle(handle) return False
其中 0x80000000 是 GENERIC_READ,0x40000000 是 GENERIC_WRITE,3 是 OPEN_EXISTING,0x80 是 FILE_ATTRIBUTE_NORMAL。关键在于第三个参数为 0(dwShareMode)。
真正难的不是写这段代码,而是理解它只告诉你“此刻打不开”,而不是“谁在用”或“多久后能用”。










