<aside> ๐Ÿงญ

Module 06 ยท Threads, races and deadlock

Module 02 showed how one process becomes two. This module is the other option: one process doing several things at once, sharing everything โ€” which is faster, and which introduces a class of bug that only shows up under load.

๐Ÿง  concept โ†’ ๐Ÿงฉ real-world analogy โ†’ ๐Ÿงช exercise โ†’ โœ… expected result (hidden) โ†’ ๐ŸŽฏ interview questions (hidden)

</aside>

<aside> โœ…

Before you start, you should already know:

From Module 01 โ€” system calls, strace, and why entering the kernel costs something.

From Module 02 โ€” processes, fork, clone, PIDs, and states R, S, D.

From Module 03 โ€” file descriptors.

From Module 04 โ€” signals.

This module comes before scheduling on purpose: Linux schedules threads, not processes, so you need to know what a thread is first.

</aside>


๐Ÿงต Part A ยท What a thread is

A1 ยท Threads and processes โ€” what is shared

<aside> ๐Ÿ“–

Official docs: pthreads(7) ยท clone(2) ยท proc(5)

</aside>

A thread is a separate line of execution inside a process. One process, several things happening at once.

The whole subject comes down to one question: what is shared, and what is not.

<aside> ๐Ÿค

Shared by all threads

The memory โ€” heap, globals, everything

Open file descriptors

The current directory

Signal handlers

The user and group IDs

The PID

</aside>

<aside> ๐Ÿ”’

Private to each thread

Its own stack

Its own CPU registers

Its own thread ID

Its own errno

Its own signal mask

Its own scheduling priority

</aside>

That left-hand column is the whole point of threads, and the whole danger of them. Two threads can read and write the same variable with no system call, no copying and no coordination. That is why threads are fast. It is also why Part B exists.

Compare with fork from Module 02, where copy-on-write gave each process its own memory. Two processes cannot corrupt each other's variables. Two threads can, easily.

<aside> ๐Ÿงฉ

Real-world analogy โ€” flatmates and neighbours

Processes are neighbours. Separate houses, separate front doors, separate fridges. To give your neighbour something you have to actually carry it round โ€” that is inter-process communication, and it is deliberate work. The upside is that whatever happens in their kitchen cannot spoil your dinner.

Threads are flatmates. One flat, one kitchen, one fridge, one front door. Everything in the shared space is available to everyone instantly, with no arranging.

Each flatmate still has their own bedroom โ€” that is the stack. Your own things, your own place in what you are doing, and nobody else goes in there.

The trade is obvious once you put it that way. Sharing a fridge is far more convenient than carrying food between houses. It also means somebody can drink your milk, and neither of you did anything wrong โ€” you just both assumed it would be there.

Where the analogy stops working, and it matters. A flatmate who sets the kitchen on fire affects the flat; the neighbours are fine.

With threads there is no such boundary. One thread crashing takes the whole process down, every thread with it, because they share one address space. That is the real safety difference between threads and processes, and it is why browsers put tabs in separate processes rather than threads.

</aside>

๐Ÿงช Exercise A1.1 โ€” Count threads, not processes

# How many PROCESSES are on this machine?
ps -e --no-headers | wc -l

# How many THREADS? (-L shows one line per thread)
ps -eL --no-headers | wc -l

# Which programs have the most threads?
ps -eLo pid,comm --no-headers | awk '{c[$1" "$2]++} END {for (k in c) print c[k], k}' \
  | sort -rn | head -6

# A process's threads are listed in /proc/PID/task
ls /proc/1/task
grep -E '^(Name|Pid|Threads)' /proc/1/status

A2 ยท How Linux really implements threads

<aside> ๐Ÿ“–

Official docs: clone(2) โ€” CLONE_VM, CLONE_FILES, CLONE_THREAD ยท gettid(2) ยท pthreads(7)

</aside>

Here is the part that makes Linux threads click, and it follows directly from Module 02.