九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
Linux從程序到進程
Linux從程序到進程

作者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉(zhuǎn)載,也請保留這段聲明。謝謝!

 

計算機如何執(zhí)行進程呢?這可以說是計算機運行的核心問題。即使我們已經(jīng)編寫好程序,但程序是死的文本,只有成為活的進程才能帶來產(chǎn)出。我們已經(jīng)從Linux進程基礎(chǔ)中了解了進程的一些概況?,F(xiàn)在我們看一下從程序到進程的漫漫征程。

 

1. 一段程序

我們下面展示一個簡單的C語言程序,我們假設(shè)該程序已經(jīng)經(jīng)過編譯,成為可執(zhí)行的程序文件vamei.exe。

#include <stdio.h>int glob=0;                                             /*global variable*/void main(void) {  int main1=5;                                          /*local variable of main()*/  int main2;                                            /*local variable of main()*/  main2 = inner(main1);                                 /* call inner() function */  printf("From Main: glob: %d \n", glob);  printf("From Main: main2: %d \n", main2);}int inner(int inner1) {                                 /*inner1 is an argument, also local to inner()*/  int inner2=10;                                        /*local variable of inner()*/  printf("From inner: glob: %d \n", glob);  return(inner1+inner2);}

(選取哪一個語言或者具體的語法并不是關(guān)鍵,大部分語言都可以寫出類似上面的程序。在看python教程的讀者也可以利用python的函數(shù)結(jié)構(gòu)和print寫一個類似的python程序。當(dāng)然,還可以是C++,Java,Objective-C等等。選用C語言的原因是:它是為UNIX而生的語言。)

 

在main()函數(shù)中,我們調(diào)用了inner()函數(shù),并在inner范圍內(nèi)進行了一次printf()以便輸出。在從該函數(shù)返回之后,我們又在main()的范圍之內(nèi)進行了兩次printf()。

我們還要注意各個變量的作用范圍。簡單地說,變量可以分為全局變量局部變量。在所有函數(shù)之外聲明的變量為全局變量,比如glob,在任何時候都可以使用。在函數(shù)內(nèi)定義的變量為局部變量,只能在該函數(shù)的作用域(range)內(nèi)使用,比如說我們在inner()工作的時候不能使用main()函數(shù)中聲明的main1變量,而在main()中我們無法使用inner()函數(shù)中聲明的inner2變量。

我們并不在意這個程序的具體功能和結(jié)果。我們關(guān)心的是這個程序的運行過程。下圖為該程序的運行過程,以及各個變量的作用范圍:

運行流程

2. 進程空間

為了進一步了解上面的程序的運行,我們還需要知道進程是如何使用內(nèi)存的。當(dāng)程序文件運行為進程的時候,進程在內(nèi)存中得到空間(進程自己的小房間)。每個進程空間按照如下方式分為不同區(qū)域:

內(nèi)存空間

Text區(qū)域用來儲存指令(instruction),來告訴程序每一步的操作。Global Data用于存放全局變量,stack用于存放局部變量,heap用于存放動態(tài)變量 (dynamic variable. 程序利用malloc系統(tǒng)調(diào)用,直接從內(nèi)存中為dynamic variable開辟空間)。TextGlobal data在進程一開始的時候就確定了,并在整個進程中保持固定大小。

 

Stack()stack frame為單位。當(dāng)程序調(diào)用函數(shù)的時候,比如main()函數(shù)中調(diào)用inner()函數(shù),stack會向下增長一個stack frame。Stack frame中存儲該函數(shù)的參數(shù)局部變量,以及該函數(shù)的返回地址(return address)。此時,計算機將控制權(quán)從main()轉(zhuǎn)移到inner(),inner()函數(shù)處于激活(active)狀態(tài)。位于Stack最下方的frame和Global Data就構(gòu)成了當(dāng)前的環(huán)境(context)。激活函數(shù)可以從中調(diào)用需要的變量。典型的編程語言都只允許你使用位于stack最下方的frame ,而不允許你調(diào)用其它的frame (這也符合stack結(jié)構(gòu)“先進后出”的特征。但也有一些語言允許你調(diào)用stack的其它部分,相當(dāng)于允許你在運行inner()函數(shù)的時候調(diào)用main()中聲明的局部變量,比如Pascal)。當(dāng)函數(shù)又進一步調(diào)用另一個函數(shù)的時候,一個新的frame會繼續(xù)增加到stack下方,控制權(quán)轉(zhuǎn)移到新的函數(shù)中。當(dāng)激活函數(shù)返回的時候,會從stack中彈出(pop,就是讀取并刪除)該frame,并根據(jù)frame中記錄的返回地址,將控制權(quán)交給返回地址所指向的指令(比如從inner()函數(shù)中返回,繼續(xù)執(zhí)行main()中賦值給main2的操作)。

下圖是stack在運行過程中的變化,箭頭表示stack增長的方向,每個方塊代表一個stack frame。開始的時候我們有一個為main()服務(wù)的frame,隨著調(diào)用inner(),我們?yōu)閕nner()增加一個frame。在inner()返回時,我們再次只有main()的frame,直到最后main()返回,其返回地址為空,所以進程結(jié)束。

stack變化

