CS 162 Lecture Notes for 1/22/07 Announcement: + We will cover enough materials about scheduling algrithms on Wednesday to get you started on hw1. + Computer accounts will be handed out tomorrow in discussions. Last lecture was about the historical overview of operating system, but OS as a whole is not well defined. Topic 1: Introduction to Processes; Dispatching + Operating systems have two general functions: + Coordinator: allow several things to work together in efficient and fair ways. Including: + Resource allocation and management. + Sharing and protection. + This emphasizes things like scheduling algorithm, multiprogramming, I/O scheduling, deadlock prevention, etc. + The OS also gives us a "Extended Machine": Standard library: provide standard facilities that everyone needs. Provide a "nice interface". Bare machine only implements "POO" (principles of operation). Extended machine has file system, I/O, multiple processes with scheduling, virtual memory, etc. (All the terms that haven't been defined will be defined before you need to know their meanings.) Discussion of Coordination Much of this course will deal with the coordination aspect: making many things work well together. This appears in several different areas. + Concurrency - OS allows several users to be working at the same time, as if each had a private personal machine. Or if we are talking about time sharing system (e.g. personal computer), one user can be doing many things at the same time. To keep track of everything, notion of process was invented (to be defined soon). + Computers have I/O devices - Don't want CPU to sit idle while an I/O device is working. (especially something slow like terminal) So, interrupts were invented: + Each I/O device has a little processor, which are fairly smart these days compare to 50 years ago, inside it so it can run independently. + CPU issues commands to I/O devices, then goes off to do other things. + When I/O device is finished, it issues interrupt to CPU: CPU recognizes that the I/O device is done, it stops whatever it was doing, and goes to special place to process I/O device request. + One of the major difficulties of designing a OS is that interrupts are asynchronized; they can arrive at any time while some other processes are running. Have to handle interrupts that are vitually simultaneous. Interrupts complicate matters inside the operating system. How can OS make sure that an interrupt doesn't destroy the work that was interrupted? What happens when many interrupts occur at about the same time? + Memory - Each process needs to use some main memory. OS coordinates their usage so they can share it. It provides protection among different processes that are running at the same time. It swaps information back and forth between disk and main memory so the system can run even if the total memory needed by all processes is greater than the size of the memory. + Files - Each user owns a collection of files. The OS coordinates how space is used for files so that they can all fit on the same disk. It provides directory for allocation of the files so users can search and find their files easily. OS also protects files from access by wrong user. + Networks - Allow groups of workstations to work together. + Coordination is difficult because needs of users conflict: some processes need large amounts of memory, small amounts of CPU time, some need lots of CPU time, but little memory, etc. OS must try to make all run reasonably well. How can we organize and conceptualize coordination so that sharing is done in a satisfactory way? A lot of optimizations are going one in OS. It is one of the main issues. + We will deal with this using decomposition; make each process thinks it owns the whole computer. One way to simplify sharing and coordination is to present the following illusions: + Single processor is made to look like many separate processors, one per user. + Single memory is made to look like many separate memories, one per process, each much larger than the real memory. + The main idea is giving every user the illusion that he has his own, unshared machine, with own file system, I/O devices, etc. + This is much easier than making the sharing and coordination visible. + This is an example of decomposing a problem. Rather than make the sharing visible, give each use the illusion of unshared resources. + Important concept: decomposition. Given hard problem, chop it up into several simpler problems that can be solved separately. (Recursion is a variation on this theme.) + With many things happening at once in a system, we need some way of separating them all out cleanly. That's a process. + What is a process? + "An execution stream in the context of a particular process state." + A more intuitive, but less precise, definition is just a running piece of code along with almost all the [local] things that the code can affect or be affected by. + Actually, don't refer to "everything", but only "local things" which does not include other processes with which it may be communicating, etc. + Process state is [everything] that can affect, or be affected by, the process: includes code, particular data values, open files, etc. + Execution stream is a sequence of instructions performed by a process. + Is a process the same as a program? + No, it's both more and less. + Process is the excution of a program. + A program is a chunk of code or the statements that a user writes, or a command he/she invokes. + More - a program is only part of the state; several processes may be derived from the same program. If I type "ls", something different happens than if you type it. + Less - one program may use several processes, e.g. cc runs other things behind your back. + Good analogy is the difference between a script (program) and a play (process). + Some systems allow only one process (mostly primitive (personal) computers). They are called uniprogramming systems (not uniprocessing; that means only one processor). Only one process is active at any given time. Easier to write some parts of OS, but many other things are hard to do. E.g. compile a program in background while you edit another file; answer your phone and take messages while you're busy hacking. Very difficult to do anything network-related under uniprogramming, because you have to wait for one process to finish before starting the next. + Most systems today allow more than one process. They are called multiprogramming systems. + Note that the concept of process (which we will discuss further) seems to capture the concept we need for organizing coordination and sharing. Obviously, sharing and coordination is important mostly when there are multiple processes. + A process can be either running or not running. So, for each process it is necessary to know certain information. (That information varies from system to system.) When the process is not running, we keep that information in the process control block. We must save anything that could get destroyed in the mean time, so the process can be resumed later. For each process, process control block holds: + Program counter. + Processor status word (condition codes, etc.). (i.e. system registers) (includes user/system status) + General purpose registers. + Floating-point registers. + Process ID to refer to the process. + All of memory? + In practice, we have a pointer to the memory info, rather than storing all of it in the process control block. + Scheduling information + Accounting and other miscellaneous information. (i.e. if you are on paid machine shared by many users.) + Pointers to other information, such as lists of open files, file descriptors.etc. + The system organizes the process control blocks into the process table. + Process table: collection of all process control blocks for all processes. In Unix the process table is a fixed-size array. In MVS, (I think) it is kept as a linked list. + Recall, we would like to give the image of one CPU per process. + Obviously, a single CPU can only be running one process at any one instant. We achieve the illusion of multiple CPUs by switching between various processes at short intervals. + (Remember the difference between human time and machine time. If you get a response from the machine in <.5 seconds, that is effectively instantaneous. On the other hand, a 2500 MIPS (2500 million instructions per second machine) can run 1250000000 instructions in that time. If your request only required 1,000,000 instructions, then 1250 requests could have been processed in that period.) + What is necessary if sharing the CPU is to work? The OS must make sure that processes don't interfere with each other. This means: + Making sure each gets a chance to run (they don't wait indefinitely.) + Making sure they don't modify each other's state (protection). + Making sure that they synchronize properly with each other. (i.e. croporating processes that affect each others inputs and outputs.) + Dispatcher/Scheduler: the portion of the OS that decides and initates which process will run at any given time. (This is one of the "inner-most" parts of the OS.) It enables processor to run processes. + (Actually, the scheduler usually decides, and the dispatcher initates the process. Like strategy vs. tactics. But most of the time these two terms are used as synonyms.) + Typically, the dispatcher functions in the following loop: + Run process for a while + Save state + Load state of another process + Run it ... + How does dispatcher decide which process to run next? + There are several issues here: + Note that we can only schedule jobs that are "ready" (runable). (e.g. jobs that are waiting for resources are not ready.) + We would like to have some way to select the "best" process to run to get the "best" performance. This is the "scheduling" problem, to be discussed later. (HW1) + We would like to select a process quickly, according to some criteria (to be discussed later) - data structure issue. E.g. we could search the process table linearly, but this might be slow. We could have a linear queue, and service jobs in a round-robin fashion. All in all, we have to pick an efficient algorithm. + CPU can only be doing one thing at a time: if user process is executing, dispatcher isn't: OS has lost control. + How does OS regain control of processor? + One approach: sleeping beauty (hope process is Prince Charming and will wake you up). The processor waits for the running process to send done signal. + Another approach: alarm clock (make sure you get woken up). What are alarm clocks? Interrupts are send to processor, so the processor knows that a ceratin process has been running long enough and it is time to switch to another process. + Some action, either by the process itself, or by something external to the process, must cause the OS and/or the dispatcher (which is part of the OS) to take control. These events are typically part of the set of events called "EIT". + To regain control of CPU, generally rely on a timer interrupt. + Interrupt is an example of a class of operations called EIT - Exceptions, Interrupts and Traps. + Traps are synchronous events in the machine, such as a page fault, divide by zero, SVC, physical memory error, out of bounds addressing, hardware error, illegal instruction, etc. Some traps require that the instruction abort; others can be handled after the instruction completes. Some cause the OS to crash. + Interrupts are asynchronous events. Typically I/O interrupts - the I/O device signals that it is done. Includes timer that tells CPU to stop and look for other processes when time is up. + The term exception refers to both interrupts and traps. + External events are usually called interrupts. They are asynchronous - might arrive at any time and might not be processed right away. They all cause a state switch into the OS. + Why? - because handling interrupts usually requires access to very sensitive parts of the machine state such as control registers. If the user program could touch that machine state, there would be no protection. User could destroy others' files, etc. + This means that user processes cannot take I/O interrupts as they occur, although can write handler to which some interrupts can be given. (e.g. floating point) + OS has two states: Supervisor state and User state. They are built into the hardware. + User state - offers protection, most state/control variables can not be changed in the user state. + Supervisor state - protection can be bypassed to have direct access to machine state or control registers. + In order to have access to machine states, OS generally needs to switch to supervisor state when an interrupt occurs. Exception include floating point trap, when OS can stay in user state. + To regain control of CPU, generally rely on a timer interrupt. + Three types of timer: + Periodic - e.g. 60 times/second. Problem: high overhead - it might not have a reason to interrupt you, low resolution - periodic time is quantized. + Time of day - interrupt when timer = TOD (time of day) clock + Elapsed Time (interval timer) - interrupt when timer decrements to zero. + How do we switch contexts between the user and OS? Need to save control block of the current process and load the control block of the new process. Must be careful not to mess up process state while saving and restoring it. + Saving state: it's tricky because the the OS needs some state to execute the state saving and restoring code. All machines provide some special hardware support for saving and restoring state: + Consider the problem of reloading the PSW (mode bit, address space pointer, protection info) and the program counter - no matter which order they're reloaded, results will not be correct. (e.g. if we reload the program counter first then we will take a random branch in the current process.) Must reload both at once, using special built in hardware support. + PDP-11: hardware doesn't know much about processes, it just moves PC and PS to/from the stack. OS then transfers to/from PCB, and handles rest of state itself (must be done carefully, using hand-coded assembler, to avoid overwriting state while saving it). + Intel 432: hardware does all state saving and restoring into process control block, and even dispatching. + CLIPPER: separate set of registers for user and upervisor. The supervisor saves the user registers with no trouble - the supervisor is allowed to refer to the user registers, but not the converse. + In general, the machine hardware saves the essentials of the state - including the PC, and PSW. It then directs through a vectored trap/interrupt table to the right routine. (Don't need to know the the detail for the purpose of this class.) + Suppose we want a new process. One approach is to create a process from scratch: + (Hard way) Creating a process from scratch: + Create (empty) call stack. + Create and initialize process control block (or reuse existing one). + Load code and data into memory. + Make process known to dispatcher - put it on some list of processes. + (Easy way) Another is to make a copy of an existing process. This in Unix and Toy is called Forking a process. (The word "fork" is used to indicate a bifurcation.) + Forking: want to make a copy of existing process. (warning: this may not be quite the same as fork from the book or fork from a programming language fork-join pair) For example, the Unix shell makes a copy of itself for each command. One waits around, the other goes off and executes the command. + Three steps to Unix fork: + (1) Allocate and initialize new PROC structure for the child process. (Give it a new process ID.) + (2) Duplicate the context of the parent process (including the user structure and virtual memory resources) for the child. + Child process gets open files, signal state, the scheduling parameters (e.g. "nice"), disk quota info. + (3) Schedule the child process to run. + What's missing? + The two processes are exactly the same, so this isn't very interesting. Don't usually want to do the same thing twice. + The usual way a fork is done in Unix is that after the process is "copied", some other program is copied into that process' memory, and executed (exec). + We aren't that interested in having two copies of that program. + Improvement is to use VFORK, which doesn't copy the original process' data and memory before overlaying the new one. It only copies the structure of the process. Topic 2: CPU Scheduling + OS makes two related kinds of decisions about resources: + Allocation: who gets what. Given a set of requests for resources, which processes should be given which resources in order to make most efficient use of the resources, while avoiding deadlock? Implication is that resources aren't easily preemptible. (Will spend a lecture one deadlock.) + Scheduling: providing preemptible resources - usually CPU and memory. When more resources are requested than can be granted immediately, in which order should they be serviced? Examples are processor scheduling (one processor, many processes), memory scheduling in virtual memory systems. Implication is that resource is preemptible. You can stop a process or take away its memory if something bad happens. + Resource #1: the processor. + Processes may be in any one of three general scheduling states: + Running. + Ready. That is, waiting for CPU time. Scheduler and dispatcher determine transitions between this and running state. + Blocked. Waiting for some other event: disk I/O, message, semaphore, user input, etc. Figure 1. Blocked <-- Run --> Done | ^ | | |---> Ready <-- Hold <-- Submit + Processes oscillate among those three states. + For the purposes of understanding scheduling, we want to create a more sophisticated model of how jobs and processes are handled: Figure 2. ----------- _______________| I/O & Page| | --| Wait | <---- time _____ | | ----------- | sharing | | | ____________ | | | | | | | _______ | | _______ | V______ |----- | | | -> -> | | | --> | | | / CPU \ --> Done batch ---> | | | ----> | | | ----> | | | --> \ / in ___|_|_| ___|_|_| __|_|_| ----- Hold Runable In Memory Queue Queue Queue + Job (Process) - all of the computations associated with a single "submission" - e.g. one command from your terminal. That command may result in several processes - e.g. a pipe, etc. May invoke a whole command file. The job is the unit of scheduling. + Note - in queueing theory, one uses the term "job". When talking about synchronization and deadlock, one talks about "processes". We really mean the same thing. In both cases, this is the unit of scheduling. + Hold Queue - holds jobs, usually batch, until the system is prepared to give them some service. Used to avoid overloading the machine. Jobs can be held here to avoid deadlock. Resources are allocated here. + Runable Queue - processes which are considered for scheduling, but which are not in memory yet. + In Memory Queue - processes which are (mostly) in memory; can be scheduled very cheaply. + Functions of job scheduler and dispatcher: + Keep track of job status (which queue, elapsed time, priority, etc.) + Choose which jobs will "run". (We'll discuss this further.) + Allocate necessary resources (memory, I/O devices, etc.) + Deallocate resources when necessary. + The terms scheduler and dispatcher are often used interchangably. Sometimes a distinction is made, as follows: + The (job) scheduler usually determines which jobs go from the hold-queue to the runable-queue, and the runable queue to the in-memory-queue. + The dispatcher usually determines which process in the in-memory-queue gets to actually run. + Note - most of the time, we won't distinguish between "scheduler" and "dispatcher". + Goals for scheduling disciplines (Optimization problem): + Efficiency of resource utilization. (keep CPU and disks busy) + Minimize overhead. (context swaps) + Minimize response time. (Define response time.) + Maximize throughput - jobs per second + (Define throughput) (Note - throughput in open system is invariant with scheduling algorithm - covered later.) + Provide guaranteed fraction of machine to specific groups. + Minimize number of waiting processes, or interactive users. + Get short jobs through quickly. + Avoid Starvation - jobs wait forever. Hunger - jobs wait for a long time. + Minimize variance of response time - i.e. make run time predictable, as function of CPU time required. + Note that this is different than minimizing variance of run time when averaged over all jobs. + Satisfy external constraints (e.g. periority, paid machine). + Meet Deadlines. + Graceful degradation under overload. + Provide Fairness??? - NO!! Fairness doesn't mean anything to computers - both round robin and first come, first serve are fair, but are not similar. + Real goal: maximize user satisfaction, keep them happy or more realistically, minimize complaints. (Remember that not all complaints are equally valid - who pays the bills?) + BUT - we need an objective measure to optimize - can't run psychological tests - use one of the above. + Typically use minimum flow time (also called minimum waiting time) - minimize the time from the submission of the job (command) to completion. f(i) is flow time of i'th job. Min ave(f(i)). + Secondary goal is to minimize the variance of f(i)/s(i) where f(i) is flow time of i'th job, and s(i) is service time of i'th job. Short job should wait shorter and long jobs should wait longer.