* CS385 Operating Systems
V processes
* a machine abstraction
* its own processor/machine state
* its own memory layout
* isolation between processes
V memory protection / user/supervisor mode operation
* user mode operation: almost no privileges
* interrupt for “mode change”
* communication with the outside world happens through system calls
* hardware abstraction
V parts of an OS
* user interface
V administrative tools
* text editor?
V development tools and libraries
* compiler
* linker
* assembler
* libc
V kernel
* unrestricted access to all hardware
*
V boot
* machine power up: runs some BIOS code
V BIOS loads boot sector from the “first disk”
* the first 512 bytes
* into address 0x7c00
V calling BIOS functions
* put some parameters in registers
* trigger an interrupt
V graphics mode
* int 13 to switch modes
* write pixel data to address 0xa0000 and up
V 16 bits of address = 2^16 = 65536 bytes = 64kibibytes
V segmented addressing (logical address)
* 16-bit segment registers: CS, DS, ES, GS
* mov (DS:)offset, reg
* mov (ES:)offset, reg
* multiplier of 16
V any address is calculated as:
* segment << 4 + offset
* linear addressing
V page table - in 32-bit mode
* 32-bits of address space = 4gb of virtual ram
* each page is 4kb -> 1 megapages = 4 gb
V 1 megaentry worth of page table needs:
* 2^20 = 1 mega -> need 20 bits for page number
* 1 bit for valid/invalid
* 1 user/supervisor permissions bit
* 1 read/write permissions
* 1 executable bit
* 1 dirty bit
* 1 accessed bit
*
* mov addr, %eax
* addr = logical address
* addr + GST[%DS].segment_start = linear virtual address
V page_table_lookup(addr + GST[%DS].segment_start) = physical address
* CR3[logical_address>>22][(logical_address>>12)&0x3ff] = page table entry
* check the bits for permissions
* physical address = (page_table_entry&0xfffff000)|(logical_address&0xfff)
V least recently used implementation
V to find an LRU-approximate page:
V scan every page with P==1 for one with A==0
* if A==1, then set A=0
* improvement: remember where we found an LRU-approximate page (A==0), start there next time
V Linux LRU-approximation for page eviction
* first of all: always evict pages that already exist on disk first
V xv6 scheduler
* shared list of processes
* round-robin policy
* O(P) time complexity (P = number of processes)
V better design: distributed scheduler / two-level hierarchical scheduler
* separate lists of processes
* long-term load balancing to ensure even load across CPUs
V priority lists O(1) scheduler
* interactive priority
* batch priority 1
* batch priority 2 -> P3 -> P1 -> P2
V GPS - Generalized Processor Sharing / CFS - completely fair scheduler
V processes get equal share of the CPU - virtual CPU runtime
* accumulate “deserved” runtime over time
* accumulate used runtime
* net available time = deserved - used time
V store all processes in a (red-black) tree
* key is used time
V Threads vs Processes
* processes are made using fork()
* threads are created using some other system call
V threads share a single process environment, but what don’t they share
* register contents
* stack
* (thread local variables)
V shared:
* virtual memory layout = page table / mmaps
* file descriptors
* process id / parent process and such
V why would you use threads?
V why not? it can get really complicated
* A programmer had a problem.
He thought to himself, "I know, I'll solve it with threads!".
has Now problems. two he
V performance reasons - more threads = faster (?)
* with a single core, no advantage for CPU-bound tasks (maybe a disadvantage)
* (*) with multiple cores, split the work and run in parallel
V second thread can continue executing while system calls block
V no performance benefit, could just use nonblocking/asynchronous calls
* poll() / select() - to check for available file descriptors
V { buf = read(from disk)
* write(to screen, buf) }
* synchronous code is simple to write and understand
* interactivity / responsiveness - event handling
* I/O handling / responsiveness or performance
V system call semantics
* exit / exec() - kill all threads
* read / write() - no difference
* fork() - copy all the threads and continue running them all as if nothing happened
V thread-specific operations
* thread_create(function pointer*)
* thread_exit()
* thread_join() - wait for a thread to exit
V Midterm topics
V boot
* BIOS - load the first boot sector it finds into 0x7c00 and then run it
* bootsector - bootasm.S (xv6)
* starting the kernel - entry.S
* kernel initialization - main()
V console and display
* how does stuff get onto the screen (boot sector/BIOS setting, protected mode)
* how do characters make it from keyboard to the application
V memory and addressing
V addressing modes & types
* logical addresses (segment : offset)
* linear address (sometimes virtual address)
* physical address (via paging / page table)
V page tables
* page directory / page table / page table entry
* physical page numbers, various bits like for example PTE_P
V mmap stuff
* what does mmap do, and why would anyone use it
* virtual memory tricks / lazy page allocation, copy on write, swapping
V system calls and interrupts
V system call implementation from userspace
* interrupt / which system call / parameters for the systemcall
* handling system call interrupt & other interrupts
* source -> object files -> executable, linking, symbol tables that sort of thing
V processes
V what’s process? what state is kept per process, how is the illusion of a process created by the OS?
* its own “CPU” in a sense
* its own memory layout
* its own file descriptors
V first user process
* what goes into starting first process
* what does it do?
V scheduling / scheduler
* scheduling from the textbook
V context switching
* register by register, line by line - what’s going on?
V threads
V i++ example
* mov i, %eax
* add 1, %eax
* mov %eax, i
* atomic instruction
V mutual exclusion
* while(xchg(locked, 0, 1));
* // critical section: work on shared datastructures
* locked = 0;
V necessary criteria for deadlock
* mutual exclusion
* hold and wait
* no preemption
* circular dependency
V livelock
* work (synchronization operations) is being done, but no progress is made
V starvation
* one or more cores never make progress
* lock type dependent
V priority inversion
* high priority - needs the lock now
* medium priority - needs the CPU now
* low priority - has the lock
* solution: priority inheritance
V priority inversion 2
* low priority process gets the lock, yields the CPU
* high priority process gets the CPU, can’t get the lock
V two solutions: priority inheritance
* sleeping until lock becomes available
V futex: fast user-space mutual exclusion
V acquire()
V while(!old_value = (atomic xchg lock))
* sys_lock_wait(lock,old_value)
V release()
* sys_lock_wakeup(lock)
V file systems
* disk = permanent storage = block storage
* a block is 512 bytes
* int read(long long blocknumber, char* block_buffer)
* int write(long long blocknumber, char* block_buffer)
V a simple file system
V superblock in block 1 (metadata about file system)
* includes root block number
*
* struct direntry { char name[15]; block_t blocknumber_for_metadata; }
* struct inode { int size;
int type;
block_t direct_block_numbers[10];
block_t indirect_block_number; // block full of block numbers
block_t double_indirect_number; // block full of block numbers of blocks full of block numbers…
int refcount; }
* block_t treble_indirect_number; //
*
* struct double_indirect_block {
* block_t indirect_block_number[128];
* }
* struct indirect_block {
block_t block_number[128];
* }
* struct freelist_entry { int size; block_t next; }
V read(byte 1000000 from file “/home/jakob/hello.txt”);
* superblock
* root folder inode
* first data block of root directory file
* home folder inode
* first data block of home directory file
* jakob folder inode
* several data blocks of jakob directory file
* inode for hello.txt
* double indirect block
* indirect block
* data block
* read(byte 2000000 from file “/home/jakob/hello.txt”);
*
*
*
*
*
*
*
*
*