PHP中mkdir()不能直接用flock()锁定目录,因flock仅作用于普通文件句柄;安全创建需用锁文件(如.dir/.mkdir.lock)配合flock实现原子性,或依赖mkdir()自身原子性+is_dir()判断防重。

PHP 中 mkdir() 本身不支持文件锁,也不能直接用 flock() 锁定目录——因为 flock() 只作用于**已打开的普通文件句柄**,而目录不是可 fopen 的文件(fopen("dir/", "r") 会失败)。但你真正需要的,其实是「**安全创建目录,避免多进程并发时重复创建或权限冲突**」。这不能靠 flock() 加在目录上实现,而是要换思路:用文件锁保护「创建目录」这个操作本身。
为什么不能对目录用 flock()
操作系统层面,flock() 是针对文件描述符(file descriptor)的 advisory lock,只适用于通过 fopen() 打开的常规文件。目录是特殊文件系统对象,PHP 不允许对其调用 fopen(),因此无法获取句柄,flock() 无从下手。试图 flock(dir_handle, ...) 会报错或直接返回 false。
正确做法:用临时文件 + flock 实现原子性 mkdir
核心逻辑是:选一个与目标目录强关联的“锁文件”(如 /path/to/dir/.mkdir.lock),用 flock() 锁住它,再执行 mkdir(),确保同一时间只有一个进程能进入创建流程。
- 创建一个轻量级锁文件路径,例如
$lockFile = $targetDir . '/.mkdir.lock'; - 用
fopen($lockFile, 'c')打开(c模式确保文件存在且不截断,适合锁文件) - 立即调用
flock($fp, LOCK_EX | LOCK_NB)尝试非阻塞加锁;失败则说明其他进程正在创建,可重试或跳过 - 加锁成功后,调用
mkdir($targetDir, 0755, true)—— 注意第三个参数true支持递归创建父级 - 无论成功与否,都先
flock($fp, LOCK_UN)释放锁,再fclose($fp);建议最后unlink($lockFile)清理(可选,但推荐)
更可靠替代:利用 mkdir() 自身的原子性
其实最简洁安全的方式,是**根本不用 flock**,而是依赖 mkdir() 在绝大多数文件系统上的天然原子性:
-
mkdir($path, $mode)调用本身是原子的:要么成功创建,要么返回 false(errno=17表示 “File exists”) - 标准写法就是:
if (!is_dir($path) && !mkdir($path, 0755, true)) { /* 处理失败 */ } - 这个判断+创建组合虽不是单条原子指令,但因
mkdir()失败时明确返回 false 且不改变状态,配合is_dir()可有效防重——比手动锁更轻量、无死锁风险 - 仅当需在创建后立即写入关键配置文件(且要求“创建+写入”整体不可中断)时,才需额外用
flock()锁住那个配置文件本身
什么情况下真要结合 flock 和 mkdir?
典型场景是「初始化共享资源目录并写入默认配置」,例如缓存根目录首次生成时写入 config.json:
- 先用上述锁文件方式确保只有 1 个进程执行初始化
- 加锁成功后,
mkdir()创建目录,再fopen($configPath, 'c+') → flock(LOCK_EX) → fwrite() → fflush() → flock(LOCK_UN) - 这样既保证目录不重复建,又确保配置写入过程不被并发干扰
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











