Skip to content

Unix, Linux, distributions

Linux, macOS and Android are three different systems that behave suspiciously alike. The same fork and exec, the same rwx permissions, the same pipelines. Yet they share almost no code.

There are practical reasons to figure out where this similarity comes from. It explains why a program builds on one system and fails to build on another, why Apple took BSD code rather than Linux, and why ls on your machine may turn out to be three different programs.

Multics, a joint project of MIT, Bell Labs and General Electric, was meant to be a time-sharing system for hundreds of users. It kept growing, kept slipping, and eventually Bell Labs pulled out of the project.

Ken Thompson and Dennis Ritchie started writing something radically simpler. The name Unics was a joke at Multics’s expense: that one did things the complicated way and for everyone, this one did them simply and for a few.

In 1973 came the decision that defined everything: the kernel was rewritten in C. Until then kernels were written in assembly, and each was tied to a single machine model. A kernel in a high-level language could be ported to another architecture, and it was portability, not elegance of design, that made Unix the foundation of everything that came after.

The Unix philosophy grew out of the limits of the hardware of its day but outlived them: small programs that do one thing well, text as the universal interface between them, composition instead of monoliths, everything represented as a file.

From the seventies on, Unix split into two branches. System V was AT&T’s commercial line, while BSD was developed at Berkeley, and that branch added the TCP/IP network stack, sockets and virtual memory. Much of what is in use today came from there.

In 1983 Richard Stallman started GNU, a project to create a completely free Unix-compatible set of programs. By the early nineties GNU had a compiler, a shell, a C library and hundreds of utilities, but no working kernel.

The kernel was written in 1991 by Linus Torvalds. It did not contain a single line of Unix code: it was an independent implementation of the same interfaces.

Licensing decided the fate of systems no less than code quality did.

The GPL, under which Linux and GNU were released, requires you to publish your changes if you distribute them. Because of this, contributions flowed into Linux from companies that could not keep their patches closed, and the kernel became a shared asset of competitors.

The BSD license, by contrast, allows taking the code into closed products with no obligations. That is why the BSD network stack ended up everywhere, and why Apple built macOS on BSD and Mach: taking Linux would have meant opening up the system.

For the same reason Android takes the Linux kernel, because the GPL applies to the kernel and changes to it are public, but does not take GNU: instead of glibc it has Bionic under a BSD license, and the rest of user space is not GPL either.

The kernel on its own does nothing; it has neither a shell nor utilities. A distribution is the kernel plus everything else.

Component Examples Role
Kernel Linux processes, memory, file systems, drivers
C library glibc, musl, Bionic wrappers around system calls
Init system systemd, OpenRC, runit PID 1, starting services
Core utilities GNU coreutils, BusyBox ls, cp, cat
Shell bash, zsh, ash command-line interface
Package manager apt, dnf, pacman, apk installation and updates

Replace any row of this table and you get a different system. Alpine Linux uses musl instead of glibc and BusyBox instead of coreutils, which makes the image dozens of times smaller, but programs built against glibc simply do not start there. This is the most common cause of “it fails in Docker but works locally”.

Debian and derivatives (Ubuntu, Mint) use apt and the .deb format, and bet on stability and a huge repository.

Red Hat and derivatives (RHEL, Fedora, Rocky) use dnf and the .rpm format; Fedora serves as the testing ground for what later lands in RHEL.

Arch ships packages right after release, and the user assembles the system themselves from a minimal base.

Alpine is built on musl and BusyBox for minimal size, and that made it the standard for containers.

NixOS describes the system configuration declaratively, so an update can be rolled back as a whole.

How these branches diverged over time and what grew out of what is shown on the map in the appendix “History of operating systems”.

macOS is a certified UNIX. The XNU kernel combines the Mach microkernel and a BSD subsystem, and user space comes from BSD; there is no GNU there.

Android uses the Linux kernel with significant changes: wakelocks, Binder for interprocess communication, a separate permission model. Instead of glibc it has Bionic, and instead of the usual set of utilities, the ART runtime.

WSL2 runs a real Linux kernel in a lightweight virtual machine on top of Hyper-V. The first version of WSL tried to translate Linux system calls into Windows calls and ran into what was discussed in module 3: reproducing someone else’s ABI completely is incredibly hard.

Terminal window
uname -r; cat /etc/os-release | head -3

The kernel version and the distribution version are unrelated. One kernel runs under dozens of distributions.

Terminal window
ldd --version | head -1; ldd /bin/ls

Which C library is in use and what ls is linked against. On Alpine these commands give a completely different result.

Terminal window
readlink -f /bin/sh

Where /bin/sh actually points. On Debian it is dash, not bash, so a #!/bin/sh script that uses bash extensions will break.

Terminal window
ls /usr/bin | wc -l; dpkg -S /bin/ls 2>/dev/null || rpm -qf /bin/ls 2>/dev/null

How many programs are on the system and which package a particular one came from. The kernel has nothing to do with any of them.

Terminal window
ps -p 1 -o comm=

What runs as PID 1: systemd, init or something else (module 5).

Terminal window
getconf _POSIX_VERSION

The POSIX version the system conforms to.

“Linux is an operating system.” Linux is a kernel. The operating system is the distribution: the kernel plus a C library, init, utilities and thousands of packages.

“Linux descends from Unix.” They share no code. Linux is an independent implementation of the same interfaces.

“POSIX means a program will run anywhere.” It means that a program that stays within POSIX will run. Most real-world code uses extensions of a particular system.

“Android is a Linux distribution.” It does have the Linux kernel, but not GNU: a different C library, a different runtime, a different permission model.

“macOS is built on Linux.” It is built on BSD and Mach, and the similar commands come from a common ancestor.

“It fails in the container because Docker is broken.” Most likely the image is built on Alpine with musl, and the program was built against glibc.

Check yourself

1. Which 1973 decision made Unix the foundation for everything that came after?
2. Why does Linux behave like Unix without containing a single line of its code?
3. Why did Apple build macOS on BSD rather than Linux?
4. A program works locally but crashes in an Alpine-based container. What is the most likely cause?
5. A script with #!/bin/sh uses bash syntax and fails on Debian. Why?
6. What is a Linux distribution?
  • The UNIX Time-Sharing System — Ritchie and Thompson, 1974
  • Raymond, The Art of Unix Programming — on the Unix philosophy
  • Isaacson, The Innovators — the history of the field as a history of people
  • Nemeth et al., UNIX and Linux System Administration Handbook
  • man 7 standards, getconf _POSIX_VERSION