深入理解Linux虚拟内存管理(七)

在这里插入图片描述

系列文章目录


Linux 内核设计与实现
深入理解 Linux 内核
Linux 设备驱动程序
Linux设备驱动开发详解
深入理解Linux虚拟内存管理(一)
深入理解Linux虚拟内存管理(二)
深入理解Linux虚拟内存管理(三)
深入理解Linux虚拟内存管理(四)
深入理解Linux虚拟内存管理(五)
深入理解Linux虚拟内存管理(六)
深入理解Linux虚拟内存管理(七)
深入理解Linux虚拟内存管理(八)


文章目录

  • 系列文章目录
  • 一、进程内存描述符
    • 1、 进程内存描述符
      • (1)初始化一个描述符
      • (2)复制一个描述符
        • (a)copy_mm
        • (b)mm_init
          • ① ⇒ pgd_alloc
          • ② ⇒ kmem_cache_free
      • (3)分配一个描述符
        • (a)allocate_mm
        • (b)mm_alloc
      • (4)销毁一个描述符
        • (a)mmput
        • (b)mmdrop
        • (c)__mmdrop
    • 2、创建内存区域
      • (1)创建一个内存区域
        • (a)do_mmap
        • (b)do_mmap_pgoff
      • (2)插入一个内存区域
        • (a)__insert_vm_struct
        • (b)find_vma_prepare
        • (c)vma_link
        • (d)__vma_link
        • (e)__vma_link_list
        • (f)__vma_link_rb
        • (g)__vma_link_file
      • (3)合并相邻区域
        • (a)vma_merge
        • (b)can_vma_merge
      • (4)重映射并移动一个内存区域
        • (a)sys_mremap
        • (b)do_mremap
        • (c)move_vma
        • (d)make_pages_present
        • (e)get_user_pages
        • (f)move_page_tables
        • (g)move_one_page
        • (h)get_one_pte
        • (i)alloc_one_pte
        • (j)copy_one_pte
      • (5)删除内存区域
        • (a)do_munmap
        • (b)unmap_fixup
      • (6)删除所有的内存区域
        • (a)exit_mmap
        • (b)clear_page_tables
        • (c)free_one_pgd
        • (d)free_one_pmd
    • 3、查找内存区域
      • (1)查找已映射内存区域
        • (a)find_vma
        • (b)find_vma_prev
        • (c)find_vma_intersection
      • (2)查找空闲内存区域
        • (a)get_unmapped_area
        • (b)arch_get_unmapped_area
    • 4、对内存区域上锁和解锁
      • (1)对内存区域上锁
        • (a)sys_mlock
        • (b)sys_mlockall
        • (c)do_mlockall
        • (d)do_mlock
      • (2)对区域解锁
        • (a)sys_munlock
        • (b)sys_munlockall
      • (3)上锁/解锁后修整区域
        • (a)mlock_fixup
        • (b)ulock_fixup_all
        • (c)mlock_fixup_start
        • (d)mlock_fixup_end
        • (e)mlock_fixup_middle
    • 5、缺页中断
      • (1)x86 缺页中断处理程序
        • (a)do_page_fault
      • (2)扩展栈
        • (a)expand_stack
      • (3)独立体系结构的页面中断处理程序
        • (a)hanle_mm_fault
        • (b)handle_pte_fault
      • (4)请求分配
        • (a)do_no_page
        • (b)do_anonymous_page
      • (5)请求分页
        • (a)do_swap_page
        • (b)can_share_swap_page
        • (c)exclusive_swap_page
      • (6)写时复制(COW) 页面
        • (a)do_wp_page
    • 6、页面相关的磁盘 I/O
      • (1)一般文件读
        • (a)generic_file_read
        • (b)do_generic_file_read
        • (c)generic_file_readahead
      • (2)一般文件 mmap
        • (a)generic_file_mmap
      • (3)一般文件截断
        • (a)vmtruncate
        • (b)vmtruncate_list
        • (c)zap_page_range
        • (d)zap_pmd_range
        • (e)zap_pte_range
        • (f)truncate_inode_pages
        • (g)truncate_list_pages
        • (h)truncate_complete_page
        • (i)do_flushpage
        • (j)truncate_partial_page
      • (4)从页面高速缓存中读入页面
        • (a)filemap_nopage
        • (b)age_cache_read
      • (5)为 nopage() 进行预读文件
        • (a)nopage_sequential_readahead
        • (b)read_cluster_nonblocking
      • (6)交换相关的预读
        • (a)swapin_readahead
        • (b)valid_swaphandles
  • 符号


一、进程内存描述符

1、 进程内存描述符

(1)初始化一个描述符

传送门 4.3.2 初始化一个描述符

    系统中的 mm_struct 开始称为 init_mm ,它在编译时由宏 INIT_MM() 静态初始化。

