2011-03-05 98 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

相关问题