Существует ли быстрый и надежный способ POSIX проверить, находится ли текущее смещение файла в конце файла? - PullRequest
4 голосов
/ 02 июля 2019

Как проверить, находится ли текущая позиция записи в конце файла, используя низкоуровневые функции POSIX?Первая идея состоит в том, чтобы использовать lseek и fstat:

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

Однако отражает ли 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;

, но это не кажется быстрым и адекватным способом.

Также оба способа кажутся неатомарными, поэтому, если к файлу добавляется другой процесс, размер можно изменить после проверки текущего смещения.

1 Ответ

0 голосов
/ 04 июля 2019

Однако st->st_size отражает фактический размер, но не размер файла диска, т.е. не включает буферизованные данные ядра?

Я не понимаю, что вы имеете в виду под данные в буфере ядра .Число в st->st_size отражает размер файла в символах.Таким образом, если файл имеет 1000000 символов, число, которое st->st_size будет 1000000, с позициями символов от 0 до 999999.

Существует два способа получить размер файла.в системах POSIX:

  • сделать off_t saved = lseek(fd, 0, SEEK_END);, который возвращает фактическую позицию (вы должны сохранить ее, чтобы восстановить ее позже), и второй вызов off_t file_size = lseek(fd, saved, SEEK_SET);, который возвращает к позиции, которой вы былидо, но возвращает в качестве числа позицию, в которой вы были до этого (это последняя позиция файла после последнего символа). Если вы отметите это, это будет соответствовать значению, возвращенному st->st_size.
  • выполните stat(2) для файлового дескриптора, чтобы получить указанное выше значение.

Первый способ имеет некоторые недостатки, если у вас есть несколько потоков или процессов, совместно использующих дескриптор файла (с помощьюdup(2) системный вызов или fork() ed-процесс), если они делают вызовы 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);
}

, в которой вы получите файл размером 1000013 байт, который использует только один или два блока дискового пространства.Это файл holed , в котором перед записанной строкой находится 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
$ _
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...