《LKD3粗读笔记》(15)进程地址空间

news2024/12/23 9:18:35
  • 进程地址空间:内核除了管理本身的内存外,还必须管理用户空间中进程的内存,这个内存就是进程地址空间,即系统中每个用户空间进程所看到的内存。
  • Linux操作系统采用虚拟内存技术,系统中的所有进程之间以虚拟方式共享内存

1、 地址空间

  • 进程地址空间由进程可寻址的虚拟内存组成,内核允许进程使用这种虚拟内存中的地址。
  • 一个进程的地址空间与另一个进程的地址空间即使有相同的内存地址,实际上也彼此互不相干–这样的进程称为线程
  • 内存地址是一个给定的值,要在地址空间范围之内。
  • 进程没有权限访问所有的虚拟地址,可被访问的合法地址空间被称为内存区域
  • 进程可以通过内核给自己的地址空间动态的添加或减少内存区域。
  • 内存区域(也叫虚拟内存区域)可以包含各种内存对象,如下:
    • 可执行文件代码的内存映射,称为代码段(text section);
    • 可执行文件的已初始化全局变量的内存映射,称为数据段(data section);
    • 包含未初始化全局变量bss段的零页(页面中的信息全部都为0值,所以可用于映射bss段等目的)的内存映射;
    • 用于进程用户空间栈的零页的内存映射;
    • 每一个诸如C库或动态链接程序等共享库的代码段、数据段和bss也会被载入进程的地址空间;
    • 任何内存映射文件
    • 任何共享内存段
    • 任何匿名的内存映射,如malloc()分配的内存。

