程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C語言運行時數據

C語言運行時數據

編輯:關於C語言

1 可執行文件的格式

      在UNIX傳統的操作系統中,所有編譯生成的輸出文件都缺省地使用同一個名字a.out,在現代操作系統中,a.out格式的可執行文件是鏈接器的輸出,而不是匯編程序的輸出(在計算機的遠古時代,a.out是匯編器的輸出,那個時候還沒有鏈接器)。

目標文件和可執行文件有幾種不同的格式,大多數都采用了一種ELF的格式,更多的格式可以使用下面的命令來查看(有的系統中可能找不到手冊):

      $ man a.out

      你可以在linux系統中,對編譯鏈接生成的可執行文件使用:

      $ file 可執行文件名

      可以看到他的輸出,說明這是一個ELF格式的可執行文件:


      yuanlu@bear-labpc:~/workspace/wifi/wifi_hello$ file hello.ko

      hello.ko: ELF 32-bit LSB relocatable, ARM, version 1 (SYSV), not stripped   

2 UNIX中段的概念

       在NUIX系統中,有很多的不同格式,但他們都有一個共同的概念—段。

      所謂的段是目標文件的概念,一個目標文件有多個段,他們是二進制文件中簡單的的區域,裡面保存了和某種特定類型相關的所有信息,如:符號表條目。這裡不要把UNIX和Intel X86中的段概念混淆,後者中的段表示一種內存模型的設計結果,在這種設計中,地址空寂並非一個整體,而是分成一些固定大小的區域,稱之為段。

對於一個目標文件,運行size命令可以告訴你這個文件的三個段的大小。這三個段分別是:代碼段(文本段),數據段和bss段:

    yuanlu@bear-labpc:~/workspace/wifi/wifi_hello$ size hello.ko

    text     data      bss      dec    hex filename

     224      300        0      524    20c hello.ko

       這裡對三個段中存放的內容做一個說明:

(1)       文本段:即代碼段,存放要執行的指令代碼

(2)       數據段:存放全局或靜態的已經初始化的數據變量

(3)       bss段:存放全局或靜態的尚未初始化的數據變量。由於BSS段只保存沒有值得變量,所以事實上他並不需要保存這些變量的映像,而只是將BSS段在運行時需要的大小記錄在目標文件中,因此BSS段並不占據目標文件的任何空間。

需要注意的是一個a.out可執行文件的組成是下面這個樣子的:


a.       a.out神奇數字

b.       a.out的其他內容

c.       BSS數據段所需大小

d.       數據段(初始化後的全局和靜態變量)

e.       文本段(可執行文件的指令)

其中文件的頭部有一個a.out的神奇數字,它是一種能夠確認一組隨機的二進制位集合的什麼數字,我們暫且不用理會;對於局部變量而言,它不存在a.out中而是在運行時創建。下面我們借用號稱最簡單的hello world驅動來證明上面的內容的正確性。

  1 #include <linux/init.h>

  2 #include <linux/module.h>

  3

  4 MODULE_LICENSE("GPL");

  5

  6 /* the init function*/

  7 static __init int hello_init(void)

  8

  9 {

 10

 11     printk(KERN_WARNING "Hello world !/n");

 12

 13     return 0;

 14 }

 15

 16 /* the distory function*/

 17 static __exit void hello_exit(void)

 18 {

 19     printk(KERN_WARNING "Goodbye!/n");

 20 }

 21

 22 module_init(hello_init);

 23 module_exit(hello_exit);

       編譯生成hello.ko可執行文件後,使用size hello.ko命令查看每個段的使用情況:

    yuanlu@bear-labpc:~/workspace/wifi/wifi_hello$ size hello.ko

    text     data      bss      dec    hex filename

     224      300        0      524    20c hello.ko


     現在的bss段的內容為空,這裡的0表示bss段的大小,data段的大小為300(單位都是字節)。我們增加分別兩個全局和靜態已經初始化的變量和未初始化的變量,在函數中也增加一個大數組的聲明:

  1 #include <linux/init.h>

  2 #include <linux/module.h>

  3

  4 MODULE_LICENSE("GPL");

  5

  6 int i;

  7 int m = 2;

  8

  9 static int j;

 10 static int n = 1;

 11

 12 /* the init function*/

 13 static __init int hello_init(void)

 14

 15 {

 16     printk(KERN_WARNING "Hello world !/n");

 17

 18     return 0;

 19 }

 20

 21 /* the distory function*/

 22 static __exit void hello_exit(void)

 23 {

 24     printk(KERN_WARNING "Goodbye!/n");

 25 }

 26

 27 module_init(hello_init);

 28 module_exit(hello_exit);

     編譯後使用size工具:

    yuanlu@bear-labpc:~/workspace/wifi/wifi_hello$ size hello.ko

    text     data      bss      dec    hex filename

     224      300        8      532    214 hello.ko

       這裡發現bss段的大小變成了8,data段卻沒有增加,通過後面的繼續求證發現這樣一個事實:

(1)對於函數內部的局部變量沒有存放在可執行文件裡

(2)對於全局變量而言,未初始化的變量存放在bss段,初始化的變量分兩種:第一,如果被初始化為0,仍然被放在bss段,否則放在數據段(這一點有點不同哦)

(3)對於靜態全局變量而言,不管有沒有初始化,都不存放在可執行文件中

     如果說前面兩點很好理解的話,俺麼對於第三點的實際表現與理論上的比較大的出入,有待後面繼續求證,這裡只能猜測是編譯器的差異(我的環境是交叉編譯環境)。

3 a.out的內存布局

       段可以被方便地映射到連接器在運行時可以直接載入的對象中,載入器只是去可執行文件的每一個段的一個映像,本質上段就是正在執行的程序中的一塊內存區域。連接器把每個段從文件拷貝到內存中,一般使用mmap()系統調用。

      下面是可執行文件中的段在內存中的布局圖:      

                                                                                                                  堆棧段

                                                                                                                    空洞

a.       a.out神奇數字

b.       a.out的其他內容

c.       BSS數據段所需大小     ---------------------------------------->            BSS段

d.       數據段(初始化後的全局和靜態變量)-------------------->    數據段

e.       文本段(可執行文件的指令)             -------------------->             文本段

       代碼段包含要執行的指令,數據段包含經過初始化的全局和靜態變量以及他們的值(可能不同的編譯器的某些特性稍有差異),BSS段的大小從可執行文件的bss段得到,緊跟在數據段之後,當bss內采取進入程序的地址空間後全部清0,數據段和BSS段統稱數據區。

       這些區域還不足以滿足一個程序的所有需求,因為一個進程還需要保存局部變量、臨時變量、傳遞到函數中的參數以及返回值等,堆棧段就是用於這個目的。當然也少不了堆空間,用於滿足進程的動態分配內存的需求。

       要注意的是一個用戶進程的虛擬地址空間最低部分並未被映射,就是說在進程的最低虛擬地址的幾K字節沒有做頁表映射(賦予物理地址),這樣它可以用於捕捉空指針和小整形值得指針引用內存情況。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved