每个系统调用都是原子操作吗?

每个系统调用都是原子操作吗?

APUE 说

我们看到了当我们描述 open 函数的 O_CREAT 和 O_EXCL 选项时,原子操作的另一个例子。当指定这两个选项时,如果文件已存在,则打开将失败。我们还说过,检查文件是否存在以及文件的创建是作为原子操作执行的。如果我们没有这个原子操作,我们可以尝试

if ((fd = open(path, O_WRONLY)) < 0) {
if (errno == ENOENT) {
if ((fd = creat(path, mode)) < 0)
err_sys("creat error");
} else {
err_sys("open error");
}
}

如果文件是由 open 和 creat 之间的另一个进程创建的,则会出现此问题。如果该文件是由另一个进程在这两个函数调用之间创建的,并且该另一个进程向该文件写入了某些内容,则在执行此创建时该数据将被删除。将存在性测试和创建合并到单个原子操作中可以避免此问题。

仅当系统调用 open() 是原子的时,引用才有意义吗?

更一般地说,在 Linux 或任何其他操作系统中,每个系统调用都是原子操作吗?

如果不是,我如何判断系统调用是否是原子的?

谢谢。

答案1

系统调用通常是原子的,因为它们要么成功,要么失败。如果失败,它们会执行“回滚”,除了向调用者返回错误之外没有任何作用。它们也是原子的,因为它们努力不将初始状态和最终状态之间的任何中间状态暴露给系统上运行的其他线程/进程。例如,文件要么已创建,要么不存在。

大多数时候,这种线程间(进程间)原子性并不真正相关。该调用open(path, O_WRONLY)在不同线程上独立运行,并导致文件被打开以在调用线程中完全异步地写入,从而在其他线程中打开和关闭对该文件的调用[*]。因此,我不明白引用的敏感性如何取决于open系统调用的原子性。

[*]在许多并发打开调用的情况下,内核当然必须保护其自己的内部数据结构免受并发更新,例如计算打开文件的线程数的变量。

相关内容