Module 2 · Assessment 1 · 15% of final grade
Assignment 1 — Parallel Log Analyzer
In this assignment you will design and implement a complete, concurrent log-analysis system that uses both processes and threads, communicates through pipes and shared memory, synchronises access to a bounded buffer with mutexes and condition variables, and shuts down gracefully on a signal. The assignment draws on every unit of Module 2 — processes, threads, scheduling, synchronization, and deadlock — and is the largest piece of programming in the course before the final project.
01 Overview
| Course | COMP 3150 / CMIS 3150 — Operating Systems |
|---|---|
| Weight | 15% of the final grade |
| Issued | Week 5 |
| Due | Week 9 (end of Module 2) |
| Estimated effort | 20–25 hours over 4 weeks |
| Submission | One archive containing source code, Makefile, test inputs, test outputs, performance logs, and a technical report (see § 09) |
| Environment | Linux VM, gcc with -Wall -Wextra -pthread, make, valgrind (with Helgrind and DRD), strace, perf, gdb |
| CLOs assessed | CLO2, CLO3, CLO6, CLO8 |
This assignment is worth 15% of the final grade. Late submission penalty: 10% per day up to three days; after three days, submission is accepted only with documented extenuating circumstances.
02 Learning outcomes assessed
On successful completion of this assignment you will be able to
- Design a multi-process system using
fork,exec,wait, and pipes, and reason about the parent-child process hierarchy. - Design a multi-threaded system using POSIX threads, including thread pools and work distribution.
- Combine processes and threads in a single coherent architecture.
- Use mutexes, condition variables, and semaphores correctly to protect shared data and to coordinate producer-consumer stages.
- Handle signals (
SIGINT,SIGTERM,SIGCHLD,SIGPIPE) to achieve graceful shutdown and to reap children without zombies. - Detect and eliminate race conditions, deadlocks, and starvation using both design review and tooling (
valgrind --tool=helgrind,valgrind --tool=drd). - Measure and interpret the performance of a concurrent system, including speedup, scaling efficiency, and the effect of synchronization overhead.
- Write a technical report that documents the design, correctness arguments, test results, and performance analysis.
This assignment assesses CLO2 (process and thread management), CLO3 (scheduling, synchronization, and deadlock), CLO6 (Linux systems programming and tools), and CLO8 (design and implementation of OS-related components).
03 The scenario
You are building the analysis engine for a small log-management service. The service receives a directory of log files produced by a fleet of application servers. Each log file is a line-oriented text file in the following format:
2024-03-15T14:22:07.412Z INFO api-server-03 GET /v1/orders 200 43ms
2024-03-15T14:22:07.418Z WARN api-server-01 slow query: orders_by_customer 812ms
2024-03-15T14:22:07.421Z ERROR api-server-03 connection reset by peer
2024-03-15T14:22:07.423Z INFO api-server-02 POST /v1/orders 201 91ms
...
Each line contains, in order: an ISO-8601 timestamp
with milliseconds, a level
(DEBUG, INFO,
WARN, ERROR), a source
identifier, and a free-form message. The analyzer
must produce a summary report containing:
- Total number of lines processed, per level.
- Per-source line counts (a histogram of source identifiers).
- A per-minute time series: for each minute in the interval covered by the logs, the count of lines at each level.
- The top 10 most frequent error messages (normalised: digits replaced by
#so thatconnection 192.168.1.5andconnection 192.168.1.7are counted together). - The 95th percentile of the latency field (the trailing
<n>msin the message, when present), per level.
The system must be concurrent: it must use multiple processes and multiple threads, and it must scale to tens of gigabytes of logs on a machine with a modest number of cores. It must also be robust: it must handle malformed lines, missing files, and an interrupt (Ctrl-C) without leaving orphaned processes, zombie processes, or partially written output.
04 Functional and non-functional requirements
4.1 Functional requirements
- F1 — Command-line interface. The program is invoked as
pla [options] <directory>. It supports at least the following options:-p <n>— number of worker processes (default: number of CPU cores, capped at 8).-t <n>— number of threads per worker process (default: 2).-q <n>— capacity of the in-worker bounded buffer (default: 256 records).-o <file>— write the report to a file instead of standard output.-v— verbose; print per-file and per-worker progress to standard error.-h— print usage and exit.
- F2 — Discovery. The master process recursively scans the given directory for files whose name ends in
.logand produces a work list. It prints the total number of files and the total byte size before processing begins. - F3 — Distribution. The master forks P worker processes. Each worker receives a share of the file list. Files are distributed dynamically: a worker that finishes its current file asks the master for another, so that a large file at the end does not leave other workers idle.
- F4 — In-worker parallelism. Each worker reads its assigned file in chunks and sends parsed records to a bounded buffer consumed by T threads. The reader is a single producer; the T threads are consumers.
- F5 — Per-worker statistics. Each worker maintains its own statistics structure (level counts, source histogram, per-minute time series, error message table, latency samples). Updates to this structure are made under a mutex, or via per-thread partial structures that are merged at the end (you choose; justify the choice in the report).
- F6 — Aggregation. When a worker finishes, it sends its statistics to the master over a pipe (using a well-defined binary protocol) and exits with status 0. The master reads all statistics, merges them, and produces the final report.
- F7 — Report. The master writes the report described in § 03 to standard output or to the file specified by
-o. The report is deterministic: two runs on the same input produce byte-identical output (except for the elapsed-time line). - F8 — Error handling. Malformed lines are counted but not parsed; a summary line reports the number of malformed lines. Missing or unreadable files are reported to standard error and skipped; the run continues.
- F9 — Signal handling. On
SIGINT(Ctrl-C), the master sendsSIGTERMto all workers, waits up to 5 seconds for them to exit, then sendsSIGKILLto any stragglers. Workers handleSIGTERMby finishing the current record, flushing partial statistics to the master, and exiting cleanly. No zombie or orphan process is left behind. - F10 — Exit status. The program exits with 0 on success, 1 on a fatal error (for example, the directory does not exist), and 2 if the run was interrupted.
4.2 Non-functional requirements
- N1 — Correctness. No line is lost, no line is counted twice, and the report is consistent with a single-threaded reference implementation on every test input.
- N2 — No races, deadlocks, or leaks. Running under
valgrind --tool=helgrindandvalgrind --tool=drdproduces no data-race reports (other than those explicitly caused by signal handlers, which must be documented). Running undervalgrind --leak-check=fullproduces no leaks. - N3 — Scalability. With a large input (at least 1 GB of logs) and P × T = number of cores, the program achieves at least 70% of the theoretical speedup predicted by Amdahl's Law, where the serial fraction is the final aggregation step. Report the measured speedup for P ∈ {1, 2, 4, 8} with T = 2.
- N4 — Portability. The program compiles with
gcc -Wall -Wextra -Werror -pthread -O2on Linux without warnings. - N5 — Documentation. Every source file begins with a header comment stating its purpose, its author, and the date. Every non-trivial function is documented with a short comment stating its contract. A
README.mddescribes how to build, run, and test the program.
05 Suggested architecture
You are free to design the system as you see fit, but the following architecture is known to work and is offered as a starting point. Deviations are permitted and encouraged if you can justify them in the report.
5.1 Master process
The master performs four tasks: it discovers the
input files, it forks workers and distributes work,
it collects statistics from the workers, and it
writes the final report. It also handles
SIGINT and SIGCHLD.
- Work distribution uses a pipe pair per worker: the master writes a file path when the worker asks for one, or writes an end-of-stream marker when the list is exhausted. (An alternative is a shared-memory queue with a mutex and condition variables; either is acceptable.)
- Statistics collection uses a separate pipe pair per worker. The worker sends a fixed-size header followed by variable-length payload; the master reads the header, reads the payload, and merges the statistics into its own accumulators.
- The master reaps children with
waitpid, either in aSIGCHLDhandler (careful:waitpidis not async-signal-safe on all systems, so a self-pipe orsigwaitis safer) or in a loop after all work has been distributed.
5.2 Worker process
Each worker runs a single reader thread (the main thread) and T consumer threads. The reader reads records from the current file and places them in the bounded buffer; the consumers pull records from the buffer and update the worker's statistics structure.
- The bounded buffer is a circular array protected by a mutex and two condition variables (
not_full,not_empty), or by three semaphores (empty,full,mutex). Either is acceptable; justify your choice. - Each consumer thread has a private partial statistics structure to minimise contention. At the end of the run, the worker's main thread merges the partial structures. (This is an optimisation; a single structure with a mutex is also acceptable if the lock is held only briefly.)
- The reader is the only producer. It must handle a partial last line, an empty file, and a file that grows while it is being read (the specification does not require handling the last case; state your assumption in the report).
5.3 Graceful shutdown
The master installs a handler for SIGINT.
The handler sets a flag (declared
volatile sig_atomic_t) and writes a byte
to a self-pipe; the master's main loop watches the
self-pipe. When the flag is set, the master:
- Sends
SIGTERMto each worker. - Waits up to 5 seconds, using
waitpidwith a loop andnanosleep, for the workers to exit. - Sends
SIGKILLto any worker still alive. - Reaps all remaining children.
- Prints a partial report to standard error (or writes it to the output file) and exits with status 2.
Each worker installs a handler for SIGTERM
that sets a flag read by the reader and the
consumers. The reader stops reading, closes the
input, and signals the buffer's end-of-stream. The
consumers drain the buffer, merge their statistics,
send them to the master, and exit with status 0.
The worker must not exit before its statistics are
sent — otherwise the master loses that worker's
contribution.
06 Milestones and suggested schedule
The assignment is designed to be completed over four weeks. The milestones below are not graded separately, but they are checked during office hours and are a strong predictor of the final grade. Submit each milestone's artefact (a short design note, a working prototype, or a test log) before the next milestone begins.
| Week | Milestone | Artefact |
|---|---|---|
| Week 5 | Design document | A 2-page design note describing the process/thread architecture, the communication protocol, the synchronization primitives, and the failure modes considered. |
| Week 6 | Single-process, single-threaded prototype | A working analyzer that processes a directory sequentially and produces a correct report. This is the reference implementation for correctness testing. |
| Week 7 | Multi-threaded worker | A worker that reads a file into a bounded buffer and processes it with T consumer threads. Correctness verified with Helgrind on a small input. |
| Week 8 | Multi-process system | Master + workers, work distribution, statistics aggregation, signal handling. Full correctness and Helgrind verification. |
| Week 9 | Performance analysis and report | Scaling measurements, speedup plot, technical report, final submission. |
07 Required test cases
You must run the following tests and include the results in your report. Each test is described by its input, the command that runs it, and the property that must hold. You may add your own tests; the ones below are the minimum.
| # | Test | Property to verify |
|---|---|---|
| T1 | Empty directory | Report contains zeros for all counters; exit status 0. |
| T2 | Single small file (1 KB) | Report matches the reference implementation byte for byte. |
| T3 | Many small files (1,000 files × 1 KB) | Report matches reference; no file is skipped; total lines = sum of per-file line counts. |
| T4 | One large file (100 MB) | Report matches reference; no line is lost at the file boundary. |
| T5 | File with malformed lines (10% of lines) | Malformed-line counter is correct; the well-formed lines are still processed. |
| T6 | File with a very long line (1 MB) | Line is handled without truncation or buffer overflow. Use valgrind to confirm. |
| T7 | Run under Helgrind | No data-race reports. |
| T8 | Run under DRD | No data-race reports. |
| T9 | Run under valgrind --leak-check=full |
No leaks; all heap blocks are freed. |
| T10 | Send SIGINT after 2 seconds |
Workers terminate within 5 seconds; no zombies (ps shows no <defunct>); exit status 2. |
| T11 | Make a file unreadable (chmod 000) |
Error is reported to standard error; run continues; exit status 0. |
| T12 | Pass a non-existent directory | Error is reported; exit status 1; no children are forked. |
| T13 | Run with P = 1, T = 1 on 100 MB | Correct report; baseline time recorded. |
| T14 | Run with P = 4, T = 2 on 100 MB | Correct report; elapsed time < baseline; speedup recorded. |
| T15 | Run with P = 8, T = 2 on 100 MB | Correct report; elapsed time recorded; note any degradation relative to P = 4. |
08 Submission instructions
Submit a single archive named
a1-<lastname>-<studentnumber>.tar.gz
containing the following files and directories:
-
src/— the C source files. Every source file must compile withgcc -Wall -Wextra -Werror -pthread -O2. Suggested files:pla.c— the main entry point and option parsing.master.c,master.h— the master process: discovery, forking, work distribution, aggregation, signals.worker.c,worker.h— the worker process: reading, thread pool, buffer, statistics collection.buffer.c,buffer.h— the bounded buffer.stats.c,stats.h— the statistics structure, its update functions, and its merge function.parser.c,parser.h— the line parser.report.c,report.h— the report writer.protocol.h— the wire format used between master and worker.
-
Makefile— builds the executableplaand amake cleantarget. The default target must compile without warnings. -
tests/— the test inputs and outputs. At minimum:tests/input-small/— a small directory of log files used for the correctness tests.tests/expected-small.txt— the expected report forinput-small.tests/run-tests.sh— a script that runs the required tests and prints a pass/fail summary.tests/generate.sh— a script that generates the larger inputs (100 MB) from a seed file, so that the archive remains small.
-
logs/— the outputs of the required tests. At minimum:logs/helgrind.txt— the Helgrind output on the small input.logs/drd.txt— the DRD output on the small input.logs/valgrind-leak.txt— the leak-check output.logs/perf.txt— the timing data for the scaling experiments (T13–T15).logs/sigint.txt— the transcript of the SIGINT test, showing the exit status and the absence of zombies.
-
README.md— build instructions, usage examples, and a short description of the architecture. -
report.pdf(orreport.md) — the technical report described in § 09. -
design-notes.md— the design document produced at the Week 5 milestone.
Archive hygiene
Do not include compiled objects
(*.o), the executable
(pla), the generated large test
files, or any editor backup files. The archive
should be self-contained: after extraction,
make should build the program, and
tests/run-tests.sh should run the
tests.
09 Technical report
The technical report is worth 10% of the assignment grade and is the primary evidence of your understanding of the design trade-offs. It should be 8–10 pages (excluding code listings and raw logs) and must contain the following sections.
- Introduction (½ page). The problem statement, the goals of the system, and a short summary of the architecture.
- Architecture (1–2 pages). A diagram of the process and thread structure, with a clear description of the master, the workers, the reader, and the consumer threads. Describe the work-distribution protocol and the statistics-collection protocol at a level sufficient for a reader to re-implement them.
- Synchronization design (2 pages). For each shared data structure, state which primitive protects it and why. For the bounded buffer, give the invariant that the buffer maintains and argue informally that your code preserves it. Explain how you avoid deadlock (lock ordering, try-lock with back-off, or a wait-for graph analysis), and how you avoid starvation (fair queues, aging, or a design argument).
-
Signal handling (1 page).
The signal handlers you install, the
async-signal-safe operations they perform, and
how the shutdown sequence is coordinated
between the master and the workers. Explain why
printf,malloc, andpthread_mutex_lockmust not be called from a signal handler, and show what you did instead. - Correctness evidence (2 pages). The results of the required tests T1–T12, presented as a table with the expected and observed results. Include the Helgrind, DRD, and leak-check summaries. For any deviation, give the diagnosis and the fix.
- Performance analysis (2 pages). The results of tests T13–T15, presented as a table and as a plot of speedup versus number of worker processes × threads. Compute the serial fraction from your measurements and compare it to your prediction. Discuss the sources of overhead (process creation, context switching, lock contention, cache-line bouncing, pipe traffic) and how they affect the scaling curve. Identify the point at which adding more workers stops helping, and explain why.
- Reflection (1 page). A discussion of what you would do differently if you were starting over. Which design decision proved most important? Which primitive or pattern was most error-prone, and how did you catch the bugs? What did you learn about concurrent programming that you did not know before?
Style requirements
The report must be written in clear academic prose, with complete sentences and paragraphs. Bullet lists are acceptable for enumerations of requirements or test results, but the analysis and the reflection must be prose. Code listings are placed in an appendix and are referenced from the body. Figures must be captioned and referenced in the text. The report must be self-contained: a reader should not need the source code to understand the design and the findings.
10 Grading rubric
The assignment is marked out of 100 points. The criteria, their weights, and the associated CLOs are given below, followed by the five-level descriptors for each criterion.
| Criterion | Weight | CLOs |
|---|---|---|
| Process implementation | 15% | CLO2, CLO6, CLO8 |
| Thread implementation | 20% | CLO2, CLO6, CLO8 |
| Synchronization design | 25% | CLO3, CLO6, CLO8 |
| Correctness | 20% | CLO2, CLO3, CLO8 |
| Code quality | 10% | CLO6, CLO8 |
| Technical report | 10% | CLO3, CLO8 |
| Total | 100% |
10.1 Process implementation (15 points)
| Level | Descriptor |
|---|---|
| Exemplary (90–100%) | The process architecture is correct and well-justified. Work distribution is dynamic and balanced. Inter-process communication is robust, uses a documented protocol, and handles partial reads and writes correctly. All children are reaped; no zombies or orphans are left behind. Signal handling is correct and async-signal-safe. The parent-child relationship is clear from the design document and the code. |
| Proficient (80–89%) | The process architecture is correct. Work distribution is static or dynamic but reasonably balanced. Inter-process communication works on the tested inputs. Children are reaped; signal handling covers the common cases. Minor issues (for example, a partial read that is not handled, or a signal handler that is not async-signal-safe in a rare path) are present but do not affect correctness on the required tests. |
| Developing (70–79%) | The process architecture exists and runs on simple inputs, but has correctness issues on some required tests (for example, a worker is not reaped, a pipe is not closed, or a file is skipped). Signal handling is absent or incorrect. The design document is thin or inconsistent with the code. |
| Beginning (60–69%) | The program forks workers but does not distribute work correctly, does not aggregate results, or does not terminate cleanly. Signal handling is missing. The design document is a restatement of the assignment rather than a design. |
| Not demonstrated (<60%) | The process model is not implemented, or the program does not run. No design document is submitted. |
10.2 Thread implementation (20 points)
| Level | Descriptor |
|---|---|
| Exemplary (90–100%) | The thread pool is correct and well-designed. The reader/consumer split is clear, the bounded buffer is correctly implemented, and the consumers correctly handle spurious wakeups and the end-of-stream. Thread creation and joining are error-checked. Partial per-thread statistics are merged correctly. The design document explains why the chosen thread count and buffer capacity are appropriate. |
| Proficient (80–89%) | The thread pool is correct on the tested inputs. The bounded buffer works but has a minor issue (for example, a missing check for spurious wakeups that happens not to fire in the tests, or a join that is not performed on an error path). The design document explains the architecture but not the tuning choices. |
| Developing (70–79%) | Threads are created but the reader/consumer coordination is incorrect on some inputs (for example, records are lost at the end of the file, or a thread exits early). Joins are missing or performed in the wrong order. The design document does not explain the thread architecture. |
| Beginning (60–69%) | Threads are created but do not communicate correctly, or the program uses threads in name only (a single thread does all the work). The bounded buffer is missing or does not work. |
| Not demonstrated (<60%) | Threads are not used, or the program does not compile with -pthread. |
10.3 Synchronization design (25 points)
| Level | Descriptor |
|---|---|
| Exemplary (90–100%) | Every shared data structure is protected by a documented primitive. The bounded buffer's invariant is stated and argued. The design avoids deadlock by construction (lock ordering, single-lock design, or a well-reasoned try-lock with back-off) and avoids starvation by design. The report explains why each primitive was chosen over the alternatives, and it correctly identifies the situations in which a semaphore is appropriate versus a mutex plus condition variable. Helgrind and DRD report no races. |
| Proficient (80–89%) | Shared data is protected. The bounded buffer is correct. Deadlock is avoided in practice, though the design argument is incomplete (for example, lock ordering is followed but not documented). Helgrind and DRD report no races, or report only races in code that is documented as intentionally unsynchronised (for example, a signal handler's flag). |
| Developing (70–79%) | Some shared data is protected and some is not. The bounded buffer works on the tested inputs but is not robust to spurious wakeups or to a slow consumer. Helgrind or DRD reports races, and the report does not explain them. The choice of primitives is not justified. |
| Beginning (60–69%) | Synchronization is attempted but incorrect: the buffer overflows or underflows, the condition variables are signalled with the wrong predicate, or the semaphores are initialised to the wrong values. Deadlock or livelock is observed on some inputs. |
| Not demonstrated (<60%) | No synchronization primitives are used, or the program produces incorrect results because of races. |
10.4 Correctness (20 points)
| Level | Descriptor |
|---|---|
| Exemplary (90–100%) | All required tests (T1–T15) pass. The report is byte-identical to the reference implementation on every deterministic input. Helgrind, DRD, and leak-check are all clean. The signal test shows no zombies and the correct exit status. The performance measurements are reproducible and the scaling curve is as predicted by the design. |
| Proficient (80–89%) | All required tests pass except one or two non-critical tests (for example, T6 or T11) that fail for documented reasons. Helgrind or DRD is clean; leak-check is clean. Performance measurements are present but the analysis is incomplete. |
| Developing (70–79%) | Most tests pass, but several fail. The report does not match the reference on some inputs. Helgrind or DRD reports races. The performance measurements are missing or inconsistent. |
| Beginning (60–69%) | Only a few tests pass. The program crashes or hangs on at least one required test. No performance data is provided. |
| Not demonstrated (<60%) | The program does not pass the basic correctness tests, or it does not run at all. |
10.5 Code quality (10 points)
| Level | Descriptor |
|---|---|
| Exemplary (90–100%) | The code compiles cleanly with -Wall -Wextra -Werror. It is modular, with clear interfaces between components. Naming is consistent and descriptive. Error handling is thorough: every system call's return value is checked, and errors are reported with context. Memory is freed on every path, including error paths. The Makefile is complete and correct. |
| Proficient (80–89%) | The code compiles with warnings enabled (a few warnings may remain, but none indicates a bug). It is reasonably modular. Most system calls are checked. Memory is freed on the main paths; a few error paths leak. The Makefile is adequate. |
| Developing (70–79%) | The code compiles with warnings. It is monolithic or poorly organised. Many system calls are unchecked. Memory leaks are present but bounded. The Makefile is minimal or has incorrect dependencies. |
| Beginning (60–69%) | The code does not compile cleanly. It is a single large function with no structure. Error handling is absent. Memory leaks are significant. |
| Not demonstrated (<60%) | The code does not compile, or it consists of copied fragments that the student cannot explain. |
10.6 Technical report (10 points)
| Level | Descriptor |
|---|---|
| Exemplary (90–100%) | The report is well-structured, evidence-based, and reflective. The design section is detailed enough to re-implement the system. The correctness evidence is complete. The performance analysis is quantitative and explains the observed scaling. The reflection shows genuine insight. Prose is clear and correct; figures are captioned and referenced. |
| Proficient (80–89%) | The report covers all required sections. The design and synchronization sections are adequate. The correctness evidence is present. The performance analysis is present but light on interpretation. The reflection is generic. Prose is clear; a few figures may be uncaptioned or unreferenced. |
| Developing (70–79%) | The report is missing one or more required sections. The design section restates the code without explaining the rationale. The performance analysis is a table with no interpretation. The reflection is a restatement of the assignment. Prose has frequent errors. |
| Beginning (60–69%) | The report is disorganised or significantly inaccurate. Claims are unsupported by evidence. Key sections are missing or superficial. |
| Not demonstrated (<60%) | No report is submitted, or the report is a copy of another source. |
11 Academic integrity
This assignment is individual work. You may discuss
the general approach with classmates, and you may
consult textbooks, man pages, and online resources
for specific technical questions (for example, the
signature of pthread_cond_wait or the
semantics of SIGCHLD). You must not
copy code from another student, from a previous
offering of the course, or from an online source
without attribution, and you must not share your
code with another student.
The following are specifically permitted:
- Using the course's lab code as a starting point for the individual components (for example, the bounded buffer from Lab 5), provided you understand it and can explain it.
- Using standard library functions and system calls.
- Using code-generation tools (for example, an AI assistant) for boilerplate (Makefile, argument parsing), provided you review the generated code, understand it, and take responsibility for it. You must disclose any such use in the report.
The following are prohibited:
- Copying code from another student or from a public repository without attribution.
- Having another person write any part of your code or your report.
- Submitting a report that misrepresents the performance of your code.
Suspected violations are handled according to the university's academic integrity policy. The penalty for a first offence is a grade of zero on the assignment; a second offence results in a grade of F in the course.
12 Resources
- Arpaci-Dusseau, R. H., & Arpaci-Dusseau, A. C. Operating Systems: Three Easy Pieces. Chapters 4–7 (processes), 25–27 (threads and concurrency), 28–31 (locks, condition variables, semaphores), 32 (deadlock).
- Silberschatz, A., Galvin, P. B., & Gagne, G. Operating System Concepts, 10th ed. Chapters 3–4 (processes and threads), 6–8 (synchronization and deadlock).
- Stevens, W. R., & Rago, S. A. Advanced Programming in the UNIX Environment, 3rd ed. Chapters 8–10 (process control, signals), 15–16 (interprocess communication, network IPC).
- Kerrisk, M. The Linux Programming Interface. Chapters 24–28 (process creation, termination, monitoring), 29–33 (threads), 44–47 (pipes and FIFOs), 53–54 (POSIX semaphores, shared memory).
- Linux man-pages:
man 2 fork,man 2 execve,man 2 wait,man 2 pipe,man 2 signal,man 7 signal,man 3 pthread_create,man 3 pthread_mutex_lock,man 3 pthread_cond_wait,man 3 sem_init,man 7 mq_overview,man 7 shm_overview. - Valgrind documentation: Helgrind and DRD sections.
- Butenhof, D. R. Programming with POSIX Threads. Addison-Wesley, 1997.
- Drepper, U. How to Write Shared Libraries (for background on dynamic linking, not directly required).