Was sind diese zusätzlichen Dateideskriptoren?

8

Beim Stöbern im /proc/selfOrdner nach meiner mkshShell habe ich eine Besonderheit festgestellt: In /proc/self/fd/*allen Standard-Dateideskriptoren (0 für stdin, 1 für stdout und 2 stderr) sind einige Dateideskriptoren enthalten, aber auch einige zusätzliche - 24, 25, 3 Und ich kann sie technisch mit einem Glob in der Shell auflisten:

$ 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

Aber wenn ich versuche, statsie zu verwenden oder findsie zu verwenden, werden sie als nicht vorhanden gemeldet.

$ 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

Gleiches tritt in auf bash, jedoch nur mit nur einem zusätzlichen Dateideskriptor.

$ 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

Die Frage ist: Was sind diese zusätzlichen Dateideskriptoren? Was ist ihr Zweck?

Sergiy Kolodyazhnyy
quelle

Antworten:

16

Das Prüfen /proc/selfist eine knifflige Angelegenheit, da es sich für jeden Prozess ändert. Wenn Sie dies tun /proc/self/fd/*, erweitert die Shell den Platzhalter, sodass sie ihre eigenen Dateideskriptoren auflistet. Wenn diese jedoch an einen anderen Befehl wie findoder übergeben werden ls, sind die Pfade jetzt für diesen Prozess vorgesehen /proc/self, und es kann fds mit diesen Nummern geben oder nicht.

Noch schwieriger ist es, wenn die Shell während der Platzhaltererweiterung Dateideskriptoren öffnet.

Ein Vergleich mit /proc/$$/fdkönnte aufschlussreich sein:

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

Indem ich es in den Hintergrund schickte, bekam ich Bash, um die PID zu drucken, und Sie können sehen, dass diese /proc/self/fd/3Punkte zu ls"eigenen" gehören /proc/<PID>/fd, die zum Scannen geöffnet wurden. Die Einträge mit 4932OTOH sind für bashs fds und der spezielle ist 255. Eine Erklärung finden Sie in diesem SO-Beitrag :

Die geöffneten Dateien sind 0 (stdin), 1 (stdout) und 2 (stderr). 255 ist ein kleiner Trick, mit dem bash eine Kopie davon aufbewahrt, wenn sie umgeleitet werden. Dies ist spezifisch für Bash.

Quelle: https://books.google.com/books?id=wWjqCF9HLfYC&pg=PA231

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

Praktisch dasselbe, außer dass das zusätzliche fd 10 ist, und ich würde wetten, dass es aus dem gleichen Grund wie bash ist, da der Quellcode angibt, dass fd 10 und höher von der Shell verwendet wird.

Ich habe nicht zwei oder drei zusätzliche FDS erhalten, aber es könnte an einer beliebigen Anzahl von Dingen liegen, die während der Wildcard-Erweiterung passieren, oder an Hintergrundjobs oder einem anderen unbekannten Grund.

Wenn ich deine forSchleife starte, bekomme ich eine kurzlebige 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

Und hier, straceum die Ausführung zu verfolgen:

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

Wir werden sehen, dass der dritte fd tatsächlich ist /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 +++

Die Frage ist nun, warum dieses fd in den früheren lsTests nicht aufgetaucht ist . Es sieht so aus, als hätte der Hintergrund etwas damit zu tun:

$ 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

Der Vordergrund lszeigt das fehlende fd.

Nun noch einmal verfolgen mit strace:

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

Wir sehen:

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

Beachten Sie die Änderung der PIDs. Es scheint, dass die Platzhaltererweiterung nach dem Gabeln stattfindet, aber die variable Erweiterung erfolgt vorher . Fd 3 existiert also, aber in einem anderen Prozess. Wenn Sie jetzt selfanstelle von verwenden $$, sehen Sie sowohl 3 als auch 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

Nachtrag

Eine ähnliche Antwort auf Unix & Linux Stack Website zitiert eine Antwort von einer Mail - Liste :

Fd 255 wird intern als Verbindung zum tty verwendet, damit die Verwendung von exec zum Verschieben von fds nicht beeinträchtigt wird. Bash weist aus dem gleichen Grund auch hohe fds zu, wenn eine Prozessersetzung "<(foo)" behandelt wird.

Andreas Schwab

muru
quelle
1
Sie können auch genau herausfinden, welche Dateien Ihre Shell geöffnet hat, indem Sie den folgenden Befehl lsof -p $$
Jake