// include/linux/sched.h
#define INIT_MM(name) \
{			 				\
	mm_rb:		RB_ROOT,			\
	pgd:		swapper_pg_dir, 		\
	mm_users:	ATOMIC_INIT(2), 		\
	mm_count:	ATOMIC_INIT(1), 		\
	mmap_sem:	__RWSEM_INITIALIZER(name.mmap_sem), \
	page_table_lock: SPIN_LOCK_UNLOCKED, 		\
	mmlist:		LIST_HEAD_INIT(name.mmlist),	\
}

// arch/i386/kernel/init_task.c
struct mm_struct init_mm = INIT_MM(init_mm);

    新 mm_struct 在建立以后,是它们父 mm_struct 的备份,并且它们由 copy_mm()init_mm() 初始化的字段来复制。

(2)复制一个描述符

(a)copy_mm

    这个函数为给定的进程复制一份 mm_struct 。它仅在创建一个新进程后且需要它自己的 mm_struct 时由 do_fork() 调用。

// kernel/fork.c
// 这一块重置没有被子 mm_struct 继承的字段并找到一个复制源 mm 的字段。
// 这些参数是为克隆而传入的标志位和那些复制 mm_struct 的进程。
static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
{
	struct mm_struct * mm, *oldmm;
	int retval;

	// 初始化与内存管理相关的task_struct字段。
	tsk->min_flt = tsk->maj_flt = 0;
	tsk->cmin_flt = tsk->cmaj_flt = 0;
	tsk->nswap = tsk->cnswap = 0;

	tsk->mm = NULL;
	tsk->active_mm = NULL;

	/*
	 * Are we cloning a kernel thread?
	 *
	 * We need to steal a active VM for that..
	 */
	// 借用当前运行进程的mm来复制。	 
	oldmm = current->mm;
	// 一个没有mm的内核线程,所以它可以立即返回。
	if (!oldmm)
		return 0;

	// 如果设置了 CLONE_VM 标志位,子进程将与父进程共享mm。像 pthreads 这
	// 样的用户需要这样做。mm_users 字段加1, 以使mm不会过早销毁。
	// good_mm 标记设置 tsk->mm 和 tsk->active_mm, 并返回成功
	if (clone_flags & CLONE_VM) {
		atomic_inc(&oldmm->mm_users);
		mm = oldmm;
		goto good_mm;
	}

	retval = -ENOMEM;
	// 分配新的mm。
	mm = allocate_mm();
	if (!mm)
		goto fail_nomem;

	/* Copy the current MM stuff.. */
	// 复制父mm, 并利用mm_init()来初始化特定进程的mm字段。
	memcpy(mm, oldmm, sizeof(*mm));
	if (!mm_init(mm))
		goto fail_nomem;
	// 为那些无法自动管理其MMU的体系结构初始化MMU上下文。
	if (init_new_context(tsk,mm))
		goto free_pt;
	// 调用dup_mmap(),它负责复制所有VMA父进程用到的区域。
	down_write(&oldmm->mmap_sem);
	retval = dup_mmap(mm);
	up_write(&oldmm->mmap_sem);

	// 一旦成功,dup_mmap() 返回 0。如果失败,标记 free_pt 将调用 mmput() 。它将
	// mm 的使用计数减 1
	if (retval)
		goto free_pt;

	/*
	 * child gets a private LDT (if there was an LDT in the parent)
	 */
	// 基于父进程为新进程复制LDT。 
	copy_segments(tsk, mm);

good_mm:
	// 设置新mm,active_mm 并返回成功。
	tsk->mm = mm;
	tsk->active_mm = mm;
	return 0;

free_pt:
	mmput(mm);
fail_nomem:
	return retval;
}

(b)mm_init

    这个函数初始化特定进程的 mm 字段。

// kernel/fork.c
#define free_mm(mm)	(kmem_cache_free(mm_cachep, (mm)))

static struct mm_struct * mm_init(struct mm_struct * mm)
{
	// 设置用户数为l。
	atomic_set(&mm->mm_users, 1);
	// 设置mm的引用计数为1。
	atomic_set(&mm->mm_count, 1);
	// 初始化保护VMA链表的信号量。
	init_rwsem(&mm->mmap_sem);
	// 初始化保护写访问的自旋锁。
	mm->page_table_lock = SPIN_LOCK_UNLOCKED;
	// 为该结构分配新的PGD。
	mm->pgd = pgd_alloc(mm);
	mm->def_flags = 0;
	if (mm->pgd)
		return mm;
	free_mm(mm);
	return NULL;
}
① ⇒ pgd_alloc

    pgd_alloc 函数

② ⇒ kmem_cache_free

    kmem_cache_free 函数

(3)分配一个描述符

    提供了两个函数来分配一个 mm_struct。虽然有点容易混淆,但它们实际上都是一样的。allocate_mm() 将从 slab 分配器中分配一个 mm_structmm_alloc() 将分配结构,然后调用 mm_init() 来初始化。

(a)allocate_mm

// kernel/fork.c
#define allocate_mm()	(kmem_cache_alloc(mm_cachep, SLAB_KERNEL))
// 从slab分配器分配一个mm_struct。

(b)mm_alloc

// kernel/fork.c
/*
 * Allocate and initialize an mm_struct.
 */
