Table of Contents

Lecture 6

Signals (continued)

The operating system utilizes signals as a form of asynchronous communication between processes. They are the operating system's system for notifying a process that an event has occurred. Signals allow for the interrupt of user process as well as inter-process communication (IPC) such as Pipes. When a process receives a signal, it calls its function specific signal handler, generally setting a few flags and returning to previously executing instruction, unless the process has been killed.

For example, if Process A calls write() and Process B calls read() through a pipe, when does Process B get the message?

Process B only gets the message when it reads on the pipe file descriptor.

Why would Process B not be reading?

What if the message is urgent?

In the worst case, Process B gets the message at most in 1 sec (arbitrary number). This occurs because even during busy waiting/polling, ever second Process B will check for a message. If there is no message it will continue with its busy work.

Need User Level Interrupts

User Level Interrupts preemptively interrupt a running process to execute a specific signal handler. Once the receiver of the signal receives the interrupt, it halts all computation and jumps to its specific signal handler function with its own stack. The specific function is specified in the interrupt command.

kill ( pid_t p, int signo)
This delivers a user level interrupt number “signo” to the process with id “p”

Note: Kill must interrupt other system calls so the receiver of the signal, if it is in a system call, returns early.

If it is the case that the process was interrupted while in a system call, then the system call will return -1 on the error and also will set the global variable ERRNO == EINTR.

During the user level interrupt hardware and kernel interrupts are still active such as the timer interrupt.

Scheduling

OS Goal: To effectively multiplex the physical machine's resources among the abstract machines

A main task for the OS is to schedule the various processes on the machine and distribute the resources. The scheduling system must determine which of the runnable processes will run next and how much CPU time it will be given. There are numerous techniques to determine the order execution and amount of CPU time, which will be discussed below.

Process State Diagram

To schedule the various runnable processes, chunks of CPU time are assigned to each abstract machine. Scheduling problems arise because of the variety of execution deadlines and the difficulty to meet each of them.

.:process_state_diagram2.png

Scheduling Metrics

Scheduling metrics are measurements that tell how good a schedule is. There are several different types of scheduling metrics:

  1. CPU Utilization is the fraction of time spent executing application code. It is denoted by ρ where 0<=ρ<=1. In this case, 0 represents low CPU utilization while 1 represents high CPU utilization.
  2. Responsiveness is the time between when a process enters the runnable state and when it starts running. It is denoted by w which stands for the average waiting time among all processes. We want w to be as low as possible (i.e. low latency). If we are measuring the waiting time of a single process i, then we use w(i) to represent its waiting time.
  3. Turnaround time is the time from when a process becomes runnable until it becomes blocked or has exited. It is denoted by TTRND which stands for the average turnaround time among all processes. We want TTRND to be as low as possible. If we are measuring the turnaround time of a single process i, then we use TTRND(i).
  4. Throughput is the mean number of jobs that can be performed per second. It is denoted by μ where μ = 1/TTRND. We want the throughput to be as high as possible. A job is a unit of work by a process (i.e. the period between when a process becomes RUNNABLE until it is blocked or has exited).

Preferences: We want to influence the schedulers decision in order to achieve the following user goals:

Scheduling 4 jobs

Let's say we are given the following four jobs to schedule along with their service times T:

Job Τ (time to complete)
A 5
B 2
C 9
D 4

We assume that all processes start running at the same time.

First Come First Serve (FCFS)

If we execute A, followed by B, followed by C, followed by D, we get the following Gantt Chart:

0 5 7 16
----A--------B----------C--------------D-----

Note: When we switch from one process to another we encounter a delay known as the contact switch time. We will denote the contact switch by using C.

Analysis of responsiveness and turnaround time:

Job W TTRND
A 0 5
B 5 + C 7 + C
C 7 + 2C 16 + 2C
D 16 + 3C 20 + 3C
Average 7 + 1.5C 12 + 1.5C

Analysis of CPU utilization: Since the CPU is not used during contact switches, the utilization is ρ = 20/(20+3C).

FCFS Predictions

We have stable load which means that we can always get a good idea of the average TTRND. Lets says that L processes are waiting in a queue while 1 process is running. If we get a new job, we can easily estimate the new average turnaround time. Below is the formula that computes it:

L*TTRND+TTRND+.5*TTRND

The first term is the turnaround time of the L queued jobs. The second term is the turnaround time of the new job. The third term is the turnaround time of the current running job.

Shortest Job First (SJF)

If we execute the jobs in order of the time they take, we get the following Gantt Chart:

0 2 6 11
--B-------D---------A--------------C---------

Analysis of responsiveness and turnaround time:

Job W TTRND
A 6 + 2C 11 + 2C
B 0 2
C 11 + 3C 20 + 3C
D 2 + C 6 + C
Average 4.75 + 1.5C 9.75 + 1.5C

Analysis of Shortest Job First Algorithm

Example of Starvation:

Time Job:T
0 A:2 , B:1
1 C:1
2 D:1

Since job A takes 2 units of time, it will be executed after job B. However, if jobs that take 1 unit of time keep appearing each second (like C and D), job A might never be executed. In this example, at time t, t+2 units of work will remain. This is known as CPU saturation.

In practice, however, CPU starvation is generally unlikely.

The FCFS and SJF algorithms are examples of cooperative scheduling. This works great for some cases but not for other cases such as the following:

Job T Arriving Time
A 10100 0
B 1 1

In this case, we want job B to preempt or interrupt job A.

Preemptive Scheduling

Round Robin

Example:
If Q = 3,
We get the following Gantt Chart:

0 3 5 8 11 13 16 17
---A-----B-----C------D-----A-----C----D----C---

Analysis of responsiveness and turnaround time:

Job W TTRND
A 0 13 + 4C
B 3 + C 5 + C
C 5 + 2C 20 + 7C
D 8 + 3C 17 + 6C
Average 4 + 1.5C 12.75 + 4.5C

If Q = 2,
We get:

W = 3 + 1.5C
TTRND = 13.25 + 6C

Priority

Example:
While still using Q = 3,
We augment our process list:

Job Τ Priority
A 5 2
B 2 2
C 9 0
D 4 1

We get the following Gantt Chart:

0 3 6 9 12 13 16 18
---C------C------C------D----D----A-----B-----A---

Analysis of responsiveness and turnaround time:

Job W TTRND
A 13 + 5C 20 + 7C
B 16 + 6C 18 + 6C
C 0 9 + 3C
D 9 + 3C 13 + 4C
Average 9.5 + 3.5C 15 + 5C

Multi-Level Priority System

Priority Inversion

Privilege and Priority

Multi-Level Feedback Queues

p_estcpu: process's estimated CPU utilization
p_nice: normal priority
p_usrpri = 1/4 * ( 50 + 1/4 * p_estcpu + 2 * p_nice )