COMP 3150Operating Systems

01 Introduction

Everything you will do in the laboratories and programming assignments of this course runs on Linux. It is the operating system that has shaped modern computing more than any other: it runs the majority of servers on the internet, every Android phone, every one of the top 500 supercomputers, and — through derivatives and inspiration — it influences macOS, Windows Subsystem for Linux, and every container in the cloud.

Linux is worth studying for two reasons. First, it is a real, production operating system, not a teaching abstraction. Every mechanism the course discusses — process scheduling, virtual memory, file systems, device drivers, networking — is implemented in a codebase you can read, build, and modify. Second, its design is exemplary: a monolithic kernel with loadable modules, strict separation between kernel and user space, and a portability discipline that has allowed it to run on virtually every hardware architecture ever designed.

This lesson introduces Linux from the outside in: what it is, how it is organised, what the kernel does, and how the open-source development model has produced and maintained it. The following four lessons in this unit will give you the skills to work with it directly.

02 Learning objectives

By the end of this lesson you will be able to

  • Describe Linux architecture.
  • Explain kernel responsibilities.
  • Identify Linux subsystems.
  • Explain open-source development.
  • Compare Linux and UNIX.

This lesson contributes to U2.1 and to course learning outcomes CLO1 and CLO6.

03 Required reading

  • Love, R. — Linux Kernel Development, 3rd ed., Chapter 1: Introduction to the Linux Kernel. A concise, authoritative overview of kernel architecture.
  • Stevens & Rago — Advanced Programming in the UNIX Environment, 3rd ed., Chapter 1: UNIX System Overview. The classical UNIX worldview, from which Linux inherits.
  • Linux kernel documentation — Documentation/process/, especially development-process.rst and submitting-patches.rst.
  • Linux man pages: man 1 intro, man 2 intro, man 7 intro. These give a quick tour of user commands, system calls, and conventions.

Reading strategy

Read Love Chapter 1 first — it is short and gives you the big-picture architecture. Then skim the appropriate sections of Stevens & Rago for the UNIX conventions that Linux inherits. Both books are secondary references throughout the course, so having a feel for their structure now will pay off later.

04 Core content

4.1 What Linux is — and is not

Colloquially, "Linux" refers to a whole family of operating systems: Ubuntu, Fedora, Debian, Red Hat Enterprise Linux, Arch, Alpine, and hundreds of others. Strictly speaking, that family is more precisely called GNU/Linux, because it is the combination of two historically separate projects:

  • Linux — the kernel, started by Linus Torvalds in 1991, which manages hardware and provides the fundamental abstractions of process, file, socket, and memory.
  • GNU — a project started by Richard Stallman in 1983 to produce a free replacement for the UNIX operating system. By 1991 the GNU project had produced almost every user-space component of a complete OS — the C library, the shell, the compiler, the core utilities — but its own kernel (Hurd) was not finished.

The combination of the GNU user space with the Linux kernel produced a complete, free operating system. This is why you will see both terms used, and why purists insist on "GNU/Linux" — but "Linux" alone is the common usage and the one we will use throughout the course.

4.2 Linux architecture

Linux follows the classical UNIX architecture, with a strict separation between two worlds: user space, where applications run, and kernel space, where the operating system itself runs.

The Linux system architecture A layered diagram of Linux. At the top, user applications and system libraries. Below them, the system-call interface. Below that, the kernel with its subsystems: process scheduler, memory manager, virtual file system, network stack, and device drivers. At the bottom, the hardware. The boundary between user mode and kernel mode is shown explicitly. USER SPACE applications editors, servers, browsers system libraries glibc, libstdc++ shell & utilities bash, coreutils init & daemons systemd, sshd SYSTEM CALL INTERFACE · mode boundary KERNEL SPACE Process scheduler CFS · real-time · preemption Memory manager virtual memory · paging VFS ext4 · XFS · Btrfs · proc Network stack TCP/IP · netfilter · netlink Device drivers loadable kernel modules Security LSM · capabilities · seccomp KERNEL SERVICES • system-call handlers • interrupt handling • synchronization primitives • timer management • IPC (pipes, sockets, shared memory) • module loading PHYSICAL HARDWARE CPU memory storage NIC peripherals
Figure 2.1 — Linux architecture. The system-call interface is the boundary between user space and kernel space; every service the kernel provides is requested through it.

4.3 Kernel responsibilities

The kernel is the software that runs in privileged mode and manages the machine's resources. Concretely, it is responsible for:

