仅适用于同一目录中的文件。

仅适用于同一目录中的文件。

在 Linux 上,openat系统调用可用于创建文件并测试它们是否存在。就 C/C++ 内存模型而言,创建文件并验证其存在会创建同步关系。我需要知道的是这些同步是否都是顺序一致的。 (我当然希望如此,但我实际上还没有在任何地方看到过这个记录。)

例如,给定进程 p1 和 p2,以及路径 A 和 B:

如果 p1 这样做:创建(A),然后创建(B)

p2 执行以下操作:尝试打开(B),然后尝试打开(A)

并且没有其他进程干扰A或B,是否有可能p2成功打开B但找不到A?

如果有什么不同,我们可以假设所有操作都在一个文件系统中。

答案1

通过所有底层磁盘和多核 CPU 优化,不一定能够确定两个进程之间操作序列的严格顺序。这就是如果存在时间相关行为的可能性则使用信号量的原因。

答案2

仅适用于同一目录中的文件。

有6条规则:

  1. 读取访问权限。锁定规则:调用者锁定我们正在访问的目录。锁被共享。

  2. 对象创建。锁定规则:与上面相同,但锁定是排他的。

  3. 物体移除。锁定规则:调用者锁定父级,找到受害者,锁定受害者并调用该方法。锁是独占的。

  4. rename()那是不是跨目录。锁定规则:调用者锁定父级并查找源和目标。在交换的情况下(使用 RENAME_EXCHANGE标志参数)锁定两者。无论如何,如果目标已经存在,则锁定它。如果源是非目录,则锁定它。如果我们需要锁定两者,请按 inode 指针顺序锁定它们。然后调用该方法。所有锁都是独占的。注意:我们可能会锁定共享的源(以及交换情况下的目标)。

  5. 链接创建。锁定规则:

    • 锁定父级

    • 检查源不是目录

    • 锁源

    • 调用该方法。所有锁都是独占的。

  6. 跨目录重命名。整个群中最棘手的。锁定规则:

    • 锁定文件系统

    • 将父母锁定在“祖先第一”的顺序中。

    • 找到来源和目标。

    • 如果旧父级等于或是目标的后代,则失败 -ENOTEMPTY

    • 如果新父级等于或是源的后代,则失败 -ELOOP

    • 如果是交换,请锁定源和目标。

    • 如果目标存在,则锁定它。如果源是非目录,则锁定它。如果我们需要锁定两者,请按照 inode 指针顺序进行操作。

    • 调用该方法。全部->i_rwsem采取独家。同样,我们可能会锁定共享的源(以及交换情况下的目标)。

上述规则显然保证了所有将要通过方法读取、修改或删除的目录都将被调用者锁定。

锁定强制线性化,因此操作在单个目录上完全有序。但是,读访问 (1)、对象创建 (2) 和对象删除 (3) 不会采用比目录锁更广泛的锁,因此无法保证不同目录中目录操作的顺序;不同的观察者可能会看到目录的线性历史以不同的方式交织。

相关内容