2011-03-05 45 views
4

mmap系統調用的文件說,如果該函數將失敗:用戶進程的有效地址空間是什麼? (OS X和Linux)

MAP_FIXED被指定和地址 說法是不對齊的頁面,或部分所需的地址空間的 駐留 在 用戶進程的有效地址空間之外。

我找不到任何地方的文檔說什麼是一個有效的地址映射。 (我有興趣在OS X和Linux上執行此操作,理想情況下,相同的地址對於這兩種操作都是有效的)。

回答

7

Linux內核爲自己保留了虛擬地址空間的一部分,以便用戶空間具有(幾乎)無法訪問並且無法映射任何內容。你正在尋找所謂的「用戶空間/內核空間拆分」。

在i386拱默認是3G/1G一個 - 用戶空間變低3 GB的虛擬地址空間,核上得到1 GB,另外有2G/2G和1G/3G分裂:

config PAGE_OFFSET 
     hex 
     default 0xB0000000 if VMSPLIT_3G_OPT 
     default 0x80000000 if VMSPLIT_2G 
     default 0x78000000 if VMSPLIT_2G_OPT 
     default 0x40000000 if VMSPLIT_1G 
     default 0xC0000000 
     depends on X86_32 

在x86_64,用戶空間住在的虛擬地址空間(目前)48位的下半部分:

/* 
* User space process size. 47bits minus one guard page. 
*/ 
#define TASK_SIZE_MAX ((1UL << 47) - PAGE_SIZE) 
+0

謝謝!我應該補充一點,我只對64位系統感興趣,這就是爲什麼固定映射甚至是一個考慮因素,因爲它似乎必須有足夠的空間來避免衝突不應該太困難。 – James 2011-03-05 17:50:57

+0

這是Linux的一個很好的答案。任何人都可以獲得64位OSX的相同信息? – Quuxplusone 2012-10-10 19:10:54

+0

回覆我自己的評論:在OSX上,它似乎大致爲'0x1_00000000'(從我的測試用例中運行到運行不等)直到'0x7fff_ffffffff';即內核保留低4GB的虛擬地址空間,並且虛擬地址被限制在與x86-64上的物理地址相同的48位範圍內。 – Quuxplusone 2012-10-10 19:34:27

1

這變化的基礎上多種因素,其中有許多是不是你的控制之下。正如Adobriyan所說,根據操作系統的不同,您有各種固定的上限,超出這些上限是內核代碼和數據所在。在32位操作系統上,通常這個上限是至少 2GB;一些操作系統提供額外的地址空間。 64位操作系統通常提供由CPU支持的虛擬地址位數(通常至少爲40位地址空間)控制的上限。但也有另一些因素超出你的控制:

  • 在最近的Linux版本,MMAP映射下面/proc/sys/vm/mmap_min_addr配置的地址將被拒絕。
  • 您無法創建與任何現有映射重疊的映射。由於動態鏈接器可自由映射任何與可執行文件的固定部分不重疊的地方,這意味着可能會拒絕任何地址。
  • 內核可能會注入其他附加映射,如system call gate
  • malloc可以自行,其放置在一定程度上任意位置

這樣執行mmaps,沒有辦法絕對是機制保障將MAP_FIXED成功,所以它通常應避免。

我見過的唯一地方MAP_FIXED需要的是在酒的啓動代碼,其儲量(使用MAP_FIXED)2G以上的所有地址,以避免混淆窗口代碼不承擔任何映射都不會顯示出來了否定地址。當然,這是對旗幟的高度專業化使用。

如果你想這樣做是爲了避免處理共享內存中的偏移量,其中一個方案是包的指針在一個類來自動處理偏移:

template<typename T> 
class offset_pointer { 
    private: 
     ptrdiff_t offset; 
    public: 
     typedef T value_type, *ptr_type; 
     typedef const T const_type, *const_ptr_type; 

     offset_ptr(T *p) { set(p); } 
     offset_ptr() { set(NULL); } 

     void set(T *p) { 
      if (p == NULL) 
       offset = 1; 
      else 
       offset = (char *)p - (char *)this; 
     } 
     T *get() { 
      if (offset == 1) return NULL; 
      return (T*)((char *)this + offset); 
     } 

     const T *get() const { return const_cast<offset_pointer>(this)->get(); } 

     T &operator*() { return *get(); } 
     const T &operator*() const { return *get(); } 
     T *operator->() { return get(); } 
     const T *operator->() const { return get(); } 

     operator T*() { return get(); } 
     operator const T*() const { return get(); } 

     offset_pointer operator=(T *p) { set(p); return *this; } 
     offset_pointer operator=(const offset_pointer &other) { 
      offset = other.offset; 
      return *this; 
     } 
}; 

注:此是未經測試的代碼,但應該給你基本的想法。

+0

謝謝:我有興趣使用它,以便我可以在進程之間傳遞包含C++對象的消息(實際上使用boost :: shared_memory),而不必使用偏移指針。 – James 2011-03-05 17:52:20

+0

@Oopopulated,增加了一個潛在的解決方案:) – bdonlan 2011-03-05 18:43:54

+0

那麼,boost已經有一個'offset_ptr',但我試圖避免必須使用它,所以沒有必要重新實現可能使用的每種類型共享內存。 – James 2011-03-06 11:44:59

相關問題