Process management
Creating, scheduling, suspending, and terminating processes and threads; providing inter-process communication and synchronisation mechanisms.
Memory management
Giving each process an isolated virtual address space; translating virtual addresses to physical frames via the MMU; managing page allocation, page replacement, and swapping.
File-system management
Providing a uniform abstraction over diverse storage devices and file-system formats; managing directories, permissions, file descriptors, and caches.
Device management
Interfacing with hardware through drivers; handling interrupts and DMA; presenting a uniform device interface to user space.
Networking
Implementing protocols (TCP/IP, UDP, UNIX sockets); routing packets; enforcing firewall rules; providing the socket API.
Security and protection
Enforcing user and group permissions, capabilities, mandatory access control policies, and the isolation between processes.

4.4 Linux kernel subsystems

The Linux kernel is organised into several major subsystems, each with its own responsibilities and internal interfaces. The subsystems you will encounter most in this course are:

Process scheduler

Decides which runnable task uses the CPU next. Linux uses the Completely Fair Scheduler (CFS) for ordinary tasks and separate real-time policies for tasks that need strict latency. Covered in Unit 5.

Memory manager

Handles the mapping between virtual and physical memory, implements demand paging, and runs the page-replacement and reclaim algorithms. Covered in Unit 9.

Virtual File System (VFS)

Provides a single unified interface to dozens of different file systems, so that read() works the same way on ext4, XFS, Btrfs, and network file systems. Covered in Unit 10.

Network stack

Implements the protocol suite, socket layer, and firewall. Runs largely in kernel space for performance reasons, though some packet processing is offloaded to user space for specialised workloads.

Device drivers

Kernel modules that know how to talk to specific hardware. They constitute the majority of kernel source code and are discussed in Unit 11.

Security subsystem

Enforces permissions and capabilities, supports pluggable MAC frameworks (SELinux, AppArmor), and provides syscall filtering. Covered in Unit 12.

4.5 Monolithic with modules

Linux is a monolithic kernel: all of these subsystems run in a single address space in kernel mode, and they call one another directly as ordinary functions. This contrasts with microkernel designs, where subsystems run as separate user-space services.

The advantage of monolithic design is performance: an operation that crosses subsystem boundaries is a function call, not an IPC round trip. The disadvantage is fragility: a bug in any subsystem can corrupt kernel memory and crash the system.

