如何实现类似"truncateat"的东西?
How to implement something similar to "truncateat"?
在研究 时,我发现 POSIX(和 Linux)中根本没有 truncateat
系统调用。
某些系统调用,例如 unlink
有一个等效的替代方法,在其名称末尾添加了 at
后缀,即 unlinkat
。这些方法之间的区别在于带有 at
后缀的变体接受一个额外的参数,一个指向目录的文件描述符。因此,传递给 unlinkat
的相对路径不是相对于当前工作目录,而是相对于提供的文件描述符(一个打开的目录)。这在某些情况下非常有用。
看truncate
,旁边只有ftruncate
。 truncate
适用于路径 - 相对于当前工作目录的绝对路径或相对路径。 ftruncate
直接作用于打开的文件句柄 - 无需指定任何路径。没有truncateat
.
很多库(各种 "alternative" C 库)做我所做的并通过使用 openat
-ftruncate
-[=27= 模仿 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
不能解决这个问题。
第二种情况用零覆盖所有内容的原因是 mode=truncate(即 openat(.., O_TRUNC)
)将首先将文件截断为长度 0。
如果您直接 ftruncate
到 200 而不是先截断为 0,则直到该点的现有数据将保持不变。
在研究 truncateat
系统调用。
某些系统调用,例如 unlink
有一个等效的替代方法,在其名称末尾添加了 at
后缀,即 unlinkat
。这些方法之间的区别在于带有 at
后缀的变体接受一个额外的参数,一个指向目录的文件描述符。因此,传递给 unlinkat
的相对路径不是相对于当前工作目录,而是相对于提供的文件描述符(一个打开的目录)。这在某些情况下非常有用。
看truncate
,旁边只有ftruncate
。 truncate
适用于路径 - 相对于当前工作目录的绝对路径或相对路径。 ftruncate
直接作用于打开的文件句柄 - 无需指定任何路径。没有truncateat
.
很多库(各种 "alternative" C 库)做我所做的并通过使用 openat
-ftruncate
-[=27= 模仿 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
不能解决这个问题。
第二种情况用零覆盖所有内容的原因是 mode=truncate(即 openat(.., O_TRUNC)
)将首先将文件截断为长度 0。
如果您直接 ftruncate
到 200 而不是先截断为 0,则直到该点的现有数据将保持不变。