dup2 / dup - warum sollte ich einen Dateideskriptor duplizieren müssen?

85

Ich versuche die Verwendung von dup2und zu verstehen dup.

Von der Manpage:

DESCRIPTION

dup and dup2 create a copy of the file descriptor oldfd.
After successful return of dup or dup2, the old and new descriptors may
be used interchangeably. They share locks, file position pointers and
flags; for example, if the file position is modified by using lseek on
one of the descriptors, the position is also changed for the other.

The two descriptors do not share the close-on-exec flag, however.

dup uses the lowest-numbered unused descriptor for the new descriptor.

dup2 makes newfd be the copy of oldfd, closing newfd first if necessary.  

RETURN VALUE

dup and dup2 return the new descriptor, or -1 if an error occurred 
(in which case, errno is set appropriately).  

Warum sollte ich diesen Systemaufruf brauchen? Was nützt es, den Dateideskriptor zu duplizieren?

Wenn ich den Dateideskriptor habe, warum sollte ich eine Kopie davon erstellen wollen?

Ich würde mich freuen, wenn Sie mir erklären und ein Beispiel geben könnten, wo dup2/ dupbenötigt wird.

Vielen Dank

JAN
quelle
Wie würden Sie die Rohrleitungsfunktionalität von Schalen ohne dupoder implementieren dup2? Sie müssen anrufen pipe(2)und dann einen der Dateideskriptoren habendupSTDIN_FILENO
Basile Starynkevitch
1
Mögliche Duplikate von praktischen Beispielen verwenden dup oder dup2
DrTyrsa

Antworten:

46

Der dup-Systemaufruf dupliziert einen vorhandenen Dateideskriptor und gibt einen neuen zurück, der auf dasselbe zugrunde liegende E / A-Objekt verweist.

Mit Dup können Shells folgende Befehle implementieren:

ls existing-file non-existing-file > tmp1  2>&1

Das 2> & 1 weist die Shell an, dem Befehl einen Dateideskriptor 2 zu geben, der ein Duplikat von Deskriptor 1 ist (dh stderr & stdout zeigen auf denselben fd).
Jetzt wird die Fehlermeldung zum Aufrufen von ls für eine nicht vorhandene Datei und die korrekte Ausgabe von ls für eine vorhandene Datei in der Datei tmp1 angezeigt .

Der folgende Beispielcode führt das Programm wc mit Standardeingabe aus, die an das Leseende einer Pipe angeschlossen ist.

int p[2];
char *argv[2];
argv[0] = "wc";
argv[1] = 0;
pipe(p);
if(fork() == 0) {
    close(STDIN); //CHILD CLOSING stdin
    dup(p[STDIN]); // copies the fd of read end of pipe into its fd i.e 0 (STDIN)
    close(p[STDIN]);
    close(p[STDOUT]);
    exec("/bin/wc", argv);
} else {
    write(p[STDOUT], "hello world\n", 12);
    close(p[STDIN]);
    close(p[STDOUT]);
}

Das Kind kopiert das Leseende auf den Dateideskriptor 0, schließt die Datei de scriptors in p und führt wc aus. Wenn wc von seiner Standardeingabe liest, liest es von der Pipe.
Auf diese Weise werden Pipes mit dup implementiert. Nun, bei einer Verwendung von dup verwenden Sie Pipe, um etwas anderes zu erstellen. Das ist das Schöne an Systemaufrufen. Sie erstellen eine Sache nach der anderen mit Tools, die bereits vorhanden sind. Diese Tools wurden von innen erstellt etwas anderes so weiter .. Am Ende sind Systemaufrufe die grundlegendsten Werkzeuge, die Sie im Kernel erhalten

Prost :)

Tiefer Gedanke
quelle
1
Ist dupalso hilfreich für den Anrufer und nicht für das lsProgramm selbst? Gibt es einen Vorteil, dupwenn Sie ein Programm wie ls selbst verwendet haben, wenn Sie bereits Zugriff auf die Datei haben? Hier werden zum Beispiel lsFehler geschrieben, auf 2die fest codiert ist, sodass ich als Verbraucher von die Möglichkeit habe, sie zu überschreiben ls. Ich denke, das ist ein subtiler Punkt, nein?
Nishant
2
Ihr Beispielprogramm scheint einen Fehler zu haben. Sie rufen an dup(p[STDIN]), werfen dann aber das Ergebnis weg. Wolltest du verwenden dup2(p[STDIN], 0)?
Quuxplusone
1
@Quuxplusone dupgibt den "Deskriptor mit der niedrigsten Nummer zurück, der derzeit vom Prozess nicht verwendet wird". Da fd 0 gerade geschlossen wurde, dupsollte 0 zurückgegeben werden. Es dup2wird explizit angegeben, welches fd verwendet werden soll, anstatt nur das niedrigste freie fd zu verwenden, daher würde ich das vorziehen.
Wodin
@Wodin: Ah, ich wette du hast Recht damit, was OP gedacht hat. Habe ich aber auch Recht, dass "nur geschlossen" relativ ist und der Code von OP beispielsweise bei gleichzeitigen Threads, die möglicherweise auch Dateien öffnen, beschädigt werden kann?
Quuxplusone
@Quuxplusone Ich vermute, Sie haben Recht, aber ich weiß es nicht genau. In diesem Fall haben Sie jedoch andere Probleme. Wenn Sie stdin schließen, weil Sie von einem anderen Ort lesen möchten, und dann ein anderer Thread eine Datei öffnet, bevor Sie dies tun, wird fd 0 angezeigt. Wenn Sie dann dup2 verwenden, wird der fd geschlossen, den der andere Thread geöffnet hat, also der andere Thread wird nun aus der von Ihnen geöffneten Datei lesen (und in diese schreiben?). Wenn ich mich richtig erinnere, sollten Sie nicht exec*von einem Multithread-Prozess aus anrufen . Aber ich bin kein Threading-Experte :)
Wodin
18

