精华内容
下载资源
问答
  • linux 页表的作用
    2022-03-14 16:25:49

    页表

    页表的作用

    进程操作的对象是物理内存通过映射得到的虚拟内存,但CPU实际上操作的还是物理内存。所以当进程访问一个虚拟内存的地址时,首先必须将虚拟内存地址转化成物理内存地址,然后CPU才能解析地址访问请求。地址的转换工作需要通过查询页表才能完成。
    页表的作用就是将虚拟内存页转换为物理内存页框
    在这里插入图片描述
    逻辑内存也就是虚拟内存。上图虚拟内存中的页0在页表中对应物理内存的页框1
    虚拟内存地址=页号+页偏移。
    上图的0代表了页号,根据页号在页表中找出对应的物理内存的页框号。
    页的大小与页框的大小是一致的,这样设计方便进行映射。

    更多相关内容
  • Linux页表与ARM硬件页表说明ARM二级页表映射关系 说明 Kernel版本:4.0.0 ARM处理器,Contex-A9,QEMU平台   内核初始化arm页表的内容,low_memory映射过程,之前也大概写了。但是在arm平台中,引入了硬件页表、...

    说明

    Kernel版本:4.0.0
    ARM处理器,Contex-A9,QEMU平台
      内核初始化arm页表的内容,low_memory映射过程,之前也大概写了。但是在arm平台中,引入了硬件页表、linux页表的概念,本文描述为何要这样处理,以及内核、arm硬件页表属性进行说明。
      参考书籍《奔跑吧Linux内核:基于Linux4.x内核源代码问题分析》2.2.1 ARM32页表映射。

    ARM二级页表映射关系

      Linux中ARM32位架构采用2级页表映射方式,MMU查找地址关系如下图,画图麻烦,直接从书抄了一张过来。
    ARM MMU映射过程
      当片上TLB中不包含要求访问的虚拟地址入口是时,转换过程被启动。转换表基地址寄存器保存着第一级转换表基址的物理地址。只有bit[31:14]有效,bits[13:0]应该是0。所以第一级页表必须16KB对齐。对应的32位系统中,section大小1M,总共4096个section,每个section 4个字节,所以一级页表大小是16Kb。
      ARM MMU页表如上图所示。如果采用单层的段映射,内存中有个段映射表,表中4096个表项,每个表项的大小是4Byte,所以这个段映射表的大小是16KB。每个段对应1MB大小的地址空间。当cpu访问内存时,32位虚拟地址的高12位用于访问段映射表的索引,从表中找到相应的表项,每个表项提供了一个12位的物理段地址,以及相应的标志位,如可读、可写等标志位。将这个12位物理地址和虚拟地址的低20位拼凑在一起,就可以得到32位的物理地址。linux初始化时,内核low memory使用的大部分是段映射的方式。
      如果采用页表映射的方式,段映射表就变成一级映射表,linux内核中成为pmd。其表项提供的不再是物理段地址,而是二级页表的基地址。32位虚拟地址的高12位作为一级页表的索引值,找到相应的一级页表,每个表项指向一个二级页表。从虚拟地址的次8位作为访问二级页表的索引值,得到相应的页表项,从这个页表项中找到20位的物理页面地址。最后将这20位物理页面地址和虚拟地址的低12位拼凑在一起,得到最终的32位物理地址。这个过程在ARM32架构中由MMU硬件完成,软件不需要介入。

    Linux页表机制

      早期Linux内核是基于x86体系结构设计的,x86页表中有3个标志位是ARM32硬件页表没有的。
    PTE_DIRTY:cpu在写操作时会设置该标志位,表示对应页面被写过,为脏页。
    PTE_YOUNG:CPU访问该页时会设置该标志位。在页面换出时,如果该标志位位置了,说明该页刚被访问过,页面是young的,不适合把该页换出,同时清除该标志位。
    PTE_PRESENT:表示页在内存中。
      针对这种情况,Linux维护了一份Linux版本的页表,用于模拟这些标志位。由于一个页面是4KB大小,每个section 1MB空间,需要256个pte页表项,占用1KB,Linux版本也需要占用1KB,所以Linux创建二级页表时,以2MB为单位,申请1个PAGE存放对应的页表项。前2KB存放linux版本页表,后2KB存放硬件页表。存放地址如下。

     *    pgd             pte
     * |        |
     * +--------+
     * |        |       +------------+ +0
     * +- - - - +       | Linux pt 0 |
     * |        |       +------------+ +1024
     * +--------+ +0    | Linux pt 1 |
     * |        |-----> +------------+ +2048
     * +- - - - + +4    |  h/w pt 0  |
     * |        |-----> +------------+ +3072
     * +--------+ +8    |  h/w pt 1  |
     * |        |       +------------+ +4096
    

    在这里插入图片描述

    ARM/Linux PTE属性

    ARM硬件PTE属性

      下图表示ARM硬件二级页表属性,Linux内核使用的是Small page。在这里插入图片描述

      可以看到每个页表项中不仅包含了下级页表或者物理地址的映射地址(除了无效页表项),而且包含了诸如:TEX S SBZ AP 等属性位,这些属性位影响着 CPU 对内存的访问动作。
    C 位代表是否cache。
    B 位代表是否使能高速buffer。
    TEX C B 用来设置内存种类与缓存策略。正常内核开发时,需要关心的基本也就是是否使能cache和buffer。
    APX/AP 被称作访问权限标志位,规定了映射到的内存的访问权限:可读、可写、只读等等。
    S 标志某块内存的共享性,设置了标志位 S 的表项所映射的物理内存是可共享的,反之则是非可共享的。
    XN 代表可执行,设置了表示页面不可执行。

    内核预定义宏

      内核中预定义了一些宏,用于页表属性设置,包括Linux版本页表,以及硬件版本页表。
      先看Linux版本页表对应的宏,L开头代表Linux。由于使用2级映射,所以宏位于pgtable-2level.h。

    /*
     * "Linux" PTE definitions.
     *
     * We keep two sets of PTEs - the hardware and the linux version.
     * This allows greater flexibility in the way we map the Linux bits
     * onto the hardware tables, and allows us to have YOUNG and DIRTY
     * bits.
     *
     * The PTE table pointer refers to the hardware entries; the "Linux"
     * entries are stored 1024 bytes below.
     */
    #define L_PTE_VALID		(_AT(pteval_t, 1) << 0)		/* Valid */
    #define L_PTE_PRESENT		(_AT(pteval_t, 1) << 0)
    #define L_PTE_YOUNG		(_AT(pteval_t, 1) << 1)
    #define L_PTE_DIRTY		(_AT(pteval_t, 1) << 6)
    #define L_PTE_RDONLY		(_AT(pteval_t, 1) << 7)
    #define L_PTE_USER		(_AT(pteval_t, 1) << 8)
    #define L_PTE_XN		(_AT(pteval_t, 1) << 9)
    #define L_PTE_SHARED		(_AT(pteval_t, 1) << 10)	/* shared(v6), coherent(xsc3) */
    #define L_PTE_NONE		(_AT(pteval_t, 1) << 11)
    
    /*
     * These are the memory types, defined to be compatible with
     * pre-ARMv6 CPUs cacheable and bufferable bits: n/a,n/a,C,B
     * ARMv6+ without TEX remapping, they are a table index.
     * ARMv6+ with TEX remapping, they correspond to n/a,TEX(0),C,B
     *
     * MT type		Pre-ARMv6	ARMv6+ type / cacheable status
     * UNCACHED		Uncached	Strongly ordered
     * BUFFERABLE		Bufferable	Normal memory / non-cacheable
     * WRITETHROUGH		Writethrough	Normal memory / write through
     * WRITEBACK		Writeback	Normal memory / write back, read alloc
     * MINICACHE		Minicache	N/A
     * WRITEALLOC		Writeback	Normal memory / write back, write alloc
     * DEV_SHARED		Uncached	Device memory (shared)
     * DEV_NONSHARED	Uncached	Device memory (non-shared)
     * DEV_WC		Bufferable	Normal memory / non-cacheable
     * DEV_CACHED		Writeback	Normal memory / write back, read alloc
     * VECTORS		Variable	Normal memory / variable
     *
     * All normal memory mappings have the following properties:
     * - reads can be repeated with no side effects
     * - repeated reads return the last value written
     * - reads can fetch additional locations without side effects
     * - writes can be repeated (in certain cases) with no side effects
     * - writes can be merged before accessing the target
     * - unaligned accesses can be supported
     *
     * All device mappings have the following properties:
     * - no access speculation
     * - no repetition (eg, on return from an exception)
     * - number, order and size of accesses are maintained
     * - unaligned accesses are "unpredictable"
     */
    #define L_PTE_MT_UNCACHED	(_AT(pteval_t, 0x00) << 2)	/* 0000 */
    #define L_PTE_MT_BUFFERABLE	(_AT(pteval_t, 0x01) << 2)	/* 0001 */
    #define L_PTE_MT_WRITETHROUGH	(_AT(pteval_t, 0x02) << 2)	/* 0010 */
    #define L_PTE_MT_WRITEBACK	(_AT(pteval_t, 0x03) << 2)	/* 0011 */
    #define L_PTE_MT_MINICACHE	(_AT(pteval_t, 0x06) << 2)	/* 0110 (sa1100, xscale) */
    #define L_PTE_MT_WRITEALLOC	(_AT(pteval_t, 0x07) << 2)	/* 0111 */
    #define L_PTE_MT_DEV_SHARED	(_AT(pteval_t, 0x04) << 2)	/* 0100 */
    #define L_PTE_MT_DEV_NONSHARED	(_AT(pteval_t, 0x0c) << 2)	/* 1100 */
    #define L_PTE_MT_DEV_WC		(_AT(pteval_t, 0x09) << 2)	/* 1001 */
    #define L_PTE_MT_DEV_CACHED	(_AT(pteval_t, 0x0b) << 2)	/* 1011 */
    #define L_PTE_MT_VECTORS	(_AT(pteval_t, 0x0f) << 2)	/* 1111 */
    #define L_PTE_MT_MASK		(_AT(pteval_t, 0x0f) << 2)
    

      内核对应的宏,位于pgtable-2level-hwdef.h

    /*
     * + Level 2 descriptor (PTE)
     *   - common
     */
    #define PTE_TYPE_MASK		(_AT(pteval_t, 3) << 0)
    #define PTE_TYPE_FAULT		(_AT(pteval_t, 0) << 0)
    #define PTE_TYPE_LARGE		(_AT(pteval_t, 1) << 0)
    #define PTE_TYPE_SMALL		(_AT(pteval_t, 2) << 0)
    #define PTE_TYPE_EXT		(_AT(pteval_t, 3) << 0)		/* v5 */
    #define PTE_BUFFERABLE		(_AT(pteval_t, 1) << 2)
    #define PTE_CACHEABLE		(_AT(pteval_t, 1) << 3)
    
    /*
     *   - extended small page/tiny page
     */
    #define PTE_EXT_XN		(_AT(pteval_t, 1) << 0)		/* v6 */
    #define PTE_EXT_AP_MASK		(_AT(pteval_t, 3) << 4)
    #define PTE_EXT_AP0		(_AT(pteval_t, 1) << 4)
    #define PTE_EXT_AP1		(_AT(pteval_t, 2) << 4)
    #define PTE_EXT_AP_UNO_SRO	(_AT(pteval_t, 0) << 4)
    #define PTE_EXT_AP_UNO_SRW	(PTE_EXT_AP0)
    #define PTE_EXT_AP_URO_SRW	(PTE_EXT_AP1)
    #define PTE_EXT_AP_URW_SRW	(PTE_EXT_AP1|PTE_EXT_AP0)
    #define PTE_EXT_TEX(x)		(_AT(pteval_t, (x)) << 6)	/* v5 */
    #define PTE_EXT_APX		(_AT(pteval_t, 1) << 9)		/* v6 */
    #define PTE_EXT_COHERENT	(_AT(pteval_t, 1) << 9)		/* XScale3 */
    #define PTE_EXT_SHARED		(_AT(pteval_t, 1) << 10)	/* v6 */
    #define PTE_EXT_NG		(_AT(pteval_t, 1) << 11)	/* v6 */
    

    PTE设置函数

      ARMv7架构,set_pte_ext位于arch/arm/mm/proc-v7-2level.S中,代码实现如下:

    #define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)
    

    该接口有三个参数,Linux版本pte地址,要写入页表的属性,以及额外的属性。

    ENTRY(cpu_v7_set_pte_ext)
    #ifdef CONFIG_MMU
    	str	r1, [r0]			@ linux version  @直接把pte属性写入Linux版本页表
    									
    									
    	bic	r3, r1, #0x000003f0			@r1值清除bit4~bit9存储到r3,r3作为临时变量存储硬件pte属性。
    	bic	r3, r3, #PTE_TYPE_MASK		@清除r3 bit0、bit1,没有清除bit3、bit2是因为cache、buffer这两位,hw和linux保持一致。
    	orr	r3, r3, r2					@r3=r3|r2,r2是额外属性
    	orr	r3, r3, #PTE_EXT_AP0 | 2	@设置hw bit4(AP0bit),设置hw bit1(根据上图,可以得知small page下bit1必须等于1)
    
    	tst	r1, #1 << 4                 @判断r1传入的页表属性,是否设置了bit4(tex位)
    	orrne	r3, r3, #PTE_EXT_TEX(1)	@如果linux属性设置了tex位,则硬件版本也要置位tex(hw bit6~8),两者bit偏移位置不一样。
    
    	eor	r1, r1, #L_PTE_DIRTY				@翻转r1 DIRTY位(bit6)
    	tst	r1, #L_PTE_RDONLY | L_PTE_DIRTY		@判断是否设置了只读。+
    	orrne	r3, r3, #PTE_EXT_APX			@设置硬件版本hw bit9(APX值(置1只读))
    
    	tst	r1, #L_PTE_USER						@判断传入属性是否设置L_PTE_USR
    	orrne	r3, r3, #PTE_EXT_AP1			@设置硬件版本hw bit5(AP1bit)
    
    	tst	r1, #L_PTE_XN						@判断传入属性是否设置XN(不可执行)
    	orrne	r3, r3, #PTE_EXT_XN				@设置硬件版本hw bit0(不可执行位)
    
    	tst	r1, #L_PTE_YOUNG
    	tstne	r1, #L_PTE_VALID
    	eorne	r1, r1, #L_PTE_NONE
    	tstne	r1, #L_PTE_NONE
    	moveq	r3, #0
    
     ARM(	str	r3, [r0, #2048]! )
     THUMB(	add	r0, r0, #2048 )
     THUMB(	str	r3, [r0] )
    	ALT_SMP(W(nop))
    	ALT_UP (mcr	p15, 0, r0, c7, c10, 1)		@ flush_pte
    #endif
    	bx	lr
    ENDPROC(cpu_v7_set_pte_ext)
    

      根据汇编代码,可以看到清除了linux页表属性的bit0、bit1、bit4~bit9。也即bit2、bit3 linux页表和硬件页表一致。其余bit不一样。基本注释都已在上面写了。
      DIRTY页模拟,创建映射时,设置硬件页表是只读的。当往页面写入时,会触发写权限缺页中断。(虽然Linux版本页面表标记了可写权限,但是ARM硬件表,没有写入权限。)在缺页中断handle_pte_fault中会在该页的Linux版本PTE中标记为“dirty”。

    tst	r1, #L_PTE_YOUNG
    tstne	r1, #L_PTE_VALID
    eorne	r1, r1, #L_PTE_NONE
    tstne	r1, #L_PTE_NONE
    moveq	r3, #0
    

      这部分代码,可以等同于如下:

    tst r1, @L_PTE_YOUNG
    tstne r1, #L_PTE_PRESENT
    moveq r3, #0
    

      如果没有设置L_PTE_YOUNG并且L_PTE_PRESENT置位,那么保持Linux版本页表不变,把ARM硬件版本页表项内容清0。

    展开全文
  • MMU的页表查询过程介绍,涉及到页表项、页表、页面、VPN、PFN等等关系的理解,为Linux内核管理奠定基础

    在Linux内核管理中,MMU的页表查询是最基础的知识,这里目的是通过理解页表查询过程,去厘清页表、页表项、页面、页面偏移量(也称页内偏移量)等概念及它们之间的关系,为Linux内核管理的学习与使用奠定基础。

    接下来这里用最简单的一级页表查询过程来说明,理解了一级页表的查询过程,那么二级页表查询、三级页表查询也就能依次类推了。

    前提条件:

    以32位宽的处理器(虚拟地址和物理地址均是32位位宽)、页面大小4KB为前提讲解。

    前提条件的分析:

    1.寻址空间大小的由来:
    由于是32位位宽,所以寻址空间大小是4GB,怎么计算来的呢?
    32位—>则可以知道一共有2^32个地址,会有地址0x0000 0000、0x0000 0001、0x0000 0002…0xffff ffff,每个相邻地址间都只相差一个字节(1Byte),那是因为arm是以单个字节为单位寻址的,其他变量类型只是单个字节char类型的变种。所以2^32*1Byte = 4GB(地址个数 * 每个地址占1Byte大小 = 总的寻址空间大小),即寻址空间大小。

    2.页面大小
    前提条件里说页面大小4KB,这个其实是指物理页面大小,这个大小是由.s文件里设定的,也可以设定为16KB、64KB等等,一开始就设定了,设定好后,后面的页表大小才能确定,这里就以经典的4KB大小来说明。

    一级页表查询

    基于上述前提条件,一级页表查询过程如下图:
    一级页表查询
    根据上图,我们先理解:
    1.虚拟地址的VA[11:0]:称为页面偏移量,因为物理页面大小是4KB,所以一个物理页面内有2^12 个地址,即页面内偏移量是0- - -2^12,需要占用12位;
    2.虚拟地址的VPN[31:12]:称为虚拟页帧号,它代表在页表中的偏移量,页表的基地址加上VPN(页表中的偏移量),就得到了页表中具体页表项的地址;
    3.有效位:单个页表项中的一个属性位v,表示页表项的属性,v=0表示对应物理页面不存在内存中;v=1表示对应的物理页面存在内存中。

    由上图我们可以知道:
    查询一个虚拟地址对应的物理地址的过程如下:
    虚拟地址的VPN作为索引(偏移量) + 页表基地址(即页表的起始地址,存放在页表基地址寄存器) = 页表中具体某一个页表项的地址
    —>
    找到页表项地址后,读取出该页表项里存放的PFN,即拿到PFN(物理页面的基地址,即页面起始地址)
    —>
    物理地址的PFN作为页面基地址(即页面起始地址,也称页帧号) + 虚拟地址的VA(即页面内偏移量,也就是等价于PA) = 物理地址

    页表、页表项、页面间的概念与关系:
    页表:由所有页表项组成;
    页表项:一个页表项占用4个字节(因为是32位宽,所以这里一个页表项占4个字节),一个页表项里面存储了一个PFN(物理页面在内存中的帧号,也即物理页面的基地址);
    页面:这个是指物理页面,物理内存中把内存分成一个个页面,每个页面大小一样;

    我们来看看,4GB物理内存,页面大小4KB时,为了能映射整个4GB物理地址空间,需要多大的页表来存储VPN到PFN的映射关系呢?页表中总共有多少页表项呢?
    4GB(总的物理内存大小) / 4KB(每个页面大小) = 2^20 个页面;
    一个页面就有一个页面基地址- -PFN(页面的起始地址,也称页帧号),一个页表项存储一个PFN信息,那么就一共有2^20 个页表项;
    2^20(页表项个数) * 4Byte(一个页表项占空间大小,4字节) = 2^20 * 2^2 B= 4M;
    所以,页表大小4M,由2^20个页表项组成。而页表项地址如上面查询过程所提,一个页表项地址对应一个VPN索引值(偏移量),所以要表示 2^20个索引值(偏移量),需要20位,即VPN占虚拟地址的位数(31:12,20位)。

    展开全文
  • Linux内存管理系列,持续更新中...

    1.综述

    用来将虚拟地址空间映射到物理地址空间的数据结构称为页表,即页表用于建立用户进程的虚拟地址空间和系统物理内存(内存、页帧)之间的关联

    实现两个地址空间的关联最容易的方法是使用数组,对虚拟地址空间中的每一页,都分配一个数组项。该数组项指向与之关联的页帧, 但有一个问题,一般Linux体系结构使用4 KiB页,在虚拟地址空间为4 GiB的前提下,则需要包含100 万项的数组。在64位体系结构上,情况会更糟糕。每个进程都需要自身的页表,因此系统的所有内存 都要用来保存页表,也就是说这个方法是不切实际的。

    因为虚拟地址空间的大部分区域都没有使用,因而也没有关联到页帧,那么就可以使用功能相同 但内存用量少得多的模型:多级分页,多级分页模型如下(以三级页表为例):

    虚拟地址的第一部分称为全局页目录(Page Global Directory,PGD)。PGD用于索引进程中的一 个数组(每个进程有且仅有一个),该数组是所谓的全局页目录或PGD。PGD的数组项指向另一些数 组的起始地址,这些数组称为中间页目录(Page Middle Directory,PMD)。

    虚拟地址中的第二个部分称为PMD,在通过PGD中的数组项找到对应的PMD之后,则使用PMD 来索引PMD。PMD的数组项也是指针,指向下一级数组,称为页表或页目录。

    虚拟地址的第三个部分称为PTE(Page Table Entry,页表数组),用作页表的索引。虚拟内存页和 页帧之间的映射就此完成,因为页表的数组项是指向页帧的。

    虚拟地址最后的一部分称为偏移量。它指定了页内部的一个字节位置。归根结底,每个地址都指 向地址空间中唯一定义的某个字节。

    页表的一个特色在于,对虚拟地址空间中不需要的区域,不必创建中间页目录或页表。与前述使 用单个数组的方法相比,多级页表节省了大量内存。

    当然,该方法也有一个缺点。每次访问内存时,必须逐级访问多个数组才能将虚拟地址转换为物 理地址。CPU试图用下面两种方法加速该过程:

    (1) CPU中有一个专门的部分称为MMU(Memory Management Unit,内存管理单元),该单元优 化了内存访问操作。

    (2) 地址转换中出现最频繁的那些地址,保存到称为地址转换后备缓冲器(Translation Lookaside Buffer,TLB)的CPU高速缓存中。无需访问内存中的页表即可从高速缓存直接获得地址数据,因而 大大加速了地址转换。

    2.数据结构

    每个指针末端的几个比特位,用于指定所选页帧内部的位置。比特位的具体数目由PAGE_SHIFT 指定。

    PMD_SHIFT指定了页内偏移量和最后一级页表项所需比特位的总数。该值减去PAGE_SHIFT,可得 最后一级页表项索引所需比特位的数目。更重要的是下述事实:该值表明了一个中间层页表项管理的部分地址空间的大小,即2PMD_SHIFT字节。

     两级页表的体系结构会将PTRS_PER_PMD和PTRS_PER_PUD定义为1。这使得内核的剩 余部分感觉该体系结构也提供了四级页转换结构,尽管实际上只有两级页表:中间层页目 录和上层页目录实际上被消去了,因为其中只有一项

    n比特位长的地址字可寻址的地址区域长度为2n字节。内核定义了额外的宏变量保存计算得到的 值,以避免多次重复计算。相关的宏定义如下:

    #define PAGE_SIZE    (1UL << PAGE_SHIFT)
    #define PUD_SIZE     (1UL << PUD_SHIFT)
    #define PMD_SIZE     (1UL << PMD_SHIFT)
    #define PGDIR_SIZE   (1UL << PGDIR_SHIFT)
    include/asm-x86/pgtable_64.h 
    #define PGDIR SHIFT 39
     #define PTRS PER PGD 512
    
    #define PUD_SHIFT 30 
    #define PTRS PER PUD 512
    
    #define PMD SHIFT 21 
    #define PTRS PER PMD 512

    内核也需要一种方法从给定地址中提取各个分量。内核使用如下定义的位掩码来完成该工作:

    #define PAGE_MASK     (~(PAGE_SIZE-1)) 
    #define PUD_MASK      (~(PUD_SIZE-1)) 
    #define PMD_MASK      (~(PMD_SIZE-1)) 
    #define PGDIR_MASK    (~(PGDIR_SIZE-1))

    上述定义已经确立了页表项的数目,但没有定义其结构。内核提供了4个数据结构(定义在page.h 中)来表示页表项的结构:

    • pgd_t用于全局页目录项。
    • pud_t用于上层页目录项。
    • pmd_t用于中间页目录项。
    • pte_t用于直接页表项。
    include/asm-x86_64/page.h
    typedef struct { unsigned long pte; } pte_t; 
    typedef struct { unsigned long pmd; } pmdt; 
    typedef struct { unsigned long pud; } pud_t; 
    typedef struct { unsigned long pgd; } pgd_t;

    使用struct而不是基本类型,以确保页表项的内容只能由相关的辅助函数处理,而决不能直接访问

     3.特定于PTE的信息

    最后一级页表中的项不仅包含了指向页的内存位置的指针,还在上述的多余比特位包含了与页有 关的附加信息。尽管这些数据是特定于CPU的,它们至少提供了有关页访问控制的一些信息。

    • _PAGE_PRESENT指定了虚拟内存页是否存在于内存中。
    • CPU每次访问页时,会自动设置_PAGE_ACCESSED。内核会定期检查该比特位,以确认页使用 的活跃程度(不经常使用的页,比较适合于换出)。在读或写访问之后会设置该比特位。
    • _PAGE_DIRTY表示该页是否是“脏的”,即页的内容是否已经修改过。
    • _PAGE_FILE的数值与_PAGE_DIRTY相同,但用于不同的上下文,即页不在内存中的时候。显 然,不存在的页不可能是脏的,因此可以重新解释该比特位。如果没有设置,则该项指向一 个换出页的位置,如果该项属于非线性文件映射,则需要设置_PAGE_FILE
    • 如果设置了_PAGE_USER,则允许用户空间代码访问该页。否则只有内核才能访问(或CPU处 于系统状态的时候)。
    • _PAGE_READ、_PAGE_WRITE和_PAGE_EXECUTE指定了普通的用户进程是否允许读取、写入、 执行该页中的机器代码。内核内存中的页必须防止用户进程写入。
    • _PAGE_BIT_NX,用于将页标记为不可执行的

     这些函数经常3个1组,分别用于设置、删除、查询某个特定的属性(例如,页的写权限)

    4.页表项的创建和操作

    展开全文
  • linux内核源码分析之页表

    千次阅读 2022-07-23 20:26:27
    每个进程有独立的页表,进程的mm_struct实例的成员pgd指向全局目录,前面四级的页表项存放下一级页表的起始地址,直接地址的页表项存放帧号(PageFrameNumberPFN)。虚拟地址被分解为6部分全局目录索引,页四级目录...
  • 一,页表 帧表存放内存帧的使用情况,页表存放页号和帧号对应,其中页号是可以省略的,因为数组的下标可以作为页号 操作系统会为每一个进程创建一份页表,当一个进程分配到cpu运行时,调度程序会将页表加载...
  • Linux内核页表的建立

    2021-06-07 23:00:35
    Linux内核页表的临时映射背景初始阶段内存的使用情况 背景 由于Linux由BIOS加载后,起始阶段其实是运行在实模式,此时并没有开启分页机制。那Linux在开启分页机制之前需要先做哪些准备工作以支持分页机制?答案是页...
  • 初学内核时,经常被“内核页表”和“进程页表”搞晕,不知道这到底是个啥东东,跟我们平时理解的页表有什么关系。内核页表:即书上说的主内核页表,在内核中其实就是一段内存,存放在主内核页全局目录init_mm.pgd...
  • 页表作用 在使用虚拟地址空间的linux操作系统上,每一个进程都工作在一个4G的地址空间上,其中0~3G是应用进程可以访问的user地址空间,是这个进程独有的,其他进程看不到也无法操作这个地址空间;3G~4G是kernel地址...
  • 当内核从16位的实模式进入保护模式(通过在汇编代码中的setup函数中设置linux的cr0寄存器的PE位),内核要创建一个有限的地址空间,容纳内核的代码段、数据段、初始页表和用于存放动态数据结构的128KB大小的空间。...
  • 在看内核代码初始化arm页表的时候,发现linux内核做了一些特殊处理,引入了硬件页表、linux页表的概念,这篇文章描述为何需要这样处理以及具体的实现方式。 原因: linux中ARM32采用2级页表映射方式,MMU映射过程...
  • linux 进程页表流程

    2022-03-15 13:57:30
    pte的映射(写时复制机制) dup_mm()->dup_mmap 中完成中间页表pmd到页表pte的映射从而建立起页表,并将每一个pte页表,置为只读,以便激发起写时复制技术dup_mmap执行,继续复制pte页表项,使子进程的每个中间页表...
  • 在每一个进程中,每个...我们以32位linux的操作系统为例。 在32位的平台下,每个地址都是32个字节,那么就有2^32对映射关系。 假设我们只有一张页表是将虚拟地址和物理地址映射的,那么这张表就有2^32对页表项,..
  • Linux下的页表映射分为两种,一是Linux自身的页表映射,另一种是ARM32 MMU硬件的映射。 为什么会分两种;看一下什么是MMU; MMU是Memory Management Unit的缩写,中文名是内存管理单元,有时称作分页内存管理单元...
  • 文章目录1 页表1.1 统一的页表框架1.2 ARM64处理器的页表 重要:本系列文章内容摘自<...在Linux 4.11版本以前,Linux内核把页表分为4级: (1)页全局目录(Page Global Directory,PGD)。 (2)页上层目录
  • Linux 页表、大页与透明大页

    千次阅读 2019-09-15 23:08:17
    一、 大页 对于类Linux系统,CPU必须把虚拟地址转换程物理内存地址才能真正访问内存...这个映射表在Linux中每个进程都要持有一份,如果映射表太大,就会大大降低CPU的TLB命中率,主流的Linux操作系统,默认页的......
  • Linux内存管理中coreVM代码中,关于页表(pagetables)管理的代码是个重点,是虚拟内存(Virtual Memory,VM)的基石,本文探讨Linux页表实现及发展过程。页表概览在虚拟内存中,页表是个映射表的概念, 即从进程能理解...
  • 目录前言Intel四级页表实操寻址获取cr3获取PGD获取PUD获取PMD获取PTE获取内容最后前言Linux四级页表作用主要就是地址映射, 将逻辑地址映射到物理地址. 很多时候, 有些地方想不明白就可以查看实际物理地址进行分析....
  • 背景 Read the fucking source code!--By 鲁迅 ...要想理解好Linux页表映射,MMU的机制是需要去熟悉的,因此将这两个模块放到一起介绍。 关于ARMv8 MMU的相关内容,主要参考文档:《ARM Cortex-A S..
  • 摘要:操作系统在加载用户程序的时候,不仅仅需要分配物理内存,来存放程序的内容;而且还需要分配物理内存,用来保存程序的页目录和页表
  • 页表与MMU CPU访问的是什么地址(虚拟地址,物理地址)? 其实CPU根本不关心它访问的是什么地址,它只访问一个地址,然后从数据线上获取数据。 启用MMU时,CPU访问地址是向MMU发送地址,然后从MMU获得数据,虚拟...
  • 页表页表的存在很好的协调了物理内存和虚拟内存,从而很好的提高系统的安全性。在一个进程里面使用的内存可能是很少的一部分,但是存在的实际内存确实很大的,所以在使用页表的时候可能会映射很小的一部分...
  • Linux页表的实现与地址转换

    千次阅读 2020-12-09 15:03:17
    每个进程都拥有一个自己的页表,在linux中,有一个页目录数组,这是分页机制的最高层,每个进程的页表对应其中的一个页目录项,通过cr3寄存器(存放页目录项的物理地址)可以访问。一个进程的页表,对应的页表项中对应...
  • Linux是几级页表

    千次阅读 2019-06-19 16:02:02
    Linux是几级页表? 先看下系统的页表大小: root@hw2:~# getconf PAGE_SIZE ...linux页表 地址分配 可表示大小 x86 二级页表 10,10,12=32 4GB x86_amd 四级页表 9,9,9,9,12=48(物理地址) 256T...
  • Linux下使用c语言实现的内存管理模拟器 假设有台计算机物理内存大小为4MB,每个内存页大小为4k。采用分页机制,使用二级页表,第一级为页目录,共有1024项,每个页目录项指向一个页表,每个页表项指向一个实际的物理...
  • linux 临时页表

    2019-06-02 23:10:43
    armv8 linux4.9 查看dma map前后mmu page table的变化的时候,有看到有的page table entry映射了2M的size,这个2M的entry是何时建立的,目的是什么是这边博客要弄清楚的...linux 内核空间的页表是在kernel 初始化时...
  • Linux | 虚拟地址&页表

    2022-03-15 15:40:52
    Linux普遍为4级页表,分别是: PGD PUD PMD PTE l_________l_______l_______l_______l_______l____________l 0 11 12 20 21 29 30 38 39 47 48 63 PGD: page Global directory(47-39), 页全局目...
  • Linux内存之页表

    2021-05-10 16:09:33
    Linux的相关页表的框架在 vmalloc分析中,__vmalloc_area_node调用流程与分配页表项两节中已经有所阐述。本文再做一些总体上的把握。文中会借用其中的一些图。所谓的页表结构如下。针对现在的64位操作系统,Linux...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 36,605
精华内容 14,642
热门标签
关键字:

linux 页表的作用