<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>
<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
<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.