おさらい

おさらい

  • 仮想アドレス <-> 物理アドレスの変換をするのがページング
    • 実際にはTLBに対象のアドレスがある場合はそちらが使われる
      • TLBとアドレスの対応を同期させるために tlb flushが必要になる
  • プロセスを造ったとき, 拡張したい時などに使われる
  • ゼロページ
    • ゼロが入っているページは書き込まれるまではすべてのプロセスが見ていていい
      • 書き込まれた場合に初めて物理メモリに割当をする
        • copy on write…
  • 仮想メモリにCPUがアクセスに行くと、ディスクをコピって制御する必要がある
    • 一旦追い出して、コピった後はtlb flushする必要がある
    • いろいろしたら tlb flush して trap return

# Xv6では

  • memlayout.h
    • v2p(仮想メモリ -> 実メモリ)などを制御するマクロが宣言されている
  • mmu.h
    • 実際に使用されるページに関するマクロが定義
    • おおよそ定数を指定しているもの, ビット演算が書かれているものがある
    • user page table sizeは4K

# vm.c

  • 大体このファイルで実装されていそう
  • staticがついているものはprivate codegear
  • xv6は4K
  • ARMは1K
    • 1つのブロックを4つに分ける
    • pde_t *kpgdir; // for use in scheduler()
      • スケジューラーに値を渡すためのglobal variable
      • contextに書き込めば問題なさそう
    • kpt_mem とかはDataGearにすれば良さそう

# init_vmm

1
2
3
4
5
void init_vmm (void)
{
    initlock(&kpt_mem.lock, "vm");
    kpt_mem.freelist = NULL;
}
  • lockのとこに0をいれるだけ
    • 0だったら誰も使ってない
    • 使っている人のidをいれるのが本来
  • freelistをNULLにしてるのはclearにしている
  • init_vmmは外から呼び出されるのでInterfaceにするべき

# _kpt_free

  • メモリページ全体をlinked listにしているrを取り出す
    • vで指しているページテーブルをフリーリストにつなげる
    • このリストは物理メモリで作られている
1
2
3
4
5
6
7
8
static void _kpt_free (char *v)
{
    struct run *r;

    r = (struct run*) v;
    r->next = kpt_mem.freelist;
    kpt_mem.freelist = r;
}

# ktp_free

  • kfreeがカーネルのメモリフリー
  • acquireとreleaseをしているので並列によばれる可能性がある
    • acquireとreleaseはメタ計算でやるべき…?
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
static void kpt_free (char *v)
{
    if (v >= (char*)P2V(INIT_KERNMAP)) {
        kfree(v, PT_ORDER);
        return;
    }

    acquire(&kpt_mem.lock);
    _kpt_free (v);
    release(&kpt_mem.lock);
}

# kpt_freerange

  • 何ページもfreeするときはページごとにlinked listに接続する
  • lockしていない
    • これを呼ぶときはマルチプロセス環境で読んではいけない
    • とはいえ外からよばれる
      • release/lockは自前でする必要がある
1
2
3
4
5
6
7
8
// add some memory used for page tables (initialization code)
void kpt_freerange (uint32 low, uint32 hi)
{
    while (low < hi) {
        _kpt_free ((char*)low);
        low += PT_SZ;
    }
}

# kpt_alloc

  • lockしてNULLだったら kmalloc
    • 失敗したらpanic
  • allocateに成功したらゼロクリア
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
void* kpt_alloc (void)
{
    struct run *r;

    acquire(&kpt_mem.lock);

    if ((r = kpt_mem.freelist) != NULL ) {
        kpt_mem.freelist = r->next;
    }

    release(&kpt_mem.lock);

    // Allocate a PT page if no inital pages is available
    if ((r == NULL) && ((r = kmalloc (PT_ORDER)) == NULL)) {
        panic("oom: kpt_alloc");
    }

    memset(r, 0, PT_SZ);
    return (char*) r;
}

# walkpgdir

  • page table側の場所を探す
  • page table entryにNULL pointerがあるとセグフォして死ぬので厳しい
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Return the address of the PTE in page directory that corresponds to
// virtual address va.  If alloc!=0, create any required page table pages.
static pte_t* walkpgdir (pde_t *pgdir, const void *va, int alloc)
{
    pde_t *pde;
    pte_t *pgtab;

    // pgdir points to the page directory, get the page direcotry entry (pde)
    pde = &pgdir[PDE_IDX(va)];

    if (*pde & PE_TYPES) {
        pgtab = (pte_t*) p2v(PT_ADDR(*pde));

    } else {
        if (!alloc || (pgtab = (pte_t*) kpt_alloc()) == 0) {
            return 0;
        }

        // Make sure all those PTE_P bits are zero.
        memset(pgtab, 0, PT_SZ);

        // The permissions here are overly generous, but they can
        // be further restricted by the permissions in the page table
        // entries, if necessary.
        *pde = v2p(pgtab) | UPDE_TYPE;
    }

    return &pgtab[PTE_IDX(va)];
}

