dup2 / dup - po co duplikować deskryptor pliku?

85

Próbuję zrozumieć użycie dup2i dup.

Ze strony podręcznika:

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).  

Po co mi to wywołanie systemowe? jaki jest pożytek z powielania deskryptora pliku?

Jeśli mam deskryptor pliku, dlaczego miałbym chcieć zrobić jego kopię?

Byłbym wdzięczny, gdybyś mógł wyjaśnić i podać przykład, gdzie dup2/ dupjest potrzebny.

Dzięki

JAN
źródło
Jak zaimplementowałbyś funkcjonalność orurowania skorup bez duplub dup2? Trzeba zadzwonić, pipe(2)a potem mieć jeden z deskryptorów plikówdupSTDIN_FILENO
nadany

Odpowiedzi:

46

Wywołanie systemowe dup powiela istniejący deskryptor pliku, zwracając nowy, który odwołuje się do tego samego bazowego obiektu we / wy.

Dup pozwala powłokom na implementację takich poleceń:

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

2> & 1 mówi powłoce, aby przekazała poleceniu deskryptor pliku 2, który jest duplikatem deskryptora 1. (tj. Stderr i stdout wskazują na to samo fd).
Teraz komunikat o błędzie dotyczący wywołania ls na nieistniejącym pliku i prawidłowe wyjście ls w istniejącym pliku pojawia się w pliku tmp1 .

Poniższy przykładowy kod uruchamia program wc ze standardowym wejściem podłączonym do końca odczytu potoku.

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]);
}

Dziecko kopiuje koniec odczytu do deskryptora pliku 0, zamyka plik de scriptors w p i wykonuje wc. Kiedy wc czyta ze swojego standardowego wejścia, czyta z potoku.
Oto jak potoki są implementowane przy użyciu dup, cóż, jedno użycie dup teraz używasz potoku do zbudowania czegoś innego, to jest piękno wywołań systemowych, budujesz jedną rzecz po drugiej używając narzędzi, które już istnieją, te narzędzia zostały zbudowane ponownie za pomocą coś jeszcze tak dalej… Ostatecznie wywołania systemowe są najbardziej podstawowymi narzędziami, jakie dostajesz w jądrze

Twoje zdrowie :)

Głęboka myśl
źródło
1
Czy dupjest to pomocne dla dzwoniącego, a nie dla lssamego programu? Czy jest jakaś korzyść z dupużywania w programie takim jak sam ls, jeśli ma już dostęp do pliku? Tutaj na przykład lszapisuje błędy, do 2których jest zakodowane na stałe, więc jako konsument mam sposób, aby je zastąpić ls. Myślę, że to subtelna kwestia, nie?
Nishant
2
Twój przykładowy program wydaje się mieć błąd; dzwonisz, dup(p[STDIN])ale odrzucasz wynik. Czy chciałeś użyć dup2(p[STDIN], 0)?
Quuxplusone
1
@Quuxplusone dupzwraca „deskryptor o najniższym numerze, który nie jest obecnie używany przez proces”. Ponieważ fd 0 właśnie się zamknęło, duppowinno zwrócić 0. dup2jest jasne, którego fd powinno być użyte, zamiast używać tylko najniższego wolnego fd, więc wolałbym to.
Wodin
@Wodin: Ach, założę się, że masz rację co do myśli OP. Czy mam też rację, że „właśnie zamknięte” jest względne, a kod OP może się zepsuć w obecności, na przykład, współbieżnych wątków, które również mogą otwierać pliki?
Quuxplusone
@Quuxplusone Podejrzewam, że masz rację, ale nie wiem na pewno. Ale w takim przypadku będziesz mieć inne problemy. Jeśli zamkniesz stdin, ponieważ chcesz czytać z innego wątku, a następnie inny wątek otworzy plik, zanim to zrobisz, otrzyma fd 0. Jeśli następnie użyjesz dup2, zamknie fd, który otworzył drugi wątek, więc drugi wątek będzie teraz czytać (i zapisywać?) plik, który otwierasz. W każdym razie, jeśli dobrze pamiętam, nie powinieneś dzwonić exec*z procesu wielowątkowego. Ale ja nie jestem ekspertem od wątków :)
Wodin
18

