Почему некоторые встроенные оболочки `read` не могут прочитать всю строку из файла в` / proc`?

Вы можете попробовать пакет pm-utils для куча команд управления питанием. Это:

pm-hibernate       pm-powersave       pm-suspend-hybrid  
pm-is-supported    pm-suspend  

Что касается блокировки, в репозитории есть пакет xscreensaver . Вы можете попробовать.

19
13.05.2016, 00:51
4 ответа

Проблема в том, что эти /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 у вас все еще будут проблемы для файлов proc размером более 128 байт, которые можно прочитать только за один системный вызов read.

bash также, похоже, отключает эту оптимизацию при использовании опции -d.

ksh93 заводит оптимизацию еще дальше, настолько, что она становится фальшивой. read ksh93 выполняет поиск назад, но запоминает дополнительные данные, которые он прочитал, для следующего 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
32
27.01.2020, 19:44

На первый взгляд, это похоже на ошибку в оболочках, которые возвращают меньше, чем возвращает настоящий Bourne Shell или его производные (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 или клон 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.

4
27.01.2020, 19:44

В файлах под /proc иногда используется символ NULL для разделения полей внутри файла. Похоже, что read не в состоянии обработать это.

0
27.01.2020, 19:44

Если вам интересно узнать, почему? это так, то вы можете посмотреть ответ на этот вопрос ответ в исходниках ядра здесь:

    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 и он поддерживает seeks, так что чтение /proc/stat не должно вызывать проблем. проблем. Реализация /proc/sys/, как мы видим, не использует этот api.

11
27.01.2020, 19:44

Теги

Похожие вопросы