XV6ソースコードリーダ - 仮想メモリ管理

Exercise1ソースコードリーダ

1.メモリ管理:kalloc.c vm.cとコードに関連するその他の文書

  • kalloc.c:するchar * kalloc(ボイド)ユーザ空間、カーネルスタック、ページテーブルと必要な物理メモリのページバッファの割り当てを担当して、物理アドレスへの仮想アドレスが返され、物理ページサイズは4Kです。空kfree(CHAR * V)がリリースされる対応する物理アドレスを見つけるために、仮想アドレスを受け取ります。次の空きメモリを指すようにポインタフィールドとして空きメモリのxv6前部を使用し、メモリ管理ユニットに割り当てられている物理ページ(4K)です。メモリの空き領域の各物理ページには、ポインタフィールドは、(仮想アドレス)が空きページを指し、そして最終的には自由なページがNULLであり、このような方法で、唯一、すなわち仮想アドレス空間上の空きリストのkmemアドレスを保持していますことができます。
// kalloc.c
// Physical memory allocator, intended to allocate
// memory for user processes, kernel stacks, page table pages,
// and pipe buffers. Allocates 4096-byte pages.

void freerange(void *vstart, void *vend);
extern char end[]; // first address after kernel loaded from ELF file

struct run {
  struct run *next;
};

struct {
  struct spinlock lock;
  int use_lock;
  struct run *freelist;
} kmem;
  • xv6スイッチングプロセスが常にページテーブルを切り替える必要がある場合に、各プロセスは、独自のページテーブル構造を持つように、設定CR3レジスタはkpgdir最初のアドレスであるswitchkvm、kpgdirは、スケジューラのカーネルスレッドを使用していました。切り替えのプロセスは、彼らはまた、切り替え処理で切り替えされる傾向にあるときに、ページテーブルと、各プロセスのカーネルスタックが一意である、xv6利用proc構造体は、それらを統一し、カーネルは、カーネルスレッドをシミュレートし、それカーネルスタックとカーネルのページテーブルkpgdir排他的な、それはすべてのプロセススケジューリングのための基礎です。TSS関連の操作を含んでいる固有のデータ構造に関連するプロセスを切り替えるための責任proc構造体を渡すことswitchuvm、およびCR3レジスタをロードし、プロセスの特定のページテーブルは、セットアッププロセスに関連する仮想アドレス空間の環境を完了する。
// vm.c

……

// Switch h/w page table register to the kernel-only page table,
// for when no process is running.
void
switchkvm(void)
{
  lcr3(v2p(kpgdir));   // switch to the kernel page table
}

// Switch TSS and h/w page table to correspond to process p.
void
switchuvm(struct proc *p)
{
  pushcli();
  cpu->gdt[SEG_TSS] = SEG16(STS_T32A, &cpu->ts, sizeof(cpu->ts)-1, 0);
  cpu->gdt[SEG_TSS].s = 0;
  cpu->ts.ss0 = SEG_KDATA << 3;
  cpu->ts.esp0 = (uint)proc->kstack + KSTACKSIZE;
  ltr(SEG_TSS << 3);
  if(p->pgdir == 0)
    panic("switchuvm: no pgdir");
  lcr3(v2p(p->pgdir));  // switch to new address space
  popcli();
}
  • カーネルにページテーブルは、アドレスの低い一部のプロセスの仮想アドレス空間は、高アドレス部分は、カーネル用に予約されたときに、プロセスのページテーブルは、多くの場合、カーネルコードのマッピングを含める必要があり、使用前に初期化する必要があり、このプロセスが再び切り替える必要がなくなり、メインインターフェイス:
    • pde_t * setupkvm(ボイド)、物理アドレス空間、ユーティリティ関数呼び出しmappages期間にテクスチャマッピングデータKMAPカーネル仮想アドレス空間に合わせて、ページディレクトリとしてkallocメモリを介して分配しました。
    • int型allocuvm(pde_t * pgdir、UINT oldsz、UINT newsz)ページのメモリユニットのoldsz newszながら、ページテーブルに提供される仮想アドレスを割り当てられました。
    • int型deallocuvm(pde_t * pgdir、UINT oldsz、UINT newsz)アイドルメモリに仮想アドレス空間に対応するoldsz newszます。
    • INT loaduvm(pde_t *はpgdir、チャー*のADDR、構造体iノード*のIP、UINT SZ、オフセットUINT)iノード・システムはallocuvm割り当てメモリと設定によって、ユーザ・インターフェース・プロセスに対応するアドレスにロードされたファイルの内容を読み取りページテーブルは、その後、メモリにロードされているファイルシステム上のloaduvmインタフェースプログラムを呼び出し、それがユーザプロセスの正式な動作のために準備するexecシステムコールのためのインタフェースを提供することができます。
    • プロセスが回復メモリの破壊を必要とする場合、(pde_t * pgdir)クリアユーザプロセスのメモリ関連の環境ボイドfreevmを呼び出して、最初の呼び出しはKERNBASE仮想アドレス空間に0を回復し、その後、全体のプロセスのページテーブルを破壊されます。
    • pde_t * copyuvm(pde_t * pgdir、UINT SZ)は、新たなページテーブルをコピーして、新しいメモリを割り当てるための責任があり、新旧メモリレイアウトはまったく同じです、xv6はフォークとして、この機能を使用する()基礎となる実装。

読書とExercise2問題

1.XV6後のmain.cを実行するように初期化すると、メモリレイアウトは(何を持っている)のようなものですか?

  • カーネルコードは、低いアドレスに物理アドレス0X100000に存在し、ページ・テーブル・アレイentrypgdir main.cのファイル、前記仮想アドレス低い4M 4M低い物理アドレスマッピング、仮想アドレス[KERNBASE、KERNBASE + 4メガバイト)は、物理アドレス[0にマッピングされ、4メガバイト)。

  • 次いで、物理メモリの4M物理メモリ空間が使用されていない、未使用PHYSTOP残りカーネル空間を初期化するためにkinit2呼び出しkinit1カーネルの初期化の呼び出しの終わり。kinit1最初のページの前に呼び出されるか、(すなわち上記メモリレイアウトこと、である)テーブルを使用して、唯一の4Mを初期化することができ、そしてためにも場合後者の実際の物理ストレージのページ・テーブルを検索するために新しいページ・テーブルを再構築するページテーブル変換メカニズムを使用しますマルチコアCPUの初めに開始されていないので、ロック機構を設定していないとして、ブートストラップの問題を構成するメモリ空間は、ページテーブルを割り当てるためのスペースの4Mビットのカーネルリリースの終わりの初めに主な機能によってxv6。カーネルでkinit2後、カーネル仮想アドレス空間へのフルアクセスを新しいページテーブルを構築するので、ここで我々はすべての物理メモリを初期化し、空きメモリリンクリストの保護のためのロック機構を開始しました。
  • そして、メインのカーネル初期化機能が無効kvmalloc(空)関数を呼び出して新しいページテーブルを達成するために、
  • 物理アドレスのカーネルメモリ空間の終わりに未使用の物理アドレスPHYSTOP、下位アドレスの物理メモリ位置の対応KERNBASE複数の部分にマッピングされた仮想アドレス空間に以下のように最後に、アドレス空間およびメモリ・レイアウトです。

// kalloc.c
// Initialization happens in two phases.
// 1. main() calls kinit1() while still using entrypgdir to place just
// the pages mapped by entrypgdir on free list.
// 2. main() calls kinit2() with the rest of the physical pages
// after installing a full page table that maps them on all cores.
void
kinit1(void *vstart, void *vend)
{
  initlock(&kmem.lock, "kmem");
  kmem.use_lock = 0;
  freerange(vstart, vend);
}

void
kinit2(void *vstart, void *vend)
{
  freerange(vstart, vend);
  kmem.use_lock = 1;
}

// kmap.c
……
// This table defines the kernel's mappings, which are present in
// every process's page table.
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
};
……

2.XV6動的なメモリ管理がどのように行われるか?物理メモリページを管理するために使用されるのkmem(リスト)は、割り当て可能があります。(VEND = 0x00400000、4Mビットのメモリページを割り当てることができる最大である)
の部分を「読むために演習1ソースコード」を参照してください、私たちは完全な答えをしました。

どのように3.XV6仮想メモリが初期化される?仮想メモリのレイアウト図XV6を描き、どのような対応するコンテンツの各部分がそう言ってください。のKMAP memlayout.hとvm.cに注意してください?

  • 空kinit1(void *型VSTART、void *型の自動販売機販売)、無効kinit2(void *型VSTART、void *型の自動販売機販売)、無効kvmalloc(ボイド)を呼び出すことにより、主な機能は、新しいページ・テーブルを実現するためにカーネル関数を初期化します。変換インターフェースの仮想アドレスと物理アドレス:
// memlayout.h
// Memory layout

#define EXTMEM  0x100000            // Start of extended memory
#define PHYSTOP 0xE000000           // Top physical memory
#define DEVSPACE 0xFE000000         // Other devices are at high addresses

// Key addresses for address space layout (see kmap in vm.c for layout)
#define KERNBASE 0x80000000         // First kernel virtual address
#define KERNLINK (KERNBASE+EXTMEM)  // Address where kernel is linked

#ifndef __ASSEMBLER__

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

#endif

#define V2P(a) (((uint) (a)) - KERNBASE)
#define P2V(a) (((void *) (a)) + KERNBASE)

#define V2P_WO(x) ((x) - KERNBASE)    // same as V2P, but without casts
#define P2V_WO(x) ((x) + KERNBASE)    // same as V2P, but without casts
  • メモリレイアウト:

4.关于XV6 的内存页式管理。发生中断时,用哪个页表? 一个内存页是多大? 页目录有多少项? 页表有多少项? 最大支持多大的内存? 画出从虚拟地址到物理地址的转换图。在XV6中,是如何将虚拟地址与物理地址映射的(调用了哪些函数实现了哪些功能)?

  • 发生中断时,将换入cpu的进程的页表首地址存入cr3寄存器;一个内存页为4k;XV6页表采用的二级目录,一级目录有\(2^{10}\)条,二级目录有\(2^{10} * 2^{10}\)条;页表项为\(2^2\)Bytes,故页表有\(2^{12} / 2^2 = 2^{10} = 1024\)项;最大支持4G内存;

  • 物理内存页的申请与释放,虚拟地址与物理地址如何映射等在“Exercise 1 源代码阅读”都已经详述了,在此主要说下mappages接口,虚拟地址 * va与物理地址 * pa映射size个字节,同时赋予该页的权限perm,如下:
// vm.c
……
// Create PTEs for virtual addresses starting at va that refer to
// physical addresses starting at pa. va and size might not
// be page-aligned.
static int
mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm)
{
  char *a, *last;
  pte_t *pte;
  
  a = (char*)PGROUNDDOWN((uint)va);
  last = (char*)PGROUNDDOWN(((uint)va) + size - 1);
  for(;;){
    if((pte = walkpgdir(pgdir, a, 1)) == 0)
      return -1;
    if(*pte & PTE_P)
      panic("remap");
    *pte = pa | perm | PTE_P;
    if(a == last)
      break;
    a += PGSIZE;
    pa += PGSIZE;
  }
  return 0;
}
……

参考文献

[1] xv6虚拟内存-博客园
[2] xv6 virtual memory-hexo
[3] xv6内存管理-简书
[4] xv6内存管理-CSDN

おすすめ

転載: www.cnblogs.com/icoty23/p/10993861.html