Midterm Flashcards
What are the key roles of an operating system?
1) Hide Hardware complexity (eg Abstraction)
Ex: OS hides the various storage devices in the system (hard drives, USB drives, etc) and provides a higher level abstractions known as a file.
2) Manage underlying hardware resources (eg Arbitration)
Ex: OS decides how many and which resources an application is allowed to use: OS allocates a certain amount of memory to an application, schedule it to run on the CPU, and controls access to various I/O devices.
3) Provide Isolation and Protection
With multiple applications running concurrently, the OS must make sure that no one application interferes with the execution of another application.
Ex: OS makes sure that memory allocated to one app is accessed by another app
Can you make distinction between OS abstractions, mechanisms, policies?
Abstractions - Entities that represent other entities in a more simplified approach. Typically enables portability.
- Ex: Files are an abstraction of physical hardware. We interact with the File and have the OS handle the complexities of reading and manipulating the physical storage.
- Additional Examples: sockets (network communications, memory page (memory management)
Policies - Rules on how a system is maintained and resources are utilized.
-Ex: Using least recently used (LRU) policy to manage memory swapping to disk. Memory that was least recently accessed will be swapped to disk.
Mechanisms - Tools used to implement Policies.
-Ex: Using a queue data structure that tracks the least recently used memory blocks as a means of implementing the LRU policy
What does the principle of separation of mechanism and policy mean?
Goal of this design principle is to use several flexible mechanisms that can support a range of policies. In different settings, applying different policies make sense.
Ex: For memory management some useful policies we could want include ‘least recently used’, ‘least frequently used’, or completely random. To support this, the OS will want to have mechanisms to track the frequency and time when the memory location was last accessed. This allows the OS to support all three policies.
What does the principle optimize for the common case mean?
Goal of this principle is to make sure that the most common use case for execution performs the best. Once the common use case is identified, pick a specific policy that makes sense and that can be supported given the underlying mechanisms and abstractions of the OS in use.
What happens during a user-kernel mode crossing?
Initially, a bit in the CPU is either set (privileged) or not set (unprivileged) to control access to the hardware. The hardware causes a trap if an application from unprivileged user mode tries to perform some instruction or access at the privileged kernel mode. The OS then determines what caused the trap and if it should grant access or terminate the action.
User-kernel mode crossing is also called a context switch: It takes CPU cycles to perform because it requires a lot more instructions and will most likely cause a switch in data locality, moving data from fast cache to slower memory. If the desired data is in cache, it is considered to be hot. If the data is in memory, the cache is considered to be cold.
What are some of the reasons why user-kernel mode crossing happens?
Any time an application needs access to underlying hardware.
For example: read from a file, write from a file
Occurs in response to a kernel trap or via a system call.
What is a kernel trap? Why does it happen? What are the steps that take place during a kernel trap?
Occurs when an unprivileged app attempts to perform a privileged operation. The hardware sends a signal to the OS which takes control at a specific location. The OS then has a chance to check what cause the trap and verify if it should grant access or if it should terminate the process.
What is a system call? How does it happen? What are the steps that take place during a system call?
So that apps can leverage the OS’s privileged access to hardware and avoid traps, the OS exposes a system call interface for the app to use if they want the OS to perform certain operations on their behalf.
-Ex: open() call to perform access to a file, send() to perform access to a socket
During a system call:
- The app makes the system call
- OS does a context switch from the app process to the kernel, making sure it holds onto the arguments passed within that system call
- The trap bit in the CPU gets adjusted so that the operation isn’t in privileged kernel mode
- The kernel executes the system call
- Once completed, the trap bit in the CPU is adjusted back
- OS does a context switch back to the app process and passes back the results
Contrast the design decisions and performance tradeoffs among monolithic, modular and microkernel-based OS designs.
Monolithic OS: Includes all services required by the app and the hardware. Ex: File system for sequential I/O and a file system for random I/O.
Pros:
-Everything is included
-Compile-time optimizations: Abstraction and all the services are packaged at the same time.
Cons:
-Lacks customization, portability, and manageability: Too much state, too much code that is hard to maintain, debug, and upgrade
-Requires a large memory footprint which impacts performance
Moduler OS: Includes basic services, APIs, and everything can be added as a module via an interface. Most common OS design today (eg linux). Pros: -Easier to maintain and upgrade -Smaller code base -> only include modules that are needed for the use case -Less resource intensive/better performance -> more memory for the app Cons: -Relying on an interface creates indirection which can impact performance but it’s not typically very significant. -Maintenance can be an issue -> Modules come from different code bases and could be a source of bugs.
Microkernel-based OS: Small, requiring only the basic OS primitives and generally customized (eg embedded devices). All other typical OS components run outside of the microkernel. Relies heavily on interprocess interactions.
Pros:
-Very small -> lower overhead and better performance
-Easier to verify -> test that the code behaves exactly as it should
Cons:
-Portability -> typically very customized to underlying hardware
-Software complexity -> many one-off versions for different hardware makes it hard to find common software components
-Frequent inter-process interactions is costly -> need for frequent user/kernel crossings is expensive
Process vs. thread, describe the distinctions. What happens on a process vs. thread context switch
Process: Instance of an executing program. An app that has been loaded in memory and is executing.
-A single threaded process is represented by it’s address space.
The address space will contain all the physical to virtual memory mapping for it’s code, data, and files.
-The process is also represented by it’s execution context (eg register values, stack pointer, program pointer).
-The OS represents all this information in a Process Control Block (PCB)
Threads:
-Part of the same virtual address space (and share all the code, data, files).
-Will potentially execute different instructions, access different portions of the address space, operate on different portions of the input, all within the same process.
-Each thread will need a different execution context (eg program counter, stack pointer, stack, and thread specific registers).
-The OS representation of a multi-threaded process will be a more complex process control block (PCB) structure.
will contain all the info shared among the threads (eg virtual address mappings, code, data, etc)
separate info about each thread’s execution context.
Process vs Thread context switch:
-Because threads share an address space, you don’t incur the indirect cost of recreating physical to virtual address space mappings. As a result, it’s not as costly to context switch between threads vs context switching between Processes which will likely require pulling in data from memory and having a cold cache.
Describe the states in a lifetime of a process?
When a process is created, it enters the “new” state. In this state, the OS will determine if it’s ok to admit the process, and if it is, allocate/initialize a PCB and some initial resources.
Assuming there are minimum available resources, the process is admitted and is in the “ready” state since it’s ready to start executing. Still not executed by the CPU. Process is waiting until the scheduler moves it into a “running” state.
When the scheduler gives the CPU a “ready” process, it is now in a “running” state. Once in a “running” state, a few things can happen:
- The process is interrupted and moved back into a “ready” state due to a need to execute a context switch.
- The process is moved into a “waiting” state because it initiated a long operation and is waiting on an event to occur (eg input from a keyboard). Once this event occurs, the process moves into the “ready” state.
- When a “running” process finishes all the operations of a program, or when it encounters an error, it will exit. It will return an exit code (success or failure) and the process will terminate.
Describe the lifetime of a thread?
A parent thread can create a child thread using a “fork” call with two paramaters, a procedure argument that reflects the procedure the new thread will execute and then the arguments for the procedure. When the new childe thread is created, a new thread data structure is created, it’s program counter will point to the first instruction on the procedure and the arguments will be provided on the thread stack.
If the parent thread terminates before the child thread, the child thread will terminate early. To prevent this, the parent thread would make a “join” call with the thread ID of the child thread and will be blocked until the child thread completes. Once the child thread completes, any resources that were allocated for its execution will be freed, the result of the computation is returned to the parent thread, and the child thread terminates.
If a child thread isn’t joined back with the parent thread and the resources of that child thread haven’t been freed, it’s called a zombie thread.
To avoid problems associated with multiple threads accessing shared data, mutual exclusion is applied so that when a thread is accessing shared data, the other threads are “blocked” until the thread has completed it’s operation. For more complicated use cases, conditional variables are used as a mechanism to support thread blocking until a specific event occurs. Once the event occurs, threads are awaken to execute their operation.
Describe all the steps which take place for a process to transition form a waiting (blocked) state to a running (executing on the CPU) state.
Typically, a process is in a waiting (blocked) state because it’s executing a long operation such as executing an I/O operation. The process is in an I/O waiting queue until the hardware completes the I/O operation and then responds. The scheduled then moves the waiting process back into a running state or it might be actually scheduled on the CPU depending on the current load on the system.
What are the pros-and-cons of message-based vs. shared-memory-based IPC.
Message-based IPC: OS establishes a communication channel (eg read/write or message to a shared buffer).
- Pros: OS manages the communication channel -> same OS-level APIs/system calls (eg send/rec data)
- Cons: Overhead -> Transferring info requires going from process 1 to channel to process 2.
Shared memory-based IPC: OS creates a shared memory channel and then maps it to the address space of each process. Both processes can ready/write from this memory and the OS is out of the way.
- Pros: No overhead -> OS is out of the way
- Cons: Need to re-implement code -> User manages all synchronization. Error prone. Don’t use OS APIs/calls.
What are benefits of multithreading? When is it useful to add more threads, when does adding threads lead to pure overhead? What are the possible sources of overhead associated with multithreading?
Multi-Core Benefits: First area of benefit is with parallelization. Assuming that each core has it’s own thread running the exact same code but with different inputs from an input matrix, each thread would execute it’s work in the same amount of time resulting in a speed up. Speed up efficiencies can also be found if each thread executes different portions of a program. Another area of benefit is with specialization. By specializing a thread, it is executing the same type of work and using the same data in cache creating a hot cache. Such result would lead to performance gains.
Single CPU: For a single CPU, multithreading allows the CPU to switch to a new thread while one thread is idle. Context switching for threads aren’t as costly as processes because the address space is shared by the threads so the time to switch is shorter. Overall, this leads to a performance benefit.
Useful to add threads when there are fewer thread than core or if you your threads are executing operations that cause it to idle often. Otherwise, adding threads is an overhead, especially if they aren’t efficiently used.
Multithreading overhead can come from not freeing up resources once a thread completes (eg zombie threads) and when managing thread synchronization.