Linux 0.11分页管理
分页,这个东西,首先就是实模式和保护模式的概念;然后就是就是逻辑地址、线性地址和物理地址的概念,之后就是页目录和页表的概念;
首先,实模式,这种模式下的寻址方式就是通常意义上的"段左移4bit然后叠加偏移量"作为最后地址进行寻址;且这种模式下兼容以前的处理器,只能寻址1M空间;
然后就是保护模式,保护模式的切换是修改寄存器CR0的PE标志位,将该标志位置一后即标志进入保护模式,就内存管理而言,进入保护模式模式之前需要设定好GDT段,因为进入保护膜模式后的段寄存器意义不再是一个段基地址,他的意义成为了段选择符,也就是GDT表的一个索引值(具体格式请百度),这种情况下,此时的程序中地址变为了逻辑地址,在寻址时,首先根据段寄存器中的段选择符找到GDT表或者IDT表中的段描述符,找到后从对应表中可以提取出整个段的段基址和段限长,其中还有权限等信息,可以根据CPL进行权限判定,根据段限长判定是否越界,起到保护作用;在获取到段基址后,再叠加偏移值即获得物理地址,然后获取对应地址数据;
保护模式之后如果想要开启分页功能,就要将CR0寄存器中的PG标志位置一,这时,上面保护模式通过逻辑地址计算得到的物理地址就要再经过页处理,此时该地址称为线性地址,线性地址会通过页目录、页表进行转换,其中页目录、页表需要手动指定;在二级页目录的概念下,线性地址分为三段,一页的空间为4096 byte,而一个空间地址占用4 byte数据,因此一页可以存储1024张页表地址的信息,即10bit数据,因此三段分别是前10bit,中间10bit以及最后的12bit数据, 此时的前面两个10bit数据作为一个索引值,第一个10bit数据从页目录中查找页表页所在位置,找到后再利用第二个10bit数据获取页表对应索引的页地址,最后的12bit数据则根据获取到的页作为索引值获取到需要的数据;
对于分页,他是通过中断进行控制的,当内存要访问一个线性空间地址,可能会触发两种页保护,一种就是缺页保护,一种就是没有写权限,前者是没有正常分配页表,后者会出现在fork的时候,fork之后,子进程复制到的页表是直接映射父进程页空间,是不具备写权限的,只有在要写的时候才会进行真正的另外页的复制,此时才具备写权限,这个机制被称为写时复制技术;
那么分页的中断是在init/main.c的trap_init()中设定的,设定中断后,当发生以上两种情况时,CPU会将CPU会将一个出错码入栈,只有3个bit有效数据,根据这个信息判断属于缺页还是写保护;另外还会将引起异常的线性段空间地址放置到寄存器cr2中;触发了缺页的话,就调用_do_no_page(),触发了写保护就调用_do_wp_page()程序;
do_no_page()程序,首先内核状态下的基地址为0x0,而linux 0.11操作系统实际管理16M物理空间,这样的话,也就是内核状态下可以访问整个16M物理空间,而对于linux中的任务n,他的使用线性空间为[64M * n, 64M * (n + 1));因此需要其他位置的页目录进行填充;对于缺页情况,分为两种情况,一种是在访问用户堆栈的时候发生缺页异常,此时需要申请新的页空间;另外一种是在访问任务的代码段或数据段,这种情况下如果有其他任务已经将对应i节点数据完成加载,那么在读取过程中无需再次加载,直接共享物理内存页面即可,共享页面之后,原本任务的页表也会丧失写属性,两个任务同时只具备读属性,如果需要写对应页数据的话,就涉及到了do_wp_page()中要讨论的;如果没有任务已经读取过数据,那么就要申请一页内存空间,然后将执行文件中的代码或数据段数据通过请求项将其读取到缓冲块(并不是直接操作缓冲块数据),然后将缓冲块数据复制到对应的物理内存上;然后将物理页地址设置到页表上,完成页数据的加载;
如果一个页面只具备读属性,而需要对该页面进行写操作,那么就会触发页故障,通过判断错误码调用do_wp_page(),该函数会将申请一张空闲页,然后将原本映射的物理页数据进行复制,接着将页表项设置为新申请到的页物理地址,然后赋予写权限,从而实现页表项的复制;此时页表项具备了写权限,可以对对应页数据执行写操作;
而在进行一个任务fork的时候,并不是直接复制父进程已有的页表,而是进行了一个共享页操作,这样可以使fork时间缩短,在需要数据写时进行复制页数据,这也就是写时复制技术;

浙公网安备 33010602011771号