メモリ管理

仮想メモリの仕組み

参考ドキュメント

  • Intel® 64 and IA-32 Architectures Software Developer's Manual, Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, and 4 Volume 3 (3A, 3B, 3C, & 3D): System Programming GuideCHAPTER 5 PAGING 参照

リニアメモリ <-> 物理メモリ変換

../_images/%E3%83%A1%E3%83%A2%E3%83%AA%E5%A4%89%E6%8F%9B%E6%A6%82%E5%BF%B5%E5%9B%B3.png

4レベルページングによるリニアアドレス変換

../_images/4-Level-Paging-%E3%83%AA%E3%83%8B%E3%82%A2%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E5%A4%89%E6%8F%9B.png

エントリの種類ごとの形式

../_images/%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AA%E3%81%AE%E7%A8%AE%E9%A1%9E%E3%81%94%E3%81%A8%E3%81%AE%E5%BD%A2%E5%BC%8F-4%E3%83%AC%E3%83%99%E3%83%AB%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%B3%E3%82%B0.png

ページング機能の利用手順

  • 初期設定

    • ページディレクトリテーブルの設定

    • ページテーブルの設定

    • CR3にページディレクトリテーブルのアドレスを設定

    • IDTにページフォルトハンドラを登録

    • CR0のPGビットを1にセットしてページングをONにする

  • ページフォルトハンドラ

    • あまり使われていない論理ページを選択

    • ページアウト

    • ページアウトしたページに物理ページとの対応を解除(PTEのPビットを0にリセット)

    • ページフォルトの発生したページに物理ページを対応させる(PTEのPビットを1にセット)

    • TLBをフラッシュ

    • ページイン

主要な変数・構造体

Page Table Entry サイズ

  • 説明 : Page Table 内の1データのサイズを定義

  • パス : sys/amd64/include/pmap.h

typedef u_int64_t pd_entry_t;   // Page Table Entry Size
typedef u_int64_t pt_entry_t;   // Page Directory Entry Size
typedef u_int64_t pdp_entry_t;  // Page Directory Pointer Table Entry Size
typedef u_int64_t pml4_entry_t; // Page Map Level 4 Entry Size

メモリをページとして扱うときの管理構造

  • path :

// Page Map Level 4 entry type
static pml4_entry_t *PT4;

// Page Directory Pointer entry type
static pdp_entry_t *PT3;
static pdp_entry_t *PT3_l, *PT3_u;

// Page Directory entry type
static pd_entry_t *PT2;
static pd_entry_t *PT2_l0, *PT2_l1, *PT2_l2, *PT2_l3, *PT2_u0, *PT2_u1;

プロセス単位の仮想メモリアドレス

        erDiagram
    vmspace ||--|| vm_map : "vm_map"
    vmspace ||--|| pmap : "vm_pmap"
    vmspace ||..|| shmmap_state : "vm_shm"
    vm_map ||--|| sx : "lock"
    vm_map ||--|| vm_map_entry : "header"
    vm_map ||..|| vm_map_entry : "root"
    vm_map ||..|| pmap : "pmap"
    vm_map ||..|| mtx : "system_mtx"
    vm_map_entry ||..|| vm_map_entry : "*prev, *next, *left, *right"
    vm_map_entry ||--|| vm_map_object : "object"
    vm_map_object ||..|| vm_object : "vm_object"
    vm_map_object ||..|| vm_map : "sub_map"
    vm_object ||..|| vm_object : "backing_object"

    vmspace {
        vm_map vm_map
        shmmap_state *vm_shm
        pmap vm_pmap
    }

    vm_map {
        vm_map_entry header
        vm_map_entry root
        sx lock
        mtx system_mtx
        int nentries
        vm_size_t size
        pmap_t pmap
    }

    pmap {
        pml4_entry_t pm_pml4
        pml4_entry_t pm_pml4u
        uint64_t pm_cr3
        uint64_t pm_ucr3
        pmap_type pm_type
    }

    shmmap_state {
        vm_offset_t va
        int shmid
    }

    vm_map_entry {
        vm_map_entry *prev
        vm_map_entry *next
        vm_map_entry *left
        vm_map_entry *right
        vm_map_object object
        ucred *cred
        thread *wiring_thread
    }

    vm_map_object {
        vm_object vm_object
        vm_map sub_map
    }

    vm_object {
        vm_object backing_object
        vm_ooffset_t backing_object_offset
    }

    sx {
        lock_object lock_object
        uintptr_t sx_lock
    }

    mtx {
        lock_object lock_object
        uintptr_t mtx_lock
    }
    
  • 実線 : 構造体の実体をそのまま内側に持っている

  • 点線 : ポインタで指しているだけで、実体は別の場所にある

