2011-02-14 59 views
2

已經有相當一段時間了,我一直使用C/C++進行編程,但有些地方仍然沒有出現。也許我沒有閱讀過寫得很好的權威材料。有關如何編譯的程序與操作系統交互的問題

(1)在Linux/Unix中,用戶程序的規模有多大?程序可以具有的最大堆棧大小?用戶程序可以使用堆中的最大內存量? (2)我知道一個C可執行文件有數據段,代碼段&堆棧段。如果程序進入許多遞歸調用,它將需要大量的堆棧。這是預定義大小的堆棧,還是隨着遞歸增加而增長。在成長的情況下,程序的地址空間也必須動態增加?如果是這樣,那會不會減慢程序? (3)類似地,當堆的內存被分配給運行時程序malloc的程序時,該堆的區域將需要被添加到程序的地址空間中?因此在這種情況下,程序的頁面表也需要更新。我的理解是否正確? (4)爲什麼2個文件(我打算組合成單個可執行文件)不能有一個同名的全局變量。這將有助於瞭解目標文件的外觀。

增加:

我從http://www.open-std.org/jtc1/sc22/wg...docs/n1256.pdf讀取ISO C99標準。 它說,第42頁:

identi網絡ERS 1的identi網絡ER在不同範圍內或在同一範圍內聲明的

6.2.2的聯繫不止一次可以 由通過這個過程被稱爲聯動指的是同一對象或函數。有 三種聯繫:外部,內部和無。

2在構成整個程序的翻譯單元和庫集中,每個 聲明具有外部鏈接的特定標識符表示相同的對象或 函數。在一個翻譯單元內,每個與內部 連接的標識符聲明表示相同的對象或功能。每個沒有 連接的標識符聲明表示一個獨特的實體。

3如果對象或函數的文件範圍標識符聲明包含存儲類speci fi er靜態,則標識符具有內部鏈接。

4對於在存儲類指定符extern中聲明的標識符,該標識符在該標識符的前置聲明是可見的範圍內,如果在先聲明指定了內部或外部鏈接,則在後面的聲明中標識符的鏈接與先前聲明中指定的聯繫相同。如果沒有事先聲明可見,或者事先聲明沒有指定鏈接,那麼標識符具有外部鏈接。

5如果函數的標識符聲明沒有存儲類指定符,則它的鏈接就像使用存儲類指定符extern聲明一樣。如果對象的標識符聲明有文件範圍和沒有存儲級別的speci fi er,其連接是外部的。

看完這個後,看起來如果我在2個源文件中聲明一個像int int a這樣的變量。那麼兩者都按照規則5和規則4具有外部聯繫,然後根據規則2,兩者都應該指向同一個對象。那麼爲什麼編譯器會產生問題。標準中暗示我們不能在2個源文件中聲明這樣的內容,並且這會導致編譯錯誤。

謝謝。

+0

http://www.linuxhowtos.org/Tips%20and%20Tricks/ulimit.htm – Anycorn 2011-02-14 06:32:46

+5

-1。四個問題應該作爲四個問題發佈,以便將來可以搜索。當前標題不可搜索 – 2011-02-14 06:44:28

回答

3

在回答您的問題 -

  1. 大多數操作系統都使用虛擬內存有每個程序認爲它擁有所有的地址空間。這意味着程序大小的限制通常是系統中物理內存的數量減去通常爲無效(認爲NULL)指針和內核預留的少量內存。最大內存限制通常取決於平臺,但在32位系統上,通常您的程序可以獲得將近4GB的內存,而在64位系統上的內存限制通常比這更多。當然,您還必須考慮磁盤的大小,這會限制您可以擁有多少虛擬內存。從理論上講,你可以編寫一個如此之大的程序,以至於你無法將其寫入內存中,但除非你使用的是嵌入式設備(這真的是一個問題),我懷疑這種情況會發生。

  2. 在大多數編程語言(包括C和C++)中,堆棧大小在編譯時並不固定,而是從程序運行時開始很小並且增長。然而,堆棧增長的方式通常使得這個特別便宜 - 爲了獲得更多的空間,你只需要將堆棧指針稍微撞一下。如果這會將您帶入目前尚未分配給程序的內存中,那麼操作系統通常會通過將頁面與堆棧現在所在的虛擬地址相關聯來爲您分配內存,這比執行堆分配要快得多。從長遠來看,這樣做的成本通常可以忽略不計,因此不要氣餒使用堆棧內存。有趣的是,一些較老的編程語言,即FORTRAN的第一代化身或者沒有動態堆棧空間,所以遞歸是不可能的。幾乎所有的現代語言都消除了這些限制。

  3. 你是對的 - 當需要更多堆空間時,通常調整頁表以增加堆空間。許多內存分配器選擇將大部分內存放入匿名內存映射文件中,以避免直接爲此使用堆空間,但原理基本相同 - 頁表被更新以爲新內存騰出空間。

  4. 如果在鏈接在一起的不同文件中有兩個全局變量,則兩個目標文件都將包含符號鏈接,表明它們需要引用具有該名稱的變量,並且這兩個目標文件都將包含定義說他們提供這個名字的象徵。當你嘗試將它們鏈接在一起時,鏈接器會注意到在兩個地方定義了相同的符號名稱,並且會報告一個錯誤,因爲它不確定哪個應該用作該全局變量的「實例」。爲了抵消這一點,至少在C中,可以標記全局變量static以使它們具有內部聯繫。這使得該符號不是全局導出的,所以生成的目標文件可以在內部解析引用或者破壞名稱,使其不與其他文件中的其他符號衝突。 C++支持這一點以及匿名命名空間功能,以達到相同的效果。

希望這有助於!如果有人在這裏發現錯誤或模棱兩可,請告訴我,我很樂意糾正它們。

2
  1. 是的,是的,是的。請參閱bash或man getrlimit中的「help ulimit」。

  2. 堆棧大小在程序啓動時設置並且不能增加。地址空間不會增加,因爲您使用的棧比以前使用的多,但內存使用量可能會增加。

    當使用「拆分堆棧」(例如在Google的Go中,但正在完成允許其他語言的gcc和其他語言的gcc和其他編譯器的工作)時,分配的附加內存不是「在堆棧上」堆棧指針被調整。這是動態管理的,因爲函數被調用並返回。

  3. 堆可能會根據需要增長。請參閱man sbrk以簡要了解這種情況,或查看various malloc implementations。你似乎理解它的要點。

  4. 因爲,至少對於C和C++來說,全局變量只能是,在整個程序中定義一次。兩個翻譯單元(您可以將TU視爲.o文件)可以使用同名全局變量,但它只能定義一次,並且必須在其他TU中聲明(使用正確的類型)。我不認爲理解對象文件的細節在這裏會有所幫助,但是理解C++中所謂的「一個定義規則」(ODR)的細節,或者它與您使用的任何語言都是等價的,可能會有用。


關於編輯,你有可能兩個課時定義一個int:

int this_is_a_definition; 

你不能做到這一點。您應該在標題中聲明:

extern int this_is_a_declaration; 

然後將該標題包含在需要變量的位置,並在一個TU中定義變量。當然,如果你不想使用相同變量在不同的課時,那麼你可能需要一個「內部」的名字,比如你與命名空間作用域的靜態或具名命名空間得到:

static int local_to_this_TU; 

namespace { 
    int another_local_to_this_TU; 
} 
+0

因此,我們對可增長的堆棧段存在強烈的分歧。需要一些證明鏈接。 – ulidtko 2011-02-14 06:54:54

相關問題