230 likes | 254 Views
Monitors: An Operating System Structuring Concept. Paper by: C. A. R. Hoare Presented by: Sabrina Brick. Why monitors?. Concurrency has always been an OS issue Resource allocation is necessary among competing processes Timer interrupts
E N D
Monitors: An Operating System Structuring Concept Paper by: C. A. R. Hoare Presented by: Sabrina Brick
Why monitors? • Concurrency has always been an OS issue • Resource allocation is necessary among competing processes • Timer interrupts • Existing synchronization mechanisms (semaphores, locks) are subject to hard-to-find, subtle bugs.
What is a monitor? • A collection of data and procedures • Mutual exclusion • allows controlled acquisition and release of critical resources • single anonymous lock automatically acquired and released at entry and exit • Data encapsulation • monitor procedures are “entry points” for accessing data
Monitors: a language construct • Monitors are a programming language construct • anonymous lock issues handled by compiler and OS • detection of invalid accesses to critical sections happens at compile time • process of detection can be automated by compiler by scanning the text of the program
An Abstract Monitor name: monitor …local declarations …initialize local data proc1 (…parameters) …statement list proc2 (…parameters) …statement list proc3 (…parameters) …statement list
Rules to Follow with Monitors • Any process can call a monitor procedure at any time • But only one process can be inside a monitor at any time (mutual exclusion) • No process can directly access a monitor’s local variables (data encapsulation) • A monitor may only access its local variables • Why? Race conditions could occur! “Chaos will ensue”!
Enforcing the Rules • “wait” operation • current process is put to sleep • “signal” operation • wakes up a sleeping process • condition variables • May have different reasons for “waiting” or “signaling” • Processes waiting on a particular condition enter its queue
car: monitor occupied: Boolean; occupied := false; nonOccupied: condition; procedure enterCar() if occupied then nonOccupied.wait; occupied = true; procedure exitCar() occupied = false; nonOccupied.signal; What possible problem exists? monitor declaration local variables / initializations procedure procedure Sabrina’s Car: a running example
The Possible Problem • As discussed in Birrell’s paper (last class): • First process: • Completes it’s work with critical section • Signals a process waiting on “nonOccupied” • Starts to release the monitor when… • Second process: • Is awakened by first process • Tries an “entry point” into the monitor • First process isn’t finished releasing it so it blocks again! • This can’t happen with Hoare’s approach: • Signal must be the last operation in a monitor. • Signal and monitor exit are a combined operation.
Multiple Conditions • Sometimes it is necessary to be able to wait on multiple things • Can be implemented with multiple conditions • Example: 2 reasons to enter car • drive (empties tank) • fill up car • Two reasons to wait: • Going to gas station but tank is already full • Going to drive but tank is (almost) empty
Sabrina’s Car: a running example car: monitor emptyTank, fullTank: condition; tank: stack; tank := stack of 10 gallons; //10 gallon tank //filled in increments of a gallon procedure driveCar() if tank.isAlmostEmpty then fullTank.wait; tank.pop(); //consume gas in increments of a gallon emptyTank.signal; procedure fillCar() if tank.isFull then emptyTank.wait; tank.push(gallons.pop); //assume gas station “stack” has infinite supply fullTank.signal;
Condition Queue • Might want to check if any process is waiting on a condition • The “condition queue” returns true if a process is waiting on a condition • Example: filling the tank only if someone is waiting to drive the car.
Sabrina’s Car: a running example car: monitor emtpyTank, fullTank: condition; tank: stack; tank := stack of 10 gallons; //10 gallon tank //filled in increments of a gallon procedure driveCar() if tank.isAlmostEmpty then fullTank.wait; tank.pop(); //consume gas in increments of a gallon emptyTank.signal; procedure fillCar() if tank.isFull then emptyTank.wait; if fullTank.queue //returns true if a process is waiting on fullTank tank.push(gallons.pop); //assume gas station “stack” has infinite supply fullTank.signal;
Priority: Scheduled Waits • A waiting process is given a number • The process with the lowest number gets woken up first • Example: People who want to drive my car are prioritized: • Highest: Me! • Medium: Family • Lowest: Friends/Others
Sabrina’s Car: a running example car: monitor emtpyTank, fullTank: condition; tank: stack; tank := stack of 10 gallons; //10 gallon tank //filled in increments of a gallon procedure driveCar(p: person) if tank.isAlmostEmpty then if p.isSabrina then fullTank.wait(1); if p.isFamily then fullTank.wait(2); if p.isFriendorOther then fullTank.wait(3); tank.pop(); //consume gas in increments of a gallon emptyTank.signal; procedure fillCar() if tank.isFull then emptyTank.wait; if fullTank.queue tank.push(gallons.pop); //assume gas station has infinite supply fullTank.signal;
Other issues • Power of Monitors • Monitor->semaphore and vice versa • Proof rules • I {b.wait} I&B • I&B {b.signal} I I : the invariant B : the condition that a process waiting on b wants to be true before its woken up b : the condition variable
Lots of Great Examples • OS Examples: • Bounded Buffer • Alarm clock • Disk head scheduler • Reader/writer • Buffer Allocation
Monitors and Granularity • Monitors are designed for course-grained locking • Example: locking an entire data structure rather than its individual components. • Is this an appropriate solution for all cases? • Why or why not? • What problems arise when the granularity is too coarse? What about the opposite case?
Disabling Interrupts car: monitor occupied: Boolean; occupied := false; nonOccupied: condition; procedure enterCar() //disableInterrupts(); if occupied then nonOccupied.wait; occupied = true; //enableInterrupts(); procedure exitCar() //disableInterrupts(); occupied = false; nonOccupied.signal; //enableInterrupts(); “Yellow code” inserted into binary by compiler Which is the better approach? Why? Using a lock car: monitor occupied: Boolean; occupied := false; nonOccupied: condition; //lock1: lock; procedure enterCar() //lock1.acquire(); if occupied then nonOccupied.wait; occupied = true; //lock1.release(); procedure exitCar() //lock1.acquire(); occupied = false; nonOccupied.signal; //lock1.release(); //lock1.acquire() //disableInterrupts(); //lockVar = 1; //enableInterrupts(); Monitors: implementation issues
What problems do monitors solve? • Mutual exclusion • Encapsulation of data • Compiler can automatically scan program text for some types of synchronization bugs • Synchronization of shared data access simplified vs. semaphores and locks • Good for problems that require course granularity • Invariants are guaranteed after waits • Theoretically, a process that waits on a condition doesn’t have to retest the condition when it is awakened.
What remains problematic? • No way to check dynamically allocated shared data • Signals are as error prone as with other synchronization mechanisms • Deadlock can still happen in monitor code • Programmer can still screw up • Monitors are available in very few languages
Conclusions • Monitors are a synchronization mechanism • A higher level, easier to use abstraction, better encapsulation than semaphores, etc. • Monitors still suffer from various problems • Let’s take a look at working model that addresses some of those issues! (Suzanne’s presentation)
References • Jon Walpole • “Monitors: An Operating Systems Structuring Concept” Hoare. • Modern Operating Systems, Second Edition. Tannenbaum, pp. 115-119. • Emerson Murphy-Hill presentation from CS533, Winter 2005 • http://en.wikipedia.org/wiki/C._A._R._Hoare