Lecture 3
Dual Mode
For any program, the process can spawn from it is identical whether it spawns in
user-mode or in kernel-mode.
Mode Switch v. Context Switch
Mode Switch
- User -> Kernel
- user makes a sys call
- an interrupt happens
- an exception
- Kernel -> User
- new process starts
- new thread starts
- return from interrupt
- return from exception
- finished up doing a sys call
- When you're done start a new process
- UNIX signal (user level upcall)
Context Switch
- Must save state into PCB
- Then load state into PCP
- Then continue execution
Etc
If a context switch happens, it wasn't necessarily also a mode switch
Sometimes there's a mode switch without a context switch
Sometimes there's a mode switch and a context switch
A context switch can cause a mode switch
Hardware Rings
Rings vs. Modes
Each ring supports one mode. Four rings, four modes, in the simplest case, just
two.
Privileged Instructions
Privileged instructions are only available in kernel mode, if these instructions
are attempted by a user-level process, an exception is thrown and the process is
terminated. This is an example of a processor exception, which is distinct
from a programming language exception
What is the difference between a processor exception and a programming
language exception?
When a program is running, if an exception happens, it is handled by language
runtime in user-land. A processor exception, on the other hand, causes the
processor to immediately transfer control to the kernel, to the exception
handler, inside the system.
Exception handling facilities provided by the operating system
Unix-like operating systems provide facilities for handling exceptions in programs via IPC. Typically, interrupts caused by the execution of a process are handled by the interrupt service routines of the operating system, and the operating system may then send a signal to that process, which may have asked the operating system to register a signal handler to be called when the signal is raised, or let the operating system execute a default action (like terminating the program). Typical examples are SIGSEGV, SIGBUS, SIGILL and SIGFPE.
Examples of privileged instructions:
- Changing which memory locations are accessible to a user process
- Reading, writing, or sending commands to/from I/O devices
- Disabling interrupts
- Jumping into kernel mode
Interrupts
An operating system is an event driven creature. It responds to things that
happen on its machine, and reacts correspondingly. It responds to syscalls, but
other things, such as clock ticks, and initialization of drivers.
Question: One of the ways in which the OS gets notified of an event is using
the "polling" technique. What is the disadvantage with this technique? What
technique do modern computers use to circumvent it?
Answer: Polling periodically checks each device to see if it needs service.
This wastes precious resources like the processor time Modern computers use
interrupts as an alternative to circumvent it. Using interrupts, an I/O
device only notifies the processor when it needs attention. Upon receiving the
signal from the device, the CPU enters the interrupt handler and performs any
necessary operations.**
An interrupt is a signal, a request generated by either a device or a program,
which is sent to the operating system
Software interrupts may be unexpectedly triggered by program execution
errors. These interrupts typically are called traps or exceptions.
For example, a divide-by-zero exception will be "thrown" (a software interrupt
is requested) if the processor executes a divide instruction with divisor
equal to zero. These are a form of synchronous interrupt.
Hardware interrupts are asynchronous signals sent by devices (such as
a drive, a keyboard, or a timer), to the CPU, which then sends that signal to
the operating system. This includes a hard drive, as well as hardware
clocks.
There are two types of asynchronous interrupts, maskable and
unmaskable
- maskable interrupts
- non-maskable interrupts: Unable to be disabled. On
Intel, there are two pins, INTR and NMI.
The signal from the device is sent through a specified pin to the
processor.
PIC: programmable interrupt controller
There's a separate kernel stack for each process
On a multi-processor environment, each processor has its own interrupt
listeners & handlers
Question: How does the processor know what code to run?
Answer: It has an interrupt vector, set up by the kernel, which has an
address stored by the processor register, and code executed in kernel mode will
only start at specific well-defined location, known as entry points.
A user-level upcall, in UNIX, is known as a signal.
The difference between a signal and an interrupt, is that interrupts are
mediated by the processor and handled by the kernel while signals are
mediated by the kernel (possibly via system calls) and handled by processes. The
kernel may pass an interrupt as a signal to the process that caused it (typical
examples are SIGSEGV, SIGBUS, SIGILL and
SIGFPE).
Signals
Ranked from weakest to strongest
| Shortcut | Output | Signal | Number | Notes |
|---|
| ⌃ Z (1) | Pause a job | SIGTSTP | 20 | Also known as suspending a job |
| ⌃ Z (2) | Continue a job | SIGCONT | 18 | Pressing ⌃Z again will continue a process that was just suspended |
| ^ C | Interrupt a job | SIGINT | 2 | Tell a process that it should not continue, the most common way to end a program |
| ⌃ \ | Quit a job | SIGQUIT | 3 | Similar to an interrupt, but a little stronger (can still be caught), and will produce a core dump. The strongest of the signals that can be called via keyboard shortcuts |
UNIX Signals
Various signals can be sent in UNIX to interact with a program. Many of these
contain keyboard shortcuts, but first it is important to go over the most common
types of signals. Programs can customize how they react to various signals by
catching, handling, or ignoring them.
To view all signals, type $ trap -l
To view all signal keyboard shortcuts, type $ stty -e or $ stty all
Signal Definitions
SIGTERM (15): Tells a program to stop, in order to allow the program to
handle its termination gracefully. Can be caught, handled, or ignored.
SIGINT (2): Used to interrupt a program that is running. It is the same as
the SIGTERM signal, but it explicitly refers to an interruption that was
called from the terminal. Can be caught, handled, or ignored.
SIGQUIT (3): Similar to SIGTERM but it will generate a core dump. Can
be caught, handled, or ignored.
SIGSTOP (17): Temporarily stop a program. Cannot be caught, handled, or ignored.
SIGTSTP (18): Sends the program a signal, telling it to temporarily stop.
Unlike SIGSTOP, it can be caught, handled, or ignored.
Handling Interrupts
- interrupt handler
an interrupt handler , also known as an interrupt service routine or
ISR , is a special block of code associated with a specific
interrupt condition. Interrupt handlers are initiated by hardware
interrupts, software interrupt instructions, or software exceptions, and
are used for implementing device drivers or transitions between
protected modes of operation, such as system calls.
Although there's truly many, many steps to handling an interrupt, at a high
level, it consists of four main components.
- Save the current processor state
- Load the state for interrupt handling
- Invoke the corresponding Interrupt Service Routine (ISR)
- Resume the program execution
It's important to note that interrupts can be preempted. A maskable
interrupt can be underway, but could then be interrupted by a non-maskable
interrupt. Or, to serve an interrupt, something else might be required. An
exception is being handled, but then we look for something that is currently not
in memory, and needs to be loaded in from storage, so we would need to invoke a
syscall to load the necessary data into memory.
xv6 Interrupts
- 0 to 31 reserved by Intel for processor exceptions
- 32 to 47 are configurable, used for device interrupts
- 64 used for system call interrupt
- On Linux, 128 (0x80) is used for system calls
- xv6 interrupt handler entries
vector.S (generated by vectors.pl, a Perl
script)
A process doesn't solely exist in user mode, so the address space of the kernel
is mapped in the memory of every single process. For this reason, it has a
user-mode context as well as a separate kernel-mode context, which means there
is a user-space stack, and also a separate kernel-space stack, used when
executing kernel mode, such as during syscalls. For this reason, it is
possible to have a mode switch without a context switch.
A process id (PID) is stored in the process control block (PCB), is inaccessible
to the corresponding user-level process, but can be requested using the
getpid() syscall.
Handling Syscalls
There is a user stub and a kernel stub
When the process initially begins running, its user-stack will store things like
functions, but the kernel stack will be empty. Once the thread is interrupted,
however, the user CPU state will be loaded into its corresponding kernel stack.
When it is waiting for I/O, or waiting for a syscall to return, the user stack
will contain the context to resume when the syscall returns, and the kernel
stack will contain the context to be returned when I/O completes.
Process Management
Most operating systems allow a user process to create another process through
the use of a system call.
- Windows uses
createProcess(), one big syscall, which creates the
process, sets up the environment, and starts up the program - UNIX-like systems have *separate syscalls
fork(): creates a processopen(), close(), and dup(): sets up the environmentexec(): loads the program image, and starting execution
init is the first process that spawns when the operating system first spins
up. For this reason, it is assigned the PID value 1. All processes become
descendents of init. Whenever a user logs in, a new proces will be created for
that user. Each user can create their own browser for their processes. Each of
these processes can spawn child processes, which can spawn child processes, and
so on.
If a process is destroyed, (for instance, because it crashed), some operating
system will begin destroying its children. In UNIX, however, init becomes the
new parent for that process.
fork: creates a copy of the current process, known as the parent process,
and starts that copy, which is known as the child process
Checking if the active process of the most recent fork() is the child or the
parent requires a bit of trickery, because fork() is called once, but it
returns twice.
```c
pid_t pid = fork();
if (pid == 0) {
printf("child\n");
}
else {
printf("parent\n");
}
```
Up to 64 processes can be active at once in the xv6 operating system.
exec: changes the data and program (text section) being run by the current
process, substituting the new program in place of the current process. It
does not create a new process.
wait: waits for a process to finish. It blocks the caller, the parent
process, and does so until one of its child process terminates. If it has no
child processes, or it forks a background process, wait returns immediately.
wait can optionally be passed the PID of a child of this parent as a
parameter.
exit: indicate that a process is finished and ready to terminate.
The ELF format defines the structure for binaries, libraries, and core files.
The formal specification allows the operating system to interpreter its
underlying machine instructions correctly. ELF files are typically the output of
a compiler or linker and are a binary format.
The executable image file loaded by exec must be in a particular format.
Zombie Process
On Unix and Unix-like computer operating systems, a zombie
process or defunct process is a process that has completed execution
(via the exit system call) but still has an entry in the
process table: it is a process in the "terminated state".
This occurs for the child processes, where the entry is still needed to
allow the parent process to read its child's exit status: once the exit
status is read via the wait system call, the zombie's entry
is removed from the process table and is said to have been "reaped".
A child process always becomes a zombie before being removed from the resource
table. In most cases, under normal system operation zombies are immediately
waited on by their parent and then reaped by the system -- processes that stay
zombies for a long time are generally an error and cause a resource leak,
but the only resource they occupy is the process table entry -- process ID.
Orphan Processes
An orphan process is a computer process whose parent process
has finished or terminated, though it remains running itself. In a
Unix-like operating system any orphaned process will be
immediately adopted by the special init system process: the kernel
sets the parent to init.
An orphan can be performing useful operations on the computer, it simply has no
parent.
This operation is called re-parenting and occurs automatically. Even though
technically the process has the init process as its parent, it is still called
an orphan process since the process that originally created it no longer exists.
In other systems orphaned processes are immediately terminated by the kernel. In
modern Linux systems, an orphan process may be reparented to a "subreaper"
process instead of init.
If a parent does not call wait, then it will become a zombie, it's a warning
from xv6 that the parent process did not call wait.
Questions
Question 1
A: The kernel services can be invoked by 3 any program.
B: Privileged instructions may be executed by 1 only the kernel
C: An application that needs to access an I/O device could not be written
without 2, kernel functions, though library functions make it more convenient
to do so
D: The kernel can execute 2 both privileged and non-privileged instructions.
Question 2
Consider two mode switch cases: user -> kernel and kernel -> user. For
each mode, list events that cause the mode switch.
A mode-switch from user-mode to kernel-mode will occur if a syscall is
made, a trap is triggered, a processor exception, or a hardware
interrupt occurs.
A mode-switch from kernel-mode to user-mode will occur when a syscall is
fully processed, an interrupt was fully handled, or a new process is
initialized.
Will a context switch always cause a mode switch? Why or why not?
A context switch may not necessarily cause a mode switch. For example, the
operating system could switch contexts from one kernel-mode process to
another kernel-mode process. A switch between a user-mode process, and
another different separate user-mode process, on the other hand, will
cause a mode switch as well.
Question 3
What do hardware interrupts and software interrupts (traps) have in common?
- Both require a mode-switch from user-mode to kernel-mode, provided that
the operating system is currently executing instructions in user-mode.
What are the differences between hardware interrupts and software interrupts
(traps)?
A trap is synchronous, and operates internal to the CPU. Hardware
interrupts are asynchronous, will be sent to the CPU before they are
handled by the operating system.
All software interrupts can be ignored by the operating system, but a
non-maskable interrupt, exclusive to hardware interrupts, cannot be
ignored by the operating system.
Question 4
If a local variable is declared in an interrupt handler (ISR), where will it
be stored?
The variable will be stored in kernel stack
How does the process coordinate between the user stack and the kernel stack?
The process itself does not coordinate between the user stack and the
kernel stack. Coordination between these two stacks is performed entirely by
the operating system.
Question 5
Consider a coffee shop where customers can order coffee by phone. Find the best
match for the coffee shop analogy:
- The system is (4) the coffee shop
- The process is (2) the customer
- The interrupt is (1) the telephone ringing
- The CPU is (5) the coffee machine
- A system call is (3) placing an order
Question 6
- When will the last line be printed?
The last line will only be printed if the call to exec directly above it
fails, and returns an error.
- List 2 feasible and 1 infeasible outputs of running this code
HC
HP
CT
HP
HC
CT
CT
HP
HC