2、内存描述符

  • 内核使用内存描述符结构体表示进程的地址空间,该结构包含了和进程地址空间有关的全部信息。

  • 内存描述符由mm_struct结构体表示,定义在文件<linux/sched.h>中。

    	struct mm_struct {
    		struct vm_area_struct * mmap;		/* list of VMAs 内存区域链表 */
    		struct rb_root mm_rb;				/* VMA 形成的红黑树 */
    		struct vm_area_struct * mmap_cache;	/* last find_vma result 最近使用的内存区域 */
    	#ifdef CONFIG_MMU
    		unsigned long (*get_unmapped_area) (struct file *filp,
    					unsigned long addr, unsigned long len,
    					unsigned long pgoff, unsigned long flags);
    	#endif
    		unsigned long mmap_base;		/* base of mmap area */
    		unsigned long mmap_legacy_base;         /* base of mmap area in bottom-up allocations */
    		unsigned long task_size;		/* size of task vm space */
    		unsigned long highest_vm_end;		/* highest vma end address */
    		pgd_t * pgd;					/* 页全局目录 */
    		atomic_t mm_users;			/* How many users with user space? 使用地址空间的用户数 */
    		atomic_t mm_count;			/* How many references to "struct mm_struct" (users count as 1) 主使用计数器 */
    		int map_count;				/* number of VMAs 内存区域的个数 */
    	
    		spinlock_t page_table_lock;		/* Protects page tables and some counters 页表锁 */
    		struct rw_semaphore mmap_sem;	/*  内存区域的信号量*/
    	
    		struct list_head mmlist;		/* 所有mm_struct 形成的链表 List of maybe swapped mm's.	These are globally strung
    							 * together off init_mm.mmlist, and are protected
    							 * by mmlist_lock
    							 */
    	
    	
    		unsigned long hiwater_rss;	/* High-watermark of RSS usage */
    		unsigned long hiwater_vm;	/* High-water virtual memory usage */
    	
    		unsigned long total_vm;		/* Total pages mapped */
    		unsigned long locked_vm;	/* Pages that have PG_mlocked set */
    		unsigned long pinned_vm;	/* Refcount permanently increased */
    		unsigned long shared_vm;	/* Shared pages (files) */
    		unsigned long exec_vm;		/* VM_EXEC & ~VM_WRITE */
    		unsigned long stack_vm;		/* VM_GROWSUP/DOWN */
    		unsigned long def_flags;
    		unsigned long nr_ptes;		/* Page table pages */
    		unsigned long start_code, end_code, start_data, end_data;	/* 代码段的首、尾 数据的首、尾地址 */
    		unsigned long start_brk, brk, start_stack;	/* 堆的首、尾地址 */
    		unsigned long arg_start, arg_end, env_start, env_end;	/* 命令行参数的首、尾 环境变量的首、尾地址 */
    	
    		unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
    	
    		/*
    		 * Special counters, in some configurations protected by the
    		 * page_table_lock, in other configurations by being atomic.
    		 */
    		struct mm_rss_stat rss_stat;
    	
    		struct linux_binfmt *binfmt;
    	
    		cpumask_var_t cpu_vm_mask_var;
    	
    		/* Architecture-specific MM context */
    		mm_context_t context;	/* 体系结构特殊数据 */
    	
    		unsigned long flags; /* Must use atomic bitops to access the bits 状态标志 */
    	
    		struct core_state *core_state; /* coredumping support 核心转储的支持 */
    	#ifdef CONFIG_AIO
    		spinlock_t		ioctx_lock;		/* AIO I/O 链表锁*/
    		struct hlist_head	ioctx_list;	/* AIO I/O 链表 */
    	#endif
    
  • mmapmm_rb 这两个不同数据结构体描述的对象是相同的:该地址空间中的全部内存区域mmap 是以链表形式存放的,mm_rb以红黑树的形式存放。

  • 所有的mm_struct结构体都通过自身的mmlist域连接在一个双向链表中,该链表的首元素是init_mm内存描述符,它代表init进程的地址空间。

  • 操作该链表的时候需要使用mmlist_lock锁来防止并发访问,该锁定义在文件kernel/fork.c中。

  1. 分配内存描述符——copy_mm()
    • 在进程的进程描述符(task_struct)中,mm域存放着该进程使用内存描述符(mm_struct),所以current->mm指向当前进程的内存描述符。

    • fork()函数利用copy_mm()函数复制父进程的内存描述符,也就是current->mm域给其子进程,而子进程中mm_struct结构体实际是通过文件kernel/fork.c中的allocate_mm()宏从mm_cachep slab缓存中分配得到的。因此通常,每个进程都有唯一的mm_struct结构体,即唯一的进程地址空间。

    • 是否共享地址空间几乎是进程和Linux中所谓的线程间本质上的唯一区别。线程对内核来说仅仅是一个共享特定资源的进程而已。

    • 如何让父进程和子进程共享地址空间?
      在调用clone()时,设置CLONE_VM标志。(这样的子进程称为线程)当CLONE_VM被指定后,内核就不再需要调用allocate_mm()函数,而仅仅需要在调用copy_mm()函数中将mm域指向其父进程的内存描述符就可以了。

  2. 撤销内存描述符
    • exit_mm -> mmput -> mmdrop -> free_mm -> kmem_cache_free
    • 当进程退出时,内核会调用定义在kernel/exit.c中的exit_mm()函数,该函数执行一些常规的撤销工作,同时更新一些统计量。其中,该函数会调用mmput()函数减少内存描述符中mm_users用户计数,如果用户计数降到零,将调用mmdrop()函数,减少mm_count使用计数。
    • 如果使用计数也等于零了,说明该内存描述符不再有任何使用者了,那么调用free_mm()宏通过kmem_cache_free()函数将mm_struct结构体归还到mm_cachep slab缓存中。
  3. mm_struct与内核线程
    • 内核线程没有进程地址空间,也没有相关的内存描述符,所以内核线程对应的进程描述符中的mm域为空,即task_struct里的mm_struct为空。(因为内核线程没有用户上下文)
    • 内核线程对应的进程描述符中mm域为空,使用谁的?
      内核线程使用前一个进程的内存描述符active_mm域的作用。当一个进程被调度时,该进程的mm域指向的地址空间被装载到内存,进程描述符中active_mm域会被更新,指向新的地址空间。内核线程没有地址空间,所有mm域为NULL。于是,当一个内核线程被调度时,内核发现它的mm域为NULL,就会保留前一个进程的地址空间,随后内核更新内核线程对应的进程描述符中active_mm域,使其指向前一个进程的内存描述符。所以在需要的时候,内核线程便可以使用前一个进程的页表。因为内核线程不访问用户空间的内存,所以它们仅仅使用地址空间和内核内存相关的信息。

3、虚拟内存区域

  • 内存区域由vm_area_struct结构体描述,定义在文件<linux/mm_types.h>中。
  • 内存区域在Linux内核中页称作虚拟内存区域(VMAs:virtual memortAreas)。
  • vm_area_struct结构体描述了指定地址空间内连续区间上的一个独立内存范围。
  • 内核将每个内存区域作为一个单独的内存对象管理,每个内存区域都拥有一致的属性。
  • 每一个VMA就可以代表不同类型的内存区域。
    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;	/* VMA 链表 */
    
    	struct rb_node vm_rb;		/* 树上该VMA的节点 */
    
    	/*
    	 * 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.  相关的mm_struct内存描述符结构体 */
    	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, or
    	 * linkage of vma in the address_space->i_mmap_nonlinear list.
    	 *
    	 * For private anonymous mappings, a pointer to a null terminated string
    	 * in the user process containing the name given to the vma, or NULL
    	 * if unnamed.
    	 */
    	union {
    		struct {
    			struct rb_node rb;						/* 树上该VMA的节点 */
    			unsigned long rb_subtree_last;
    		} linear;
    		struct list_head nonlinear;
    		const char __user *anon_name;
    	} 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 匿名VMA对象  */
    
    	/* 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, *not* PAGE_CACHE_SIZE */
    	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
    };
    
  • 每个内存描述符都对应域进仓地址空间中的唯一区间。
  • vm_start域指向区间的首(最低)地址,vm_end域指向区间的尾(最高)地址之后的第一个字节–vm_start在区间内,vm_end在区间外。
  • 在同一个地址空间内的不同内存区间不能重叠。
  • vm_mm域指向和VMA相关的mm_struct结构体,注意,每个VMA对其相关的mm_struct结构体来说都是唯一的,所以即使两个独立的进程将同一个文件映射到各自的地址空间,它们分别都会有一个vm_area_struct结构体来标志自己的内存区域;
  • 如果两个线程共享一个地址空间,那么它们也同时共享其中的所有vm_area_struct结构体。
  1. VMA 标志
    • VMA标志是一种位标志,其定义在<linux/mm/h>

    • 它包含在vm_flags域内,标志了内存区域所包含的页面的行为和信息。

    • VMA标志可取值如下表:

      标志对VMA及其页面的影响
      VM_READ页面可读取
      VM_WRITE页面可写
      VM_EXEC页面可执行
      VM_SHARED页面可共享
      VM_MAYREADVM_READ 标志可被设置
      VM_MAYWRITEVM_WRITE 标志可被设置
      VM_MAYEXECVM_EXEC 标志可被设置
      VM_MAYSHAREVM_SHARE 标志可被设置
      VM_GROWSDOWN区域可向下增长
      VM_GROWSUP区域可向上增长
      VM_SHM区域可用作共享内存
      VM_DENYWRITE区域映射一个不可写文件
      VM_EXECUTABLE区域映射一个可执行文件
      VM_LOCKED区域中的页面被锁定
      VM_IO区域映射设备 I/O 空间
      VM_SEQ_READ页面可能被连续访问
      VM_RAND_READ页面可能被随机访问
      VM_DONTCOPY区域不能再fork()时被拷贝
      VM_DONTEXPAND区域不能通过mremap()增加
      VM_RESERVED区域不能被换出
      VM_ACCOUNT该区域是一个记账 VM 对象
      VM_HUGETLB区域使用了hugetlb 页面
      VM_NONLINEAR该区域是非线性映射的
  2. VMA 操作 -> vm_ops -> vm_operations_struct
    • vm_area_struct 结构体中的 vm_ops 域指向与指定内存区域相关的操作函数表,内核使用表中的方法操作VMA
    • vm_area_struct 作为通用对象代表了任何类型的内存区域,而操作表描述符对特定的对象实例的特定方法。
    • 操作函数表由vm_operations_struct结构体表示,定义在文件 <linux/mm.h>中:
      /*
       * 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 (*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
      	/* called by sys_remap_file_pages() to populate non-linear mapping */
      	int (*remap_pages)(struct vm_area_struct *vma, unsigned long addr,
      			   unsigned long size, pgoff_t pgoff);
      };
      
      函数描述
      *void open(struct vm_are_struct *area)当制定的内存区域被加入到一个地址空间时,该函数被调用
      *void close(struct vm_area_struct *area)当指定的内存区域从地址空间删除时,该函数被调用
      *void fault(struct vm_area_struct *area,struct vm_fault *vmf)当没有出现在物理内存中的页面被访问时,该函数被页面故障处理调用
      *int page_mkwrite(struct vm_area_struct *area, struct vm_fault *vmf)当某个页面为只读页面时,该函数被页面故障处理调用
      *int access(struct vm_area_struct *vma, unsigned long address, void *buf, int int write)get_uset_pages()函数调用失败时,该函数被access_process_vm()函数调用
  3. 内存区域的树型结构和内存区域的链表结构——mm_struct*mmapmm_rb
    • mmap域使用单独链表连接所有的内存区域对象。每一个vm_area_struct结构体通过自身的vm_next域被连入链表,所有的区域按地址增长的方法排序,mmap域指向链表中第一个内存区域,链中最后一个结构体指针指向空。
    • mm_rb域使用红黑树连接所有的内存区域对象。mm_rb域指向红黑树的根节点,地址空间中每一个vm_area_struct结构体通过自身的vm_rb域连接到树中。
    • 链表用于需要遍历全部节点的时候,而红黑树适用于在地址空间中定位特定内存区域的时候。内核为了内存区域上的各种不同操作都能获得高性能,所以同时使用了这两种数据结构。
  4. 实际使用中的内存区域——/proc文件系统或pmap(1)工具
    • 可以使用/proc文件系统和pmap(1)工具查看给定进程的内存空间和其中所含的内存区域。
    • eg:
      在这里插入图片描述
    • 可以使用cat /proc/\<pid>/mapspmap \<pid>列出该进程地址空间中包含的内存区域。
    • 其中由代码段、数据段和bss段等,假设该进程与C库动态链接,那么地址空间还将分别包括libc.sold.so对应的上述三种内存区域。此外,地址空间还要包含进程栈对应的内存区域。
      在这里插入图片描述在这里插入图片描述
    • 每行数据格式如下:
      • 开始-结束 访问权限 偏移 主设备号:次设备号 i 节点 文件
      • 前三行分别对应C库中的libc.so的代码段、数据段和bss段;
      • 接着两行是可执行对象的代码段和数据段;
      • 接下来三行是动态链接程序ld.so的代码段、数据段和bss段;
      • 最后一行是进程的栈。
    • 代码段具有可读且可执行权限,数据段和bss具有可读、可写但不可执行权限。而堆栈则可读、可写,甚至还可执行。
    • 共享不可写内存的方法节约了大量的内存空间。
    • 没有映射文件的内存区域的设备标志位00:00,索引节点标志也为0,这个区域就是零页–零页映射的内容全为零。
    • 如果将零页映射到可写的内存区域,那么该区域将全初始化为0(bss段需要的就是全0的内存区域)。
    • 由于内存未被共享,所以只要一有进程写该处数据,那么该处数据就将被拷贝出来(写时拷贝-COW),然后才被更新。
    • 每个和进程相关的内存区域都对应一个vm_area_struct结构体,进程不同于线程,进程结构体stask_struct包含唯一的mm_struct结构体引用,而线程则是共享了地址空间,即copy_mm

