POSIX is (among other things) the standard which defines how we all interact with files and file systems in Linux and other Unix-like environments. Directories, files, symlinks, and all the API calls we use to mess with them are defined by the POSIX standard.

It’s easy to equate “POSIX” with “file systems” but the distinction is a little more complex because there are two halves to POSIX: its API and its semantics.

POSIX API and data model

The POSIX API defines common operations like open, close, read, write, and the like. The fact that you cannot read from a file without first opening it is a result of the POSIX API.

The fact that the standard way to retrieve data from a file is by reading and writing bytes from offsets is a reflection of the POSIX data model. POSIX treats files as just streams of bytes. It does not tell you how to interpret those bytes. This is different from the data model of a database, for example, where data is stored as rows in tables, not bytes in files.

POSIX semantics

The POSIX semantics define how the POSIX API should behave. For example, the simple write API call has the following semantics:1

After a write() to a regular file has successfully returned:

  • Any successful read() from each byte position in the file that was modified by that write shall return the data specified by the write() for that position until such byte positions are again modified.
  • Any subsequent successful write() to the same byte position in the file shall overwrite that file data.

As simple as this sounds (you should be able to read anything you just wrote), it’s got far-reaching implications in the modern era: a write call will hang until the file system can guarantee that a read call from anywhere can see that write. In a parallel or distributed file system, this means that a write has to go all the way out to the file system before it can return. Or, if the write is cached locally, an attempt to read the file at that same position from another node must be carefully orchestrated to ensure that the read doesn’t return old data.

This behavior gives rise to all the locking you see in parallel file systems like Lustre.

Footnotes

  1. write