Download presentation
Presentation is loading. Please wait.
1
Multi-programming in THE
Landon Cox January 25, 2017
2
THE (First SOSP ’67) SIGOPS Hall of Fame summary “The first paper to suggest that an operating system be built in a structured way. That structure was a series of layers, each a virtual machine that introduced abstractions built using the functionality of lower layers. The paper stimulated a great deal of subsequent work in building operating systems as structured systems.”
3
Across systems categories
Many common, important problems Fault tolerance Coordination of concurrent activities Geo. separated but linked data Large-scale data sets Protection from mistakes and attacks Interactions with many people All of these problems lead to complexity
4
Complexity How do we control complexity?
Build abstractions that hide unimportant details Establish conventional interfaces Enable composition of simple, well-defined components None of these ideas a specific to computer systems These are just principles of good engineering Civil engineering, urban planning, mechanical engineering, aviation and space flight, electrical engineering, ecology and political science
5
Dealing with complexity
How do you impose order on complex programs? Break functionality into modules Goal is to “decouple” unrelated functions Narrow the set of interactions between modules Hope to make whole system easier to reason about How do we specify interactions between code modules? Procedure calls int foo(char *buf) How do procedure calls reduce complexity? They make interface between modules explicit They hide implementation details Source:
6
Dealing with complexity
P(a){…} C(){… y=P(x); …} C and P communicate via a shared, in-memory stack What is on the stack? Stack: regs, args, RA, P's vars, maybe args ... C: push regs, push args, push PC+1, jmp P, pop args, pop regs, ... R0 P: push vars, ..., pop vars, mov xx -> R0, pop PC The call contract P returns P returns to where C said P restores stack pointer P doesn't modify stack (or C's other memory) P doesn't wedge machine (e.g., use up all heap memory) Source:
7
Dealing with complexity
P(a){…} C(){… y=P(x); …} Functions are programming-language constructs Can you think of a PL construct that shatters modularity? goto Inspired famous article by Dijkstra in CACM in 1968 “Go To Statement Considered Harmful” Several months earlier … “Structure of THE multi-programming system” "The unbridled use of the goto statement has as an immediate consequence that it becomes terribly hard to find a meaningful set of coordinates in which to describe the process progress. ... The goto statement as it stands is just too primitive, it is too much an invitation to make a mess of one's program.” Source:
8
THE-multiprogramming system
An operating system is a very complex program Has to manage other processes Has to handle interrupts Has to handle I/O devices Has to handle synchronization primitives How THE handles all of this complexity Breaks functionality into stacked layers Higher layers depend on lower layers How are layers different than function calls? Function can call each other. Lower layers cannot invoke code in higher layers, layers can only invoke code in layer immediately below it
9
THE-multiprogramming system
What is a layer? A layer is a set of related procedure calls or functionality Represents a level in a hierarchy How do layers differ from procedure calls w/in a program? Code can only call within layer and into layers below Can build a layer without worrying about what happens above Why is this additional structure appealing? Further limits how modules interact Reduces dependencies between modules Hopefully reduces complexity and bugs Do not have to worry about code in Layer 1 calling into Layer 4 How are layers different than function calls? Function can call each other. Lower layers cannot invoke code in higher layers, layers can only invoke code in layer immediately below it
10
THE-multiprogramming system
Batch system Users submit programs OS decides what to run 5 user processes at a time Multi-tasking Manage multiple programs concurrently Need to switch between programs
11
Multi-programming Want more than 1 process in phys memory
Processes can have same virtual addrs Cannot share physical addrs Addresses must be translated Programs cannot manipulate phys addrs anymore Statically: occurs before execution Dynamically: occurs during execution Protection becomes harder
12
Static address translation
Want two processes in memory With address independence Without translating on-the-fly Using static translation How to do this?
13
Static address translation
Adjust loads/stores when put in memory This is called a linker-loader Programming Assume memory starts at 0 Linker-loader Adds 0x20000 to offsets for P2 Similar to linking phase of compile fffff (high memory) . OS kernel 80000 . 3ffff . User process 2 20000 1ffff . User process 1 00000 (low memory) load $1, $base + offset
14
Layer 0 Layer 1 Layer 2 Layer 3 Layer 4 User programs I/O devices Console Pager Scheduler
15
Layer 0 Primary responsibilities of Layer 0
Ensure efficient, fair allocation of processor (government) Hide number of processors (abstraction layer) How did the scheduler ensure fair allocation? Handled all transitions between processes Some were voluntary transitions (synchronization calls) Some were involuntary transitions (timer interrupts) Could Layer 0 code be swapped out? No, timer-interrupt handler always has to be in memory
16
Layer 1 Primary responsibilities of Layer 1
Ensure efficient, fair allocation of memory (government) Hide drum addresses (abstraction layer) What were a segment, core page, and drum page? Segment = coarse-grained unit of data, visible to program Core page = in-memory container for a segment Drum page = on-disk container for a segment
17
Layer 1 How were processes isolated?
No hardware support for virtual memory Relied on language and compiler Programs written in Algol used private segment identifiers Like the static address translation discussed last time Could Layer 1 code be swapped out? No, memory manager always has to be in memory Otherwise have a serious bootstrapping problem
18
Layer 1 How did paging work without hardware support?
Similar to way compiler handles procedure calls Compiler needed to know which segments to be accessed Compiler could insert calls into layer 1 to page-in segments Was any of this enforced? No, just like procedure-call contract is not enforced At the lowest-level, could easily corrupt other processes Bugs and attacks could crash the whole system
19
Coordination of processes
“A society of harmonious sequential processes” Each layer implemented as a process To move between layers, need way to transition safely What primitives provided safe switching? Semaphores: Up (V) and Down (P) Down (may) block the calling process Up (may) allow another process to be scheduled
20
Semaphores First defined by Dijkstra for THE
Two operations: up and down // aka “V” (“verhogen”) up () { // begin atomic value++ // end atomic } // aka “P” (“proberen”) down () { do { // begin atomic if (value > 0) { value-- break } // end atomic } while (1) What is going on here? Can value ever be < 0?
21
More semaphores Key state of a semaphore is its value
Initial value determines semaphore’s behavior Value cannot be accessed outside semaphore (i.e., there is no semaphore.getValue() call) Semaphores can be both synchronization types Mutual exclusion (like locks) Ordering constraints (like monitors)
22
Semaphore mutual exclusion
Ensure that 1 (or < N) thread is in critical section How do we make a semaphore act like a lock? Set initial value to 1 (or N) Like lock/unlock, but more general (could allow 2 threads in critical section if initial value = 2) Lock is equivalent to a binary semaphore s.down (); // critical section s.up ();
23
Semaphore ordering constraints
Thread A waits for B before proceeding How to make a semaphore behave like a monitor? Set initial value of semaphore to 0 A is guaranteed to wait for B to finish Doesn’t matter if A or B run first Like a CV in which condition is “sem.value==0” Can think of as a “prefab” condition variable // Thread A s.down (); // continue // Thread B // do task s.up ();
24
Prod.-cons. with semaphores
Same before-after constraints If buffer empty, consumer waits for producer If buffer full, producer waits for consumer Semaphore assignments mutex (binary semaphore) fullBuffers (counts number of full slots) emptyBuffers (counts number of empty slots)
25
Prod.-cons. with semaphores
Initial semaphore values? Mutual exclusion sem mutex (?) Machine is initially empty sem fullBuffers (?) sem emptyBuffers (?)
26
Prod.-cons. with semaphores
Initial semaphore values? Mutual exclusion sem mutex (1) Machine is initially empty sem fullBuffers (0) sem emptyBuffers (MaxSodas)
27
Prod.-cons. with semaphores
Semaphore mutex(1),fullBuffers(0),emptyBuffers(MaxSodas) consumer () { down (fullBuffers) down (mutex) take soda out up (mutex) up (emptyBuffers) } producer () { down (emptyBuffers) down (mutex) put soda in up (mutex) up (fullBuffers) } Use one semaphore for fullBuffers and emptyBuffers? Remember semaphores are like prefab CVs.
28
Prod.-cons. with semaphores
Semaphore mutex(1),fullBuffers(0),emptyBuffers(MaxSodas) consumer () { down (mutex) down (fullBuffers) take soda out up (emptyBuffers) up (mutex) } producer () { down (mutex) down (emptyBuffers) put soda in up (fullBuffers) up (mutex) } 2 1 Does the order of the down calls matter? Yes. Can cause “deadlock.”
29
Prod.-cons. with semaphores
Semaphore mutex(1),fullBuffers(0),emptyBuffers(MaxSodas) consumer () { down (fullBuffers) down (mutex) take soda out up (emptyBuffers) up (mutex) } producer () { down (emptyBuffers) down (mutex) put soda in up (fullBuffers) up (mutex) } Does the order of the up calls matter? Not for correctness (possible efficiency issues).
30
Prod.-cons. with semaphores
Semaphore mutex(1),fullBuffers(0),emptyBuffers(MaxSodas) consumer () { down (fullBuffers) down (mutex) take soda out up (mutex) up (emptyBuffers) } producer () { down (emptyBuffers) down (mutex) put soda in up (mutex) up (fullBuffers) } What about multiple consumers and/or producers? Doesn’t matter; solution stands.
31
Prod.-cons. with semaphores
Semaphore mtx(1),fullBuffers(1),emptyBuffers(MaxSodas-1) consumer () { down (fullBuffers) down (mutex) take soda out up (mutex) up (emptyBuffers) } producer () { down (emptyBuffers) down (mutex) put soda in up (mutex) up (fullBuffers) } What if 1 full buffer and multiple consumers call down? Only one will see semaphore at 1, rest see at 0.
32
Monitors vs. semaphores
Separate mutual exclusion and ordering Semaphores Provide both with same mechanism Semaphores are more “elegant” Single mechanism Can be harder to program
33
Monitors vs. semaphores
Where are the conditions in both? Which is more flexible? Why do monitors need a lock, but not semaphores? // Monitors lock (mutex) while (condition) { wait (CV, mutex) } unlock (mutex) // Semaphores down (semaphore)
34
Monitors vs. semaphores
When are semaphores appropriate? When shared integer maps naturally to problem at hand (i.e., when the condition involves a count of one thing) // Monitors lock (mutex) while (condition) { wait (CV, mutex) } unlock (mutex) // Semaphores down (semaphore)
35
Classic synchronization problem
Each barber/stylist is a thread Each customer is a thread Sofa capacity = 4 Standing room Only 9 customers in room at a time. Customer room capacity = 9
36
Try to come up with a solution We’ll talk about it in 10 minutes…
Stylist(int id) { } Customer () { } Break up into groups Try to come up with a solution We’ll talk about it in 10 minutes…
37
Salon with semaphores Is anything weird here? Customer () {
room.down () // enter room sofa.down () // sit on sofa chair.down () // sit on chair sofa.up () customer.up () cut.down () // leave chair chair.up () // leave room room.up () } Semaphore room = 9, sofa = 4, chair = 3, customer = 0, cut = 0; Stylist () { while (1) { customer.down() // cut hair cut.up () } Is anything weird here?
38
Enter room lock; roomCV; numInRoom=0; sofaCV; numOnSofa=0; chairCV;
Customer () { lock.acquire (); while (numInRoom == 9) roomCV.wait (lock); numInRoom++; while (numOnSofa == 4) sofaCV.wait (lock); numOnSofa++; while (findFreeChair () == NONE) chairCV.wait (lock); stylist = findFreeChair (); freeChairs[stylist] = 1; numOnSofa--; sofaCV.signal (lock); customerCVs[stylist].signal (lock); cutCVs[stylist].wait (lock); freeChairs[stylist] = 0; chairCV.signal (lock); numInRoom--; roomCV.signal (lock); lock.release (); } lock; roomCV; numInRoom=0; sofaCV; numOnSofa=0; chairCV; freeChairs[3]; customerCVs[3]; cutCVs[3]; Enter room Sit on sofa Sit in chair Wake up stylist Wait for cut to finish Leave the shop
39
Customer () { lock.acquire (); while (numInRoom == 9) roomCV.wait (lock); numInRoom++; while (numOnSofa == 4) sofaCV.wait (lock); numOnSofa++; while (findFreeChair () == NONE) chairCV.wait (lock); stylist = findFreeChair (); freeChairs[stylist] = 1; numOnSofa--; sofaCV.signal (lock); customerCVs[stylist].signal (lock); cutCVs[stylist].wait (lock); freeChairs[stylist] = 0; chairCV.signal (lock); numInRoom--; roomCV.signal (lock); lock.release (); } Stylist(int id) { lock.acquire (); while (1) { while (!freeChairs[id]) customerCVs[id].wait (lock); cutHair (); cutCVs[id].signal (); } lock.release ();
40
Next time More history Questions? Multics
“Almost every important idea in OS can be found somewhere in Multics.” So complicated and hard to use, it inspired UNIX Questions?
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.