Ami Domadia cs162-as Feb 11 2009 CS162 Lec: Monitors, Semaphore Implementation & Disabling Interrupts Class Announcements: Initial Design Document for Project 1 due Tues 2/17/09 11:59pm Signup for Design Review with Jingtao for Tues 2/17/09 or 2/18/09 on the class website Readings: Read Monitors (Hoare article and Textbook-6.7) Read Semaphore Implementation (Textbook-6.5) Monitors How are monitors used to solve standard synchronization problems? Example: + Readers and writers problem with monitors: each synchronization operation gets encapsulated in a monitored procedure: checkRead(), checkWrite(), doneRead() and doneWrite(). Use conditions O KToRead and OKToWrite. - This is all part of one monitor. - If any process wants to read/write, it calls the below procedures. + checkRead() { if ((AW+WW) > 0) { WR = WR+1; wait(OKToRead); WR =WR-1; } AR = AR+1 ; READ } + doneRead() { AR = AR-1; if (AR==0 & WW>0) signal(OKToWrite); } + checkWrite() { while ((AW+AR) > 0) { WW = WW+1; wait(OKToWrite); WW = WW-1; } AW += 1; WRITE } + doneWrite() { AW -= 1; if (WW > 0) signal(OKToWrite); else broadcast(OKToRead); } - This method of synchronization uses the same logic as semaphores. It provides the same efficiency, just using a different structure to organize the logic. - This example demonstrates what monitors are and how they are used. + Producers and Consumers (from Hoare; fixed): bounded buffer: monitor begin buffer: array 0..N-1 of portion last pointer:0..N-1; count:0..N; nonempty, nonfull: condition; + procedure append(x; portion); begin if count==N then nonfull.wait; buffer[lastpointer] =x; last pointer = (lastpointer + 1) mod N count = count+1; nonempty.signal end append; + procedure remove (result x; portion); begin if count==0 then nonempty.wait; x=buffer[(lastpointer - count) mod N]; count=count-1; nonfull.signal; end remove count=0; lastpointer=0; end bounded buffer - In this case, the monitor ensures that there is only 1 process running at a time. - Since the processes wait on 2 condition variables: nonfull and nonempty. + Disk Head Scheduler (from Hoare (not fixed)): procedure request- called before issuing command to move head to dest. procedure release - called after cylinder is finished. headpos - current location of head sweep - up or down - direction of head movement busy - whether disk is busy diskhead: monitor begin: headpos: cylinder; direction: (up, down); busy: Boolean; upsweep, downsweep: condition; + procedure request(dest:cylinder); begin if busy then [if {((headpos < dest) or [headpos == dest & direction==up]) then upsweep.wait(dest) else downsweep.wait(dest)}]; else [busy=true; headpos=dest;] end request; + procedure release; begin busy=false; if direction==up then if {upsweep.queue then upsweep.signal else {direction=down; downsweep.signal}} else if downsweep.queue then downsweep.signal else {direction=up; upsweep.signal} end release; headpos=0; direction=up; busy=false; end diskhead - This is essentially, an elevator algorithm. - The head moves up if there are any pending requests, else turns around and moves in the other direction. Q. Why we need monitors for disk head scheduler? - Need synchronization because we get requests coming in and only 1 request can be serviced at a time. - When it is finished processing a request, call release(). Q. What does ' then upsweep.wait(dest)' do? - If the incoming request is above the disk head, then add it to the thread queue for the upsweep condition variable i.e. wait on the upsweep condition variable. Q. How is the order in any direction is decided? - It depends on how the queues are organized. + Summary: + Not present in very many languages, but extremely useful. + Semaphores use a single structure for both exclusion and scheduling, monitors use different structures for each. + Monitors implement mutual exclusion at the entry and implement scheduling in the body. + Monitors enforce a style of programming where complex synchronization code doesn't get mixed with other code: it is separated and put in monitors. + A mechanism similar to wait/signal is used internally in Unix for scheduling OS processes. + Unix: + Unix uses generalized semaphores. They are created in sets. Several operations on semaphores can be done simultaneously, and increments and decrements can be by values greater than 1. The kernel does these operations atomically. + Associated with each semaphore is a queue of processes suspended on that semaphore. + The semop system call takes a list of semaphore ops (sem-op), each defined on a semaphore in the set, and does them one at a time. + If sem-op is positive, kernel increments value of semaphore and awakens all processes waiting for the value of the semaphore to increase. + If sem-op is zero, kernel checks semaphore value. If 0, continues with list. Otherwise, blocks process, and has it wait on this semaphore. + If sem-op is negative, and its absolute value is less than or equal to the semaphore value, the kernel adds sem-op (a negative number) to the semaphore value. If the result is 0, the kernel awakens all processes waiting for the value of the semaphore to equal 0. + If sem-op is negative, and its absolute value is greater than the semaphore value, the kernel suspends the process until the value of the semaphore increases. + It is not very clear why semop system was built to use in unix. It was created in the course of trying to improve the efficiency. Semops are built above semaphores. + Unix also uses signals. Processes may send each other signals, which are software interrupts. A signal is processed when a process wakes up, or when it returns from a system call. There are about 20 defined signals. (e.g. interrupt, quit, illegal instruction, trace trap, floating point exception, kill, bus error, segmentation violation, write on a pipe with no readers, alarm clock, death of a child, power failure, etc.) + Signals are software interrupts. This mechanism makes sense. + At some level, all processes are called by the shell so every process is a child process. Semaphore Implementation + Semaphores are complex and require implicit scheduling. There is no hardware implementation. + They are too complicated and hence we don.t want scheduling in hardware. + They are too hard to make atomic. This would take a lot of instructions. + It takes too long to disable interrupts. + We cannot disable traps. + All interrupts cannot be disabled. + We need a way to do mutual exclusion to implement P & V operations. Q: How are interrupts disbaled? Where are they processed? + Interrupts/traps have to kick you into the kernel mode. We process interrupts in the kernel mode. + Sometimes, the kernel gives the user program to handle interrupts such as floating point overflow. + Interrupts like I/O machine check, timer, hardware interrupts are processed in the Supervisor mode. + How does the user process interrupts? + User programs can't disable interrupts. They have to do system call to do P & V. + User programs can have mask interrupt flags. Q: How do user programs mask interrupts? + When an interrupt comes in, it is masked (AND with another number) and then sets a bit to indicate an interrupt occurred (interrupt flag). + After the critical section is executed, the user program checks the interrupt flag and calls the interrupt handler. Q: When you would want to mask interrupts? + When you are in the middle of processing another interrupt. + The Operating system deals with hardware interrupts. + Busy waiting implementation + Most machines provide atomic 'Read-Modify-Write' instruction. This instruction is used to implement atomic operations and this makes it easier to do critical sections. Now we can do busy-waiting implementation of P & V. Example: Atomic -> {increment value in memory} and { load incremented value} Atomic-> {decrement value in memory} When we do these atomic operations, in hardware, it basically locks the bus. For busy waiting: .Init A=0 (START OF SYSTEM) .loop: {incr A in mem, load A} if(A~=1), {decr A in mem} goto loop .Critical section here .{decr mem location of A} Problems: + Uses CPU time but P & V are not very big so it is ok. + Bug: Infinite loop ->3 processes will cause indefinite postponement. For 3 processes, if 1 leaves, value oscillates between 0,1,2. Example-Improvement#1: SWAP lock -> global variable local->every process has a local swap(local(i), lock)-> interchanges values of 2 variables. lock = true (if locked) Busy waiting loop: .Init lock=false (START OF SYSTEM) .local(i)=true .repeat swap(local(i),lock) until local(i)==false .critical section here .lock=false + Swapping is slower than store. +Assumption: swap is atomic. Problems: + May not get lock in finite time, if lot of processes but stochastically it should get the lock sometime. + It is easier to do synchronization with atomic swap. + Before each process loops, its local has to be set to true. Example-Improvement#2: Test and Set This method was used by IBM's mainframe S/360 .Tset(local(i), lock): {local(i)=lock, lock=true} .For process i: .busy waiting loop .init lock=false .repeat(Tset(local(i),lock), until local(i)==false) .critical section .lock=false Q: How to implement atomic instructions in hardware? + Using the memory or in the processor by refusing to release memory. + Using TSet to implement semaphores. For each semaphore, keep a Tset integer in addition to semaphore integer and queue of waiting processes. Q: How much busy waiting can be allowed? + It depends on the duration of the repeated operation. + For multiprocessor system, S->Semaphore P(S): If(S>0) (not Blocked => execute) return else //(blocked=>waiting) call dispatcher //to run some one else V(S): Disable interrupts local(i)=true repeat (Tset(local(i),lock), until local(i)==false) if(Semaphore queue is empty) S=S+1 else remove a process from Semaphore queue and wake it. S.lock=false Enable interrupts Q: Why do we disable interrupts? + We don't have to disable interrupts, but we still do it. + So that nobody ca enter the critical section. + Atomic operations are built on existing atomic operations. There has to be at least 1 atomic operation implemented in hardware to start with. + So the layering of these concepts is important: ------------------------------------- READ/WRITE --------------------------------------- P & V --------------------------------------- TSET -------------------------------------- +NEXT LECTURE------------->DEADLOCKS