在進程運行的過程中,通過調(diào)用和返回函數(shù),控制權(quán)不斷在函數(shù)間轉(zhuǎn)移。進程可以在調(diào)用函數(shù)的時候,原函數(shù)的stack frame中保存有在我們離開時的狀態(tài),并為新的函數(shù)開辟所需的stack frame空間。在調(diào)用函數(shù)返回時,該函數(shù)的stack frame所占據(jù)的空間隨著stack frame的彈出而清空。進程再次回到原函數(shù)的stack frame中保存的狀態(tài),并根據(jù)返回地址所指向的指令繼續(xù)執(zhí)行。上面過程不斷繼續(xù),stack不斷增長或減小,直到main()返回的時候,stack完全清空,進程結(jié)束。

 

 當(dāng)程序中使用malloc的時候,heap()向上增長,其增長的部分就成為malloc從內(nèi)存中分配的空間。malloc開辟的空間會一直存在,直到我們用free系統(tǒng)調(diào)用來釋放,或者進程結(jié)束。一個經(jīng)典的錯誤是內(nèi)存泄漏(memory leakage), 就是指我們沒有釋放不再使用的heap空間,導(dǎo)致heap不斷增長,而內(nèi)存可用空間不斷減少。

由于stack和heap的大小則會隨著進程的運行增大或者變小,當(dāng)stack和heap增長到兩者相遇時候,也就是內(nèi)存空間圖中的藍(lán)色區(qū)域(unused area)完全消失的時候,進程會出現(xiàn)棧溢出(stack overflow)的錯誤,導(dǎo)致進程終止。在現(xiàn)代計算機中,內(nèi)核一般都會為進程分配足夠多的藍(lán)色區(qū)域,如果我們即時清理的話,stack overflow是可以避免的。但是,在進行一些矩陣運算的時候,由于所需的內(nèi)存很大,依然可能出現(xiàn)stack overflow的情況。一種解決方式是增大內(nèi)核分配給每個進程的內(nèi)存空間。如果依然不能解決問題的話,我們就需要增加物理內(nèi)存。

Stack overflow可以說是最出名的計算機錯誤了,所以才有IT網(wǎng)站(stackoverflow.com)以此為名。

 

在高級語言中,這些內(nèi)存管理的細(xì)節(jié)對于用戶來說不透明。在編程的時候,我們只需要記住上一節(jié)中的變量作用域就可以了。但在想要寫出復(fù)雜的程序或者debug的時候,我們就需要相關(guān)的知識了。

 

3. 進程附加信息

除了上面的信息之外,每個進程還要包括一些進程附加信息,包括PID,PPID,PGID(參考Linux進程基礎(chǔ)以及Linux進程關(guān)系)等,用來說明進程的身份、進程關(guān)系以及其它統(tǒng)計信息。這些信息并不保存在進程的內(nèi)存空間中。內(nèi)核會為每個進程在內(nèi)核自己的空間中分配一個變量(task_struct結(jié)構(gòu)體)以保存上述信息。內(nèi)核可以通過查看自己空間中的各個進程的附加信息就能知道進程的概況,而不用進入到進程自身的空間 (就好像我們可以通過門牌就可以知道房間的主人是誰一樣,而不用打開房門)。每個進程的附加信息中有位置專門用于保存接收到的信號(正如我們在Linux信號基礎(chǔ)中所說的“信箱”)。

 

4. fork & exec

現(xiàn)在,我們可以更加深入地了解forkexec(參考Linux進程基礎(chǔ))的機制了。當(dāng)一個程序調(diào)用fork的時候,實際上就是將上面的內(nèi)存空間,包括text, global data, heap和stack,又復(fù)制出來一個,構(gòu)成一個新的進程,并在內(nèi)核中為改進程創(chuàng)建新的附加信息 (比如新的PID,而PPID為原進程的PID)。此后,兩個進程分別地繼續(xù)運行下去。新的進程和原有進程有相同的運行狀態(tài)(相同的變量值,相同的instructions...)。我們只能通過進程的附加信息來區(qū)分兩者。

程序調(diào)用exec的時候,進程清空自身內(nèi)存空間的text, global data, heap和stack,并根據(jù)新的程序文件重建text, global data, heap和stack (此時heap和stack大小都為0),并開始運行。

(現(xiàn)代操作系統(tǒng)為了更有效率,改進了管理fork和exec的具體機制,但從邏輯上來說并沒有差別。具體機制請參看Linux內(nèi)核相關(guān)書籍)

 

這一篇寫了整合了許多東西,所以有些長。這篇文章主要是概念性的,許多細(xì)節(jié)會根據(jù)語言和平臺乃至于編譯器的不同而有所變化,但大體上,以上的概念適用于所有的計算機進程(無論是Windows還是UNIX)。更加深入的內(nèi)容,包括線程(thread)、進程間通信(IPC)等,都依賴于這里介紹的內(nèi)容。

 

總結(jié):

函數(shù),變量的作用范圍,global/local/dynamic variables

global data, text,

stack, stack frame, return address, stack overflow

heap, malloc, free, memory leakage

進程附加信息, task_struct

fork & exec

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
細(xì)談Go變量的內(nèi)存分布
C語言面試題大匯總之微軟亞洲技術(shù)中心面試題
進程的地址空間:TEXT,DATA,BSS,HEAP,STACK
匯編中bss,data,text,rodata,heap,stack的意義
解析 STM32 的啟動過程(寫的不錯)
linux 下 C 程序(進程) 內(nèi)存布局|『 C 』
更多類似文章 >>
生活服務(wù)
熱點新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服