Quais são esses descritores de arquivos auxiliares?

6

Enquanto folheava a pasta /proc/self para meu mksh shell, descobri uma coisa peculiar: em /proc/self/fd/* há todos os descritores de arquivo padrão (0 para stdin, 1 para stdout e 2 stderr) alguns descritores de arquivo , mas também alguns extras - 24, 25, 3. E eu tecnicamente posso listá-los com um glob no shell:

$ 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

Mas quando eu tento stat deles ou uso find neles, eles são relatados como inexistentes.

$ 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

O mesmo ocorre em bash , mas apenas com apenas um descritor de arquivo auxiliar.

$ 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

A pergunta é: quais são os descritores de arquivos adicionais? Qual é o propósito deles?

    
por Sergiy Kolodyazhnyy 01.01.2017 / 11:11

1 resposta

10

Sondar /proc/self é um negócio complicado, pois muda para cada processo. Quando você executa /proc/self/fd/* , o shell expande o caractere curinga, listando seus próprios descritores de arquivo. Mas quando estes são passados para outro comando, como find ou ls , os caminhos agora serão para esse processo ' /proc/self , e pode ou não ter fds com esses números.

Ainda mais complicado, o shell pode abrir descritores de arquivos durante a expansão do caractere curinga.

A comparação com /proc/$$/fd pode ser esclarecedora:

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

Ao enviá-lo para o segundo plano, recebi o bash para imprimir o PID, e você pode ver que /proc/self/fd/3 aponta para ls 'own /proc/<PID>/fd , que ele abriu para digitalizar. As entradas com 4932 , OTOH, são para fds do bash, e a especial é 255. Uma explicação é encontrada em este post do SO :

  

Os arquivos abertos são 0 (stdin), 1 (stdout) e 2 (stderr). 255 é um   pequeno truque que bash usa para manter uma cópia destes para quando eles estão   redirecionado. Isso é específico para o bash.

     

Fonte: link

com 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/* 
Praticamente a mesma coisa, exceto que o extra fd é 10, e eu aposto que é pelo mesmo motivo que o bash, já que o código-fonte indica que o fd 10 e em diante é usado pelo shell.

Eu não consegui dois ou três fds extras, mas isso pode ser devido a qualquer número de coisas que acontecem durante a expansão de curingas, ou devido a trabalhos em segundo plano ou algum outro motivo obscuro.

Se eu executar o seu loop for , recebo um efêmero 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

E aqui, usando strace para rastrear a execução:

strace -e open -o log bash -c 'for fd in /proc/$$/fd/* ; do : ; done'

Veremos que o terceiro fd é, na verdade, /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 +++

Agora a questão é: por que esse fd não apareceu nos testes anteriores de ls ? Parece que o backgrounding tem algo a ver com isso:

$ 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

O primeiro plano ls mostra a falta de fd.

Agora, novamente rastreando com strace :

strace -fe open,execve,fork -o log bash -ic 'ls -l /proc/self/fd /proc/$$/fd/* &'

Nós vemos:

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

Observe a mudança nos PIDs. Parece que a expansão de curingas ocorre após a bifurcação, mas a expansão de variáveis acontece antes disso. Então, o fd 3 existe, mas em um processo diferente. Agora que você usa self em vez de $$ , você verá os dois e os 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
    
por muru 01.01.2017 / 12:32