1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/rcore-os-rCore-Tutorial

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
part-3.md 9.6 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 29.11.2024 21:14 7f8a4fd

Реализация таблицы страниц

Для реализации таблицы страниц Sv39 мы решили использовать в качестве основы одну из выделенных физических страниц, которая автоматически уничтожается (FrameTracker), и заполнить её данными. Каждая запись в таблице страниц представляет собой 8-байтовый элемент таблицы страниц.

Для работы с битовыми уровнями нам потребуются два дополнительных ящика:

  • bitflags версии 1.2.1;
  • bit_field версии 0.10.0.

Сначала мы создали функцию, которая позволяет получить трёхуровневый 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 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/rcore-os-rCore-Tutorial.git
git@api.gitlife.ru:oschina-mirror/rcore-os-rCore-Tutorial.git
oschina-mirror
rcore-os-rCore-Tutorial
rcore-os-rCore-Tutorial
master