In operating system design, file descriptors are resources that users may either read bytes from or write bytes to. These may include actual files.

By convention:

  • 0: standard input (read)
  • 1: standard output (write)
  • 2: standard error (write)

In UNIX systems

On UNIX and Linux systems, there are several syscalls and utilities we can use to interact with file descriptors.

  • ssize_t write(int fd, const void buf[.count], size_t count): writes count bytes to a file descriptor.
  • ssize_t read(int fd, void buf[.count], size_t count): reads count bytes from a file descriptor. Blocking syscall. If the file descriptor cannot be read from, it’ll exit the program. Otherwise, it won’t properly exit.
  • int dup(int oldfd): allocates a new file descriptor that refers to the same file descriptor as oldfd. The new FD number is the lowest available number.
  • int dup2(int oldfd, int newfd): atomically changes the FD newfd to point to oldfd. This closes newfd, and allocates its same FD number to point to newfd. This is done atomically to prevent any race conditions associated with doing this process manually, i.e., calling close(), then dup().