Lecture 7

Multilevel Queue Scheduling

FCFS or FIFO views the "ready queue" as a true queue. That is, it treats it as the data structure commonly known as a queue. The shortest job first (SJF) and priority based scheduling depart from that concept. They use a "queue" more as a list that may be searched. Multilevel Queue scheduling algorithms stretch the use of the word "queue" even further. This class of algorithms thinks of the "ready queue" as an ordered set of subqueues (see Figure 7.1). Each subqueue is a queue managed a scheduling algorithm and that algorithm may be different for each subqueue in the queue. The ordering is thought of as priority. That is, in a multilevel queue with N subqueues ordered q0, q1, … qn-1, then q0 would be thought of as the highest priority level, q1, the second highest priority … and qn-1 the lowest priority. Usually qk is referred to as the level-k queue.

Multilevel queue scheduling algorithms select a process from the "ready queue" by invoking the scheduling algorithm associated with the highest priority non empty subqueue. That is, if the level-0 and level-1 queues are empty but the level-2 queue is not, then the level-2 scheduling algorithm would be invoked to select a process from that queue. Of course, this means that, if the level-2 queue is never empty, level-3 processes will never run. Multilevel queue algorithms must address this potential "starvation" problem, as must any priority based scheduling algorithm.

Figure 7.1 Multilevel Queuing algorithms use a "ready queue" that is composed of several queues. The "ready queue" shown here has three. These subqueues are ordered by priority level.

When a process enters the "ready queue", a multilevel queue scheduling algorithm must have a method for deciding in which subqueue the process is to be placed. There must be a description of what kinds of processes belong in each subqueue.

Example:

The ready queue has two subqueues.

Level-0 This is the high priority subqueue and only foreground processes will be placed in this queue. It is scheduled using the round robin scheduling algorithm.

Level-1 Background jobs will be placed in this queue. It is scheduled on an FCFS basis.

In addition, level-0 processes will preempt level-1 processes.

In this system, foreground (usually interactive) processes are treated with high priority and will probably get good response time, unless there are too many of them. Background processes will plod along, using time that is left over. If the system is always very busy with interactive jobs, the background jobs will not make much progress, if any.

 

Example

For each priority level there is a subqueue scheduled on a FCFS basis. Hi priority processes preempt lower priority processes.

Multilevel Feedback Queue Scheduling

One class of multilevel queue scheduling allows processes move from one subqueue to another. The examples given so far don't allow this. In both cases, this can lead to starvation. Multilevel feedback queue scheduling can be a fix for this problem. If, for example, a process sits in a subqueue for long time and does not get a chance to "run", it could be moved to the next higher level. If it sits there too long, it could "moved up" again … and again, until it starts making progress.

Figure 7.2 Multilevel feedback queue scheduling provides policies that move processes from subqueue to subqueue. See the example in the text below.

Example (Refer to Figure 7.2)

Figure 7.2 shows a "ready queue" with three subqueues. The subqueues are managed by the following scheduling algorithms:

Queue-0 Runs as round robin with time slice = 8ms
Queue-1 " " " " " " " " " " = 16ms
Queue-3 " " " " " " " " " " = 40ms

The system keeps track of the average CPU burst for each process. Initially this average is set to zero. When a process renters the ready queue it is assigned to the subqueue based on the running average. If the average is less than 8, it will be assigned to queue-0, if it is greater than 8 but less than 16, it will be assigned to queue-1, otherwise it is assigned to queue-2.

Each PCB has a ready wait field. When a process enters the queue, this field is set to zero. Periodically, this field is incremented for PCBs in the queue(s) and if the field for a PCB goes above a certain value, the PCB is moved to a higher level queue.

Interactive processes get high priority.

CPU intensive processes get low priority.

Processes in this system do not starve.

 

Example

As in the previous example, the "ready queue" has three subqueues. And, as before, the subqueues are managed by the following scheduling algorithms:

Queue-0 Runs as round robin with time slice = 8ms
Queue-1 " " " " " " " " " " = 16ms
Queue-3 " " " " " " " " " " = 40ms

The first time a PCB is placed in "the ready queue" it is placed in queue-0.

