Организация ядра логического адресного пространства Linux


8

Согласно «Write Great Code» почти во всех операционных системах оперативная память организована в следующие регионы:

ОС | Стек | Куча | Текст | Статический | Хранение / BSS

[В адресной моде все больше]

Процесс пространства пользователя использует более высокую область памяти для своих различных типов объектов данных.

Процесс пространства ядра также имеет различные типы объектов данных. Разделяют ли эти объекты области памяти пользовательского пространства (стек, куча и т. Д.) Или у них есть свои собственные отдельные подразделы (куча, стек и т. Д.), Расположенные в области ОС. И, если да, то в каком порядке они расположены , Спасибо,

Ответы:


5

Это неправильно по поводу заказа. Операционная система расположена в верхней части памяти, которая обычно выше отметки 3 ГБ (0xC0000000) в 32-разрядном ядре, а в 64-разрядном ядре это половина промежуточного значения 0x8000000000000000 IIRC.

Расположение стека и кучи рандомизировано. Нет реального правила упорядочения сегментов text / data / bss в основной программе, и каждая динамическая библиотека имеет их собственный набор, поэтому многие из них разбросаны по всей памяти.

В те времена, когда динозавры правили землей (более 20 лет назад), адресное пространство программы было линейным (без дыр), а порядок был текст, данные, bss, стек, куча. Также не было динамических библиотек или потоков. Это все изменилось с виртуальной памятью.

Процессы ядра полностью содержатся в части ядра адресного пространства; пользовательская часть игнорируется. Это позволяет ядру ускорить переключение контекста между потоками ядра, поскольку ему не нужно обновлять таблицы страниц, поскольку все процессы совместно используют одну и ту же часть ядра таблиц страниц.


4

Это не относится к «почти всем ОС». Типы представленных областей памяти довольно типичны, но нет причин, по которым они должны быть в каком-то определенном порядке, и может быть более одного фрагмента данного вида.

В Linux вы можете посмотреть на адресное пространство процесса, cat /proc/$pid/mapsгде $pidнаходится идентификатор процесса, например, cat /proc/$$/mapsпосмотреть на оболочку, из которой вы работаете cat, или cat /proc/self/mapsпосмотреть на catсобственные отображения процесса. Команда pmapпроизводит немного более приятный вывод.

08048000-08054000 r-xp 00000000 08:01 828061     /bin/cat
08054000-08055000 r--p 0000b000 08:01 828061     /bin/cat
08055000-08056000 rw-p 0000c000 08:01 828061     /bin/cat
08c7f000-08ca0000 rw-p 00000000 00:00 0          [heap]
b755a000-b7599000 r--p 00000000 08:01 273200     /usr/lib/locale/en_US.utf8/LC_CTYPE
b7599000-b759a000 rw-p 00000000 00:00 0 
b759a000-b76ed000 r-xp 00000000 08:01 269273     /lib/tls/i686/cmov/libc-2.11.1.so
b76ed000-b76ee000 ---p 00153000 08:01 269273     /lib/tls/i686/cmov/libc-2.11.1.so
b76ee000-b76f0000 r--p 00153000 08:01 269273     /lib/tls/i686/cmov/libc-2.11.1.so
b76f0000-b76f1000 rw-p 00155000 08:01 269273     /lib/tls/i686/cmov/libc-2.11.1.so
b76f1000-b76f4000 rw-p 00000000 00:00 0 
b770b000-b7712000 r--s 00000000 08:01 271618     /usr/lib/gconv/gconv-modules.cache
b7712000-b7714000 rw-p 00000000 00:00 0 
b7714000-b7715000 r-xp 00000000 00:00 0          [vdso]
b7715000-b7730000 r-xp 00000000 08:01 263049     /lib/ld-2.11.1.so
b7730000-b7731000 r--p 0001a000 08:01 263049     /lib/ld-2.11.1.so
b7731000-b7732000 rw-p 0001b000 08:01 263049     /lib/ld-2.11.1.so
bfbec000-bfc01000 rw-p 00000000 00:00 0          [stack]

