链式命令是原子的吗?

链式命令是原子的吗?

如果有一个进程不断写入文件,并且我想用 root 控制该文件,我可以这样做:

sudo rm somefile; sudo touch somefile

附加过程是否可以在这两个命令之间附加到文件?如果是这样,有没有办法确保其间没有其他命令运行?

答案1

链式命令行基本上是一个小的 shell 脚本;它将使用通常的 fork+exec 过程运行第一个命令,等待它退出,然后以相同的方式运行第二个命令。在这两个命令之间,外壳程序在其簿记和处理中花费了一些任意的时间,在此期间发生普通的多处理,并且任意其他进程可以执行任意其他操作。所以答案是“不”。 (如果你真的这样做,你会发现目录条目somefile消失了,但文件本身仍然存在(因为它是由进程打开的),直到它被关闭。在此之前,文件使用的磁盘空间不会被回收。同时,该touch命令将创建一个具有相同名称和路径的新的、不相关的文件。)

如果您想将文件的所有权更改为 root,只需执行sudo chown root:root somefile(尽管我不确定这将如何影响具有打开文件句柄的进程)。如果您想销毁当前文件内容,请尝试truncate -s 0 somefile(正在运行的进程将继续追加到现在为空的文件中)。如果是其他事情,也许可以澄清你想做什么。

答案2

不。一个rm命令后面跟着一个touch命令根本不是原子的。两个命令之间的时间间隔很长 - 可能在毫秒范围内。在那段时间里可能会发生很多事情。如果运气不好,您的 sudo 凭据甚至可能会过期。

调用unlinkopen调用的单个程序将为竞争留下更短的窗口,但它仍然可能发生。

更安全的方法是使用临时名称创建新文件并使用rename系统调用。使用系统调用“覆盖”名称rename保证是原子的。这可以使用touch和来实现mv

但是,打开旧文件进行写入的进程可以在删除旧文件后很长时间内继续写入该文件。对于使用 删除的文件unlink和使用 删除的文件都会出现这种情况rename

答案3

一旦进程打开文件句柄以供读取,您对所有权或权限执行的操作都无关紧要:该进程可以继续访问该文件。您甚至可以删除该文件,进程将能够通过文件句柄继续访问它。

将权限视为获取文件句柄的控制门。

如果你想自动创建一个文件,那么不要这样做:

sudo rm somefile
sudo touch somefile

你可以考虑这个:

sudo touch anotherfile
sudo perl -e "rename 'anotherfile', 'somefile'"

它将自动替换somefileanotherfile. (我更愿意使用,mv -f但我找不到保证调用rename(2)系统调用的语句。


也许您可以更新您的问题来解释“控制文件”的含义。您可能有一个XY问题这里。

答案4

不是,但

sudo rm somefile; sudo touch somefile

在大多数情况下是安全的。

大多数进程打开文件,然后使用获取的文件描述符来访问文件内容。

如果进程在 sh 中打开某个文件:

 exec 3>somefile

然后另一个进程可以自由地取消链接(=删除)somefile,并且第一个进程(在本例中为3)打开somefile的文件描述符将继续引用somefile的原始内容,该文件现在是一个处于不确定状态的文件。

sudo touch somefile

将创建一个新的、不相关的 somefile,并且所有打开了旧 somefile 且现在仅使用文件描述符来引用它的进程都不会受到影响,因为它们引用的是另一个文件 - 一个现在处于不确定状态的文件。

如果非根进程尝试按名称引用某个文件,它们将收到 EPERM 错误,因为新的某个文件是根拥有的。

如果您想防止同一用户(例如 root)下的多个进程损坏文件,Linux 具有强制和建议的文件锁定功能。您可以flock在 shell 脚本中使用该命令来进行建议文件锁定(有关详细信息,请参阅联机帮助页)。

相关内容