Linux2.6.32内核笔记(2)内存管理子系统

    不得不说自己对这一块一直搞得不是很清楚,内存,物理地址,虚拟地址,时间一长总是容易忘记,写博客确实是个好习惯,希望自己可以坚持下去,很多东西自己敲一遍理解一遍做一遍跟看一遍听一遍区别非常大。下面进入正题吧。


一、Linux各子系统认识

    Linux可以说由七个子系统组成,分别为:System Call Interface(SCI系统调用接口)、Processmanagemant(进程管理子系统)、MemoryManagemant(内存管理子系统)、VFS(虚拟文件系统)、Network Stack(网络堆栈)、Arch(体系架构相关代码)、Device Driver(设备驱动)。

    这里做一个简单的总结,大致知道每个子系统是干嘛的:

    SCI:用户通过软件中断,调用系统内核提供的功能,这样在内核提供的服务和用户空间之间接口称为系统调用。由于系统调用是在内核里,用户空间是无法使用的,内核提供了一些列的接口函数用来供用户调用,例如system_call。

    PM进程管理子系统:在Linux上,所有的工作都是通过进程来实现的,Linux提供了一系列的进程操作库函数,用户可以进行很多动态地操作,例如创建,kill等。在用户空间,进程是由进程标示符(PID)表示的。从用户角度看,一个PID是一个数字值,可以唯一标识一个进程,一个PID值在进程的整个生命周期中不会更改,但是PID可以在进程销毁后被重新使用。在linux内核空间,每个进程都有一个独立的数据结构,用来保存该进程的ID、优先级、地址的空间等信息,这个结构也被称做进程控制块(Process Control Block)。所谓的进程管理就是对进程控制块的管理。当我们fork一个进程之后,进程会进入就绪态,被加入到就绪态队列,当轮到它的cpu时间片时,切换到进程的代码,进程被执行,当进程的时间片到了以后被换出。如果进程发生I/O操作也会被提前被换出,并且存放到等待队列,当I/O请求返回后,进程又被放入就绪队列。

    VFS(虚拟文件系统):在Linux中,一切皆文件,但是我们有ext2,vfat,还有字符设备,网络套接字,块设备这些,对应用程序来说,并不知道文件系统的类型,甚至不知道文件的类型,这就是虚拟文件系统在背后做的工作。虚拟文件系统屏蔽了不同文件系统间的差异,向用户提供了统一的接口。通过使用同一套文件 I/O 系统调用即可对Linux中的任意文件进行操作而无需考虑其所在的具体文件系统格式;更进一步,文件操作可以在不同文件系统之间进行。

    网络堆栈:Linux具有强大的网络功能,就是因为网络堆栈代码的设计思想。网络数据从用户进程到达实际的网络设备需要经过四个层次,分别是:用户进程,套接字,网络协议,网络设备。linux网络子系统对网络层次采用了类似面向对象的设计思路,把需要处理的层次抽象为不同的实体,并且定义了实体之间的关系和数据处理流程。主要是四个实体:网络协议,套接字,设备接口和网络缓冲区。

    Arch: Linux内核支持众多体系结构,内核把与设备无关的代码放在arch目录,对应的头文件放在include/asm-<体系名称>目录下。这样的划分代码结构清晰,同时提高了代码的复用率。在arch目录里,每个子目录对应一种体系结构,存放这种体系结构对应的代码。还记得我们之前把内核移植到mini2440的时候,就是修改arch/arm目录下的mach-mini2440.c文件吧。

    设备驱动:Linux提供了具体硬件无关的设备驱动接口,这样的好处是屏蔽了具体设备的操作细节,用户通过操作系统提供的接口就可以访问设备,而具体设备的操作细节由设备驱动完成,驱动程序开发人员只需要向操作系统提供相应接口即可。分为字符设备,块设备和网络设备。Linux内核对设备按照主设备号和从设备号的方法访问,主设备号描述控制设备的驱动程序,从设备号区分同一个驱动程序的不同设备。当用户向外部设备发起数据请求时,通过设备无关软件会调用设备的相应驱动程序,驱动程序通过总线或者寄存器访问外部硬件设备,发起请求,驱动程序会在初始化的时候向系统的中断向量表注册一个中断处理程序,外部硬件有请求返回的时候会发出中断信号,内核会调用响应的中断处理程序,中断处理程序从硬件的寄存器读取返回的数据,然后转交给内核中的设备服务程序,由设备服务程序把数据交给设备无关的软件,最终到达用户进程。驱动占了内核代码量的一半以上,相当重要哦。

 

二、内存管理子系统

    内存管理子系统主要做了两件事:第一:虚拟地址到物理地址的映射,第二:物理内存的分配。

    1、虚拟地址和物理地址

    国嵌视频里老师说,物理地址就是出现在芯片手册里面的地址,我们控制GPACON的地址是0x56000000,最后访问到硬件,一定是要得到物理地址的,换言之:物理地址就是出现在CPU地址总线上寻址物理内存的地址信号,是地址变换的最终结果。虚拟地址就是用户空间程序当中使用的地址,这个地址和物理地址是有区别的,比如使用malloc分配一段内存,我们得到的内存是虚拟地址。arm的地址总线是32位,一共能有4G的虚拟地址空间,第一部分0~3G是用户空间,3~4G是内核空间。其中3~4G的内核空间又被分为四个部分:3G+896M的直接映射区,vmalloc区,永久映射区和固定映射区,分为四个部分是根据他们的映射方式不同来区别的。

   2、虚拟地址到物理地址的转化

   

    首先将32位虚拟地址分为三个部分,高10位,中间10位和低12位,转换过程对应图中:

    第一步:cr3存放的是页目录基地址,,高10位存放页目录的偏移地址,这样先找到页目录,页目录里面存放的是页表的基地址。

    第二步:使用页表的基地址,再加上中间的10位偏移地址,找到页表,页表当中存放的是各个物理页的基地址。

    第三步:物理页的基地址,加上低12位的页内偏移地址,就能找到最终的存储单元。

    打个比方,我们要到小区里的一栋大楼里面的一个房间拿东西(操作某个具体的物理地址对应的存储单元),对方告诉你地址在1区15栋A单元208房间。1区对应我们的基地址,15栋就是cr3和高十位偏移地址得到的值(相当于页目录,里面存放的是页表的基地址),A单元就是页目录和中间十位偏移地址得到的值(相当于页表,里面存放的是物理页的基地址),208房间就是物理页的基地址和页内偏移量得到的值(具体对应我们的物理内存的存储单元)。这样我们就找到自己要进去的房间拿东西了(操作内存)。

 

3、1G内核空间映射方式

    内核空间为3G~4G的1G空间,分配方式如下:

   

    直接映射区:线性空间中从3G开始最大896M的区间,为直接内存映射区,该区域的线性地址和物理地址存在线性转换关系为线性地址=3G+物理地址。

    动态内存映射区:该区域由内核函数vmalloc来分配,特点是:线性空间连续,但是对应的物理空间不一定连续。vmalloc分配的线性地址所对应的物理页可能处于低端内存,也可能处于高端内存。

    永久内存映射区:该区域可访问高端内存。访问方法是使用alloc_page(_GFP_HIGHMEM)分配高端内存页或者使用kmap函数将分配到的高端内存映射到该区域。

    固定映射区:该区域和4G的顶端只有4k的隔离带,其每个地址项都服务于特定的用途,如ACPI_BASE等。

 

四、物理内存分配方式

    Linux在内存分配的时候,使用的是缺页异常,当我们执行malloc的时候,得到一段虚拟地址,但是这时候还没有拿到实际的物理内存,也就是虚拟地址和物理地址之间还没有实际的映射,因为我们并没有去使用它,当我们读和写的时候,内核会产生一个缺页异常,这时候才会把实际的物理地址分配给我们,这样灵活的方式提高了效率,避免了内存的浪费。

    把这里分为三条线,第一条对应malloc,fork,mmap,第二条对应vmalloc,第三条对应kmalloc。其中1和2都是按照这种缺页异常方式分配内存。但是2vmalloc虽然分配的虚拟地址是连续的,但是实际对应的物理地址可以是不连续的,这是由于我们的内存分配伙伴系统会产生大量的碎片,linux灵活的使用vmalloc来把这些碎片整理好,供给用户空间使用,所以vmalloc一般分配得到的内存都是页的整数倍。3kmalloc是使用了slab分配器和内存池,它是事先分配好的一小块一小块的物理内存放在mempool里面,它们的虚拟地址和物理地址已经映射好了,当使用kmalloc的时候,得到的虚拟地址已经对应了实际的物理地址。

    早先读过lddr3那两本书,虽然现在已经忘得差不多了,但是当时读的很仔细,即使很多地方看不懂,现在在复习这块的时候,很多思想和linux的机制方式还是映在了脑袋里,只可惜那时候没有认真做笔记,都在书上胡乱画了。就做到这里吧,如有不正确的地方还请指出,大家共同进步。

 

    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值