MIT6.S081 Lecture11 Machinery about synchronize
Lec10 Machinery about synchronize
Today’s lecture talks about some synchronize mechanism, from lock, condition to wait to give a clear concept of what these mechanisms really effect.
Diagram about switch
To avoid deadlock and repeat processes while doing switch, kernel needs a right order to lock and unlock.
- We need to make sure no other locks during swtch.
- Assume we have just 1 CPU and once p1 switch with 1 lock to p2, if p2 also tring to acquire the lock-> deadlock
- And acquire() turns off interrupt to avoid another deadlock…(because in interrupt handler, it also needs lock)
Coordination – wake and sleep
To make thread wait on specific condition or event.
Given a simple example synchronize code:
1 | static int tx_done; // has the UART finished sending? |
UART driver use uartwrite()
to actually write character, we could find in code that they proceed 1 character 1 time so it needs to yield cpu instead of spin.
UART hardware will raise interrupt to uartintr
then wakeup uartwrite()
to consume character.
lost wakeup is situation that one process sends wakeup signal but missed somehow by receiver. It is caused by mistakes on adding lock. If we replace sleep() by broken_sleep() which just takes one parameter indicating the sleep channel.
1 | void |
Previous snippet will like:
1 | while(tx_done == 0){ |
So we need to do this three line atomically in sleep().
1 | void |
In the wakeup, we have to check proc table preceding with acquiring p’s lock. So sleep with firstly get p’s lock then release lk
to make sure all steps are atomic.
- Actually, semaphore is a more easier understaning way to use. Because caller have no worry about lost wakeups. (But internal semaphore, it takes good care about it)
exit and kill
As a process exits, we have to free memory, free pagetable and trapframe, clean up states, free stack…
- We cannot kill another thread directly, because it may in some critical area.
- In exit(), the process should reparent its children and set its state into ZOMBIE
- Parent should explicitly use wait() to reap zombie children.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33wait(uint64 addr){
...
for(;;){
// Scan through table looking for exited children.
havekids = 0;
for(np = proc; np < &proc[NPROC]; np++){
// this code uses np->parent without holding np->lock.
// acquiring the lock first would cause a deadlock,
// since np might be an ancestor, and we already hold p->lock.
if(np->parent == p){
// np->parent can't change between the check and the acquire()
// because only the parent changes it, and we're the parent.
acquire(&np->lock);
havekids = 1;
if(np->state == ZOMBIE){
// Found one.
if(addr != 0 && copyout(p->pagetable, addr, (char *)&np->xstate,
sizeof(np->xstate)) < 0) {
release(&np->lock);
release(&p->lock);
return -1;
}
freeproc(np);
release(&np->lock);
release(&p->lock);
return pid;
}
release(&np->lock);
}
}
}
...
}