Чому деякі вбудовані оболонки `read` не вдається прочитати весь рядок з файлу в` / proc`?


19

У деяких Bourne-подібні оболонок, то readвбудований не може прочитати весь рядок з файлу /proc(команда нижче повинні бути запущені в zsh, замініть $=shellз $shellіншими оболонками):

$ for shell in bash dash ksh mksh yash zsh schily-sh heirloom-sh "busybox sh"; do
  printf '[%s]\n' "$shell"
  $=shell -c 'IFS= read x </proc/sys/fs/file-max; echo "$x"'       
done
[bash]
602160
[dash]
6
[ksh]
602160
[mksh]
6
[yash]
6
[zsh]
6
[schily-sh]
602160
[heirloom-sh]
602160
[busybox sh]
6

readСтандарт вимагає, щоб стандартний вхід повинен бути текстовим файлом , чи викликає ця вимога різноманітна поведінка?


Прочитайте визначення текстового файлу POSIX , я перевіряю:

$ od -t a </proc/sys/fs/file-max 
0000000   6   0   2   1   6   0  nl
0000007

$ find /proc/sys/fs -type f -name 'file-max'
/proc/sys/fs/file-max

У NULвмісті немає жодного символу /proc/sys/fs/file-max, і він також findповідомив про це як звичайний файл (це помилка find?).

Я думаю, що оболонка зробила щось під кришкою, наприклад file:

$ file /proc/sys/fs/file-max
/proc/sys/fs/file-max: empty

Відповіді:


31

Проблема полягає в тому, що ті /procфайли в Linux, наскільки stat()/fstat()це стосується, є як текстові файли , але не ведуть себе як такі.

Оскільки це динамічні дані, ви можете здійснювати лише один read()системний дзвінок на них (для деяких з них принаймні). Якщо більше ніж один, ви зможете отримати два шматки двох різних вмістів, тому натомість здається, що другий read()на них просто нічого не повертає (маючи на увазі кінець файлу) (якщо ви не lseek()повернетесь до початку (і лише до початку)).

readУтиліта має зчитувати вміст файлів один байт в той час , щоб переконатися , що не читати повз символу нового рядка. Ось що dashробить:

 $ strace -fe read dash -c 'read a < /proc/sys/fs/file-max'
 read(0, "1", 1)                         = 1
 read(0, "", 1)                          = 0

Деякі оболонки, як-от, bashмають оптимізацію, щоб уникнути необхідності робити багато read()системних дзвінків. Спочатку вони перевіряють, чи є файл доступним, і якщо так, читають шматками, оскільки тоді вони знають, що можуть повернути курсор відразу після нового рядка, якщо вони прочитали його:

$ strace -e lseek,read bash -c 'read a' < /proc/sys/fs/file-max
lseek(0, 0, SEEK_CUR)                   = 0
read(0, "1628689\n", 128)               = 8

З bash, у вас все ще будуть проблеми з файлами Pro, які мають більше 128 байт, і їх можна читати лише за один системний виклик читання.

bashтакож, здається, відключити оптимізацію, коли використовується -dпараметр.

ksh93забирає оптимізацію ще більше, щоб стати неправдою. ksh93's readнамагається повернутись назад, але запам'ятовує додаткові дані, які він прочитав протягом наступного read, тому наступний read(або будь-який з інших його вбудованих файлів, які читають дані, як-от catабо head), навіть не намагається отримати readдані (навіть якщо ці дані були змінені інші команди між ними):

$ seq 10 > a; ksh -c 'read a; echo test > a; read b; echo "$a $b"' < a
1 2
$ seq 10 > a; sh -c 'read a; echo test > a; read b; echo "$a $b"' < a
1 st

Ага так, straceпояснення на основі базування набагато простіше!
Стівен Кітт

Дякую, динамічні дані мають сенс. То як оболонка визначає динамічні дані? Якщо я це зроблю cat /proc/sys/fs/file-max | ..., проблема пішла.
cuonglm

3
Оболонка не виявляє. Той факт, що це динамічні дані, означає, що procfsне можна обробляти кілька послідовних read(2)дзвінків до одного файлу; поведінка не залежить від оболонки. Використання catта конфігурування працює тому, що catчитає файл досить великими фрагментами; readпотім вбудована оболонка зчитує з труби один символ за часом.
Стівен Кітт

