如何实现类似于“ truncateat”的内容?

时间:2019-04-19 16:55:31

标签: linux posix system-calls glibc

在研究this question时,我发现一个事实,即在POSIX(和Linux)中根本没有truncateat系统调用。

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

看着truncate,旁边只有ftruncatetruncate适用于路径-绝对路径或相对于当前工作目录的路径。 ftruncate直接在打开的文件句柄上工作-无需指定任何路径。没有truncateat

许多库(各种“替代” C库)都执行我的操作,并使用tuncateat-openat-ftruncate序列来模仿close。在大多数情况下,此方法有效,除了...

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

  • 处理“ A”
  • 处理“ B”

现在想象以下事件序列(伪代码):

A: fd = open(path = 'filename', mode = write)
A: ftruncate(fd, 100)
A: write(fd, 'abc')
B: truncate('filename', 200)
A: write(fd, 'def')
A: close(fd)

以上工作正常。在进程“ 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”从技术上讲落在了正确的位置(三个字节,就好像我在写seek(fd_a, 3)之前就调用了它)。

我可以很好地处理第一个操作序列。但是在我的用例中,就进程“ B”而言,我不能依赖相对于当前工作目录的路径。我真的很想使用相对于文件描述符的路径。如何实现这一目标-而不会遇到第二步操作中演示的问题?仅在fsync之后从进程“ A”调用write(fd_a, 'abc')不能解决此问题。

1 个答案:

答案 0 :(得分:1)

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

如果您立即将ftruncate设置为200,而没有先将其截断为0,则直到该点的现有数据都将保持不变。