Для реализации таблицы страниц Sv39 мы решили использовать в качестве основы одну из выделенных физических страниц, которая автоматически уничтожается (FrameTracker), и заполнить её данными. Каждая запись в таблице страниц представляет собой 8-байтовый элемент таблицы страниц.
Сначала мы создали функцию, которая позволяет получить трёхуровневый VPN на основе виртуального номера страницы:
impl VirtualPageNumber {
/// Получить первый, второй и третий уровни номера страницы
pub fn levels(self) -> [usize; 3] {
[
self.0.get_bits(18..27),
self.0.get_bits(9..18),
self.0.get_bits(0..9),
]
}
}
Затем мы реализовали таблицу страниц, представляющую собой оболочку для 8-байтового значения usize. Мы также можем использовать только что добавленные ящики для работы с битами для создания удобных функций для извлечения определённых сегментов:
/// Структура элемента таблицы страниц Sv39
#[derive(Copy, Clone, Default)]
pub struct PageTableEntry(usize);
/// Позиция битов флагов в элементе таблицы страниц Sv39
const FLAG_RANGE: core::ops::Range<usize> = 0..8;
/// Позиция номера физической страницы в элементе таблицы страниц Sv39
const PAGE_NUMBER_RANGE: core::ops::Range<usize> = 10..54;
impl PageTableEntry {
/// Создать элемент таблицы страниц с заданным номером страницы и флагами
pub fn new(page_number: Option<PhysicalPageNumber>, mut flags: Flags) -> Self {
// Установить бит Valid в зависимости от наличия page_number
flags.set(Flags::VALID, page_number.is_some());
Self(
*0usize
.set_bits(FLAG_RANGE, flags.bits() as usize)
.set_bits(PAGE_NUMBER_RANGE, page_number.unwrap_or_default().into()),
)
}
/// Обновить номер физической страницы, установив бит Valid в соответствии с наличием ppn
pub fn update_page_number(&mut self, ppn: Option<PhysicalPageNumber>) {
if let Some(ppn) = ppn {
self.0
.set_bits(FLAG_RANGE, (self.flags() | Flags::VALID).bits() as usize)
.set_bits(PAGE_NUMBER_RANGE, ppn.into());
} else {
self.0
.set_bits(FLAG_RANGE, (self.flags() - Flags::VALID).bits() as usize)
.set_bits(PAGE_NUMBER_RANGE, 0);
}
}
/// Получить номер страницы
pub fn page_number(&self) -> PhysicalPageNumber {
PhysicalPageNumber::from(self.0.get_bits(10..54))
}
/// Получить адрес
pub fn address(&self) -> PhysicalAddress {
PhysicalAddress::from(self.page_number())
}
/// Получить флаги
pub fn flags(&self) -> Flags {
unsafe { Flags::from_bits_unchecked(self.0.get_bits(..8) as u8) }
}
/// Проверить, является ли элемент пустым (может быть непустым, но не Valid)
pub fn is_empty(&self) -> bool {
self.0 == 0
}
}
impl core::fmt::Debug for PageTableEntry {
fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter
.debug_struct("PageTableEntry")
.field("value", &self.0)
.field("page_number", &self.page_number())
.field("flags", &self.flags())
.finish()
}
}
bitflags! {
/// 8 флагов в элементе таблицы страниц
#[derive(Default)]
pub struct Flags: u8 {
/// Бит Valid
const VALID = 1 << 0;
/// Бит Readable
const READABLE = 1 << 1;
/// Бит Writable
const WRITABLE = 1 << 2;
/// Бит Executable
const EXECUTABLE = 1 << 3;
/// Пользовательский бит
const USER = 1 << 4;
/// Глобальный бит, который мы не используем
const GLOBAL = 1 << 5;
/// Использованный бит, используемый для алгоритма замены
const ACCESSED = 1 << 6;
/// Изменённый бит, используемый для алгоритма замены
const DIRTY = 1 << 7;
}
}
### Таблица
Имея таблицу страниц, мы можем создать 4 КБ физической страницы с 512 последовательными элементами таблицы страниц. Также можно добавить дополнительные функции, такие как многоуровневое добавление сопоставления, чтобы создать полноценную таблицу страниц.
```rust
/// Таблица страниц с 512 элементами таблицы страниц
///
/// Обратите внимание, что мы не будем использовать обычный синтаксис Rust для создания `PageTable`. Вместо этого мы выделим физическую страницу, соответствующую определённому участку физической памяти, и будем напрямую читать и записывать её как таблицу страниц. В операционной системе мы будем использовать указатель [`PageTableTracker`] для отслеживания этой таблицы страниц.
#[repr(C)]
pub struct PageTable {
pub entries: [PageTableEntry; PAGE_SIZE / 8],
}
impl PageTable {
/// Обнулить таблицу
pub fn zero_init(&mut self) {
self.entries = [Default::default(); PAGE_SIZE / 8];
}
}
Однако мы не будем постоянно передавать этот огромный массив между функциями. Вместо этого мы воспользуемся особенностями Rust и создадим структуру PageTableTracker
, которая будет служить оболочкой для FrameTracker
. Эта структура будет использоваться для записи и управления выделенной физической страницей, которую мы рассматриваем как таблицу страниц. Кроме того, PageTableTracker
и PageTableEntry
используют автоматическое разрешение ссылок в Rust, что упрощает реализацию последующих функций. Например, мы можем рассматривать PageTableTracker
как PageTable
, а если PageTableEntry
указывает на другую PageTable
, то... Мы можем напрямую и удобно позволить компилятору автоматически выполнить эту работу.
/// Подобно [`FrameTracker`], используется для отслеживания таблицы страниц в памяти
///
/// Обратите внимание, что «настоящая таблица страниц» будет находиться в выделенной нами физической странице, а не в стеке или куче операционной системы.
/// А `PageTableTracker` будет храниться в метаданных потока (то есть в куче операционной системы) и указывать на его настоящую таблицу страниц.
///
/// Когда `PageTableTracker` удаляется, он автоматически удаляет `FrameTracker`, освобождая кадр.
pub struct PageTableTracker(pub FrameTracker);
impl PageTableTracker {
/// Инициализирует выделенный кадр, формируя пустую таблицу страниц
pub fn new(frame: FrameTracker) -> Self {
let mut page_table = Self(frame);
page_table.zero_init();
page_table
}
/// Получает номер физической страницы
pub fn page_number(&self) -> PhysicalPageNumber {
self.0.page_number()
}
}
На этом мы закончили с таблицей страниц в физической странице. Далее мы сделаем более детальное отображение сегментов в ядре и заменим первоначальную грубую таблицу отображения страниц.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )