日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

高级操作系统——XV6内存管理

發布時間:2023/12/9 windows 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 高级操作系统——XV6内存管理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

xv6閱讀報告

一:XV6 執行 main.c 時,內存布局是怎樣的(其中已有哪些內容)?
首先經過entry.S,使得
1:內核代碼存在于物理地址低地址的0x100000處具體怎樣實現的不清楚?
2:通過頁表為main.c文件中的entrypgdir數組,使得虛擬地址 [KERNBASE, KERNBASE+4MB) 映射到物理地址[0, 4MB)

pde_t entrypgdir[NPDENTRIES] = {//Map VA's [0, 4MB) to PA's [0, 4MB)[0] = (0) | PTE_P | PTE_W | PTE_PS,//|或操作// Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB)[KERNBASE>>PDXSHIFT] = (0) | PTE_P | PTE_W | PTE_PS, };

其中PTE_P =0x001 PTE_W=0x002 PTE_U=0x004 PDXSHIFT=22(4MB)具體怎樣實現的不清楚?

二: XV6 的動態內存管理是如何完成的? 有一個 kmem(鏈表),用于管理可分配的物理內
存頁。(vend=0x00400000,也就是可分配的內存頁最大為 4Mb)
首先明確動態內存管理是什么?指的是物理地址如何管理的(與虛擬無關)
XV6將物理地址按照頁(4MB)連接成鏈表,當需要申請一個物理頁時,就從freelist中摘下一頁

struct {struct spinlock lock;int use_lock;struct run *freelist; } kmem;

可以看到主要擁有數據結構struct run *freelist,這個類似于利用鏈表管理頁表當想分配頁表時,利用函數kalloc(void),當想回收頁表時,利用函數kfree(char *v)

那么分配器從哪里獲得內存來存放這些數據結構呢?實際上,分配器將每個空閑頁的 run 結構體保存在該空閑頁本身中,因為空閑頁中沒有其他數據。分配器還用一個 spin lock來保護空閑鏈表。鏈表和這個鎖都封裝在一個結構體中,這樣邏輯就比較明晰:鎖保護了該結構體中的域

那么freelist是如何初始化的呢?
是通過main.cc里的kinit1和kinit2進行初始化
kinit1(end, P2V(410241024))
kinit2(P2V(410241024), P2V(PHYSTOP))
1:從這里我們可以看到,是將end(這里是kernal data=end)到PHYSTOP之間的物理地址初始化給freelist因為0x0到0x01000000分配給I/O,0x01000000-kernal data分配給內核數據和代碼。那至于PHYSTOP到-0xffffffff的數據如何初始化,我也不知道
2:在memlayout.h中有如下定義

static inline uint v2p(void *a) { return ((uint) (a)) - KERNBASE; } static inline void *p2v(uint a) { return (void *) ((a) + KERNBASE); }

可以看到xv6中物理地址轉為虛擬地址直接+KERNBASE,原因在于xv6將
虛擬地址KERNBASE:KERNBASE+PHYSTOP直接映射為物理地址的0:PHYSTOP所以直接加減很方便的實現轉化

為什么要分開成兩部初始化?
我的理解是kinit1使用的是頁表entrypgdir,所以只能初始化4MB,而kinit2使用的是kpgdir ,所可以初始化其他部分。同時由于后期再構建新頁表時也要使用頁表轉換機制來找到實際存放頁表的物理內存空間,這就構成了自舉問題具體怎樣實現的不清楚?

初始化操作解讀:以kinit1為例子

void kinit1(void *vstart, void *vend) {initlock(&kmem.lock, "kmem");kmem.use_lock = 0;freerange(vstart, vend); }

可以看到調用freerange,freerange可以理解為初始化vstart到vend的物理頁

void freerange(void *vstart, void *vend) {char *p;p = (char*)PGROUNDUP((uint)vstart);for(; p + PGSIZE <= (char*)vend; p += PGSIZE)kfree(p); }

可以看到vstart轉化成p ,根據判斷 p + PGSIZE <= (char*)vend即一次初始化一頁,知道到達vend停止,kfree為初始化一頁

voidkfree(char *v){struct run *r;if((uint)v % PGSIZE || v < end || v2p(v) >= PHYSTOP)panic("kfree");// Fill with junk to catch dangling refs.memset(v, 1, PGSIZE);if(kmem.use_lock)acquire(&kmem.lock);r = (struct run*)v;r->next = kmem.freelist;kmem.freelist = r;if(kmem.use_lock)release(&kmem.lock);}

可以看到kfree主要執行
1:判斷是否越界 小于end或者大于PHYSTOP
2:將物理地址對應的值初始化為1
3:將物理頁放在freelist上

三: XV6 的虛擬內存是如何初始化的?畫出 XV6 的虛擬內存布局圖,請說出每一部分對應的
內容是什么。 見 memlayout.h 和 vm.c 的 kmap 上的注釋
首先明白什么是虛擬內存是如何初始化?其實這里應該理解為如何為進程的虛擬空間的內核空間分配物理頁。首先明確幾點
1:進程的虛擬空間分為用戶空間和內核空間,每個進程的用戶空間單獨享有,內核空間共享。用戶空間是在創建進程的時候分配,內核空間實在操作系統初始化后分配
2:因為用戶的內核空間目前還只有虛擬頁,理解為用戶空間的內核空間第i頁有什么呢?內核代碼?沒有,內核數據?沒有,因為此時還沒有對應物理頁,所以我們需要建立虛擬空間到物理空間的映射關系

虛擬內存的初始化是main.c中的kvmalloc()執行

kvmalloc(void) {kpgdir = setupkvm();switchkvm(); }

可以看到關鍵是setupkvm,返回一個目錄kpgdir ,這就是用戶的內核空間頁表[PTE]

pde_t* setupkvm(void) {pde_t *pgdir;struct kmap *k;//分配頁表if((pgdir = (pde_t*)kalloc()) == 0)return 0;memset(pgdir, 0, PGSIZE);if (p2v(PHYSTOP) > (void*)DEVSPACE)panic("PHYSTOP too high");for(k = kmap; k < &kmap[NELEM(kmap)]; k++)//kmap[NELEM(kmap)]=kmap[4]if(mappages(pgdir, k->virt, k->phys_end - k->phys_start, (uint)k->phys_start, k->perm) < 0)//return 0;return pgdir; }

setupkvm主要完成以下工作:

1:if((pgdir=(pde_t*)kalloc()) == 0) kalloc()為申請一頁物理地址,返回為pgdir。
補充:注意到kalloc()

kalloc(void) {struct run *r;if(kmem.use_lock)acquire(&kmem.lock);r = kmem.freelist;if(r)kmem.freelist = r->next;if(kmem.use_lock)release(&kmem.lock);return (char*)r; }

是從freelist中申請一頁,而這一頁正好是我們kinit1初始化的!
2:完成映射整塊【很多頁】虛擬地址到物理地址的映射工作,相當于此時pgdir就已經是一項項頁表,內含物理地址,虛擬地址,PTE_P,PTE_W,PTE_PS等等
這里我們必須看一下

static struct kmap {void *virt;uint phys_start;uint phys_end;int perm; } kmap[] = {{ (void*)KERNBASE, 0, EXTMEM, PTE_W}, // I/O space{ (void*)KERNLINK, V2P(KERNLINK), V2P(data), 0}, // kern text+rodata 內核代碼加數據{ (void*)data, V2P(data), PHYSTOP, PTE_W}, // kern data+memory{ (void*)DEVSPACE, DEVSPACE, 0, PTE_W}, // more devices };

kmap由四值組成,分別是虛擬地址 ,物理起始地址,物理結束地址,是否可讀
而執行具體的初始化頁表項的操作是由mappages完成

mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm) {char *a, *last;pte_t *pte;a = (char*)PGROUNDDOWN((uint)va);//將va轉為起始地址a last = (char*)PGROUNDDOWN(((uint)va) + size - 1);//計算結束地址last for(;;){//一直for循環if((pte = walkpgdir(pgdir, a, 1)) == 0)//完成一頁虛擬地址到物理地址的映射return -1;if(*pte & PTE_P)panic("remap");*pte = pa | perm | PTE_P;if(a == last)//當起始地址達到結束地址時,跳出for循環break;a += PGSIZE;pa += PGSIZE;}return 0; }

mappages主要完成起始就是從起始地址a到結束地址last,將其中的虛擬地址映射為物理地址。而walkpgdir是將一頁虛擬與物理的映射

static pte_t * walkpgdir(pde_t *pgdir, const void *va, int alloc) {pde_t *pde;pte_t *pgtab;pde = &pgdir[PDX(va)];if(*pde & PTE_P){pgtab = (pte_t*)p2v(PTE_ADDR(*pde));} else {if(!alloc || (pgtab = (pte_t*)kalloc()) == 0)return 0;memset(pgtab, 0, PGSIZE);*pde = v2p(pgtab) | PTE_P | PTE_W | PTE_U;}return &pgtab[PTX(va)]; }

可以看到 *pde = v2p(pgtab) | PTE_P | PTE_W | PTE_U;這里可以理解為pde 就含有物理地址,是否存在,是否寫,屬于哪個用戶信息
至此,分配結束

此時我們可以看一下,整個虛擬空間和內核空間的映射如下
僅僅是個人猜測
關于對應關系:
首先說明一些宏定義:
虛擬地址:
#define KERNBASE 0x80000000 // 內核虛擬地址的起始位置
#define EXTMEM 0x100000 // 擴展內存的起始位置
#define KERNLINK (KERNBASE+EXTMEM) //內核的連接地址
#define DEVSPACE 0xFE000000 // 其余設備在高地址空間
物理地址:
#define PHYSTOP 0xE000000 // 224M的物理內存
說明:0xFFFFFFFF不知道書寫是否正確,反正就是到頂

總結

以上是生活随笔為你收集整理的高级操作系统——XV6内存管理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。