浅谈操做系统与内存

浅谈操做系统与内存

对于计算机的发明,相信你们都有耳闻那个占地面积按平米算的第一台计算机。在那个时候,CPU的资源是极其珍贵的,随着这些年日新月异的发展,一片指甲盖大小的民用级CPU一秒钟能执行的指令数能够达到上亿级别。linux

随着计算能力的增加,芯片外围的硬件和配套的软件也是一路高歌,发生了天翻地覆的变化,今天咱们简单回顾历史,来看一看操做系统和内存机制的演变,不只要了解它们是怎样,同时也看看它们为何会是这样。程序员


CPU的运行

一说到CPU(Center processing unit),你们都以为这是很是了不得的东西,咱们的手机电脑都是由它进行核心控制,拥有掌管一切的能力,可是,它真的有传说中那么聪明么?算法

事实并不是如此,CPU惟一的能力其实就是处理二进制数据,CPU的组成是这样的:编程

  • CPU有三种总线:控制总线,地址总线,数据总线,这些总线统称为系统总线,主要用来与外设交换数据。
  • 内部寄存器若干,通常只有几十个字节,普通寄存器负责传输数据,特殊寄存器如PC,SP,LD则控制程序流程。
  • ALU-算数逻辑运算单元,既然是处理数据,固然也就依赖运算单元,这些运算单元只能处理加减乘法,其实严格来讲,就是处理加法,减和乘是在加法基础上实现的。

这三个就是早期CPU的主要部件了,那它是怎么处理数据的呢?windows

系统总线负责与外部的数据交换,将交换的数据暂时放在寄存器中,而后CPU再从寄存器中获取数据使用算数逻辑运算单元进行运算,必要时将数据写回。安全

是的,仅此而已,CPU不能直接播放音乐,它也不能生成游戏界面,只是孜孜不倦地拿数据,处理数据,写回数据,像个一刻也不停的流水线工人。服务器


系统的存储

上文中咱们提到CPU的内部寄存器,由于是集成在CPU内部,因此速度很是快,可是同时也由于集成度的问题,CPU中通常只有几十字节的寄存器,这对于数据处理是远远不够的,因此当运行时存储空间不够的时候咱们必须有另外一个地方进行存储,这就是系统内存。多线程

即便都是数据,也分别有不一样的属性,好比是否须要掉电保存,数据处理的速率要求。并发

在程序执行时,须要在存储设备中保存一些运行参数,这部分存储数据的速率直接决定了程序运行速率,因此对这部分存储的要求是速度快,同时由于是存储运行时参数,因此不须要掉电保存。函数

在程序执行以外,须要保存大量的静态数据,好比用户数据,文件,这部分数据须要掉电可以保存,且要求可存储数据量大,因为访问并不频繁,因此对速率要求能够下降以节省成本。

由此,存储设备分化出了ram和rom两大类,ram有速率快,掉电不保存的特性,而rom是速率慢,存储量大,掉电保存。

ram和rom只是分别对应易失性存储器和非易失性存储器的统称,事实上ram有sram,sdram等等,而rom有eeprom,flash,硬盘等等。


微控制器和单板机

上面说到CPU的做用,可能你开始有疑问了,既然CPU只能处理简单的数据,那么它是怎么处理复杂软件的运行的呢?

这就是不少人的一个常见误区,将CPU和MCU,还有单板机搞混了。

MCU(Microcontroller unit):微控制器,又被称为单片微型计算机,常被直接称为单片机。它一般集成了CPU,rom,ram以及硬件控制器、外围电路等等,ram和rom的容量有限,接口电路也有限。

单板机:单板机是把微型计算机的整个功能体系电路(CPU、ROM、RAM、输入/输出接口电路以及其余辅助电路)所有组装在一块印制电板上,再用印制电路将各个功能芯片链接起来,通常来讲资源好比主频、ram&rom容量要比MCU高出一大截。

事实上,在咱们常见的手机电脑中,用户常说的CPU,事实上指的是单板机。而在嵌入式设备上,常常会使用到单片机。

常见的第二个误区就是:认为整个单板机(或者MCU)上只有CPU是可编程元件,其余部分都是硬件搭建起来的。

事实上,除了CPU,像各类硬件控制器好比gpio、i2c、dma或者硬盘控制器,这些都是可编程器件,只是这些控制器所扮演的都是一个从机角色,而主控是CPU。

CPU经过系统总线与各个控制器之间进行数据交互,经过特定的预约义的指令来控制控制器的行为:

