淘宝上找人做网站,百度糯米网站怎么做,那个网做网站便宜,移动端网站一般宽做多大虚拟内存
单片机没有操作系统#xff0c;每次写完代码#xff0c;都需要借助工具把程序烧录进去#xff0c;这样程序才能跑起来。 另外#xff0c;单片机的CPU是直接操作内存的物理地址。 在这种情况下#xff0c;想在内存中同时运行两个程序是不可能的#xff0c;如果第…虚拟内存
单片机没有操作系统每次写完代码都需要借助工具把程序烧录进去这样程序才能跑起来。 另外单片机的CPU是直接操作内存的物理地址。 在这种情况下想在内存中同时运行两个程序是不可能的如果第一个程序在2000的位置写入新值将擦掉第二个程序放在相同位置上的内容所以同时运行两个程序是根本不通的会立刻崩溃。
操作系统如何解决
这里的关键问题是这两个程序都引用了绝对物理地址这是我们最需要避免的。
我们可以把进程使用的地址隔离开来让操作系统为每个进程分配一套独立的虚拟地址人人都有大家只操作自己的地址互不干涉。 但是有个前提是每个进程都不能访问物理地址至于虚拟地址最终怎么落到物理内存里对进程来说是透明的由操作系统安排。 操作系统会提供一种机制将不同的虚拟地址和不同内存的物理地址映射起来。 如果程序要访问虚拟内存的时候由操作系统转换成不同的物理地址这样不同的进程运行的时候写入的是不同的物理地址这样就不会冲突了。
我们程序使用的内存地址叫做虚拟内存地址存在硬件空间地址叫做物理内存地址
操作系统引入虚拟内存进程将持有的虚拟地址通过CPU芯片中的内存管理单元MMU的映射关系转换变成物理地址再通过物理地址访问内存。 操作系统如何管理虚拟地址和物理地址之间的关系呢
内存分段
程序由若干个逻辑分段组成如可由代码分段、数据分段。栈段、堆段组成。不同的段有不同的属性就用分段的形式把这些段分离出来。
分段机制下的虚拟地址由两部分组成段选择因子和段内偏移量。
段选择因子保存在段寄存器里面段选择因子里面最重要的是段号用作段表的索引。段表里保存的是这个段的基地址、界限和特权等级。段内偏移量位于0和段界限之间如果段内偏移量是合法的就将段基地址加上段内偏移量得到物理内存地址。
内存分段的缺点
内存碎片的问题。内存交换的效率低的问题。
我们来看这样一个例子。我现在手头的这台电脑有 1GB 的内存。我们先启动一个图形渲染程序占用了 512MB 的内存接着启动一个 Chrome 浏览器占用了 128MB 内存再启动一个 Python 程序占用了 256MB 内存。这个时候我们关掉 Chrome于是空闲内存还有 1024 - 512 - 256 256MB。按理来说我们有足够的空间再去装载一个 200MB 的程序。但是这 256MB 的内存空间不是连续的而是被分成了两段 128MB 的内存。因此实际情况是我们的程序没办法加载进来。
内存交换 我们可以把Python程序占用的256MB内存写到硬盘上然后再从硬盘上读回来到内存里面。不过读回来的时候我们不再把它加载到原来的位置而是紧紧跟在那已经被占用了的 512MB 内存后面。这样我们就有了连续的 256MB 内存空间就可以去加载一个新的 200MB 的程序。
如果自己安装过Linux操作系统应该遇到过分配一个swap硬盘分区的问题。这块分出来的磁盘空间就是专门给Linux操作系统进行内存交换用的。
虚拟内存、分段再加上内存交换看起来似乎已经解决了计算机同时装载运行很多个程序的问题。不过你千万不要大意这三者的组合仍然会遇到一个性能瓶颈。硬盘的访问速度要比内存慢很多而每一次内存交换我们都需要把一大段连续的内存数据写到硬盘上。所以如果内存交换的时候交换的是一个很占内存空间的程序这样整个机器都会显得卡顿。为了解决内存分段的内存碎片和内存交换效率低的问题就出现了内存分页。
内存分页
分段的好处是能产生连续的内存空间但是会出现内存碎片和内存交换的空间太大的问题。
要解决这些问题那么就要想出能少出现一些内存碎片的办法。另外当需要进行内存交换的时候让需要交换写入或者从磁盘装载的数据更少一点这样就可以解决问题了。这个办法也就是内存分页Paging。
分页是把整个虚拟和物理内存空间切成一段段固定尺寸的大小。这样一个连续并且尺寸固定的内存空间称为页Linux下每一页为4KB。
虚拟地址与物理地址之间通过页表来映射。
页表实际上存储在CPU的内存管理单元MMU中于是CPU可以直接通过MMU找出要实际访问的物理内存地址。
当进程要访问的虚拟地址在页表中查不到时系统会产生一个缺页异常进入系统内核空间分配物理内存更新进程页表最后返回用户空间恢复进程的运行。
分页怎么解决分段的内存碎片、内存交换效率低的问题
由于内存空间都是预先划分好的也就不会像分段会产生间隙非常小的内存采用了分页释放的内存都是以页为单位释放的也就不会产生给进程使用的小内存。
如果内存空间不够操作系统会把其它正在运行的进程中最近没使用的内存页面给释放掉也就是暂时写作硬盘上称为换出。一旦需要的时候再加载进来称为换入。
一次性写入磁盘的只有少数的一个页或者几个页不会花太多时间内存交换的效率也就相对较高。
分页方式使得我们在加载程序的时候不再需要一次性把程序加载到物理内存中。我们完全可以在进行虚拟内存和物理内存的页之间的映射之后并不真地把页加载到物理内存里而是只在程序运行时需要用到对应虚拟内存页里面的指令和数据时再加载到物理内存里面去。
分页机制下虚拟地址分为两部分页号和页内便宜页号作为页表的索引页表包含物理页每页所在物理内存的基地址这个基地址与页内偏移的组成就形成了物理内存地址。
对于一个内存地址转换其实也就三个步骤
把虚拟内存地址切分成页号和偏移量。根据页号从页表里面查询对应的物理页号。直接拿物理页号加上前面的偏移量就得到了物理内存地址。
简单分页
因为操作系统可以同时运行非常多的进程也就意味着页表会非常庞大。
在32位的环境下虚拟地址空间有4GB假设一个页的大小是4KB那么大概需要100万个页每个页表项需要4个字节大小存储那么共需要4MB的内存存储页表。
这4MB大小的页表看起来也不是很大。但是要知道每个进程都有自己的虚拟地址空间也就说都有自己的页表。
那么100个进程就需要400MB的内存来存储页表这是非常大的内存了更别说64位的环境了。
多级页表
要解决上面的问题就需要采用一种叫做多级页表的解决方案。
对于单页表的实现方式在32位和页大小4KB的环境下一个进程的页表需要装下100多万个页表项并且每个页表项占用4字节大小于是每个页表占用4MB大小的空间。
我们把这个100多万个页表项的单级页表再分页将页表分为1024个页表二级页表每个表二级页表中包含1024个页表项形成二级分页。
为什么多级分页比普通分页更节省内存
分了二级页表映射4GB地址空间就需要4KB一级页表4MB二级页表的内存这样占用空间不是更大了吗
当然如果4GB的虚拟地址全部映射到了物理内存上二级分页占用空间确实更大了但是我们不会为一个进程分配那么多内存。
每个进程都有4GB的虚拟地址空间而对于大多数程序来说其使用到的空间远未达到4GB因为会存在部分对应的页表项都是空的根本没有分配对于已分配的页表项如果存在最近一定时间未访问的页表在物理内存紧张情况下操作系统会将页面换出到硬盘也就是说不会占用物理内存。
如果使用了二级分页一级页表就可以覆盖整个4GB的虚拟地址空间如果某一个一级页表的页表项没有被用到就不需要创建这个页表项对应的二级页表了即可以在需要时创建二级页表。
程序局部性原理
程序在运行时对数据的访问往往呈现出局部性特征即在一段时间内程序的大部分执行都集中在程序的某一部分并且这段代码所引用的数据也大多位于相邻的内存区域。
程序局部性原理可以分为两种形式
时间局部性在程序执行过程中如果某个指令或数据已经被访问过那么在不久之后该指令或数据很可能再次被访问。例如在循环体内同一组指令和数据会被反复执行多次。空间局部性某个存储单元被访问过那么在不久之后其相邻的存储单元也很可能被访问例如数组中的相邻元素通常会被连续访问。
根据程序局部性原理计算机体系结构中设计了多级缓存、虚拟存储器等技术操作系统中设计了页面调度、LRU缓存替换算法等机制。
页表缓存TLB
多级页表虽然解决了空间上的问题但是虚拟地址到物理地址的转换就多了几道转换的工序这显然就降低了这两个地址转换的速度也就是带来了时间上的开销。
程序是有局部性的即在一段时间内整个程序的执行仅限于程序中某一部分。相应地执行所访问的存储空间也局限于某个内存区域。
我们利用这一特性把最常访问的几个页表项存储到访问速度更快的硬件于是计算机科学家们就在CPU芯片中加入了一个专门存放程序最常访问的页表项的Cache这个Cache就是TLB,通常称为页表缓存转址旁路缓存、快表等。
在 CPU 芯片里面封装了内存管理单元Memory Management Unit芯片它用来完成地址转换和 TLB 的访问与交互。有了 TLB 后那么 CPU 在寻址时会先查 TLB如果没找到才会继续查常规的页表。TLB 的命中率其实是很高的因为程序最常访问的页就那么几个。
Linux内存管理
在Linux操作系统中虚拟地址空间的内部又被分为内核空间和用户空间两部分。 32位系统的内核空间占用1G位于最高处剩下的3G是用户空间。
内核空间与用户空间的区别
进程在用户态时只能访问用户空间内存只有进入内核态时才可以访问内核空间的内存。
虽然每个进程都有自己的虚拟内存但是每个虚拟内存中的内核地址其实关联的都是相同的物理内存。 进程切换到内核态后就可以很方便地访问内核内存空间。