1
Тут є дещо брудне рішення mksh. read -N 10 a < /proc/sys/fs/file-max
Іпор Сірсер

1
@IporSircer. Справді. Схожа робота навколо, здається, працює з zsh: read -u0 -k10(або використовувати sysread; $mapfile[/proc/sys/fs/file-max]не працює, оскільки ці файли неможливо mmapредагувати). У будь-якому випадку, з будь-якою оболонкою завжди можна a=$(cat /proc/sys/fs/file-max). З деякими включаючи mksh, zshі ksh93, a=$(</proc/sys/fs/file-max)також працює і не розгортає процес для читання.
Стефан Шазелас

9

Якщо вам цікаво знати, чому? це так, відповідь ви можете побачити у джерелах ядра тут :

    if (!data || !table->maxlen || !*lenp || (*ppos && !write)) {
            *lenp = 0;
            return 0;
    }

В основному, пошук ( *pposне 0) не реалізується для читання ( !write) значень sysctl, які є числами. Щоразу, коли зчитування виконується з /proc/sys/fs/file-max, відповідна процедура __do_proc_doulongvec_minmax()розпочинається з запису file-maxв таблиці конфігурації того самого файлу.

Інші записи, такі як /proc/sys/kernel/poweroff_cmdреалізовані, за допомогою proc_dostring()яких дозволяють шукати, тому ви можете робити dd bs=1на ній і читати з оболонки без проблем.

Зауважте, що оскільки ядро ​​2.6 /procчитає, було реалізовано за допомогою нового API під назвою seq_file, і це підтримує прагнення, наприклад, читання /proc/statне повинно викликати проблем. /proc/sys/Реалізація, як ми можемо бачити, не використовувати цей API.


3

З першої спроби це виглядає як помилка в оболонках, які повертаються менше, ніж справжня оболонка Борна або її похідні повертаються (sh, bosh, ksh, heirloom).

Оригінальний Bourne Shell намагається прочитати блок (64 байти). Нові варіанти Bourne Shell читають 128 байт, але вони починають читати знову, якщо немає нового символу рядка.

Передумови: / procfs та подібні реалізації (наприклад, змонтований /etc/mtabвіртуальний файл) мають динамічний вміст, і stat()виклик спочатку не викликає відновлення динамічного контенту. З цієї причини розмір такого файлу (від читання до EOF) може відрізнятися від stat()повернення.

Зважаючи на те, що стандарт POSIX вимагає, щоб утиліти в будь-який час очікували короткого читання , програмне забезпечення, яке вважає, що показник, read()який повертає менше, ніж впорядкована кількість байтів, є показником EOF. Правильно реалізована утиліта викликає read()другий раз у випадку, якщо вона повертається менше, ніж очікувалося - до повернення 0. У разі readвбудованих команд, було б, звичайно , буде досить, щоб читати до EOF або до тих пір , NLвидно.

Якщо ви запустите trussабо крок ферми, ви повинні мати можливість перевірити неправильну поведінку для оболонок, які повертаються лише 6в експерименті.

У цьому спеціальному випадку, здається, це помилка ядра Linux, див.

$ sdd -debug bs=1 if= /proc/sys/fs/file-max 
Simple copy ...
readbuf  (3, 12AC000, 1) = 1
writebuf (1, 12AC000, 1)
8readbuf  (3, 12AC000, 1) = 0

sdd: Read  1 records + 0 bytes (total of 1 bytes = 0.00k).
sdd: Wrote 1 records + 0 bytes (total of 1 bytes = 0.00k).

Ядро Linux повертає 0 з другим, readі це, звичайно, неправильно.

Висновок: оболонки, які спочатку намагаються прочитати досить великий фрагмент даних, не викликають помилку ядра Linux.


Гаразд, вийшов з відповіді з новою верифікацією помилки ядра Linux.
шилі

Це не помилка, це особливість!
Guntram Blohm підтримує Моніку

Це справді дивна претензія.
шилі

Це було б особливістю, якби це було зафіксовано документально. Читаючи kernel.org/doc/Documentation/filesystems/proc.txt , я не бачу жодної документації щодо поведінки. Однак, це чітко працює як задумано від реалізатора, тому якщо це вважати помилкою, це помилка в дизайні, а не в реалізації.
Чарльз Даффі

0

Файли під / proc іноді використовують символ NULL для розділення полів у файлі. Здається, що читати не в змозі з цим впоратися.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.