计算机底层原理杂谈(白话文)

  简单说一下写这篇文章的原因。首先这个不是教学类型的,是我Java实在学不下去了,由于好多计算机底层原理都不是很清楚,每次学新东西都因为想不明白底层原理困惑,因此下决心中止学习Java的新东西,开始搞明白底层。一开始搞的所谓的底层是“Java虚拟机”,而后又C语言汇编语言什么的,实际上是想图快,尽快接近如今作的事情。后来发现不行,这事快不了,因此干脆就从物理层面用导线灯泡集成芯片开始动手作一个cpu开始吧。其实也没多久,大概三个多月吧,从我以前写的【从零开始自制cpu】系列的学习文章也能够看到开始时间。cpu作好以后(其实后来因为总是烧坏各类电路没作完),开始看操做系统,如今刚开始看一点。总结起来就是我认为的学习路线应该是:html

  自制个cpu--微机原理--简单数据结构与算法--计算机组成原理--汇编语言--操做系统--C语言--编译原理--复杂数据结构与算法--计算机网络--Java--Java虚拟机--源码研究--多线程linux高并发Spring负载均衡各类以这些评判一我的Java水平的东东...linux

  我认为Java应该在那样一个遥远的地方,好多人却以它为起点了。我如今粗略地看到操做系统,固然不断地在补前面的知识。这篇文章想借此机会跟你们说说计算机底层的理解,学的知识很碎,你们顺便帮我挑挑错误,若是对你有帮助那就更好了。我用大白话说,想到哪说到哪,这样更容易展示出漏洞和错误,但愿你们在评论区里积极吐槽~
算法

 

