Почему hexdump пытается прочитать EOF?

Если бы это были два обычных задания, то простое fgвывело бы второе задание на передний план, если это было самое последнее приостановленное фоновое задание или фоновое задание, которое было запущено последним (без каких-либо других приостановленных заданий. ).

Однако из-за характера первого задания (, считывающего из /dev/tty), оно будет остановлено, как только попытается прочитать из TTY. Таким образом, fgпоместит его на передний план.

Здесь можно использовать либо %-, либо %2, учитывая, что других заданий нет.


Когда задание переводится в фоновый режим либо путем запуска задания с завершающим символом &, либо путем приостановки приоритетного задания с помощью Ctrl+Z с последующим вводом команды bg, заданию присваивается «идентификатор задания» или «спецификация задания». Этот идентификатор задания обычно обозначается как %N, где N— некоторое десятичное число.

Вы можете использовать идентификатор задания с несколькими командами, такими как wait, kill, fgи bg. Команда kill, например, может использоваться для отправки сигнала фоновому заданию с использованием его идентификатора задания.

Команда fgпо умолчанию работает с «текущим заданием». Идентификатор текущего задания доступен как %+или %%. Текущее задание — это самое последнее запущенное фоновое задание или, если есть приостановленные задания, последнее приостановленное задание.

Чтобы переместить произвольное фоновое задание на передний план, используйте его идентификатор задания:

$ sleep 1000 &
[1] 37327
$ sleep 500 &
[2] 83532
$ jobs
[1]-  Running                 sleep 1000 &
[2]+  Running                 sleep 500 &
$ fg %1
sleep 1000

+в выводе jobsобозначает «текущее задание» (, на которое можно было бы переключиться простым вызовомfg).

Стандартные идентификаторы специальных заданий POSIX:

  • %%, текущее задание.
  • %+, то же, что и выше.
  • %-, предыдущее задание.
  • %N, номер задания N.
  • %string, задание, команда которого начинается с string.
  • %?string, задание, команда которого содержит string.
5
04.05.2019, 09:42
2 ответа

Благодаря @JdeBP за подсказку , я смог создать небольшой тестовый пример, который делает то же самое, что и hexdump:

.
#include <stdio.h>

int main(void){
        char buf[64]; size_t r;
        for(;;){
                printf("eof=%d, error=%d\n", feof(stdin), ferror(stdin));
                r = fread(buf, 1, sizeof buf, stdin);
                printf("read %zd bytes, eof=%d, error=%d\n",
                        r, feof(stdin), ferror(stdin));
                if(!r) return 0;
        }
}

При запуске в системе на основе glibc (типичный рабочий стол Linux ).

prompt$./fread-test
eof=0, error=0
<control-D>
read 0 bytes, eof=1, error=0

prompt$./fread-test
eof=0, error=0
hello
<control-D>
read 6 bytes, eof=1, error=0
eof=1, error=0
<control-D>
read 0 bytes, eof=1, error=0

При запуске на bsd, solaris, busybox (uclibc ), android и т. д.:

prompt$./fread-test
eof=0, error=0
hello
<control-D>
read 6 bytes, eof=1, error=0
eof=1, error=0
read 0 bytes, eof=1, error=0

Основываясь на моей неумелой интерпретации стандарта, это похоже на ошибку в glibc (библиотеки GNU C ).

Оfread:

For each object, size calls shall be made to the fgetc() function and the results stored, in the order read, in an array of unsigned char exactly overlaying the object.

Оfgetc:

If the end-of-file indicator for the input stream pointed to by stream is not set and a next byte is present, the fgetc() function shall obtain the next byte

Кажется, что glibc попытается "получить следующий байт", даже если установлен индикатор eof.

Действительно, это на самом деле ошибка в библиотеке GNU C, которой нет в библиотеках BSD или musl C. Об этом стало известно в 2005 году . Ульрих Дреппер закрыл отчет об ошибке, не исправив ошибку в 2007 году. Это обсуждалось в 2012 году , где было отмечено, что другие библиотеки C не имеют и не имеют такого поведения, что стандарт C 1999 года довольно специфичен. об этом, и что в Solaris даже есть специальный механизм для этого, который вызывается, когда c99используется в качестве компилятора вместо cc.

Это было окончательно исправлено в 2018 году . Исправление находится в версии 2.28 библиотеки GNU C. Текущая «стабильная» версия Debian, версия 9, относится к версии 2.24 библиотеки GNU C , и поэтому эта ошибка продолжает проявляться спустя 14 лет после того, как о ней сообщили.

Как отмечалось в обсуждениях библиотеки GNU C, существует возможность того, что программное обеспечение, написанное так, требует особенностей библиотеки GNU C, независимо от других библиотек C, таких как musl, или поведения на других платформы. Однако в вышеупомянутых дискуссиях за эти годы такой программы выявлено не было. Принимая во внимание, что было обнаружено несколько программ, которые сломаны старой библиотекой GNU C и требуют от пользователей дважды сигнализировать EOF;в том числе hexdumpздесь и patchна StackOverflow еще в 2018 году .

3
27.01.2020, 20:41

Вывод строки

…
read(0, "hello\n", 1024)                = 6
read(0, "", 1024)                       = 0
read(0, "hello\n", 1024)                = 6
read(0, "", 1024)                       = 0
read(0, "", 1024)                       = 0
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 11),...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8dd4cef000
write(1, "0000000   h   e   l   l   o  \\n "..., 72) = 72
write(1, "000000c\n", 8)                = 8
exit_group(0)                           = ?
+++ exited with 0 +++

Здесь я сделал hello «ctrl -d» hello «ctrl -d» «ctrl -d» (второе приветствие усекается в выводе журнала ). Так что, похоже, это так запрограммировано.

Для запуска strace

  • Открыть две клеммы
  • В первом типеll /proc/self/fd/1
  • Во втором типеstrace hexdump -C 2>/dev/pts/«number-at-end-of-output-of-previous-command»
0
27.01.2020, 20:41

Теги

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