LINUX.ORG.RU

История изменений

Исправление firkax, (текущая версия) :

Вот примерная (условная и упрощённая, но суть передаёт) схема работы страничной виртуальной памяти на 32-битной системе с PAE:

struct process {
  ptr64 pages[1048576];
};

// этот массив хранится напрямую в физической памяти, на самом деле он конечно выделяется динамически а не хранится для всех возможных pid-ов
struct process processes[MAX_PID];

Допустим, в коде (обычном) встречается чтение из ячейки с адресом p:
ptr32 p = 0x12345678;
x = *(char*)p;
эта операция на самом деле выполняется так:
p64 = processes[getpid()].pages[p >> 12]; // p>>12 это 0x12345-й элемент массива
// ^ чтобы тут не лазить каждый раз в память, в проце есть специальный кеш, это не тот кеш который L1 L2 L3 а отдельный; он свой у каждого ядра и у каждого гипертрединг-потока, сбрасывается ядром при переключении процесса, либо в нём сбрасываются ядром отдельные записи когда надо подправить таблицу pages[]

if(p64 & FLAG_MISSING) {
  raise_page_fault_exception();  // вызов ОС - страницы нет в физ. памяти, назад уже не вернёмся
  // ОС либо сегфолтнет процесс, если страницы этой вобще нет и не предполагалсь,
  // либо аллоцирует в физ. памяти новую пустую страницу, если это первое обращение к блоку после юзерспейсной аллокации (оверкоммит)
  // либо прочитает её из свапа, если она в свапе
  // после этого ОС вернёт управление процессу на то же место чтоб он попробовал прочитать ещё раз заново
}
if(!(p64 & FLAG_ALLOW_USERSPACE)) {
  raise_page_fault_exception(); // вызов ОС - процесс хочет нарушить права доступа, назад уже не вернёмся
}
p64 = (p64 & 0xFFFFFF000) | (p & 0x0FFF); 
// ^ в младших 12 битах p64 - флаги, старшие 28 бит зарезервированы
// младшие биты берём из p, там 0x678
x = *(char*)p64;

Кстати, если PAE выключен, то отличие будет только такое: таблица pages станет 32-битной, и p64 тоже станет 32-битным (назовём p32), вся логика остаётся как была.

Исправление firkax, :

Вот примерная (условная и упрощённая, но суть передаёт) схема работы страничной виртуальной памяти на 32-битной системе с PAE:

struct process {
  ptr64 pages[1048576];
};

// этот массив хранится напрямую в физической памяти, на самом деле он конечно выделяется динамически а не хранится для всех возможных pid-ов
struct process processes[MAX_PID];

Допустим, в коде (обычном) встречается чтение из ячейки с адресом p:
ptr32 p = 0x12345678;
x = *(char*)p;
эта операция на самом деле выполняется так:
p64 = processes[getpid()].pages[p >> 12]; // p>>12 это 0x12345-й элемент массива
// ^ чтобы тут не лазить каждый раз в память, в проце есть специальный кеш, это не тот кеш который L1 L2 L3 а отдельный; он свой у каждого ядра и у каждого гипертрединг-потока, сбрасывается ядром при переключении процесса, либо в нём сбрасываются ядром отдельные записи когда надо подправить таблицу pages[]

if(p64 & FLAG_MISSING) {
  raise_page_fault_exception();  // вызов ОС - страницы нет в физ. памяти, назад уже не вернёмся
  // ОС либо сегфолтнет процесс, если страницы этой вобще нет и не предполагалсь,
  // либо аллоцирует в физ. памяти новую пустую страницу, если это первое обращение к блоку после юзерспейсной аллокации (оверкоммит)
  // либо прочитает её из свапа, если она в свапе
  // после этого ОС вернёт управление процессу на то же место чтоб он попробовал прочитать ещё раз заново
}
if(!(p64 & FLAG_ALLOW_USERSPACE)) {
  raise_page_fault_exception(); // вызов ОС - процесс хочет нарушить права доступа, назад уже не вернёмся
}
p64 = (p64 & 0xFFFFFF000) | (p & 0x0FFF); 
// ^ в младших 12 битах p64 - флаги, старшие 28 бит зарезервированы
// младшие биты берём из p, там 0x678
x = *(char*)p64;

Исходная версия firkax, :

Вот примерная (условная и упрощённая, но суть передаёт) схема работы страничной виртуальной памяти на 32-битной системе с PAE:

struct process {
  ptr64 pages[1048576];
};

// этот массив хранится напрямую в физической памяти, на самом деле он конечно выделяется динамически а не хранится для всех возможных pid-ов
struct process processes[MAX_PID];

Допустим, в коде (обычном) встречается чтение из ячейки с адресом p:
ptr32 p = 0x12345678;
x = *(char*)p;
эта операция на самом деле выполняется так:
p64 = processes[getpid()].pages[p >> 12]; // p>>12 это 0x12345-й элемент массива
// ^ чтобы тут не лазить каждый раз в память, в проце есть специальный кеш, это не тот кеш который L1 L2 L3 а отдельный; он свой у каждого ядра и у каждого гипертрединг-потока, сбрасывается ядром при переключении процесса, либо в нём сбрасываются ядром отдельные записи когда надо подправить таблицу pages[]

if(p64 & FLAG_MISSING) {
  raise_page_fault_exception(); // вызов ОС - страницы нет в физ. памяти, назад уже не вернёмся
}
if(!(p64 & FLAG_ALLOW_USERSPACE)) {
  raise_page_fault_exception(); // вызов ОС - процесс хочет нарушить права доступа, назад уже не вернёмся
}
p64 = (p64 & 0xFFFFFF000) | (p & 0x0FFF); 
// ^ в младших 12 битах p64 - флаги, старшие 28 бит зарезервированы
// младшие биты берём из p, там 0x678
x = *(char*)p64;