UC头条:程序是怎么跑起来的(上)
CPU
单元前问题:
程序是什么:指示计算机每一步动作的一组指令
程序是由什么组成的:指令和数据(例如在C语言中:printf(“helloworld”);中,printf是指令,helloworld是数据)
什么是机器语言:CPU可以直接识别并直接使用的语言
正在运行的程序存储在什么位置:内存(硬盘和磁盘等媒介上保存的程序被复制到内存后才能执行)
什么是内存地址:内存中,用来表示命令和数据存储位置的数值(内存中保存命令和数据的场所,通过地址来标记和指定。地址由整数值表示)
计算机的构成元件中,负责程序的解释和运行的是哪个:CPU
CPU的内部结构解析
CPU所负责的就是解释和运行最终转化成机器语言的程序内容
CPU和内存是由许多晶体管组成的电子部件。通常成为IC(集成电路)
CPU包括:
寄存器:用来暂存指令,数据等处理对象。可当作内存的一种。一个CPU有20-100个寄存器。
控制器:把内存上的指令,数据读入寄存器。并根据指令结构控制整个计算机。
运算器:运算从内存读入寄存器的数据。
时钟:发出CPU开始计时的时钟信号。有的计算机的时钟位于CPU的外部。
控制是什么
其实所谓的控制就是指数据运算以外的处理(主要是数据输入输出的时机控制机制)。比如内存和磁盘等媒介的输入输出、键盘和鼠标的输入,显示器和打印机的输出等,这些都是控制的内容。
CPU是寄存器的集合
汇编
汇编语言用助记符来编写程序。每一个原本是电器信号的机器语言指令会有一个与其对应的助记符,助记符通常为指令功能的英语单词的简写。例如(mov:数据存储,add:相加)
汇编语言基本上和机器语言是一一对应的。
汇编:讲汇编语言编写的程序转化为机器语言的过程
反汇编:把机器语言转化为汇编语言程序的过程。
编译
讲使用的高级编程语言编写成的程序转化为机器语言的过程,其中用于转化的程序被称为编译器。
寄存器
CPU四个组成中,程序员只需要了解寄存器即可,其余三个不用太过关注。因为程序是把寄存器当作对象来描述的。对于程序员来说,CPU是具有各种功能的寄存器的集合体
内存的存储场所通过地址编号来区分,而寄存器的种类则通过名字来区分。例如eax:累加寄存器,ebp:基址寄存器
根据功能:可以将寄存器大致划分为8类。寄存器中既可以是指令,也可以是数据。其中数据分为:用于运算的数值,表示内存地址的数值。
一般程序计数器,累加寄存器,标值寄存器,指令寄存器和栈寄存器都只有一个。其他寄存器一般都多个
决定程序流程的程序计数器
说明
实际上,一个命令和数据通常被存储在多个地址上。
流程:操作系统把程序从硬盘复制到内存——将程序计数器设置为0100——开始运行——CPU每执行一条指令,程序计数器的值就增加1
(当执行的指令占据多个内存地址时,增加与指令长度相应的数值,)
CPU控制器会参照程序计数器的数值,从内存中读取命令并执行
条件分支和循环结构
程序流程
顺序执行:按照地址内容的顺序执行指令
条件分支:根据条件执行任意地址的指令
循环:只重复执行同一地址的指令
(若程序中存在条件分支和循环,机器语言指令就可以将程序计数器的值设置为任意地址。不是+1)。
标志寄存器
无论当前累加寄存器的运算结果是负数,零还是正数,标值寄存器都会将其保存(也负责存放溢出和奇偶校验的结构)
CPU运算时,标志寄存器的数值会根据运算结果自动设定。
CPU比较机制
实际上就是在CPU内部做减法运算
函数调用机制
即使是高级语言编写的程序,函数调用处理也是通过把程序计数器的值设定成函数的存储地址来实现的。
函数调用与循环和分支不同的地方是:单纯的跳转指令无法实现函数的调用。函数的调用需要完成函数内部的处理后,处理流程返回到函数调用点。
call指令
函数调用使用的是call指令,而不是跳转指令。在将函数的入口地址设定到程序计数器之前,call指令会把调用函数后要执行的指令地址存储在名为栈的主存内。
return指令
函数处理完毕后,再通过函数的出口来执行return命令。Return命令的功能是把保存在栈中的地址设定到程序计数器中。
通过地址和索引实现数组
变址寄存器、基址寄存器
例如查看10000000地址~1000FFFF地址时,可以将10000000存入基址寻址器,并使变址寄存器的值在00000000~0000FFFF变化。CPU会把基址寄存器+变址寄存器的值解释为实际查看的内存地址。变址寄存器的值就相当于高级编程语言程序中数组的索引功能。
CPU的处理其实很简单
CPU可以进行的处理非常少,所以尽管高级编程语言编写的程序看起来非常负责,但CPU实际处理的事情就是这么简单
数据是用二进制表示的
用二进制数表示计算机信息的原因
计算机内部是由IC这种电子部件构成的。IC(IntegratedCircuit)集成电路,IC分为模拟IC和数字IC,这里讲的是数字电路
IC的所有引脚,只有直流电压0V或5V两个状态。(即IC的一个引脚只能表示两个状态)IC的这个特性决定了计算机的信息数据只能用二进制来处理。
IC的一个引脚表示二进制数的1位
对于用二进制表示的信息,计算机不会区分它是数值、文字,还是某种图片的模式,而是根据编写程序的各位对计算机发出的指示来进行信息的处理。
二进制数
略
移位运算和乘除运算的关系
移位运算能替代乘法运算和除法运算
略
补码
计算机做减法运算时,其实是内部在做加法运算
为了实现这个功能,就要将负数用正数来表示,类似于时钟拨一圈,搞出个补码概念
逻辑右移和算数右移的区别
当二进制表示图形模式而非数值时,移位后需要在最高位补0.类似于霓虹灯往右滚动的效果,这就是逻辑右移。
当二进制数作为带符号的数值进行运算时,移位后要在最高位填充移位前符号位的值,这就是算术右移。
符号填充
略
逻辑运算
逻辑非(NOT运算)
逻辑与(AND运算):同时为1时结果为1,其他情况都为0
逻辑或(OR运算):至少有一方为1时,运算结果为1
逻辑异或(XOR运算):排斥相同数值的运算。两个数值不同,运算结果为1
计算机进行小数运算时出错的原因
将0.1累加100次也得不到10
用二进制表示小数
略
计算机运算出错的原因
因为有一些十进制的小数无法转化成二进制数(例如:0.1)
例如二进制小数点后面四位表示的数
数值范围为0.0000~0.1111。因此,这里只能表示0.5,0.25,0.125,0.0625这四个二进制小数点后面的位权组合组成的小数。
浮点数
由来
像1011.0011这样的带小数点的表现形式,完全是纸面上二进制数表现行式,在计算机内部是无法使用的。
浮点数:用符号,尾数,基数和指数这四个部分来表示的小数。计算机基数为2,故只用符号、尾数、指数这三个部分即可表示浮点数
正则表达式
尾数部分使用正则表达式,可以将表现形式多样的浮点数统一为一种表现行式。二进制中使用的是:“将小数点前面的值固定为1的正则表达式”(由于第一位必须是1,因此,省略该部分后就省略了一个数据位,从而也就可以表示更多的数据范围)
EXCESS系统
指数部分中使用的EXCESS系统:是为了表示负数时不使用符号位
含义:通过将指数部分表示范围的中间值设置为0,使得负数不需要用符号来表示
如何避免计算机计算出错
回避
根据程序目的不同,有时一些微小的偏差不会造成什么问题
将小数转化为整数
进行整数运算只要不超过可处理的数值范围,就一定不会出现问题
比如0.1相加100倍可以转换为1相加100倍再除以10
BCD编码
用4位来表示0-9的一位数字
在设计财务计算等不允许出现误差的情况,一定要将小数转化成整数或者BCD方法,以确保最终得到准确的数值
二进制数和十六进制数
二进制位数太多的话,看起来比较麻烦,故有时会采用十六进制代替2进制
内存
单元前问题
高级编程语言中的数据类型表示的是什么:占据内存区域的大小和存储在该区域的数据类型
在32位内存地址的环境中,指针变量的长度是多少位:32位
与物理内存有着相同构造的数组的数据类型长度是多少:1字节(物理内存是以字节为单位进行数据存储的)
用LIFO方式进行数据读写的数据结构成为什么:栈(LastInFirstOut)
根据数据的大小链表分叉成两个方向的数据结构称为什么:二叉查找树
内存的物理机制很简单
内存实际上是一种名为内存IC的电子元件
(包含DRAM,SRAM,ROM等多种形式)
内存IC中有电源、地址信号、数据信号、控制信号等用于输入输出的大量引脚(IC的引脚),通过为其指定地址来进行数据的读写)
点击加载图片
上图表示的内存IC中,VCC和GND是电源,A0-A9是地址信号的引脚,D0-D7是数字信号的引脚,RD和WR是控制信号的引脚。
含义:数据引脚有8个,表示一次可以输入输出8位。此外,地址信号引脚有10个,表示可以指定0000000000-1111111111共1024个地址。而地址表示数据的存储场所,因此我们可以得出这个内存可以存储1024个字节的数据。所以该内存IC的容量就是1KB。
读写过程
点击加载图片
点击加载图片
内存的逻辑模型是楼房
假设存储为低字节序(小端模式):将多字节数据的低位字节存储在内存低位地址的方式
点击加载图片
点击加载图片
简单的指针
指针
指针:是一种变量。表示的不是数据的值,而是存储着数据的内存的地址。
指针长度:在32的机器上,指针变量的长度是32位,64位上为了兼容也设置为32位
各种数据类型指针含义
目的是从指针存储的地址中一次能读写的数据的字节数
点击加载图片
点击加载图片
数组是高效使用内存的基础
数组的索引和内存地址的变换工作是由编译器自动实现的
数组是使用内存的基本,因为数组和内存的物理构造是一样的
点击加载图片
栈、队列以及环形缓存区
略
链表使元素的追加和删除更容易
略
二叉查找树使数据结构搜索更有效
略
内存和磁盘的亲密关系
单元前问题
通过使用内存来提高磁盘访问速度的机制称为什么:
DiskCache(磁盘缓存)(把从磁盘中读出来的数据存储在主存中,当该数据再次被读时,不是从磁盘而是直接从内存中高速读取)
把磁盘的一部分作为假象内存来使用的机制称为什么:
虚拟内存(VirtualMemory)(借助虚拟内存,哪怕时内存容量不足的计算机,也能运行很大的程序)
Windows中,在程序运行时,存储着可以动态加载调用的函数和数据的文件称为什么:
DLL(DLL文件)(DynamicLinkLiabrary)
在EXE程序文件中,静态加载函数的方式称为什么:
静态链接
内存利用电流来实现存储,磁盘使用磁效应实现存储
通常内存高速高价,磁盘低速低价
不读入内存就无法运行
磁盘中存储的程序,必须加载到内存后才能运行。在磁盘中保存的原始程序是无法直接运行的。因为,负责解析和运行程序内容的CPU,需要通过内部程序计数器来指定内存地址,然后才能读出程序,即使CPU能直接读出来并运行磁盘中保存的程序,但是这样速度会很慢
磁盘缓存加快了磁盘访问速度
现在效果没有之前那么明显
把低速设备的数据保存在高速设备上,需要时可以直接将其从高速设备中读出,这种在其他情况下也会用到:例如在Web浏览器中使用,会把比如图片缓存在磁盘上
点击加载图片
虚拟内存把磁盘当作部分内存来使用
虚拟内存:把磁盘的一部分当作假想的内存来使用
借助虚拟内存,在内存不足时也可以运行程序。
(例如在只剩下5M内存空间情况下也能运行10M大小的程序(但是CPU只能执行加载到内存中的程序,虚拟内存虽说把磁盘作为内存的一部分来使用,但实际上正在运行的程序部分,在这个时间点上时必须存在在内存中的,也就是说,为了实现虚拟内存,就必须把实际内存的内容,和磁盘上的虚拟内存上的内容进行部分置换,并同时运行程序))
Windows提供了虚拟内存机制作为操作系统。
虚拟内存方法有两种:分页式,分段式。Windows采用的是分页式。大小为4KB
为了实现虚拟内存的功能,Windows在磁盘上提供了虚拟内存用的文件,页文件(pagefile)。该文件由Windows自动做成管理。文件的大小也就是虚拟内存的大小,通常是实际内存的相同程度至两倍程度。(通过Windows控制面板可以查看或改变当前虚拟内存的设定)
点击加载图片
节约内存的编程方法
虚拟内存无法从根本上解决内存不足的问题。
虚拟内存确实能避免因内存不足导致的应用无法启动,不过由于低速的访问,整个过程会变得迟钝。虚拟内存无法从根本上解决内存不足的问题。
解决方法
增加内存容量
把运行的应用文件变小
把应用程序变小的方法
一、通过DLL文件实现函数共有
DLL(DynamicLinkLibrary)文件,程序运行时可以动态加载Library(函数和数据的集合)的文件,且多个应用可以共有同一个DLL文件。通过共用同一个DLL文件就可以达到节约内存的效果
Windows操作系统本身也是多个DLL文件的集合。又是有时安装新应用时,DLL文件也会被追加。应用则会通过利用这些DLL文件的功能来运行。
DLL文件还有一个优点就是,在不变更EXE文件的情况下,只通过升级DLL文件就可以更新
点击加载图片
点击加载图片
二、通过调用_stdcall来减小程序文件的大小
_stdcall是standardcall(标准调用)
Windows提供的DLL文件内的函数,基本上都是_stdcal调用方式
C语言特有的调用方式称为C调用。C语言之所以默认不使用_stdcall,是因为C语言所对应的函数的传入参数是可变的(可以设定任意参数),只有函数调用方才能知道到底有多少个参数,而在这种情况下,栈的清理作业便无法进行。不过在C语言中,如果函数的参数数量固定的话,指定_stdcall时没有任何问题的
函数调用时,需要用到栈,当函数调用完成后,栈需要清除。(栈清理处理:把不需要的数据从接收和传递函数的参数时使用的内存上的栈区域中清理出去。该命令不是程序记述的,而是在程序编译时由编译器自动附加到程序中的。编译器默认将该处理附加在函数的调用方。)
C语言中,函数的返回值是通过寄存器,而非栈,来返回的
栈清除处理,比起在调用方进行,在反复被调用的函数一方进行时,程序整体要小一些。这是使用的就是_stdcall,就可以把栈清理处理变为被调用函数一方进行,intMyFunc(inta,intb)部分写成int_stdcallMyFunc(inta,intb),进行编译后,add,esp,8同样的处理就会在函数MyFunc一方执行。
点击加载图片
点击加载图片
磁盘的物理结构
磁盘的物理结构是指磁盘存储数的形式。磁盘通过把物理表面划分为多个空间来使用的
划分方式:扇区方式(划分成固定长度的空间),可变长方式(把磁盘划分为长度可变的空间)
磁道:扇区方式中,把磁盘表面分成若干个同心圆的空间
扇区:把磁道按照固定大小(能存储的数据长度相同)划分而成的空间就是扇区,是对磁盘进行读写的最小的单位
Windows中使用的磁盘,一般一个扇区为512字节
不过在逻辑方面,对磁盘进行读一些的单位是扇区整数倍的簇。根据磁盘的容量不同,1簇可以是512(1扇区)字节,可以是1KB(2扇区),可以是2KB。。。磁盘的容量越大,簇的容量也越大。
在软盘中,1簇=512字节=一扇区,簇和扇区的大小是相等的。
不管是硬盘还是软盘,不同的文件时不能存储在同一个簇中的,否则就会导致只有一方的文件不能被删除。因此,不管多么小的文件,都会占用1簇的空间。这样,所有的文件都会栈用1簇的整数倍的磁盘空间。