Notes/Operating Systems/Memory Management.md

158 lines
6.3 KiB
Markdown

---
type: theoretical
backlinks:
- "[[Inter-Process Communication]]"
- "[[Overview#Multiprogramming]]"
- "[[Overview#Multitasking/Timesharing]]"
---
![](Pasted%20image%2020250502181012.png)
(C.U. - Control Unit, does FDE and communicates with everything like ALU, registers, etc.)
## Single-Programing Operating Systems
Can only run one program lmao. MS-DOS is like that.
## Address Binding
- Depends on availability of memory space
- Addresses of instructions and variables change with memory location (where the program is loaded)
- Source code addresses are usually **symbolic**, while compiled code addresses bind to **relocatable** addresses
- Linker[^1] or loader[^2] will bind relocatable addresses to **absolute** addresses.
This last step is what's called **address binding**
## Binding to memory
Can happen at different "times":
- Compile time
- Load time
- Execution time
## Multi-Programming Operating systems
Introduce some decisions that the OS needs to make:
- Where to load each program
- Protecting the programs
- Optimization
### Protection
Operating system should be protected against unauthorized accesses by user process to its memory space.
We could do this via hardware, where the CPU must check every memory access generated in user mode to be sure it is between base and limit for that user.
## Contiguous memory -> Segmentation :)
The first solution for memory management is segmentation.
- Allocates memory space to each process
- As big as the process
### Fragmentation
- Each process requires a contiguous block
> [!caution] But fragmentation bad!
> It happens when the free space of the main memory is divided up into multiple parts **between** processes. It's pretty clear why that's bad.
![](Pasted%20image%2020250502182934.png)
![](Pasted%20image%2020250502183002.png)
### Memory Allocation Algorithms
Define which free segment should be allocated to a new process.
#### Best Fit
![](Pasted%20image%2020250502183152.png)
#### Worst fit
![](Pasted%20image%2020250502183221.png)
#### First fit
![](Pasted%20image%2020250502183242.png)
#### Compaction
![](Pasted%20image%2020250502183310.png)
>[!warning]- Issues with this
>1. If main memory is large, it can take a long time
>2. Complex
>3. Moving a block while it is being used will cause data loss
## Direct Memory Access (DMA)
In order to let CPU execute process instructions while data is being transferred from disk to memory, direct memory access (DMA) is used.
![](Pasted%20image%2020250502183523.png)
## Fix-sized memory blocks
In this scheme, memory is partitioned into blocks of the same size. Every block has the same number of bytes, which makes it easier for the system to keep track of which portions of memory are allocated or available.
>[!IMPORTANT]- What's a block?
> A block refers to one of these fixed-size segments. When a process requests memory, the operating system can allocate one or more blocks to satisfy that request. The fixed size means that each block is predictable in terms of its starting address and size.
We can circumvent the problem using fixed and equal-sized memory blocks.
This, however, introduces another problem - If multiple blocks are allocated to a process, addressing will be a challenge. Which blocks do we address???
>[!example]-
>If an element of an array is at address 2000, the next element may be at another block at a very different address
### Attributes of a block
Each block belongs to one of the memory segments of the program:
- Code
- Data
- Stack
- Heap
For each block, the access permissions can be defined.
>[!question]- What permissions?
> - A block is defined as execute only if it is part of the code segment
> - A block from stack segment has read and write permissions but no execute permission
### Storing block information
For each process, a list of allocated blocks is created. This list shows where each block of the program has been loaded in memory. Also, the permissions are set.
## Paging
Ahaha!
This is a logical follow-up of blocks. What if we have a lot of blocks? Well, we put them in a **page or frame = page frame**.
- The memory is divided into equal-size blocks named page frames
- A page frame has the same size as a page
### Loading
Each page is loaded onto the first available page frame of the main memory.
A page can belong to one segment of the program only ([Attributes of a block](Memory%20Management.md#Attributes%20of%20a%20block)).
### Logical v. Physical addresses
Each page is **numbered**.
- An instruction or a variable is located by its page number and offset inside the page. This is called a **logical address**.
- The actual address (i.e. `20010`) is referred to as a **physical address**.
### Page tables
Yet another box for a box for a box of boxes. This is just a data structure which holds pages.
- Each process has its own page table.
- Threads **share** the page table of the process
- [The process control block - PCB](Processes%20and%20Threads.md#The%20process%20control%20block%20-%20PCB) includes a pointer to the page table.
#### Implementation
Page table is kept in main memory.
Every data/instruction access requires two memory accesses - one for page table and one for the data. This can significantly slow down execution time.
So:
>[!question]- How do we solve this?
>We use Translation Look-Aside Buffers (TLB) - also called memory, which is *another* fucking data structure to hold the boxes of boxes of boxes of boxes.
#### TLBs
- Typically small
- Parallel and fast
- On a miss, value is loaded into the TLB for faster access next time
>[!IMPORTANT] **Effective access time** -- Super-duper important for exam
> - Hit ratio = % hits
> $$ EAT = (\text{hit ratio} \times T_{hit} + ((1 - \text{hit ratio}) \times T_{miss}) $$
> When solving these exercises, first identify the times for TLB hit (including TLB access and memory access) and for a TLB miss (which involves an extra memory access), then calculate the Effective Access Time (EAT) using the weighted average with the hit ratio. Finally, plug in the hit ratio and respective access times into the EAT formula and compute the result.
---
[^1]: That shit makes your program executable. Officially: The linker takes one or more object modules that contain relocatable addresses and combines them into a single executable.
[^2]: The loader is takes the executable and loads it into memory when the program is run.