Linux 如何实现类似于;“吃什么?”;?

Linux 如何实现类似于;“吃什么?”;?,linux,posix,system-calls,glibc,Linux,Posix,System Calls,Glibc,在研究过程中,我发现在POSIX(和Linux)中根本没有truncateat系统调用 某些系统调用,例如unlink,有一个等效的替代方法,在其名称末尾添加at后缀,即unlinkat。这些方法之间的区别在于,带有at后缀的变体接受一个附加参数,即指向目录的文件描述符。因此,传递到unlinkat的相对路径不是相对于当前工作目录,而是相对于提供的文件描述符(开放目录)。这在某些情况下非常有用 查看truncate,它旁边只有ftruncate截断在路径上工作-绝对路径或相对于当前工作目录ftr

在研究过程中,我发现在POSIX(和Linux)中根本没有
truncateat
系统调用

某些系统调用,例如
unlink
,有一个等效的替代方法,在其名称末尾添加
at
后缀,即
unlinkat
。这些方法之间的区别在于,带有
at
后缀的变体接受一个附加参数,即指向目录的文件描述符。因此,传递到
unlinkat
的相对路径不是相对于当前工作目录,而是相对于提供的文件描述符(开放目录)。这在某些情况下非常有用

查看
truncate
,它旁边只有
ftruncate
<代码>截断在路径上工作-绝对路径或相对于当前工作目录
ftruncate
直接在打开的文件句柄上工作-不指定任何路径。没有
truncateat

许多库(各种“可选”C库)都像我一样,通过使用
openat
-
ftruncate
-
-
close
-序列来模拟
tuncatet
。这在大多数情况下都有效,除了

我遇到了以下问题。我花了好几个月才弄明白发生了什么事。在Linux上测试,不同的3.X和4.X内核。设想两个进程(不是线程):

  • 过程“A”
  • 过程“B”
现在想象以下事件序列(伪代码):

上面的方法很好用。在进程“A”打开文件,将其大小设置为100并向其中写入一些内容之后,进程“B”将其大小重新设置为200。然后过程“A”继续。最后,文件大小为200,开头包含“abcdef”,后面是零字节

现在,让我们试着模仿类似于
truncateat

A: fd_a = open(path = 'filename', mode = write)
A: ftruncate(fd_a, 100)
A: write(fd_a, 'abc')
B: fd_b = openat(dirfd = X, path = 'filename', mode = write | truncate)
B: ftruncate(fd_b, 200)
B: close(fd_b)
A: write(fd_a, 'def')
A: close(fd_a)
我的文件长度是200,好的。它以三个零字节开始,不正常,然后是“def”,然后又是零字节。我刚刚丢失了进程“A”的第一次写入,而“def”技术上落在了正确的位置(3个字节,就好像我在写入之前调用了
seek(fd_A,3)


我可以很好地处理第一个操作序列。但在我的用例中,就进程“B”而言,我不能依赖于相对于当前工作目录的路径。我真的想处理相对于文件描述符的路径。如何做到这一点?而不碰到第二个操作序列中演示的问题?在写入(fd_A,'abc')
之后从进程“A”调用
fsync
并不能解决这个问题。

第二种情况下用零覆盖所有内容的原因是mode=truncate(即
openat(..,O_TRUNC)
)将首先将文件截断为长度0


如果您不首先将其截断为0,而是立即将
ftruncate
运行到200,则在该点之前的现有数据将保持不变。

您是说
truncate
在您的情况下会起作用吗?@我测试过的另一个家伙。事实上,它确实起作用。所以问题在于,您使用mode=truncate打开文件,从而进行了两次截断(首先在字节0处,然后在字节200处)?@otherguy确实“在truncate模式下打开”意味着截断为零?@otherguy这将是一个非常愚蠢的错误。让我在没有模式的情况下尝试=截断。。。
A: fd_a = open(path = 'filename', mode = write)
A: ftruncate(fd_a, 100)
A: write(fd_a, 'abc')
B: fd_b = openat(dirfd = X, path = 'filename', mode = write | truncate)
B: ftruncate(fd_b, 200)
B: close(fd_b)
A: write(fd_a, 'def')
A: close(fd_a)