【问题标题】:How to implement something similar to "truncateat"?如何实现类似于“truncateat”的东西?
【发布时间】:2019-09-09 22:07:11
【问题描述】:

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

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

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

很多库(各种“替代”C 库)都按照我的做法进行操作,并通过使用 openat-ftruncate-close-sequence 来模仿 tuncateat。这在大多数情况下都有效,除了 ...

我遇到了以下问题。我花了几个月的时间才弄清楚发生了什么。在 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”而言,我不能依赖与当前工作目录相关的路径。我真的很想使用相对于文件描述符的路径。怎样才能做到这一点 - 而不会遇到第二个操作序列中展示的问题?在write(fd_a, 'abc') 之后从进程“A”调用fsync 并不能解决此问题。

【问题讨论】:

  • 你是说truncate 对你有用吗?
  • @thatotherguy 我测试了它。它确实有效。
  • 那么问题就在于您使用 mode=truncate 打开文件,从而进行了两次截断(首先在字节 0 处,然后在 200 处)?
  • @thatotherguy “以截断模式打开”是否意味着截断为零?
  • @thatotherguy 那将是一个非常愚蠢的错误。让我尝试不使用 mode=truncate ...

标签: linux posix system-calls glibc


【解决方案1】:

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

如果您直接将 ftruncate 改为 200 而没有先截断为 0,则直到该点的现有数据将保持不变。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-08
    • 2015-06-04
    • 1970-01-01
    • 1970-01-01
    • 2011-01-14
    • 2020-09-28
    • 1970-01-01
    相关资源
    最近更新 更多