The difference between threads and processes used to confused me. Why do we need threads since we already have processes to support concurrency? What’s the mechanism of the context switch between threads?(rather than processes) How is locks implemented? This article may be your answer.
Acknowledgement: Some of the sentences and graphs are taken from <Operating System Concepts 7th edition> written by Silberschatz, G. G. and the sildes of CS422 conducted by Zhong Shao, Yale University.
Early computer only allowed one program to be executed at a time. This program had complete control of the CPU and had access to all the system’s resources. It works and is simple, intuitive, but nasty.
Actually it’s unfair to claim that a CPU running only one program is nasty. Indeed, it’s often the case in embedded system that the MCU (micro control unit) only runs one program with the support of several interrupt vectors. But it is another story which won’t be covered in this article.
There are several cons if a computer can only run one program at a time:
- The speed of I/O is dramatically slow compared to the speed of CPU. CPU has to halt to wait for I/O.
- It doesn’t support multiple tasks. Say I can’t type this article and listen to music at the same time.
Context Switch between processes
PCB(process control block), Page table, Open file handles all need to be stored and switched.
Context switch between threads is much more efficient comparing with that between processes. The overhead of creating a thread is also much smaller.
Pros of using a thread
- The overhead of creating a thread is small. We don’t need to create a new page table for it. All the threads within a same process shares a same page table.
- The context switch of threads is faster, which leads to high responsive ability. Say, in server softwares, one thread listens to an port. Whenever a new request of a client comes, it will spawn a new thread to handle this request.
- Share data(communicate)
- All threads within the same process shares heap space together.
Context switch in threads
4 functions related to context switch
- thread_create(thread, func, args) – create a new thread to run func(args)
- thread_yield() – relinquish processor voluntarily
- thread_join(thread) – In parent, wait for forked thread to exit, then return
- thread_exit() – quit thread and clean up, wake up joiner if any
Thread context can be classified into two types:
- private state
- program counter
- contents of memory (global variables, heap)
- file system
Classifying program variables
int x; // global variable
int y; //stack variable
x = 1;
y = 1;
p = (int *)malloc(sizeof(int));
* p = 1; //head access
- Addresses of global variables are defined at link-time
- Addresses of heap variables are defined at run-time(malloc-time)
- Addresses of stack variables are defined at call-time
Thread control block (TCB)
TCB stores the following info
- current state
- ready: ready to run
- running: currently running
- waiting: waiting for resources(I/O, locks, etc.)
- program counter(EIP)