php file_put_contents 偶尔没有那个文件或目录

php file_put_contents no such file or directory occasionally

我启动了8个进程来执行一个作业,作业中的代码是这样的:

<?php
$dir = "/home/test/fileputcontents/0";
if(! is_dir($dir)){
    mkdir($dir, 0755, true);
}
file_put_contents("{$dir}/0.txt", "aaaa\n", FILE_APPEND | LOCK_EX);

但有时会出现“没有这样的文件或目录”的错误,不是很频繁,非常感谢。

是因为php_mkdir_ex方法吗?多进程同时创建目录

/* DEPRECATED APIs: Use php_stream_mkdir() instead */
PHPAPI int php_mkdir_ex(const char *dir, zend_long mode, int options)
{
    int ret;

    if (php_check_open_basedir(dir)) {
        return -1;
    }

    if ((ret = VCWD_MKDIR(dir, (mode_t)mode)) < 0 && (options & REPORT_ERRORS)) {
        php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
    }

    return ret;
}

是否所有 8 个进程 运行 同时写入同一个文件?如果两个进程同时写入文件,则文件可能被锁定(由 LOCK_EX 标志引起)。所以一个进程会获胜(可以写入文件)而另一个进程不会等待,而是会引发错误。

终于从php源码中找到了原因。这是因为目录创建冲突。例如:

  1. 我们有 2 个进程同时创建目录“/home/test/fileputcontents/0”。
  2. 现在只有/home目录存在,我们需要创建test/fileputcontents/0
  3. 一个进程(我们称为A)已经创建了test,即将创建fileputcontents,另一个进程(我们称为B)将再次创建test,但是它已经存在。
  4. 然后进程 B 停止创建目录,因为它认为所有目录都存在
  5. 当 B 试图在 A 创建所有目录之前调用 file_put_contents 时,它会警告 "No such file or directory",因为 fileputcontents/0 不存在。

我的解决方案,创建目录,因为进程启动。

已在 8.1 中修复

https://github.com/php/php-src/pull/7383

- if (ret < 0) {
+ if (ret < 0 && errno != EEXIST) {