vmspace(sys/vm/vm_map.h)

  • プロセスのアドレス空間を表現するための、機種依存・機種非依存の両方の情報を包含する構造体

/*
* Shareable process virtual address space.
*
* List of locks
*   (c)     const until freed
*/
struct vmspace {
    struct vm_map vm_map;         /* VM address map */
    struct shmmap_state *vm_shm;  /* SYS5 shared memory private data XXX */
    segsz_t vm_swrss;             /* resident set size before last swap */
    segsz_t vm_tsize;             /* text size (pages) XXX */
    segsz_t vm_dsize;             /* data size (pages) XXX */
    segsz_t vm_ssize;             /* stack size (pages) */
    caddr_t vm_taddr;             /* (c) user virtual address of text */
    caddr_t vm_daddr;             /* (c) user virtual address of data */
    caddr_t vm_maxsaddr;          /* user VA at max stack growth */
    volatile int vm_refcnt;       /* number of references */
    /*
    * Keep the PMAP last, so that CPU-specific variations of that
    * structure on a single architecture don't result in offset
    * variations of the machine-independent fields in the vmspace.
    */
    struct pmap vm_pmap;          /* private physical map */
};

vm_map(sys/vm/vm_map.h)

  • 機種非依存な仮想アドレス空間を表現するための最上位のデータ構造

/*
*   A map is a set of map entries.  These map entries are
*   organized both as a binary search tree and as a doubly-linked
*   list.  Both structures are ordered based upon the start and
*   end addresses contained within each map entry.
*
*   Counterintuitively, the map's min offset value is stored in
*   map->header.end, and its max offset value is stored in
*   map->header.start.
*
*   The list header has max start value and min end value to act
*   as sentinels for sequential search of the doubly-linked list.
*   Sleator and Tarjan's top-down splay algorithm is employed to
*   control height imbalance in the binary search tree.
*
*   List of locks
*   (c)     const until freed
*/
struct vm_map {
    struct vm_map_entry header;     /* List of entries */
/*
    map min_offset  header.end      (c)
    map max_offset  header.start    (c)
*/
    struct sx lock;                 /* Lock for map data */
    struct mtx system_mtx;
    int nentries;                   /* Number of entries */
    vm_size_t size;                 /* virtual size */
    u_int timestamp;                /* Version number */
    u_char needs_wakeup;
    u_char system_map;              /* (c) Am I a system map? */
    vm_flags_t flags;               /* flags for this vm_map */
    vm_map_entry_t root;            /* Root of a binary search tree */
    pmap_t pmap;                    /* (c) Physical map */
    int busy;
};

vm_map_entry(sys/vm/vm_map.h)

  • 保護状態や継承される属性、および同じ外部記憶オブジェクトを共有する、仮想的に連続したアドレス範囲からバッキングストアの vm_object へのマッピングを記述する構造体

/*
*   Address map entries consist of start and end addresses,
*   a VM object (or sharing map) and offset into that object,
*   and user-exported inheritance and protection information.
*   Also included is control information for virtual copy operations.
*/
struct vm_map_entry {
    struct vm_map_entry *prev;      /* previous entry */
    struct vm_map_entry *next;      /* next entry */
    struct vm_map_entry *left;      /* left child in binary search tree */
    struct vm_map_entry *right;     /* right child in binary search tree */
    vm_offset_t start;              /* start address */
    vm_offset_t end;                /* end address */
    vm_offset_t pad0;
    vm_offset_t next_read;          /* vaddr of the next sequential read */
    vm_size_t adj_free;             /* amount of adjacent free space */
    vm_size_t max_free;             /* max free space in subtree */
    union vm_map_object object;     /* object I point to */
    vm_ooffset_t offset;            /* offset into object */
    vm_eflags_t eflags;             /* map entry flags */
    vm_prot_t protection;           /* protection code */
    vm_prot_t max_protection;       /* maximum protection */
    vm_inherit_t inheritance;       /* inheritance */
    uint8_t read_ahead;             /* pages in the read-ahead window */
    int wired_count;                /* can be paged if = 0 */
    struct ucred *cred;             /* tmp storage for creator ref */
    struct thread *wiring_thread;
};

vm_map_object(sys/vm/vm_map.h)

/*
*   Objects which live in maps may be either VM objects, or
*   another map (called a "sharing map") which denotes read-write
*   sharing with other maps.
*/
union vm_map_object {
    struct vm_object *vm_object;    /* object object */
    struct vm_map *sub_map;         /* belongs to another map */
};

