Session 06: Processes and Threads — Full Contents¶
This directory holds the complete version of session 06: reference solutions, full explanations, and the reference output of every command the exercises ask for.
Use it after the live session for a full view of the solutions and the reasoning behind them.
The corresponding directory used during the session, with skeletons and task descriptions, is 06-processes-threads-live/.
Learning objectives¶
By the end of this session you should be able to:
- Start a program from your own with
fork()andexecvp(), and explain why it takes two calls rather than one. - Wait for a child process with
waitpid(), take its exit status apart, and act on it. - Write a shell that runs commands with arguments, chains them with
;,&&and||, and runs them in the background with&. - Say what a zombie process is, and collect finished children without blocking.
- Start threads with
pthread_create(), pass each one its own work through a struct, and collect what it returns withpthread_join(). - Split a memory region, or an array of tasks, between threads so that each one works on its own part, with nothing shared for writing and therefore nothing to lock.
- Handle the boundaries of a partition: the bytes left over, the matches that straddle a cut, the batch that is smaller than the others.
- Say what a process and a thread each have of their own, and what they share.
Prerequisites and required tools¶
- Session 03 concepts: what
mmap()does with a file. All file handling in this session is provided. - Comfort with C pointers,
structs and arrays of strings. - A Linux environment with
gcc,make,straceandpsinstalled.
Contents¶
| Task | Type | Objective |
|---|---|---|
demo-minimal-shell |
Demo | fork(), exec(), waitpid(): one command at a time, no arguments. |
demo-substring-search |
Demo | pthread_create(), pthread_join(), and a mapped file cut into one region per thread. |
01-shell-chaining |
Core | execvp() with an argument array, the exit status unpacked, and ;, &&, \|\|. |
02-aggregate-matches |
Core | A result returned from each thread, collected in order, and the matches lost at the cuts. |
03-file-encryptor |
Core | An array of files partitioned into batches, one thread each, writing through shared mappings. |
bonus-incomplete-batches |
Bonus | The thread count rounded up and the last batch clamped. |
bonus-background-processes |
Bonus | &, zombies, and waitpid() with WNOHANG. |
Each task directory contains:
README.md— the tutorial: goal, background, how to build and run, and what the results mean.FURTHER.md— optional extensions and discussion points, with answers.INSTRUCTOR.md— notes for whoever runs the session.prompt.txt— the note the exercise was written from.
The through-line of the session¶
The session is about two ways of getting a second thing to run, and what each one shares with the first.
demo-minimal-shellstarts a process: a copy of the shell, made byfork(), whose program is replaced byexec(), and which the shell waits for withwaitpid(). The copy shares nothing with the original once it exists; what it inherits, it inherits as a copy.demo-substring-searchstarts threads: the same system call underneath,clone, with the flags that keep the memory, the files and the signal handlers shared. A pointer handed to a thread is the memory, not a copy, which is what makes threads useful and what makes them dangerous.01-shell-chainingis the process half with the two things the demo left out: the argument array, which is the convention between a shell and a program, and the exit status, which is the one byte a process says to its parent, and on which every&&,||andifis built.02-aggregate-matchesis the thread half with the thing the demo could not do: use the results. A thread returns a pointer, the pointer has to outlive the thread, and the join is what makes the result safe to read. The matches lost at the cuts are the first boundary problem.03-file-encryptorpartitions an array of tasks instead of a region of bytes, and the threads write. It is safe because the batches are disjoint, and that argument, not a lock, is what makes it correct.bonus-incomplete-batchesis the second boundary problem: n items into batches of k when k does not divide n.bonus-background-processescloses the process half: a child that ends while nobody is waiting is a zombie, and a parent's duty to wait does not go away because it was busy.
The design rule that runs through every thread exercise is partition, compute, reduce: split the input so that each worker owns a part, let the workers run with nothing shared for writing, then combine the results in one place after the joins. No mutex, no atomic, no shared counter appears in this session, and students should hear that this is a choice that works whenever the partition is right, not an omission. Session 07 measures what the partition buys; session 08 is what to do when the partition is impossible.