struct mm_struct * mm_alloc(void)
{
	struct mm_struct * mm;
	// 从slab分配器分配一个mm_struct。
	mm = allocate_mm();
	if (mm) {
	// 将结构的所有字段归0。
		memset(mm, 0, sizeof(*mm));
	// 进行基本的初始化。		
		return mm_init(mm);
	}
	return NULL;
}

(4)销毁一个描述符

    mm 的一个新用户利用如下调用将使用计数加1:
    atomic_inc( &mm->mm_users );
    利用 mmput() 将使用计数减 1。如果 mm_users 计数减到 0,将调用 exit_mmap() 将所有的已映射区域删除,而且所有的页表也将会销毁,因为已经没有任何使用用户空间部分的使用者。mm_count 计数由 mmdrop() 减 1,因为页表和 VMA 的使用者都记为一个 mm_struct 使用者。在 mm_count 减到 0 时,mm_struct 将会被销毁。

(a)mmput

// kernel/fork.c
/*
 * Decrement the use count and release all resources for an mm.
 */
void mmput(struct mm_struct *mm)
{
	if (atomic_dec_and_lock(&mm->mm_users, &mmlist_lock)) {
		extern struct mm_struct *swap_mm;
		if (swap_mm == mm)
			swap_mm = list_entry(mm->mmlist.next, struct mm_struct, mmlist);
		list_del(&mm->mmlist);
		mmlist_nr--;
		spin_unlock(&mmlist_lock);
		exit_mmap(mm);
		mmdrop(mm);
	}
}

(b)mmdrop

(c)__mmdrop

2、创建内存区域

(1)创建一个内存区域

(a)do_mmap

(b)do_mmap_pgoff

(2)插入一个内存区域

(a)__insert_vm_struct

(b)find_vma_prepare

(c)vma_link

(d)__vma_link

(e)__vma_link_list

(f)__vma_link_rb

(g)__vma_link_file

(3)合并相邻区域

(a)vma_merge

(b)can_vma_merge

(4)重映射并移动一个内存区域

(a)sys_mremap

(b)do_mremap

(c)move_vma

(d)make_pages_present

(e)get_user_pages

(f)move_page_tables

(g)move_one_page

(h)get_one_pte

(i)alloc_one_pte

(j)copy_one_pte

(5)删除内存区域

(a)do_munmap

(b)unmap_fixup

(6)删除所有的内存区域

(a)exit_mmap

(b)clear_page_tables

(c)free_one_pgd

(d)free_one_pmd

3、查找内存区域

(1)查找已映射内存区域

(a)find_vma

(b)find_vma_prev

(c)find_vma_intersection

(2)查找空闲内存区域

(a)get_unmapped_area

(b)arch_get_unmapped_area

4、对内存区域上锁和解锁

(1)对内存区域上锁

(a)sys_mlock

(b)sys_mlockall

(c)do_mlockall

(d)do_mlock

(2)对区域解锁

(a)sys_munlock

(b)sys_munlockall

(3)上锁/解锁后修整区域

(a)mlock_fixup

(b)ulock_fixup_all

(c)mlock_fixup_start

(d)mlock_fixup_end

(e)mlock_fixup_middle

5、缺页中断

(1)x86 缺页中断处理程序

(a)do_page_fault

(2)扩展栈

(a)expand_stack

(3)独立体系结构的页面中断处理程序

(a)hanle_mm_fault

(b)handle_pte_fault

(4)请求分配

(a)do_no_page

(b)do_anonymous_page

(5)请求分页

(a)do_swap_page

(b)can_share_swap_page

(c)exclusive_swap_page

(6)写时复制(COW) 页面

(a)do_wp_page

6、页面相关的磁盘 I/O

(1)一般文件读

(a)generic_file_read

(b)do_generic_file_read

(c)generic_file_readahead

(2)一般文件 mmap

(a)generic_file_mmap

(3)一般文件截断

(a)vmtruncate

(b)vmtruncate_list

(c)zap_page_range

(d)zap_pmd_range

(e)zap_pte_range

(f)truncate_inode_pages

(g)truncate_list_pages

(h)truncate_complete_page

(i)do_flushpage

(j)truncate_partial_page

(4)从页面高速缓存中读入页面

(a)filemap_nopage

(b)age_cache_read

(5)为 nopage() 进行预读文件

(a)nopage_sequential_readahead

(b)read_cluster_nonblocking

(6)交换相关的预读

(a)swapin_readahead

(b)valid_swaphandles

符号

   
⇐ ⇒ ⇔ ⇆ ⇒ ⟺
①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿
⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑿⒀⒁⒂⒃⒄⒅⒆⒇
➊➋➌➍➎➏➐➑➒➓⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴
⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵
ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ
ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ

123

y = x 2 + z 3 y = x^2 + z_3 y=x2+z3

y = x 2 + z 3 + a b + b a y = x^2 + z_3 + \frac {a}{b} + \sqrt[a]{b} y=x2+z3+ba+ab

y = x 2 + z 3 (1) y = x^2 + z^3 \tag{1} y=x2+z3(1)

你可能感兴趣的:(Linux内核,linux,运维,服务器)