├── .gitattributes ├── How all of this works 7868ca41311a42b5ba45aa4e1b1033fd ├── Untitled 1.png ├── Untitled 2.png ├── Untitled 3.png └── Untitled.png ├── README.md └── xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788 ├── Untitled 1.png ├── Untitled 2.png ├── Untitled 3.png ├── Untitled 4.png ├── Untitled 5.png ├── Untitled 6.png ├── Untitled 7.png └── Untitled.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /How all of this works 7868ca41311a42b5ba45aa4e1b1033fd/Untitled 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zarif98sjs/xv6-memory-management-walkthrough/8025057e75f397d006062566a67efb1284b29913/How all of this works 7868ca41311a42b5ba45aa4e1b1033fd/Untitled 1.png -------------------------------------------------------------------------------- /How all of this works 7868ca41311a42b5ba45aa4e1b1033fd/Untitled 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zarif98sjs/xv6-memory-management-walkthrough/8025057e75f397d006062566a67efb1284b29913/How all of this works 7868ca41311a42b5ba45aa4e1b1033fd/Untitled 2.png -------------------------------------------------------------------------------- /How all of this works 7868ca41311a42b5ba45aa4e1b1033fd/Untitled 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zarif98sjs/xv6-memory-management-walkthrough/8025057e75f397d006062566a67efb1284b29913/How all of this works 7868ca41311a42b5ba45aa4e1b1033fd/Untitled 3.png -------------------------------------------------------------------------------- /How all of this works 7868ca41311a42b5ba45aa4e1b1033fd/Untitled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zarif98sjs/xv6-memory-management-walkthrough/8025057e75f397d006062566a67efb1284b29913/How all of this works 7868ca41311a42b5ba45aa4e1b1033fd/Untitled.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 📑 **`Xv6 Memory Management Walkthrough`** 2 | 3 | # **`Table of Content`** 4 | 5 | - [Terminology](#terminology) 6 | - [Overview of page table](#overview-of-page-table) 7 | - [Memory Layout](#memory-layout) 8 | - [Functions of interest {Part 1 : building blocks}](#functions-of-interest-part-1--building-blocks) 9 | - [Functions of interest {Part 2 : core functions}](#functions-of-interest-part-2--core-functions) 10 | - [How all of this works](#how-all-of-this-works) 11 | - [Final Words](#final-words) 12 | - [Acknowledgement](#resource) 13 | 14 | # **`Terminology`** 15 | 16 | - VA : Virtual Address 17 | - PA : Physical Address 18 | - PD : Page Directory 19 | - PDE : Page Directory Entry 20 | - PT : Page Table 21 | - PTE : Page Table Entry 22 | - PPN : Physical Page Number 23 | 24 | # **`Overview of xv6 page table`** 25 | 26 | All process works on virtual address. Machine’s RAM is physical address. Page Table maps virtual address to physical address. 27 | 28 | Xv6 does this in 2 steps. 29 | 30 | ![Untitled](xv6%20memory%20management%20walkthrough%2052f10c25c9dd4de39e601e386e6a1788/Untitled.png) 31 | 32 | Some things to note for xv6 : 33 | - 32 bit virtual address (so, virtual address space 4GB) 34 | - page size of 4KB 35 | - The CPU register `CR3` contains **a pointer to the outer page directory** of the **current running process**. 36 | 37 | ![Untitled](xv6%20memory%20management%20walkthrough%2052f10c25c9dd4de39e601e386e6a1788/Untitled%201.png) 38 | 39 | ### **Caution** 40 | 41 | xv6 **does not** do **demand paging**, so there is **no concept of virtual memory**. That is, all valid pages of a process are always allocated physical pages. 42 | 43 | 44 | # **`Memory Layout`** 45 | 46 | Every page table in xv6 has mappings for user pages as well as kernel pages. The part of the page table dealing with kernel pages is the same across all processes. 47 | 48 | ## **Kernel Only Memory** 49 | 50 | ![Untitled](xv6%20memory%20management%20walkthrough%2052f10c25c9dd4de39e601e386e6a1788/Untitled%202.png) 51 | 52 | - virtual memory > `KERNBASE` (0x8000 0000) is for kernel 53 | - always mapped as kernel-mode only 54 | - check `PTE_U` fag 55 | - **protection fault** for user-mode programs to access 56 | - **physical memory address** `N` is mapped to `KERNBASE+N` or `0:PHYSTOP` to `KERNBASE:KERNBASE+PHYSTOP` 57 | - Note that although the size of physical memory is 4 GB, only 2 GB can be used by Xv6 58 | - kernel code loaded into contiguous physical addresses 59 | 60 | ## **User Only Memory** 61 | 62 | Each process has separate page table. For any process, user memory VA range is `0:KERNBASE` where `KERNBASE` is 0x80000000 i.e. 2 GB of memory is available to process. 63 | 64 | ![Untitled](xv6%20memory%20management%20walkthrough%2052f10c25c9dd4de39e601e386e6a1788/Untitled%203.png) 65 | 66 | - The kernel code doesn’t exactly begin at `KERNBASE`, but a bit later at `KERNLINK`, to leave 67 | some space at the start of memory for **I/O devices**. Next comes the kernel **code+read-only** data from the kernel binary. Apart from the memory set aside for kernel code and I/O devices, the **remaining memory is in the form of free pages** managed by the kernel. When any user process requests for memory to build up its user part of the address space, the kernel allocates memory to the user process from this free space list. That is, most physical memory can be mapped twice, once into the kernel part of the address space of a process, and once into the user part. 68 | 69 | ![Untitled](xv6%20memory%20management%20walkthrough%2052f10c25c9dd4de39e601e386e6a1788/Untitled%204.png) 70 | 71 | ## **`P2V` and `V2P`** 72 | 73 | - `V2P(a)` (virtual to physical) 74 | - convert **kernel address** `a` to **physical address** 75 | - subtract `KERNBASE` (0x8000 0000) 76 | 77 | ```cpp 78 | #define V2P(a) (((uint) (a)) - KERNBASE) 79 | ``` 80 | 81 | - `P2V(a)` (physical to virtual) 82 | - convert **physical address** `a` to **kernel address** 83 | - add `KERNBASE` (0x8000 0000) 84 | 85 | ```cpp 86 | #define P2V(a) ((void *)(((char *) (a)) + KERNBASE)) 87 | ``` 88 | 89 | 90 | # **`Functions of interest {Part 1 : building blocks}`** 91 | 92 | ## **`PGROUNDUP`** 93 | 94 | What it does is round the address up as a multiple of page number i.e. do a CEIL type operation. 95 | 96 | ```cpp 97 | #define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1)) 98 | ``` 99 | 100 | `PGROUNDUP(620)` → ((620 + (1024 -1)) & ~(1023)) → 1024 101 | 102 | ## **`PGROUNDDOWN`** 103 | 104 | another related function is `PGROUNDDOWN` , works similarly. just makes the address round down as a multiple of page number 105 | 106 | ```cpp 107 | #define PGROUNDDOWN(a) (((a)) & ~(PGSIZE-1)) 108 | ``` 109 | 110 | `PGROUNDDOWN(2400)` → (2400 & ~(1023)) → 2048 111 | 112 | ## **`switchuvm`** 113 | 114 | - `u` here stands for user 115 | - basically OS loads the user process information to run it 116 | - loads process's Page Table to `%cr3` 117 | 118 | ## **`kalloc`** 119 | 120 | This function is responsible to return an address of one **new, currently unused** page (4096 byte) in RAM. `kalloc` removes first free page from `kmem` and returns its (virtual!) address, where `kmem` points to the head of a list of free (that is, available) pages of memory. 121 | 122 | **Failure :** If it **returns 0**, that means there are no available unused pages currently. 123 | 124 | ## **`walkpgdir`** 125 | 126 | Main job of this function is to get the content of 2nd level PTE. This is such an important function that we will go line by line. 127 | 128 | ```cpp 129 | static pte_t * 130 | walkpgdir(pde_t *pgdir, const void *va, int alloc) 131 | { 132 | pde_t *pde; 133 | pte_t *pgtab; 134 | 135 | pde = &pgdir[PDX(va)]; // PDX(va) returns the first 10 bit. pgdir is level 1 page table. So, pgdir[PDX(va)] is level 1 PTE where there is PPN and offset 136 | if(*pde & PTE_P){ // not NULL and Present 137 | pgtab = (pte_t*)P2V(PTE_ADDR(*pde)); // PTE_ADDR return the first 20 bit or PPN. PPN is converted to VPN for finding 2nd level PTE. pgtab is level 2 page table 138 | } else { 139 | if(!alloc || (pgtab = (pte_t*)kalloc()) == 0) 140 | return 0; 141 | // Make sure all those PTE_P bits are zero. 142 | memset(pgtab, 0, PGSIZE); 143 | // The permissions here are overly generous, but they can 144 | // be further restricted by the permissions in the page table 145 | // entries, if necessary. 146 | *pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U; 147 | } 148 | return &pgtab[PTX(va)]; // PDX(va) returns the second 10 bit. So, pgtab[PTX(va)] is level 2 PTE where there is PPN and offset 149 | } 150 | ``` 151 | 152 | ### **Case 1 : when no allocation needed (`alloc = 0`)** 153 | 154 | Recall that 155 | 156 | ```cpp 157 | // +--------10------+-------10-------+---------12----------+ 158 | // | Page Directory | Page Table | Offset within Page | 159 | // | Index | Index | | 160 | // +----------------+----------------+---------------------+ 161 | // \--- PDX(va) --/ \--- PTX(va) --/ 162 | ``` 163 | 164 | ![Untitled](xv6%20memory%20management%20walkthrough%2052f10c25c9dd4de39e601e386e6a1788/Untitled%205.png) 165 | 166 | - `PDX(va)` returns the first 10 bit. `pgdir` is level 1 page table. So, `pgdir[PDX(va)]` is level 1 PTE where there is PPN and offset 167 | 168 | ```cpp 169 | pde = &pgdir[PDX(va)]; 170 | ``` 171 | 172 | - `PTE_ADDR` return the first 20 bit or PPN. PPN is converted to VPN using `P2V` for finding 2nd level PTE. `pgtab` is level 2 page table 173 | 174 | ```cpp 175 | pgtab = (pte_t*)P2V(PTE_ADDR(*pde)); 176 | ``` 177 | 178 | - `PDX(va)` returns the second 10 bit. So, `pgtab[PTX(va)]` is level 2 PTE where there is PPN and offset 179 | 180 | ```cpp 181 | &pgtab[PTX(va)]; 182 | ``` 183 | 184 | 185 | ### **Case 2 : creating second-level page tables (When `alloc = 1`)** 186 | 187 | - return **NULL** if **not trying to make new page table** otherwise use `kalloc` to allocate it 188 | 189 | ```cpp 190 | if(!alloc || (pgtab = (pte_t*)kalloc()) == 0) 191 | return 0; 192 | ``` 193 | 194 | - clear the new second-level page table. Make sure all those **`PTE_P`** bits are zero. 195 | 196 | ```cpp 197 | memset(pgtab, 0, PGSIZE); 198 | ``` 199 | 200 | - now that we have made the 2nd level page table, we have to put that address in 1st level page table. plus add some flags 201 | 202 | ```cpp 203 | *pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U; 204 | ``` 205 | 206 | 207 | ## **`mappages`** 208 | 209 | ```cpp 210 | static int 211 | mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm) 212 | { 213 | char *a, *last; 214 | pte_t *pte; 215 | 216 | a = (char*)PGROUNDDOWN((uint)va); 217 | last = (char*)PGROUNDDOWN(((uint)va) + size - 1); 218 | for(;;){ 219 | if((pte = walkpgdir(pgdir, a, 1)) == 0) 220 | return -1; 221 | if(*pte & PTE_P) 222 | panic("remap"); 223 | *pte = pa | perm | PTE_P; 224 | if(a == last) 225 | break; 226 | a += PGSIZE; 227 | pa += PGSIZE; 228 | } 229 | return 0; 230 | } 231 | ``` 232 | 233 | As the name suggests, `mappages` adds a mapping from the virtual address (starting from given `VA` to `VA+SIZE`) to the physical address (starting from given `PA` to `PA+SIZE`) 234 | 235 | - for each virtual page in range, **get its page table entry**. if 2nd level not present allocate it (recall `alloc=1` case of `walkpgdir`). failure happens if runs out of memory 236 | 237 | ```cpp 238 | if((pte = walkpgdir(pgdir, a, 1)) == 0) 239 | return -1; 240 | ``` 241 | 242 | - make sure it’s not already set 243 | 244 | ```cpp 245 | if(*pte & PTE_P) 246 | panic("remap"); 247 | ``` 248 | 249 | - set page table entry to valid value pointing to physical page at `PA` with specified permission (`perm`) and `P` for present 250 | 251 | ```cpp 252 | *pte = pa | perm | PTE_P; // pa is first 20 bit, perm is flags, PTE_P is done to mark as present 253 | ``` 254 | 255 | - advance to next physical page (`PA`) and next virtual page (`VA`) 256 | 257 | ```cpp 258 | a += PGSIZE; 259 | pa += PGSIZE; 260 | ``` 261 | 262 | 263 | 264 | 265 | # **`Functions of interest {Part 2 : core functions}`** 266 | 267 | ## **`allocuvm`** 268 | 269 | Allocates user virtual memory (page tables and physical memory) to grow process. This function is responsible to **increase** the user's virtual memory in a specific page directory from `oldsz` to `newsz` . This function used for initial allocation plus expanding heap on request 270 | 271 | ```cpp 272 | int 273 | allocuvm(pde_t *pgdir, uint oldsz, uint newsz) 274 | { 275 | char *mem; 276 | uint a; 277 | 278 | if(newsz >= KERNBASE) 279 | return 0; 280 | if(newsz < oldsz) 281 | return oldsz; 282 | 283 | a = PGROUNDUP(oldsz); 284 | for(; a < newsz; a += PGSIZE){ 285 | mem = kalloc(); 286 | if(mem == 0){ 287 | cprintf("allocuvm out of memory\n"); 288 | deallocuvm(pgdir, newsz, oldsz); 289 | return 0; 290 | } 291 | memset(mem, 0, PGSIZE); 292 | if(mappages(pgdir, (char*)a, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0){ 293 | cprintf("allocuvm out of memory (2)\n"); 294 | deallocuvm(pgdir, newsz, oldsz); 295 | kfree(mem); 296 | return 0; 297 | } 298 | } 299 | return newsz; 300 | } 301 | ``` 302 | 303 | Returns `newsz` if succeeded, 0 otherwise. 304 | 305 | - walks the virtual address space between the old size and new size in page-sized chunks. For each new logical page to be created, it allocates a new free page from the kernel, and adds a mapping from the virtual address to the physical address by calling `mappages` 306 | - allocate a new, zero page 307 | 308 | ```cpp 309 | mem = kalloc(); 310 | ``` 311 | 312 | - add page to second-level page table, also do the allocation. recall that `mappages` call `walkpgdir` with `alloc = 1` 313 | 314 | ```cpp 315 | mappages(pgdir, (char*)a, PGSIZE, v2p(mem), PTE_W|PTE_U); 316 | ``` 317 | 318 | - There are indeed 2 cases where **this function can fail**: 319 | 320 | Case 1: `kalloc` (kernel allocation) function failed. This function is responsible to return an address of a **new, currently unused** page in RAM. If it **returns 0**, that means there are no available unused pages currently. 321 | 322 | Case 2: `mappages` function failed. This function is responsible of making the **new allocated page to be accessible by the process** who uses the given page directory by **mapping that page with the next virtual address available in the page directory**. If this function fails that means it failed in doing so, probably due to the page directory being already full. 323 | 324 | In both cases, `allocuvm` didn't managed to increase the user's memory to the size requested, Therefore, `deallocuvm` is undoing all allocations until the point of failure, so the virtual memory will remain unchanged, and returns an error it self. 325 | 326 | ## **`deallocuvm`** 327 | deallocuvm looks at all the logical pages from the (bigger) old size of the process to the (smaller) new size, locates the corresponding physical pages, frees them up, and zeroes out the corresponding PTE as well. 328 | 329 | 330 | ## **`copyuvm`** 331 | Once a child process is allocated, its memory image is setup as a complete copy of the parent’s memory image by a call to `copyuvm` 332 | 333 | - `setupkvm` : sets up kernel virtual memory 334 | - walks through the entire address space of the parent in page-sized chunks, gets 335 | the physical address of every page of the parent using a call to `walkpgdir` 336 | - allocates a new physical page for the child using `kalloc` 337 | - copies the contents of the parent’s page into the child’s page, **adds an entry to the child’s page table** using `mappages` 338 | - returns the child’s page table 339 | - has similar failure cases as discussed in `allocuvm` 340 | 341 | 344 | 345 | 346 | 347 | 348 | 366 | 367 | 374 | 375 | 376 | 377 | # **`How all of this works`** 378 | 379 | Now that we are done with the function basics, let’s focus on how things actually work. Most of the findings and intuition gained are from a shit ton of debug statements and by reading a few books and notes. So, take it with a grain of salt. 380 | 381 |   382 | 383 | The first user process is the `init` function. It is initiated by `userinit` inside `main` of `main.c`. 384 | 385 | Let’s focus on this first. 386 | 387 | ## **`init`** 388 | 389 | ### **`userinit` function** 390 | 391 | ```cpp 392 | //PAGEBREAK: 32 393 | // Set up first user process. 394 | void 395 | userinit(void) 396 | { 397 | struct proc *p; 398 | extern char _binary_initcode_start[], _binary_initcode_size[]; 399 | 400 | p = allocproc(); 401 | 402 | initproc = p; 403 | if((p->pgdir = setupkvm()) == 0) 404 | panic("userinit: out of memory?"); 405 | inituvm(p->pgdir, _binary_initcode_start, (int)_binary_initcode_size); 406 | p->sz = PGSIZE; 407 | memset(p->tf, 0, sizeof(*p->tf)); 408 | p->tf->cs = (SEG_UCODE << 3) | DPL_USER; 409 | p->tf->ds = (SEG_UDATA << 3) | DPL_USER; 410 | p->tf->es = p->tf->ds; 411 | p->tf->ss = p->tf->ds; 412 | p->tf->eflags = FL_IF; 413 | p->tf->esp = PGSIZE; 414 | p->tf->eip = 0; // beginning of initcode.S 415 | 416 | safestrcpy(p->name, "initcode", sizeof(p->name)); 417 | p->cwd = namei("/"); 418 | 419 | // this assignment to p->state lets other cores 420 | // run this process. the acquire forces the above 421 | // writes to be visible, and the lock is also needed 422 | // because the assignment might not be atomic. 423 | acquire(&ptable.lock); 424 | 425 | p->state = RUNNABLE; 426 | 427 | release(&ptable.lock); 428 | } 429 | ``` 430 | 431 | Now let’s see what things are done step by step 432 | 433 | - `allocproc` : for every process `pid` needs to be assigned, `proc` structure needs to be initialized. all of this is done here. for the `init` process `pid` is returned 1 by `allocproc` 434 | - `kalloc` : `allocproc` calls `kalloc` which sets up **data** on the **kernel stack** 435 | - `setupkvm` : creates **kernel page table** of this `init` process 436 | - `inituvm` : allocates **one page** of physical memory, copies the **init executable** into that memory, sets up a page table entry (PTE) for the first page of the user virtual address space. 437 | 438 | Let’s also keep track of how many pages are being allocated where, this will be key to our understanding when we want to find what which pages are being deallocated in `deallocuvm` in later part. 439 | 440 | ```cpp 441 | PAGE ALLOCATED HERE = 1 442 | ``` 443 | 444 | 445 | ### **`init executable`** 446 | 447 | now the **init executable** that has been loaded runs, which to my understanding points to this part inside code 448 | 449 | ![Untitled](How%20all%20of%20this%20works%207868ca41311a42b5ba45aa4e1b1033fd/Untitled.png) 450 | 451 | ### **`exec`** 452 | 453 | `SYS_exec` in turn calls `exec` . The `exec` function looks like this - 454 | 455 | ```cpp 456 | int 457 | exec(char *path, char **argv) 458 | { 459 | char *s, *last; 460 | int i, off; 461 | uint argc, sz, sp, ustack[3+MAXARG+1]; 462 | struct elfhdr elf; 463 | struct inode *ip; 464 | struct proghdr ph; 465 | pde_t *pgdir, *oldpgdir; 466 | struct proc *curproc = myproc(); 467 | 468 | begin_op(); 469 | 470 | if((ip = namei(path)) == 0){ 471 | end_op(); 472 | cprintf("exec: fail\n"); 473 | return -1; 474 | } 475 | ilock(ip); 476 | pgdir = 0; 477 | 478 | // Check ELF header 479 | if(readi(ip, (char*)&elf, 0, sizeof(elf)) != sizeof(elf)) 480 | goto bad; 481 | if(elf.magic != ELF_MAGIC) 482 | goto bad; 483 | 484 | if((pgdir = setupkvm()) == 0) 485 | goto bad; 486 | 487 | // Load program into memory. 488 | sz = 0; 489 | for(i=0, off=elf.phoff; i= MAXARG) 521 | goto bad; 522 | sp = (sp - (strlen(argv[argc]) + 1)) & ~3; 523 | if(copyout(pgdir, sp, argv[argc], strlen(argv[argc]) + 1) < 0) 524 | goto bad; 525 | ustack[3+argc] = sp; 526 | } 527 | ustack[3+argc] = 0; 528 | 529 | ustack[0] = 0xffffffff; // fake return PC 530 | ustack[1] = argc; 531 | ustack[2] = sp - (argc+1)*4; // argv pointer 532 | 533 | sp -= (3+argc+1) * 4; 534 | if(copyout(pgdir, sp, ustack, (3+argc+1)*4) < 0) 535 | goto bad; 536 | 537 | // Save program name for debugging. 538 | for(last=s=path; *s; s++) 539 | if(*s == '/') 540 | last = s+1; 541 | safestrcpy(curproc->name, last, sizeof(curproc->name)); 542 | 543 | // Commit to the user image. 544 | oldpgdir = curproc->pgdir; 545 | curproc->pgdir = pgdir; 546 | curproc->sz = sz; 547 | curproc->tf->eip = elf.entry; // main 548 | curproc->tf->esp = sp; 549 | 550 | switchuvm(curproc); 551 | 552 | freevm(oldpgdir); 553 | 554 | return 0; 555 | 556 | bad: 557 | if(pgdir) 558 | freevm(pgdir); 559 | if(ip){ 560 | iunlockput(ip); 561 | end_op(); 562 | } 563 | return -1; 564 | } 565 | ``` 566 | 567 | - `setupkvm` : The thing that I understood so far is that, the 1 page allocated inside `inituvm` is responsible for executing this exec function. **Whatever this exec function now wants to execute**, will be in a separate new kernel page table, hence a `setupkvm` is called once more. (what I exactly mean here will be more clear when I talk about other user processes) 568 | - `allocuvm` : allocates pages for the executable it wants to run, here 1 page is sufficient for `init` 569 | 570 | ```cpp 571 | PAGE ALLOCATED HERE = 1 572 | ``` 573 | 574 | - `loaduvm` : loads the executable from disk into the newly allocated pages in `allocuvm` 575 | - `allocuvm` : the new memory image so far only has executable **code and data**, now we also need **stack** space. Rather than allocating 1 page for the stack, it allocates 2. The 2nd one is the actual stack, 1st one serves as a guard page. This is the only page in the user’s memory which is marked as present but not user-accessible (`PTE_U` is cleared) 576 | 577 | ***Reason for having a guard page :*** To guard a stack growing off the stack page, xv6 places a guard page right below the stack. As the guard page is not mapped, if the stack runs off the stack page, the **hardware** will generate an **exception** because it cannot translate the faulting address. 578 | 579 | Strings containing the **command-line arguments**, as well as an **array of pointers** to them, are at the very top of the stack 580 | 581 | ![Untitled](How%20all%20of%20this%20works%207868ca41311a42b5ba45aa4e1b1033fd/Untitled%201.png) 582 | 583 | ```cpp 584 | PAGE ALLOCATED HERE = 2 585 | ``` 586 | 587 | - **trap frame update** : It is important to note that exec **does not replace/reallocate** the kernel stack. The exec system call only replaces the **user part of the memory image**, and does nothing to the kernel part. And if you think about it, there is no way the process can replace the kernel stack, because the process is executing in kernel mode on the kernel stack itself, and has important information like the trap frame stored on it. 588 | 589 | Recall that a process that makes the exec system call has moved into kernel mode to service 590 | the software interrupt of the system call. Normally, when the process moves back to user mode again (**by popping the trap frame on the kernel stack**), it is expected to return to the instruction after the system call. However, in the case of exec, the process doesn’t have to return to the instruction after exec when it gets the CPU next, but instead must start executing the new executable it just loaded from disk. So, the code in exec changes the return address in the trap frame to point to the entry address of the binary. It also sets the stack pointer in the trap frame to point to the top of the newly created user stack. 591 | 592 | ```cpp 593 | curproc->tf->eip = elf.entry; // main 594 | curproc->tf->esp = sp; 595 | ``` 596 | 597 | - `switchuvm` : Finally, once all these operations succeed, `exec` **switches page tables** to start using the new memory image. That is, it writes the address of the new page table into the CR3 register, so that it can start accessing the new memory image when it goes back to userspace. 598 | - `freevm` : the process that called exec i.e. `init` , was still using the old memory image. we free the old memory image that it was pointing to after updating 599 | - `deallocuvm` : The pages that are deleted here are the pages that were created in `inituvm` . So, 600 | 601 | ```cpp 602 | PAGE DEALLOCATED HERE = 1 603 | ``` 604 | 605 | 606 | The new memory image created by exec looks like this 607 | 608 | ![Untitled](How%20all%20of%20this%20works%207868ca41311a42b5ba45aa4e1b1033fd/Untitled%202.png) 609 | 610 | At this point, the process that called `exec` can start executing on the new memory image 611 | when it returns from trap 612 | 613 | **Note** : exec waits until the end to do this switch of page tables, because if anything went wrong in the system call, exec returns from trap into the old memory image and prints out an error 614 | 615 | ## `sh` 616 | 617 | Apart from `init`, all other processes are created by the fork system call. When the `init` process runs, it executes the **init executable**, whose main function forks a shell (`sh`) and starts listening to the user 618 | 619 | ### `fork` 620 | 621 | - `allocproc` : returns `pid = 2` for `sh` 622 | - `copyuvm` : once a child process is allocated, its memory image is setup as a complete copy of the parent’s memory. This is done using `copyuvm` . 623 | - `setupkvm` : inside copyuvm, a new kernel page table is created, and parent memory is allocated here. 624 | 625 | Recall that 3 pages were created in `exec` of `init` . These 3 pages are copied here 626 | 627 | ```cpp 628 | PAGES ALLOCATED HERE = 3 629 | ``` 630 | 631 | 632 | ### `exec` 633 | 634 | The description of exec for shell is the same as discussed for `init` . So lets just find out how many pages are allocated where 635 | 636 | - `setupkvm` 637 | - `allocuvm` 638 | 639 | The executable that `sh` wants to run needs more memory than `init` needed. So 2 pages are allocated here. 640 | 641 | ```cpp 642 | PAGES ALLOCATED HERE = 2 643 | ``` 644 | 645 | - `loaduvm` 646 | - `allocuvm` 647 | - `freevm` 648 | - `deallocuvm` The pages that were allocated (copied) using `copyuvm` are deallocated as they are pointing to old page directory 649 | 650 | ```cpp 651 | PAGE DEALLOCATED HERE = 3 652 | ``` 653 | 654 | 655 | Now let’s look at a general user process. Every other user process will fork `sh` . Let’s look at `echo` 656 | 657 | ## `echo` 658 | 659 | ### `fork` 660 | 661 | - `allocproc` 662 | - `copyuvm` : here 4 page will be copied (2 + 2 pages that were allocated inside `sh` exec) 663 | 664 | ```cpp 665 | PAGE ALLOCATED HERE = 4 666 | ``` 667 | 668 | 669 | ### `growproc` 670 | 671 | After fork, `growproc` is called to grow userspace memory image. `growproc` can be called by the `sbrk` system call. It basically increases the heap size 672 | 673 | ```cpp 674 | PAGE ALLOCATED HERE = 8 675 | ``` 676 | 677 | ### `exec` 678 | 679 | - `allocuvm` 680 | 681 | ```cpp 682 | PAGE ALLOCATED HERE = 1 683 | ``` 684 | 685 | - `loaduvm` 686 | - `allocuvm` 687 | 688 | ```cpp 689 | PAGE ALLOCATED HERE = 2 690 | ``` 691 | 692 | - `freevm` 693 | - `deallocuvm` : 4+8 pages deleted of old memory image 694 | 695 | ```cpp 696 | PAGE DEALLOCATED HERE = 12 697 | ``` 698 | 699 | 700 | ### `exit` 701 | 702 | - `freevm` 703 | - `deallocuvm` : 1+2 pages deleted of new memory image 704 | 705 | ```cpp 706 | PAGE DEALLOCATED HERE = 3 707 | ``` 708 | 709 | Finally, an overview of all the things said in a picture - because ofcourse a picture speaks more than a thousand words! 710 | 711 | ![Untitled](How%20all%20of%20this%20works%207868ca41311a42b5ba45aa4e1b1033fd/Untitled%203.png) 712 | 713 | --- 714 | 715 | # **`Final Words`** 716 | 717 | Thanks for reading this far ! I am not sure how much helpful this has been for you, but I hope you can start navigating the memory management part of the xv6 codebase after reading this. Go through this writeup multiple times if you don't undersand anything and if you have some more time to spare, please go through [this](https://www.cse.iitb.ac.in/~mythili/os/notes/old-xv6/xv6-memory.pdf) doc. A lot of the part of this writeup has been taken or inspired from this. 718 | 719 | May the force be with you! ***And if you liked the write-up, you can support me by buying me a cup of coffee!*** 720 |

721 | Buy Me A Coffee 722 |

723 | 724 | # **`Resource`** 725 | 726 | - Xv6 book 727 | - [Memory Management in Xv6](https://www.cse.iitb.ac.in/~mythili/os/notes/old-xv6/xv6-memory.pdf) 728 | - [https://www.cs.virginia.edu/~cr4bd/4414/F2018/slides/20181011--slides-1up.pdf](https://www.cs.virginia.edu/~cr4bd/4414/F2018/slides/20181011--slides-1up.pdf) 729 | - [https://www.cs.virginia.edu/~cr4bd/4414/F2019/slides/20191015--slides-1up.pdf](https://www.cs.virginia.edu/~cr4bd/4414/F2019/slides/20191015--slides-1up.pdf) 730 | 731 | - [https://iitd-plos.github.io/os/2020/lec/l15.html](https://iitd-plos.github.io/os/2020/lec/l15.html) 732 | 733 | - [https://pdos.csail.mit.edu/6.828/2009/lec/l5.html](https://pdos.csail.mit.edu/6.828/2009/lec/l5.html) 734 | - [http://course.ece.cmu.edu/~ece447/s13/lib/exe/fetch.php?media=onur-447-spring13-lecture18-virtual-memory-iii-afterlecture.pdf](http://course.ece.cmu.edu/~ece447/s13/lib/exe/fetch.php?media=onur-447-spring13-lecture18-virtual-memory-iii-afterlecture.pdf) 735 | - [https://github.com/YehudaShapira/xv6-explained](https://github.com/YehudaShapira/xv6-explained) 736 | - [https://stackoverflow.com/questions/56258056/what-does-deallocation-function-in-xv6s-allocation-function](https://stackoverflow.com/questions/56258056/what-does-deallocation-function-in-xv6s-allocation-function) 737 | - [https://www.cs.columbia.edu/~junfeng/11sp-w4118/lectures/mem.pdf](https://www.cs.columbia.edu/~junfeng/11sp-w4118/lectures/mem.pdf) 738 | -------------------------------------------------------------------------------- /xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zarif98sjs/xv6-memory-management-walkthrough/8025057e75f397d006062566a67efb1284b29913/xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled 1.png -------------------------------------------------------------------------------- /xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zarif98sjs/xv6-memory-management-walkthrough/8025057e75f397d006062566a67efb1284b29913/xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled 2.png -------------------------------------------------------------------------------- /xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zarif98sjs/xv6-memory-management-walkthrough/8025057e75f397d006062566a67efb1284b29913/xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled 3.png -------------------------------------------------------------------------------- /xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zarif98sjs/xv6-memory-management-walkthrough/8025057e75f397d006062566a67efb1284b29913/xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled 4.png -------------------------------------------------------------------------------- /xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zarif98sjs/xv6-memory-management-walkthrough/8025057e75f397d006062566a67efb1284b29913/xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled 5.png -------------------------------------------------------------------------------- /xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled 6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zarif98sjs/xv6-memory-management-walkthrough/8025057e75f397d006062566a67efb1284b29913/xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled 6.png -------------------------------------------------------------------------------- /xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled 7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zarif98sjs/xv6-memory-management-walkthrough/8025057e75f397d006062566a67efb1284b29913/xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled 7.png -------------------------------------------------------------------------------- /xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zarif98sjs/xv6-memory-management-walkthrough/8025057e75f397d006062566a67efb1284b29913/xv6 memory management walkthrough 52f10c25c9dd4de39e601e386e6a1788/Untitled.png --------------------------------------------------------------------------------