(四)進程環境

Table of Contents

0.那麼進程所需的運行環境有哪些?

1. 啓動代碼 

1.1 啓動代碼的作用

1.2 啓動代碼是由誰提供的

(1)啓動代碼一般都是由編譯器提供的,一般有兩種提供方式

(2)gcc -v

1.3 啓動代碼做了些什麼 

1.3.1 啓動代碼使用什麼語言編寫的

1.3.2 啓動代碼大致做了些什麼呢? 

1.4 程序是如何運行起來的

1.4.1 裸機的情況 

1.4.1 有OS的情況  

2. 進程(程序)的終止方式

2.1 正常終止

1)main函數調用return關鍵字

2)在程序的任意位置調用exit函數 

3)在程序的任意位置調用_exit函數 

4)return、exit和_exit的返回值問題

2.2、異常終止

2.3 atexit函數

2.4 有OS時,進程從啓動 到 正常終止的全過程

2.4.1 圖示

2.4.2 爲什麼調用exit、return正常終止時,會刷新標準io的緩存

2.5 命令行參數

3. 環境變量表    

3.1 windows的環境變量    

3.2 環境變量表

3.3 Linux的環境變量

 修改Linux的環境變量表

1)如何使用命令來修改“命令行窗口進程”的環境變量表

2)通過API修改環境變量

4. c程序內存空間佈局

1)什麼是c程序的內存空間

2)c程序的內存空間結構

5. 庫    


0.那麼進程所需的運行環境有哪些?

所需環境有:啓動代碼、環境變量、c程序的內存空間佈局、庫等。

1. 啓動代碼 

1.1 啓動代碼的作用

所有高級語言的程序,都有自己的啓動代碼。

C程序運行時,最開始運行的是啓動代碼,啓動代碼再去調用main函數,然後整個C程序都已運行。

                                                     

總之,高級語言程序 = 啓動代碼 + 自己代碼。

1.2 啓動代碼是由誰提供的

(1)啓動代碼一般都是由編譯器提供的,一般有兩種提供方式

1)源碼形式

     以源碼形式提供時,編譯器會將啓動代碼的源文件和自己程序的源文件一起編譯。

     

     像開發單片機這種沒有OS的計算機的C程序時,啓動代碼一般是源碼形式提供

2)二級制的.o(目標文件)形式

    直接以.o形式提供時,省去了我自己對“啓動代碼”的編譯。

     

     如果開發的程序是運行在OS上時,那麼編譯器一般是以.o形式來提供啓動代碼

     比如我們gcc a.c時,gcc就是以.o形式提供的,基於OS運行的程序的啓動代碼,相對而言,自然比較複雜些。

 

gcc時加一個-v選項,查看gcc編譯鏈接的詳細情況時,可以看到有很多.o,這些.o就是gcc提供的啓動代碼。

(2)gcc -v

gcc -v a.c

在編譯的詳細信息裏面,有很多的事先就被編譯好的.o文件,這些.o文件就是用來生成啓動代碼的

1.3 啓動代碼做了些什麼 

1.3.1 啓動代碼使用什麼語言編寫的

基本都是彙編寫的。

1.3.2 啓動代碼大致做了些什麼呢? 

大致上有兩件重要的事情:

· 對c程序的內存空間進行佈局,得到c程序運行所需要的內存空間結構。

· 留下相應庫接口

(1)對c內存空間進行佈局

c等高級語言程序在運行時,函數調用需要“棧”,啓動代碼就需要在c內存空間上建立“棧”,

說白了就是從從c內存空間中劃出一段空間,然後以“棧”的形式來進行管理。

 

· 思考:爲什麼啓動代碼,基本都是使用匯編來編寫?

  •  在程序的內存空間結構還沒有佈局起來之前,高級語言程序還無法運行,此時只能使用匯編
  •  當利用匯編編寫的啓動代碼將高級語言的內存空間結構建立起來後,自然就可以運行c/c++等高級語言的程序了。

(2)爲庫的調用預留接口

如果程序使用的是動態庫的話,編譯時,動態庫代碼並不會被直接編譯到程序中,只會留下相應的接口

程序運行起來後,纔會去對接庫代碼,爲了能夠對接動態庫,啓動代碼會留下動態庫的對接接口。

1.4 程序是如何運行起來的

1.4.1 裸機的情況 

(1)內存和硬盤一體式