# mappages

  • code gearに書き換えなくても良さそう
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 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 ap)
{
    char *a, *last;
    pte_t *pte;

    a = (char*) align_dn(va, PTE_SZ);
    last = (char*) align_dn((uint)va + size - 1, PTE_SZ);

    for (;;) {
        if ((pte = walkpgdir(pgdir, a, 1)) == 0) {
            return -1;
        }

        if (*pte & PE_TYPES) {
            panic("remap");
        }

        *pte = pa | ((ap & 0x3) << 4) | PE_CACHE | PE_BUF | PTE_TYPE;

        if (a == last) {
            break;
        }

        a += PTE_SZ;
        pa += PTE_SZ;
    }

    return 0;
}

# switchuvm

  • pageを切り替えるやつ
  • inline 展開されそう
  • push/pop cliで割り込み禁止
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Switch to the user page table (TTBR0)
void switchuvm (struct proc *p)
{
    uint val;

    pushcli();

    if (p->pgdir == 0) {
        panic("switchuvm: no pgdir");
    }

    val = (uint) V2P(p->pgdir) | 0x00;

    asm("MCR p15, 0, %[v], c2, c0, 0": :[v]"r" (val):);
    flush_tlb();

    popcli();
}

# inituvm

  • pageディレクトリを作るやつ
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Load the initcode into address 0 of pgdir. sz must be less than a page.
void inituvm (pde_t *pgdir, char *init, uint sz)
{
    char *mem;

    if (sz >= PTE_SZ) {
        panic("inituvm: more than a page");
    }

    mem = alloc_page();
    memset(mem, 0, PTE_SZ);
    mappages(pgdir, 0, PTE_SZ, v2p(mem), AP_KU);
    memmove(mem, init, sz);
}

# loaduvm

  • swapから呼び出す
  • kernelの中をぐるぐるしてreadiに入る
    • 入った場合は待ちになる
    • user proc側が街になる
  • context側でどこで止まっていて、どこから実行するかを書かないとだめ
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Load a program segment into pgdir.  addr must be page-aligned
// and the pages from addr to addr+sz must already be mapped.
int loaduvm (pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz)
{
    uint i, pa, n;
    pte_t *pte;

    if ((uint) addr % PTE_SZ != 0) {
        panic("loaduvm: addr must be page aligned");
    }

    for (i = 0; i < sz; i += PTE_SZ) {
        if ((pte = walkpgdir(pgdir, addr + i, 0)) == 0) {
            panic("loaduvm: address should exist");
        }

        pa = PTE_ADDR(*pte);

        if (sz - i < PTE_SZ) {
            n = sz - i;
        } else {
            n = PTE_SZ;
        }

        if (readi(ip, p2v(pa), offset + i, n) != n) {
            return -1;
        }
    }

    return 0;
}

# allocuvm/deallocuvm

  • page tableをallocateするか消すかの対応になっている
  • memory clearはここでは行われない
  • user virtual momeryの略?

# copyuvm

  • forkのときによばれる
    • forkの場合は自分のメモリ空間を子供にコピるので
  • for文のパラメーターをどこかに保存しておく必要がある
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Given a parent process's page table, create a copy
// of it for a child.
pde_t* copyuvm (pde_t *pgdir, uint sz)
{
    pde_t *d;
    pte_t *pte;
    uint pa, i, ap;
    char *mem;

    // allocate a new first level page directory
    d = kpt_alloc();
    if (d == NULL ) {
        return NULL ;
    }

    // copy the whole address space over (no COW)
    for (i = 0; i < sz; i += PTE_SZ) {
        if ((pte = walkpgdir(pgdir, (void *) i, 0)) == 0) {
            panic("copyuvm: pte should exist");
        }

        if (!(*pte & PE_TYPES)) {
            panic("copyuvm: page not present");
        }

        pa = PTE_ADDR (*pte);
        ap = PTE_AP (*pte);

        if ((mem = alloc_page()) == 0) {
            goto bad;
        }

        memmove(mem, (char*) p2v(pa), PTE_SZ);

        if (mappages(d, (void*) i, PTE_SZ, v2p(mem), ap) < 0) {
            goto bad;
        }

# copyout

  • 仮想メモリのコピーに巻き込まれそう…
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int copyout (pde_t *pgdir, uint va, void *p, uint len)
{
    char *buf, *pa0;
    uint n, va0;

    buf = (char*) p;

    while (len > 0) {
        va0 = align_dn(va, PTE_SZ);
        pa0 = uva2ka(pgdir, (char*) va0);

        if (pa0 == 0) {
            return -1;
        }

        n = PTE_SZ - (va - va0);

        if (n > len) {
            n = len;
        }

        memmove(pa0 + (va - va0), buf, n);

        len -= n;
        buf += n;
        va = va0 + PTE_SZ;
    }

    return 0;
}

# 方針

  • free以外のfor/while文はCodeGearに変換する
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy