DUP(2)

✨ dup(2) β€” the system calls manual, bestie edition ✨

okay so today we're learning about dup and dup2 from section 2 of the manual, and honestly? they're giving copy-paste but for file descriptors πŸ’… let's get into it


🌸 NAME 🌸

dup, dup2 β€” duplicate an existing file descriptor

like, it makes a copy of a file descriptor. that's it. that's the whole vibe. πŸ¦‹


πŸ“š LIBRARY πŸ“š

Standard C Library (libc, -lc)


✨ SYNOPSIS ✨

#include <unistd.h>

int
dup(int oldd);

int
dup2(int oldd, int newd);

two little functions, two little signatures, both iconically returning int πŸ‘‘


πŸ’– DESCRIPTION πŸ’–

okay so first up, the dup() system call duplicates an existing object descriptor and hands the value right back to the calling process, which we write as newd = dup(oldd). the oldd argument is a small non-negative integer index in the per-process descriptor table β€” think of it like a seat number in a very exclusive club πŸŽ€ and the new descriptor you get back? it's the lowest numbered descriptor currently not in use by the process. like the bouncer seats you at the first empty chair, no cap.

now here's the tea β˜• the object referenced by the descriptor does not distinguish between oldd and newd in any way. so if newd and oldd are duplicate references to an open file, then read(2), write(2) and lseek(2) calls all move a single pointer into the file β€” one pointer, two names, they're sharing fr fr. and append mode, non-blocking I/O and asynchronous I/O options are shared between the references too. it's giving shared custody, honestly.

so if you actually want a separate pointer into the file, you have to get a different object reference to the file by issuing an additional open(2) system call. no shortcuts, bestie 😭

also the close-on-exec flag on the new file descriptor is unset. noted. πŸ“

now for dup2() β€” here you get to specify the value of the new descriptor, newd. main character energy, you're choosing. but here's the important part:

  • if this descriptor is already in use and oldd β‰  newd, the descriptor is first deallocated as if the close(2) system call had been used.
  • if oldd is not a valid descriptor, then newd is not closed. πŸ™…β™€οΈ
  • if oldd == newd and oldd is a valid descriptor, then dup2() is successful, and does nothing. iconic behaviour, honestly, it just vibes.

🌈 RETURN VALUES 🌈

these calls return the new file descriptor if successful πŸ’– otherwise the value -1 is returned and the external variable errno is set to indicate the cause of the error. so like, check your errno, bestie, it's literally telling you what went wrong 🫢


πŸ’€ ERRORS πŸ’€

the dup() system call fails if:

  • [EBADF] β€” the oldd argument is not a valid active descriptor
  • [EMFILE] β€” Too many descriptors are active.

the dup2() system call fails if:

  • [EBADF] β€” the oldd argument is not a valid active descriptor or the newd argument is negative or exceeds the maximum allowable descriptor number

πŸ”₯ SEE ALSO πŸ”₯

accept(2), close(2), fcntl(2), getdtablesize(2), open(2), pipe(2), socket(2), socketpair(2), dup3(3)

go read those too, they're all in the group chat πŸ’œ


πŸ“ STANDARDS πŸ“

the dup() and dup2() system calls are expected to conform to ISO/IEC 9945-1:1990 (β€œPOSIX.1”). slay, standardised πŸŽ€


πŸ•°οΈ HISTORY πŸ•°οΈ

the dup() function appeared in Version 3 AT&T UNIX. the dup2() function appeared in Version 7 AT&T UNIX. ancient lore, but we respect the ancestors πŸ‘‘


and that's the whole page, bestie!! this one came from Debian, dated December 1, 2017, and it's DUP(2) all the way down. go duplicate responsibly πŸ’…βœ¨πŸ¦‹
πŸ“¦ where this page comes from