Part 4 · 1 chapters · ~8 min

Files and Descriptors

open, read, write and close, descriptor tables and open file descriptions, offsets and O_APPEND, dup and dup2 for redirection, pipes, buffering and fsync for durability, file permissions, descriptor limits and leaks, and memory-mapped files.

5

Reading, writing, durability

code
int fd = open("ledger.log", O_WRONLY | O_CREAT | O_APPEND, 0644);   // O_APPEND: every write goes to the end atomically
if (fd < 0) { perror("open"); exit(1); }
write_all(fd, line, len);
if (fsync(fd) < 0) perror("fsync");      // write() reaching the page cache is not durable; fsync is (Storage course)
close(fd);                               // leaking descriptors ends in EMFILE: "too many open files"

// redirection, as a shell does it: cmd > out.txt 2>&1
int out = open("out.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
dup2(out, 1); dup2(out, 2); close(out);

// memory-mapped file: the file appears as memory; the page cache backs it
void *p = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);

Durability on macOS: fsync there does not force the drive to flush its own cache; databases use fcntl(fd, F_FULLFSYNC) on macOS for true durability. On Linux, fsync of a new file also needs an fsync of its directory to make the name durable.

FILE DESCRIPTORS
small integers that name kernel objects
process fd table0 stdin, 1 stdout, 2 stderr, 3 ...open file descriptionoffset, flagsinode / socket / pipethe actual objectafter forkchild shares descriptions
swipe the figure sideways, or tap expand for full screen
1/4
a number
open, socket and pipe return the lowest free integer in the process's descriptor table. 0, 1 and 2 are standard input, output and error.
an index into a table0, 1, 2 are standard