好比控制gpio的操做,将某个引脚的电平从0设置成1,这样再配合上继电器等硬件上的电路,就能够实现220V电路的控制。

再者是经过控制gpio以经过预约义的控制协议与链接在另外一端的设备进行通讯。

又或者是告诉硬盘控制器,CPU须要访问某些数据,硬盘控制器将数据传递给CPU。

CPU就是这样经过将指令一级级地将数据传递给外设,经过在外设处理器中预约义了一些指令字段,外设接收CPU的数据至关于接收到控制指令,以此实现外设的控制。

而CPU自己则只是孜孜不倦地处理数据,反馈数据。


单片机到操做系统

在早期的CPU上,CPU都是顺序执行,一个CPU只运行一个程序,这样形成的问题就是:当程序在等待某个资源或者读写磁盘的时候,CPU就处于空闲状态,这对CPU来讲是很是浪费的。因为CPU的资源很是珍贵,人们不得不想办法解决这个问题,因此就有人编写多任务程序:

一个CPU中能够存在多个任务,一个时刻只容许一个任务运行,当检测程序检测到某个任务处于空闲状态,就切换下一个任务运行。或者是当前任务主动放弃运行权,切换下一个任务运行。

这就是操做系统的原型,检测程序通常被称为调度器,遵循某种调度算法。


任务调度算法

调度算法又常被称为调度策略,目前的操做系统经常使用的调度策略有两种:

  • 基于优先级的调度,优先级高的任务老是有权利抢到CPU的使用权,高优先级的任务经过主动睡眠和被动睡眠让出CPU使用权(如等待信号量,等待锁)
  • 时间片机制,每一个任务分配时间片,通常是ms级别的时间,当时间片用完就切换到下一个任务,营造一种全部任务同时运行的假象。

事实上,操做系统每每在基于上述调度算法的基础上,运行着更复杂的调度算法,好比以优先级为权重分配时间片,好比任务分组,不一样的分组有不一样的调度策略等等....


任务调度的执行原理

上文说到,操做系统的的最先雏形就是容许CPU中存在多个任务,经过某种调度算法来使每一个任务交替运行。

那么,CPU究竟是怎么作到这件事的呢?

首先咱们要了解如下概念:时钟、中断,程序执行流的切换。

  • 时钟:是CPU上最重要的部分之一,任何一个CPU都必须有时钟部分,它提供了CPU上时间的概念,系统不一样部分之间的交互和同步都要靠时钟信号来进行同步。通俗来讲,就像人类定义了时间的概念,咱们才能够统一作到准时上下班,相约同一时间去作某事。CPU也是同样,在进行数据传输时,通讯双方必须统一传输单元数据的时间,才能进行同步和数据解析。
  • 中断:即便是目前很是简单的单片机,也提供中断功能。咱们能够将中断理解为CPU中的突发事件须要紧急处理,这时CPU暂停处理本来任务转去处理中断,处理完中断以后再回头来处理原先的任务。
  • 程序执行流的切换:CPU中通常会有多个寄存器,分为通用寄存器和特殊寄存器,通用寄存器用来存取CPU处理的数据,而特殊寄存器中须要说起的就是PC指针(程序计数器),SP(栈指针),PC指针中存储着下一条将要执行的指令地址,而SP指针则指向栈地址。
    在正常状况下,程序顺序运行,PC指针也是按顺序装载须要执行程序的地址,CPU每次执行一条指令都会从PC指针读取程序执行地址,而后从执行地址上取到数据,执行相应动做,而栈则保存着函数调用过程当中的信息(由于调用完须要返回到调用前状态)。

在发生中断的时候,由于须要跳转去执行中断服务函数,确定须要更改程序执行流,在单片机上,CPU将会去查中断向量表,找到须要执行的函数地址装载到PC指针处,而后保存一些当前运行参数,好比寄存器数据,栈数据,下一个指令周期将会跳转执行中断服务函数。

既然程序的执行流能够由程序员自由控制,只要控制PC指针指向就能够,那么咱们也能够预先定义两个任务,当一个任务空闲时,直接跳转执行到另外一个任务,每一个任务的运行参数好比寄存器数据,栈数据,使用的资源等等进行保存,在切换到某个任务时,只要将保存的信息恢复到原来的状态就能够继续运行任务,这种操做方式固然能够从两个延伸到多个,这就是操做系统的模型。

一般,保存信息的部分通常使用结构体链表,通常被称为任务控制块(每一个操做系统的叫法不同,但实现的思想是同样的),每一个线程都有独立的栈空间以保存每一个线程的运行时状态。

