Linux设备驱动程序——内存和I/O基础知识学习:(1)内核中相关基础知识学习

来源:互联网 发布:为什么mac没有flash 编辑:程序博客网 时间:2024/04/28 12:06

这部分的内容来源于《Linux内核完全剖析》这里面主要牵涉的是内核中的东西,之所以花了很长时间来学习这方面的东西,是因为前一段在看和网络设备驱动相关的代码和资料的过程中,牵涉到了很多的内存相关的知识点,而对着方面的东西又不懂,搞的很头疼。而内存的分配和管理又是非常庞大和复杂的一块,所以痛下决心把这一块的知识好好学习一下,这样后面的学习过程也会变得比较容易由于是自己打出来的,有些是按照自己理解写出来的,中间可能有些错误,希望给予指正


     内存是一组有序字节组成的数组,每个字节有着唯一的内存地址。对于80X86CP来说,总线的宽有32位,因而会有2^32个不同的物理地址,即内存的物理地址空间有4G

X86中使用了一种段的寻址技术,这种技术把内存空间分成一个或者多个称为段的线性区域,从而对内存中的一个数据对象中的就需要一个段的起始地址(即段地址)和一个段的偏移地址来组成,其中段地址16位,用来标识不同的段的有14位,所以总共2^14=16384个段。段内的地址使用32位的值来确定,所以段内的地址是0~4G,最大可以达到4G。总共的48位地址称为逻辑地址(也叫虚拟地址)。

X86中为段部分提供了6个存放段选择符的段寄存器:CSDSESSSFSGS,其中CS总是用来寻址代码段,SS用来寻址堆栈段,在任何时候CS寻址的称为当前代码段,而EIP寄存器中则包含了当前代码段中下一条执行指令的段内偏移地址,即CS[EIP]

     段寄存器中的SS寻址的段称为当前的堆栈段,栈顶由ESP急窜起内容来指定。SS:[ESP],l另外的是四个寄存器是通用寄存器。默认情况下DS是数据段寄存器。


地址变换

内存管理系统包括两个关键的部分:保护和地址变换。保护作用可以防止一个任务访问另外一个任务的内存区域。如同前面所说逻辑地址有48位,但是并不能由这个地址直接访问物理内存,需要经过地址变换将之映射到物理内存上面。

     X86中这个映射过程有两个:分段和分页两种机制。前一项是必须有的,后面一项是可以选择的。下面图片显示了由逻辑地址到物理地址的详细过程。


综述:

1、分段机制

     分段机制提供了隔绝各个代码、数据和堆栈区域的机制,使得多个程序(任务)运行在同一台机器上面相互之间不受干扰。分页为传统需求页和虚拟内存提供了实现机制。



 

     如图所示,分段把处理器可以寻址的线性地址空间划分成为一些小的受保护的地址空间区域。一个系统中所有使用到的段都在线性地址空间内。

     逻辑地址包括一个段选择符和一个偏移量。

2分页机制

      因为多任务系统通常定义的线性地址空间都比其含有的物理内存容量大很多,所以需要某种“虚拟化”线性地址空间,使用虚拟存储技术,给编程人员造成一种内存空间比计算机中的实际的物理内存大的多的假象。利用这种错觉我们就可以随意编制大型程序而不用考虑实际的物理内存究竟有多大。

      分页机制支持虚拟存储技术,在虚拟存储的环境中,大容量的线性地址空间需要用小块的物理内存(RAM或者ROM)以及某些外部存储空间来模拟。当使用分页的时候每个段被划分成页面(通常为4KB,页面存在于物理内存或者硬盘上面,操作系统通过维护一个页目录和页表来留意这些页面,当程序想要访问线性地址空间中的一个位置的时候,处理器就会使用页目录和页表把线性地址转换成为一个物理地址,从而执行相关的操作。

      如果当前访问的页面不在物理内存中,处理器会中断程序的执行(产生一个错误异常),之后操作系统会把页面从硬盘上面读入内存中,并且继续执行被中断的程序。

    分段和分页是两种不同的地址变换机制,尽管两种机制都私用了存储在内存中的变换表,但是两种表的结构不同。段存储表存在于线性地址空间,而页表存在于物理地址空间


详细学习分段和分页相关知识:

分段机制

在X86中,虚拟地址由一个段部分和一个偏移部分组成,每个段有三个参数来组成:

1、     段基地址:这个地址用来指定段在线性地址空间中的开始的地址。这是一个线性地址

2、     段限长:是虚拟地址空间中段内最大的可用屁啊你位置,它定义段的长度。:

3、     段属性:指定段的特性。是否可读、可写

需要注意的一点是:多个段映射到线性地址中的范围是可以重复的,如下图所示:



段的基地址、段限长以及段的保护属性放在一个段描述符结构项中。段描述符保存在内存中断描述符表中,可以认为段描述符表是包含段描述符的一个简单的数组。处理器会将逻辑地址转换成为线性地址。线性地址和物理地址类似有着平坦的4G的地址空间。

为了把一个逻辑地址转换成为一个线性地址,处理器需要执行下面的操作:

1、使用段选择符中的恶偏移值(段索引)在GDT和LDT(这个不详细说,可以参考课本上相关的内容)

2、利用段描述符检验段的访问权限和范围

3、将段描述符中去的的基地址加到偏移量上面,最后形成线性地址。

如下图所示:



分页机制

分页是X86中的第二部分,分页用来把线性地址转换成为物理地址。处理器分页机制会将线性地址划分成页面,然后将这些线性地址页面可以被映射到物理地址中任何页面上。


X86使用2^12字节固定大小的页面,每个页面的大小都是4KB,也就是说我们把4G(2^32)的线性地址空间划分成为了2^20个页面。由于4K大小的页面作为一个单元进行映射,并且对其与4K边界。

因此线性地址的低12比特位可以作为页内偏移量直接作为物理地址的低12位,分页机制执行的重新定位的功能可以看成是把线性地址的高20位转换成物理地址的高20位。

此外,线性地址到物理地址的转换功能还可以是无效的,不产生一个物理地址,分为两种情况:操作系统不支持线性地址或者页面是在磁盘上面的。在X86中云寻线性地址直接映射到内存上或者是硬盘上面,后面一种情况称为虚拟内存。

页表结构

分页转换功能由驻留在内存中的表来描述,这个表称为页表,放在物理空间中。页表可以看做是简单的2^20的物理地址数组。

两级的页表结构,为了减少内存占用量,使用了两级列表。

第一级称为页目录。被存放在一页4k的页面中,具有1k个4字节长度的表项。线性地址的最高10位(31-22)作为一级表(页目录)表项索引值来选择2^10个二级表之一。

第二级的表称为页表,长度也是一个页面,最多含有1k个4字节的表项。每个四字节的表项含有相关页面的20位物理地址,和线性地址中的低12位地址放到一块儿就会产生转换过程中最后的地址结果,也就是最后的物理地址。

具体的过程如下图所示:




内存的分页管理机制的基本原理是将CPU的整个线性内存区域分配成4096个字节的为1页的内存页面。当程序申请试用内存的时候,系统就以页为单位进行分配。在使用内存分页机制的时候,每个执行重大额任务(进程)可以使用比实际内存大得多的连续地址空间。

在目前的LInux系统中内核和所有的任务共同使用一个页目录表,因此任何时候处理器线性地址到物理地址的映射函数都一样,为了使内核中的任务之间不再相互干扰,那么从虚拟空间到线性空间映射出来的结果必须不同。

对于80386系统来说,其中的CPU最多提供4G的线性地址空间。下面着色的这部分没有看明白:在LInux中,每个最大进程定义成64MB,因此每个进程的逻辑地址加上(任务号)*64MB,就可以转换成为线性空间中的地址。