1)典型的比如51單片機,51沒有單獨的內存和單獨硬盤,使用的是內存和硬盤功能二合一的norflash。

    (a)爲什麼能身兼內存的功能?

            因爲norflash的訪問速度很快,因此cpu能夠直接從norflash上讀取指令並執行,此時norflash就是一個內存。

    (b)爲什麼身兼硬盤的功能?

            因爲norflash能夠永久保存數據,設備關電後,數據依然存在

2)程序運行的過程

            

            

(2)內存和硬盤分開式 

比如arm芯片

1)爲什麼傳統的計算機,硬盤和內存都是分開的

               

2)內存和硬盤分開的這種情況,如果直接以裸機方式使用的話,程序是如何運行起來的

       兩種:
                      第一種:直接將下載到內存中,然後運行。
                      第二種:先下載硬盤永久保存,開機時自動從硬盤中將代碼拷貝到內存上,然後運行。

      (a)將程序直接下載到內存

                

                這種方式最大的缺點就是掉電就沒了,所以這種方式只適合於平時的測試 。

       (b)將程序下載到硬盤

               

1.4.1 有OS的情況  

 上OS的計算機,基本都是內存和硬盤分開式的情況。

  OS是怎麼運行啓動起來的呢?

  

(1)有OS時,可執行程序都是直接放在了硬盤上

(2)有OS時,程序如何運行起來

       1)有OS支持時,如何啓動程序呢

           (a)裸機時,是怎麼啓動程序的呢?

                    

          (b)有OS支持時,啓動程序方式有三種

                     · 雙擊快捷圖標運行
                     · 在命令行運行    
                     · 設置爲開機自啓動    
      2)OS是怎麼實現拷貝的

                對於我們自己寫的裸機程序來說,我們需要自己寫拷貝代碼

                對於OS這個裸機程序來說,由啓動程序來負責代碼的拷貝。

                

2. 進程(程序)的終止方式

2.1 正常終止

進程主動調用終止函數/返回關鍵字所實現的結束,就是正常終止。

        · main調用return關鍵字結束
        · 程序任何位置調用exit函數結束
        · 程序任何位置調用_exit函數結束

1)main函數調用return關鍵字

return關鍵字的作用是返回上一級函數,如果main函數的子函數調用return的話,返回的上一級是main函數。
如果main函數調用return的話,main函數所返回的上一級是啓動代碼。

(a)顯式調用

· 返回值的意義

    

疑問:如果return時我不寫返回值會怎樣呢?

   

(b)隱式調用

就是不明寫出return,當main函數中的最後一句代碼執行完畢後,會默認的調用return返回

不過隱式return時,默認返回0。

2)在程序的任意位置調用exit函數 

其實,main函數調用return返回到啓動代碼後,啓動代碼也是調用exit函數來實現正常終止的。

#include <stdlib.h>

void exit(int status);

這個參數就是返回值(進程終止狀態)。

main函數調用return將返回值返回給啓動代碼後,啓動代碼又會調用exit(返回值),將返回值返回。

3)在程序的任意位置調用_exit函數 

_exit是一個系統函數(系統API),而exit是c庫函數,exit就是調用_exit來實現的。

4)return、exit和_exit的返回值問題

 return、exit和_exit的返回值,也被稱爲進程終止狀態。

(a)裸機時:

(b)有OS時:

 return、exit、_exit,使用哪種來返回都行。

 

2.2、異常終止

進程不是因爲return、exit和_exit函數而終止的,而是被強行發送了一個信號給無條件終止了,這就是異常終止。

2.3 atexit函數

函數原型

#include <stdlib.h>

int atexit(void (*function)(void));

   

   

登記“進程終止處理函數”有什麼意義?

   

2.4 有OS時,進程從啓動 到 正常終止的全過程

2.4.1 圖示

 

2.4.2 爲什麼調用exit、return正常終止時,會刷新標準io的緩存

 有關標準IO的庫緩存的緩衝有三種,無緩衝、行緩衝、全緩衝

(1)回顧行緩衝

標準輸出(printf)的庫緩存就是行緩衝的,在緩存中積壓數據,直到以下情況時,纔會刷新輸出,否則一直擠壓】

(2)爲什麼調用exit正常終止時,會刷新標準io的緩存呢?

  •  因爲exit會調用fclose關閉所有的標準io,關閉時會自動調用fflush來刷新數據。
  •  這裏要注意:如果進程時異常終止的話,是不會刷新緩存區的,因爲異常退出時,跟exit函數半毛錢關係都沒有。

2.5 命令行參數

第一個參數永遠都是程序名

將命令行參數傳遞給main函數形參的過程

3. 環境變量表    

