簡體   English   中英

有關已編譯程序如何與操作系統交互的問題

[英]Questions on how compiled programs interact with the operating system

已經有一段時間了,我一直在用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頁上說:

6.2.2標識符的鏈接1可以通過稱為鏈接的過程使在不同范圍或同一范圍中聲明多次的標識符引用同一對象或功能。鏈接有三種:外部,內部和內部。沒有。

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

3如果對象或函數的文件范圍標識符的聲明包含存儲類指定者靜態變量,則該標識符具有內部鏈接。

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

5如果一個函數的標識符聲明沒有存儲類說明符,則其鏈接的確定與使用存儲類指定符extern聲明時完全相同。如果一個對象的標識符聲明具有文件范圍且沒有存儲空間類規范,它的鏈接是外部的。

看完這篇文章后,如果我在2個源文件中聲明一個像int a這樣的變量,就可以了。 那么它們都具有根據規則5和4的外部鏈接,然后具有根據規則2的外部鏈接。 那為什么編譯器會產生問題。 在標准的哪里暗示我們不能在2個源文件中這樣聲明,這應該引發編譯錯誤。

謝謝。

在回答您的問題時-

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

  2. 在大多數編程語言(包括C和C ++)中,堆棧大小在編譯時並不固定,而是開始時很小,並隨着程序運行而增長。 但是,堆棧的增長方式通常使它特別便宜-要獲得更多空間,您只需要稍微碰一下堆棧指針即可。 如果這將您帶入當前尚未為程序分配的內存,則OS通常會通過將頁面與堆棧現在所在的虛擬地址相關聯來為您分配內存,這比進行堆分配要快得多。 從長遠來看,這樣做的成本通常可以忽略不計,因此不要灰心使用堆棧存儲器。 有趣的是,某些較舊的編程語言(即FORTRAN的第一個版本)沒有動態堆棧空間,因此無法進行遞歸。 幾乎所有現代語言都消除了這些限制。

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

  4. 如果在不同文件中有兩個全局變量鏈接在一起,則這兩個目標文件將包含符號鏈接,表明它們需要引用具有該名稱的變量,並且兩個目標文件都將包含定義,表明它們提供了一個此名稱的符號。 當您嘗試將它們鏈接在一起時,鏈接器會注意到在兩個地方都定義了相同的符號名稱,並會報告錯誤,因為無法確定應將哪個符號用作該全局變量的“實例”。 為了解決這個問題,至少在C語言中,您可以將全局變量標記為static以使其具有內部鏈接。 這使得符號不能全局導出,因此生成的目標文件可以在內部解析引用,也可以修改名稱,以免與其他文件中的其他符號沖突。 C ++允許此功能以及匿名名稱空間功能實現相同的效果。

希望這可以幫助! 如果有人在這里發現錯誤或歧義,請告訴我,我們很樂意糾正它們。

  1. 是的,是的,是的。 請參見bash或man getrlimit中的“幫助ulimit”。

  2. 堆棧大小是在程序啟動時設置的,無法增加。 隨着使用的堆棧數量比以前使用的更多,地址空間不會增加,但是內存使用量可能會增加。

    當使用“拆分堆棧”時(例如在Google的Go中,但正在做一些工作以允許在gcc和其他語言的其他編譯器中使用),分配的額外內存不在“堆棧上”,並且調整了堆棧指針。 當調用函數並返回它們時,將對其進行動態管理。

  3. 堆可能會根據需要增長。 請參閱man sbrk以獲得有關此情況的簡短概述,或查看各種 malloc 實現 您似乎了解它的要旨。

  4. 因為至少在C和C ++中,全局變量只能在整個程序中定義一次 兩個轉換單元(您可以將TU視為.o文件)可以使用相同名稱的全局變量,但只能定義一次,並且必須在其他TU中聲明(具有正確的類型)。 我認為了解目標文件的詳細信息並不會有所幫助,但了解C ++中的“單一定義規則”(ODR)的詳細信息或等效的語言(無論您使用的是哪種語言)可能會很有用。


關於編輯,您可能已經在兩個TU中定義了一個int:

int this_is_a_definition;

你不可以做這個。 您應該在標頭中聲明它:

extern int this_is_a_declaration;

然后在需要變量的位置包含該標頭,並在一個TU中准確定義變量。 當然,如果您不想在不同的TU中使用相同的變量,那么您可能需要一個“內部”名稱,例如使用命名空間范圍靜態或未命名的命名空間:

static int local_to_this_TU;

namespace {
  int another_local_to_this_TU;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM