CS 111Scribe Notes for 10/20/09by Stephen Chen, Charles LingScheduling (continued)Scaling: The Bigger PictureWhen viewing scheduling in the "big picture", we assume we are operating on a grid or cluster. These grids or clusters can contain medium sized nodes who all have CPU-bound jobs that need to be handled. This type of scheduling differs from FCFS, SJF, and RR in the following ways.
Priority SchedulingUsually, the lower number the priority, the higher a process is in the queue. A job with priority 1 will be run ahead of job with priority 3. However it is a good idea to verify how priority is defined in the system you are working in as this may not be the standard. There are two types of priorities:
Sidebar on PrioritiesIn general, priority number is not equal to precedence. One workaround for this can be seen in Unix, which implements priority using "niceness". A job with niceness 1 is higher priority than a job with niceness 2, and the default for most processes is 0. The difference however is that there can be negative niceness, or very high priority. $ nice +5 cat foo // runs "cat foo" with a niceness 5 greater than $ nice -5 firefox // setting negative niceness is prohibited unless root Realtime SchedulingHard Realtime SchedulingHard realtime schedulers involve hard deadlines, which means that all deadlines have to be met. In this instance, developers of apps and the kernel ensure that all deadlines are met. This type of scheduling is used for example in brake systems of cars, where a program response to a brake signal requires say a 10 ms response time. Repeatability is crucial and more important than speed in hard realtime schedulers. Developers can turn cache off to achieve this. Soft Realtime SchedulingSoft realtime schedulers allow deadlines to be missed. When it is overloaded with jobs, it skips the low-priority jobs and takes the hit in performance. For example, frame skips in media players are the result of soft realtime schedulers. If the streaming deadline of a packet is missed, it results in the frame skip (hit in performance). Types of Schedulers
SynchronizationThe difficulty in synchronization is when race conditions arise. Consider the following example on pipes. (We use simplifying assumption that all I/O to pipes is 1 byte)
In this instance, the buffer wraps around. Can we say that the pipe is empty if r == w, and that the pipe is full if r == w? NO. Actual read pointer is r % N and actual write pointer is w % N. We also cannot use the same comparison to determine if it is full AND empty. Therefore we have to use the comparison r + N = w to determine if the buffer is full.
Critical SectionsA critical section is a set of instructions, such that indivisibility is preserved if at most 1 thread's instruction pointer is in the set at any given time. Critical sections are implemented through mutual exclusion. Mutual exclusion means that when one guy goes in, everyone else stays out. This prevents race conditions from occuring. They are also implented using bounded wait. This means that if someone is trying to get into the critical section, there must exist an upper bound or timed wait to prevent starvation. MechanismThe underlying principle involves a uniprocessor, system calls and interrupts. We assume that syscalls will either fail or schedule another task and not wait indefinitely in the case of an error. What we want is read-write coherence, in other words:
The following pseudocode represents instructions we wish to execute atomically:
The high-level equivalent includes implementation of a lock, used to enforce access on a shared resource:
Implementing SpinlocksSpinlocks are used when multiple threads need to run. Only a thread that has the lock may run, while other threads wait for it to be available. This is useful only when threads aren't likely to be hanging (waiting) for long durations. Implementation is as follows:
Atomic instructions must be used when data needs protection from multiple threads of access. A real life analogy would be that of a bank account shared by two people. Even though both may attempt to access and change the value of whats in the account simultaneously, the account cannot cater to both at the same time and must atomically change its value so that read-write coherence is maintained. Implementation would look something like this:
Coarse-grained vs. Fine-grained Locking
Coarse-grained locking
Fine-grained locking
Minimizing Critical SectionsWe don't want critical sections to be too large since locking and unlocking large sections of code halts threads, leads to contention and produces bottlenecks. Alternatively we cannot have critical sections that are too small; they must be big enough to include all necessary and delicate code so that values that can be accessed both inside and outside the critical section don't cause race conditions or contention. How to find a minimal critical section
Blocking MutexesBlocking mutexes avoid polling by telling the OS what lock a specific thread is looking for. Example:
Semaphores
Semaphores were the first type of blocking mutexes. Its data structure contains a lock that is an integer. Its considered unlocked when the lock is greater than zero, and locked when it is equal to zero. The lock decrements everytime it is taken by a thread. Instead of |