ftell /fseek отличается от фактической длины считываемых данных в файле /sys /class

#c #linux #file #sysfs

#c #linux #файл #sysfs

Вопрос:

встроенная система Linux

  1. ls -al /sys/class/xxxx
    -r--r--r-- 4096

  2. ftell/lstat(st_size) возвращает 4096

  3. fread(fp, 1, 4096, buf) возвращает 3

На самом деле, в файле всего 3 байта, для открытия которого используется vi.

В чем проблема?

Комментарии:

1. Если /sys является разделом sysfs, файл представляет собой одну страницу, поэтому 4k

2. Я думаю, что 4096 байт — это «размер на диске» файла, а не фактический размер файла

3. @Programmerdude Размер на диске сообщается du -sh . Фактический размер указывается ls -l и du -sh --apparent-size .

Ответ №1:

/sys это специальная файловая система, в которой файлы генерируются ядром по требованию. Смотрите man sysfs(5) для получения более подробной информации:

Файловая система sysfs — это псевдофайловая система, которая предоставляет интерфейс к структурам данных ядра. (Точнее, файлы и каталоги в sysfs предоставляют представление структур kobject, определенных внутри ядра.) Файлы в sysfs предоставляют информацию об устройствах, модулях ядра, файловых системах и других компонентах ядра.

Размер файлов в, /sys сообщаемых ls , не является фактическим размером, потому что содержимое файлов не генерируется только с целью получения размера файла. Содержимое генерируется только при read системном вызове.

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

Ответ №2:

В чем проблема?

Проблема в том, что fseek() / ftell() является принципиально неверным способом определения размера файла.

Файлы могут быть открыты либо в двоичном, либо в текстовом режиме.

Для двоичных файлов fseek(file, 0, SEEK_END) явно не определено поведение. Согласно сноске 268 стандарта C:

Установка индикатора положения файла в значение конец файла, как и в fseek(file, 0, SEEK_END) , имеет неопределенное поведение для двоичного потока (из-за возможных завершающих нулевых символов) или для любого потока с зависящей от состояния кодировкой, который не обязательно заканчивается в начальном состоянии сдвига.

Согласно 7.21.9.2 функции fseek, параграф 3:

… Двоичный поток не обязательно должен осмысленно поддерживать вызовы fseek со значением откуда SEEK_END.

Таким образом, нет переносимого, совместимого со стандартами способа поиска до конца двоичного потока.

И вы не можете использовать ftell() для получения размера файла, открытого в текстовом режиме. Согласно 7.21.9.4 Функции ftell, параграф 2(обратите внимание на выделенные жирным шрифтом части):

ftell Функция получает текущее значение индикатора положения файла для потока, на который указывает stream . Для двоичного потока значением является количество символов с начала файла. Для текстового потока его индикатор положения файла содержит неопределенную информацию, используемую функцией fseek для возврата индикатора положения файла для потока к его положению во время вызова ftell; разница между двумя такими возвращаемыми значениями не обязательно является значимой мерой количества записанных или прочитанных символов.

Вы не можете искать до конца двоичного потока, и вы не можете использовать ftell для получения размера текстового потока.

Комментарии:

1. Понижающий голос без комментариев, когда код, размещенный в вопросе, является явно неопределенным поведением, потому что он использует fseek(.., SEEK_END) в двоичном потоке? И получает неожиданные результаты?

2. Очень хорошо объяснено. Спасибо!

3. Но этот метод де-факто работает с обычными файлами в большинстве систем, поэтому IDK, если он объясняет поведение, наблюдаемое в этом вопросе. Системные вызовы Unix, подобные lseek , могут иметь ту же проблему в файлах sysfs, и в этом случае это на самом деле не связано с отказом уровня C stdio определять достаточное поведение, чтобы быть полезным. Это интересно, но IDK, если это подходящее место для этого. Наверняка есть вопросы и ответы о переносимом поиске длин файлов в C, где этот ответ подошел бы, предостерегая от того, что я считал стандартным способом? (Ссылка на такой ответ послужила бы хорошим комментарием к этому вопросу).

4. Да, только что попробовал это с strace программой, которая создает lseek(fd, 0, SEEK_END) , и этот системный вызов возвращает 4096 strace ./lseek /sys/class/tty/console/active , а wc в том же файле считывает только 5 байт. godbolt.org/z/Mcderd7Gx для источника моей оболочки lseek. Таким образом, даже четко определенные системные вызовы POSIX не работают; на самом деле это не связано с тем, что C позволяет реализации оставлять это поведение неопределенным.

Ответ №3:

В системе unix размер блока файловой системы должен составлять 4 КБ.
Я не хочу дублировать ответ, поэтому проверьте это:

https://unix.stackexchange.com/questions/62049/why-are-text-files-4kb
https://en.wikipedia.org/wiki/File_system#Space_management

Если вы хотите иметь реальный размер файла в байтах, используйте это:

 fp = fopen("...", "rb");
fseek(fp, 0L, SEEK_END);
size_t size = ftell(fp);
  

И если вы хотите прочитать из файла reset, попробуйте начать:

 rewind(fp);
  

или

 fseek(fp, 0L, SEEK_SET);
  

Комментарии:

1. fp = fopen("...", "r"); открывает файл в текстовом режиме. Вы не можете использовать ftell() для получения размера файла в текстовом режиме. Согласно 7.21.9.4 функции ftell , параграф 2 стандарта C : «Для текстового потока его индикатор положения файла содержит неопределенную информацию, используемую функцией fseek для возврата индикатора положения файла для потока в его положение во время вызова ftell; разница между двумя такими возвращаемыми значениями не обязательно является значимой мерой количества записанных или прочитанных символов. »

2. @AndrewHenle Вы правы, я не говорю, что это количество символов , это размер в байтах . Но истина в том, что открытый файл, подобный тексту, может сбивать с толку, я изменю.

3. ls -l отображает фактический размер в байтах, а не используемый размер. В большинстве файловых систем указанный размер соответствует фактической читаемой длине для обычных файлов, это sysfs, что странно. (Он не знает точную длину, потому что содержимое генерируется «на лету» только при фактическом чтении. Было бы пустой тратой процессорного времени на самом деле делать это только для получения длины для stat системных вызовов.)