程序:指示计算机每一步动作的一组指令
程序 = 指令 + 数据
机器语言:CPU可以直接识别并使用的语言
正在运行的程序存贮在计算机的内存
内存地址:内存中,用来表示命令和数据存储位置的数值
计算机的构成原件中,CPU负责程序的解释和运行
CPU
Central Processing Unit 中央处理器
-
CPU的内部结构
寄存器:暂存指令、数据等处理对象,一般一个CPU有20~100个寄存器;
控制器:负责将内存上的指令、数据等读入寄存器,并根据指令的执行结果来控制整个计算机(控制器就是控制除了数据运算以外的处理,例如内存和磁盘等媒体介质的输入输出、键盘和鼠标的输入、显示器和打印机的输出等);
运算器:运算从内存读入寄存器的数据;
时钟:发出CPU开始计时的时钟信号。
程序运行流程:1)程序员用C语言等高级语言编写程序;2)将程序编译后转换成机器语言的EXE文件;3)程序运行时,在内存中生成EXE文件的副本;4)CPU解释并执行程序内容。
程序启动后,根据时钟信号,控制器会从内存中读取指令和数据。通过对这些指令加以解释和运行,运算器就会对数据进行运算,控制器根据该运算结果来控制计算机。
-
CPU是寄存器的集合体
程序员了解CPU的寄存器即可,因为程序是把寄存器作为对象来描述的。(程序员眼中的CPU是:程序计数器+标志寄存器+累加寄存器+基址寄存器+变址寄存器+通用寄存器,注:后三个都不止一个)
汇编语言 assembly :是一种机器语言的助记符(memonic)。
CPU处理程序:使用高级语言编写的程序会在编译后转化成机器语言,然后再通过CPU内部的寄存器来处理。
寄存器中存储的内容可以是:指令和数据(分为用于运算的数值和表示内存地址的数值)。
-
程序流程:程序计数器
操作系统operating system :管理和控制计算机硬件和软件资源的计算机程序。
程序计数器决定着程序的流程。
-
条件分支和循环机制
程序的流程:顺序执行、条件分支、循环
一位 bit = binary digit:就是一个位数的二进制数,表示0或1的数值。32位CPU指的就是用32位的二进制数来表示数据及地址的数值。
-
函数调用机制
在编译高级编程语言的程序后,函数调用的处理会转换成call指令,函数结束的处理则会转换成return指令。函数调用使用的是call指令,而不是跳转指令,在将函数的入口地址设定到程序计数器之前,call指令会把调用函数后要执行的指令地址存储在名为栈的贮存内。函数处理完毕后,再通过函数的出口来执行return命令。
栈 stack:表示不断地存储各种数据的内存区域,函数调用后之所以能正确地返回调用前的地址,就是栈的功劳。
-
通过地址和索引实现数组
实际地址 = 基址寄存器的值(固定) +变址寄存器的值(相当于高级编程语言程序中数组的索引功能)
-
CPU处理的功能
及其语言指令的主要类型和功能:
32位是四个字节
二进制数01011100转换成十进制是92
二进制数00001111左移两位后,会变成原数的4倍(2*2)
补码形式表示的8位二进制数11111111,用十进制数表示的话是-1
补码形式表示的8位二进制数10101010,用16位的二进制数表示的话是1111111110101010(使用原数的最高位1来填充)
反转部分图形模式时,使用的是XOR逻辑运算符
二进制
-
用二进制数表示计算机信息的原因
计算机内部是由IC电子部件构成的(ICIntegrated Circuit 集成电路,有模拟IC和数字IC两种),CPU(微处理器)和内存也是IC的一种,IC的所有引脚只有直流电压0V或5V两个状态,IC的这种特征,决定了计算机的信息数据只能用二进制数来处理。
8位二进制数被称为一个字节,字节是最基本的信息计量单位,位是最小单位,字节是基本单位(内存和磁盘都使用字节单位来存储和读写数据,使用位单位则无法读写数据,因此,字节是信息的基本单位)。字节 bite “咬下的一口”。
-
二进制数
二进制数的值转换成十进制数的值,只需将二进制数的各数位的值和位权相乘,然后将相乘的结果相加即可。(使用2的幂次方来记忆)
-
移位运算和乘除运算(溢出的位忽略)
<< 左移 >> 右移
几位就是2的几次幂方 几位就是2的几次幂方分之一
空出来的右边的数补0 *****
-
补数:便于计算机处理
二进制数中表示负数值时,一般会把最高位作为符号来使用,因此将最高位称为符号位,符号位是0时表示正数,1时表示负数。
计算机在做减法运算时,实际上内部是在做加法运算,用假发运算来实现减法运算。因此,在表示负数时,就要使用二进制的补数,补数就是用正数来表示负数。
补数就是取反加1.
例如:1-1 其实就是1+(-1),于是有00000001+?=0,这里?是酱紫的
首先1就是00000001,于是取反后有,11111110,加一后,11111111
最后11111111+00000001=100000000,去掉1,就是0了。
再例如:3-5就是00000011+?= -2
5是000000101,取反11111010,加一11111011
就是00000011+11111011=11111110,结果11111110取反00000001,加一00000010,即时2,加上原来的符号位 -2 。
-
逻辑右移和算术右移
左移只需要将右边缺省的补0即可,右移的话有补0和补1两种情况。
逻辑右移:例如霓虹灯右移的情况,最高位补0.
算术右移:根据符号位来补,若是正数的话补0,负数的话补1。
符号扩充:只需用符号位的值来填充高位即可。
-
逻辑运算
计算机处理的运算,大体可以分为算术运算(加减乘除)和逻辑运算(对二进制数各字位的0和1分别进行处理的运算,有逻辑非NOT、逻辑与AND、逻辑或OR、逻辑异或XOR)两种。
二进制数0.1,用十进制表示的话是:0.5。
用小数点后三位的二进制数,能表示十进制数0.625吗? Y 。
将小数分为符号、尾数、基数、指数4部分进行表现的形式为:浮点数形式。
二进制数的基数是2。
通过把0作为数值范围的中间值,从而在不使用符号位的情况下来表示负数的表示方法称为EXCESS系统表现(EXCESS是“剩余”的意思,例如把01111111看作是0的话,比这个数小1的011111110的数就是-1)。
二进制小数分析
-
将0.1累加100次也得不到10
看代码:
-
用二进制数表示小数
二进制数小数转换成十进制数的方法:将各数位的数值和位权相乘的结果相加。(位权:是用来与各数字位的数字相乘的数值)
-
计算机运算出错的原因
有一些十进制数的小数无法转换成二进制。
例如:小数点后4位用二进制数表示时的数值范围为0.0000~0.1111,因此只能表示0.5、0.25、0.125、0.0625这四个二进制数小数后面的位权组合而成的小数。
(四)浮点数
像1011.1101这样带小数点的表现形式,只是纸面上的二进制数表现形式,在计算机内部是无法正常使用的。
双精度浮点数类型用64位、单精度浮点数类型用32位来表示全体小数。
浮点数是指用符号、尾数、基数和指数这四部分来表示的小数。
+m*n^e (+或者-的符号、m尾数、n基数、e指数)
64位的双精度浮点数 = 1位符号 + 11位指数 + 52位尾数 。
32位的单精度浮点数 = 1位符号 + 8位指数 + 23位尾数。
符号部分是指使用一个数据位来表示数值的符号,1表示为负,0表示为正。
尾数部分用的是“将小数点前面的值固定为1的正则表达式”。
指数部分用的是“EXCESS系统表现”。
小数就是 “尾数部分*2的指数部分次幂”这样的形式来表现的。
-
正则表达式和EXCESS系统
尾数部分使用正则表达式(按照特定的规则来表示数据的形式即为正则表达式),我们使用的是“将小数点钱买你的值固定为的正则表达式”。
EXCESS系统表现是指,通过将指数部分表示范围的中间值设为0,使得附属不需要用符号来表示。
-
例子:用C语言实现32位浮点数的表现
看代码:
#include<stdio.h>
#include<string.h>
int main()
{
float data;
unsignedlong buff;
int i;
char s[34];
data = (float)0.75;
memcpy(&buff,&data,4);
//把数据复制到4字节长度的整数变量buff中逐个提取出每一位
for( i = 33 ; i >= 0 ; i--)
{
if(i == 1 || i == 10)
{
s[i] = '-';
}
else
{
if(buff % 2 == 1)
{
s[i] = '1';
}
else
{
s[i] = '0';
}
buff /= 2;
}
}
s[34] = '\0';
printf("%s\n",s);
}
结果:
验证?自己算
-
避免计算机出错的策略
-
回避:在计算的误差范围之内,无视这些错误。
-
把小数换成整数来计算。
除此之外,还有个BCD(Binary CodedDecimal)也是一种使用二进制表示十进制的方法,简单来说,BCD就是用4位来表示0~9的1位数字的处理办法。
-
二进制数和十六进制数
0x加在数字之前,就表示为十六进制数。
4位二进制数表示为1位十六进制数,缺省用0补,十六进制数的表示范围是0~9和A~F。
有十个地址信息引脚的内存IC(集成电路)可以指定的地址范围是
0000000000~1111111111(十进制表示为0~1023)。
高级编程语言中的数据类型表示:占据内存区域的大小和存储在该内存区域的数据类型。
在32位内存地址的环境中,指针变量的长度是32位。(指针指的是用于存储内存地址的变量)
与物理内存有着相同构造的数组的数据类型长度是:1字节。(物理内存是以字节为数据单位进行存储的)
用LIFO方式进行数据读写的数据结构称为:栈。(Last In First Out,后进先出的数据结构是栈)
根据数据的大小链表分叉成两个方向的数据结构称为:二叉树。(二叉查找树 binary search tree 指的是从节点分为两个叉的树状数据结构)
内存
计算机是进行数据处理的设备,而程序表示的就是处理顺序和数据结构。物理上的认识(硬件)和逻辑上的认识(软件)。
使用内存、实现栈处理、链表处理、二叉查找树处理等,数组是进行这些处理的基础。
-
内存的物理机制
内存实际上是一种名为内存IC的电子元件。内存IC中有电源、地址信号、数据信号、控制信号等用于输入输出的大量引脚(IC引脚),通过为其指定地址(address)来进行数据的读写。
ROM (Read OnlyMemory)是一种只能用来读取的内存。
RAM (RandomAccess Memory)是可被读取和写入的内存,分为需要经常refresh刷新以保存数据的DRAM (Dynamic RAM),以及不需要刷新电路即能保存数据的SRAM (Static RAM)。
一下两个图分别是RAM内存引脚、读写的状态。
图1的说明:大多数情况下,+5v的直流电压表示1,0v表示0。
数据信号引脚有D0~D7八个,表示一次可以输入输出8位(=1字节)的数据。
地址引脚有A0~A9共十个,表示可以指定0000000000~1111111111共1024个地址,而地址用来表示数据的存储场所,因此我们可以得出这个内存IC中可以存储1024个1字节的数据。1024 = 1 K ,1000 = 1 k。
图2的说明:控制信号 RD为ReaD ,WR为WRite。
详细看图。
-
内存的逻辑模型是楼房
编程语言中的数据类型表示存储的是何种类型的数据。
在C语言中,32位CPU中的int 是32位的,以前的16位的CPU是16位的,8字节(= 64位)的double类型是最大的,char类型是最小的,占一个字节。
低字节序 little endian:将多字节数据的地位字节存储在内存地位地址的方式,反之,高字节序就是把数据的高位字节存储在内存低位的方式。
-
简单的指针
指针也是一种变量,它所表示的不是数据的值,而是存储着数据的内存的地址。通过使用指针,可以对任意地址的数据进行读写。
在程序中,通过明确标记数据类型来记述变量的过程称为定义变量。例如 short a; ,就表示定义了2个字节的short类型的变量a。变量定义后据可依进行读写了。
指针的数据类型表示一次可以读写的长度。例如:
char *a; short *e; long *f;
分别表示占据1字节、2字节、4字节长度;若d、e、f都是100,那么存储的分别为:
100 100,0 100,0,0,0
-
数组是高效实用内存的基础----数组和内存的物理构造是一样的
数组是指多个同样数据类型的数据在内存中来内需排列的形式。(这个数据类型也表示一次能够读写的内存大小)
作为数组元素的各个数据会通过连续的编号(索引 index)被区分开来。指定索引后,就可以对该索引所对应地址的内存尽心读写操作(CPU是通过利用基址寄存器和变址寄存器来指定内存地址的)。而索引和内存地址的变换工作则是由编译器自动实现的。
使用数组能够使编程工作变得更加高效,特别是在loop循环处理中。
-
栈 队列 环形缓冲区
栈和队列都可以不通过指定地址和索引来对数组的元素进行读写。需要临时保存计算过程中的数据、连接在计算机上的设备后者输入输出的数据时,都可以通过这些方法来使用内存。
在对内存数据进行读写时,栈用的是LIFO,队列用的是FIFO。如果我们在内存中预留出栈和队列所需要的空间,并确定好写入和读出的顺序,就不用再指定地址和索引了。
栈:Push()函数入栈,Pop()函数出栈。
队列:EnQueue()函数进入队列,DeQueue()出队列。
队列一般是以环状缓冲区(ringbuffer)的方式来实现的,见图:
-
链表使元素的追加和删除更容易
链表和二叉查找树都是不用考虑索引的顺序就可以对数组元素进行读写的方式。通过使用链表,可以更加高效地对数组数据(元素)进行追加和删除处理,而通过使用二叉查找树,可以更加高效地对数组数据进行检索。
在数组的各个元素中,除了数据的值之外,通过为其附带上下一个元素的索引,即可实现链表。数据的值和下一个元素的索引组合在一起,就构成了数组的一个元素。(详见C++数据结构与算法一书)
-
二叉查找树使数据搜索更有效
二叉查找树是指在链表的基础上往数组中追加元素时,考虑到数据的大小关系,将其分成左右两个方向的表现形式。使用二叉查找树的便利之处在于可以使数据的搜索等更有效率。在使用一般数组时,必须从数组的开头按照索引顺序来查找目标数据。而使用二叉查找树时,当目标数据比现在都出来的数据小时就可以转到左侧,反之目标数据较大时即可转到链表的右侧,这样就加快了找到目标数据的速度。
存储程序方式:在存储程序中保存程序,并逐一运行的方式。
通过使用内存来提高磁盘访问速度的机制称为Disk Cache(磁盘缓存:把从磁盘中读取的数据存储在内存中,当该数据再次被读取时,不是从磁盘而是直接从内存中高速读出)。
把磁盘的一部分作为假想内存来使用的机制称为虚拟内存(virtual memory),借助虚拟内存,哪怕是内存容量不足的计算机,也可以运行很大的程序。
Windows中,在程序运行时,存储着可以动态加载调用的函数和数据的文件称为:DLL文件(Dynamic Link Library)。
在EXE程序文件中,静态加载函数的方式称为:静态链接。函数的加载方式有静态链接和动态链接两种。
在windows计算机中,一般磁盘的1个扇区是512字节。(扇区是磁盘保存数据的物理单位)
磁盘
从都具有存储程序命令和数据这点来看,内存和磁盘的功能是相同的。在计算机的5大部件(输入装置、输出装置、存储器、运算器、控制器)中,内存和磁盘也都被归纳为存储部件。不过,利用电流来实现存储的内存,同利用磁效应实现存储的磁盘,还是有差异的。从存储容量来看,内存是高速高价,而磁盘是低速廉价的。在计算机这个系统中,高速小容量的内存和低速高容量的磁盘进行协同作业。
-
不读入内存就无法运行
磁盘中的程序加载到内存中,然后再通过CPU来运行(此时程序在内存中)。
存储程序方式(程序内置方式):程序保存在存储设备中,通过有序地被读取出来实现运行。这种存储程序方式的提出是里程碑式的,因为在以前的以前,程序都是通过改变计算机的布线等来变更程序的。
计算机中主要的存储部件是内存和磁盘。磁盘中存储的程序,必须要加载到内存后才能运行。在磁盘中保存的原始程序是无法直接运行的。(这是因为,负责解析和运行程序内容的CPU,需要通过内部程序计数器来指定内存地址,然后才能读出程序。即使CPU可以直接读出并运行磁盘中保存的程序,由于磁盘读取速度慢,程序的运行速度还是会降低)
-
磁盘缓存加快了磁盘访问速度
Disk Cache磁盘缓存:把磁盘中读出的数据存储到内存空间中的方式(cache 缓存,是高速缓存、仓库的意思)。当接下来需要读取同意数据时,就不用通过实际的磁盘,而是从磁盘缓存中把内容读出。使用磁盘缓存大大改善磁盘数据的访问速度。
把低速设备的数据保存在高速设备中,需要时可以直接将其从高速设备中读出,这种缓存的方式在其他情况下也会用到。Web浏览器就可以把获取的数据暂时保存在磁盘中,然后再需要时再显示磁盘中的数据。
-
虚拟内存把磁盘作为部分内存来使用
虚拟内存 virtual memory:把磁盘的一部分作为假想的内存来使用。这与磁盘缓存是假想的磁盘(实际上是内存)相对,虚拟内存是假想的内存(实际上是磁盘)。
通过借助虚拟内存,在内存不足时也可以运行程序。但是cpu只能执行加载到内存中的数据,虚拟内储虽说是把磁盘作为内存的一部分来使用,但实际上正在运行的程序的部分,在这个时间点上必须存在在内存中。为了实现虚拟内储存,就必须把实际内存(物理内存)的内容,和磁盘上的虚拟内存的内容进行部分置换(swap),并同时运行程序。
虚拟内存的方法有分页式和分段式两种;分段式虚拟内存是指,把要运行的程序分割成以处理集合及数据集合等为单位的段落,然后再以分割后的段落为单位在内存和磁盘之间进行数据置换。Windows系统采用的是分页式,即为在不考虑程序构造的情况下,把运行的程序按照一定大小的页(page)进行分割,并以页为单位在内存和磁盘间进行置换。
-
节约内存的编程方法
像windows这样,窗口的菜单及图标等都可以进行可视化操作的方式称为图形用户界面(GUI, Graphical User Inerface),windows的前身是MS-DOS操作系统,是由键盘输入命令来进行操作的CLI(命令行界面),即使是512M的内存,也会不足。为了从根本上解决内存不足的问题,需要增加内存的容量,或者尽量把运行的应用文件变小。
-
通过DLL问价实现函数共有
DLL(Dynamic LinkLibrary)文件,是在程序运行时可以动态加载Library(函数和数据的集合)的文件,多个应用可以共用一个DLL文件,通过共用一个DLL文件则可以达到节约内存的效果。优点:节约内存;在不变更EXE文件时,只通过升级DLL文件就可以更新。
-
通过调用_stdcall来减小程序文件的大小
_stdcall是 standardcall(标准调用)的略称,Windows提供的DLL文件内的函数,基本上都是_stdcall调用方式,这主要是为了节约内存,另一方面,用C语言编写的程序内的函数,默认设置都不是_stdcall,因为C语言所对应的函数的传入参数是可变的,如果函数的参数固定的话,指定_stdcall是没有任何问题的。
C语言中,在调用函数后,需要执行栈清理处理指令。栈清理处理:把不需要的数据从接受和传递函数的参数时使用的内存上的栈区域中清理出去。该命令不是程序记述的,而是在程序编译时由编译器自动附加到程序中的,编译器默认将该处理附加在函数调用方。
C语言通过栈来传递函数的参数。栈清理处理,比起在函数调用方进行,在反复被调用的函数一方进行时,程序整体要小些。
例如:
//函数调用方
void main()
{
int a;
a = MyFunc(123,456);
}
//被调用方
int MyFunc(inta,int b)
{
……
}
-
磁盘的物理结构
磁盘的物理结构是指磁盘存储数据的形式。磁盘是通过把其物理表面划分成多个空间来使用的。划分的方式有扇区方式(将磁盘划分为固定长度的空间)和可变长方式(把磁盘划分为长度可变的空间)两种。
扇区是对磁盘进行物理读写的最小单位,Windows中使用的磁盘,一般1扇区是512字节。Windows在逻辑方面(软件方面)对磁盘进行读写的单位是扇区整数倍簇,1簇可以是1KB(512字节)也可以是N KB(N是2的幂次方的值)。
软、硬盘中,不同文件是不能存储在同一个簇中的,否则就会导致只有一方的文件不能被删除。因此,不管是多么小的文件,都会占用1簇的空间。以簇为单位进行读写时,1簇中没有填满的区域会保持不被使用的状态。
文件储存的基本单位是:1字节(8位)。
文件内容用“数据的值*循环次数”来表示的压缩方法是RLE算法还是哈夫曼算法:是RLE算法,例如AAABBBB压缩后就是A3B4.
在Windows计算机经常使用的SHIFTJIS字符编码中,半角英文数字是用1字节来表示的,汉字等全角字符是用2个字节来表示的。
BMP格式的图像文件是没有被压缩的,因此要比JPEG格式等压缩过的图像文件大不少。
可逆压缩:压缩后的数据能复原;非可逆压缩:无法复原压缩前的数据。像照片用JPEG格式,压缩后也不会让人感到不自然,是因为使用了非可逆压缩。
压缩数据
-
文件以字节单位保存
文件是将数据存储在磁盘等存储媒介中的一种形式。文件就是字节数据的集合。在任何情况下,无论是文本文件还是图像文件,文件中的字节数据都是连续存储的。
-
RLE算法的机制
把文件内容用“数据*重复次数”的形式来表示的压缩方法称为RLE(Run Length Encoding,行程长度编码)算法。RLE算法是一种很好的压缩方法,经常被用于压缩传真的图像等。因为图像文件本质上也是字节数据的集合体,所以可以用RLE算法来压缩。
-
RLE算法的缺点
在实际的文本文件中,同样字符多次重复出现的情况并不多见。
-
通过莫尔斯编码来看哈夫曼算法的基础
哈夫曼算法的关键在于“多次出现的数据用小于8位的字节数来表示,不常用的数据则可以用超过8位的字节数来表示”。但是要注意,无论是否满8位的数据,最终都是以8位为单位保存到文件中(磁盘是以字节为单位来保存数据的),为了实现这个,需要在编程上花功夫。
-
哈夫曼算法
哈夫曼算法是指,为各压缩对象文件分别构造最佳的编码体系,并以该编码体系为基础来进行压缩。因此,用什么样式的编码(哈夫曼编码)对数据进行分割,就要由各个文件而定。用哈夫曼算法压缩过的文件中,存储着哈夫曼编码信息和压缩过的数据。
-
哈夫曼算法能够大幅提升压缩比率
使用哈夫曼树后,出现频率越高的数据所占用的数据位数就越少,而且数据的区分也可以很清晰地实现。
-
可逆压缩和非可逆压缩
文本文档是可逆压缩,图像文件格式分多种。
-
BMP (Bitmap)是使用Windows自带的画笔来做成的一种图像文件,是完全未压缩的,由于显示器及打印机输出的bit(点)是可以直接俄映射的(mapping),因此BMP=Bitmap。
-
GIF (Graphics InterchangeFormat)这种数据格式要求色数不超过256色。是可逆压缩,但是256色数的限制使得数据有损。
-
JPEG (Joint PhotographicExperts Group)是数码相机等常用的一种图像数据形式,是非可逆压缩。(三种压缩方式:i.把构成图像的点阵的颜色信息由RGB形式转换成YCbCr(亮度、蓝色色度、红色色度)形式。 ii.将每个点的色素变化看作是波形的信号变化,进行傅里叶变化。Iii.将已经瘦身的图像数据通过哈夫曼算法进行压缩。)详见wiki。
应用的运行环境:操作系统和计算机本身(硬件)的种类。
不同的硬件种类需要不同的操作系统。所以,Macintosh用的操作系统(MacOS),在AT 兼容机上不能运行。
应用是为了在特定操作系统上运行而做成的。所以,Windows上的应用,在MacOS上不能运行。
Java虚拟机的功能是:运行Java应用的字节代码。
程序的运行环境
-
运行环境=操作系统+硬件
同一类型的硬件可以选择安装多种操作系统。从程序的运行环境这一角度考量硬件时,CPU的种类是特别重要的参数。CPU只能解释其自身固有的机器语言。不同的CPU能解释的机器语言的种类也是不同的。
机器语言的程序称为本地代码(nativecode)。程序员用C语言等编写的程序是源代码,在编写阶段仅仅是文本文件,它是可以在任何环境下显示和编辑的。通过对源代码的百衲衣,就可以得到本地代码。
-
Windows克服了CPU意外的硬件差异
计算机的硬件不仅仅是CPU,还有用于存储程序指令和数据的内存,以及通过I/O连接的键盘、显示器、硬盘、打印机等外围设备。
但是windows也无法吸收CPU类型的差异,因为windows应用软件都是用特定的CPU的本地代码来完成的。
-
不同操作系统的API不同
同样机型的计算机,可安装的操作系统类型会有多种选择。例如,AT兼容机的情况下,除Windows之外,还可以采用Unix系列的Linux等多个操作系统。应用软件则必须根据不同的操作系统类型来专门开发。CPU的类型不同,所对应的机器语言也不同,同理,操作系统的类型不同,应用程序向操作系统传递指令的途径也是不同的。
应用程序向操作系统传递指令的途径称为API(ApplicationProgramming Interface)。Windows及Unix系列操作系统的API,提供了任何应用程序都可以利用的函数组合。因为不同操作系统的API是有差异的,因此,将同样的应用程序移植到其他操作系统时,就必须要重写应用中利用到API的部分。像键盘输入、鼠标输入、显示器输出、文件输入输出等同外围设备进行输入输出操作的功能,都是通过API提供的。
在同类型操作系统下,不管硬件如何,API基本上没有差别。针对某特定操作系统的API所编写的程序,在任何硬件上都可以运行。当然,由于CPU种类不同,机器语言也不相同,因此本地代码也是不同的。因此,需要利用能够生成各CPU专用的本地代码的编译器,来对源代码进行重新编译(本地代码的运行环境是由操作系统和硬件来决定的)。
(四)既然CPU类型不同会导致同样的本地代码无法重复利用,那么可以直接把源代码分发给程序,部分Unix系列操作系统对此有应用。
(五)利用虚拟机获得其他操作系统环境
虚拟机软件,可以实现即使不通过移植,也可以运行其他操作系统的应用。
(六)提供相同环境的Java虚拟机
Java,有两层意思,一是作为编程语言的Java(将java语法记述的源代码编译后运行),另一个是作为程序运行环境的Java。不过,编译后生成的并不是特定CPU使用的本地代码,而是名为字节代码的程序;字节代码的运行环境称为Java虚拟机(JavaVM , Java Virtual Machine),java虚拟机一边把java字节代码逐一转换成本地代码一边运行。
例如,在使用用于AT兼容机的Java编译器和Java虚拟机的情况下,编译器会将程序员编写的源代码(sample.java)转换成字节代码(sample.class)。而java虚拟机(java.exe)则会把字节代码变换成叉86系列CPU适用的本地代码,然后由叉86系列CPU负责实际的处理。
Java虚拟机的缺点:一是,不同的java虚拟 及之间无法进行完整互换;二是运行速度问题。
(七)BIOS和引导
程序的运行环境中,存在着名为BIOS(Basic Input/Output System)的系统。BIOS存储在ROM中,是预先内置在计算机主机内部的程序。BIOS除了键盘、磁盘、显卡等基本控制程序外,还有启动“引导程序”(是存储在启动驱动器其实区域的小程序)的功能。
通过编译得到的本地代码(及其语言代码)是CPU可以解析和运行的程序形式。
链接器将多个目标文件结合生成EXE文件。(通过编译和链接得到EXE文件)
扩展名为.obj的目标文件的内容是本地代码。
链接器会从库文件中抽出必要的目标文件将其结合到EXE文件中。此外,还存在一种程序运行时结合的DLL形式的库文件。
仅包含Windows的DLL文件中存储的函数信息的文件称为导入库。把导入库信息结合到EXE文件中,这样程序在运行时就可以利用DLL内的函数了。
在程序运行时,用来动态申请分配的数据和对象的内存区域形式称为堆。堆的内存空间会根据程序的命令进行申请及释放。
编译链接
-
计算机只能运行本地代码
用某种编程语言编写的程序就称为源代码,保存源代码的文件称为源文件。源代码是无法直接运行的,CPU能直接解析并运行的不是源代码而是本地代码的程序。本地(native)有“母语”的意思,对CPU来说母语就是机器语言,而转换成机器语言的程序就是本地代码。
-
本地代码的内容
将本地代码Dump一下,就可以看到代码的数据形式。
-
编译器负责转换源代码
编译器是能够把C语言等高级编程语言编写的源代码转换成本地代码的程序。
编译器首先读入代码的内容,然后再把源代码转换成本地代码。有相应的对应表,要进行语法解析、语句解析、语义解析等,才能生成本地代码。
根据CPU类型的不同,本地代码的类型也不同。因而,编译器不仅和编程语言的种类有关,和CPU的类型也是相关的。
总结:1.不同编程语言需要不同的编译器2.编译器生成的本地代码随CPU的不同而又异3.编译器的运行环境不同(编译器也是软件)。
-
仅靠编译时无法得到可执行文件的
编译器转换源代码后就会生成本地文件。本地文件是无法直接运行的,还要在链接后才可以。
-
启动及库文件
程序的启动:记述同所有程序起始位置相结合的处理内容的目标文件。因此,即使陈旭不调用其它目标文件的函数,也必须要进行链接,并和启动结合起来。
库文件:把多个目标文件集成保存到一个文件中的形式。链接器指定库文件后,就会从中把需要的目标文件抽取出来,并同其他目标文件结合生成EXE文件。
标准函数:不是通过源代码新上市而是通过库文件形式和编译器一起提供的函数。通过以目标文件的形式或集合多个目标文件的库文件形式来提供函数,就可以不用公开标准函数的源代码内容。
-
DLL文件及导入库
Windows中,API的目标文件,并不是存储在通常的库文件中,而是存储在名为DLL(Dynamic Link Library)文件的特殊文件中。
存储着目标文件的实体,并直接和EXE文件结合的库文件形式称为静态链接库。
-
可执行文件运行时的必要条件
EXE文件是作为单独的文件存储子硬盘中的。本地代码在对程序中记述的变量进行读写时,是参照数据存储的内存地址来运行命令的。在调试函数时,程序的处理流程就会跳转到存储着函数处理内容的内存地址上。在类似于windows操作系统这样的可以加载多个可执行程序的运行环境中,每次运行时,程序内的变量及函数被分配到的内存地址都是不同的。那么,在EXE文件中,变量和函数的内存地址的值,需要再配置信息(EXE文件中给变量及函数分配了虚拟的内存地址。在程序运行的时候虚拟的内存地址就会转换成实际的内存地址。链接器会在EXE文件的开头,追加转化内存地址所需的必要信息,这些信息就是再配置信息)来处理。
EXE文件的再配置信息,就成了变量和函数的相对地址。
-
程序加载时会生成栈和堆
不管是什么程序,程序的内容都是由处理和数据构成的。大多数编程语言都是用函数来表示处理,用变量来表示数据。
EXE文件的内容:再配置信息、变量组、函数组。除此之外,程序加载到内存后,还有栈和堆,栈:是用来存储函数内部临时使用的变量(局部变量),以及函数调用时所用的参数的内存区域。堆:是用来存储程序运行时的任意数据及对象的区域领域。
EXE文件中并不存在栈及堆的组。栈和堆需要的内存空间是在EXE文件加载到内存后开始运行时得到分配的。
栈和堆:同:他们的内存空间都是在程序运行时得到申请分配的。
异:内存的使用方法上,栈是由编译器自动实现的,堆是需要程序员自己编写的。
内存泄漏 memory leak:C语言通过malloc()函数申请分配、free()函数释放;C++语言是new运算符来申请分配、delete运算符释放的。无论是C还是C++,如果没有在程序中明确释放堆的内存空间,那么即使在处理完毕后,该内储存空间仍会一直残留。
Q and A
监控程序的主要功能是:程序的加载和运行,监控程序也可以说是操作系统的原型。
在操作系统上运行的程序称为应用。
调用操作系统功能称为系统调用 system call,应用通过系统调用间接控制硬件。
GUI ,Graphical User Interface(用户图形界面)。
WYSIWYG,What You See Is WhatYou Get,是windows的特征之一。
操作系统
-
操作系统功能的历史
操作系统 OperatingSystem:基础软件,是计算机运行时不可或缺的控制程序,以及在控制程序下运转的为其他软件运行提供操作环境的软件的统称。
监控程序(监控程序的功能:加载程序、运行程序)是操作系统的原型。
初期的操作系统:监控程序+基本的输入(通过键盘)输出(输出到显示器上)程序。
操作系统是多个程序的集合体。
-
控制程序:硬件控制、程序运行控制。。。。。。
-
编程语言处理器:汇编、编译、解析。。。。。。
-
使用程序:文本编辑器、调试工具、Dump程序。。。。。。
-
意识到操作系统
在操作系统这个运行环境下,应用并不是直接控制硬件,而是通过操作系统来间接控制硬件的。
-
系统调用和高级编程语言的移植性
操作系统的硬件控制功能,通常是通过一些小的函数集合体的形式来提供的。这些函数及调用函数的行为统称为系统调用。
C语言等高级编程语言并不依赖于特定的操作系统,高级语言的编程机制是,使用独自的函数名,然后再在编译时将其转换成相应操作系统的系统调用。
-
操作系统和高级编程语言使硬件抽象化
通过使用操作系统提供的系统调用,程序员就没必要编写直接控制硬件的程序了。而且,通过使用高级编程语言,有时无需考虑系统调用的存在。
-
windows操作系统的特征
1.32位OR 64位操作系统
2.通过API函数集来提供系统调用
3.提供采用了图形用户界面的用户界面
4.通过WYSIWYG实现答应输出
5.提供多任务功能
6.提供网络功能及数据库功能
7.通过即插即用实现设备驱动的自动设定
本地代码的指令中,表示其功能的英语缩写称为:助记符(汇编语言是通过利用助记符来记述程序的)。
汇编语言的源代码转换成本地代码的方式称为:汇编(使用汇编器这个工具来进行汇编)。
本地代码转换成汇编语言的源代码的方式称为:反汇编(通过反汇编,得到人们可以理解的代码)。
汇编语言的源文件的扩展名,通常的格式是:.asm。.asm是assembler汇编器的缩写。
汇编语言程序中的段定义指的是:构成程序的命令和数据的集合组。(在高级编程语言的源代码中,即使指令和数据在编写时是分散的,编译后也会在段定义中集合汇总起来)
汇编语言的跳转指令,是将程序流程跳转到其他地址时需要用到的指令。(在汇编语言中,通过跳转指令,可以实现循环和条件分支)
汇编语言
-
汇编语言和本地代码是一一对应的
计算机CPU能直接解释运行的只有本地代码(机器语言)程序。
例如,在加法运算的本地代码中加上dd(addition)、在比较运算的本地代码中加上cmp(compare)等,这些缩写称为助记符,使用助记符的编程语言称为汇编语言。
即使是汇编语言编写的源代码,最终也必须要转换成本地代码才能运行。负责转换工作的程序称为汇编器,转换这一处理本身称为汇编。
用汇编语言编写的源代码和本地代码是一一对应的。因而,本地代码也可以反过来转换成汇编语言的源代码。持有该功能的逆变换程序称为反汇编程序,逆变换这一处理本身称为反汇编。
总结:本地代码->汇编语言的源代码 是 汇编
汇编语言的源代码->本地代码 是反汇编
-
通过编译器输出汇编语言的源代码
获取汇编语言的源代码的方法:对本地代码进行汇编、通过编译器对C语言代码转换成汇编源代码。
-
不会转换成本地代码的伪指令
汇编语言的源代码,是由转换成本地代码的指令(操作码)和针对汇编器的伪指令构成的。
伪指令负责把程序的构造及汇编的方法指示给汇编器(转换程序)。伪指令本身是无法汇编转换成本地代码的。
-
段定义:由伪指令segment和ends围起来的部分,是给构成程序的命令和数据的集合体加上一个名字而得到的。Segment有区域的意思。
-
过程:伪指令proc和endp围起来的部分,表示的是procedure的范围。在汇编语言中,这种相当于C语言的函数的形式称为过程。
-
汇编语言的语法是“操作码+操作数”
操作码表示的是指令动作,操作数表示的是指令对象。被转换成CPU可以直接解析运行的二进制的操作码和操作数,就是本地代码。
能够使用何种形式的操作码是由CPU的种类决定的。操作数中指定了寄存器名、内储存地址、常数等。
本地代码加载到内存后才能运行。内存中存储着构成本地代码的指令和数据。程序运行时,CPU会从内存中把指令和数据读出,然后再将其存储在CPU内部的寄存器中进行处理。
寄存器是CPU中的存储区域。寄存器并不仅仅具有存储指令和数据的功能,也有运算功能。
-
mov指令
指令中最常用的是对寄存器和内存进行数据存储的mov指令。mov指令的两个操作数,分别用来指定数据的存储地和读出源。操作数中可以指定寄存器、常数、标签(附加在地址前),以及用方括号([])围起来的这些内容。如果指定了没有用方括号围起来的内容,就表示对该值进行处理;如果指定了用方括号围起来的内容,方括号中的值则会被解释为内存地址。
-
对栈进行push 和 pop
程序运行时,会在内存上申请分配一个称为栈的数据空间。Stack有“干草堆积如山”的意思,数据在存储时是从内存的下层(大的地址编号)逐渐往上层(小的地址编号)积累,读出时则是按照从上往下的顺序进行的。
栈是存储临时数据的区域,它的特点是通过push指令和pop指令进行数据的存储和读出。32位叉86系列的CPU中,进行1次push或pop,即可处理4字节的数据。
后续关于汇编语言及其程序的运行略。(函数调用机制、函数内部的处理、始终确保全局变量的内存空间、临时确保局部变量的内存空间、循环处理的实现方法、条件分支的实现方法、了解程序运行方式的必要性)
在汇编语言中,是用IN指令和OUT指令来同外围设备进行输入输出操作的。
I/O是:Input/Output。(用来实现计算机主机和外围设备输入输出交互的IC称为I/O控制器或简称为I/O。)
用来识别外围设备的编号是;I/O地址或称为I/O端口号。(所有连接到计算机的外围设备都会分配一个I/O地址编号)
IRQ是:InterruptReuest。(IRQ指的是用来执行硬件中断请求的编号)
DMA是DirectMemory Access的缩写,指的是不经过CPU中介处理,外围设备直接同计算机的主内存进行数据传输。
用来识别具有DMA功能的外围设备的编号称为:DMA编号。(像磁盘这样用来处理大量数据的外围设备都具有DMA功能)
硬件控制
-
应用和程序无关
控制CPU,只需要把编译器或汇编器生成的本地代码加载到主内存并运行,那么程序是怎么控制CPU和主内存以外的硬件呢?
-
支持硬件输入输出的IN指令和OUT指令
Windows控制硬件时借助的是输入输出指令。其中的IN指令和OUT指令是代表性的,也是汇编语言的助记符。
IN指令通过指定端口号的端口输入数据,并将其存储在CPU内部的寄存器中。OUT指令则是把CPU寄存器中存储的数据,输出到指定端口号的端口。
计算机主机中,附带了用来连接显示器及键盘等外围设备的链接器。而各链接器的内部,都连接有用来交换计算机主机同外围设备之间电流特性的IC。这些IC,统称为I/O控制器。由于电压不同,数字信号和模拟信号的电流特性也不同,计算机主机和外围设备是无法直接连接的。I/O控制器就是解决这个问题的。
I/O控制器中有用于临时保存输入输出数据的内存。这个内存就是端口 port。I/O控制器内部的内存,也称为寄存器。虽然都是寄存器,但它同CPU内部的寄存器在功能上是不同的;CPU内部的寄存器是用来进行数据运算处理的,而I/O寄存器则主要用来临时存储数据的。
在实现I/O控制器功能的IC中,会有多个端口。一个I/O控制器既可以控制一个外围设备,也可以控制多个外围设备。各端口之间通过端口号进行区分。端口号也成为I/O地址。
-
外围设备的中断请求
IRQ InterruptRequest,是中断请求的意思。IRQ是用来暂停当前正在运行的程序,并跳转到其他程序运行的必要机制。该机制称为中断处理。实施中断请求的是连接外围设备的I/O控制器,负责实施中断处理程序的是CPU。为了进行区分,外围设备的中断请求会使用不同于I/O端口的其他编号,该编号称为中断编号。操作系统及BIOS(Basic Input Output System,位于计算机主板或扩张卡上内置的ROM中,里面记录了用来控制外围设备的程序和数据。)会提供响应中断编号的中断处理程序。
中断控制器,会把从多个外围设备发出的中断请求有序地传递给CPU。CPU接受到来自中断控制器的中断请求后,会把当前正在运行的主程序中断,并切换到中断处理程序。中断处理程序的第一步处理,就是把CPU所有寄存器的数值保存到内存的栈中,在中断处理程序中完成外围设备的输入输出后,把栈中保存的数值还原到CPU寄存器中,然后再继续进行对主程序的处理。
-
用中断来实现实时处理
-
DMA可以实现短时间传送大量数据
DMA,DirectMemory Access,DMA是指在不通过CPU的情况下,外设直接和主内存进行传送数据。磁盘等多用,因为CPU作为中介的时间被节省了,所以传送的快。
-
文字及图片的显示机制
显示器中显示的信息一直存储在某内存中。该内存称为VRAM(video RAM),在程序中,只要往VRAM中写入数据,该数据就会在显示器中显示出来,实现该功能的程序,是由操作系统或BIOS提供,并借助中断来进行处理的。
在现在的计算机中,显卡等专用的硬件中一般都配置有与主内存相独立的VRAM和GPU(Graphics Processing Unit,图形处理器,也称图形芯片)。
总结:
用软件来控制硬件听起来很难,但实际上只是利用输入输出指令同外围设备进行输入输出的处理而已。中断处理是根据需要来使用的选项功能,DMA则直接交给对应的外围折别即可。虽然计算机新技术涌现,但是计算机能处理的事情,始终只是对输入的数据进行运算,并把结果输出,这一点是不会发生变化的。不管程序内容是什么,最终都是数据的输入输出和运算。
用计算机进行的模拟试验称为计算机模拟。(计算机模拟是指用软件来进行实际试验)
伪随机数:通过公式产生的伪随机数。
随机数的种子:生成伪随机数的公式中使用的参数。
作为计算机大脑的CPU,其本身并不具有思考功能。
计算机有记忆功能,磁盘及内存等有记忆的功能。
AIArtificial Intelligence是“人工智能”的意思。
计算机的思考
-
作为工具的程序和为了思考的程序
程序就如同是由计算机执行的各种指令罗列起来的文章。计算机内部的CPU,通过对该文章的内容进行解析和运行,来控制连接到计算机的各种外围设备。控制就是指CPU和各种设备之间配合进行数据的输入输出处理。
(二)程序生成随机数的方法、活用记忆功能以达到更接近人类的判断、用程序来表示人类的思考方式略。