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 jjj How Descriptor Numbers Are Chosen)
dup()
: Program#
Create second reference using
dup()
Append bytes to file via original file descriptor
Inquire offset via dupped file descriptor
#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
Standardout Redirection With dup()
#
println()
(and many others) write tostdout
(descriptor number 1)Want them to write to
/tmp/somefile
instead
Solution:
Close
stdout
Use
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!