Easiest to see with user input.
User types say 1 character per second average.
The CPU takes 100 microseconds
to put character into a memory buffer.
For 999,900 out of every 1,000,000 microseconds, the CPU is doing nothing.
This holds even for non-user I/O.
e.g. Read from disk is at very slow speed
compared to speed at which CPU can work.
Even read from RAM is at slow speed
compared to speed at which CPU can work.
CPU could be doing other jobs while the read is going on.
CPU is kept free (responsive to user, able to run other code)
while device I/O is going on.
Modern OS driven by interrupts.
Interrupts are to do with the massive asymmetry between CPU speeds and device speeds.
They are how the OS program runs on hardware with slow devices.
Interrupts (IRQs)
on Windows 10.
My Computer - Properties - Device Manager - View - Resources by Type - Interrupt Request (IRQ)
Note IRQ 0 - System timer.
Examples of Interrupts
Completion of user input (every key press)
causes interrupt.
Sometimes each key press is handled just
by hardware device controller, which builds up a buffer,
and even handles line editing itself -
and only when you (say) hit return at end of command-line
is interrupt sent to OS.
Imagine a typical command-line:
Shell process may have waited hours for completion
of that Input.
Completion of any type of Input or Output.
Deliberate system call in a program causes interrupt
(essentially, the start of all I/O).
System calls:
Process - exit, load, execute, wait, signal, alloc
Info - query date, set date, query-set system info
Might just be a long loop.
We have no way of knowing.
But still, even if long loop, control must switch occasionally
- time-slicing.
It is a timer interrupt that switches control.
But loop runs forever, time-sliced.
Unsolved problems in mathematics.
Note that if you could detect an infinite loop in general, then could solve all problems
of the form:
Does there exist a solution to f(n)
for n > t?
by asking the OS if the following:
repeat
n := n+1
test solution
until solution
is an infinite loop, or just a long loop?
Then our OS could solve many of the world's great mathematical problems.
Many mathematical problems can be phrased as
infinite-loop problems.
Note that many "infinite loops" actually terminate with a crash,
because they are using up some resource each time round the loop.
e.g. This program:
f ( int x )
{
f(x);
}
f(1);
will eventually crash with a stack overflow.
This program however:
while true { }
will run forever, time-sliced.
Interrupts - Keeping the OS in charge
Interrupt idea is a way of periodically keeping the OS in charge
so nothing runs forever without reference to the OS.
e.g. In time-slicing, periodic timer interrupt to give OS a chance to
do something else with the CPU.
Remember, the CPU is just looking for a stream of instructions
to process, whether they come from the "OS" or from "user programs".
When the OS "runs" a program, it points the CPU
towards a stream of instructions in the user program
and basically hands over control.
How do we know program cannot now control CPU for ever?
Note: Interrupt does not necessarily mean that OS
immediately attends to
the process that sent it
(in the sense of giving it CPU time).
It just means the OS is now aware that that process wants attention.
The OS will note that its "state" has changed.
Asynchronous programming
An interrupt-driven OS is an example of
Asynchronous programming.
It can be hard to code like this, where functions may be called at any time
rather than in some predictable order.
And for the same reason:
Because JavaScript I/O (like fetching a resource from a server)
takes a long time compared to CPU time.
So we start the request, and carry on, and we get an "interrupt" when it finishes some time in the future.
There must be a concept of an "OS-type program"
(which can do anything, including scheduling ordinary programs)
and an "ordinary program"
(which is restricted in what it can do on the machine).
The restrictions on the "ordinary program" are normally not a problem
- they are basically just:
"Other programs have to be able to run at the same time as you".
This obviously makes sense in a multi-user system,
but in fact it makes sense on a PC as well.
When you're writing a program and you make an error,
you don't want your crashed program to be able to crash the whole OS.
You still want to be able to turn to the OS
(which should still be responsive)
and say "terminate that program".
Code is executed either in
kernel/system/monitor mode
or in
user mode.
boot in system mode, load OS
when run program, switch to user mode
when interrupt, switch to system mode
and jump to OS code
when resume, switch back to user mode
and return to next instruction in user code
Privileged instructions can only be executed in system mode.
e.g. Perhaps any I/O at all
- user can't do I/O, user has to ask OS to do I/O for it
(OS will co-ordinate it with the I/O of other processes).
Superuser
A related but different issue is switching between "ordinary" users and users
with administration rights.
On Linux, if you have admin (root/superuser) access to your own Linux, see these commands:
su
- "substitute user" - change to another user (e.g. root)
sudo
- "substitute user do" - run command as other user (typically root)
See
sudo apt
to install software
on your own Linux.
Consider the different types of code, OS or applications:
Parts of OS that
are not scheduled themselves.
Are always in memory.
Operate in kernel/system mode.
This part of the OS is called the kernel.
Includes:
memory management
process scheduling
device I/O
Parts of OS that can be scheduled.
Come in and out of memory.
Operate in user mode.
Includes:
command-line
GUI
OS utilities
Parts of OS that could be in either of the above:
file system
network interface
Applications. All scheduled.
Come in and out of memory.
Operate in user mode.
Running Linux
inside Windows
with
VMware.
I have used VMware in DCU labs
to teach
system administration of an imaginary multi-machine network
all running on one machine.
Image from here.
Java is "compiled" to
a
"virtual" instruction set
called
Java bytecodes.
These run on a JVM,
where they are actually mapped to native hardware instructions.
Promises of Java and the JVM:
Portability. Write an application once, run everywhere.
Run code from Internet on client-side.
Even though from a site with different hardware, they will run on your hardware.
Java applets.
Client-side code on the Web.
Written in Java, compiled bytecodes transmitted.
Played an important role in the history of client-side code.
But now obsolete.
Mostly replaced by what language?
OS'es are written mainly in HLL,
with some Assembly in Kernel.
Assembly fast but hardware-specific.
HLL much more portable, much easier to write/debug/change/maintain.
UNIX
/ Linux
Mainly written in C.
Some parts in Assembly.
Windows
Mainly written in C, C++.
Some parts in Assembly.
Perhaps only 5 % of the code is performance-critical.
Things executed constantly - Interrupt handler, Short-term CPU scheduler.
Other 95 % can be HLL for portability.
5 % can be machine-specific - needs a rewrite for each new hardware.
Best to find out what is important to performance
rather than pre-judge.
(In which loops does the system really spend its time?
You might be surprised.)
In practice, C/C++ is often faster than Assembly. Why?
Because C/C++ compiler is probably a better optimiser of code than your hand-coded Assembly.
So often stick to a HLL like C/C++ even for performance-critical code.
Compiler often has a
-optimise
switch
to take extra time analysing HLL code to
generate faster Assembly.
Part of the Linux OS source. Written in C.
This is part of
init/main.c
in Linus Torvalds'
Linux source.