vm_object(sys/vm/vm_object.h)

  • 物理メモリや命令やデータを含む各種資源のデータの出自を記述する構造体

struct vm_object {
    struct rwlock lock;
    TAILQ_ENTRY(vm_object) object_list; /* list of all objects */
    LIST_HEAD(, vm_object) shadow_head; /* objects that this is a shadow for */
    LIST_ENTRY(vm_object) shadow_list; /* chain of shadow objects */
    struct pglist memq;             /* list of resident pages */
    struct vm_radix rtree;          /* root of the resident page radix trie*/
    vm_pindex_t size;               /* Object size */
    int generation;                 /* generation ID */
    int ref_count;                  /* How many refs?? */
    int shadow_count;               /* how many objects that this is a shadow for */
    vm_memattr_t memattr;           /* default memory attribute for pages */
    objtype_t type;                 /* type of pager */
    u_short flags;                  /* see below */
    u_short pg_color;               /* (c) color of first page in obj */
    u_int paging_in_progress;       /* Paging (in or out) so don't collapse or destroy */
    int resident_page_count;        /* number of resident pages */
    struct vm_object *backing_object; /* object that I'm a shadow of */
    vm_ooffset_t backing_object_offset;/* Offset in backing object */
    TAILQ_ENTRY(vm_object) pager_object_list; /* list of all objects of this pager type */
    LIST_HEAD(, vm_reserv) rvq;     /* list of reservations */
    void *handle;
    union {
        /*
        * VNode pager
        *
        *   vnp_size - current size of file
        */
        struct {
            off_t vnp_size;
            vm_ooffset_t writemappings;
        } vnp;

        /*
        * Device pager
        *
        *   devp_pglist - list of allocated pages
        */
        struct {
            TAILQ_HEAD(, vm_page) devp_pglist;
            struct cdev_pager_ops *ops;
            struct cdev *dev;
        } devp;

        /*
        * SG pager
        *
        *   sgp_pglist - list of allocated pages
        */
        struct {
            TAILQ_HEAD(, vm_page) sgp_pglist;
        } sgp;

        /*
        * Swap pager
        *
        *   swp_tmpfs - back-pointer to the tmpfs vnode,
        *                if any, which uses the vm object
        *                as backing store.  The handle
        *                cannot be reused for linking,
        *                because the vnode can be
        *                reclaimed and recreated, making
        *                the handle changed and hash-chain
        *                invalid.
        *
        *   swp_blks -   pc-trie of the allocated swap blocks.
        *
        */
        struct {
            void *swp_tmpfs;
            struct pctrie swp_blks;
        } swp;
    } un_pager;
    struct ucred *cred;
    vm_ooffset_t charge;
    void *umtx_data;
};

sx(sys/sys/_sx.h)

/*
* Shared/exclusive lock main structure definition.
*/
struct sx {
    struct lock_object      lock_object;
    volatile uintptr_t      sx_lock;
};

mtx(sys/sys/_mutex.h)

/*
* Sleep/spin mutex.
*
* All mutex implementations must always have a member called mtx_lock.
* Other locking primitive structures are not allowed to use this name
* for their members.
* If this rule needs to change, the bits in the mutex implementation must
* be modified appropriately.
*/
struct mtx {
    struct lock_object      lock_object;    /* Common lock properties. */
    volatile uintptr_t      mtx_lock;       /* Owner and flags. */
};

shmmap_state(sys/kern/sysv_shm.c)

struct shmmap_state {
    vm_offset_t va;
    int shmid;
};

pmap(sys/amd64/include/pmap.h)

/*
* The kernel virtual address (KVA) of the level 4 page table page is always
* within the direct map (DMAP) region.
*/
struct pmap {
    struct mtx              pm_mtx;
    pml4_entry_t            *pm_pml4;       /* KVA of level 4 page table */
    pml4_entry_t            *pm_pml4u;      /* KVA of user l4 page table */
    uint64_t                pm_cr3;
    uint64_t                pm_ucr3;
    TAILQ_HEAD(,pv_chunk)   pm_pvchunk;     /* list of mappings in pmap */
    cpuset_t                pm_active;      /* active on cpus */
    enum pmap_type          pm_type;        /* regular or nested tables */
    struct pmap_statistics  pm_stats;       /* pmap statistics */
    struct vm_radix         pm_root;        /* spare page table pages */
    long                    pm_eptgen;      /* EPT pmap generation id */
    int                     pm_flags;
    struct pmap_pcids       pm_pcids[MAXCPU];
};

主要なマクロ

#define     G(x)    (1ULL * (x) * 1024 * 1024 * 1024)
  • 1ULL : 1 unsigned long long を意味する

主要な関数