有没有一种快速可靠的 POSIX 方法来检查当前文件偏移量是否位于文件末尾?

Nic*_*ick 5 c linux posix

如何使用低级 POSIX 函数检查当前写入位置是否位于文件末尾?第一个想法是使用 lseek 和 fstat:

off_t sk;
struct stat st;
sk = lseek (f, 0, SEEK_CUR);
fstat (f, &st);
return st->st_size == sk;
Run Code Online (Sandbox Code Playgroud)

但是,是否st->st_size反映了实际大小而不是磁盘文件大小,即不包括内核缓冲数据?

另一个想法是使用

off_t scur, send;
scur = lseek (f, 0, SEEK_CUR);
send = lseek (f, 0, SEEK_END);
lseek (f, scur, SEEK_START);
return scur == send;
Run Code Online (Sandbox Code Playgroud)

但这似乎不是快速且充分的方法。

此外,这两种方式似乎都是非原子的,因此如果有另一个进程附加到文件,则可以在检查当前偏移量后更改大小。

Lui*_*ado -1

但是,是否st->st_size反映了实际大小而不是磁盘文件大小,即不包括内核缓冲数据?

我不明白你所说的内核缓冲数据是什么意思。中的数字st->st_size反映了文件的大小(以字符为单位)。因此,如果文件有1000000字符,则数字st->st_size将为1000000,字符位置从0999999

POSIX系统中有两种获取文件大小的方法:

  • 执行一个off_t saved = lseek(fd, 0, SEEK_END);,它返回实际位置(您必须保存它,以便稍后恢复它),第二次调用off_t file_size = lseek(fd, saved, SEEK_SET);返回您之前的位置,但以数字形式返回您之前的位置(这是文件,在最后一个字符之后)如果您选中此项,这将与 . 返回的值匹配st->st_size
  • stat(2)对文件描述符执行 a以获取您提到的值。

如果您有多个线程或进程与您共享文件描述符(通过dup(2)系统调用或ed 进程),第一种方法有一些缺点,如果它们在您的两个调用之间fork()执行read(2)write(2)或调用,您将丢失您之前在文件上的位置,并且将无法恢复到正确的位置。这很奇怪,并且使得第一种方法不值得推荐。lseek(2)lseek

最后,内核中完成的文件缓冲与文件大小没有关系。您始终可以在 上获得真实的文件大小stat(2)。唯一会让您感到困惑的是,当您运行以下代码片段时,在内核中完成的节省(但这对您来说是透明的,您不必考虑它,除非您要将文件复制到另一个文件地方)。只需运行这个小程序:

#include <fcntl.h>
#include <unistd.h>
int main()
{
    int fd = open("file", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    lseek(fd, 1000000, SEEK_SET);
    char string[] = "Hello, world";
    write(fd, string, sizeof string);
    close(fd);
}
Run Code Online (Sandbox Code Playgroud)

其中将以1000013字节文件结束,但仅使用一两个磁盘空间块。这是一个有洞的文件,其中1000000您写入的字符串之前有零字节,并且系统不会在磁盘中为其分配块。只有当您在这些块上写入时,系统才会用新块填充您写入的部分以保存您的数据......但在此之前,系统将向您显示零字节,但它们不会存储在任何地方。

$ ll file
-rw-r-----  1 lcu  lcu  1000013  4 jul.  11:52 file
$ hd file
[file]:
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 :................
*
000f4240: 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 00          :Hello, world.
000f424d
$ _
Run Code Online (Sandbox Code Playgroud)