Ein weiterer Grund für das Duplizieren eines Dateideskriptors ist die Verwendung mit fdopen. fcloseSchließt den Dateideskriptor, an den übergeben fdopenwurde. Wenn Sie also nicht möchten, dass der ursprüngliche Dateideskriptor geschlossen wird, müssen Sie ihn dupzuerst duplizieren .

R .. GitHub HÖREN SIE AUF, EIS ZU HELFEN
quelle
fdopen()Anscheinend wird kein Dateideskriptor dupliziert, sondern nur ein Puffer im Benutzerbereich erstellt.
Eric Wang
3
Du hast meine Antwort falsch verstanden. Der Punkt ist, dass Sie dupdas fd möglicherweise möchten, bevor Sie es übergeben, fdopenda fclosees geschlossen wird.
R .. GitHub STOP HELPING ICE
1
@ theferrit32: Wenn Sie ein FILEHandle für den Zugriff auf eine bereits vorhandene geöffnete Datei über stdio-Schnittstellen zuweisen , müssen Sie aufrufen fclose, FILEum die Zuordnung dieses Handles aufzuheben . Wenn Sie die zugrunde liegende geöffnete Datei weiterhin verwenden möchten oder wenn Ihre Softwarearchitektur so ist, dass der ursprüngliche "Eigentümer" -Code für den Dateideskriptor dies tut close, ist die Tatsache, dass fcloseauch der zugrunde liegende Dateideskriptor, den Sie übergeben haben, geschlossen fdopenwird, ein Problem. Sie können dieses Problem vermeiden, indem Sie dupeinen neuen Dateideskriptor für dieselbe geöffnete Datei erstellen, an die übergeben werden soll fdopen, damit fclosedie ursprüngliche Datei nicht geschlossen wird.
R .. GitHub STOP HELPING ICE
1
Der Punkt ist, dass fdopen () das Eigentum an der fd auf die verschiebt FILE, anstatt sie zu kopieren . Das sollten Benutzer beachten. Verbraucher, die fdzusätzlich zum FILEObjekt ein verwendbares Handle behalten müssen, müssen das duplizieren fd. Das ist alles.
Conrad Meyer
1
@ConradMeyer: Ja, das ist eine sehr gute Art, es auszudrücken, mit dem Hinweis, dass es keine Operation gibt, um das Eigentum von dem zu entfernen, FILEsobald Sie das Eigentum darauf verlagern.
R .. GitHub STOP HELPING ICE
4

dup wird verwendet, um die Ausgabe eines Prozesses umleiten zu können.

Wenn Sie beispielsweise die Ausgabe eines Prozesses speichern möchten, duplizieren Sie die Ausgabe (fd = 1), leiten die duplizierte fd in eine Datei um, geben den Prozess auf und führen ihn erneut aus. Wenn der Prozess abgeschlossen ist, leiten Sie die Ausgabe erneut um fd zur Ausgabe gespeichert.

Alinsoar
quelle
4

Einige Punkte im Zusammenhang mit dup / dup2 können bitte notiert werden

dup / dup2 - Technisch gesehen besteht der Zweck darin, einen Dateitabelleneintrag innerhalb eines einzelnen Prozesses durch verschiedene Handles gemeinsam zu nutzen. (Wenn wir forken, wird der Deskriptor im untergeordneten Prozess standardmäßig dupliziert und der Dateitabelleneintrag wird ebenfalls freigegeben.)

Das bedeutet, dass wir mit der Funktion dup / dup2 mehr als einen Dateideskriptor mit möglicherweise unterschiedlichen Attributen für einen einzelnen Eintrag in einer offenen Dateitabelle haben können.

(Obwohl derzeit anscheinend nur das FD_CLOEXEC-Flag das einzige Attribut für einen Dateideskriptor ist).

http://www.gnu.org/software/libc/manual/html_node/Descriptor-Flags.html

dup(fd) is equivalent to fcntl(fd, F_DUPFD, 0);

dup2(fildes, fildes2); is equivalent to 

   close(fildes2);
   fcntl(fildes, F_DUPFD, fildes2);

Unterschiede sind (für den letzten) - Abgesehen von einigen Fehlern zwischen dup2 und fcntl close, gefolgt von fcntl, können sich die Rennbedingungen erhöhen, da zwei Funktionsaufrufe beteiligt sind.

Details können unter http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html überprüft werden

Ein Anwendungsbeispiel -

Ein interessantes Beispiel für die Implementierung der Jobsteuerung in einer Shell, in der die Verwendung von dup / dup2 unter dem folgenden Link zu sehen ist

http://www.gnu.org/software/libc/manual/html_node/Launching-Jobs.html#Launching-Jobs

Tanmoy Bandyopadhyay
quelle