1. 程式人生 > >從馮•諾依曼結構看電腦科學的發展史

從馮•諾依曼結構看電腦科學的發展史

作者:孫東風2012-8-30     轉載請註明出處

本文主要內容包括:

Р    馮·諾依曼體系結構

Р    組合語言對馮·諾依曼結構的抽象

Р    面向過程語言對馮·諾依曼結構的抽象

Р    面嚮物件語言對馮·諾依曼結構的抽象

Р    程式架構設計與馮·諾依曼結構

1.1 馮·諾依曼體系結構

1946年美籍匈牙利科學家馮·諾依曼提出儲存程式原理,把程式本身當作資料來對待,程式和該程式處理的資料用同樣的方式儲存,並確定了儲存程式計算機的五大組成部分和基本工作方法,如圖1-1所示。

圖1-1 馮·諾依曼體系結構

馮·諾依曼理論的要點是:數字計算機的數制採用二進位制;計算機應該按照順序執行程式。

如圖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;
DF1
 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;
比較AXDI,判斷輸出是否應結束
 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語言的第一個版本。

圖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所示:

圖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

圖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中被大量使用。