4、操作内存区域

  1. find_vma()
    • 内核提供了find_vma()函数用来找到一个给定的内存地址属于哪一个内存区域(在指定的地址空间中搜索第一个vm_end大于addr的内存区域,也就是寻找第一个包含addr或者首地址大于addr的内存区域)。定义在文件<mm/mmap.c>中:
      struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr);
      
  2. find_vma_prev()
    • find_vma_prev()函数和find_vma()工作方式相同,但它返回第一个小于addrVMA
    • 该函数定义和声明分别在文件mm/mmap.c和文件<linux/mm.h>中:
      struct vm_area_struct *find_vma_prev(struct mm_struct *mm,unsigned long addr,struct vm_area_struct **pprev);
      
    • pprev参数存放指向先于addr的VMA指针。
  3. find_vma_intersection()
    • find_vma_intersection()函数返回第一个和指定地址区间相交的VMA
    • 该函数是内联函数,定义在文件<linux/mm.h>中:
      /* Look up the first VMA which intersects the interval start_addr..end_addr-1,
         NULL if none.  Assume start_addr < end_addr. */
      static inline struct vm_area_struct * find_vma_intersection(struct mm_struct * mm, unsigned long start_addr, unsigned long end_addr)
      {
      	struct vm_area_struct * vma = find_vma(mm,start_addr);
      
      	if (vma && end_addr <= vma->vm_start)
      		vma = NULL;
      	return vma;
      }
      
    • 参数mm是要搜索的地址空间;
    • start_addr是区间的开始首地址;
    • end_addr是区间的尾地址。
    • 如果find_vma()返回NULL,那么find_vma_interesection()也会返回NULL
    • 如果find_vma()返回有效的VMAfind_vma_intersection()只有在该VMA的起始位置于给定的地址区间结束位置之前,才将其返回。如果VMA的起始位置大于指定位置范围的结束位置,则该函数返回NULL

