Что это за вспомогательные файловые дескрипторы?
Пока ковыряюсь /proc/self папка для моего mksh раковину я обнаружил странную вещь: в /proc/self/fd/* есть все стандартные файловые дескрипторы (0 для stdin, 1 для stdout и 2 stderr), некоторые файловые дескрипторы, но также есть и некоторые дополнительные - 24, 25, 3. И я технически могу перечислить их с глобусом в оболочке:
$ for fd in /proc/self/fd/* ; do echo $fd ; done
/proc/self/fd/0
/proc/self/fd/1
/proc/self/fd/2
/proc/self/fd/24
/proc/self/fd/25
/proc/self/fd/3
Но когда я пытаюсь stat их или использовать find на них они сообщаются как несуществующие.
$ find /proc/self/fd/*
/proc/self/fd/0
/proc/self/fd/1
/proc/self/fd/2
find: ‘/proc/self/fd/24’: No such file or directory
find: ‘/proc/self/fd/25’: No such file or directory
/proc/self/fd/3
То же самое происходит в bash, но только с одним вспомогательным файловым дескриптором.
$ for fd in /proc/self/fd/* ; do echo $fd; done
/proc/self/fd/0
/proc/self/fd/1
/proc/self/fd/2
/proc/self/fd/255
/proc/self/fd/3
$ find /proc/self/fd/*
/proc/self/fd/0
/proc/self/fd/1
/proc/self/fd/2
find: ‘/proc/self/fd/255’: No such file or directory
/proc/self/fd/3
Вопрос: что это за дополнительные файловые дескрипторы? Какова их цель?
1 ответ
Прощупывание /proc/self это сложный бизнес, так как он меняется для каждого процесса. Когда вы делаете /proc/self/fd/*оболочка расширяет подстановочный знак, поэтому она перечисляет свои собственные файловые дескрипторы. Но когда они передаются другой команде, как find или же lsпути теперь будут для этого процесса " /proc/selfи может иметь или не иметь fds с этими числами.
Еще хитрее, оболочка может открывать файловые дескрипторы во время расширения по шаблону.
Сравнивая с /proc/$$/fd может быть освещающим:
bash:
$ ls -l /proc/self/fd /proc/$$/fd/* &
[1] 5172
$ lrwx------ 1 muru muru 64 Jan 1 20:16 /proc/4932/fd/0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:16 /proc/4932/fd/1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:16 /proc/4932/fd/2 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:16 /proc/4932/fd/255 -> /dev/pts/1
/proc/self/fd:
total 0
lrwx------ 1 muru muru 64 Jan 1 20:24 0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:24 1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:24 2 -> /dev/pts/1
lr-x------ 1 muru muru 64 Jan 1 20:24 3 -> /proc/5172/fd
[1]+ Done ls --color=auto -l /proc/self/fd /proc/$$/fd/*
Отправив его на задний план, я получил bash для печати PID, и вы можете видеть, что /proc/self/fd/3 указывает на ls' своя /proc/<PID>/fd, который он открыл для сканирования. Записи с 4932, OTOH, предназначены для fds bash, а специальный - 255. Объяснение можно найти в этом посте SO:
Открытые файлы: 0 (стандартный вывод), 1 (стандартный вывод) и 2 (стандартный вывод). 255 - это маленькая хитрость, которую использует bash, чтобы сохранить их копии, когда они перенаправляются. Это специфично для bash.
Источник: https://books.google.com/books?id=wWjqCF9HLfYC&pg=PA231
С mksh:
$ ls -l /proc/self/fd /proc/$$/fd/* &
[1] 5075
$ lrwx------ 1 muru muru 64 Jan 1 20:22 /proc/5074/fd/0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:22 /proc/5074/fd/1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:22 /proc/5074/fd/10 -> /dev/tty
lrwx------ 1 muru muru 64 Jan 1 20:22 /proc/5074/fd/2 -> /dev/pts/1
/proc/self/fd:
total 0
lrwx------ 1 muru muru 64 Jan 1 20:22 0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:22 1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 1 20:22 2 -> /dev/pts/1
lr-x------ 1 muru muru 64 Jan 1 20:22 3 -> /proc/5075/fd
[1] + Done ls -l /proc/self/fd /proc/$$/fd/*
Практически то же самое, за исключением того, что дополнительный fd равен 10, и я бы поспорил, что это по той же причине, что и bash, поскольку исходный код указывает, что fd 10 и далее используется оболочкой.
У меня не было двух или трех дополнительных fds, но это могло произойти из-за любого количества вещей, происходящих во время расширения по шаблону, или из-за фоновых заданий или по какой-то другой неясной причине.
Если я буду управлять вашим for петля, я получаю эфемерный FD 3:
$ for fd in /proc/$$/fd/* ; do ls -l $fd ; done
lrwx------ 1 muru muru 64 Jan 2 17:39 /proc/6012/fd/0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:39 /proc/6012/fd/1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:39 /proc/6012/fd/2 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:39 /proc/6012/fd/255 -> /dev/pts/1
ls: cannot access '/proc/6012/fd/3': No such file or directory
И вот, используя strace отследить исполнение:
strace -e open -o log bash -c 'for fd in /proc/$$/fd/* ; do : ; done'
Мы увидим, что третий FD, на самом деле, /proc/<PID>/fd:
$ tail log
open("/usr/lib/libreadline.so.7", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/libncursesw.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/dev/tty", O_RDWR|O_NONBLOCK) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/gconv/gconv-modules.cache", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/gconv/gconv-modules", O_RDONLY|O_CLOEXEC) = 3
open("/proc/9975/fd/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
+++ exited with 0 +++
Теперь вопрос, почему этот fd не появился в более ранних ls тесты? Похоже, фон был как-то связан с этим:
$ ls -l /proc/$$/fd/* &
[1] 10091
$ lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/2 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/255 -> /dev/pts/1
[1]+ Done ls --color=auto -l /proc/self/fd /proc/$$/fd/*
$ ls -l /proc/$$/fd/*
ls: cannot access '/proc/10076/fd/3': No such file or directory
lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/2 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 17:46 /proc/10076/fd/255 -> /dev/pts/1
Передний план ls показывает недостающий фд.
Теперь снова отслеживание с strace:
strace -fe open,execve,fork -o log bash -ic 'ls -l /proc/self/fd /proc/$$/fd/* &'
Мы видим:
10731 execve("/usr/bin/bash", ["bash", "-ic", "ls -l /proc/$$/fd/* &"], [/* 67 vars */]) = 0
10731 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
# snip
10734 open("/proc/10731/fd/", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
10734 execve("/usr/bin/ls", ["ls", "--color=auto", "-l", "/proc/10731/fd/0", "/proc/10731/fd/1", "/proc/10731/fd/2", "/proc/10731/fd/255"], [/* 68 vars */]) = 0
Обратите внимание на изменение в PID. Кажется, что расширение подстановки происходит после разветвления, но расширение переменной происходит до этого. Итак, fd 3 существует, но в другом процессе. Теперь я использую self вместо $$вы увидите 3 и 255:
$ strace -fe open,execve -o log bash -ic 'ls -l /proc/self/fd/* &'
[1] 10790
ls: cannot access '/proc/self/fd/255': No such file or directory
ls: cannot access '/proc/self/fd/3': No such file or directory
lrwx------ 1 muru muru 64 Jan 2 18:04 /proc/self/fd/0 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 18:04 /proc/self/fd/1 -> /dev/pts/1
lrwx------ 1 muru muru 64 Jan 2 18:04 /proc/self/fd/2 -> /dev/pts/1
добавление
Соответствующий ответ на сайте Unix & Linux Stackexchange цитирует ответ из списка рассылки:
Fd 255 используется внутри как соединение с tty, так что это не мешает использованию exec для перемещения fds. Bash также выделяет высокие fds при обработке подстановки процесса `<(foo) 'по той же причине.
Андреас Шваб