本文主要内容包括:
Ð 冯·诺依曼体系结构
Ð 汇编语言对冯·诺依曼结构的抽象
Ð 面向过程语言对冯·诺依曼结构的抽象
Ð 面向对象语言对冯·诺依曼结构的抽象
Ð 程序架构设计与冯·诺依曼结构
1.1 冯·诺依曼体系结构
1946年美籍匈牙利科学家冯·诺依曼提出存储程序原理,把程序本身当作数据来对待,程序和该程序处理的数据用同样的方式存储,并确定了存储程序计算机的五大组成部分和基本工作方法,如图1-1所示。
![](https://img-my.csdn.net/uploads/201208/30/1346260938_8217.png)
冯·诺依曼理论的要点是:数字计算机的数制采用二进制;计算机应该按照顺序执行程序。
如图1-1所示,冯·诺依曼定义了计算机的三大组成部件:
ü I/O设备:负责数据和程序的输入输出
ü 存储器:存储程序和数据
ü 处理器:分成运算器和控制器,运算器负责数据的加工处理,控制器控制程序的逻辑
注意
传统的教科书上又把冯·诺依曼理论分成五部分:输入设备、输出设备、存储器、运算器和控制器。
计算机科学的历史就是一直围绕着这三大部件,从硬件革命到软件革命的发展史。从软件革命的历史来看,计算机科学一直围绕着数据、逻辑和界面三大部分演变,数据对应着存储器、逻辑对应着处理器、界面对应着I/O设备。
1.2 汇编语言对冯·诺依曼结构的抽象
汇编语言是对冯·诺依曼结构最为直接的抽象,在汇编语言中只有三种操作对象:寄存器、存储器和I/O设备。
寄存器是处理器的主要组成部分,这些寄存器分别承担着运算器和控制器的角色,比如指令寄存器(IR,InstructionRegister)和段寄存器 (SR,Segment Register)负责程序的逻辑处理,而累加寄存器(AR,Accumulator Register)则负责程序的运算。
其中段寄存器是为了对存储器的分段管理而设置的,在16位的CPU中有四个16位段寄存器:
ü 代码段寄存器(CS,Code Segment)存放当前运行的程序代码所在段的段基址;
ü 数据段寄存处(DS,Data Segment)存放当前程序使用的数据所在段的段基址;
ü 堆栈寄存器(SS,StackSegment)存放堆栈段的段基址;
ü 附加段寄存器(ES,ExtraSegment)存放当前程序使用的附件数据段的段基址;
如果想使用汇编语言实现两个数的加法并输出十进制到屏幕,代码如下:
DATA SEGMENT
STR1 DB 'Please input the two numbers:',13,10,'$'
STR2 DB 13,10,'$'
STR3 DB 13,10,'Continue? [Y/N] ','$'
FIRST DB 40 DUP(0);为被加数分配空间
SECOND DB 40 DUP(0);为加数分配空间
SUM DB 40 DUP(0);为和分配空间
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START:
MOV AX,DATA
MOV DS,AX
LEA DX,STR1
MOV AH,09
INT 21H;显示字符串1
MOV SI,OFFSET FIRST+20;SI指向被加数地址中部
MOV BX,OFFSET SECOND+20;BX指向加数地址中部
MOV CX,00;CX初始为0
INPUT1:
MOV AH,01
INT 21H;输入字符
AND AL,0FH;转换成非组合的BCD码
MOV [SI],AL
CMP AL,0DH;用回车判断是否输入结束
JE L1;输入结束,转L1
INC SI;输入未结束,则继续输入
INC CL;用CL为被加数的位数计数
JMP INPUT1
L1:
LEA DX,STR2
MOV AH,09
INT 21H;回车换行
MOV DL,'+'
MOV AH,02
INT 21H;显示器输出字符'+'
LEA DX,STR2
MOV AH,09
INT 21H;回车换行
INPUT2: MOV AH,01
INT 21H;输入字符
AND AL,0FH;转换成非组合的BCD码
MOV [BX],AL
CMP AL,0DH;用回车判断是否输入结束
JE L2;输入结束,转L2
INC BX;输入未结束,则继续输入
INC CH;用CH为被加数的位数计数
JMP INPUT2
L2:
LEA DX,STR2
MOV AH,09
INT 21H;回车换行
MOV DL,'='
MOV AH,02
INT 21H;显示器输出字符'='
AND AX,00H;AX置0
DEC SI
DEC BX
MOV DI,OFFSET SUM;DI指向和数
CLC;CF置0
CMP CL,CH;比较被加数和加数的位数
JGE L4;如果被加数位数不小于加数位数,转L4
JL L3;如果被加数位数小于加数位数,转L3
L3:
MOV CL,CH
JMP L4
L4:
AND CH,0;置CX为被加数和加数的位数中大的
INC CX;CX加1,考虑最高位可能进位
STD;置DF为1
JMP L5
L5:
LODSB;被加数载入,SI自动递减
ADC AL,[BX];带进位的加法
AAA;十进制调整
MOV [DI],AL;将所加的数装入DI所指示的和处
INC DI;DI自增
DEC BX;BX自减
LOOP L5;循环
L6:
DEC DI
MOV DL,[DI]
CMP DL,00H;首先判断最高位是否为0
JE L7;最高位为0,转L7
JNE L8;最高位不为0,转L8
L7:
JMP L6;最高位为0,跳过
L8:
INC DI;最高位不为0,重新开始
JMP L9
L9:
DEC DI
MOV DL,[DI]
OR DL,30H;转换成ASCII码
MOV AH,02H
INT 21H;输出和数
MOV AX,OFFSET SUM;将和数的地址偏移量装入AX
CMP DI,AX;比较AX和DI,判断输出是否应结束
JE L10;输出完毕
JMP L9;输出未结束,继续输出
L10:
LEA DX,STR3
MOV AH,09
INT 21H;询问是否需要继续进行
MOV AH,01
INT 21H
CMP AL,59H;判断是否需要继续进行
JE L11
JNE L12
L11:
LEA DX,STR
MOV AH,09
INT 21H
JMP START;继续进行
L12:
MOV AH,4CH
INT 21H
CODE ENDS
END START
从上面的程序中可以看到,首先定义了数据段DATA SEGMENT并对存储器中的数据进行赋值,然后定义了代码段CODE SEGMENT,在冯·诺依曼结构中,数据段和代码段被统一放入存储器中并通过处理器的寄存器进行逻辑控制和运算,在需要通过I/O设备显示的时候调用INT中断程序输出到屏幕上。
那么就可以这样理解,汇编程序是对冯·诺依曼结构的最原始的抽象,汇编程序把数据、逻辑和显示进行了最原始的封装,使得程序员可以更方便的控制存储器、处理器和I/O设备,而不是和枯燥的二进制数据直接打交道。
1.3 面向过程语言对冯·诺依曼结构的抽象
在计算机科学的发展史上,计算机软件科学的发展围绕着这样一个趋势:如何对数据、逻辑和界面进行更深层次的抽象,使得人类可以更方便的控制存储器、处理器和I/O设备,从而极大的提供人类的生产效率。
1973年,Ritchie在开发Unix系统的过程中完成了C语言的第一个版本。
![](https://img-my.csdn.net/uploads/201208/30/1346260938_4144.jpg)
图3-1 C语言之父丹尼斯·利奇
C语言是对冯·诺依曼体系结构的最高层次的抽象,C语言让程序员可以从容的控制存储器、处理器和I/O设备,同样是计算两个数的加法,C语言的实现代码如下:
#include<stdio.h>
voidmain()
{
int a,b;
printf(“请输入两个数字:”);
scanf(“%d %d”, &a, &b);
printf(“%d+%d = %d\n”, a, b, a+b);
}
上面的代码中,int a,b就是程序的数据段,而整个main()方法就是程序的代码段,printf()方法和scanf()方法则对应着INT中断程序。
可以看到,C语言对冯·诺依曼体系结构的抽象更为高级,同时,C语言也通过对存储器、处理器和I/O设备的更高层次的抽象,使得开发过程中不需要再关心哪些是数据段,哪些是代码段,如何去控制处理器以及如何去操作I/O设备。在上面的程序中,数据段int a,b就位于代码段main()方法中。
C语言也是面向过程编程语言的代表,在C语言中,不需要再去关注存储器、处理器以及I/O设备,开发人员只需要对某一个具体的操作过程抽象,比如两个数的加法、两个数的减法以及其它的操作,都可以封装一个具体的方法,这些方法中不再区分数据段和代码段,也不再关注处理器如何和存储器、I/O设备之间交互。
1.4 面向对象语言对冯·诺依曼结构的抽象
从冯·诺依曼体系结构来说,面向过程编程语言的出现打破了存储器、处理器和I/O设备之间严格的功能界限划分,如图4-1所示:
![](https://img-my.csdn.net/uploads/201208/30/1346260938_6533.jpg)
图4-1 C语言面向过程编程
这种打破计算机系统结构限制的面向过程编程语言的诞生,在初期极大的提高了生产效率,使得开发人员从传统的面向系统结构编程的烦恼中解放了出来,开发人员不需要再去关心不同的硬件之间汇编指令的区别,也不需要再去关心硬件层次的数据、逻辑以及显示之间严格的界限。
随着软件系统复杂性的日益庞大,这种不再对数据、逻辑和显示进行严格区分的过程语言逐渐暴露它的缺陷,数据日益庞大、逻辑处理也日益复杂、界面又分散在各个过程函数中。毫无疑问的是,面向过程语言逐渐使得一些新的开发人员,特别是对冯·诺依曼体系结构知之甚少的开发人员的代码更加难以维护。
面向对象编程语言对冯·诺依曼体系结构的抽象更进化了一个层次,面向对象编程语言使得每个对象的存储、逻辑和显示区分开来,最终这些子对象组合在一起形成了复杂的计算机软件系统。
面向对象的出现,是对冯·诺依曼体系结构的一次回归,比如对一个人的抽象,Java代码如下:
public class Human {
public String _name;
public String _wugong;
Human() {
_name = "";
_wugong = "";
}
Human(String name, String wugong) {
_name = name;
_wugong = wugong;
}
}
在一个对象中,有这个对象的数据段和操作数据的代码段,而对象的大小就是数据段所占用空间的大小(不考虑占位字节),在实例化一个具体的对象之前,并不会占用数据段的空间,每一个具体的对象都会在数据段中占据空间。
面向对象编程方法的出现,是最接近人类的思维方式和冯·诺依曼体系结构的计算机科学。从更根本的层次上讲,面向对象是一种编程方法,这种方法接近人类的思维方式,也接近冯·诺依曼体系的结构,开发人员可以使用C、Java、C++甚至是汇编来实现面向对象的编程方法。
1.5 程序架构设计与冯·诺依曼结构
在计算机科学的发展历史中,一直围绕着对存储器、处理器和I/O设备的抽象,使得生产效率得到更大的提高。
冯·诺依曼体系结构的特点又决定了数据、逻辑和显示是注定要分离的,并且分离的越彻底,健壮性和可扩展性就越高。因为无论采用面向过程还是面向对象的编程方法,只要冯·诺依曼体系结构不发生变化,那么数据就注定要加载到存储器中、逻辑就注定要由处理器来控制、显示也必须让I/O中断来触发。
在计算机的发展历史长河中,架构设计越接近系统结构,那么健壮性和扩展性也就越强。自然界的规律概莫如此,这和一个社会的制度越符合民众需求就越稳固是一样的道理。如图5-1
![](https://img-my.csdn.net/uploads/201208/30/1346260938_7689.jpg)
图5-1 软件架构设计和系统结构
所以在软件架构设计中,如何让数据、逻辑和显示更彻底的分离,就成为衡量一个架构设计合理的标准,比如Struts+Spring+Hibernate框架。
同样,清晰的知道代码中每个部分的职责,也可以解释平时很多看起来很有迷惑性的问题,比如下面的代码:
#include"iostream.h "
class Test{
int i;
public:
Test(){cout < <"constructor " < <endl;};
void play(){delete this;};
~ Test(){cout < <"destructor " < <endl;};
};
void main()
{
Test*mTest;
mTest=new Test();
mTest-> play();
}
上面的代码比较有迷惑性,play()方法中通过delete this;这行代码来删除当前对象所占用的内存空间是否会有问题?
如果能清楚的知道mTest对象的数据段和代码段是分离的,int i是mTest对象的数据段,它在mTest对象初始化的时候在数据段分配并占据4个字节的空间,而代码段是独立于对象之外的,mTest对象通过隐式的this指针调用play()方法,从而释放mTest对象所占用的内存空间,所以上面的代码是没有错误的,这种写法在com中被大量使用。