Страница замещения
[info] Внимание Этот раздел содержит сложный материал, реализация может иметь недочёты, код приведён исключительно в качестве примера.
Поскольку необходимо работать с «файлом замещения страниц», реализация замещения страниц будет использовать структуру фреймворка файловой системы (эксперимент пять). Однако эксперимент пять предоставит очень абстрактный интерфейс, и нам пока не нужно полностью понимать методы реализации последующих экспериментов.
Если вы решите следовать нашему экспериментальному руководству и самостоятельно реализовать операционную систему, вы можете прочитать этот раздел, но пока пропустить эту часть реализации. Это не повлияет на функциональность последующих экспериментов.
Одним из основных преимуществ виртуальной памяти является возможность использования ограниченного физического пространства памяти для создания почти бесконечного пространства виртуальной памяти. Принцип заключается в том, что только часть данных виртуальной памяти, соответствующих определённому адресу, хранится в физической памяти, а остальные данные хранятся на диске (внешнем устройстве). Когда поток выполняет операцию над адресом, который не находится в физической памяти, возникает исключение нехватки страницы (Page Fault). В этом случае операционная система вмешивается, обменивая часть данных между физической памятью и диском, чтобы необходимые данные были помещены в физическую память.
В таблице страниц бит Valid указывает, соответствует ли страница данным, находящимся в физической памяти. Поэтому операционная система также должна обновить таблицу страниц и обновить кэш.
Мы можем естественным образом предположить, что операционная система должна хранить в физической памяти те «страницы, которые часто используются», а «не так часто используемые» — во внешнем хранилище. Однако компьютер не может предсказать, какие адреса будут доступны в будущем. Мы можем использовать некоторые алгоритмы замещения, основанные на использовании памяти за предыдущий период времени, чтобы оценить, какие адреса могут быть использованы в будущем, и сохранить эти данные в физической памяти.
Проще говоря, мы можем предположить, что если программа только что обратилась к части памяти, то она, вероятно, снова обратится к ней. Таким образом, каждый раз, когда возникает нехватка страницы, страница, которая была последней использована в физической памяти, заменяется. Это называется алгоритмом LRU (Least Recently Used). Но этот алгоритм требует поддержания приоритетной очереди и обновления при каждом доступе к памяти. Очевидно, это нереалистично, поскольку приводит к слишком большим затратам.
Существует множество алгоритмов замещения, и мы можем начать знакомство с ними на Википедии.
Сначала мы создадим файл замещения страниц на диске для хранения всех заменённых страниц. Чтобы упростить реализацию, мы напрямую упаковываем файл SWAP_FILE
, полностью состоящий из нулей, в образ.
{% label %}user/Makefile{% endlabel %}
# Компиляция, упаковка, преобразование формата, резервирование места
build: dependency
@cargo build
@echo Targets: $(patsubst $(SRC_DIR)/%.rs, %, $(SRC_FILES))
@rm -rf $(OUT_DIR)
@mkdir -p $(OUT_DIR)
@cp $(BIN_FILES) $(OUT_DIR)
--> @dd if=/dev/zero of=$(OUT_DIR)/SWAP_FILE bs=1M count=16
@rcore-fs-fuse --fs sfs $(IMG_FILE) $(OUT_DIR) zip
@qemu-img convert -f raw $(IMG_FILE) -O qcow2 $(QCOW_FILE)
@qemu-img resize $(QCOW_FILE) +1G
Мы хотим, чтобы каждый процесс мог управлять этими страницами замещения (освобождать их при уничтожении), как он управляет физическими страницами, поэтому мы реализовали SwapTracker
, аналогичный FrameTracker
. Его конкретная реализация использует некоторые операции файловой системы, и заинтересованные читатели могут обратиться к исходному коду. Вкратце: SwapTracker
записывает страницу, заменённую из физической памяти, и предоставляет несколько удобных интерфейсов операций.
{% label %}os/src/fs/swap.rs{% endlabel %}
/// Подобно [`FrameTracker`], эквивалентно `Box<страница в файле замещения>`
///
/// Внутреннее сохранение индекса этой страницы в файле замещения
///
/// [`FrameTracker`]: crate::memory::frame::FrameTracker
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct SwapTracker(pub(super) usize);
impl SwapTracker {
/// Из файла замещения выделяется новая страница
pub fn new() -> MemoryResult<Self> {
...
}
/// Чтение данных страницы
pub fn read(&self) -> [u8; PAGE_SIZE] {
...
}
/// Запись данных страницы
pub fn write(&self, data: &[u8; PAGE_SIZE]) {
...
}
}
impl Drop for SwapTracker {
fn drop(&mut self) {
...
}
}
Затем мы определили интерфейс алгоритма замещения и реализовали очень простой алгоритм замещения.
{% label %}os/src/memory/mapping/swapper.rs{% endlabel %}
/// Управление операциями замещения для сопоставленных страниц потока
pub trait Swapper {
/// Создание нового распределителя с ограничением квоты
fn new(quota: usize) -> Self;
/// Достигнут ли предел квоты?
fn full(&self) -> bool;
/// Вытащить набор сопоставлений
fn pop(&mut self) -> Option<(VirtualPageNumber, FrameTracker)>;
/// Добавить набор сопоставлений (не вызывается при достижении предела квоты)
fn push(&mut self, vpn: VirtualPageNumber, frame: FrameTracker);
/// Сохранять только элементы, соответствующие определённому условию (используется для удаления диапазона виртуальных адресов)
fn retain(&mut self, predicate: impl Fn(&VirtualPageNumber) -> bool);
}
Здесь Swapper
заменяет роль mapped_pairs: Vec<(VirtualPageNumber, FrameTracker)>
, которую играет Mapping
. Поэтому мы заменяем члены Mapping
:
{% label %}os/src/memory/mapping/mapping.rs{% endlabel %}
/// Сопоставление памяти процесса
pub struct Mapping {
/// Сохранить все используемые таблицы страниц
page_tables: Vec<PageTableTracker>,
/// Корневая таблица страниц физического номера страницы
root_ppn: PhysicalPageNumber,
/// Все сопоставления физических страниц
mapped_pairs: SwapperImpl,
/// Страницы, сохранённые в файле виртуальной памяти
swapped_pages: HashMap<VirtualPageNumber, SwapTracker>,
}
Наконец, давайте реализуем замещение памяти: при возникновении исключения нехватки страницы найдите необходимый номер страницы, необходимые данные страницы и замените один физический номер страницы и данные страницы, обменяв их.
{% label %}os/src/memory/mapping/mapping.rs{% endlabel %}
impl Mapping {
/// Обработка исключения нехватки страницы
pub fn handle_page_fault(&mut self, stval: usize) -> MemoryResult<()> {
let vpn = VirtualPageNumber::floor(stval.into());
let swap_tracker = self
.swapped_pages
.remove(&vpn)
.ok_or("stval page is not mapped")?;
let page_data = swap_tracker.read();
if self.mapped_pairs.full() {
// Вытащить сопоставление
let (popped_vpn, mut popped_frame) = self.mapped_pairs.pop().unwrap();
// print!("{:x?} -> {:x?}", popped_vpn, vpn);
// Обмен данными
swap_tracker.write(&*popped_frame);
(*popped_frame).copy_from_slice(&page_data);
... // Изменить отображение таблицы страниц
self.invalidate_one(popped_vpn)?;
self.remap_one(vpn, popped_frame.page_number())?;
// Обновить запись
self.mapped_pairs.push(vpn, popped_frame);
self.swapped_pages.insert(popped_vpn, swap_tracker);
} else {
// Если текущий лимит ещё не достигнут, можно продолжить распределение физических страниц. В настоящее время такая ситуация не возникает
// Добавить новое сопоставление
let mut frame = FRAME_ALLOCATOR.lock().alloc()?;
// Скопировать данные
(*frame).copy_from_slice(&page_data);
// Изменить сопоставление
self.remap_one(vpn, frame.page_number())?;
// Обновить запись
self.mapped_pairs.push(vpn, frame);
}
Ok(())
}
Затем, вызвав функцию выше при возникновении исключения отсутствия страницы, мы завершаем реализацию замены страницы.
```rust
/// Обработка исключения отсутствия страницы
///
/// todo: теоретически здесь необходимо определить тип доступа и сравнить его с флагами в таблице страниц
fn page_fault(context: &mut Context, stval: usize) -> Result<*mut Context, String> {
println!("page_fault");
let current_thread = PROCESSOR.lock().current_thread();
let memory_set = &mut current_thread.process.write().memory_set;
memory_set.mapping.handle_page_fault(stval)?;
memory_set.activate();
Ok(context)
}
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )