skip to content

Unix & BSD

Traditional Unix and the BSD-derived systems that still run firewalls, appliances, and part of macOS. This area is about design heritage as much as daily use, and interviewers do ask where Linux inherited its ideas from.

on this pageshow

explore

  • Unix5 questions
  • FreeBSD6 questions

questions

11

The Unix philosophy is usually summarised as "write programs that do one thing well, and write programs to work together". What concrete design conventions make that composition possible, and what does the approach cost you?

level: juniorimportance: must knowfreq 62%

answer

  1. one job per program
  2. streams in, streams out
  3. the shell wires, the kernel buffers
  4. text carries no schema
  5. last command sets the status

basics

~20 s

Unix builds small single-purpose programs that read bytes from standard input and write bytes to standard output, so a shell can chain them with pipes. Composition becomes nearly free; structured data and precise error reporting do not.

solid answer

~50 s

Three conventions carry the whole idea. First, every process starts with three inherited file descriptors — standard input, standard output and standard error — so a program reads and writes without knowing whether the other end is a terminal, a file or another program. Second, the kernel provides pipes, and the shell wires one program's output descriptor to the next one's input descriptor, so composition is decided at run time rather than designed into either program. Third, a program reports success or failure as a small integer exit status, which lets the shell branch on it. Diagnostics go to standard error so they never contaminate the data stream. The payoff is that tools written decades apart still interoperate. The cost is that a line of text carries no schema: consumers parse by convention and break on spaces, changed columns or locale differences, and an exit status is a very thin error channel.

code

bash · 1 line
bash
grep ERROR app.log | cut -d' ' -f1 | sort | uniq -c | sort -rn | head -5

go deeper

for a junior

Be able to say what standard input, standard output and standard error are, and show a two- or three-stage pipeline you have actually used. Knowing that exit status zero means success is expected.

for a middle

Explain the mechanics: the shell forks and rewires descriptors before exec, the pipe is a kernel buffer that blocks in both directions, and the stages run concurrently. Name at least one concrete parsing hazard.

for a senior

Show judgment about when to stop composing. Argue for a stable machine-readable output contract, describe how you handle failures that a single exit status cannot express, and point at where per-process overhead makes a pipeline the wrong shape.

for a principal

Own the tradeoff at the platform level: which interfaces in your systems are stable contracts versus convenience formats, how you version them, and what you accept when you choose human-readable text as an integration surface.

## The rule Doug McIlroy's much-quoted summary from Bell Labs is: make each program do one thing well; expect the output of every program to become the input to another, as yet unknown, program; and write programs to handle text streams, because that is a universal interface. It is a statement about **interfaces**, not about program size — a tool is "one thing" when its contract is one thing. ## The three conventions that make it work **Standard descriptors.** A Unix process starts life with file descriptor 0 open for input, 1 for output and 2 for diagnostics, inherited from whatever started it. A program that reads 0 and writes 1 is automatically usable against a keyboard, a file, a device or another process; it never contains code to decide which. That is why redirection needs no cooperation from the program. **Pipes.** The `pipe()` system call returns a pair of descriptors joined by an in-kernel buffer. To build `a | b`, the shell creates the pipe, forks twice, points the left child's descriptor 1 at the write end and the right child's descriptor 0 at the read end, then execs both. Both processes run *concurrently*; the kernel buffer supplies backpressure — the writer blocks when the buffer is full, the reader blocks when it is empty, and the reader observes end-of-file only when every write end has been closed. Nothing is staged through a temporary file. ```sh grep ERROR app.log | cut -d' ' -f1 | sort | uniq -c | sort -rn ``` Five independent programs, none of which knows the others exist, produce a ranked error report. The composition lives in the command line, not in any of the programs. **Exit status.** A process returns an integer to its parent, zero meaning success. That single convention is what makes `&&`, `||` and scripted control flow possible across programs written in different languages. **Separate diagnostic channel.** Because errors go to descriptor 2, a warning printed mid-run does not appear as a data record to the next stage of a pipeline. ## What the model buys Composition is combinatorial rather than planned: *n* small tools yield far more useful combinations than *n* features inside one program, and no tool needs a plugin API to be extended, because the extension point is the stream itself. Each tool is independently testable, independently replaceable, and can be written in any language that can read and write bytes. And because the interface is bytes, the wiring can be decided interactively by a human at a prompt — the reason ad-hoc analysis on a Unix box is so fast. ## What the model costs **No schema.** "Whitespace-separated columns of text" is a convention, not a contract. A filename containing a space, a newline in a field, a locale that formats dates or sorts characters differently, or a tool that adds a column in a new release will all break a downstream parser silently. The NUL-delimited convention (`find -print0` feeding `xargs -0`) exists precisely because the default delimiter is unsafe — and note that neither of those options is in POSIX; they are widely implemented extensions. **Thin error semantics.** One integer plus free-form prose on standard error is a poor substitute for a structured failure value. There is no portable way to say *which* record failed and why. **Failure hiding in pipelines.** A POSIX shell reports a pipeline's exit status as the status of the *last* command, so a failure upstream is discarded unless you use a shell option such as `pipefail` in bash, ksh or zsh. **Boundary cost.** Every stage is a separate process with its own creation cost, its own memory, and no shared type system. For tight loops over huge data, one program doing three transformations beats three programs doing one each. **Poor fit for nested data.** Line-oriented streams model records well and trees badly, which is why modern tools bolt structured output formats back on top of the same descriptor interface. ## Why interviewers ask it They are checking whether you can reason about interface design, not whether you can quote McIlroy. The strong answer names the mechanisms (inherited descriptors, kernel pipes, exit status), states the payoff honestly, and then volunteers the failure modes — because the same tradeoff reappears every time you decide between a stable machine-readable contract and a convenient human-readable one.

  • In that pipeline, do the programs run one after another or at the same time?
    At the same time. The shell forks every stage and connects them with kernel pipes before any of them runs, so data flows through as it is produced. The kernel buffer provides flow control: a fast producer blocks once the buffer fills, and a fast consumer blocks waiting for data. Nothing accumulates the full output of one stage before the next starts.
  • Why is a filename containing a space such a recurring source of breakage in this model?
    Because whitespace is the default field separator, so a name with a space parses as two fields. The tools are not wrong — the stream simply has no way to say where one value ends. The usual mitigation is a delimiter that cannot occur in a filename, which is why NUL-separated output exists as an option on find and xargs, though it is an extension rather than part of POSIX.
  • Does "do one thing well" mean a program must stay small?
    No — it means its contract should be one thing. A compiler is enormous and still obeys the philosophy, because it exposes one job through one predictable interface. The rule is violated by a tool that mixes unrelated responsibilities behind one entry point, not by a tool that is internally large.

