Preparation for
MIT-OSE Lab4Ben
2012/05/29
● Locking● Scheduling
● Coordination and processes
● Locking● Scheduling● Coordination and processes
Abstract SMP architecture
● processors with one shared memory ● devices ● interrupts processed in parallel
Avoiding list race
insert(int data){ List *l = new List; l->data = data;
l->next = list ; // A list = l; // B
}
Lock list_lock; // one per list
insert(int data){ List *l = new List; l->data = data;
acquire(&list_lock);
l->next = list ; // A list = l; // B
release(&list_lock);}
● multiple processes adding to the disk queue
critical section, or atomic section
Race Condition
TimeA
A B
BCPU1
CPU2
Memoryl->next
l->next
list
list
Implementing lock and release
● use atomic instructions (e.g., xchg)For example, the x86 <tt>xchg %eax, addr</tt> instructiondoes the following: freeze other CPUs' memory activity for address addrtemp := *addr*addr := %eax%eax = tempun-freeze other memory activity
x86 asm to implement a spinlocklock: ; The lock variable. 1 = locked, 0 = unlocked. dd 0 spin_lock: mov eax, 1 xchg eax, [lock] ; Atomically swap EAX register with lock variable. test eax, eax jnz spin_lock ret spin_unlock: mov eax, 0 ; Set the EAX register to 0. xchg eax, [lock] ; Atomically swap EAX register with lock variable. ret ; The lock has been released.
Design Issues - locks and interrupts
● ide disk generates interrupt when disk operation completes
● interrupt causes ideintr to run● ideintr acquires lock?
deadlock? give interrupt handler lock? (recursive locks)
● xv6: critical sections have no interrupts turned on
Design Issues - locks and modularity
move(l1, l2) {
e = del(l1) insert(l2, e)
}
move(l1, l2) { acquire(l1.lock); acquire(l2.lock); e = del(l1) insert(l2, e) release(l1.lock) release(l2.lock) }
● recursive locks are a bad ideaideintr should certainly not use that instead of disabling interrupts!
move(l1, l2) { acquire(l1.lock); e = del(l1) release(l1.lock) acquire(l2.lock); insert(l2, e) release(l2.lock) }
Modifiedrecursiveoriginal
● Locking● Scheduling● Coordination and processes
Goals for solution
● Switching transparent to user threads● User thread cannot hog a processor
Code - Concurrency
- plock held across switch; why? yield: p is set runnable, p must complete switch before another scheduler chooses p
- hard to reason about; coroutine style helps
- can two schedulers select the same runnable process? - why does scheduler release after loop, and re-acquire it immediately? (run with interrupts!)
Code - Thread clean up
● let's look at kill: can we clean up killed process? (no: it might be running, holding locks etc.) before returning to user space: process kills itself by calling exit
● let's look at exit; can thread delete its stack?
(no: it has to switch off it!)
● wait() does the cleanup
● Locking● Scheduling● Coordination and processes
Required reading: remainder of proc.c, sys_wait, sys_exit, and sys_kill.
Coordination and more processes
Big picture
Multiple threads executing in the kernel and sharing memory, devices and various data structures. ● Allow more than one process to be
executing to take advantage of multiple CPUs
● Allow system calls that block waiting for I/O.
producer consumer queuestruct pcq { void *ptr;};
void*pcqread(struct pcq *q){
while((p=q->ptr)==0); q->ptr = 0; return p;}
pcqwrite(struct pcq *q, void *p){ while(q->ptr != 0); q->ptr = p;}
Need lock for pcq->ptr if multiple processors
Waste CPU Time. Instead of polling,Use event-based (sleep and wakeup)
Sleep & wakeupsleep(chan): curproc->chan = chan curproc->state = SLEEPING sched()
wakeup(chan): foreach p in proc[]: if p->chan == chan and p->state==SLEEPING: p->state = RUNNABLE
sleep(void *chan, struct spinlock *lk){ struct proc *p = curproc[cpu()]; p->chan = chan; p->state = SLEEPING; release(lk); sched(); acquire(lk);}
wakeup(void *chan){ for(each proc p) { if(p->state == SLEEPING
&& p->chan == chan) p->state = RUNNABLE; }}
producer consumer queue (cont.)struct pcq { void *ptr;};
void*pcqread(struct pcq *q){
while((p=q->ptr)==0); q->ptr = 0; return p;}
pcqwrite(struct pcq *q, void *p){ while(q->ptr != 0); q->ptr = p;}
struct pcq { void *ptr; struct spinlock lock; };
void* pcqread(struct pcq *q) { acquire(&q->lock); while(q->ptr == 0)sleep(q, &q->lock); p = q->ptr; q->ptr = 0; wakeup(q); /* wake pcqwrite */ release(&q->lock); return p;}pcqwrite(struct pcq *q, void *p) { acquire(&q->lock); while(q->ptr != 0) sleep(q, &q->lock); q->ptr = p; wakeup(q); /* wake pcqread */ release(&q->lock); return p;}
Q & A
LockingSchduling
Threads
?