Что это за вспомогательные файловые дескрипторы?

Обойдя папку /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

Вопрос: что это за дополнительные файловые дескрипторы? Какова их цель?

8
задан 1 January 2017 в 23:47

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/*

Путем отправки его в фон я заставил удар печатать PID, и Вы видите это /proc/self/fd/3 точки к ls'собственный /proc/<PID>/fd, который это открыло для сканирования. Записи с 4932, OTOH, для fds удара, и специальный - 255. Объяснение найдено в этом, ТАК отправьте:

Открытые файлы 0 (stdin), 1 (stdout), и 2 (stderr). 255 немного приема, которые колотят использование для хранения копии их для того, когда они перенаправляются. Это характерно для удара.

Источник: 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, и я держал бы пари, что это по той же причине как удар, так как исходный код указывает, что 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'

Мы будем видеть, что третий фарадей, на самом деле, /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 показывает пропавших без вести fd.

Теперь, снова трассировка с 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

Обратите внимание на изменения в PIDs. Кажется, что подстановочное расширение происходит после разветвления, но переменное расширение происходит перед этим. Так, 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, так, чтобы это не вмешивалось в использование должностного лица для перемещения fds. Bash также выделяет высокий fds при обработке замены процесса' <(нечто)' по той же причине.

Andreas Schwab

15
ответ дан 1 January 2017 в 23:47

Другие вопросы по тегам:

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