3.1 windows的環境變量    

3.1.1 爲什麼在命令行執行我自己的程序,需要指明路徑

在windows下,如果你不加路徑的話,會道默認到當前路徑下找程序,沒有的話就找不到你的程序。

3.1.2 能不能不加路徑,我隨便在什麼目錄下都可執行我的程序呢?

當然可以,只要把程序所在路徑,加入windows的path環境變量即可。

3.1.3 爲什麼設置了path環境變量後,可以不加路徑就能執行程序

(1)path這個環境變量的作用

專門記錄各種可執行程序所在路徑。

(2)path記錄後

(3)path的意義

一般來說,我們自己的安裝的程序,都沒有設置環境變量

3.1.4 再來看看windows的環境變量

3.2 環境變量表

(1)什麼是環境變量表

用於存“放環境變量”的表,就是環境變量表。

什麼是環境變量呢?

答:其實就是進程在運行時,會用到的一些字符串信息,環境表就好比是工具箱,

       裏面放了各種進程運行時需要用到的“工具”,比如各種的路徑。
 

(2)環境變量文件

(3)每個進程的環境變量表

每一個進程都在自己的內存空間(堆空間)保存了一份自己的環境變量表。

 

每個進程空間中的環境變量表又是怎麼來的?

                     顯然從環境變量文件中得來的。

(4)如果某環境變量的數據有很多條,在環境變量表中,多條數據之間怎麼區分

在windows這邊使用;分隔,Linux這邊則使用:分隔。

(5)爲什麼只有重新打開“命令行窗口”後, 新設置的“環境變量”才生效?

3.3 Linux的環境變量

 修改Linux的環境變量表

(1)永久修改

1)圖形方式操作

2)直接修改“環境變量文件”

(2)臨時修改

· 什麼是臨時修改?

就是隻修改當前進程自己的“環境變量表”,其它不相關進程“環境變量表”及“環境變量文件”中數據,不會發生任何變化,

· 如何實現臨時修改

1)如何使用命令來修改“命令行窗口進程”的環境變量表

(a)查看所有環境變量

(b)顯示單個的環境變量

(c)添加一個新的環境變量

(d)修改已有環境變量

(d)刪除

2)通過API修改環境變量

對於我們自己所寫的程序來說,我們可以調用API來修改自己所寫程序的“環境變量表”。

(a)獲取環境表中的所有環境變量

1  environ全局變量:   char **environ;

    - environ與main函數的argv一樣,指向的都是一個字符串指針數組。

         argv:與命令行參數有關
         environ:與環境變量表有關

 
2 main函數的第三個參數: char **environ

(b)調用API:實現環境變量的添加、修改等

· putenv、setenv:添加和修改環境變量

#include <stdlib.h>

int putenv(char *string);
int setenv(const char *name, const char *value, int overwrite);

  

 

· unsetenv:刪除環境變量

#include <stdlib.h>

int unsetenv(const char *name);

 

·getenv:獲取環境變量 

(c)疑問:我自己所寫程序的環境表是怎麼來的

我命令行窗口執行./a.out,那麼a.out進程就屬於“命令行窗口進程”的子進程,子進程的環境表是從父進程複製得到的

 

當有OS支持時,基本所有的進程都是由父進程“生”出來的: 

疑問1:最原始的進程從哪來的 

     答:OS啓動完畢後演變得到的。

4. c程序內存空間佈局

1)什麼是c程序的內存空間

c程序運行時,是運行在內存上的,也就是說需要在內存上開闢出一塊空間給c程序,然後將C代碼從硬盤拷貝到內存空間上運行,至於說是不是將代碼全部會被拷貝到內存上,這就不一定的了

2)c程序的內存空間結構

(a)有結構的要求嗎?

有,這段空間必須佈局爲c程序運行所需的空間結構,c程序才能運行

如果空間沒有佈局好,進程將無法運行,因此程序的內存空間佈局是非常重要的進程環境。

(b)c的內存結構是誰來構建的

是由啓動代碼來搭建的,比如啓動代碼會把c內存空間的某一部分空間構建爲“棧”,或者說以“棧”的方式來管理這片空間。

(c)不光是C程序

所有高級語言的程序在運行時,都涉及內存空間的結構佈局,不過它們的結構都是相似的

5. 庫    

寫程序時,絕對不可能從零開始寫代碼,都是要依賴別人所寫的代碼的,比如別人寫的庫,所以庫也是程序非常重要的

進程環境,沒有庫的支持,我們的程序根本做不了什麼複雜的事情。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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