You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下Python如何判断文件是否被其他进程打开写入且未关闭?

在Linux Python环境中处理文件读写冲突的方案

首先明确:可以判断文件是否处于写入未关闭状态,但这种方法存在竞态条件和性能问题,更推荐用原子操作或锁机制从根源避免冲突,解决你遇到的读进程读取未完成文件的问题。

一、不推荐:直接判断文件是否被写入中

在Linux上可以通过/proc文件系统检查文件是否被其他进程以写入模式打开,但缺点很明显:

  • 遍历所有进程的文件描述符,性能差;
  • 存在竞态条件——刚检查完文件就被关闭/打开;
  • 需要root权限才能查看所有进程的fd信息。

示例代码(仅作演示,不推荐生产用):

import os

def is_file_being_written(file_path):
    try:
        file_inode = os.stat(file_path).st_ino
    except FileNotFoundError:
        return False

    for pid in os.listdir('/proc'):
        if not pid.isdigit():
            continue
        fd_dir = f'/proc/{pid}/fd'
        try:
            for fd in os.listdir(fd_dir):
                try:
                    link_path = os.readlink(f'{fd_dir}/{fd}')
                    if link_path != file_path:
                        continue
                    # 检查文件打开模式是否为写入
                    fd_info_path = f'/proc/{pid}/fdinfo/{fd}'
                    with open(fd_info_path, 'r') as f_info:
                        for line in f_info:
                            if line.startswith('flags:'):
                                flags = int(line.split()[1], 8)
                                # O_WRONLY(0o2) 或 O_RDWR(0o20000) 表示可写入
                                if (flags & 0o2) or (flags & 0o20000):
                                    return True
                except (OSError, PermissionError):
                    continue
        except (OSError, PermissionError):
            continue
    return False

二、推荐:从根源避免冲突的可靠方案

1. 原子重命名(最优解)

Linux下同一文件系统内的文件重命名是原子操作——要么重命名完成,要么完全没发生。写进程先写到临时文件,写完后再重命名为目标文件名,读进程只读取最终的目标文件。

写进程代码:

import os

temp_path = 'cache.tmp'
target_path = 'cache.dat'

# 先写入临时文件
with open(temp_path, 'w') as f:
    f.write('你的缓存数据')
    # 强制把数据刷到磁盘,避免系统缓存延迟
    f.flush()
    os.fsync(f.fileno())

# 原子替换目标文件,读进程只会看到完整的文件
os.rename(temp_path, target_path)

读进程代码:

target_path = 'cache.dat'

if os.path.exists(target_path):
    with open(target_path, 'r') as f:
        data = f.read()

这个方案完全不会出现读进程读取到半完成文件的情况,性能也最优。

2. fcntl文件锁(适合多进程读写场景)

利用Linux的fcntl实现建议性锁:写进程加排他锁,读进程加共享锁,确保同一时间只有一个进程写入,或多个进程读取。

写进程代码:

import fcntl
import os

file_path = 'cache.dat'

with open(file_path, 'w') as f:
    # 加排他锁,其他进程的锁请求会等待
    fcntl.flock(f, fcntl.LOCK_EX)
    f.write('你的缓存数据')
    f.flush()
    os.fsync(f.fileno())
    # 文件关闭时自动释放锁

读进程代码:

import fcntl
import os

file_path = 'cache.dat'

if os.path.exists(file_path):
    with open(file_path, 'r') as f:
        # 加共享锁,等待排他锁释放后再读取
        fcntl.flock(f, fcntl.LOCK_SH)
        data = f.read()

注意:建议性锁需要所有进程都遵守锁规则,如果有进程直接跳过锁读写,还是会出问题。

3. 独立锁文件(简单场景适用)

写进程开始写入时创建锁文件,写完后删除;读进程检查锁文件是否存在,存在则等待或跳过。关键用os.O_EXCL原子创建锁文件,避免多个写进程同时创建锁。

写进程代码:

import os

lock_path = 'cache.lock'
target_path = 'cache.dat'

# 原子创建锁文件,避免竞态
try:
    fd = os.open(lock_path, os.O_CREAT | os.O_EXCL | os.O_RDWR)
    os.close(fd)
except FileExistsError:
    # 已有进程在写,可重试或退出
    pass

try:
    with open(target_path, 'w') as f:
        f.write('你的缓存数据')
        f.flush()
        os.fsync(f.fileno())
finally:
    # 无论成功失败都清理锁文件
    if os.path.exists(lock_path):
        os.unlink(lock_path)

读进程代码:

import os
import time

lock_path = 'cache.lock'
target_path = 'cache.dat'

# 等待锁文件消失
while os.path.exists(lock_path):
    time.sleep(0.1)

if os.path.exists(target_path):
    with open(target_path, 'r') as f:
        data = f.read()

总结

直接判断文件是否在写入状态不是靠谱的解决方案,优先用原子重命名,这是最可靠且高效的方式;如果需要多进程同时读写(比如追加写入),用fcntl锁;锁文件适合简单场景,但要注意异常情况下锁的清理。

内容的提问来源于stack exchange,提问作者orange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 08:55:20