Linux mitigates the fragility with loadable kernel modules (LKMs). A module is kernel code that can be inserted into and removed from a running kernel on demand — usually a device driver or a file-system implementation. Modules preserve the performance advantage of monolithic design (they run in kernel mode and share the kernel's address space) while allowing the kernel to remain small and modular.

# List currently loaded modules
$ lsmod | head -5
Module                  Size  Used by
nls_iso8859_1          16384  1
nls_cp437              20480  1
vfat                   24576  1

# Show metadata about a specific module
$ modinfo e1000e | head -6
filename:       /lib/modules/.../kernel/drivers/net/ethernet/intel/e1000e/e1000e.ko
version:        3.2.6-k
license:        GPL
description:    Intel(R) PRO/1000 PCI-Express Network Driver
author:         Intel Corporation
srcversion:     ...

Modules are why Linux adapts to hardware

The same kernel binary can support thousands of different devices, because only the modules relevant to the detected hardware are loaded at boot. This is one of the reasons Linux has been able to run on everything from phones to mainframes.

4.6 Kernel and user space

The boundary between kernel space and user space is enforced by the CPU's privilege modes. On x86-64, kernel code runs in ring 0 (the most privileged) and user code in ring 3. When a user-space program wants a service that only the kernel can provide — opening a file, creating a process, sending a packet — it issues a system call: a controlled trap into the kernel.

System calls are the only legitimate way for user code to reach kernel services. There are roughly 350 of them on modern Linux, grouped by category:

  • Process control. fork, execve, exit, wait4, clone.
  • File operations. openat, read, write, close, stat, mmap.
  • Memory management. brk, mmap, mprotect, madvise.
  • Communication. pipe, socket, sendto, recvfrom.
  • Information. getpid, uname, time, getrlimit.
  • Protection. chmod, chown, setuid, capset.

This interface is stable across kernel versions — an important property, because applications compiled years ago continue to work on today's kernels. The Linux kernel's developers are famously strict about never breaking the system-call interface.

4.7 The open-source development model

Linux is not developed by a single company or a single team. It is the largest collaborative software project in history:

  • Scale. Roughly 30 million lines of code, with contributions from over 20,000 developers across more than 1,500 companies.
  • Cadence. A new major release approximately every 9–10 weeks. Each release incorporates thousands of patches from hundreds of contributors.
  • Licensing. Released under the GNU General Public License version 2 (GPLv2), which requires that derivative works also be released under the same license. This is why Linux cannot be made proprietary, and why companies that modify it must publish their changes.
  • Governance. A hierarchical, trust-based structure. Contributors submit patches to subsystem maintainers, who review them and forward accepted work to Linus Torvalds, who makes the final decision for each release.

This model has practical consequences for anyone working with Linux:

  • You can read the source. Every subsystem this course discusses is available at kernel.org and mirrored on GitHub. When the textbooks describe a mechanism, you can see how it actually works.
  • You can build your own kernel. Compiling a custom kernel — with only the drivers and features you need — is a standard operation and part of the Lab 1 workflow.
  • You can contribute. The process is documented in Documentation/process/ and is open to anyone, regardless of employment. First-time contributors are welcomed and mentored.
  • Distributions matter. Because the kernel is just one piece, a Linux distribution bundles the kernel with a user space, package manager, init system, and policy choices — which is why Ubuntu, Fedora, and Debian feel different even though they share a kernel.

4.8 Linux and UNIX

Linux is not UNIX. UNIX is a trademarked operating system originally developed at AT&T Bell Labs in the 1970s; today the trademark is held by The Open Group, and systems that meet its specification can be certified as "UNIX". Linux has never been certified, and the name is deliberately different: "Linux Is Not UniX".

What Linux shares with UNIX is far more important than the branding:

Linux and UNIX: shared and distinct characteristics
Property Traditional UNIX Linux
Process model fork/exec, signals, pipes Same, plus clone and richer threading
File model Inodes, hierarchical directories, everything-is-a-file Same, plus multiple filesystem formats and VFS
Permission model Owner/group/other with rwx bits Same, extended with POSIX ACLs and capabilities
Shell and utilities POSIX-standardised Same utilities, plus GNU extensions
Licensing Proprietary (various vendors) Free software (GPLv2)
Kernel architecture Varies (monolithic, hybrid, microkernel) Monolithic with loadable modules
Hardware support Vendor-specific Virtually every architecture ever built
Development Vendor-controlled Global open-source community

POSIX is the unifying standard

The reason a program written on macOS often runs on Linux — and vice versa — is the POSIX standard: a formal specification of the UNIX system-call interface, shell, utilities, and threading model. Linux implements POSIX; so do macOS, the BSDs, and Solaris. For the purposes of this course, "POSIX" is more useful than "UNIX" as a description of what Linux is compatible with.

4.9 Practical consequences for this course

Two consequences follow directly from what we have covered, and they will shape everything you do in the laboratories:

  1. You will work in user space, using system calls. Every program you write for this course — the process management labs, the threading labs, the file-system labs — will be a user-space program that requests services through the system-call interface. This is the natural place to observe the OS's behaviour.
  2. You can inspect the kernel when you need to. The /proc and /sys pseudo-filesystems, the strace and perf tools, and the source code itself are all available. When the textbook says "the scheduler picks the process with the smallest virtual runtime", you can open kernel/sched/fair.c and read the implementation.

The next four lessons in this unit will give you the tools to work with Linux effectively: the shell, users and groups, permissions, and the development environment. Every subsequent laboratory in the course assumes the skills you will build there.

05 Worked example

To make the architecture concrete, trace what happens when a simple user-space program asks the kernel for a service.

#include <unistd.h>
#include <stdio.h>

int main(void) {
    /* Both of these lines cross the system-call interface */
    write(STDOUT_FILENO, "hello\n", 6);
    pid_t me = getpid();
    printf("my pid is %d\n", me);
    return 0;
}

Following the system calls

  1. Compile and run. The shell (a user-space program) asks the kernel to create a new process running the compiled binary — a sequence of fork and execve.
  2. The new process executes main. When it reaches write, the C library (glibc) prepares the arguments and executes a syscall instruction.
  3. The CPU traps into kernel mode. Control transfers to a fixed entry point in the kernel, where the syscall dispatcher reads the syscall number from a register and calls the corresponding handler — sys_write.
  4. The kernel does the work. sys_write validates the file descriptor, checks that the caller has permission to write, and — because descriptor 1 is a terminal — hands the bytes to the terminal driver. The kernel then returns the number of bytes written.
  5. Back in user space. The library function write returns to the program; the program continues.
  6. The getpid call. This is even simpler: the kernel reads the PID from the current process structure and returns it. No hardware access, no I/O, just a quick lookup and a return.
  7. Exit. Returning from main causes the C runtime to call exit_group, which terminates the process. The kernel tears down its address space, closes its file descriptors, and notifies the parent (the shell), which collects the exit status with wait4.

If you want to see these calls yourself, run the program under strace:

$ strace -e trace=write,getpid,exit_group ./hello
write(1, "hello\n", 6)                 = 6
getpid()                              = 12345
write(1, "my pid is 12345\n", 17)      = 17
exit_group(0)                         = ?
+++ exited with 0 +++

Every line in this output is a system call — a controlled transition from user space to kernel space and back. Every program you write for this course is built on exactly this mechanism.

06 Interactive activity

Activity · explore Linux architecture on your VM

Open a terminal and run the following. Record what you observe for each command; the goal is to see the architecture we have just discussed in a live system.

  1. Kernel version and architecture. Run uname -a and cat /proc/version. What kernel version is running? Which architecture?
  2. Loaded modules. Run lsmod | head -15. How many modules are loaded? Pick one and read its metadata with modinfo <name>.
  3. Kernel command line. Run cat /proc/cmdline. These are the parameters the bootloader passed to the kernel. What do they tell you about how the kernel was started?
  4. System-call interface. Run strace -c ls /tmp. The -c flag summarises system calls by count and duration. Which calls dominated? How many were there in total?
  5. Kernel interfaces. List the top-level entries in /proc and /sys: ls /proc | head -20 and ls /sys | head -20. These two pseudo-filesystems are the primary windows into the kernel from user space.
  6. Kernel log. Run dmesg | head -20 (may need sudo). The kernel's own boot messages are here; you can see the hardware it detected, the modules it loaded, and the subsystems it initialised.
  7. Distribution identification. Run cat /etc/os-release. Which Linux distribution is your VM based on? Note that the kernel version (from step 1) and the distribution are independent — the same kernel could be paired with a different distribution.

Reflect: which of the four layers in Figure 2.1 (user applications, libraries, system-call interface, kernel) did each of these commands exercise?

07 Knowledge check

Check your understanding

Question 1

Why is Linux described as a "monolithic kernel with loadable modules" rather than as a microkernel?

Show answer
Because all of its subsystems — scheduler, memory manager, file systems, network stack, device drivers — run in a single address space in kernel mode and call each other as ordinary functions. This is the classic monolithic design, and it is chosen for performance: crossing between subsystems is a function call rather than an IPC round trip. In a microkernel, most of those subsystems would run as separate user-space services, communicating through messages sent via a minimal kernel. Linux mitigates the fragility of monolithic design with loadable kernel modules, which can be inserted and removed at runtime — preserving the performance advantage while keeping the running kernel small.

Question 2

What is the difference between "Linux" and "GNU/Linux", and why does the distinction matter?

Show answer
Linux is strictly the kernel — the software, started by Linus Torvalds in 1991, that manages hardware and provides fundamental abstractions. GNU/Linux is the combination of that kernel with the GNU user space (C library, shell, coreutils, compiler) developed by the Free Software Foundation's GNU project. The distinction matters historically (a complete operating system requires both the kernel and the user space) and philosophically (the GNU project is a free-software movement with its own goals), but in common usage "Linux" is used for the whole family, and that is the convention followed throughout this course.

Question 3

Why is the system-call interface so important, and what does it mean that this interface is stable?

Show answer
The system-call interface is the only legitimate way for a user-space program to request a service from the kernel. It is the boundary that enforces the privilege separation between user space and kernel space: the CPU's privilege modes prevent user code from directly executing privileged instructions, so every request must go through a defined, checked entry point. Because the interface is stable — new system calls are added, but existing ones are never removed or have their semantics changed — binaries compiled years ago continue to work on today's kernels. This back-compatibility is what has allowed the Linux ecosystem to grow without the constant recompilation that a changing ABI would require, and it is a deliberate policy of the kernel developers.

Question 4

Name three Linux subsystems and briefly describe what each is responsible for.

Show answer
Any three of the following: Process scheduler — decides which runnable task uses the CPU next. Memory manager — manages virtual-to-physical address translation, demand paging, and page replacement. Virtual File System — provides a uniform interface to diverse file-system formats. Network stack — implements the protocol suite and provides the socket API. Device drivers — kernel modules that talk to specific hardware. Security subsystem — enforces permissions, capabilities, and MAC policies.

Question 5

Linux is not certified as UNIX, yet many UNIX programs run on it without modification. Explain.

Show answer
Because Linux implements the POSIX standard, which specifies the UNIX system-call interface, shell, utilities, and threading model. POSIX is what actually determines whether a program is portable across UNIX-like systems, not whether the system bears the UNIX trademark. Linux implements POSIX (with the extensions that glibc provides), so a program that uses only POSIX facilities will run on Linux, macOS, the BSDs, and any other POSIX-conforming system. The UNIX trademark is a certification mark held by The Open Group; Linux has never been submitted for that certification, but the underlying standard is what matters for compatibility.

08 Unit discussion

Discussion prompt

Linux is a monolithic kernel, while microkernels like QNX and seL4 are architecturally cleaner and, in principle, more secure. Yet Linux dominates servers, phones, and supercomputers, and no microkernel has come close to displacing it.

What explains this? Consider the historical moment at which each design was introduced, the developer ecosystem that grew around Linux, the performance characteristics of monolithic versus microkernel designs, and the practical difficulty of migrating from one to the other. Argue for whichever factor you consider most important, and acknowledge the strongest counter-argument.

Post your response to the Unit 2 discussion forum and reply to at least two classmates. This activity supports CLO1 and CLO7.

09 Practice exercise

This ungraded exercise consolidates the lesson. Aim to spend about 30–45 minutes on it.

  1. Draw the architecture from memory. Without looking at Figure 2.1, sketch the layers of Linux and label each subsystem. Then compare with the figure and note what you missed.
  2. Enumerate your kernel's modules. Run lsmod and group the loaded modules by category (filesystem, networking, storage, sound, graphics, other). Which category has the most?
  3. Trace a command's system calls. Run strace -c cat /etc/hostname. Which system calls did cat make? Group them into the categories from section 4.6 (process control, file operations, memory, information).
  4. Read a real kernel source file. Browse to github.com/torvalds/linux and open kernel/sched/fair.c. You do not need to understand the algorithm yet — just observe the size of the file and its structure. This is the file that implements the process scheduler the course will discuss in Unit 5.
  5. Compare two distributions. Look up the current stable versions of Ubuntu, Fedora, and Debian. Note the kernel version, the default init system, and the package manager. What differences do you observe between them, given that they all use the same kernel?
  6. Write a short reflection. In your own words, describe one thing the kernel does for the programs running on your VM right now, and one thing those programs would have to do for themselves if the kernel did not exist.

10 Summary

  • Linux, strictly speaking, is the kernel; the complete operating system is GNU/Linux — the kernel paired with a user space. In common usage, "Linux" refers to the whole family of distributions.
  • Linux follows the classical UNIX architecture: a strict separation between user space and kernel space, with the system-call interface as the boundary between them.
  • The kernel is responsible for process management, memory management, file systems, device management, networking, and security.
  • The main kernel subsystems are the process scheduler, memory manager, virtual file system, network stack, device drivers, and security framework.
  • Linux is a monolithic kernel with loadable modules: all subsystems run in one address space for performance, and modules can be inserted or removed at runtime for flexibility.
  • Linux is developed as an open-source project under the GPLv2, with contributions from thousands of developers across many companies. You can read, build, and modify the source.
  • Linux is not certified UNIX, but it implements POSIX, which is what determines whether a program is portable across UNIX-like systems.

Looking ahead

Lesson 2.2 turns to the shell — the primary interface through which you will interact with Linux for the rest of the course. Everything from file navigation to compilation will be done from the command line.

11 References

  • Love, R. Linux Kernel Development, 3rd ed., Chapter 1. Addison-Wesley, 2010.
  • Stevens, W. R., & Rago, S. A. Advanced Programming in the UNIX Environment, 3rd ed., Chapter 1. Addison-Wesley, 2013.
  • Torvalds, L., & Diamond, D. Just for Fun: The Story of an Accidental Revolutionary. HarperCollins, 2001. (Historical account of Linux's origins.)
  • Linux kernel source: https://kernel.org and https://github.com/torvalds/linux.
  • Linux kernel documentation: Documentation/process/.
  • POSIX.1-2017 / IEEE Std 1003.1, Standard for Information Technology — Portable Operating System Interface (POSIX).
👤