Process Synchronization
Notes
- This is chapter 6 and 7 of the book.
- Plus several chapters in Kerrisk
- I will probably use the term parallel wrong here
- We will be looking at computations that have multiple streams of instructions running simultaneously
- This could be a the process level
- Or at the thread level.
- We have yet to discuss threads, but let's consider threads and processes the same for now.
- In any case, in a multi-stream computation, there frequently are points where the streams need access to a shared resource
- Up to now we have avoided this
- But avoiding this limits our freedom of computation.
- Examples could be a shared memory location or a queue.
- A critical section is a point in multi-stream code where we wish to limit access to limited resource.
- Controlling access to such a resource is the critical section problem
- As we have discussed, if we don't do this properly, we could have errors due to data corruption.
- Assume two processes are running the following code
section .data sum: dq 0 section .text ... A mov rax, sum B inc rax C mov sum, rax ...- In this case assume sum holds 0
- Some possible cases
- What we would like to see
process 1 rax process 2 rax sum mov rax, sum 0 swapped out ? 0 inc rax 1 swapped out ? 0 mov sum, rax 1 swapped out ? 1 swapped out ? mov rax, sum 1 1 swapped out ? inc rax 2 1 swapped out ? mov sum, rax 2 2 - What we don't want to see
process 1 rax process 2 rax sum mov rax, sum 0 swapped out ? 0 inc rax 1 swapped out ? 0 swapped out 1 mov rax, sum 0 0 swapped out 1 inc rax 1 0 swapped out 1 mov sum, rax 1 1 mov sum, rax 1 swapped out 1 1
- What we would like to see
- There are other symmetric cases and other similar cases.
- The problem is we don't know when our processes/threads will be interrupted.
- This is demonstrated in broken.cpp
- The general structure of the critical section problem is
while (true) { beginning section entry into critical region critical section exit critical section remainder section } - Solutions to the problem must
- support mutual exclusion: if a process is executing a critical section, no other process can be be
- maintain progress: only processes waiting to enter a critical section can participate in deciding what process goes into the critical section next and all processes will eventually be able to enter the critical section.
- maintain a bounded waiting time: or limit the amount of time a process must wait (in terms of other processes entering the critical section) before it can enter the critical section.
- There are many places, even in the kernel, where a race condition can occur
- Consider two processes on a multiprocessor forking at the "same time."
- Each needs a new pid for the child process.
- So there must be a critical section in the kernel for allocation of pid.
- There are many other places (allocating memory, hard drive space, file descriptors, selecting the "next" process from a queue, ...)
- If there is a single kernel, running on only one processor this is not a problem.
- But if there are multiple versions of the kernel, (which there are in the end), there is a problem.
- preemptive kernels can be interrupted and have race conditions
- non-preemptive kernels can not be, but we still have a distributed problem.
- Consider two processes on a multiprocessor forking at the "same time."
- If you care, look at pid.c from the kernel code
-
alloc_pid()is the routine that allocates a process id - In multiple places spin_lock and spin_unlock are called.
- These are implmented with atomic instructions.
-