zl程序教程

您现在的位置是:首页 >  系统

当前栏目

【Linux 内核 内存管理】内存映射相关数据结构 ④ ( vm_area_struct 结构体成员分析 | vm_ops 成员 | vm_operations_struct 结构体成员分析 )

2023-09-14 09:07:26 时间

在博客 【Linux 内核 内存管理】虚拟地址空间布局架构 ⑦ ( vm_area_struct 结构体成员分析 | vm_start | vm_end | vm_next | vm_prev |vm_rb) 中 , 分析了 vm_start vm_end vm_next vm_prev vm_rb 5 5 5 个结构体成员作用 ;

在博客 【Linux 内核 内存管理】内存映射相关数据结构 ② ( vm_area_struct 结构体成员分析 | vm_mm 成员 | vm_page_prot 成员 | vm_flags 成员 ) 中 , 分析了 vm_area_struct 结构体中的 vm_mm vm_page_prot vm_flags 成员作用 ;

在博客 【Linux 内核 内存管理】内存映射相关数据结构 ③ ( vm_area_struct 结构体成员分析 | shared 成员 | anon_vma_chain 成员 | anon_vma 成员 ) 中 , 分析了 vm_area_struct 结构体中的 shared anon_vma_chain anon_vma 成员作用 ;





一、vm_area_struct 结构体成员分析




1、vm_ops 成员


vm_ops 成员是 " 虚拟内存操作集合 " , 该 vm_operations_struct 结构体中封装了大量的虚拟内存操作 ;

	/* Function pointers to deal with this struct. */
	const struct vm_operations_struct *vm_ops;




二、vm_operations_struct 结构体成员分析



vm_operations_struct 结构体 定义在 Linux 内核源码的 linux-4.12\include\linux\mm.h#361 位置 ;

/*
 * These are the virtual MM functions - opening of an area, closing and
 * unmapping it (needed to keep files on disk up-to-date etc), pointer
 * to the functions called when a no-page or a wp-page exception occurs. 
 */
struct vm_operations_struct {
	void (*open)(struct vm_area_struct * area);
	void (*close)(struct vm_area_struct * area);
	int (*mremap)(struct vm_area_struct * area);
	int (*fault)(struct vm_fault *vmf);
	int (*huge_fault)(struct vm_fault *vmf, enum page_entry_size pe_size);
	void (*map_pages)(struct vm_fault *vmf,
			pgoff_t start_pgoff, pgoff_t end_pgoff);

在这里插入图片描述

下面对 vm_operations_struct 结构体成员进行分析 ;


1、open 函数指针


open 函数指针 , 指向的函数 , 在 创建 " 虚拟内存区域 " 时调用 ;

	void (*open)(struct vm_area_struct * area);

2、close 函数指针


close 函数指针 , 指向的函数 , 在 删除 " 虚拟内存区域 " 时调用 ;

	void (*close)(struct vm_area_struct * area);

3、mremap 函数指针


mremap 函数指针 , 指向的函数 , 在 使用系统调用 mremap 函数 移动 " 虚拟内存区域 " 时调用 ;

	int (*mremap)(struct vm_area_struct * area);

4、fault 函数指针


【Linux 内核 内存管理】内存映射原理 ② ( 内存映射概念 | 文件映射 | 匿名映射 | 内存映射原理 | 分配虚拟内存页 | 产生缺页异常 | 分配物理内存页 | 共享内存 | 进程内存 ) 博客中 , 介绍了内存映射的原理 , 分配 " 虚拟内存区域 " 后 ,

第一次访问会产生 " 缺页异常 " ,

之后对于 " 文件映射 " , 如果没有映射 " 物理内存页 " , 就会回调 fault 函数 , 将 文件中的数据 读取到 " 物理内存页 " 中 ;

fault 函数指针 , 指向的函数 , 就是在 回调 fault 函数时 时调用 ;

	int (*fault)(struct vm_fault *vmf);

5、huge_fault 函数指针


huge_fault 函数指针 , 与上面的 fault 函数指针类似 , 只是 huge_fault 函数指针针对的是 使用 " 透明巨型页 " 的文件映射 的情况 ;

	int (*huge_fault)(struct vm_fault *vmf, enum page_entry_size pe_size);

6、map_pages 函数指针


【Linux 内核 内存管理】内存映射原理 ② ( 内存映射概念 | 文件映射 | 匿名映射 | 内存映射原理 | 分配虚拟内存页 | 产生缺页异常 | 分配物理内存页 | 共享内存 | 进程内存 ) 博客中 , 介绍了内存映射的原理 , 分配 " 虚拟内存区域 " 后 ,

第一次访问 " 文件映射 " 对应的 " 虚拟内存页 " 时 , 如果发现 文件 没有映射到该 " 虚拟内存页中 " , 会报 " 缺页异常 " ,

" 异常处理程序 " 会读取 正在访问的文件页 , 以及 预读取 后续的文件页 ,

调用 map_pages 函数指针指向的函数 , 为 文件页 分配 " 物理内存页 " ;

