Goals (what we want):
- Protection – we want to prevent a program running by one user from accessing the data of another
- Robustness – programs do not affect one another in adverse ways
- Utilization – the computer is a resource, we don’t want to waste it
- Performance
– we’re trying to get the best performance from applications, not to be
confused with utilization though they certainly overlap.
- Flexibility – trying to make the computer as flexible as possible to maximize what can be done
- Simplicity – we seek clarity
These goals cannot all be satisfied at once, a balance is sought
Virtualized Processor (what we have)
- Lets us (the OS) take control when an application behaves improperly (i.e. halt, etc…) such as a privileged instruction.
- Lets us (the OS) take control periodically no matter what
- For example when a program seems to fall into a loop
- Lets us (the OS) take control when an application accesses memory that it “shouldn’t.”
This “shouldn’t” is defined by us
- Lets
us (the OS) take control when the application attempts to communicate
with the outside world (i.e. network connections, other applications,
I/O devices, USB ports, DVD, clock, etc…)
- Usually occurs on a case by case basis
- The “outside world” has a disputed definition
- Runs at “full” speed
Virtualized processor + OS = support for a set of processes
-
A process is a program running atop an abstract computer
- Lacks halt, etc. (privileged instructions)
- Has higher-level “instructions” built on these lower level ones
- Referred to as system calls
- Created using a primitive call
- Pid_t fork(void);
- it takes a process to create a process”
- when a computer boots the OS initially calls a process called init who then “forks” the other processes.
- It creates a near clone of the existing process. One is the parent, and the other is the child.
- Int execvp(char const *file, char const **argv);
- File – file containing new process
- Argv – pointer to a an array of pointers to a an array of characters containing each parameter
- Terminates a current process and starts another
- Returns an Int
- -1 = error
- error type stored in errno. To do so, we need to have #include <errno.h>
- always fails because the caller no longer exists…
- historical value
- x86 is completely unaware of these functions
layering build a more-useful high level of abstraction (e.g. fork) atop a more detailed low level of abstraction (e.g. halt int)
- at the bottom is the hardware layer (controlled by intel)
- at the top are applications (written by user)
- in the middle are the low level called OS Kernel and some allowed instructions.
- OS
Kernel has the ability to access privileged hardware instruction. It
also has as set of system calls available to applications to allow
controlled access to hardware
- Allowed hardware instructions include: load, store, multiply, etc…
- ‘read’ is too low level for most users
operates on file descriptors
above it exist libraries like stdio (in the C library) that contain
functions that more efficiently manage the calling of kernel system
calls, such as getchr()
a separate layer or abstract machine
How do we do system calls?
- Method 0 – implement them as function calls
- This
method is just like using other function calls in C-program. It is very
popular in embedded applications where applications are under tight
control. The downside of this method is that it will not prevent
application from bypassing restrictions (using privileged insfructions.)
- Method 1 - protected transfer of control
- Ordinary applications can’t execute “bad” instructions
- Kernel can
- Memory is divided into app area and kernel area
- Privileged instructions only work when they exist in the kernel area
- Does not necessarily prevent applications from writing into kernel
- Does not necessarily prevent applications from jumping to privileged instructions
- X86 – to get protected transfer of control, execute a privileged instruction
- By convention the INT (interrupt) instruction
- Causes the processor to enter kernel state at a place specified by the kernel
- In the kernel
- Interrupt address table
- 256 pointers to interrupt handler code (there are normally more pointers than this, i.e. larger than 8 bit operand)
- x86 pushes onto the kernel stack
- ss – stack segment
- esp – stack pointer
- eflags –
- cs – code segment
- eip – instruction pointer
- error code sets ip
- what if another interrupt comes in? (i.e. from a device)
- one must be aware of stack overflow
- a separate area of memory is reserved for the kernel
- after the error code is executes
- RETI instruction occurs
- Pops the kernel stack and returns control
Levels of control:
- X86 has 4 levels of control:
- Kernel at the center
- Applications at the outermost
- Two levels in between for other services such as drivers
- In this class, we only use 2 levels (Kernel and Applications)
Syscall
- Similar to function calls
- Somewhat slower
- More stuff saved on stack
- Compiler can’t optimize as well
- Decoding overhead in interrupt handler
- We want these to be relatively rare
- What (machine resources) are we protecting/managing with the syscall mechanism?
- Components that need management
- ALU – user code gets full access
- Registers
- General purpose – user code gets full access
- Gets more complicated with shared memory
- Privileged – read-only or sometimes no access
- Primary memory (RAM)
- User memory – full (or partial) access
- Kernel memory – protected
- I/O devices
- Typically no access for user code
- There are exceptions
- Video memory for graphics, etc
- Time
- Register control
- Assume virtualizable processor (hardware) controls access
- Each process has its own copy of all these registers
- Kernel has a table of all the processes on the system: process descriptor table
- Each process descriptor contains a copy of all registers as well as other information
- While the process is running the information in the table is useless
- Context switch overhead – time lost doing this.
- Memory control
- Virtual memory – same analogy as applied to virtual processor
- Process sees a segment of memory that is in actuality spread out around physical memory.
- A magic box handles all mapping and makes interrupts when necessary.
- The magic box has virtual memory page tables
- Stored in kernel memory
- Process descriptor table gives location of virtual memory page tables.
- The magic box prevents access to the stack
- Device Control
- Typically so slow that operating system does not attempt to optimize
- System calls on their own tend to work
- Classic examples: read, write, open, close, lseek
- Robustness is often a real issue
- High probability of something going wrong
- Accesses are checked more carefully
- Devices tend to differ greatly
- More work to develop clean and portable interfaces
- Two “types” of devices (line is blurry)
- Network/stream devices
- More spontaneous
- Potentially infinite data
- Random access devices
UNIX’s big idea:
- Treat all resources as files. Therefore, it allows to use anything simply by executing "read" and "write"
- Linux has a whole directory called /proc/
- How well does this work?
It definitely works for a keyboard; however, some things don’t fit and have extra functions (
i.e. changing the speed of a serial port and ioctl)
|