Duplicating (dup() And Friends)#
#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
int dup3(int oldfd, int newfd, int flags);
What’s A File Descriptor Again?#
A reference to in-kernel “open file description”
Open file description contains all runtime information
Duplicating: Creating Another Reference#
Sometimes a second reference - a file descriptor - to an open file description is needed
Example: Shell output redirection
Operations on either file descriptor share the same underlying structure
⟶ manipulate a shared offset
Here dup() is used to create a new reference to an existing open
file. dup() implicitly chooses the next free file descriptor
number - 42 in this case. (See How Descriptor Numbers Are Chosen)
Demo: dup()#
Create second reference using
dup()Append bytes to file via original file descriptor
Inquire offset via duplicated file descriptor (see File Position/Offset)
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <print>
int main()
{
int origfd = open("/tmp/somefile", O_WRONLY|O_CREAT|O_EXCL, 0666);
if (origfd == -1) {
perror("open");
return 1;
}
int dupfd = dup(origfd);
if (dupfd == -1) {
perror("dup");
return 1;
}
char content[] = "0123456789";
ssize_t nwritten =
write(origfd, content, sizeof(content)); // <-- write via origfd
if (nwritten == -1) {
perror("write");
return -1;
}
off64_t pos = lseek64(dupfd, 0, SEEK_CUR); // <-- offset via dupfd
if (pos == -1) {
perror("lseek");
return 1;
}
std::println("offset: {}", pos);
return 0; // <-- fds automatically closed at exit
}
$ touch /tmp/somefile
$ ./sysprog-dup-dup
offset: 11
Standard Output Redirection With dup()#
println()(and many others) write tostdout(descriptor number 1)Want them to write to
/tmp/somefileinstead
Solution:
Close
stdoutUse
dup()to select 1 as new file descriptor (see here for why)
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <print>
int main()
{
int origfd = open("/tmp/somefile", O_WRONLY|O_CREAT|O_EXCL, 0666);
if (origfd == -1) {
perror("open");
return 1;
}
close(1); // <-- or use STDOUT_FILENO for clarity
int dupfd = dup(origfd); // <-- dupfd == STDOUT_FILENO
if (dupfd == -1) {
perror("dup");
return 1;
}
std::println("hah, this won't be seen on the terminal");
return 0;
}
$ ./sysprog-dup-stdout-redir
$ cat /tmp/somefile
hah, this won't be seen on the terminal
Standardout Redirection With dup2()#
dup()chooses first free file descriptorFragile trickery needed when I insist in
STDOUT_FILENO⟶
dup2()lets me specify target numberIf target file descriptor is already occupied,
dup2()releases (close()) it first
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <print>
int main()
{
int origfd = open("/tmp/somefile", O_WRONLY|O_CREAT|O_EXCL, 0666);
if (origfd == -1) {
perror("open");
return 1;
}
int dupfd = dup2(origfd, 1); // <-- close 1 (terminal), reallocate/return 1
if (dupfd == -1) {
perror("dup2");
return 1;
}
std::println("hah, this won't be seen on the terminal");
return 0;
}
$ ./sysprog-dup-stdout-redir-dup2
$ cat /tmp/somefile
hah, this won't be seen on the terminal
Shell IO Redirection And Pipes#
Together with file descriptor inheritance,
dup()is at the heart of the shell’s redirection features⟶ Very cool OS concepts!