程序到進程的過程

程序就是一堆代碼組合起來實現某種功能,進程可以說是跑起來的程序。

程序經過編譯之後會生成一個.exe可執行文件

#include<stdio.h>
#include<string.h>

int global = 1;                 //全局變量

int func(){                             //函數1
    int var1 = 1;                       //局部變量
    printf("Func: var1 = %d\n",var1);   
    printf("global = %d\n",global);
    return var1 + global;                //返回值
}

int main(void){
    int var1 = 1;
    int var2 = 2;

    int var3 = func();
    printf("main: var3 = %d\n",var3);
}
 <font size=2>程序中有兩種變量(局部變量和全局變量),聲明在函數體外部
 的爲全局變量,全局變量作用在整個程序的存活週期裏,聲明在函數裏面的是局部
 變量,局部變量的作用域是它存在的哪個函數裏面,當函數return後,變量就會
 被釋放。</font>

重點是程序的運行過程:

這裏寫圖片描述

當成需文件運行爲進程的時候是需要內存的,但是進程從內存中獲取的進程空間是什麼樣子的呢?

內存空間

Text區域用來儲存指令(instruction),說明每一步的操作。Global Data用於存放全局變量,棧(Stack)用於存放局部
變量,堆(heap)用於存放動態變量 (dynamic variable. 程序利用malloc系統調用,直接從內存中爲dynamic variable
開闢空間)。Text和Global data在進程一開始的時候就確定了,並在整個進程中保持固定大小。


棧(Stack)以幀(stack frame)爲單位。當程序調用函數的時候,比如main()函數中調用func()函數,stack會向下增長一幀。幀中存儲該函數的參數和局部變量,以及該函數的返回地址(return address)。此時,計算機將控制權從main()轉移到func(),func()函數處於激活(active)狀態。位於棧最下方的幀,和全局變量一起,構成了當前的環境(context)。激活函數可以從環境中調用需要的變量。典型的編程語言都只允許你使用位於stack最下方的幀 ,而不允許你調用其它的幀 (這也符合stack結構“先進後出”的特徵。但也有一些語言允許你調用棧的其它部分,相當於允許你在運行func()函數的時候調用main()中聲明的局部變量,比如Pascal)。當函數又進一步調用另一個函數的時候,一個新的幀會繼續增加到棧的下方,控制權轉移到新的函數中。當激活函數返回的時候,會從棧中彈出(pop,讀取並從棧中刪除)該幀,並根據幀中記錄的返回地址,將控制權交給返回地址所指向的指令(比如從func()函數中返回,繼續執行main()中賦值給var3的操作)。


在進程運行的過程中,通過調用和返回函數,控制權不斷在函數間轉移。進程可以在調用函數的時候,原函數的幀中保存有在我們離開時的狀態,併爲新的函數開闢所需的幀空間。在調用函數返回時,該函數的幀所佔據的空間隨着幀的彈出而清空。進程再次回到原函數的幀中保存的狀態,並根據返回地址所指向的指令繼續執行。上面過程不斷繼續,棧不斷增長或減小,直到main()返回的時候,棧完全清空,進程結束。


當程序中使用malloc的時候,堆(heap)會向上增長,其增長的部分就成爲malloc從內存中分配的空間。malloc開闢的空間會一直存在,
直到我們用free系統調用來釋放,或者進程結束。一個經典的錯誤是**內存泄漏**(memory leakage), 就是指我們沒有釋放不再使用
的堆空間,導致堆不斷增長,而內存可用空間不斷減少。

棧和堆的大小則會隨着進程的運行增大或者變小。當棧和堆增長到兩者相遇時候,也就是內存空間圖中的藍色區域(unused area)完全消失的時候,再無可用內存。進程會出現棧溢出(stack overflow)的錯誤,導致進程終止。在現代計算機中,內核一般會爲進程分配足夠多的藍色區域,如果清理及時,棧溢出很容易避免。即便如此,內存負荷過大,依然可能出現棧溢出的情況。我們就需要增加物理內存了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章