COMP 3150Operating Systems

01 Introduction

Every abstraction in an operating system has a data structure behind it. Files have inodes, memory mappings have page tables, and network connections have socket buffers. Processes are no different: each one is described by a kernel-side record called a process control block (PCB). Where the process's address space is the process's view of the world, the PCB is the kernel's view of the process.

The PCB exists from the moment the process is created until the moment its entry is removed after the parent reaps it. During that lifetime, every event that happens to the process — every state transition, every resource allocation, every context switch — is reflected as an update to one or more fields in the PCB. When the scheduler decides which process to run, it looks at the PCBs of the ready processes. When the memory manager needs to know where a process's page tables are, it looks at the PCB. When the file system checks who owns a file a process is opening, it looks at credentials stored in the PCB. The structure is central in a way that few other kernel data structures are.

This lesson examines the PCB from three angles: what it is for, what it contains, and how the kernel uses it. The lesson also introduces process context — the full set of information that must be present for a process to execute correctly — which is the concept Lesson 3.5 will build on when it examines context switching.

Prerequisites

This lesson builds directly on Lesson 3.2. If you have not yet read the state model — particularly the distinction between Ready and Blocked — return to that lesson before continuing. The PCB is where the state field from Lesson 3.2 actually lives.

02 Learning objectives

By the end of this lesson you will be able to

  • Explain PCB purpose.
  • Identify PCB contents.
  • Explain scheduler use of PCBs.
  • Describe process context.
  • Analyse process management.

This lesson contributes to U3.3 and to course learning outcomes CLO2 and CLO6.

03 Required reading

  • Operating System Concepts — Chapter 3, Section 3.3 (Process Control Block). The classical presentation of what a PCB contains and why.
  • OSTEP — Chapter 4, the discussion of the process control block and what happens on a context switch. OSTEP's framing of "what the OS must save" makes the PCB concrete.
  • Love, R. — Linux Kernel Development, 3rd ed., Chapter 3 (Process Management), especially the discussion of task_struct and the kernel stack. This is how the abstract PCB is realised in a real kernel.
  • Linux man pages: man 5 proc (the full documentation of what is exposed through /proc/<pid>), man 2 getrusage, man 2 getrlimit.
  • Linux kernel source (optional): include/linux/sched.h for the definition of struct task_struct. Even a skim of the first few hundred lines will show how much information the kernel tracks about each process.

Reading strategy

Read Silberschatz Section 3.3 first for the abstract model, then Love's Chapter 3 for the Linux implementation. The proc(5) man page is the practical companion to this lesson — it documents the exact user-visible representation of most of the fields we will discuss, and you will refer to it during the interactive activity.

04 Core content

4.1 What the PCB is for

A running process is described by two things: what it can see (its address space) and what the kernel knows about it (its PCB). The address space is the process's own view of the world; the PCB is the kernel's.

The PCB exists for three reasons.

  1. To identify the process. Every system call, every signal, and every resource reference must be attributed to a particular process. The kernel needs a way to look up "which process is calling me?" and to record what each process owns. The PCB is where that identity lives.
  2. To record the process's state. The current state (Ready, Running, Blocked) and the saved register values must be preserved when the process is not running, so that it can be resumed exactly where it left off. Without this, a context switch would be impossible.
  3. To give the kernel a place to record metadata. Accounting, priority, resource limits, ownership, parent relationships, open files, signal handlers — everything the OS needs to know about the process beyond what the process itself contains.

The PCB is owned by the kernel, not the process

The PCB lives in kernel memory, not in the process's own address space. A process cannot read or write its own PCB directly. It can only affect it indirectly, through system calls — for example, setrlimit changes a field in the PCB, and getpid reads one. This is essential for isolation: if a process could edit its own credentials or scheduling priority, the security model would collapse.

4.2 Contents of the PCB

The exact layout of a PCB differs between operating systems, and Linux's is unusually rich. But the categories of information are stable, and every general-purpose OS tracks the same broad set of fields. A representative PCB falls into four groups:

Contents of a process control block A PCB shown as a vertical stack of fields, grouped into four categories. Group one is process identification (PID, credentials). Group two is the saved processor state (program counter, registers, stack pointer). Group three is process control (state, scheduling, memory, open files, signals, limits). Group four is accounting (CPU time, page faults, I/O counts). THE PROCESS CONTROL BLOCK PCB · one per process GROUP 1 · PROCESS IDENTIFICATION PID · PPID · thread group ID · process group · session Real UID · effective UID · saved UID · filesystem UID Real GID · effective GID · supplementary groups GROUP 2 · SAVED PROCESSOR STATE (written on every context switch) Program counter (rip) Stack pointer (rsp) · frame pointer (rbp) General-purpose registers (rax, rbx, ... r15) Status flags · control registers · FPU / vector state GROUP 3 · PROCESS CONTROL State (Ready / Running / Blocked) · exit code Scheduling: priority · nice · recent CPU usage · time slice Memory: page-table base · address-space limits · mm pointer Open file table · signal handlers · pending signals Resource limits · working directory · parent · children GROUP 4 · ACCOUNTING User-mode CPU time · kernel-mode CPU time · page faults · I/O counts · context switches
Figure 3.3 — A representative process control block. Real kernels (Linux in particular) hold many more fields, but they all fall into these four groups.

4.3 Group 1 · Process identification

Every process has a numeric process ID (PID) unique within its PID namespace. On Linux, threads in the same process share a thread group ID (TGID), which is what user space calls the process's PID; each thread has its own internal task ID. For single-threaded processes, the two are the same.

Most systems also track:

  • Parent process ID (PPID). The PID of the process that created this one. Used for the process tree, for signalling (a parent can signal its children), and for orphan handling (an orphaned process is reparented to PID 1).
  • Process group and session. Used for job control in the shell and for delivery of signals to groups of processes at once.

Ownership information determines what the process is allowed to do. UNIX-like systems track several variants of user and group identity:

Real UID
The user who started the process. Used for accounting and for signalling (a process can only signal processes with the same real or effective UID, unless it has the appropriate privileges).
Effective UID
The user whose permissions are checked on each file access. Normally equal to the real UID. Changed by setuid programs, so a program can act on behalf of another user for a short period.
Saved UID
A saved copy of the effective UID, allowing a privileged process to drop its privileges temporarily and then regain them — the mechanism behind the privilege-bracketing discipline discussed in Lesson 12.3.
Filesystem UID
On Linux, a separate identity used specifically for file permission checks. This allows a process to control file access independently of its signalling identity — used by some servers to enforce "can read this file but cannot signal the owning process" policies.

The same four variants exist for group IDs, plus a set of supplementary groups. A process is considered a member of a group if the group matches its effective GID or any of its supplementary GIDs.

4.4 Group 2 · Saved processor state

When a process is not running, its register values must be stored somewhere. That somewhere is the PCB. On a context switch (Lesson 3.5), the kernel saves the outgoing process's registers into its PCB and restores the incoming process's registers from its own PCB.

The registers that must be saved are the complete state of the CPU as it relates to the process. On x86-64, the essentials are:

  • Program counter (rip). The address of the instruction to resume at. Without this, the process could not be resumed at the right place.
  • Stack pointer (rsp). The top of the process's stack. Restoring this restores the process's local-variable state; the stack frames themselves are in the process's address space, not in the PCB, but the CPU needs to know where the top of that stack is.
  • Frame pointer (rbp). A stable reference to the current stack frame. Used by debuggers and by some compilers for stack unwinding.
  • General-purpose registers. On x86-64 there are sixteen of these (rax through r15), plus a few special ones such as fs and gs base. They hold temporary values that must survive the switch.
  • Status register (rflags). Condition codes (zero, carry, overflow, sign), the interrupt enable flag, and the current privilege level.
  • Floating-point and vector register file. On modern systems this is large — several hundred bytes for AVX-512 state — and is often saved lazily: only when the incoming process actually uses the FPU.

Lazy FPU saving

Because saving and restoring the entire floating-point register file is expensive, many kernels defer it. The FPU state is left in place when a context switch occurs, and a flag is set. If the incoming process tries to use the FPU before the outgoing process's state has been saved, the kernel saves it first. The optimisation works because most processes do not use floating-point at all — a shell, a text editor, or a database in the middle of an integer-only operation never touches the FPU, so no FPU save is ever needed. The cost of this design is a small amount of bookkeeping and a "lazy FPU" fault path. The benefit is that context switches between non-FPU processes are much cheaper. (The mechanism has also been the source of security vulnerabilities, when a subsequent process could observe the previous process's FPU state; modern kernels save eagerly when switching between processes with different security contexts.)

4.5 Group 3 · Process control

This group contains everything the kernel needs to manage the process. It is the largest group on any real system, and it is what most of the course will touch.

Current state
One of the states from Lesson 3.2 — Ready, Running, Blocked, or one of the additional codes Linux uses (S, D, T, Z). The state field is what ps reads when it prints the STAT column.
Scheduling information
Priority, nice value, recent CPU usage, virtual runtime, time-slice remaining, and the scheduling policy. This is what Unit 5 will examine in detail.
Memory-management information
On Linux, a pointer to the mm_struct that describes the process's address space. The mm_struct holds the base address of the top-level page table (the PGD on x86-64), the layout of the process's memory regions, and the page-table bookkeeping. On systems with a flat memory model (no MMU) the PCB would instead hold the base and limit registers discussed in Lesson 8.2.
Open file table
A pointer to the process's table of file descriptors. Each descriptor refers to a system-wide open-file object that records the file's offset, access mode, and a pointer to the in-core inode. On Linux the pointer is to a files_struct.
Signal handlers and pending signals
Which signals the process has installed handlers for, which are currently blocked, which are pending delivery, and which dispositions are set to default or ignored. Delivered by the kernel asynchronously — a mechanism the course touches on in this unit and again in Unit 6.
Resource limits
Maximum CPU time, maximum file size, maximum number of open files, maximum address space, maximum core dump size, and many others. On Linux these are visible through getrlimit and setrlimit, and modifiable at the shell with ulimit.
Parent, children, and siblings
Pointers connecting the process into the process tree. Used for wait, for signal delivery in some cases, and for walking the tree in tools such as pstree.
Working directory and root
The current working directory (used to resolve relative paths) and the process's root directory (used to resolve absolute paths). These are part of the process's mount namespace, so on Linux they are indirect references through the fs_struct.

This group is where most of the action is. Every system call the process makes will read or modify one or more of these fields — some to check permissions, some to record new state, some to update bookkeeping.

4.6 Group 4 · Accounting