Innym powodem duplikowania deskryptora pliku jest używanie go z rozszerzeniem fdopen. fclosezamyka deskryptor pliku, do którego został przekazany fdopen, więc jeśli nie chcesz, aby oryginalny deskryptor pliku był zamykany, musisz dupnajpierw zduplikować go za pomocą .

R .. GitHub PRZESTAŃ POMÓC LODOWI
źródło
fdopen()wydaje się, że nie powiela deskryptora pliku, po prostu tworzy bufor w przestrzeni użytkownika.
Eric Wang
3
Źle odczytałeś moją odpowiedź. Chodzi o to, że możesz chcieć do dupfd przed przekazaniem go do, fdopenponieważ fclosezamknie go.
R .. GitHub ZATRZYMAJ SIĘ NA LODZIE
1
@ theferrit32: Jeśli przydzielisz FILEuchwyt, aby uzyskać dostęp do istniejącego wcześniej otwartego pliku przez interfejsy stdio, musisz wywołać, fcloseaby zwolnić ten FILEuchwyt. Jeśli chcesz nadal używać podstawowego otwartego pliku lub jeśli architektura twojego oprogramowania jest taka, że ​​oryginalny kod „właściciela” deskryptora pliku będzie closeto robił, fakt, że fcloserównież zamyka podstawowy deskryptor pliku, który przekazałeś, fdopenjest problemem. Możesz uniknąć tego problemu, używając dupdo utworzenia nowego deskryptora pliku dla tego samego otwartego pliku, do którego ma zostać przekazany fdopen, tak aby fclosenie zamykał oryginalnego.
R .. GitHub PRZESTAŃ POMÓC W LODZIE
1
Chodzi o to, że fdopen () przenosi własność fd do katalogu FILE, zamiast go kopiować . To jest coś, o czym użytkownicy powinni wiedzieć. Konsumenci, którzy muszą zachować fduchwyt użyteczny oprócz FILEobiektu, muszą zduplikować rozszerzenie fd. To wszystko.
Conrad Meyer
1
@ConradMeyer: Tak, to bardzo dobry sposób na określenie tego, z uwagą, że nie ma operacji „przeniesienia własności” z momentu FILEprzeniesienia własności.
R .. GitHub PRZESTAŃ POMÓC NA LODZIE
4

dup służy do przekierowania wyjścia z procesu.

Na przykład, jeśli chcesz zapisać dane wyjściowe z procesu, duplikujesz dane wyjściowe (fd = 1), przekierowujesz zduplikowany plik fd do pliku, następnie forkuj i wykonuj proces, a po zakończeniu procesu przekierowujesz ponownie zapisano fd do wyjścia.

alinsoar
źródło
4

Proszę zwrócić uwagę na kilka punktów związanych z dup / dup2

dup / dup2 - Technicznie celem jest współdzielenie jednego wpisu tabeli plików w ramach jednego procesu pomocą różnych uchwytów. (Jeśli rozwidlamy deskryptor jest domyślnie powielany w procesie potomnym, a wpis w tablicy plików jest również współdzielony).

Oznacza to, że możemy mieć więcej niż jeden deskryptor pliku mający prawdopodobnie różne atrybuty dla jednego wpisu tablicy otwartego pliku przy użyciu funkcji dup / dup2.

(Chociaż wydaje się, że obecnie jedyna flaga FD_CLOEXEC jest jedynym atrybutem deskryptora pliku).

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);

Różnice są (ostatnie) - oprócz pewnej wartości errno między wartościami errno beteen dup2 i fcntl close, po których następuje fcntl, mogą podnieść warunki wyścigu, ponieważ w grę wchodzą dwa wywołania funkcji.

Szczegóły można sprawdzić pod adresem http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html

Przykład użycia -

Ciekawy przykład implementacji kontroli zadań w powłoce, gdzie użycie dup / dup2 można zobaczyć .. w linku poniżej

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

Tanmoy Bandyopadhyay
źródło