	void (*map_pages)(struct vm_fault *vmf,
			pgoff_t start_pgoff, pgoff_t end_pgoff);

7、page_mkwrite 函数指针


要 修改 " 私有文件映射 " 对应的 " 虚拟文件页 " 时 ,

如果是 第一次 写该 内存映射 时 , 会生成 " 页错误异常 " ,

" 异常处理程序 " 会执行 " 写复制 " 机制 ,

调用该 page_mkwrite 函数指针指向的函数 , 通知该 " 文件页 " 马上要变成可写状态 ,

此时 " 文件系统 " 会检查该 写操作 是否合法 , 是否允许修改该 文件页 , 是否需要等待以便进入合适的状态再进行写操作 ;


	/* 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_fault *vmf);




三、vm_area_struct 结构体完整源码



vm_area_struct 结构体完整源码 :

/*
 * This struct defines a memory VMM memory area. There is one of these
 * per VM-area/task.  A VM area is any part of the process virtual memory
 * space that has a special rule for the page-fault handlers (ie a shared
 * library, the executable area etc).
 */
struct vm_area_struct {
	/* The first cache line has the info for VMA tree walking. */

	unsigned long vm_start;		/* Our start address within vm_mm. */
	unsigned long vm_end;		/* The first byte after our end address
					   within vm_mm. */

	/* linked list of VM areas per task, sorted by address */
	struct vm_area_struct *vm_next, *vm_prev;

	struct rb_node vm_rb;

	/*
	 * Largest free memory gap in bytes to the left of this VMA.
	 * Either between this VMA and vma->vm_prev, or between one of the
	 * VMAs below us in the VMA rbtree and its ->vm_prev. This helps
	 * get_unmapped_area find a free area of the right size.
	 */
	unsigned long rb_subtree_gap;

	/* Second cache line starts here. */

	struct mm_struct *vm_mm;	/* The address space we belong to. */
	pgprot_t vm_page_prot;		/* Access permissions of this VMA. */
	unsigned long vm_flags;		/* Flags, see mm.h. */

	/*
	 * For areas with an address space and backing store,
	 * linkage into the address_space->i_mmap interval tree.
	 */
	struct {
		struct rb_node rb;
		unsigned long rb_subtree_last;
	} shared;

	/*
	 * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
	 * list, after a COW of one of the file pages.	A MAP_SHARED vma
	 * can only be in the i_mmap tree.  An anonymous MAP_PRIVATE, stack
	 * or brk vma (with NULL file) can only be in an anon_vma list.
	 */
	struct list_head anon_vma_chain; /* Serialized by mmap_sem &
					  * page_table_lock */
	struct anon_vma *anon_vma;	/* Serialized by page_table_lock */

	/* Function pointers to deal with this struct. */
	const struct vm_operations_struct *vm_ops;

	/* Information about our backing store: */
	unsigned long vm_pgoff;		/* Offset (within vm_file) in PAGE_SIZE
					   units */
	struct file * vm_file;		/* File we map to (can be NULL). */
	void * vm_private_data;		/* was vm_pte (shared mem) */

#ifndef CONFIG_MMU
	struct vm_region *vm_region;	/* NOMMU mapping region */
#endif
#ifdef CONFIG_NUMA
	struct mempolicy *vm_policy;	/* NUMA policy for the VMA */
#endif
	struct vm_userfaultfd_ctx vm_userfaultfd_ctx;
};




四、vm_operations_struct 结构体完整源码



vm_operations_struct 结构体完整源码 :

/*
 * These are the virtual MM functions - opening of an area, closing and
 * unmapping it (needed to keep files on disk up-to-date etc), pointer
 * to the functions called when a no-page or a wp-page exception occurs. 
 */
struct vm_operations_struct {
	void (*open)(struct vm_area_struct * area);
	void (*close)(struct vm_area_struct * area);
	int (*mremap)(struct vm_area_struct * area);
	int (*fault)(struct vm_fault *vmf);
	int (*huge_fault)(struct vm_fault *vmf, enum page_entry_size pe_size);
	void (*map_pages)(struct vm_fault *vmf,
			pgoff_t start_pgoff, pgoff_t end_pgoff);

	/* 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_fault *vmf);

	/* same as page_mkwrite when using VM_PFNMAP|VM_MIXEDMAP */
	int (*pfn_mkwrite)(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);

	/* Called by the /proc/PID/maps code to ask the vma whether it
	 * has a special name.  Returning non-NULL will also cause this
	 * vma to be dumped unconditionally. */
	const char *(*name)(struct vm_area_struct *vma);

#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);
#endif
	/*
	 * Called by vm_normal_page() for special PTEs to find the
	 * page for @addr.  This is useful if the default behavior
	 * (using pte_page()) would not find the correct page.
	 */
	struct page *(*find_special_page)(struct vm_area_struct *vma,
					  unsigned long addr);
};