《程序是怎样跑起来的》笔记
主要参考文章:https://blog.youkuaiyun.com/u014222687/article/category/6295175
第1章.对程序员来说CPU是什么
1:程序是什么?
指令序列,指示计算机每一步动作的一组指令。
2:程序是由什么组成的?
指令和数据,程序是指令和数据的组合体。
3:什么是机器语言?
CPU可以直接识别和使用的语言,CPU能够直接识别和执行的只有机器语言。
4:正在运行的程序存储在什么位置?
内存,硬盘等媒介上保存的程序被复制到内存后才能运行。
5:什么是内存地址?
内存中,用来表示命令和数据存储位置的数值。内存是保存命令和数据的场所,通过地址来标记和指定。地址由整数值表示。
附注:通过地址寻找,地址其实也是抽象概念,找到一个地方,称之为原点或零点,然后寻址就是,从原点开始转过的角度,间隔的距离。
6:计算机的构成元件中,负责程序的解释和运行的是哪个?
CPU(中央处理器(Central Processing Unit))。计算机的构成元件中,根据程序的指令来进行数据运算,并控制整个计算机的设备称作CPU。
内存:通常说的内存指的是计算机的主存储器,简称主存。主存通过控制芯片等与CPU相连,主要负责存储指令和数据,主存由可读写的元素构成,每个字节都带有一个地址编码。CPU可以通过地址读取主存中的指令和数据,当然也可以写入数据。
CPU与内存:CPU是用来计算处理信息的,而信息来自电脑的硬盘。一条信息要经过硬盘,内存,三级缓存,二级缓存,一级缓存才能进入CPU内部,再经过里面的存储单元,最后被CPU处理。以上的路径信息提取速度逐渐增加。内存如果大的话,CPU单位时间内处理的信息量就多。所以内存和CPU之间有一点小关系的。
程序运行流程:
CPU的组成:
运行过程:程序启动后,根据时钟信号,控制器会从内存中读取指令和数据。通过对这些指令加以解释和运行,运算器就会对数据进行运算,控制器根据运算结果来控制计算机,处理的场所在寄存器内。(也就是程序开始时,时钟开始调用时,指令数据存储近寄存器,运算器对寄存器进行运算,通过控制器输入输出,这是正常的简单处理,其余还有)
其中CPU主要的就是各种寄存器的集合体。
程序计数器是用于存放下一条指令所在单元的地址的地方。
上面是顺序执行,程序的流程还有条件分支、循环和函数调用的情况,条件分支是根据CPU的结果实现跳转,然后再依次顺序执行下去。循环是根据CPU的运行结果判断是否循环,循环结束后再依次执行后面的指令。函数调用是指先跳转到函数的内存地址,执行函数体指令(函数主体),函数执行完成后,返回到函数调用原点,再依次执行后面的指令。PS:这里的无论是按顺序还是实现跳转都是修改程序计数器内的地址来实现的。
函数调用:通过call和return指令实现,在将函数的入口地址设定到程序计数器前,call指令会把调用函数后要执行的指令地址存储在名为栈(stack本意为“干草堆堆积如山”)的主存内。函数处理完毕后,再通过函数的出口来执行return命令。return命令的功能是把保存在栈中的地址设定到程序计数器中。
附注:数组的起始地址是装在基址寄存器,而索引(偏移值)装在变址寄存器中。
内存/CPU/寄存器/程序计数器关系:
第2章.数据是用二进制数据表示的
1:32位是几个字节?
4字节:8位为一个字节,所以是4个字节。
2:二进制数0101 1100转换成十进制是多少?
0*128+1*64+0*32+1*16 + 1*8+1*4+0*2+0*0 = 4+8+16+64 = 92:将二进制各位数与位权相乘再相加即可。
3:二进制数0000 1111左移两位后,会变成原数的几倍?
4被:因基数为2,左移1位即为原值两倍,所以左移2位为原值4倍。
4:补码形式表示的8位二进制数1111 1111,用十进制数表示的话是多少?
-1:因+1为1 0000 0000,9位,溢出一位,即为0,所以为-1;1111 1111的补数为求反加1,为0000 0001,因此补码表示的1111 1111为-1。
5:补码形式表示的8位二进制数1010 1010,用16位的二进制数表示的话是多少?
前面加8个1。此为符号扩充,符号扩充就是指在保持值不变的情况下将其转换成16位和32位的二进制数。不管是正数还是用补数表示的负数,都只需要用符号位的值(0或者1)填充高位即可。这就是符号扩充的方法。
6:数值、字符串和图像等信息在计算机内部是以什么形式表现的?
都是以二进制数值的形式来表现的。
位是最小单位,字节是基本单位。内存和磁盘都使用字节单位来存储和读写数据,使用位单位则无法读写数据。因此字节是信息最基本的单位。
二进制数的值转化成十进制数的值,只需要将二进制数的各位的值和位权相乘,然后将相乘的结果相加即可。十进制的基数为10,二进制的基数为2。
二进制数中表示负数值时,一般会把最高位作为符号来使用,因此我们把这个最高位称为符号位。符号位是0时表示正数,符号位是1时表示负数。
补码:用正数来表示负数。
补数求解的变换方法就是“取反+1”。例如1的二进制时00000001,因此-1就是先取反11111110,然后加1,就是11111111,所以1的补码就是11111111,也就是-1就是11111111。
再举一例,11111110表示的负数是多少?我们可以利用负负得正这个性质。假若11111110是负A,那么11111110的补数就是正A,11111110的补数就是00000010,是2,所以11111110是-2。
为什么要加1呢:因为任意一个数与它本身的位取反相加必定得到最大值,即每一位都是1。所以负数定义为取反加1,这样正负数相加就会刚好溢出,回到0的状态。
右移位运算分为逻辑右移与算术右移。移动后最高位补0称为逻辑右移动。右移后最高位补原本最高位的值称为算术右移,也就是原来是正数补0,原来是负数补1,负数右移后依然是负数,正数右移后依然是正数。
符号扩充:不管是正数还是用补数表示的负数,都只需用符号位的值(0或者1)填充高位即可。比如将0111111这个正的8位二进制数转换成16位二进制数时,是0000000001111111。11111111转成16位二进制数是1111111111111111。
第3章:计算机进行小数运算时出错的原因
计算机计算出错的原因:有一些十进制的小数无法转换成二进制数,就像下面这样,小数点后4位用二进制数表示时的数值范围0.0000-0.1111。能表示的数也只有下面几种,所以无法表示全部的十进制的小数。实际上,十进制数0.1转换成二进制后,会变成0.00011001100…(1100循环)这样的循环小数。
很多编程语言都提供两种表示小数的数据类型。分别是双精度浮点数和单精度浮点数(double、float)。双精度浮点说类型用64位、单精度浮点数用32位来表示全体小数。
浮点数是指用符号、尾数、基数和指数四部分表示的小数。
双精度浮点数:
符号部分[1位]指数部分[11位]尾数部分[52位];
单精度浮点数:
符号部分[1位]指数部分[8位]尾数部分[23位]。
尾数部分用的是“将小数前面的值固定为1的正则表达式”。
指数部分用的则是“EXCESS系统表现”。
第4章:熟练使用有棱有角的内存
1:有十个地址信号引脚的内存IC可以指定的地址范围是多少?
能够表示2的十次冪个数据,十进制表示的话就是0-1023.
2:高级编程语言中的数据类型表示的是什么?
不仅仅表示存储在内存数据的数据的类型,还表示占据内存区域的大小。
3:在32位内存地址的环境中,指针变量的长度是多少位?
32位,指针指的是用来存储内存地址的变量,因此在32位内存地址的环境中必有32位。
4:用LIFO方式进行数据读写的数据结构称为什么?
栈,LIFO=Last In First Out.
内存实际上是一种名为内存IC的电子元件。内存IC主要分为:ROM(Read Only Memory)只能用来读取的内存和RAM(Random Access Memory)可被读取和写入的内存。
RAM又可分为需要经常刷新(refresh)以保存数据的DRAM(Dynamic RAM),以及不需要刷新电路既能保存数据的SRAM(Static RAM)。
内存IC中有电源引脚、地址信号引脚、数据信号引脚、控制信号引脚等用于输入输出的大量引脚(IC的引脚)、通过为其指定地址(address),来进行数据的读写。
那么,这个内存IC中能存储多少数据呢?数据信息引脚又D0~D7共八个,表示一次可以输入输出8位(=1字节)的数据。此外,地址信号引脚有A0~A9共十个,表示可以指定00000000~11111111共1024个地址。而地址用来表示数据的存储场所,因此我们可以得出这个内存IC中可以存储1024个1字节的数据。因此1024=1K,所以该内存IC的容量就是1KB。
但是内存容量不可能只有1KB,但是一台计算机又不可能放入很多的内存IC,通常情况下,计算机使用的内存IC中会有更多的地址信号引脚,这样就能在一个内存IC中存储数十兆字节的数据。因此,只用数个内存IC,就可以达到512MB的容量。
如我们想向内存内写入数据,则有:接通电源—>用地址引脚确定存储场所—>由数据引脚输入数据—>用WR或RD引脚决定写入或者读出。像WR和RD这样可以控制IC运行的信号称为控制信号,其中WR和RD同时为0时,写入和读出的操作都无法运行。
内存可以把它假想成每层都存储着数据的楼房,例如可以这样想,内存为1KB时,表示的时如下的1024层楼房。
数据类型表示存储的何种类型的数据,从内存来看,就是占用的内存大小(占有的楼层数)的意思。即使是物理以1个字节为单位来逐一读写数据的内存,再程序中,通过指定其类型(变量的数据类型等),也能实现以特定字节数为单位进行读写。
// 定义变量
char a;
short b;
long c
这三个变量的数据类型分别是,表示1字节长度的char,表示2字节长度的short,以及表示4字节长度的long。变量的数据类型不同,所占用的内存大小也不一样,如下图所示。
根据程序中指定的变量的数据类型的不同,读写的物理地址大小也会随之变化,这其实是非常方便的。假如程序中只能逐个字节地对内存进行读写,那该多么不便啊。
理解指针地关键点就是要弄清数据类型这个概念。
指针是一种变量,他所表示的不是数据的值,而是存储着数据的内存的地址。通过使用指针,就可以对任意指定地址的数据进行读写。
-- 各种数据类型指针的定义
char *d;
short *e;
long *f;
实际上,这些数据类型表示的是从指针存储的地址中一次能够读写的数据字节数。
CPU是通过利用基址寄存器和变址寄存器来指定内存地址的。
附注:Windows计算机上使用的程序通常都是32位(4字节)的内存地址。这种情况下,指针变量的长度也是32位。为什么会这样?因为指针变量是用来存储地址信息的,如果它的容量小于系统的地址大小,那么这个指针变量的用处就不大。所以指针变量的大小是=系统地址大小。那么为什么小于系统地址的大小就用处不大呢?因为最大寻址能力。
数组是指多个同样数据类型的数据在内存中连续排列的形式。作为数据元素的各个数据会通过连续的编号被区分开来,这个编号被称为索引(index)。
指定索引后,就可以对该索引所对应地址的内存进行读写操作。而索引和内存地址的变换工作由编译器自动实现(CPU是通过基址寄存器和变址寄存器来指定内存地址的)。
-- 各种类型的数组定义
char g[100];
short h[100];
long i[100];
栈和队列,都可以不通过指定地址和索引来对数组的元素进行读写。栈用的是LIFO(Last Input First Out,后入先出)方式,而队列用的则是FIFO(First Input First Out,先入后出)方式。
二叉查找树是指在链表的基础上往数组中追加元素时,考虑到数据的大小关系,将其分成左右两个方向的表现形式。
使用二叉查找树时,当目标数据比现在读出来的数据小时就可以转到左侧,反之目标数据较大时即可转到链表的右侧,这样就加快了找到目标数据的速度。
第5章:内存和磁盘的亲密关系
1:存储程序的方式指的是什么?
在存储装置中保存程序,并逐一运行的方式。现代计算机采用的就是存储程序方式。
2:通过使用内存来提高磁盘访问速度的机制称为什么?
Disk Cache(磁盘缓存),磁盘缓存指的是,把从磁盘中读出的数据存储在内存中,当该数据再次被读取时,不是从磁盘而是从内存中直接高速读出。
3:把磁盘的一部分作为假想内存来使用的机制称为什么?
虚拟内存(virtual memory),借助虚拟内存哪怕是内存不足的计算机,也可以运行很大的程序。
4:Windows中,在程序运行时,存储着可以动态加载调用的函数和数据的文件称为什么?
DLL(DLL文件),Dynamic Link Liabrary的简称。
5:在EXE程序文件中,静态加载函数的的方式称为什么?
静态链接,函数的加载方式有静态链接和动态链接两种。
6:在Windows计算机中,一般磁盘中1个扇区是多少字节?
通常为512字节,扇区是磁盘保存数据的物理单位。
利用电流来实现存储的内存,利用磁效应来实现存储的磁盘。
存储程序方式:程序保存在存储设备中,通过有序地被读出来实现运行,这一机制称为存储程序方式。磁盘中存储的程序,必须要加载到内存后才能运行。在磁盘中保持的原始程序是无法直接运行的。(所以Windows双击打开一个程序文件就是把这个存储在硬盘上的文件加载到内存里面,然后看“任务管理器”内存消耗就变多了。结束某个程序,就是把加载到内存中的这个程序从内存中剔除。)
磁盘缓存(disk cache):磁盘缓存指的是把磁盘中读出的数据存储到内存空间中来,这样当下一次需要读取同一数据时,就不用通过实际的磁盘,而是从磁盘缓存中把内容读出。这样可以大大改善磁盘数据的访问速度。
把低速设备的数据保存到高速设备中,需要时可以直接将其从高速设备中读出,这种缓存的方式在其他情况下也会用到。其中的一个实例就是在Web服务器中使用。Web浏览器就可以把获取的数据暂时保存在磁盘中,然后在需要时再显示磁盘中的数据。也就是说,把低速网络数据保存到相对高速的磁盘中。
虚拟内存(virtual memory):虚拟内存指的是把磁盘的一部分作为假想的内存来使用。虚拟内存虽说是把磁盘作为内存的一部分来使用,但实际上正在运行的程序部分,在这个时间点上必须存在在内存中。也就是说,为了实现虚拟内存,就必须把实际内存(也称物理内存)的内容,和磁盘上的虚拟内存的内容进行部分置换(swap),并同时运行程序。
虚拟内存的方法有分页式和分段式两种。
分页式虚拟内存的机制:
节约内存的编码方法:(1)通过DLL文件实现函数共有(2)通过调用_stdcall来减小程序文件的大小。
扇区是对磁盘进行物理读写的最小单位。在Window的软件方面对磁盘进行读写的单位是扇区的整数倍簇(cu)1簇可以是1KB(两个扇区)、2KB、4KB等等。不过不同的文件不能存储在同一个簇中,否则就会导致有一方的文件不能被删除。因此不管多小的文件,都会占用1簇的空间。这样以来,所有的文件都会占用1簇的整数倍的磁盘空间。
如果减少簇的容量,磁盘访问次数就会增加,就会导致读写文件的时间变长。
磁盘是通过把其物理表面划分成多个空间来使用的。划分的方式分为扇区方式和可变长方形两种,一般使用的是扇区方式,把磁盘分为若干个同心圆的空间就是磁道。把磁盘按照固定大小(能存储的数据长度相同)划分成的空间就是扇区。