- exceptions --- when system call versus not exception = way for the hardware to run the operating system multiple ways to classify why an exception would happen one option: asynchronous exceptions --- triggered by something that's not what's being run (example: keypress, network input, the memory is broken, ...) synchronous exceptions --- triggered by what is being run "faults" --- errors or unusual events that need special handling example: divide-by-zero, out-of-page-table memory access, ... system calls --- deliberate requests by program for help why ask for help --- because programs run in user mode and therefore can't do some things for themselves (example: I/O (request info about keypresses), manipulating other programs, ...) example: reading keypresses case 1: user presses key async exception [not system call] happens and OS records keypress later, program asks to read keypress [SYSTEM CALL] OS handler runs and copies previously recorded keypress case 2: program asks to read keypress [SYSTEM CALL] OS records that program wants keypress and runs something else later, user press key async exception [not system call] happens and OS records keypress OS realizes program was waiting for keypress and copies to that process - system calls and context switches context switch --- changes what thread/process a core is running! saving a process or thread state somewhere to resume later and resuming some other process/thread [special case: I'll consider starting running a new thread/process or termatinating a running thread/process a context switch even though we don't have to save/restore one of the threads] [just calling a function is not a context switch --- it's the same thread/process] if we're switching between processes, this requires changing the page table base register and so must be done in kernel mode (that is, by the OS) in order for the OS to run to do this, an exceptoin needs to occur at some point sometimes this might be a non-system-call exceptions (example: keypress, segfault, ...) sometimes this might be a system call (example: exit() system call, read() system call, ...) - process definition thread(s) + address space thread = "virtual processor" --- program counter + registers something you can run on a real processor address space = "virtual meory" --- implemented typically with page tables a notion what addresses correspond to what data - signals versus exceptions exceptions --- hardware runs the OS [handlers in the OS for exceptions run in KERNEL mode, have access to things user programs do not] signals --- with signal handlers: the OS runs a user function in a program [runs it in USER mode [no special access] and using the same thread as it was running before] with "default" handling: OS does something to a process in response to an event (like kill it because of control-C) most signals are the OS "reflecting" an somtehing it noticed as part of an exceptoin to a program example: typing control-C trigers an exceptoin to run the OS to handle that the handler for this keypress might decide that rathre than just saving it, it'll need to kill the program { "signal delivery" it'll need to run a handler in the porgram { example: program makes a kill() system call to send a signal to another program [exception (system call)] the routine that handles system call decides to terminate the program { run a handler in the program { "signal delivery" ... { - dividing addresses for caches divide address into {tag}{index}{offset} ^ ^----- least significant bit \--- most significant bit 1. look up the cache set corresponding to {index} in that set, we'll find cache blocks with: data bytes a tag a valid bit [sometimes other data] 2. see if a cache block has in the set is valid (valid bit = 1) and has a tag which matches {tag} from the address 3. if yes, then use the data {offset} bytes into the data bytes from the block 4. if no, then probably add something to the cache in the set (replacing something that's already there if necessary) [probably = definitely if read; if write = if have a write-allocate policy] implication: {offset} is enough bits to identify which data byte K data bytes per block, then offset must be log_2(K) bits {index} is enough bits to identify which set S sets, then the index must be log_2(S) bits - page table calculation divide address into {page number}{page offset} {page offset} is enough bits to identify which bytes of a page K-byte pages --> log_2(K) bit page offsets we use the virtual page number [page number in a virtual address] as the index into page tables if we have multiple levels of page tables, we use the high-order bits of the page number for the first level then move on to the lower bits in a page table, we find a physical page to look at (and valid/permission bits) once we find the final physical page, we combine the page number with a page offset to get a new physical addrss size of a page table --- how many bits of the page number are we using and how big is each entry page tables are arrays -- E bytes per entry * S entries --- E*S bytes size of entries --- usually told, but if not need to at least have physical page number and a valid bit - locks "object" (OO sense) that lets us take turns doing somtehing "lock" the object to take my turn and prevent anyone else from acting right away locking WAITS until everyone is finished "unlock" to let stop my turn and let other poeple go have a policy that we only access X after "locking" a lock ensures that we talke turns accessing X and prevents us from getting "weird" results example: incrementing a bank balance --- make sure we don't "miss" an increment because of the timing of reads+writes of the balance by ensuring we complete our read + add + write before anyone else gets looks - producer/consumer pattern producer: adding to a queue and waiting if there's no space to add to the queue consumer:removing from a queue and waiting if there's nothing in the queue yet can have multiple producers AND multiple consumers to implement this waiting, we need something more than just locks one example: monitors - monitors (mutexes (=locks) + condition variables) condition variables (badly named) represent a list of waiting threads example: consumer pattern, if the queue is empty we add ourselves to that list (cond_wait operation) and producer will dequeue thread (stop it from waiting) when it adds something (cond_signal operatoin) problem: we need to make sure that we don't check if we need to wait, then someone else makes us not need to wait, then we start waiting solution: we combine condition variables with locks use locks to ensure that while we're determining or changing if we need to wait or if others need to wait we take turns doing that - deadlock conditions - waiting for yourself in directly - 1. holding some resource while waiting for another - 2. there's a "loop" of threads such that X is holding Y's resource is holding Z's resource is holding ... X's resources - 3. while we are waiting for another resource, we just wait indeifnitely (aren't going to give up naturally, have a time limit, etc.) - network layers - we don't build web browsers from scratch we have layers tha thandle different parts of that functionality - physical layer: putting bits on {radio, wire,...} - link layer: sending messages ("frames") to machines on the local network (in radio range of each other; same wire/switch; etc.) - network layer: sending messages ("packets") between networks using routers to copy messages from one network to another - transport layer: grouping messages ("segments") into streams and handling reliable transmission and handling getting to the correct program on a machine - applicatoin layer(s): assigning meaning to messages applicatoin layer uses the transport layer in its implementation transport layer user the network layer in its implementation etc. for example, when we send something using TCP (transport layer), this will result in several message sent back in forth using the network layer and the network layer will implement each of those messages by sending frames (link-layer messageS) from several routers along the "path" the messages need to travel - TLS handshake - after handshake: want to use symmetric (shared key) encryption (secrecy-protetoin) + message authnetication codes (tamper-protection) - setup up shared keys by: - using key exchange to make a new shared key - using digital signatures to verify that key exchange came from "Real" server (not an imposter for the server) [to verify the digital signature, the server sends a certificate from a trusted certificate authority saying they really are the right server example of "right server" might be "www.google.com"] - pipelining --- when to stall - would be natural time we have values for instructoin - register values - program counters to deal with - if we don't have them: - stall until we do; and/or - use forwarding/branch prediction to wait less - if forwarding/branch prediction isn't enough to prevent stalling, we definitely need to stlal at least until they are - Spectre --- code pattern that can be attacked array1[array2[ATTACKER VALUE] * SOMETHING] ^^^^^^^^^^^^^^^^^^^^^^--------------------------- if we know where array2 is, we can make this be anywhere in memory ("chosen memory locatoin") as long as bounds checking doesn't work ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-------------- what index of array1 we access depends on the value at a chosen memory location if we know what's evicted by the array1 access, that tells us something about the valu ein the chosen memory locatoin (in particular, we get index bits from the addres, and we know ?????[index bits]???? = array1 + (interesting value * something) * array1_element_size can do algebra to learn about interesting value)