首页运维零件 › 3)物理地址vns威尼斯城官网登入:(用于内存芯片级内存单元寻址,我们了解逻辑地址通过分段机制转换为线性地址的过程

3)物理地址vns威尼斯城官网登入:(用于内存芯片级内存单元寻址,我们了解逻辑地址通过分段机制转换为线性地址的过程

两级页表结构

两级表结构的第一级称为页目录,存储在一个4K字节的页面中。页目录表共有1K个表项,每个表项为4个字节,并指向第二级表。线性地址的最高10位(即位31~位32)用来产生第一级的索引,由索引得到的表项中,指定并选择了1K个二级表中的一个表。

两级表结构的第二级称为页表,也刚好存储在一个4K字节的页面中,包含1K个字节的表项,每个表项包含一个页的物理基地址。第二级页表由线性地址的中间10位(即位21~位12)进行索引,以获得包含页的物理地址的页表项,这个物理地址的高20位与线性地址的低12位形成了最后的物理地址,也就是页转化过程输出的物理地址。

vns威尼斯城官网登入 1

扩展分页

从奔腾处理器开始,Intel微处理器引进了扩展分页,它允许页的大小为4MB。
vns威尼斯城官网登入 2
在扩展分页的情况下,分页机制把32位线性地址分成两个域:最高10位的目录域和其余22位的偏移量。

  • 内存地址 
    1. 三种内存地址:1)逻辑地址(机器指令中操作数或指令的地址) 分段单元
      2)线性地址(虚拟地址)分页单元
      3)物理地址(用于内存芯片级内存单元寻址
    2. 多CPU时,共享同一内存,RAM芯片由独立的CPU并发访问;
      由内存仲裁器保证RAM的读写的串行执行

         

  • Linux中的分段 
    1. 80X86才使用分段(把程序划分为逻辑相关的实体),Linux更喜欢使用分页(当所有进程使用相同的段Register值时,它们共享同样的一组线性地址,这样内存管理简单;
      RISC对分段的支持有限.).
    2. 两者都划分进程的物理地址空间:分段可以给每一个进程分配不同的线性地址空间,而分页可以把同一线性地址空间映射到不同的物理空间.
    3. 段选择符由宏__USER/KERNEL_CS/DS定义.对内核代码寻址,吧__KERNEL_CS宏产生的值装入cs寄存器即可.这样执行指令时,只需指定逻辑地址的偏移部分,段选择符已经隐含在寄存器内.
    4. 所有段(内核/用户的数据/代码段)的Base=oX0000000,即逻辑地址(的偏移量字段值)=线性地址(的值).所有进程使用相同的逻辑地址.
    5. 每个CPU一个GDT,会插入未使用的项使得经常一起访问的描述符能够处于同一32字节的硬件Cache中;
      大多数用户态APP不适用局部描述符表,所以定义了一个缺省的LDT供进程共享,同时进程可以创建自己的LDT.

         

  • 硬件中的分页 
    1. 分页单元的一个关键任务就是把所请求的访问类型与现行地址的访问权限相比较,如果访问无效,产生一个缺页异常.
    2. 页:
      线性地址被分为固定长为单位的页,页内连续的线性地址被映射到连续的物理地址中,内核仅需为页指定物理地址和存取权限,提高了效率.
    3. 页框:
      相应地,把RAM也分为固定长度的页框(物理页),一个页框包含一个页(长度一致).
    4. 页表: 把线性地址映射到物理地址的数据结构.存放于主存中
    5. CPU通过设置cr0寄存器的PG=0标志启动线性地址到物理地址的翻译
    6. 常规分页 
      1. 页Size=4KB.从而32位的线性地址= Directory(10b:目录)+
        Table(10b:页表)+ Offset(低12b:偏移量)
      2. 转换分为两步,每一步基于一种转换表.使用二级模式为了减少每个进程页所需的RAM的数量.二级模式只为进程实际使用的那些虚拟内存区请求页表来减少内存容量.
      3. 每个活动进程都有一个页目录,但是仅在进程实际需要一个页表时才给该页表分配RAM.
    7. 扩展分页:
      页框Size=4MB.用于把大段连续的线性地址转换成相应的物理地址.不适用中间页表.32b线性地址=Directory(10)+Offset(22)
    8. 64位系统中的分页:
      使用了额外的分页基本.不然的话每个进程的页目录和页表含有的表项太多.
    9. 物理地址扩展(PAE)分页机制 
      1. CPU所支持的RAM容量受地址总线上的管脚数限制,将该数从32改为36即可为2(36)=64GB.
      2. PAE机制:1)64GB的RAM分为2(24)个页框,页表项的物理地址字段从20变为24位.所以PAE页表项=12个标志位+24个物理地址位=36,页表项Size=64(>32).所以一个4KB的页表包含512个页表而非1024个.
      3. 2)引入页目录指针表(PDPT)的级别,由4个64位表项组成.
      4. 3)cr3控制寄存器含有一个27位的PDPT的基地址,因PDPT在RAM的前4GB中,并在32字节的倍数上对其,所以27可以表示其基地址
      5. 解释32位线性地址(4KB/2MB的页):1)cr3指向PDPT;
        2)31-30指向PDPT中4个项之一;
        3)29-21指向页目录中512个项中的一个;
        4)20-12指向页表512项之一+11-0为4KB页内Offset/20-0为2MB页中Offset.
      6. PAE的主要问题是线性地址仍是32位.并没有扩大进程的线性地址空间,而只处理物理地址.内核编程人员必须用同一线性地址映射不同的RAM区.
      7. 由于只有内核才能修改进程的页表,所以用户态下的进程不能使用>4GB的物理地址;
        但内核能够使用更多的物理地址从而增加了系统中的进程数量.
    10. 硬件Cache 
      1. 由行(几十个连续的字节)组成.其位于分页单元和主内存之间.由硬件Cache内存(真正的行)+Cache控制器(表项数据.一个表项(标签(辨别行映射的内存单元)+标志)对应一个行)组成
      2. 类型:
        直接映射/充分关联/N-路组关联,来将主存中的一个行,关联到Cache中的一个行.
      3. 写策略:通写(同时写RAM和行,一般为了效率,关闭Cache;
        回写:只写Cache的行,当CPU没有命中时,Cache的行被写回到内存中.
      4. 一个CPU一个Cache,需要使用高速缓存侦听(snooping)来同步他们的内容.
      5. Pentium
        CPU可以为每一个页框指定Cache策略,Linux忽略之,使得所有的页框都启用Cache,且回写策略.
    11. 转换后援缓冲器(TLB):
      加速线性地址的转化.物理地址经过一次转化后放于TLB表项内.其每个CPU都有一个,但是不必同步,因为运行在现有CPU上的进程可以使同一线性地址于不同的物理地址关联.

         

  • Linux中的分页 
    1. 组成:页全局目录+页上级目录+页中间目录+页表.
      并在32位系统中将页中间目录和页上级目标级别的位设置为0来对应.
    2. 物理内存布局:
      Linux内核安装于RAM的第二个MB开始.原因:页框0由BIOS使用;
      640KB~1MB为洞(物理地址被保留但不能被使用).布局=内核代码+已初始化过的Data+未初始化过的Data.
    3. 进程页表:线性地址空间,用户态 < oXc000000, 内核态 >
      oXc0000000.
    4. 内核页表
二级页表

现代操作系统都是2级页表,那为什么需要二级页表呢?

  • 一级页表最多可以容纳个1M(4G/4K)个页表项,每个页表项4字节,如果全部占满的话需要4M。
  • 一级页表中所有页表项必须提前建好,原因是操作系统要占用4GB虚拟地址空间中的高1GB,用户进程占用低3GB。
  • 每个进程都有自己的页表,进程一多,页表占用的空间就很大了。

归根结底,我们需要:不要一次性的将全部页表项建好,需要时动态创建页表项。

二级页表则很好的解决了这个问题。

无论几级页表,标准页尺寸都是4KB,所以4G内存最多有1M个页标准页,一级页表是将标准页放置到一张页表中。

二级页表是将1M个标准页平均放置到1024个页表中,每个页表包含1024个页表项,一个页表占用大小1024*4=4K,刚好是一个标准页的大小。

然后再用一个页目录表存储这些页表。每个页表的物理地址以页目录项(Page
Directory
Entry,PDE)的形式存储。1024个目录项,每个4字节,刚好也是4K,即一个标准页的大小。

vns威尼斯城官网登入 3

二级页表.png

前面一级页表的方法是将虚拟地址拆分成2部分,高20位虚拟地址用于定位一个物理页起始地址,低12位用于表示偏移地址。

在二级页表中,现在它们是这样的:
定位一个物理页,需要先找到其所在的页表,页目录中有1024个页目录表项,只需要10位二进制位就够了。然后再从页表中定位物理页,因为一个页表有1024个页表项,同样只需要10位二进制就够了。余下的12位仍然表示物理页内的偏移。

所以我们规定,32位虚拟地址中,我们用高10位(31-22)用于在页目录中定位页表,中间10位(21-12)用于在也表中定位物理页,低12位(11-0)用于表示物理页内偏移。

vns威尼斯城官网登入 4

二级页表地址翻译.png

为什么使用两级页表

假设每个进程都占用了4G的线性地址空间,页表共含1M个表项,每个表项占4个字节,那么每个进程的页表要占据4M的内存空间。为了节省页表占用的空间,我们使用两级页表。每个进程都会被分配一个页目录,但是只有被实际使用页表才会被分配到内存里面。一级页表需要一次分配所有页表空间,两级页表则可以在需要的时候再分配页表空间。

段描述符

所谓描述符(Descriptor),就是描述段的属性的一个8字节存储单元。在实模式下,段的属性不外乎是代码段、堆栈段、数据段、段的起始地址、段的长度等等,而在保护模式下则复杂一些。IA32将它们结合在一起用一个8字节的数表示,称为描述符

vns威尼斯城官网登入 5
           IA32的一个通用的段描述符的结构

从图可以看出,一个段描述符指出了段的32位基地址和20位段界限(即段长)。这里我们只关注基地址和段界限,其他的属性略过。

一级页表

分页机制其实是建立在分段机制上的。这是因为内存分段是属于Intel
IA32架构骨子里的东西。

尽管在保护模式中段寄存器中的内容已经是选择子了
,但选择子最终就是为了找到段基址,其内存访问的核心仍是"段基址:段内偏移地址",这2个地址相加后之后得到的是绝对地址,此地址在分段机制下被CPU认为是物理地址,是可以直接拿去地址总线上用。

vns威尼斯城官网登入 6

内存分段机制下的内存访问.png

但是如果CPU打开了分页机制,段部件输出的线性地址就不再等同于物理地址了,将其称之为虚拟地址。此虚拟地址对应的物理地址需要在页表中查找,这项工作是由页部件完成的。

为了搞清页部件的工作原理,我们需要明白:

  • 分页机制的思想
    分页机制的思想是:通过映射,可以使连续的线性地址与任意物理地址相关联,逻辑上连续的线性地址其对应的物理地址可以是不连续的。

vns威尼斯城官网登入 7

image.png

  • 页表的结构

    在内存中,最简单的映射是逐字节映射,即一个虚拟地址对应一个物理地址,若线性地址0x1对应0x10,我们需要一个结构来存储这种关系,这个就是页表,页表中的每一项称为页表项

    如果采取逐字节映射,显然是不可能的。4GB内存最大地址32位,则每一个页表项需要4字节,一共4G,则需要4G个页表项,那么页表就需要4G*4Byte
    = 16G,显然是不可能的。

    所以我们需要减少页表项的数目,增大内存块尺寸。

    vns威尼斯城官网登入 8

    页尺寸.png

所以现在,假设内存块尺寸为4K,则4G/4K = 2^20,

vns威尼斯城官网登入 9

页表和页表项.png

所以现在我们需要解决的是第2个问题,因为任一地址最终都会落到某一个物理页中,但我们还需要一个偏移地址才能访问到一个物理页的每一个字节。所以如"图页尺寸",右边12位就可以拿来作偏移量。

段部件出来的32位地址,现在怎么从中得出足够信息将其翻译。

分页机制打开前,要将页表地址加载到控制寄存器CR3上。

一个页表对应一个页,所以虚拟地址的高20位作为页表项的索引,每个页表项占4位。我们将其乘以4就是该页表项相对于页表物理地址的偏移量。

这个偏移量加上页表地址,就得到了我们想要的物理页地址,再用虚拟地址的低12位作为偏移量加上物理页地址,就得到了真实的物理地址。

vns威尼斯城官网登入 10

一级页表地址翻译.png

扩展分页

从奔腾处理器开始,Intel微处理器引进了扩展分页,它允许页的大小为4MB。

vns威尼斯城官网登入 11

在扩展分页的情况下,分页机制把32位线性地址分成两个域:最高10位的目录域和其余22位的偏移量。

IA32的段寄存器

IA32中有六个16位段寄存器:CS, DS, SS, ES,FS,
GS.跟8086的段寄存器不同的是,这些寄存器存放的不再是某个段的基地址,而是某个段的选择符(Selector)。

页表项 和 页目录项

vns威尼斯城官网登入 12

image.png

  • 页表物理地址
    32位地址应该用32位来表示,为什么这里会用20位?因为标准物理页大小是4K,所以最低12位总是0,所以用20位就够了。

  • AVL

  • D 位
    Dirty,意为脏页位,当 CPU
    对一个页面执行写操作时,就会设置对应页表项的 D 位为1

  • A 位
    Accessed,意为访问位,为1表示该页已经被CPU访问过了。

  • PCD 位
    Page-level Cache
    Disable,页级告诉缓存禁止位,若为1表示该页启用高速缓存。为0
    表示禁用高速缓存。

  • PWD 位

  • US 位
    User/Supervisor
    ,若为1表示处于User级,0,1,2,3特权级的程序都可以访问该页。若为0表示处于Supervisor级,特权级为3不允许访问该页。

  • G 位

  • RW 位
    1表示可读可写,为0表示可读不可写。

  • P 位
    P,Present,存在位,为1表示该页存在于物理内存中。

总结

这里我们不讨论代码实现,只关注原理。从上面的讨论可以看到分页机制主要依赖硬件的实现。Linux采用的四级页表只是为了最大化兼容不同的硬件实现,单就IA32架构的CPU来说,就有多种分页实现,常规分页机制,PAE机制等。

我们虽然讨论的是Linux的分页机制,实际上我们用了大部分篇幅来讨论Intel
CPU的分页机制实现。因为Linux的分页机制是建立在硬件基础之上的,不同的平台需要有不同的实现。Linux在软件层面构造的虚拟地址,最终还是要通过MMU转换为物理地址,也就是说,不管Linux的分页机制是怎样实现的,CPU只按照它的分页实现来解读线性地址,所以Linux传给CPU的线性地址必然是满足硬件实现的。例如说:Linux在32位CPU上,它的四级页表结构就会兼容到硬件的两级页表结构。可见,Linux在软件层面上做了一层抽象,用四级页表的方式兼容32位和64位CPU内存寻址的不同硬件实现。

总结

分段机制是IA32架构CPU的特色,并不是操作系统寻址方式的必然选择。Linux为了跨平台,巧妙的绕开段机制,主要使用分页机制来寻址。

参考资料
《深入分析Linux内核源码》

在上一篇文章Linux内存寻址之分段机制中,我们了解逻辑地址通过分段机制转换为线性地址的过程。下面,我们就来看看更加重要和复杂的分页机制。

分页机制在段机制之后进行,以完成线性—物理地址的转换过程。段机制把逻辑地址转换为线性地址,分页机制进一步把该线性地址再转换为物理地址。

为什么需要分页?

vns威尼斯城官网登入 13

分段式进程运行.png

最开始进程A,进程B,进程C已经被加载到了内存中。然后这时,进程B运行完毕,腾出20M可用内存,需要运行进程D,却发现,内存中没有足够大的内存空间可以加载进程D。这时,有
2 种方案:

  • 等待进程C运行完毕,这样就有足够连续的内存加载进程D。
  • 将进程A 的段 A3 或进程 C 的段 C1 换出到硬盘上,腾出一部分空间。

第一个方案比较简单直接,但是需要等待。
第二个方案虽然解决了内存不足的问题,但也有缺陷,假如,极端情况下,物理内存特别小,无法容纳任何一个进程段,就没法运行进程,也没法做到段的换入换出的问题。

所以问题的本质是什么?
在目前只分段的情况下,CPU认为线性地址等于物理地址,而线性地址是由编译器编译出来的,他本身是连续的,所以物理地址也必须要连续才可以。
我们需要做的就是打破线性地址和物理地址一一对应的关系,然后将它们建立一种映射关系,可以将线性地址映射到任意物理地址。

硬件中的分页

分页机制由CR0中的PG位启用。如PG=1,启用分页机制,并使用本节要描述的机制,把线性地址转换为物理地址。如PG=0,禁用分页机制,直接把段机制产生的线性地址当作物理地址使用。分页机制管理的对象是固定大小的存储块,称之为页(page)。分页机制把整个线性地址空间及整个物理地址空间都看成由页组成,在线性地址空间中的任何一页,可以映射为物理地址空间中的任何一页(我们把物理空间中的一页叫做一个页面或页框(page
frame))

vns威尼斯城官网登入 14

80386使用4K字节大小的页。每一页都有4K字节长,并在4K字节的边界上对齐,即每一页的起始地址都能被4K整除。因此,80386把4G字节的线性地址空间,划分为1G个页面,每页有4K字节大小。分页机制通过把线性地址空间中的页,重新定位到物理地址空间来进行管理,因为每个页面的整个4K字节作为一个单位进行映射,并且每个页面都对齐4K字节的边界,因此,线性地址的低12位经过分页机制直接地作为物理地址的低12位使用。

前言

本文涉及的硬件平台是X86,如果是其他平台的话,如ARM,是会使用到MMU,但是没有使用到分段机制;
最近在学习Linux内核,读到《深入理解Linux内核》的内存寻址一章。原本以为自己对分段分页机制已经理解了,结果发现其实是一知半解。于是,查找了很多资料,最终理顺了内存寻址的知识。现在把我的理解记录下来,希望对内核学习者有一定帮助,也希望大家指出错误之处。

启用分页机制

启用分页机制,我们需要做3件事:

  • 准备好页目录表和页表
  • 将页表地址写入控制寄存器CR3
  • 寄存器CR0的PG位置1

在规划页目录表和页表时,我们得首先明白操作系统和用户进程的关系,用户进程运行在低特权级,用户进程需要访问硬件相关资源时,必须要向操作系统申请,之后将结果返回给操作系统,用户进程有很多,操作系统1个,怎么实现把操作系统共享给用户进程

vns威尼斯城官网登入 15

每个进程都有自己的虚拟空间.png

这个很简单,把操作系统属于用户进程的虚拟地址空间就可以了。把虚拟地址空间的高1G部分划分给操作系统,
0 - 3GB是用户进程自己虚拟地址空间。

; 准备好页目录表和页表
call setup_page
......
; 把页目录地址赋值给cr3
    mov eax, PAGE_DIR_TABLE_POS
    mov cr3, eax

; 打开CR0的PG位
    mov eax, cr0
    or eax, 0x80000000
    mov cr0, eax
.....
;----------------------------------------------------------
; setup_page
; 功能: 创建页目录和页表
; 参数: 无
;----------------------------------------------------------
setup_page:
    ; 先把页目录占用的空间逐字节清0
    mov ecx, 4096
    mov esi, 0
    .clear_page_dir:
        mov byte [PAGE_DIR_TABLE_POS + esi], 0
        inc esi
        loop .clear_page_dir

    ; 开始创建页目录项
    .create_pde:
    mov eax, PAGE_DIR_TABLE_POS
    add eax, 0x1000     ; eax = 0x100000 + 0x1000 = 0x00101000 
    mov ebx, eax        ; ebx = 0x00101000

    or eax, PG_US_U | PG_RW_W | PG_P     ; eax = 0x00101007
    mov [PAGE_DIR_TABLE_POS + 0x0], eax  ;       
    mov [PAGE_DIR_TABLE_POS + 0xc00], eax

    sub eax, 0x1000
    mov [PAGE_DIR_TABLE_POS + 4092], eax

    ; 下面创建页表项
    mov ecx, 256        ; 1M/4K=256
    mov esi, 0
    mov edx, PG_US_U | PG_RW_W | PG_P   ; edx=0x00000007

    .create_pte:
        mov [ebx + esi*4], edx

        add edx, 4096
        inc esi
        loop .create_pte

    ; 创建内核(高1G, 768-1023)页目录项
    mov eax, PAGE_DIR_TABLE_POS
    add eax, 0x2000     ; eax = 0x00100000 + 0x2000 = 0x00102000
    or eax, PG_US_U | PG_RW_W | PG_P       ; eax = 0x00102007

    mov ebx, PAGE_DIR_TABLE_POS     ; ebx = 0x00100000
    mov ecx, 254
    mov esi, 769
    .create_kernel_pde:
        mov [ebx+esi*4], eax
        inc esi, 
        add eax, 0x1000
        loop .create_kernel_pde

    ret

vns威尼斯城官网登入 16

页目录和页表.png

Linux中的分页机制

Linux使用了一个适合32位和64位系统的分页机制。

vns威尼斯城官网登入 17Linux分页模型

  • 页全局目录
  • 页顶级目录
  • 页中间目录
  • 页表

页全局目录包含若干页上级目录的地址,页上级目录又依次包含若干页中间目录的地址,而页中间目录又包含若干页表的地址。每一个页表项指向一个页框。线性地址因此被分成五个部分。图中没有显示位数,因为每一部分的大小与具体的计算机体系结构有关。

对于没有启用物理地址扩展的32位系统,两级页表已经足够了。从本质上说Linux通过使“页上级目录”位和“页中间目录”位全为0,彻底取消了页上级目录和页中间目录字段。不过,页上级目录和页中间目录在指针序列中的位置被保留,以便同样的代码在32位系统和64位系统下都能使用。内核为页上级目录和页中间目录保留了一个位置,这是通过把它们的页目录项数设置为1,并把这两个目录项映射到页全局目录的一个合适的目录项而实现的。

启用了物理地址扩展的32 位系统使用了三级页表。Linux的页全局目录对应80×86
的页目录指针表(PDPT),取消了页上级目录,页中间目录对应80×86的页目录,Linux的页表对应80×86的页表。

最后,64位系统使用三级还是四级分页取决于硬件对线性地址的位的划分。

Linux中的分页机制

Linux使用了一个适合32位和64位系统的分页机制。
vns威尼斯城官网登入 18

  • 页全局目录
  • 页顶级目录
  • 页中间目录
  • 页表

页全局目录包含若干页上级目录的地址,页上级目录又依次包含若干页中间目录的地址,而页中间目录又包含若干页表的地址。每一个页表项指向一个页框。线性地址因此被分成五个部分。图中没有显示位数,因为每一部分的大小与具体的计算机体系结构有关。

对于没有启用物理地址扩展的32位系统,两级页表已经足够了。从本质上说Linux通过使“页上级目录”位和“页中间目录”位全为0,彻底取消了页上级目录和页中间目录字段。不过,页上级目录和页中间目录在指针序列中的位置被保留,以便同样的代码在32位系统和64位系统下都能使用。内核为页上级目录和页中间目录保留了一个位置,这是通过把它们的页目录项数设置为1,并把这两个目录项映射到页全局目录的一个合适的目录项而实现的。

启用了物理地址扩展的32 位系统使用了三级页表。Linux的页全局目录对应80×86
的页目录指针表(PDPT),取消了页上级目录,页中间目录对应80×86的页目录,Linux的页表对应80×86的页表。

最后,64位系统使用三级还是四级分页取决于硬件对线性地址的位的划分。

页面高速缓存

vns威尼斯城官网登入 19

由于在分页情况下,每次存储器访问都要存取两级页表,这就大大降低了访问速度。所以,为了提高速度,在386中设置一个最近存取页面的高速缓存硬件机制,它自动保持32项处理器最近使用的页面地址,因此,可以覆盖128K字节的存储器地址。当进行存储器访问时,先检查要访问的页面是否在高速缓存中,如果在,就不必经过两级访问了,如果不在,再进行两级访问。平均来说,页面高速缓存大约有98%的命中率,也就是说每次访问存储器时,只有2%的情况必须访问两级分页机构。这就大大加快了速度。

线性地址到物理地址的转换

  1. CR3包含着页目录的起始地址,用32位线性地址的最高10位A31~A22作为页目录的页目录项的索引,将它乘以4,与CR3中的页目录的起始地址相加,形成相应页表的地址。
  2. 从指定的地址中取出32位页目录项,它的低12位为0,这32位是页表的起始地址。用32位线性地址中的A21~A12位作为页表中的页面的索引,将它乘以4,与页表的起始地址相加,形成32位页面地址。
  3. 将A11~A0作为相对于页面地址的偏移量,与32位页面地址相加,形成32位物理地址。

转载本站文章请注明出处:vns威尼斯城官网登入 http://www.tiec-ccpittj.com/?p=4902

上一篇:

下一篇:

相关文章