When a process runs, three things can happen:

    1. The process terminates. In this case it leaves the system.
    2. The process CPU burst does not finish before the time slice runs out. In this case the process is preempted and placed in the next lower level queue (if there is one).
    3. The process CPU burst finishes before its 50% of its time slice has expired. In this case, when it enters the run queue again it will be placed in the next higher queue.
    4. Otherwise the next time the process enters the "ready queue" it is place in the same subqueue.

A job with "cpu time to I/O" of 7 ms will settle in Queue-0

A job with "cpu time to I/O of 100ms will sink to Queue-2.

Multiple Processor Scheduling

To schedule processes in a multiple CPU system, we could, in principle, use any of the above algorithms. One simple way to implement the scheduling would be to have a separate ready queue for each CPU. This would work but may not be very efficient because a queue may empty quickly and leave the CPU idle. Placing new (and returning) processes in the shortest queue each time would lessen this problem. It won't solve it, however.

Using only one queue (Figure 7.3) solves this problem, but a new problem that arises. Who does the scheduling? If each CPU does its own scheduling, there is the possibility that two (or more) CPUs will select the same process and begin it running. We can protect against this possibility using a mutex (or other process synchronization service) but this protection does have a cost. There will be some lost CPU time each time two or more processes try to access the queue. All but one of those CPUs will be idle for a time.

Figure 7.3 Scheduling in a multiple CPU system.

As an alternative, all the scheduling could be done by one of the CPUs. This CPU could either run the entire OS or just handle scheduling.

Real-Time Scheduling

Don't use an operating system (chuckle).

Windows Programming:

More about WinMain():

The only way a user can interact with a Windows based program is "via a window". A user may work "simultaneously" with many windows but he/she can only interact with the program (process, thread) associated with the currently "active" window. The use can select a window using the mouse or the keyboard. This action communicates with the OS to make the selected window "active". A Windows Program creates windows for this purpose.

Figure 7.4 The typical structure of a WinMain(…) function.

Often windows are created in the initialization portion of the WinMain() function (see Figure 7.4). There are three major steps to the process of creating a window:

  1. A window class must be defined and registered. Defining a window class amounts to declaring a WNDCLASS structure and initializing its members. Registering the class is done by calling the function RegisterClass().
  2. When you define a window class, you are specifying how windows of that class will behave, or at least, you are defining certain aspects of its behavior. To be specific, you are defining:

    The name for this class.
    How the cursor will appear when it is in the window.
    How the icon will appear when the window is "minimized".
    The background color of the window.
    The menu.
    A few style attributes.
    The function (the MainWindowFunction) that will handle messages that the window receives.

  3. Once a class has been registered, a window of that class can be created using CreateWindow(). When you call CreateWindow(), you specify the window class, where you want the window to appear, how big it should be, what title will be displayed in the window, and a few additional behaviors. This action does not display it. It just causes it to exist.
  4. After a window has been created, it can be displayed, using ShowWindow(). This will make the window actually appear … provided you have defined your MainWindowFunction properly.

The objective of the MainWindowFunction is to handle messages. This is usually done in a switch statement, switching on the value of the variable message (one of the arguments passed to MainWindowFunction). There are some messages that must be handled by MainWindowFunction. You can use the function DefWindowProc(….) to handle these messages in a default manner.

Figure 7.5

A complete, working Windows program (using a minimal MainWindowFunction that only calls DefWindowProc) is shown in Figure 7.5. This program displays a window. It does not display a menu and does not terminate when you tell it to exit. To make that happen we need to do more.

Exercise:

Play with some of the values used in defining the window class and passed to CreateWindow to see what happens.

To fix this program so it will terminate properly, we can add the switch statement in the window procedure (this is the generic name for functions like MainWindowFunction). To terminate the program we need to terminate the message loop in WinMain(..). We do this by sending a message with PostQuitMessage(..). This sends a WM_QUIT message to WinMain(…). What does this do? See Figure 7.5.

We Change MainWindowFunction so it handles the WM_DESTROY message, as shown in Figure 7.6.

Figure 7.6


Copyright Ó 1997 Claude L. Fennema, Jr. Comments about this page can be sent to cfennema@mtholyoke.edu