アドレススペースを処理する
カーネルは、独自のメモリを管理するだけでなく、ユーザースペースのメモリも管理する必要があります。
ユーザースペース内のプロセスのメモリは、プロセスアドレススペースと呼ばれ、システム内の各ユーザースペースプロセスによって認識されるメモリです。
Linuxは仮想メモリテクノロジを使用しているため、システム内のすべてのプロセスが仮想的な方法でメモリを共有します。
プロセスの場合、システム全体のすべての物理メモリにアクセスできるようです。
プロセスアドレススペースは、32ビットまたは64ビットのフラットアドレススペースであるプロセスアドレス可能な仮想アドレスで構成されます。
「フラット」とは、アドレス空間の範囲が独立した連続範囲であることを意味します。
仮想メモリを使用する最新のオペレーティングシステムは、通常、セグメント化されたメモリモデルではなく、フラットなアドレス空間を使用します。
プロセスは4G(32ビット)仮想メモリをアドレス指定できますが、すべての仮想アドレスにアクセスする権限はありません。
アクセスできる有効なアドレス空間は、メモリ領域と呼ばれます。
メモリ領域には、関連するプロセスに対して、読み取り可能、書き込み可能、実行可能などの関連する権限があります。
プロセスが有効なテンプレートにないメモリ領域にアクセスした場合、または誤った方法でメモリ領域にアクセスした場合、カーネルはプロセスを終了し、セグメンテーション障害を返します。
メモリ領域には、さまざまなメモリオブジェクトが含まれています。
- コードスニペット
- データセグメント
- BSSセグメント;
- ユーザースペーススタック;
- 共有ライブラリのコードセグメント、データセグメント、およびBSSセグメント。
- メモリマップファイル。
- 共有メモリセグメント。
- 匿名メモリマッピング(たとえば、malloc()によって割り当てられたメモリ)。
メモリ記述子
メモリ記述子構造はプロセスのアドレス空間を表し、 include \ linux \ mm_types.hにあります。
task_struct構造のmmメンバーは、プロセスで使用されるメモリ記述子を表します。
プロセスのmm_struct構造は、allocate_mm()マクロを使用してmm_cachepスラブキャッシュから割り当てられます。
/*
* Allocate and initialize an mm_struct.
*/
struct mm_struct * mm_alloc(void)
{
struct mm_struct * mm;
mm = allocate_mm();
if (mm) {
memset(mm, 0, sizeof(*mm));
mm = mm_init(mm, current);
}
return mm;
}
プロセスのメモリ記述子の内容は、copy_mm()によってコピーされた親プロセスのメモリ記述子から取得されます。
static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
スレッドの場合、メモリ記述子は一般的であるため、新しいメモリ記述子は必要ありません。
if (clone_flags & CLONE_VM) {
atomic_inc(&oldmm->mm_users);
mm = oldmm;
goto good_mm;
}
メモリ記述子の使用をキャンセルします。
static void exit_mm(struct task_struct * tsk);
カーネルスレッドはプロセスアドレススペースを必要としないため、カーネルスレッドに対応するプロセス記述子のmmメンバーは空です。したがって、プロセス記述子の使用は少し無駄です。
ただし、カーネルスレッドは、ページテーブルなど、カーネル記述子の一部のコンテンツも使用する必要があるため、カーネルスレッドは前のプロセスのメモリ記述子を使用することを選択します。
仮想メモリ領域
vm_area_struct構造は、include \ linux \ mm_types.hにあるメモリ領域を記述するために使用されます。
Linuxメモリのメモリ領域は、仮想メモリ領域(仮想メモリ領域、VMA)とも呼ばれます。
vm_area_struct構造は、指定されたアドレス空間の連続した間隔での独立したメモリ範囲を記述します。
各メモリ領域には一貫した属性があります。
同じアドレス空間内の異なるメモリ領域は重複できません。
vm_area_struct構造には、メモリ領域に含まれるページの動作と情報を記述するために使用されるVMAフラグが含まれます。これは、ハードウェア要件ではなく、ページを処理するときにカーネルが準拠する必要がある行動規範です。
VMAのロゴは次のとおりです。
vm_area_struct構造には、指定されたメモリ領域に関連する操作関数を指すvm_opsメンバーが含まれています。
struct vm_operations_struct {
void (*open)(struct vm_area_struct * area);
void (*close)(struct vm_area_struct * area);
int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf);
/* notification that a previously read-only page is about to become
* writable, if an error is returned it will cause a SIGBUS */
int (*page_mkwrite)(struct vm_area_struct *vma, struct vm_fault *vmf);
/* called by access_process_vm when get_user_pages() fails, typically
* for use by special VMAs that can switch between memory and hardware
*/
int (*access)(struct vm_area_struct *vma, unsigned long addr,
void *buf, int len, int write);
#ifdef CONFIG_NUMA
/*
* set_policy() op must add a reference to any non-NULL @new mempolicy
* to hold the policy upon return. Caller should pass NULL @new to
* remove a policy and fall back to surrounding context--i.e. do not
* install a MPOL_DEFAULT policy, nor the task or system default
* mempolicy.
*/
int (*set_policy)(struct vm_area_struct *vma, struct mempolicy *new);
/*
* get_policy() op must add reference [mpol_get()] to any policy at
* (vma,addr) marked as MPOL_SHARED. The shared policy infrastructure
* in mm/mempolicy.c will do this automatically.
* get_policy() must NOT add a ref if the policy at (vma,addr) is not
* marked as MPOL_SHARED. vma policies are protected by the mmap_sem.
* If no [shared/vma] mempolicy exists at the addr, get_policy() op
* must return NULL--i.e., do not "fallback" to task or system default
* policy.
*/
struct mempolicy *(*get_policy)(struct vm_area_struct *vma,
unsigned long addr);
int (*migrate)(struct vm_area_struct *vma, const nodemask_t *from,
const nodemask_t *to, unsigned long flags);
#endif
};
メモリ記述子には、メモリ領域にアクセスするために使用される2つのメンバーmmapとmm_rbがあります。これら2つのメンバーが指すメモリ領域は同じですが、前者はリンクされたリストであり、すばやく移動できます。後者は赤黒の木であり、要素の検索が高速です。
メモリーエリア操作機能:
/* Look up the first VMA which satisfies addr < vm_end, NULL if none. */
extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr);
extern struct vm_area_struct * find_vma_prev(struct mm_struct * mm, unsigned long addr,
struct vm_area_struct **pprev);
static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr)
アドレス範囲を作成する
カーネルはdo_mmap()を使用して新しい線形アドレス範囲を作成し、プロセスのアドレス空間にアドレス範囲を追加します。
static inline unsigned long do_mmap(struct file *file, unsigned long addr,
unsigned long len, unsigned long prot,
unsigned long flag, unsigned long offset)
ユーザースペースは、mmap()システム呼び出しを使用して、カーネル関数do_mmap()の関数を取得できます。
do_munmap()関数に対応して、指定されたアドレス範囲を特定のプロセスアドレススペースから削除します。
extern int do_munmap(struct mm_struct *, unsigned long, size_t);
ページテーブル
メモリ記述子のpgdメンバーは、グローバルカタログを指します。
Linuxは、3レベルのページテーブルを使用してアドレス変換を完了します。