Ответ 1
1. Объясните, почему cat <(cat)
производит EIO
(Я использую Debian Linux 8.7, Bash 4.4.12)
Давайте заменим <(cat)
на долго работающий <(sleep)
, чтобы посмотреть, что происходит.
Из pty # 1:
$ echo $$
906
$ tty
/dev/pts/14
$ cat <(sleep 12345)
Перейдите к другому pty # 2:
$ ps t pts/14 j
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
903 906 906 906 pts/14 29999 Ss 0 0:00 bash
906 29998 906 906 pts/14 29999 S 0 0:00 bash
29998 30000 906 906 pts/14 29999 S 0 0:00 sleep 12345
906 29999 29999 906 pts/14 29999 S+ 0 0:00 cat /dev/fd/63
$ ps p 903 j
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
1 903 903 903 ? -1 Ss 0 0:07 SCREEN -T linux -U
$
Позвольте мне объяснить это (согласно книге APUE, 2-е издание):
TPGID
, являющийся29999
, указывает, чтоcat
(PID29999
) является группой процессов переднего плана, которая теперь управляет терминалом (pts/14
). Иsleep
находится в группе фоновых процессов (PGID906
).- Группа процессов
906
теперь является потерянной группой процессов, потому что "родитель каждого члена либо сам является членом группы, либо не является членом сеанса групп". (PID906
PPID -903
, а903
находится в другом сеансе.) - Когда процесс в потерянной группе фоновых процессов читает со своего управляющего терминала,
read()
завершится ошибкой сEIO
.
2. Объясните, почему cat <(cat)
иногда работает (не совсем!)
Даниэль Война упомянул в комментарии, что cat <(cat)
работает на OS X с Bash 3.2.57
. Мне только что удалось воспроизвести это и на Linux с Bash 4.4.12
.
Из pty # 1:
bash-4.4# echo $$
10732
bash-4.4# tty
/dev/pts/0
bash-4.4# cat <(cat)
cat: -: Input/output error
bash-4.4#
bash-4.4#
bash-4.4# bash --norc --noprofile # start a new bash
bash-4.4# tac <(cat)
<-- It waiting here so looks like it working.
(Первая cat <(cat)
ошибка с EIO
была объяснена в первой части моего ответа.)
Перейдите к другому pty # 2:
bash-4.4# ps t pts/0 j
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
10527 10732 10732 10732 pts/0 10805 Ss 0 0:00 bash
10732 10803 10803 10732 pts/0 10805 S 0 0:00 bash --norc --noprofile
10803 10804 10803 10732 pts/0 10805 S 0 0:00 bash --norc --noprofile
10804 10806 10803 10732 pts/0 10805 T 0 0:00 cat
10803 10805 10805 10732 pts/0 10805 S+ 0 0:00 tac /dev/fd/63
bash-4.4# ps p 10527 j
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
10526 10527 10527 10527 ? -1 Ss 0 0:00 SCREEN -T dtterm -U
bash-4.4#
Давайте посмотрим, что происходит:
TPGID
, являющийся10805
, указывает, чтоtac
(PID10805
) является группой процессов переднего плана, которая теперь управляет терминалом (pts/0
). Иcat
(PID10806
) находится в группе фоновых процессов (PGID10803
).Но на этот раз pgrp
10803
не потерян, поскольку его родительский элемент PID10803
(bash
) (PID10732
,bash
) находится в другом pgrp (PGID10732
) и в том же сеансе (SID10732
).Согласно книге APUE,
SIGTTIN
будет "сгенерирован драйвером терминала, когда процесс в (не потерянной) группе фоновых процессов попытается прочитать данные с его управляющего терминала". Поэтому, когдаcat
читает stdin, ему будет отправленоSIGTTIN
, и по умолчанию этот сигнал остановит процесс. Вот почему столбецcat
STAT
был показан какT
(остановлен) на выходеps
. Так как он остановил данные, которые мы вводим с клавиатуры, ему вообще не отправляются. Так что это выглядит как это работает, но это не совсем так.
Вывод:
Таким образом, различные варианты поведения (EIO
и SIGTTIN
) зависят от того, является ли текущий Bash лидером сеанса или нет. (В 1-й части моего ответа bash PID 906
является лидером сеанса, но bash PID 10803
во 2-й части не является лидером сеанса.)