5、mmap()do_mmap():创建地址区间

  • mmap()->do_mmap()

  • 内核使用do_mmap()函数创建一个新的线性地址区间。但是说该函数创建了一个新的VMA并不非常准确,因为如果创建的地址区间和一个已经存在的地址区间相邻,并且它们具有相同的访问权限,两个区间合并为一个。如果不能合并,则确实需要创建一个新的VMA。

  • 无论哪种情况,do_mmap()函数都会将一个地址区间加入到进程的地址空间中–无论是扩展已存在的内存区域还是创建一个新的区域。

  • do_mmap()函数定义在文件kernel/arch/powerpc/kernel/syscalls.c,以内联形式展示,书中<linux/mm.h>中。 ps:比较旧的内核版本是do_mmap,后面更新成do_mmap2,区别仅仅在于最后一个参数,后者使用页面偏移作为最后一个参数,使用页面偏移量可以映射更大的文件和更大的偏移位置:

    unsigned long sys_mmap2(unsigned long addr, size_t len,
                            unsigned long prot, unsigned long flags,
                            unsigned long fd, unsigned long pgoff)
    {
            return do_mmap2(addr, len, prot, flags, fd, pgoff, PAGE_SHIFT-12);
    }
    
    unsigned long sys_mmap(unsigned long addr, size_t len,
                           unsigned long prot, unsigned long flags,
                           unsigned long fd, off_t offset)
    {
            return do_mmap2(addr, len, prot, flags, fd, offset, PAGE_SHIFT);
    }
    
    
    static inline unsigned long do_mmap2(unsigned long addr, size_t len,
                            unsigned long prot, unsigned long flags,
                            unsigned long fd, unsigned long off, int shift)
    {
            unsigned long ret = -EINVAL;
    
            if (!arch_validate_prot(prot))
                    goto out;
    
            if (shift) {
                    if (off & ((1 << shift) - 1))
                            goto out;
                    off >>= shift;
            }
    
            ret = sys_mmap_pgoff(addr, len, prot, flags, fd, off);
    out:
            return ret;
    }
    
  • 映射没有和文件相关,该情况称作匿名映射(anonymous mapping);

  • 如果指定了文件名和偏移量,则该映射称为文件映射(file-backed mapping);

  • 虽然C库仍然可以使用原始版本的映射方法(mmap),但它其实还是基于函数mmap2()进行的,因为对原始mmap()方法的调用是通过将字节偏移转化为页面偏移,从而转化为对mmap2()函数的调用来实现的。

