首页 > 其他 > 详细

cp: 无法创建普通文件 : 文件已存在

时间:2020-05-25 23:52:20      阅读:173      评论:0      收藏:0      [点我收藏+]

背景

碰到一个偶现的编译出错问题,如图
技术分享图片

报错的信息是

cp: 无法创建普通文件"xxx": 文件已存在

排查原因

看了下 Makefile,这句非常简单,就是 cp ./xxx ../xxx 而已,本身没什么问题。

那再结合上下文出现的打印,一个异常之处就是 Makfeile 被并行重复执行了,猜测是并行导致 cp 操作出错。

只考虑解决问题,那无疑是修改外层 Makefile ,避免此处被并行重复执行,至少这句 cp 不要被并行,就可以解决了。

但为什么 cp 并行执行会出错呢?如果在另外的场景下确实有并行执行cp的可能,有没有办法规避这个错误呢?这就得探究下了。

单独执行 cp,默认的行为就是覆盖已存在的文件,并不会因为 “文件已存在” 这样的原因出错,随便做下实验,touch a b; cp a b就可以确认正常是不会报错的。

那问题还是得结合并行来分析,碰到这种情况,要么是从搜索资料获得提示,要么就是实践出真知,自己设计一个可快速复现的方式,然后使用调试工具来追踪问题发生时的具体情况。

具体到这个问题,我是搜索到相同的stackexchange问题,那就省点工夫不用自己去复现分析了。

这里插下题外话,搜索优先使用google,对于中文报错信息查不到的可改成英文查询。例如中文的 cp: 无法创建普通文件 文件已存在 就不好找到答案,换成 cp cannot create regular file file exists 就好找了。(只敲一部分,搜索引擎就能提示完整的信息)
技术分享图片

技术分享图片

stackexchage上给出了一个脚本,用于复现问题并使用 strace 将追踪的系统调用记录下来

#!/bin/bash

touch a

f() {
  while true; do
    rm -f b
    strace -o /tmp/cp${BASHPID}.trace cp a b || break
  done
}

cleanup() {
  kill -9 %1 %2
}

f &
f &

trap cleanup exit

wait

附上我自己的实验结果,可以看出cp的实现上,会先用stat来判断目标文件b是否存在,如果不存在则会使用 open("b", O_WRONLY|O_CREAT|O_EXCL, 0664) 来创建目标文件并将源文件写入目标文件,完成复制。

那么如果两个 cp 并发,就可能出现

cp1                  cp2
stat判断b不存在     
                     stat判断b不存在
open成功,创建文件b         
                     open失败,因为此时文件已经被cp1创建好了

stracelog 看到的就是
技术分享图片

由于 cp 不是原子的,如果两个 cp 刚好几乎同时执行,则可能两个 cpstat都判断到文件不存在,那最终只有一个 cp 能创建文件,另一个就失败了。

顺便看看,文件存在和不存在的open参数差异
技术分享图片

解决办法

既然两个cp同时执行会出错,那就加锁呗。

如果所有调用 cp 的地方都是我们可控的,那劝告锁就足够了,在 shell 中可以直接使用 flock

约定好一个文件锁x, 将原来的cp a b 改成 flock x cp a b 即可。

例如正常在两个控制台中,执行top是可以并行的,但如果改成执行 flock /tmp/toplock top,那就只有控制台1会执行top,控制台2则处于等待文件锁的状态。此时若控制台1退出top,则控制台2获得锁,开始执行top

更多文件锁的细节,可以看看 man flock

blog: https://www.cnblogs.com/zqb-all/p/12942556.html
公众号:https://sourl.cn/S42YSr

cp: 无法创建普通文件 : 文件已存在

原文:https://www.cnblogs.com/zqb-all/p/12942556.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!