dup(2)
โจ dup(2) โจ
okay so bestie, this is the System Calls Manual page for dup(2) and honestly? it's giving "how to clone a file descriptor" and it ate ๐
no cap.
๐ธ NAME ๐ธ
dup, dup2, dup3 โ duplicate a file descriptor
๐ LIBRARY ๐
Standard C library (libc, -lc) ๐ซถ
โจ SYNOPSIS โจ
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <fcntl.h> /* Definition of O_* constants */
#include <unistd.h>
int dup3(int oldfd, int newfd, int flags);
the _GNU_SOURCE thing is literally the vibe check you gotta pass before dup3 will even look at you, iykyk ๐
๐ DESCRIPTION ๐
so the dup() system call is the main character here: it allocates a brand-new file descriptor that refers to the same open file description as the descriptor oldfd. (and if you're sitting there like "bestie what even is an open file description", go read open(2), it explains it, trust ๐.)
the new file descriptor number is guaranteed to be the lowest-numbered file descriptor that was unused in the calling process. like, it literally goes looking for the first free spot. so efficient. so iconic. ๐
after a successful return, the old and new file descriptors may be used interchangeably. and because the two file descriptors refer to the same open file description, they share the file offset and the file status flags. for example: if the file offset is modified by using lseek(2) on one of the file descriptors, the offset is also changed for the other one. they're linked. soulmates, basically ๐ซถ๐
BUT โ and this is the plot twist โ the two file descriptors do not share file descriptor flags. that's the close-on-exec flag. the close-on-exec flag (FD_CLOEXEC; see fcntl(2)) for the duplicate descriptor is off. so it starts fresh, no baggage ๐
๐ฆ dup2() ๐ฆ
the dup2() system call does the same task as dup(), but instead of grabbing the lowest-numbered unused file descriptor, it uses the file descriptor number you specify in newfd. in other words: newfd gets adjusted so that it now refers to the same open file description as oldfd. you're not getting a hand-me-down slot, you're claiming a specific one โจ
if the file descriptor newfd was previously open, it gets closed before being reused โ and that close is performed silently, which means any errors during the close are not reported by dup2(). it just... doesn't tell you. ๐
the steps of closing and reusing the file descriptor newfd are performed atomically. this is important, bestie, because if you tried to build this yourself with close(2) and dup() you'd be living in race condition city, where newfd might get reused between those two steps. that reuse could happen because the main program got interrupted by a signal handler that allocates a file descriptor, or because a parallel thread allocates a file descriptor. chaos. not the cute kind either ๐
note the following points:
- if
oldfdis not a valid file descriptor, then the call fails, andnewfdis not closed. (protective king energy, honestly.) - if
oldfdis a valid file descriptor, andnewfdhas the same value asoldfd, thendup2()does nothing, and returnsnewfd. it simply refuses to do the thing. periodt.
๐ dup3() ๐
dup3() is the same as dup2(), except that:
- the caller can force the close-on-exec flag to be set for the new file descriptor by specifying
O_CLOEXECinflags. (see the description of the same flag inopen(2)for reasons why this may be useful ๐) - if
oldfdequalsnewfd, thendup3()fails with the errorEINVAL. no silent no-op for you, sorry babe.
๐ RETURN VALUE ๐
on success, these system calls return the new file descriptor. on error, -1 is returned, and errno is set to indicate the error. classic. ๐คทโโ๏ธ
๐ ERRORS ๐
EBADFโoldfdisn't an open file descriptor.EBADFโnewfdis out of the allowed range for file descriptors (see the discussion ofRLIMIT_NOFILEingetrlimit(2)).EBUSYโ (Linux only) this may be returned bydup2()ordup3()during a race condition withopen(2)anddup().EINTRโ thedup2()ordup3()call was interrupted by a signal; seesignal(7)๐ญEINVALโ (dup3())flagscontain an invalid value.EINVALโ (dup3())oldfdwas equal tonewfd.EMFILEโ the per-process limit on the number of open file descriptors has been reached (see the discussion ofRLIMIT_NOFILEingetrlimit(2)). you've officially collected too many. hoarder behavior. respect. ๐ENOMEMโ insufficient kernel memory was available.
โญ STANDARDS โญ
POSIX.1-2024. she standards-certified, your honor โจ
๐ง HISTORY ๐ง
dup() , dup2() โ 4.3BSD, SVr4, POSIX.1-1988.
dup3() โ POSIX.1-2024. Linux 2.6.27, glibc 2.9.
look at the lore. the ancestry. we love a documented glow-up ๐ธ
๐ NOTES ๐
the error returned by dup2() is different from the error returned by fcntl(..., F_DUPFD, ...) when newfd is out of range. on some systems, dup2() also sometimes returns EINVAL like F_DUPFD. it's messy, it's inconsistent, we simply accept it ๐ซ
if newfd was open, any errors that would have been reported at close(2) time are lost. and if this is of concern to you โ then, unless the program is single-threaded and does not allocate file descriptors in signal handlers โ the correct approach is not to close newfd before calling dup2(), because of the race condition described above. instead, code something like the following could be used:
/* Obtain a duplicate of 'newfd' that can subsequently
be used to check for close() errors; an EBADF error
means that 'newfd' was not open. */
tmpfd = dup(newfd);
if (tmpfd == -1 && errno != EBADF) {
/* Handle unexpected dup() error. */
}
/* Atomically duplicate 'oldfd' on 'newfd'. */
if (dup2(oldfd, newfd) == -1) {
/* Handle dup2() error. */
}
/* Now check for close() errors on the file originally
referred to by 'newfd'. */
if (tmpfd != -1) {
if (close(tmpfd) == -1) {
/* Handle errors from close. */
}
}
bestie this is the correct pattern, do not fight it, do not "optimize" it, just copy it ๐
๐ฅ SEE ALSO ๐ฅ
close(2), fcntl(2), open(2), pidfd_getfd(2)
and that's the whole page, no crumbs left ๐ this certified banger of a man page comes to you from Linux man-pages 6.20, dated 2026-02-08 โ go forth and duplicate responsibly, my love ๐โจ
๐ฆ where this page comes from
- arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- ๐ the page's license: Linux-man-pages-copyleft
- Copyright 1992, Drew Eckhardt <drew@cs.colorado.edu>
- Copyright 2005-2014, Michael Kerrisk <mtk.manpages@gmail.com>
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms