You may have heard the phrase "Everything is a file" in relation to how things work in Linux or Unix.

Normal files

What's meant by this, is that most things in Linux share something in common with files.

For example, regular files are opened with the open(2) system call, but so are directories with the O_DIRECTORY flag, which is how the standard library's opendir(3) works.

Symbolic links aren't generally openable, but every file path is openable with the O_PATH option, which along with directory file descriptors, can be passed to system calls like fchdir(2) or the *at(2) family of system calls.

Devices

Along with all the normal files, there's also special device files in /dev.

There's block devices, like /dev/sda, which represent your physical devices. These let you store persistent data, usually by mounting them as a file system with the mount(2) system call.

Because block devices are files, they can be copied like any normal file to make backups.

There's also a variety of character devices, you are unlikely to need to know all of them, but there's a hand-full of useful ones that everyone should be aware of, like /dev/null, which allows you to discard all writes to a file, everywhere a file is needed, but you don't need the output that would be written there.

There's also /dev/full, which can be used to test how a program handles low space conditions, by always saying that there's no space to write to the file.

/dev/zero produces an inexhaustible supply of zero-bytes, which can be convenient for obliterating the contents of another file.

/dev/random and /dev/urandom are the traditional interfaces to the random number generator, though getrandom(2) is a recent addition, which doesn't require a file to be opened, so it's available when /dev is not reachable, and when you are at your file descriptor limit.

There's also many files in /dev that only work with open(2), close(2) and the device-specific ioctl(2) system call, which is not massively like regular files, but there is still value in sharing the same ownership semantics.

Sockets

There is also the mkfifo(3) system call, which creates a "named pipe", which is an alternative way of creating pipes to the pipe(2) system call, which is often easier to use to have two processes have either end of a pipe.

Speaking of which, the read end of a pipe can be read like any regular file, and the write end can be written like any regular file. This is how pipelines work.

unix(7) sockets also appear on the file system, and unlike pipes, the resultant file descriptor can be both read from and written to.

There's also a variety of other types of sockets that can't be opened from the file system, typically created with the socket(2) or socketpair(2) system calls.

Other files

There's plenty of other file-like objects that are made availably by more esoteric system calls.

There's the memfd_create(2) system call for creating anonymous memory maps, which can later be sealed and passed to another process, as a way of sharing data structures between processes.

Sealing the file descriptor is required so that the data can safely be passed around, since if the receiver can't trust that it won't be modified by the sender, then it can't safely use it, since you could change it while it is being used.

Most system calls that handle resources will reference them with a file descriptor, so an exhaustive list would be both boring and very long, but if you're interested in learning about others, just browse man7.org.