6、munmap()和do_munmap():删除地址区间

  • munmap()kernel/include/linux/syscalls.h) ->do_munmap()
  • do_munmap()函数从特定的进程地址空间中删除指定地址区间,该函数定义在文件kernel/mm/nommu.c,书中是<linux/mm.h>
    int do_munmap(struct mm_struct *mm,unsigned long start, size_t len);
    
  • 第一个参数指定要删除区域所在的地址空间,删除从地址start开始,长度为len字节的地址区间;成功返回零,失败返回负数错误码。
  • 系统调用munmap()给用户空间程序提供了一种从自身地址空间删除指定地址区间的方法,和系统调用mmap()作用相反:
    asmlinkage long sys_munmap(unsigned long addr, size_t len);
    

7、页表–转换虚拟地址和物理地址的工作

  • 虽然应用程序操作的对象是映射到物理内存上的虚拟内存,但是处理器直接操作的却是物理内存。

  • 页表为何存在?
    当应用程序访问一个虚拟地址时,首先必须将虚拟地址转化成物理地址,然后处理器才能解析地址访问请求。地址的转换工作需要通过查询页表才能完成,即地址转换需要将虚拟地址分段,使每段虚拟地址都作为一个索引指向页表,而页表项则指向下一级别或指向最终的物理页面。

  • Linux中使用三级页表完成地址转换。利用多级页表能够节约地址转换需占用的存放空间。
    在这里插入图片描述

  • 顶级页表是页全局目录(PGD),包含了一个pgd_t类型数组,多数体系结构图中pgd_t类型等同于无符号长整型类型。PGD中的表项指向二级页目录中的表项:PMD

  • 二级页表是中间页目录(PMD),是个pmd_t类型数组,其中的表项指向PTE中的表项;

  • 最后一级的页表简称页表(PTE),包含了pte_t类型的页表项,该页表项指向物理页面。

  • 每个进程都有自己的页表(线程会共享页表)。

  • 内存描述符的pgd域指向的就是进程的页全局目录(PGD)。

  • 操作和检索页表时,必须使用page_table_lock锁,该锁在相应的进程的内存描述符中,以防止竞争条件。

  • 页表对应的结构体依赖于具体的体系结构,定义在文件<asm/oage.h>

  • 页表机制的性能?

    • 由于几乎每次对虚拟内存中的页面访问都必须先解析页表,从而得到物理内存中的对应地址,所以页表操作的性能非常关键。但不幸的是,搜索内存的物理地址速度很有限,因此为了加可搜索,多数体系结构图都实现了一个翻译后缓冲器(translate lookaside buffer,TLB)。
    • TLB作为一个将虚拟地址映射到物理地址的硬件缓存,当请求访问一个虚拟地址时,处理器将首先检查TLB中是否缓存了该虚拟地址到物理地址的映射,如果在缓存中直接命中,物理地址立刻返回;否则,就需要再通过页表搜索需要的物理地址。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/564102.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

面试都不会造火箭,还想拿20k,过于离谱

前段时间公司缺人&#xff0c;也面了许多测试&#xff0c;一开始瞄准的就是中级水准&#xff0c;当然也没指望能来大牛&#xff0c;提供的薪资在15-20k这个范围&#xff0c;来面试的人有很多&#xff0c;但是平均水平真的让人很失望。看了简历很多上面都是写有4年工作经验&…

iOS App外包开发的内存泄露解决

在iOS应用开发中&#xff0c;内存泄露是一个常见的问题。内存泄露会导致应用程序占用越来越多的内存&#xff0c;最终可能导致应用程序崩溃。为了解决这个问题&#xff0c;可以使用以下工具和方法来查找和修复内存泄露。今天和大家分享这方面的知识&#xff0c;希望对大家有所帮…

AttributeError: module ‘gym‘ has no attribute ‘benchmark_spec‘解决办法

报错如下&#xff1a; 我安装的gym版本是gym-0.26.2 报错原因&#xff1a;gym版本太高了&#xff0c;需要降低版本 pip install gym0.9.0 -i https://pypi.douban.com/simple

@Qualifier注解详解

本文来说下Qualifier注解使用 文章目录 Autowired注解Qualifier注解Primary注解 Resource注解 Autowired注解 spring Autowire 的注解默认是按类型注入bean 如果存在多个实现类&#xff0c;可以使用 Qualifier注解 Qualifier注解 Primary注解 存在多个实现类&#xff0c;也可以…

支持双向充放电的高效Buck-Boost控制器——TMI5810

传统方案下&#xff0c;MCU需要分别控制“充电电路”和“放电电路”的功能模块实现电池包充放电&#xff0c;整个过程相当复杂&#xff0c;电路设计也比较繁琐。 拓尔微TMI5810是一颗集双向充放电和高效率于一身的同步升降压电源管理芯片&#xff0c;能有效解决多节锂电池充放电…

RAM Sequential

前段时间&#xff0c;在微信公众号上偶然看到一篇很不错的技术分享文章&#xff1a;《南湖处理器DFT设计范例》。文中详细介绍了中科院计算所的RISC-V处理器实施的DFT设计。 去年&#xff0c;也基于一款处理器应用过Share Test Bus技术&#xff0c;但在memory界面fault测试的问…

基于 ESP32 创建 HTTP Server 服务器,支持载入文件到服务器,并对载入文件进行删除管理

软件编程指南参见&#xff1a;HTTP 服务器 测试准备 任意一款 ESP32 系列开发板2.4GHz 路由器热点准备各种格式的文件 测试软件 可基于 esp-idf/examples/protocols/http_server /file_serving 例程进行测试 测试步骤 1、软件配置 只需要设置 ESP32 连接的 2.4GHz 的 WiFi…

linux 部署Nginx

1&#xff0c;进入官网下载Nginx资源: 官网下载&#xff1a;http://nginx.org/en/download.html 2、下载 nginx-1.20.2 3、上传服务器 /opt目录 4、解压nginx-1.20.2.tar.gz 到当前目录 tar -zxvf nginx-1.20.2.tar.gz 5、配置基本信息&#xff0c;命令如下 #配置config…

香港服务器如何操作域名解析让网站上线?

​  网站上线是一个需要多个步骤的过程&#xff0c;其中之一就是解析IP。在这个过程中&#xff0c;您需要将您的网站域名解析到香港服务器IP地址&#xff0c;以便访问者可以通过域名来访问您的网站。 下面是解析IP的一般步骤&#xff1a; 1. 获取服务器IP地址&#xff1a;首先…

回收站文件恢复教程:如何找回误删除的重要文件

在使用电脑的过程中&#xff0c;误删除文件是一件很常见的事情&#xff0c;尤其是对于那些不太熟悉电脑操作的人来说&#xff0c;这种情况更为普遍。当重要文件误删除之后&#xff0c;人们最希望的就是能够快速地找回它。其实&#xff0c;在 Windows 操作系统中&#xff0c;回收…

View的工作原理

View的工作原理 当Activity对象被创建的时候,会将DecorView添加到Window中,同时创建ViewRootImpl对象并将它和DecorView关联起来 ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot完成的 View的绘制是从ViewRoot的perf…

如何编写快速的SQL查询(一)——MySQL8.0优化器查询优化处理与样例

当希望MySQL能够以更高的性能运行查询时&#xff0c;最好的办法就是弄清楚MySQL是如何优化和执行查询的。一旦理解了这一点&#xff0c;很多查询优化工作实际上就是遵循一些原则让优化器能够按照预想的合理的方式运行。 MySQL是如何执行一个查询的过程的&#xff1f;根据图8-1可…

Java基础 变量与数据类型(类型转换)

变量 为什么需要变量 一花一世界&#xff0c;如果把一个程序看做一个世界或一个社会的话&#xff0c;那么变量就是程 序世界的花花草草、万事万物。即&#xff0c;变量是程序中不可或缺的组成单位&#xff0c;最基 本的存储单元。 初识变量 变量的概念 内存中的一个存储区域…

4.计算机网络基础

文章目录 1.网络互联模型2.常见网络协议&#xff08;1&#xff09;应用层协议&#xff1a;FTP、HFTP、HTTP&#xff08;2&#xff09;传输层协议&#xff1a;TCP、UDP三次握手、四次挥手&#xff08;重要&#xff09;TCP 和 UDP 区别&#xff08;1&#xff09;连接&#xff08;…

【可乐荐书】人工智能数学基础

本栏目将推荐一些经典的、有趣的、有启发性的书籍&#xff0c;这些书籍涵盖了各个领域&#xff0c;包括文学、历史、哲学、科学、技术等等。相信这些书籍不仅可以让你获得知识&#xff0c;还可以让你感受到阅读的乐趣和魅力。 今天给大家推荐的书籍是&#xff1a;《人工智能数…

1688阿里巴巴中国站按关键字搜索抓取新品数据API接口展示示例(封装可高并发)(Java系列)

一、电商平台上新的重要性 电商平台上新非常重要。 首先&#xff0c;持续的新品上线可以吸引更多的用户访问平台和留存用户的兴趣。新品可以激发用户想要知道更多、购买更多的欲望&#xff0c;从而提高用户的使用频率和转化率。此外&#xff0c;新品上线也可以使电商平台更具…

windows下lib文件中的函数列表查看

可以使用Visual Studio中自带的dumpbin工具&#xff0c;首先应确保该文件路径已经加载到了环境变量中&#xff0c;以博主环境为例&#xff0c;其路径在 D:\xxxxxxxxxxxxx\Microsoft Visual Studio 14.0\VC\bin dumpbin.exe /LINKERMEMBER libfile.lib

pytorch房价预测(线性回归)

文章目录 一、前言二、实现方法 一、前言 任务目标&#xff1a;根据统计在csv中的房屋属性相关数据&#xff0c;预测房屋最终成交价格数据集&#xff1a;《住宅属性数据集》&#xff0c;自取https://download.csdn.net/download/weixin_43721000/87785277 3.数据集字段解释&am…

JAVA开发(对大表数据逐条进行处理踩的坑记录一下)

一、编程语言介绍 所使用的编程语言为JAVA。纯后端开发。 二、炫技代码分享 现在我编写代码一般通过逆向工程生成&#xff0c;只需要设计好数据库表就可以逆向生成后端的接口模块。 三、案例分享 逆向工程。逆向工程涉及到的输出模块。具体运用需要关注和联系博主分享使用。 ##…

day82【Leetcode】

文章目录 前言一、检查替换后的词是否有效&#xff08;力扣1003&#xff09;二、有效的括号&#xff08;力扣20&#xff09;【1003类似题目】每日一题&#xff1a;数青蛙&#xff08;力扣1419&#xff09; 前言 1、检查替换后的词是否有效 2、有效的括号 3、数青蛙 一、检查替…