代码的背后(《深入理解计算机系统》总结一)

目前我是看到第一部分的前三章,所以只说说前三章的内容。

基于计算机硬件的设备,现在几乎在我们周边随处可见,手机,电脑,电视,汽车等等,可以说现在的设备,如果不嵌入块芯片,那就不能算作设备,一来他上档次,因为他看起来比较智能,用起来也很智能,二来他的确给产品赋予了很多的能力,让它能够完成更多的任务。而这些都是基于处理器或者说是芯片,或者说是一块板子起的作用,当然他是怎么起的作用或许要我们的电气相关或者至少是学过数电模电的人来说哈吧,要我说,我也是无言乱语一顿吹逼,说完一看,相当于啥都没说。而我能说的就是基于这些硬件上的逻辑设计,也就是我们的软件部分。

任何硬件要能够运行一段程序,都要有一定的基础,也就是必须要有能够知道这些代码是要干让机器干什么的基础,这个基础就是操作系统,操作系统提供了代码的输入保存,编译,执行等功能,可以说正是有了操作系统,才有了程序猿的世界,才有了现在的各种app软件。而操作系统面对的就是硬件,而要弄清楚我们的程序是如何操作我们的机器,就必须要先理解操作系统是怎么将我们的代码转换为机器能够识别的信息的。

从操作系统最开始说起,可能大家都知道,最开始的对硬件的操作,都是通过0101这种二进制数据进行操作,他直接对应的是高电平和低电平,在硬件上面的作用就是控制电流的开关,而最开始的程序也就是这种0101的代码,在要在机器上运行,只需要用打孔纸或者其他东西直接控制电路的开关,到后来有了储存设备,这些物理代码就转换为了真正的电子设备,而代码的运行也是直接将这些数据读出来再运行就可以了,这样的效率十分的慢,并且代码的量也很大,往往一段基本的加减法运算就可能要写上万行的代码,后来随着人们对基本的操作进行封装,形成了基本的指令集后,代码的量才开始减少,这个时候计算机操作系统才开始发展起来。可以说,计算机操作系统,就是在0101的基础上,对基本的指令的封装。

那么,操作系统又为我们封装了那些基本操作指令?首先是基本的算法指令,然后就是基本的读写指令,基本的寻址功能,逻辑运算指令,这些指令是计算机系统的基础,通过这些指令,我们可以改变和移动内存中的值,这样就可以完成一段程序的执行,软件就是对信息进行处理的,而这些信息就是出存在储存设备中的。

而在很早以前,或者在现在的一些比较低端的设备上,计算机对信息的储存结构是非常简单的,大部分可能就是一块闪存或者ram,机器指令直接对整个储存进行读写数据,但随着数据量的增加,同时要求我们能够同时处理更多的数据,通过反复读写内存的数据实际上已经远远低于了处理器的处理速度,而针对这样的优化,就只能从内存模型上着手,首先是加入了多级缓存,将经常用到的数据直接放到快速缓存中,运算单元可以直接从缓存中将数据快速读入到操作寄存器中,这样就加快了程序的执行效率,在加入多级缓存的同时,也加入了更先进的数据总线控制,以及增加了其他的读写核心,将一些纯数据复制的功能独立成一块芯片,而增加更多的运算时间。从这里我们就可以看出,现代计算机系统,大概就分为处理器,多级缓存,总线,内存,其他挂载点。

说完计算机操作系统的基本构成,再说说我们的代码是如何运行的,以及他是怎么操作内存的,在说到编程语言之前,我认为每个程序猿都只少要了解下汇编语言对我们的让我们的操作系统做了什么,因为汇编语言提供给我们相对直观的信息而又不像二进制程序那种难懂的信息,而几乎所有的高级语言,都是基于汇编语言进行操作的,当然我们可以看到,不同的操作系统厂商对汇编语言的定义是不太相同的,但他们进行的操作是一样的。汇编语言实际上就是对我们的地址进行操作,就我目前从书上了解到的信息来说,汇编语言,直接对寄存器,内存进行操作,可能还会对其他芯片发送一些指令。所以,基于汇编语言的高级代码,同样的也是对地址进行操作。

计算机系统的地址,在没有人为定义之前,地址是没有长度和值的二进制中的0,并且是没有指向和范围的,任何指令都可以对任意位置的值进行修改,这样操作就相当的不规范,很容易出现数据覆盖和遗漏,所以操作系统对基本的地址进行了封装,一般将8个位作为操作的基本单元,虽然现在的操作系统一次性能够读取和写入64位,但这并不影响8位的数据格式,同时根据不同的操作系统,每个寄存器不是8个字节,就是4个字节,寄存器是直接机器指令操作的直接地址,所以寄存器的值范围就是和计算机的一次性操作位一致的。所以如果我们写程序,最好也要考虑到,一个值是否能够占满8个字节,这样就可以不用读更小的内存地址值了。大部分的时候,指令是直接从指令或者寄存器中读取值,但如果要从内存中读值,则需要在寄存器中保存这个值的地址,这就是地址操作的起源,所有的c的指针操作,以及java对象操作,python语言的容器和方法指针传递都是通过这种操作完成的,将一个值的地址放到寄存器中,然后将这个寄存器的值复制给另外一个地址上的寄存器对应的变量。

说完处理器的寄存器操作,然后再来说说寄存器的存值操作,对于一些数据结构,比如数组,结构体,连接体等,操作系统会开辟一段连续的空间用来储存这段值,而针对每个下标的值,就是将寄存器中的地址加上或减去一定的偏移量得到的,这样做的好处就是能够快速的访问到内存或者寄存器中的值,而不用过度的偏移地址读值,一个不好的地方就是,如果出现溢出,就可能污染后面的空间,当然这点操作系统已经做出了优化,以防止我们将错误下标的值存入到内存中。总的来说,寄存器的存值操作就是通过在内存中开辟一段空间,将值复制到对应的空间。

对于一段程序,执行的过程中,必然会有很多的方法调用和返回,一段代码有时间并不是完全线性的无跳转的执行的,而为了实现这中功能,在操作系统中,又定义了一个存储结构—栈,栈的作用就是用来模拟方法的调用和返回的,每当我们调用一个方法,他就会将上一个方法的返回地址等压入到栈中,同时保存一些当前执行方法的信息,每调用一个方法都会进行这样的操作,而每遇到返回指令,都会弹出返回信息,并跳到先前的位置,栈的后进先出完全模拟了方法调用的数据使用和指令执行过程。栈中的返回信息是内存溢出最危险的地方,因为一旦我们通过不规范的手段,覆盖了这段地址,就可能导致程序跳往其他地方执行,这也是早期黑客的手段。

说是总结,实际上只是个人的回想,十分抱歉,说的不够精炼。另外,可是文艺圈,哈哈哈。

你可能感兴趣的:(代码的背后(《深入理解计算机系统》总结一))