В чем разница между & 6 и / dev / fd / 6?

11

Для чтения из файлового дескриптора 6 я могу использовать <&6или </dev/fd/6(иначе /proc/self/fd/6). Обычно оба работают одинаково хорошо. Однако если этот дескриптор файла оказывается сокетом, происходят странные вещи. Например:

$ bash -c 'ls -l /dev/fd/6;cat /dev/fd/6' 6</dev/tcp/localhost/12345
lrwx------ 1 michas michas 64 Jan 10 19:50 /dev/fd/6 -> socket:[315010]
cat: /dev/fd/6: No such device or address

Здесь lsпоказано, дескриптор действительно присутствует. Но получить доступ к данным таким образом невозможно. Если я использую cat <&6вместо этого все снова работает хорошо.

В чем разница между обоими способами доступа к дескриптору файла?

Есть ли хороший способ получить доступ к дескриптору, если число указано в переменной? ( </dev/fd/$fdбудет работать, но <&$fdне работает.)

(Вышеуказанная ситуация может наблюдаться в Linux, но не в OpenBSD. - Кажется, дескриптор файла является обычным символическим устройством там.)

Михась
источник
1
Это дублированный файл unix.stackexchange.com/q/98958/38906
cuonglm
2
Благодарю. Это связано, но на самом деле не дубликат.
Михас

Ответы:

5

Это так, потому что чтение из /dev/fd/записей, представляющих сокеты, не реализовано в Linux. Вы можете найти довольно хорошую рецензию здесь. Таким образом, вы можете позвонить statпо ссылке, и именно поэтому вы видите его ls, но доступ намеренно запрещен.

Теперь о второй части - почему bash -c 'ls -l /dev/fd/6; cat <&6' 6</dev/tcp/localhost/12345работает? Это потому, что сокет читается с использованием API сокета / файла, а не /procфайловой системы. Вот что я наблюдал за происходящим:

  1. bash экземпляр, работающий в вашем терминале, создает сокет с fd 6.
  2. Ребенок bashбегает и звонит dup2(6, 0), чтобы присоединить вашу розетку как catи stdin.
  3. Если dup2звонок не удался, кошка читает stdin.

Вы можете воспроизвести и наблюдать это с:

netcat -lp 12345    # in another terminal session (GNU netcat)
strace -f -e trace=open,read,write,dup2 bash -c 'ls -l /dev/fd/6; cat <&6' \
 6</dev/tcp/localhost/12345

Если вам интересно, почему bashдочерний процесс имеет доступ к fd 6 - дескрипторы файлов сохраняются fork, и если они не помечены для закрытия exec, они также не закрываются там.

ТЯО
источник
3

Чтобы ответить на ваш прямой вопрос «в чем разница ?»:

Когда вы перенаправляете с <&6, оболочка использует dup2()системный вызов для дублирования дескриптора файла. Когда вы (попытка) перенаправить с </dev/fd/6, он будет использовать open().

Ядро не поддерживает open()сокеты в /dev/fd; они присутствуют в каталоге только для оформления информации.

Тоби Спейт
источник