saying these in an interview costs you the question

  • Says the shell buffers all output before starting the next stage
  • Claims a pipeline fails as soon as any stage fails
  • Treats "do one thing" as a rule about lines of code
  • Assumes columns in a tool's output are a stable contract
  • Thinks pipes cannot carry binary data

context

open as a page

FreeBSD is described as shipping a "base system" rather than being assembled like a Linux distribution. What does that mean concretely on a running machine, and how does it change the way you patch and upgrade it?

level: middleimportance: must knowfreq 70%

basics

~10 s

FreeBSD's kernel and core userland are built from one source tree and released, patched and upgraded as a single versioned unit, while every third-party program installs separately under /usr/local and is managed by pkg.

open as a page

Unix is often described with the slogan "everything is a file". What does that actually mean for a program at the system-call level, and where does the abstraction leak?

level: middleimportance: must knowfreq 55%

basics

~20 s

Most kernel objects — regular files, devices, pipes and sockets — are reached through a file descriptor and the same read, write and close calls. The abstraction leaks: sockets and terminals need their own calls, and not every object has a filesystem name.

open as a page

What is a FreeBSD jail, and how does its isolation model differ structurally from the way Linux builds containers?

level: seniorimportance: must knowfreq 60%

basics

~20 s

A jail is one kernel object that confines a process tree to a directory root, a hostname, an address set and a restricted root user in a single step. Linux has no equivalent single object; containers there are composed from several independent kernel facilities.

open as a page

On FreeBSD, what is the difference between installing software from the ports tree and installing it with pkg, and when is building from ports actually worth the time?

level: juniorimportance: should knowfreq 72%

basics

~20 s

The ports tree is a collection of build recipes you compile from source with your own options; pkg installs prebuilt binary packages that the project produced from those same recipes using default options. Most installs should use pkg.

open as a page

Explain the difference between FreeBSD's CURRENT, STABLE and RELEASE branches, and what the trailing "-p6" in a version string such as 14.1-RELEASE-p6 tells you.

level: middleimportance: should knowfreq 42%

basics

~20 s

CURRENT is FreeBSD's main development branch, STABLE is the per-major-version branch that keeps kernel and userland interfaces compatible while taking merged changes, and RELEASE is a tagged snapshot cut from it. The -p number is the applied security and errata patch level.

open as a page

FreeBSD ships ZFS in the base system and its installer offers a root-on-ZFS layout. What does that integration let you do around upgrades that a filesystem added on afterwards does not?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Because ZFS is in base and the loader can boot any root dataset, FreeBSD supports boot environments: you clone the running system before an upgrade and, if it goes wrong, activate the old clone and reboot back into the previous OS in seconds.

open as a page

A shell script and a small C program both run correctly on Linux but misbehave on another Unix system such as macOS or AIX. What does POSIX actually guarantee, and what does it deliberately leave unspecified that would explain failures like these?

level: seniorimportance: should knowfreq 44%

basics

~20 s

POSIX guarantees source-level portability: a C system-interface API, the shell command language, and a set of utilities with a required minimum of options and behaviours. It says nothing about vendor extensions, binary compatibility, or kernel-specific interfaces, which is where portable-looking code breaks.

open as a page

The Unix model composes small single-purpose programs over untyped byte streams. When you design the tooling around a platform your team owns, how would you decide between that model and a single integrated program producing structured output, and what does each choice cost?

level: principalimportance: should knowfreq 20%

basics

~20 s

Decide by asking who consumes the output and how stable that contract must be. Composition over text streams wins for ad-hoc human work and independent evolution; an integrated tool with a versioned structured format wins when machines consume it and partial failure must be expressed precisely.

open as a page

What separates an operating system that may legally be called UNIX from one described as "Unix-like", and how did the System V and BSD branches produce that distinction?

level: middleimportance: nice to knowfreq 26%

basics

~20 s

UNIX is a trademark held by The Open Group. A system may use the name only after being certified against the Single UNIX Specification, which costs money and testing. Systems such as Linux and the BSDs implement the same interfaces without certifying, so they are called Unix-like.

open as a page

Your company is building a closed-source network appliance and is choosing an operating-system base. How does FreeBSD's permissive licence change that decision compared with a Linux base, and what does choosing permissive cost you?

level: principalimportance: nice to knowfreq 32%

basics

~20 s

FreeBSD's base ships under a permissive two-clause BSD licence, so you may modify and ship it inside a closed product without publishing your changes. The costs are no reciprocity from others, a smaller driver and vendor ecosystem, and a much smaller hiring pool.

open as a page