- Разделение программы на произвольные сегменты.
Они не могут быть совершенно произвольными; например, я мог бы создать очень большой вектор для некоторого приложения:
#define N 10000000
int v[N];
....
for (i = 0; i < N; i++) {
v[i] = ...
}
Компилятор действительно хочет, чтобы v занимал последовательные ячейки памяти. Итак, ваш сегментатор должен знать об этих элементах; но становится еще хуже:
int *v;
v = malloc(sizeof(*v) * N);
for (i = 0; i < N; i++) {
v[i] = ...;
}
Обоснование накладных расходов:
Теперь вам нужно найти большой кусок физически непрерывной оперативной памяти во время выполнения, а поскольку у вас нет механизма перемещения, у вас нет возможности перемещать ранее выделенные фрагменты. Это проблема фрагментации; а без mmu в стиле страницы это очень сложно решить.
Вы можете решить эту проблему, превратив ваши скомпилированные языки в псевдоинтерпретируемые языки; но у чего больше накладных расходов: компиляция:
a = v[i];
into:
ld R0,R1+R2*4 ; + overhead of mmu.
or:
mov R0, R1+R2*4
call findseg
ld R0, R0
В общем случае накладные расходы с точки зрения ОЗУ составляют порядка 0,1%; для конкретного примера 10 байтов для страницы размером 4 КБ на архитектуре ARM64 или AMD64. lib c. Итак, в моей системе linux это около 2M текста + 40k данных; большинство программ используют очень небольшое количество этого. Благодаря подкачке памяти, только используемые биты библиотеки lib c должны занимать память. В системе 64G с 32 процессами экономия только lib c устраняет накладные расходы на таблицу страниц.
3: Отслеживание. Есть несколько способов атаковать это. Один из них - несколько размеров страницы, которые поддерживаются на большинстве архитектур. Во-вторых, ОС не обязана обеспечивать детализацию MMU. Например, в системе с 4 КБ страниц он может настаивать на передаче памяти только блоками по 64 КБ. Таким образом, уменьшаются накладные расходы на управление в 16 раз при незначительном увеличении потерь детализации.