Вы можете увидеть код и данные для чтения и записи (текст и BSS) из исполняемого файла, затем кучи, затем отображенный в память файл, затем немного больше данных для чтения и записи, затем код, данные только для чтения и данные для чтения. запись данных из общей библиотеки (снова текст и BSS), больше данных для чтения и записи, другая общая библиотека (точнее, динамический компоновщик) и, наконец, стек единственного потока.

Код ядра использует свои собственные диапазоны адресов. На многих платформах Linux использует верхнюю часть адресного пространства для ядра, часто верхнюю 1 ГБ. В идеале этого пространства должно быть достаточно для отображения кода ядра, данных ядра, а также системной памяти (ОЗУ) и каждого устройства, отображенного в памяти. На сегодняшних типичных 32-битных ПК это невозможно, для чего требуются искажения, которые представляют интерес только для хакеров ядра.

Пока код ядра обрабатывает системный вызов, в идеале (когда вышеупомянутые искажения отсутствуют) память процесса отображается по тем же адресам. Это позволяет процессам передавать данные в ядро, и ядро ​​может читать из указателя напрямую. Это не большой выигрыш, так как указатели все равно должны быть проверены (так что процесс не может обмануть ядро ​​в чтении из памяти, к которой у процесса не должно быть доступа).

Зоны памяти внутри пространства ядра Linux довольно сложны. Существует несколько различных пулов памяти, и основные различия заключаются не в том, откуда берется память, а в том, с кем она используется. Если вам интересно, начните с LDD3 .


1

Не ответ, но к вашему сведению, который требует больше места.

Я не думаю, что ваша концепция логического расположения адресов вообще верна.

Вы можете скомпилировать и запустить эту программу, чтобы увидеть, что имеет пользовательский процесс для адресов:

#include <stdio.h>
long global_initialized = 119234;
long global_uninitialized;
extern int _end, _edata, _etext;
int
main(int ac, char **av)
{
        long local;

        printf("main at 0x%lx\n", main);
        printf("ac at   0x%lx\n", &ac);
        printf("av at   0x%lx\n", &av);
        printf("av has  0x%lx\n", av);
        printf("initialized global at 0x%lx\n", &global_initialized);
        printf("global at             0x%lx\n", &global_uninitialized);
        printf("local at              0x%lx\n", &local);
        printf("_end at               0x%lx\n", &_end);
        printf("_edata at             0x%lx\n", &_edata);
        printf("_etext at             0x%lx\n", &_etext);
        return 0;
}

У Red Hat Enterprise Server, который у меня запущен, есть readelf, который можно использовать, чтобы указать, куда ядро ​​(логически) загрузит исполняемый файл:

readelf -S where

Показывает мне много той же адресной информации, которую whereдает вывод .

Я не думаю, что readelfбудет легко работать на ядре Linux (/ boot / vmlinuz или что-то подобное), и я думаю, что ядро ​​по умолчанию начинается с 0x80000000 в своем собственном адресном пространстве: оно не отображается в пользовательском процессе, несмотря на использование адрес над вершиной стека пользовательских областей в 0x7fffffff (x86, 32-битная адресация).


Спасибо за пример! Просто мое замечание по части Linux - я только что попробовал этот пример, как where.cпри использовании Ubuntu 11.04 gcc where.c -o where; сообщает "main at 0x80483c4". Пробовал readelf -S where, и он сообщает, скажем "[13] .text PROGBITS 08048310 ...", который выглядит примерно так? Хотя я также получаю "ac at 0xbfb035a0" и "local at 0xbfb0358c", и этот диапазон адресов (0xbf ...), по-видимому, не сообщается readelf -S.
sdaau

@sdaau - аргументы acи avи автоматическая переменная local, вероятно, будут иметь разные адреса при каждом вызове. Большинство современных ядер Linux имеют «рандомизацию расположения адресного пространства», чтобы затруднить использование переполнения буфера.
Брюс Эдигер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.