----------------------------------------华丽的正文分割线------------------------------------------编程

  CPU就是由一堆导线将一堆部件链接在一块的东西,每一个部件里面又是由一堆导线和一堆更小的部件链接在一块。把整个cpu当作一个部件,那它和RAM,磁盘等外设又是用一堆导线链接在一块,我感受就像套娃似的。而后每一个部件从外部看就是暴露了一堆针脚,能够用导线连上,给他们传递要么高电平要么低电平的电流,你好比说下面这款74LS173(3态输出端的4位D型寄存器,简单说就是一个能存4位数据的东西,能够用来作寄存器。网络

  

  看这么多阵脚,我感受分三类理解就好了,别管他们是数据输入、数据输出、信号控制啥的,他们都是一类,反正要给他们要么高要么低的电信号。还有一类就是接+接-的vcc和gnd,目的是让这个部件有“电”。还有一个是时钟clock,这你给他一个一会高一会低来回变的电信号就好了,目的通常是让它电平上升沿的时候这个部件“触发”一个什么效果,对这个寄存器来讲,就是存了个数据或者读出了数据,固然有的部件不须要时钟信号。数据结构

  CPU以及它关联的其余设备,全都是一个个这样的“部件”,这些部件别管它多复杂,最终暴露出来的都是一个时钟、一个接正接负、一堆其余的乱七八糟的针脚。有的部件用来存东西,有的用来作运算好比加法器,有的用来计数好比程序计数器,有的部件用来作各类翻译呀转换呀什么的,好比地址译码什么的。最终它们都链接在一个大boss上那就是时钟信号发生装置,反正它的做用就是特别快速地输出一个高低高低高低电平来回转换的电信号。因此如今我总结出cpu就是一个boss时钟信号,一堆小弟部件,一堆导线把部件有规律地连在一块,最终都连在大boss上。多线程

  那这样一堆部件是怎么运行的呢?首先第一个部件是程序计数器pc,它就是靠时钟信号不断累加,输出的针脚从0000,0001,0002一直往上加。固然你能够一个时钟周期(就是高低电平来一下)就+1,但这样除非你其余部件一个时钟周期内就能把一条指令执行完,通常不行,那怎么办呢,就再弄一个计数器,好比只能从0加到5。这个计数器每次都从0开始,而后没到5以前都把程序计数器的一个状态针脚变成“不触发加”的状态,这样程序计数器就不变了。而后各个部件在5个时钟周期内把指令执行完,而后程序计数器再加1。这就是单指令周期,每一个指令都用5个时钟周期执行完。固然这很差,你可变化呀,让这个计数器从0加到一个动态的值,这个值根据指令类型改变,这就多指令周期了。而后程序计数器一直往上加也很差,给他弄几个针脚,再弄一个状态,能够直接设置一个新值,这就实现程序跳转了。一个指令周期能够动态改变,还能够强行设置程序计数器的值,这差很少就够了。架构

  第二个部件是寄存器堆,其实就是一个存东西的地方,跟内存呀硬盘呀同样,只不过离cpu近,就光用距离除以电流流速来讲都能说明它比较快。因此一些运算出来的中间结果呀,甚至我从内存中读出来要作加法或者要写入内存的数据都先放寄存器里面。寄存器都同样的你存哪一个都行,只不过为了统一,别一我的一个样,把寄存器分门别类弄了些专用的功能,地址数据就放你地址数据该存的寄存器,表示状态的数据就放在你状态该存的寄存器,仅此而已。寄存器正由于有不一样人给它赋予定义才麻烦,就好比IO接口中的端口,就是寄存器而已,只不过好比像硬盘接口,你往它3号端口写个011101啥的,他就表示你要读数据了,而后硬盘把数据放到4号寄存器等着你读。并发

  第三个部件是算术逻辑单元,你能够先假设它就是个只能执行加法的部件,8个针脚数据1,8个针脚数据2,再来8个针脚表示这俩数据的和,完事。负载均衡

  第四个部件我不想叫它控制单元,我感受我一开始困惑的就是由于这种叫法,我感受它更像是一种布线的方式,只不过抽象地说出来逼格高,更容易写成教材。简单说就是几个针脚接收指令,另几个针脚输出各类不一样高低电平信号,链接在其余部件的针脚上起到一些控制做用。你好比我输入一个“写入内存”的指令,那我输出的针脚确定有一个是接在内存的“是否写入”这个针脚上,这不就控制了么。

  总结起来,其实部件就那么几种,存储部件:寄存器呀,RAM ROM呀;控制部件:就是联系全部部件的控制它们可读可写可加这种逻辑的;算数部件:数学运算用的;发动机部件:这我给命名成发动机部件吧,就是时钟信号产生,还有程序计数器,这些都是将整个部件激活、发动的感受,没有它们就没了源动力。外设部件:也能够叫IO部件,注意千万不要把硬盘理解成存储部件,它跟网口、鼠标、键盘是同样的,都是IO,你能从磁盘中读数据,你也能从键盘中读数据,当它们接到IO接口上时,全都视为同一个东西了。

  我拿键盘举个例子,无论谁家生产的键盘,都要接在我一个叫“键盘接口”的东西上,这个键盘接口中有5个端口,1号2号3号4号5号,其实就是寄存器,接口上面的寄存器就叫端口。我这个键盘生产商能够写个说明书,告诉你们大家听好啦,1号端口就是个人按键数据,我按了键盘中的A,我就往1号端口中写00100011,你cpu读到了怎么处理我就无论啦。固然我很好心,我给你2号端口也搞一个数据,为0的时候说明我没按键,为1的时候我就按键了,这够能够了吧。这时候cpu就能够处理了,我去读这个2号端口的数据,就像我读内存数据同样,读到了我发现它是1,那我知道键盘按键了,我接着读1号端口的数据,而后各类处理最终给显示器接口中的一堆端口写上一堆奇奇怪怪的数据,显示器读到这些数据后又作了一堆处理最终在屏幕上亮了几个灯泡,亮出了一个A。这里面cpu不断读2号端口看键盘有没有操做就叫用轮询IO的方式检查设备,读了1号端口的数据作各类处理最终给显示器接口写入数据,就是驱动程序。最后显示器读这些数据显示到屏幕上,那这是另外一个设备的物理细节了,它里面也有个像咱们这个cpu的东西就不去细究了。

  完美,不过上面的过程又有些问题,若是io设备不少,轮询io的方式就很没效率了,最好是io有动做的时候主动通知cpu。那能够这样作,好比键盘有动做,我不是往我2号端口写数据了,而是往你cpu中一个寄存器中写一个号码,cpu读到这个寄存器中有数据了,经过查它的号码找到对应驱动程序的内存地址,执行这个程序。这个过程就叫中断,而查询号码去找程序的地方,叫作中断向量表。这里其实我真的也不想叫它中断,由于又是这个词让我困惑很久。由于cpu是经过增长一个时钟周期专门检测是否有中断信号产生,也就是说若是没有任何中断信号,这个时钟周期也是须要空跑一次的。因此你看,从更物理的时钟周期的层面看,这个中断方式仍然是轮询,只不过轮询的单位不是指令,而是时钟

  这说法完美,不过上面还有个问题,就是像键盘这样的还好,由于它确实须要执行一段特殊的驱动程序去完成功能。但想磁盘这种,单纯的就是读出数据写到内存或者读内存数据写到磁盘,这种操做很低级可是很耗时间,若是每次都是经过中断而后数据经过cpu先传到寄存器在一个个传到内存,那就让cpu太大材小用了。这种重复的耗时的劳动,最好别占用cpu,直接从硬盘经过某个设备到内存就行了,这个设备就叫作IO控制器DMA。硬盘接收到cpu的读请求后,向dma发请求信号。dma完成了从硬盘写入内存操做后,再向cpu发一个中断信号,简单执行一下数据处理完的中断程序就好了,至于数据传输的过程,cpu能够作其余更高级的事情。

  完美,不过上面的又有一些问题,就是你虽然不占用我cpu时间,但你占用总线啊,咱们是公用一条总线传输数据的,你传输数据占用总线的时候,我cpu就占不了了。或者你等我cpu不用总线的时候你在再用,这个叫作dma的时钟周期窃取。但这样也很差,我他妈就但愿你离我越远越好,别占我cpu时间也别占个人地方,让专门一个能够执行简单指令的设备和你公用一条单独的总线去完成这件事,我称之为low版cpu,他就是io通道

 

  

  再说说IO端口地址问题,cpu如何指定一个端口呢,能够用一个部分表示地址,另外一个部分表示是IO地址仍是内存地址。还有一种方式是,将io端口也加入到内存同样的地址范围中,而后访问一个端口跟访问一个内存地址没什么差异。因此上述到就是IO端口的两种编址方式,第一种是独立编址,采用端口映射io,第二种是统一编制,采用内存映射io,如今基本都是内存映射。整个io这一块大致的骨架就是这样子的,你看刚刚所说的中断呀,dma呀这些,我以为能够理解为操做系统,或者说因为使用cpu的需求倒逼出的产物。固然全部这些均可以用软件来实现,但当需求足够大的时候可让cpu为操做系统作出一些改变的,这并非cpu本来就是这个样子。其实上面提到的中断,是外部中断,固然也能够是内部中断,就是指令本身去出发一个中断。这是根据中断源的不一样分的。固然本质是同样的,都是往一个寄存器或者几个寄存器里写数,cpu一个时钟周期专门查看一下这个寄存器,而后查下中断向量表找到对应的程序执行一下,执行完了恢复以前的pc再继续往下进行。

  整个io差很少就是这样的骨架,因此你看为何操做系统关注io,关注内存管理,关注多进程,由于没啥别的东西可关注了,cpu本来能作的事情太简单了,所谓操做系统也好,dma这些新增的硬件也好,没有什么技术上高端的事情,或者说在计算机底层,高端的本质就是复杂和麻烦,这也回答了我很久以前写过的一篇《究竟什么是技术》。你包括个人第一张74LS173的针脚图,若是你看了我说的什么“部件”巴拉巴拉明白了,你能够说你懂,固然你把cpu主要部件的针脚图都看了,都记住了而且在面包板或者焊接版上接过了,你也能够说你懂。但这层次就不一样了,所谓理解得深不深,其实就在于细节。

  再说说内存地址管理,或者说寻址方式,固然你能够在指令中的地址就表示绝对的地址,不通过任何转换直接到内存或者相应的设备中输入这个地址信号而后读数据。你或者把俩地址拼一块,造成一个新地址。再或者你造成新地址后再经过某种方式转换映射一下,或者再映射一下。等等,操做系统对内存的管理就是这些,全都是细节。我只是简单入了个门,最开始cpu是绝对地址寻址,就是我指令中的地址直接输入到某个部件的地址线上。第一个搞事情的是8086cpu,也就是x86架构的鼻祖cpu,它有16位数据线,但有20位地址线。固然你能够只用16位地址线但当时刚好人们以为地址不够用了,而后又各类缘由不能弄成32位的cpu,因而乎寻址的时候就把一个寄存器看成段地址数据,另外一个看成段内地址,其实别管那么多,就是应给凑成了20位地址罢了,这样寻址范围就扩大了。但这设计好很差?美其名曰段地址和段内地址,其实这很麻烦,若是cpu位数够,没人给本身找这种麻烦,以致于后来的32位cpu为了兼容之前的拍脑门设计,即使是寻址空间已经够了但仍是采用这种段方式。但后来又说操做系统变得复杂了,倒逼着cpu弄出实模式和保护模式,每一个段也有本身的权限呀长度呀等等各类标志了,这样段寄存器这样的设计就硬生生变得有用起来了,指向一张段表记录这些标志型的数据。

  段的长度是能够改变的,咱们先假定它大小固定这样好说明,假如我内存一共能容纳10个段,而后我硬盘能容纳1000个段,我段表就记录我内存中的这10个段对应着硬盘中的哪段数据。而后呢我编程的时候,地址范围写成硬盘那样大,而后有个专门的硬件mmu用来把我程序中的地址经过查表翻译成内存中的地址,若是没有,那就把硬盘中的那个地址的数据放在内存中,最终翻译成的仍是内存中的地址。这里就用到了虚拟地址的概念,我程序中的地址是虚拟地址,帮我查表翻译娜硬盘到内存的装置叫MMU,查的表叫作段表,最终翻译成的内存地址是物理地址。用段的方式来管理这个虚拟内存就叫作段式内存管理。就酱。

  段式的管理好处是长度可变比较灵活,很差的地方是你好比你A段和C段原来是B段,大小是1000,如今不用了这个地方挪出来了,你新来个从硬盘调来的数据,大小是1001,是否是很膈应人。看着放进去正好却恰恰差一个,因而乎你只能把整个C段往前娜。要是心来个数据是900,其余地方都放不下只能放在这,那剩下的100就很尴尬。这个尴尬的100就叫作内存碎片。根据角度不一样,若是你说这个100是因为那个900挤进来了剩下的空间,那就叫内部碎片,若是你说这个100过小新来的程序放不进来,那就叫外部碎片。这块我以为经过段式叫外部页式叫内部很差,但愿你们来讨论下。那为了解决这个问题就有了页式管理,页就是个概念而已,你愿意叫他不变的段也行。硬盘被分为固定大小的物理页,操做系统逻辑上页分为了同等数量同等大小的逻辑页,而后一样有个叫页表的东西记录了逻辑页和物理页的对应关系,而后和段同样当请求的一个页不在页表中,准确说是页表中标志了这个页不在内存,那就把硬盘中的页调进来,这个过程就叫缺页中断。这个页式固然页有好有坏,而后又有个和稀泥的办法就是段页式管理。一句话,怎么的都行,如今操做系统基本都是页,完事。

  再说说进程的部分,哎不说了,这块是在连入门都不算,彻底不懂就不bb了,留在下一篇吧。

  其实上面从某个地方忽然就从计算机组成原理的画风转成操做系统了,下面简单说说操做系统为啥出现。固然一开始那个cpu和外设已经能够作想作的任何事了,你彻底能够纯手工的方式去把内存中的一块区域一个个地写入代码嘛,而后程序计数器搞一个初值,电一通跑起来。但这太恶心了,因而有了卡片机,再来个卡片机读入的程序事先写好,这样你就不用手工操做内存了,你制做好卡片就好,反正是方便了一点,但本质同样。这叫作手工操做系统,也能够叫没有操做系统。后来发现即便是彻底相同的工做,仍然须要每次取出纸片再放进去,好比有两个卡片1和2,有个程序是执行1122121这种顺序,那就须要一我的来来回回放纸,这不科学。因而有了批处理操做系统,人能够事先把1和2加载到内存,而后弄一个c卡片来负责调用这个1和2卡片,这就是调度程序,也能够叫监督程序。这就叫作批处理操做系统。再后来能够交替执行多个任务,一个任务遇到io操做就切换,这叫多道程序系统。但一个做业扔进去以后就不受用户管了,没有交互,因而有了分时操做系统,能够有多个终端使用cpu经过命令的方式并获得响应。但若是某些特别操做须要马上相应可能就无法作到,因而经过引入中断和严格的中断时间控制作到了实时操做系统。再后来就是咱们如今的操做系统啦。其实对这些叫法和定义我并非特别理解,因此你能够发现我讲的其实挺乱的,这里也但愿有大佬给个好的解释。

  今天就写到这吧,想着这些天好像学了不少东西,但本身写出来发现把全部肚子里东西吐出来就这么点,没什么系统就是想到哪写到哪能串的尽可能串一下了,工做之余偷偷溜走写的。再有这里面的地址呀数据呀好多就是举例,不是准确的值,为了方便理解而已,主要我也不肯意花时间搞个准确的放在这样一篇随便写的文章,之后会把某些地方具体拿出来说。但愿各位大佬给出批评指正或者吐槽探讨,感激涕零!  

相关文章
相关标签/搜索