The kernel records how much of each resource the process has used. This supports billing (in shared systems where CPU time is metered), profiling (identifying which processes are consuming the most), and scheduling decisions (the scheduler may favour processes that have used less CPU recently). Typical fields include:

  • User-mode CPU time consumed (in clock ticks or nanoseconds).
  • Kernel-mode CPU time consumed (time spent on the process's behalf in system calls).
  • Wall-clock time since creation.
  • Number of voluntary and involuntary context switches.
  • Number of page faults handled (minor and major).
  • Number of I/O operations issued and bytes transferred.
  • Peak memory usage (the high-water mark of the resident set size).

On Linux, this information is exposed through /proc/<pid>/stat and by tools such as time, ps, and pidstat. You will see several of these fields in the worked example.

4.7 How the scheduler uses the PCB

The scheduler's job is to choose the next process to run. To do so, it needs information about every ready process. That information lives in the PCB. A simplified account of what the scheduler does on each scheduling decision:

  1. Look at the ready queue. The ready queue is a data structure containing references to PCBs of ready processes. On Linux it is a per-CPU run queue, implemented as a red-black tree keyed on virtual runtime.
  2. Read each candidate's scheduling fields. Priority, nice value, recent CPU usage, waiting time — the scheduler needs these values to apply its policy.
  3. Apply the policy. Choose the process that the scheduler's algorithm selects (lowest virtual runtime, highest priority, or whatever the current policy specifies).
  4. Read the winner's memory-management pointer. Load the page-table base from the winner's mm_struct into the MMU, so that address translation uses the winner's address space.
  5. Restore the winner's saved registers. Copy the register values from the winner's PCB back into the CPU.
  6. Transition the winner to Running and transfer control to its saved program counter.

Steps 4 and 5 together constitute the context switch, which is the subject of Lesson 3.5. For now, the important observation is that every one of these steps reads from the PCB.

Why the PCB must be efficient to access

The scheduler runs on the very CPU it is allocating, so every cycle it spends reading PCBs is a cycle not spent running a process. On a heavily loaded system, the scheduler may make thousands of decisions per second, each touching several PCBs. Linux keeps the running process's PCB (and its kernel stack) readily accessible via a per-CPU variable and caches scheduling-relevant fields so that the scheduler does not have to touch unrelated parts of the structure.

4.8 Process context

The term process context refers to the full set of information that must be present for the process to execute correctly. The PCB holds most of it; the rest lives elsewhere but is referenced from the PCB. The context includes:

  • The register state. Program counter, stack pointer, general registers, status flags, FPU state — the fields from Group 2 of the PCB.
  • The address space. The process's memory image — text, data, heap, stack, and any shared mappings — as described by the page table and the memory-map structures. Technically this is not stored in the PCB, but the PCB points to it, and the MMU must be programmed with it on every switch.
  • The kernel's bookkeeping. Open file descriptors, signal handlers, credentials, resource limits, and everything else in Group 3. These are the values that determine what the process may do and what it will observe.

When the kernel performs a context switch, it makes the incoming process's context the current one. The register state is loaded from its PCB. The MMU is programmed from its page table. And the kernel's own reference to "the current process" is updated to point at its PCB. After the switch, the incoming process's code runs with the incoming process's resources, as if it had never been interrupted.

4.9 Linux's PCB: task_struct

The Linux kernel uses a structure called struct task_struct, defined in include/linux/sched.h, to represent a process or thread. It is one of the largest data structures in the kernel — several thousand lines of field declarations — and reflects three decades of accumulating requirements.

A partial list of what Linux's PCB tracks will give you a sense of the scope:

  • Identity: pid, tgid, real_parent, parent, children, sibling.
  • State: __state (the current process state), exit_state, exit_code, exit_signal.
  • Scheduling: prio, static_prio, normal_prio, rt_priority, se (the CFS scheduling entity, holding virtual runtime), policy.
  • Memory: mm (the mm_struct pointer), active_mm (used by kernel threads), vmacache (a small cache of recent VMAs for faster lookup).
  • Files: fs (working directory and root), files (the file-descriptor table).
  • Credentials: cred (a pointer to the process's credential structure), real_cred.
  • Signals: sighand (signal handlers), signal (pending and blocked signals), sas_ss_sp (alternate signal stack).
  • Timers: utime, stime (user and system CPU time), start_time, real_start_time.
  • Accounting: min_flt, maj_flt (minor and major page faults), nvcsw, nivcsw (voluntary and involuntary context switches).
  • Kernel stack: stack — a pointer to the process's kernel-mode stack. Every process has a separate kernel stack so that system calls and interrupt handlers have their own working space, isolated from the user-mode stack.
  • Thread group: thread_group (a list of other threads in the same process), thread_info, and the per-thread register state in thread_struct.

The full structure includes fields for namespaces (containers), cgroups (resource limits), audit context, seccomp filters, perf events, RCU bookkeeping, lockdep instrumentation, and much more. Not every field is used in every configuration; many are conditional on kernel configuration options.

The current process

When the kernel is executing on behalf of a process — inside a system call, or in an interrupt handler that interrupted that process — it needs a fast way to find that process's PCB. On x86-64 Linux this is done through a per-CPU variable accessed via the gs segment. The macro current expands to a read of that variable, which is a single memory access — fast enough to be used freely throughout the kernel.

4.10 Where the PCB lives

The PCB is kernel memory, allocated from the kernel heap when the process is created and freed when it is reaped. It is not part of any user-space address space, so no process can directly observe or modify its own PCB or any other process's.

Linux associates each task_struct with a separate kernel stack: a small (typically 8 or 16 KB) region of kernel memory that the kernel uses as a stack while executing on behalf of that process. When the process makes a system call, the CPU switches to the kernel stack; when the system call returns, the CPU switches back to the user stack. Keeping the two stacks separate is what prevents a user-space stack overflow from corrupting kernel data — a subtle but important protection that we take for granted.

Historically, the task_struct and the kernel stack were allocated together in a single page-aligned block, and the kernel could find one from the other by masking the stack pointer. On modern kernels they are allocated separately, with the current pointer maintained in a per-CPU variable. The performance is comparable, and the separate allocation gives more flexibility (larger stacks, better memory locality).

05 Worked example

To make the PCB concrete, inspect the PCB of a real process through the kernel's /proc interface.

Reading the PCB of a shell process

  1. Identify the process.
    $ echo $$
    4321
    The number printed is the shell's PID. Write it down — you will substitute it for <PID> in the commands below.
  2. Read Group 1 (identification).
    $ cat /proc/4321/status | head -12
    Name:   bash
    Umask:  0022
    State:  S (sleeping)
    Tgid:   4321
    Ngid:   0
    Pid:    4321
    PPid:   4300
    TracerPid:      0
    Uid:    1000    1000    1000    1000
    Gid:    1000    1000    1000    1000
    FDSize: 256
    Groups: 1000 4 24 27 30 46 100 114 1001
    Notice the four UID fields: real, effective, saved, and filesystem. All four are the same here because the shell has not run any setuid programs. The Groups line lists the supplementary group IDs.
  3. Read the state field.
    $ awk '{print "state:", $3}' /proc/4321/stat
    state: S
    The S confirms the shell is in interruptible sleep — waiting for input, which is exactly what a shell does when it is not running a command.
  4. Read Group 3 (control). The status file exposes several control fields:
    $ grep -E 'SigQ|SigPnd|SigBlk|SigIgn|SigCgt' /proc/4321/status
    SigQ:   0/63278
    SigPnd: 0000000000000000
    SigBlk: 0000000000010000
    SigIgn: 0000000000380004
    SigCgt: 000000004b817efb
    The SigCgt bitmap shows which signals the shell has installed handlers for. SigBlk shows which signals are currently blocked.
  5. Read Group 3 (memory).
    $ grep -E 'VmPeak|VmSize|VmRSS|VmData|VmStk|VmExe' /proc/4321/status
    VmPeak:    22268 kB
    VmSize:    22268 kB
    VmData:      640 kB
    VmStk:       132 kB
    VmExe:       984 kB
    VmRSS:      3844 kB
    VmSize is the total virtual address space; VmRSS is the resident set size — the portion currently in physical memory. The difference is memory that has been mapped but not yet faulted in.
  6. Read Group 3 (open files).
    $ ls -l /proc/4321/fd
    total 0
    lrwx------ 1 alice alice 64 Oct 15 14:20 0 -> /dev/pts/0
    lrwx------ 1 alice alice 64 Oct 15 14:20 1 -> /dev/pts/0
    lrwx------ 1 alice alice 64 Oct 15 14:20 2 -> /dev/pts/0
    lrwx------ 1 alice alice 64 Oct 15 14:20 255 -> /dev/pts/0
    Descriptors 0, 1, and 2 are standard input, output, and error — all pointing at the pseudoterminal the shell is connected to. Descriptor 255 is bash's internal bookkeeping descriptor.
  7. Read Group 4 (accounting).
    $ awk '{print "utime:", $14, "stime:", $15, "cutime:", $16, "cstime:", $17, "minflt:", $10, "majflt:", $12, "nvcsw:", $42, "nivcsw:", $43}' /proc/4321/stat
    utime: 8 stime: 12 cutime: 143 cstime: 51 minflt: 3837 majflt: 2 nvcsw: 1451 nivcsw: 37
    • utime and stime are the shell's own user-mode and kernel-mode CPU time, in clock ticks.
    • cutime and cstime are the CPU time accumulated by reaped children of the shell — the "c" stands for "children".
    • minflt and majflt are minor and major page faults.
    • nvcsw and nivcsw are voluntary and involuntary context switches.
    The voluntary switch count is high because the shell spends most of its time blocked on input; each time it blocks, that is a voluntary context switch. The involuntary count is low because the shell is rarely preempted — it never runs long enough to exhaust a time slice.
  8. Read the scheduling fields. The scheduler-specific fields are in /proc/<pid>/sched:
    $ head -8 /proc/4321/sched
    bash (4321, #threads: 1)
    ---------------------------------------------------------
    se.exec_start                                :    12345678.901234
    se.vruntime                                  :         1234.567890
    se.sum_exec_runtime                          :          12345678.90
    se.nr_migrations                             :                    0
    nr_switches                                  :                 1488
    nr_voluntary_switches                        :                 1451
    nr_involuntary_switches                      :                   37
    These are the CFS-specific scheduling fields — the virtual runtime on which the scheduler's fairness is based, along with migration and switch counts.

Every field you just read is stored in the shell's task_struct in kernel memory. The /proc interface is a user-space window into that structure, formatted for human consumption. When you read these files, the kernel is populating a buffer with values pulled directly from the PCB.

06 Interactive activity

Activity · compare the PCBs of two different processes

Open two terminals in your course VM. In the first, start a CPU-bound process; in the second, start an I/O-bound one. Then compare their PCBs field by field.

  1. Set up. In terminal 1, run yes > /dev/null & and note its PID (call it PID_CPU). In terminal 2, run sleep 300 & and note its PID (call it PID_IO).
  2. Compare states.
    $ ps -p PID_CPU,PID_IO -o pid,stat,cmd
      PID STAT CMD
     1234 R    yes
     1235 S    sleep
    The CPU-bound process is R — it is on the run queue, constantly competing for the CPU. The I/O-bound process is S — it is blocked on the timer, doing nothing.
  3. Compare CPU time.
    $ for p in PID_CPU PID_IO; do
        echo "PID $p:"
        awk '{print "  utime:", $14, "stime:", $15}' /proc/$p/stat
    done
    The CPU-bound process will have accumulated significant CPU time within seconds. The I/O-bound process will have essentially none.
  4. Compare priority and nice. Both processes were started with the default priority, so their prio and nice fields should match. To see how they change, run renice +10 -p PID_CPU and re-check the fields in /proc/PID_CPU/stat. The nice value changes, and so does the scheduling priority.
  5. Compare memory.
    $ for p in PID_CPU PID_IO; do
        echo "PID $p:"
        grep -E 'VmRSS|VmSize' /proc/$p/status
    done
    Which process uses more memory? The yes process is a very small program but has been running long enough to have a full working set in memory; the sleep process is even smaller.
  6. Compare file descriptors.
    $ for p in PID_CPU PID_IO; do
        echo "PID $p:"
        ls -l /proc/$p/fd | tail -n +2
    done
    Both processes have the standard three descriptors pointing at the terminal. The differences will be subtle — see whether you can identify them.
  7. Compare context switches.
    $ for p in PID_CPU PID_IO; do
        echo "PID $p:"
        awk '{print "  nvcsw:", $42, "nivcsw:", $43}' /proc/$p/stat
    done
    The CPU-bound process should have a high involuntary switch count — it is constantly being preempted because it never blocks on its own. The I/O-bound process should have a low involuntary count and (if you leave it running long enough to complete a sleep cycle) a correspondingly higher voluntary count.
  8. Clean up.
    $ kill PID_CPU PID_IO

Reflect: which PCB fields differ most sharply between the CPU-bound process and the I/O-bound one? Which are essential for distinguishing the two? What would the scheduler need to know from the PCB to make a good decision about which of the two to run next?

07 Knowledge check

Check your understanding

Question 1

Why must the PCB store the process's register values rather than leaving them in the CPU registers?

Show answer
Because the CPU registers are shared hardware. When a different process is dispatched, it will overwrite them with its own values. To resume a preempted process correctly, its register values must be preserved somewhere the kernel controls — the PCB. On the next dispatch, the kernel copies those saved values back into the CPU registers so the process resumes exactly where it left off. This is the mechanism that makes multiprogramming possible: without it, there would be no way to suspend one process and resume another.

Question 2

What information in the PCB changes on every context switch, and what information rarely changes?

Show answer
The saved register state changes on every context switch — it is written on the way out and read on the way in. The current state field also changes whenever the process transitions between states (from Running to Ready on a preemption, or from Ready to Running on a dispatch). What rarely changes: the process ID, ownership, memory map, resource limits, and much of the accounting. Fields such as open file descriptors change only when the process opens or closes files, which is much less frequent than context switches. The scheduler's efficiency depends on separating the fields that must be touched every switch from the ones that can be left alone.

Question 3

Why does the kernel need to know a process's priority and recent CPU usage?

Show answer
Because the scheduler uses these values to choose among ready processes. Priority determines relative importance; recent CPU usage determines whether a process has had enough CPU lately. A scheduler that wants to be fair will favour processes that have used less CPU recently. A scheduler that wants to favour interactive responsiveness will promote processes that have just become ready after blocking. In both cases, the decision depends on data the PCB must record — and the values must be updated on every dispatch and every preemption.

Question 4

Distinguish the four variants of user ID that a Linux PCB tracks, and give one use of each.

Show answer
The real UID is the user who started the process; it is used for accounting and for determining which processes the kernel will allow this one to signal. The effective UID is the identity used for file permission checks; a setuid program temporarily sets this to its owner's UID. The saved UID is a copy of the effective UID, allowing a setuid program to drop privileges for a short period and then restore them — the discipline called privilege bracketing. The filesystem UID is a Linux-specific extra identity used specifically for file access checks; it lets a process control what files it can access independently of what processes it can signal, which is useful for server programs that need to hand work off to an unprivileged helper.

Question 5

What is the "process context," and why does the term include things that are not literally stored in the PCB?

Show answer
The process context is the full set of information that must be present for the process to execute correctly. It includes the register state (which is stored directly in the PCB), the address space (which is described by a page table that the PCB points to, but which is too large to store inside the PCB), and the kernel's bookkeeping structures such as the open-file table and the credential structure (which live in separate kernel data structures referenced from the PCB). The term is used to emphasise that all of these pieces must be switched together when the process is suspended and resumed — it is not just the registers or just the memory, but the whole coherent set of state. This is why the context switch in Lesson 3.5 involves more than a register save and restore: the memory map is switched, and the kernel's "current process" pointer is updated.

08 Unit discussion

Discussion prompt

A PCB is fundamentally a passive record — a set of fields. Everything about a process's management lives in one. But the Linux task_struct has grown to include fields for namespaces, cgroups, security contexts, and dozens of other subsystems that did not exist when the PCB concept was invented.

Is this growth a strength or a design smell? Argue for one view, using concrete examples of fields that were added over time and what they were added to support. Consider the alternatives: putting new state in separate per-subsystem structures, using extensible key-value metadata, or splitting the PCB into a small "hot" core and a larger "cold" annex. What are the trade-offs of each?

Post your response to the Unit 3 discussion forum and reply to at least two classmates. Supports CLO2 and CLO8.

09 Practice exercise

This ungraded exercise consolidates the lesson and prepares you for the PCB-inspection aspects of Lab 2. Aim to spend 45–60 minutes on it.

  1. Enumerate PCB fields from memory. Without consulting the lesson, list every PCB field you remember and the group it belongs to. Then check the lesson and note which you missed. Are the missed fields more or less important than the ones you remembered?
  2. Map fields to /proc. For each PCB field you listed, identify the corresponding entry in /proc/<pid>/. Which fields are not visible from user space? Why might a field be hidden? (Consider credentials, for example.)
  3. Write a query script. Write a shell script that takes a PID and prints a formatted summary: state, priority, nice value, CPU time, memory usage, and count of open file descriptors. Handle the case where the PID does not exist. This script will be useful throughout the course.
  4. Read task_struct. If you have the kernel source available (the linux-source package on Ubuntu), open include/linux/sched.h and find the definition of struct task_struct. Name three fields related to scheduling and two related to security. Comment on the total size of the structure — wc -l on the definition will give you a rough sense.
  5. Consider a hypothetical. Imagine designing an OS without PCBs — storing all process state in the process's own address space instead. What problems would arise? List at least three concrete issues and explain why each is fundamental. (Hint: think about what happens when the process is suspended, when the process is misbehaving, and how the kernel would find the state when it needed to.)
  6. Reflect. In your own words, explain why the PCB must live in kernel memory rather than in the process's own address space. Give one concrete attack that would be possible if a process could modify its own PCB.

10 Summary

  • The process control block is the kernel's record of a process. It exists from creation until the entry is reaped after termination.
  • A PCB contains four groups of information: process identification, saved processor state, process control, and accounting.
  • The saved processor state — program counter, registers, stack pointer, status flags, FPU state — is what makes it possible to suspend and resume a process. It is rewritten on every context switch.
  • The scheduler uses PCB fields to select which ready process runs next, and to locate the memory map and file table when dispatching it.
  • The full set of information needed to run a process is called its context. Restoring a context on dispatch creates the illusion that each process has the machine to itself.
  • On Linux the PCB is struct task_struct, one of the largest data structures in the kernel. Much of it is visible from user space through /proc.
  • The PCB lives in kernel memory and is inaccessible to user code. This is what makes the security and isolation guarantees of the whole system possible.

Looking ahead

Lesson 3.4 turns to the scheduling problem — what criteria matter, what schedulers the kernel maintains, and what metrics are used to compare them. The PCB fields you have just seen are the inputs to every scheduling decision.

11 References

  • Silberschatz, A., Galvin, P. B., & Gagne, G. Operating System Concepts, 10th ed., Chapter 3, Section 3.3.
  • Arpaci-Dusseau, R. H., & Arpaci-Dusseau, A. C. Operating Systems: Three Easy Pieces, Chapter 4. Available online at ostep.org.
  • Love, R. Linux Kernel Development, 3rd ed., Chapter 3 (Process Management), especially the task_struct discussion and the kernel-stack section. Addison-Wesley, 2010.
  • Linux man-pages: man 5 proc, man 2 getrusage, man 2 getrlimit, man 2 setrlimit, man 1 ps, man 1 top.
  • Linux kernel source: include/linux/sched.h (definition of struct task_struct), kernel/fork.c (where a new task_struct is allocated).
  • Bach, M. J. The Design of the UNIX Operating System, Chapter 6. Prentice Hall, 1986. (Classical reference for the UNIX process control block.)
👤