那么,咱们怎么知道何时执行另外一个任务呢?这就须要借助时钟中断提供一个时间的概念,任务的运行时间就以这个时钟中断做为度量,具体的作法就是每隔一个周期(一般很是短,1ms、10ms等)产生一次中断,在中断程序中检查是否须要切换任务运行,而切换的缘由就比较多了,好比运行时间到了、等待某项资源自主休眠等等。

事实上,到这里,你已经了解了嵌入式实时系统的运行原理了,好比ucos,freeRTOS之类的,建议去看看相关源代码,尤为是ucos,代码量少且易懂,麻雀虽小五脏俱全。


嵌入式实时操做系统的内存限制

在早期的单片机上,程序运行在物理内存中,也就是说,程序在运行时直接访问到物理地址,在程序运行开始,将所有程序加载到内存中,全部的数据地址和程序地址就此固定。

在运行多任务系统时,比较直接的办法也是直接为每一个任务分配各自须要的内存空间,好比总内存为100M,task1须要40M,task2须要50M,task3须要20M,那么最简单的办法是给task1分配40M,给task2分配50M,而task3,很差意思,内存不够了,不容许运行,这样简单的分配方式有如下问题:

  • 地址空间不隔离 试想一下,全部程序都访问物理地址,那么就存在task1能够直接访问到task2任务内存空间的状况,这就给了一些恶意程序机会来进行一些非法操做,即便是非恶意可是有bug的程序,也可能会致使其余任务没法运行,这对于须要安全稳定的的计算机环境的用户是不能容忍的。
  • 内存使用效率极低 因为没有有效的内存管理机制,一般在一个程序执行时,将整个程序装载进内存中运行,若是咱们要运行task3,内存已经不够了,其实系统彻底能够将暂时没有运行的task2先装入磁盘中,将task3装载到内存,当须要运行task2时,再将task2换回,虽然牺牲了一些执行效率,但总归是能够支持更多程序运行。
  • 程序地址空间不肯定 须要了解的是程序在编译阶段生成的可执行文件中符号地址是连续且肯定的,即便实现了内存数据与磁盘的交换,将暂时不须要的任务放入磁盘而运行其余任务,程序每次装入内存时,都须要从内存中分配一段连续内存,这个空间是不肯定的,可是在程序实现过程当中,有些数据每每要求其地址空间是肯定的,这就会给编写程序带来很大的麻烦。

虚拟内存机制

那么怎么解决这个问题呢?曾经有人说过一句名言:

计算机科学领域的任何问题均可以经过增长一个间接的中间层来解决。

在这里这个道理一样适用,后来的操做系统设计者设计了一种新的模型,在内存中增长一层虚拟地址。

面对开发者而言,程序中使用的地址都是虚拟地址,这样,只要咱们能妥善处理好虚拟地址到物理地址的映射过程,而这个映射过程被看成独立的一部分存在,就能够解决上述提到的三个问题:

  • 首先,对于地址隔离,由于程序员操做的是虚拟地址,虚拟地址在最后会转化为物理地址,可能程序A访问的0x8000,转换到物理地址是0x3000,而程序B一样访问0x8000,可是会被转换到物理地址0x4000,这样程序中并不知道本身操做的物理地址,也操做不到实际的物理地址,由于虚拟地址的转换总会将地址映射到不冲突的物理地址上,同时进行检测,也就无从影响到别的任务执行。
  • 对于内存使用率来讲 创建完善的内存管理机制,能够更方便地进行内存与磁盘数据的交换,将空置的内存利用起来而不增长编程者的负担。
  • 对于程序空间不肯定 虚拟内存机制完美地解决了这个问题,在直接操做物理地址时,由于内存与磁盘的交换,可能致使函数或者变量地址的变化,原来程序中的数据必须进行更新。
    而引入虚拟内存机制,程序员只操做虚拟地址,函数、变量虚拟地址不变,物理地址可能变化,可是程序员只须要关注虚拟地址就好了,虚拟地址到物理地址的转换就由内存管理部分负责。

既然增长了一个中间层,那么这个中间层最好是由独立的部分进行管理,实现这个功能的器件就是MMU,它接管了程序中虚拟地址和物理地址的转换,MMU通常直接集成在CPU中,不会以独立的器件存在。


内存分段分页机制

在经典32位桌面操做系统中,有32条地址线(特殊状况下可能36条),那么CPU可直接寻址到的内存空间为2^32字节,也就是4GB,虽然说内存寻址能够到4G,可是经常在单板机上 并不会有这么大的物理内存。

根据实际状况而不一样,多是512M或者更少,可是因为虚拟内存机制的存在,程序看起来可操做的内存就是4GB,由于MMU总会找到与程序中的虚拟地址相对应的物理地址,在内存不够用时,它就会征用硬盘中的空间,在linux下安装系统时会让你分出一片swap分区,顾名思义,swap分区就是用来内存交换的。

那4GB这么大的内存,若是不进行组织,在CPU读写数据时将会是一场灾难,由于要找到一个数据在最坏的状况下须要遍历整个内存。

就像一个部队,总要按照军、师、旅、团、营、连、排、班来划分,若是全由总司令管理全部人,那也会是一场灾难。

所以,对于内存而言,衍生了分段和分页机制,根据功能划分段,而后再细分红页,通常一页是4K,固然,这其中会有根据不一样业务的差异作一些特别的定制。

它的问题就是即便是只存储一个字节,也要用掉一页的内存,形成必定的浪费。

可是若是将分页粒度定得过细,将会形成访问成本的增长,由于在不少时候,进行访问都是直接使用轮询机制。并且,就像每本书都有目录和前言,段和页的信息都要在系统中进行记录,分页更细则表明页数更多,这部分的开销也就更多。这也是一种浪费。

就像程序中时间与空间的拉锯战,计算机中充满了妥协。


单片机与单板机程序执行的不一样

处理器是否携带MMU几乎彻底成了划分单片机和单板机的分界线。

带MMU的处理器直接运行桌面系统,如linux、windows之类的,与不带MMU的单片机相比,体如今用户眼前的最大区别就是进程的概念,一个进程就是一个程序的运行实体,使得桌面系统中能够同时运行多个程序,而每一个程序因为虚拟机制的存在,看起来是独占了整个内存空间。

而不带MMU的处理器,通常是嵌入式设备,程序直接在物理地址上运行,支持多线程。

从程序的加载执行来讲,桌面系统中程序编译完成以后存储在文件系统中,在程序被调用执行时由加载器加载到内存中执行。而在单片机中,程序编译生成的可执行文件通常是直接下载到片上flash(rom的一种)中。

32位单片机的寻址范围为4G,因为不支持虚拟内存技术,通常在总线上链接的ram不会太大,因此能够直接将rom也挂载在总线上,CPU能够直接经过总线访问flash上的数据,因此程序能够直接在片上flash中执行,在运行时只是将数据部分加载到ram中运行。


无系统单线程,嵌入式实时操做系统和桌面操做系统

能够在单板机上运行桌面操做系统,是否是运行单线程的单片机和嵌入式实时操做系统就能够抛弃了呢?

从功能实现上来讲,单板机确实比单片机强不少,可是资源的增长同时带来成本的增长,因此在一些成本敏感的应用场合下反而是单片机的天下。

而对于操做系统而言,嵌入式实时操做系统因为没有一些臃肿的系统服务,有时反而有着比桌面系统更好的实时性,在实时性要求高的场合更是风生水起,好比无人机、车载系统。并且运行在单片机上,成本上更有优点。

对于嵌入式开发而言,颇有必要了解这三种程序运行方式:无系统单线程,嵌入式实时操做系统,桌面操做系统。

  • 无系统单线程很好理解,程序顺序执行,除了发生中断以后跳转执行中断服务函数,通常不会再发生程序执行流的切换,程序直接操做物理地址,经常使用于嵌入式小型设备,如灯、插座开关等等。
  • 嵌入式实时操做系统:支持多线程的并发执行,多任务循环执行,遵循必定的调度算法,程序直接操做物理地址。例如ucos、free rtos,一般也在移植在资源相对较多的单片机上。
  • 桌面操做系统:CPU自带MMU,因此支持虚拟内存机制,支持多进程和多线程编程,程序操做虚拟地址,在人机交互上有很好的表现。例如windows,linux,经常使用的手机、ipad、服务器、我的电脑。

在单片机上,无系统单线程模式能够很简单地切换到嵌入式实时操做系统,进行相应的移植便可。

可是是否能在其上运行桌面操做系统,是否集成MMU是一个决定性的因素。


好了,关于操做系统和内存的讨论就到此为止啦,若是朋友们对于这个有什么疑问或者发现有文章中有什么错误,欢迎留言

原创博客,转载请注明出处!

祝各位早日实现项目丛中过,bug不沾身. (完)

相关文章
相关标签/搜索