Каково допустимое адресное пространство для пользовательского процесса? (OS X и Linux) - PullRequest
4 голосов
/ 05 марта 2011

В документации по системному вызову mmap сказано, что функция завершится ошибкой, если:

MAP_FIXED был указан и адрес аргумент не был выровнен по странице или частично желаемого адресного пространства находится из допустимого адресного пространства для пользовательский процесс.

Я нигде не могу найти документацию о том, что будет действительным адресом для карты (Я заинтересован в том, чтобы сделать это на OS X и Linux, в идеале один и тот же адрес будет действителен для обоих ...)

Ответы [ 2 ]

7 голосов
/ 05 марта 2011

Ядро Linux резервирует часть виртуального адресного пространства для себя, где пользовательское пространство (почти) не имеет доступа и не может ничего сопоставить.Вы ищете то, что называется «разделение пространства пользователя / пространства ядра».

На архитектуре i386 по умолчанию используется 3G / 1G единица - пользовательское пространство получает меньше 3 ГБ виртуального адресного пространства, ядро ​​получает верхние 1 ГБ, дополнительно естьРазделение 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)
1 голос
/ 05 марта 2011

Это зависит от ряда факторов, многие из которых не находятся под вашим контролем. Как упоминал адобриан, в зависимости от ОС у вас есть различные фиксированные верхние пределы, за которыми лежат код ядра и данные. Обычно этот верхний предел составляет не менее 2 ГБ в 32-разрядных ОС; некоторые ОС предоставляют дополнительное адресное пространство. 64-разрядные операционные системы обычно предоставляют верхний предел, контролируемый количеством виртуальных адресных битов, поддерживаемых вашим ЦП (обычно это адресное пространство не менее 40 бит). Однако есть и другие факторы, которые вы не можете контролировать:

  • В последней версии linux, сопоставления mmap ниже адрес, настроенный в /proc/sys/vm/mmap_min_addr, будет отклонен.
  • Вы не можете создавать сопоставления, которые перекрываются с любыми существующими сопоставлениями. Поскольку динамический компоновщик может свободно отображаться в любом месте, которое не пересекается с фиксированными разделами вашего исполняемого файла, это означает, что потенциально любой адрес может быть отклонен.
  • Ядро может внедрять другие дополнительные сопоставления, например, системный вызов gate .
  • malloc может выполнять mmaps самостоятельно, которые расположены в несколько произвольном месте

Таким образом, нет абсолютно никакой гарантии, что MAP_FIXED будет успешным, поэтому его обычно следует избегать.

Единственное место, которое я видел, где необходимо MAP_FIXED, это код запуска Wine, который резервирует (используя MAP_FIXED) все адреса выше 2G, чтобы избежать путаницы в коде Windows, который предполагает, что сопоставления не будут отображаться. с отрицательным адресом. Это, конечно, узкоспециализированное использование флага.

Если вы пытаетесь сделать это, чтобы избежать необходимости работать со смещениями в разделяемой памяти, один из вариантов будет заключать в класс указатели для автоматической обработки смещений:

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;
        }
};

Примечание. Это непроверенный код, но он должен дать вам основную идею.

...