The UNIX Time-Sharing System
Lecture Notes for CS 190
Spring 2016
John Ousterhout
- Classic 1974 paper on UNIX by Ritchie and Thompson.
- Perhaps it seems like no big deal?
- We take all of this stuff for granted today.
- Must consider the prevailing state-of-the-art when Unix appeared.
- Historical context for Unix:
- Late 1960's
- OS/360: most ambitious commercial OS of its time
- Announced in 1964
- Designed to run on a family of computers
- Ambitious feature set
- Large project
- Very late, very buggy
- Multics: visionary OS: combined many untested technical ideas
- Started in 1964
- Collaboration between MIT, GE, Bell Labs
- Timesharing
- Single level store
- Flexible and powerful sharing
- Dynamic linking
- Security (protection rings)
- Hierarchical file system
- First OS written in a high-level language
- Also very late, very buggy, ultimately not very successful
- Ritchie and Thompson both worked on Multics
- Unix was in many ways a reaction to Multics
- Key overall ideas:
- Decompose system into a collection of components
- Each component individually simple and obvious
- Components plug together in obvious ways that are
very powerful.
- Eliminate distinctions: power comes from making everything
look the same; find the least common denominator
- Makes everything interchangeable
- File data model:
- Before Unix: record-oriented, multiple formats
- Unix: uninterpreted collection of bytes
- Before Unix: lengths often pre-declared
- Unix: variable-length, can grow dynamically
- File system APIs:
- Before Unix:
- Buffering at application level
- No buffer cache in kernel
- DMA between device and user space
- Complex control blocks
- Separate APIs for sequential and random access
- Asynchronous I/O interface (start I/O, etc.)
- Unix:
- Buffer cache in kernel, delayed writes
- Simple read-write kernel calls:
- Same APIs for sequential and random-access (implicit
access position maintained by kernel)
- Kernel copies to/from user buffers
- File names and directories
- Before Unix:
- Often just one directory per user
- Directories represented differently on disk than files
- Unix:
- Hierarchical names
- Directories stored just like other files
- Working directory
- Devices:
- Before UNIX:
- Different kernel calls to open each kind of device
- Different control block structures for every device
- Unix: device-independent I/O:
- Open just like files
- Same kernel calls for reading and writing
- ioctl for device-specific functions
- Mountable file systems:
- Combine multiple trees into one tree
- Extremely simple mechanism
- Access control:
- Simple 6-bit scheme (subsequently extended to 9 bits)
- Too restrictive?
- Compare to complex ACLs with inheritance, etc.
- Set-user-id
- (vs. implement all system functionality in the kernel)
- Processes:
- Before Unix: one process per user
- Processes extremeley heavyweight
- Unix: multiple processes per user
- Passing arguments to processes:
- Before Unix:
- Every command interprets the entire command line
- Limits shell capabilities (different syntax for each command)
- Unix: standard mechanism for passing textual arguments to
programs
- Inheriting open files:
- Before Unix: none
- Each command opens its own files; pipelines impossible
- Unix:
- Several open files inherited from parent
- Standard conventions: always read from descriptor 0,
write to descriptor 1
- Enables pipelines
- Fork-exec-wait distinction:
- Fork-exec distinction allows parent to tailor
environment for child (I/O redirection)
- Exec-wait distinction allows parent to spawn
multiple children (pipelines)
- Controversial because of cost of copying address space
- Shell programs:
- Before Unix:
- Command interpreter built into kernel
- Fixed built-in command set
- Commands are different from programs (e.g. "run foo", not "foo")
- Unix:
- Shell is stand-alone program
- Different users can have different shells
- Allows new shells to develop
- Commands are just names of files
- Simple path mechanism
- Personal commands; run my programs just like
system programs
- Processes as building blocks
- One process for each command
- Shell performs computations on arguments (wildcard
expansion), independent of command
- I/O redirection, pipelines
- Lots of small filters can be combined
into powerful pipelines
- Allowed another entire level of (de)composition
- Would have been impossible without several other
ideas:
- Multiple processes
- Fork-exec-wait, file descriptor inheritance
- Device-independent I/O
- Shell as programming language: can automate program invocation!
- Shell scripts:
- Use redirection on the shell itself
- Another entirely new level of programming
- Background processing
- Unix changed the boundary between the kernel and
user processes.
- Things Unix moved into the kernel:
- Buffer cache
- Control blocks
- Device differences
- Things Unix moved out of the kernel:
- Command-line interpreter
- Setuid programs
- Knowledge of file contents
- Some of the Unix features had been discussed for earlier
systems but were thought to be too complex or expensive.
- Unix did all of this in about 40KBytes of object code
- One tenth the size of Multics
- One final example: what happens when a file is deleted
while open?
- Windows: don't allow deletion
- Or, even worse, the file can be deleted, but name
is still locked down
- Unix: name is deleted and can be reused; file contents
persist as long as file is open.