1. 程式人生 > 遊戲資訊 >尼古拉斯/Nicolas (上古卷軸主要人物)

尼古拉斯/Nicolas (上古卷軸主要人物)

程式和程序

什麼是程式?

  • 程式就是一個被動的實體(需要被使用者指定執行),就是一個儲存在磁碟上的檔案包含了一系列的指令(被稱作可執行檔案)。

什麼事程序?

當可執行檔案被載入進記憶體後,程式就變成了程序。這個可執行檔案包含了指令,當載入進記憶體後,cpu就可以執行這些指令,直到所有指令執行完成。
程序是一個活動的實體,這個實體中包含了程式計數器用來指明下一條需要執行的指令

程式計數器(pc--programer counter):是一個cpu中的暫存器,裡面存放了下一條要執行指令的記憶體地址,通常,cpu取完一條指令之後會將PC暫存器的值加 "1",用來計算下一條要執行指令的地址。

當將一個程式程式碼載入進記憶體,並將第一條指令地址寫入pc暫存器之後,程序就建立完成了。

程序再記憶體中的分佈

當程序被載入進記憶體後,程式會將內分成幾塊區域----> 程式碼段、資料段,堆,棧

int global = 100; //全域性變數

void f(int x,int y){ //
  int* p = malloc(100);
  return;
}

void g(int a){ //
  f(a,a+1);
  return;
}
int main(){
  static int i = 10; //靜態變數
  g(i);
  return 0;
}
  1. 以上程式碼編譯後會變成二進位制指令,這些指令會放在text 區域,這個區域是隻讀區域。

  2. 這個程式碼的入口是 main 函式, 先將主函式的返回地址寫入棧,發現主函式中有staic 變數i和全域性變數global,則將這兩個變數存入data

    區域

  3. 呼叫g 函式,將a 放入stack 上,接著將g()函式的返回值(再main函式內地址,方便g函式返回的時候繼續執行main函式接下來的程式碼)也壓入棧

  4. g函式會呼叫f函式,因此同理,將x,y,p 三個區域性變數放入棧中,接著將f的返回值放入棧中

  5. p變數被動態分配了一塊記憶體,因此將p變數放入到heap上,同時分配100個位元組

  6. f函式執行完返回:將f函式的返回地址讀取出來,由於函式要返回了,因此需要將f函式中的區域性變數地址清除,以及f的返回值也清除,接著執行g函式

  7. g函式返回,取出g函式的返回地址,定位到main函式中的g(i)那段程式碼,同時清除g函式返回地址和g函式內部區域性變數

  8. main函式返回,清空堆記憶體(由於沒有free p變數,p變數要等到函式退出才會銷燬)

併發的程序

併發與並行

再同一個時間發生或者存在的兩個或多個事件被稱為併發(concurrency)

再同一個時間同時執行的兩個或多個事件被稱為並行(parallel)

當再單核機器上,同時建立多個程式,那就是併發,這兩個程式同時存在,但是再同一個時間只有一個程序再執行,如果在2核cpu上,執行兩個程序,兩個程序分別由不同的cpu來執行,那麼這種就叫做並行,因為他們再同一時間都在執行。

cpu上的併發

cpu再執行程式的時候會被劃分成一個個時間片,由作業系統來排程再這個時間片上由哪個程序來由cpu執行。當一個程序因為執行的時間到了而被另外一個程序頂替的佔有cpu,這個過程叫“程序切換

其中實線代表的是程序佔有cpu,虛線代表的是程序沒有佔有cpu

程序狀態

根據上面併發的知識知道,程序並不是一直都在執行的,而且會由作業系統進行排程,有時執行,有時並沒有執行,因此需要給程序標記上他當前的狀態,好讓作業系統可以來排程管理程序。

三種基本狀態

程序總共有五種狀態,其中兩種是建立和終止,執行中的程序一般只在以下三個狀態切換

  • 就緒態(ready):程序的程式碼再cpu上執行
  • 執行態(running):程序具備執行條件,等待分配cpu
  • 等待態(waiting):程序再等待某些時間的發生(比如IO操作結束或者一個訊號),不具備執行條件==

通過上面圖可以看出:

  • running----> ready: 執行中的程序再遇到系統中斷(時鐘中斷患者io中斷等)的時候會被切換成就緒態或者有高優先順序的程序到達
  • running----> waiting: 執行中的程序再遇到IO或者事件阻塞(比如將字串輸出到顯示器的時候,由於這些操作很慢,程序就會被切換成等待)
  • ready-----> running: 當作業系統開始排程分配程序的時候,程序就有就緒態轉換為執行態
  • waiting----->ready :當遇到IO或者某些事件完成