Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

Page Table Management

Линукс поддерживает концепцию 3-уровневой page-tables.
Различие между различными типами страниц расплывчато и определимо с помощью флагов .
 Основные термины :
   Memory Management Unit (MMU)
   Page Middle Directory (PMD)
   Page Global Directory (PGD)
   Translation Lookaside Buffer (TLB)
 У каждого процесса есть указатель mm_struct->pgd на свою PGD . 
 PGD физически - страница памяти .
 Он включает массив pgd_t . При загрузке mm_struct->pgd копируется в регистр cr3 .
 В свою очередь , каждая строка PGD включает в себя указатель 
 на страницу памяти с массивом PMD типа pmd_t , 
 а та в свою очередь указывает на страницы PTE типа pte_t , которые указывают
 наконец-то на реальные адреса пользовательских данных .
 
  На рисунке видно , что линейный адрес состоит из 4 частей .
 Есть группа макросов - SHIFT,SIZE,MASK . Макрос SHIFT определяtт битовую 
 длину каждой части .
 Макрос MASK используется как коэффициент для подправки с учетом 
 выравнивания.
 Макрос SIZE определяет количество байт у адреса для каждого уровня .
 Связь между макросами MASK и SIZE показана ниже .
 
   #define PAGE_SHIFT       12
   #define PAGE_SIZE	   (1UL << PAGE_SHIFT)
   #define PAGE_MASK	   (~(PAGE_SIZE-1))
  PAGE_SHIFT=12 - это длина смещения в линейном адресе в битах . 
 Не для интеловской архитектуры это число может быть другим . 
 PAGE_SIZE = 2^PAGE_SHIFT. PAGE_MASK вычисляется как логическое
 отрицание от (PAGE_SIZE-1).
 PMD_SHIFT , PMD_SIZE , PMD_MASK берутся из page table 2-го уровня 
 и вычисляются аналогично .
 PGDIR_SHIFT , PGDIR_SIZE , PGDIR_MASK берутся из page table 1-го , 
 верхнего уровня и вычисляются аналогично .
 Макрос PTRS_PER_x - это число строк в page table .
 PTRS_PER_PGD - число строк в PGD , которое обычно фиксировно и равно 1024 .
 PTRS_PER_PMD - число строк в PMD , которое = 1 .
 PTRS_PER_PTE - число строк для page table самого низкого уровня и обычно = 1024 .
 Следующая таблица показывает назначение отдельных битов в pte_t :

Page Table Entry Protection and Status Bits

Bit

Function

_PAGE_PRESENT

Page is resident in memory and not swapped out.

_PAGE_PROTNONE

Page is resident, but not accessible.

_PAGE_RW

Set if the page may be written to

_PAGE_USER

Set if the page is accessible from userspace

_PAGE_DIRTY

Set if the page is written to

_PAGE_ACCESSED

Set if the page is accessed

  В файле  определены макросы для работы с page table .
 3 макроса для работы с page directory :
  pgd_offset() - возвращает адрес PGD
  pmd_offset() - возвращает адрем PMD
  pte_offset() - возвращает адрес PTE
  pte_none(), pmd_none() and pgd_none() - эти макросы обрабатывают 
  отсутствие адреса в page table
  pte_present(), pmd_present() and pgd_present() - аналогично для наличия
  pte_clear(), pmd_clear() and pgd_clear() - убирают строку в page table
 Пример использования : 
       pgd_t *pgd;
       pmd_t *pmd;
       pte_t *ptep, pte;
       pgd = pgd_offset(mm, address);
       if (pgd_none(*pgd) || pgd_bad(*pgd))   goto out;
 Еще одна группа макросов проверяет статус адресов и их права 
 на запись,выполнение :
  pte_read(), set with pte_mkread() , pte_rdprotect()
  pte_write(), set with pte_mkwrite(),pte_wrprotect()
  pte_exec(), set with pte_mkexec(),pte_exprotect().
 
 Следующая группа функций и макросов имеет отношение к маппингу адресов PTE.
 Макрос mk_pte()  формирует pte_t путем комбинации структуры page и protection bits .
 Макрос pte_page() наоборот возвращает структуру page по данному pte_t .
 
 Последний набор функций выделяет память для page tables .
 Выделение памяти под page tables - критический момент,
 в течение которого прерывания должны
 быть задисэйблены.Это очень популярная процедура и должна 
 выполняться максимально быстро .
 Страницы кэшируются в различные списки , которые называются quicklists.
 PGDs, PMDs и PTEs имеют 2 набора функций для размещения и удаления страниц .
 Это соответственно pgd_alloc(), pmd_alloc() ,pte_alloc() и pgd_free(), 
 pmd_free() and pte_free().
 В основе формирования этих списков лежит структура LIFO - Last In, First Out .
 Если страница не может быть помещена в кеш , она размещается 
 в памяти с помощью  page allocator .
 Специальный механизм следит за тем , чтобы кеш находился 
 в пределах фиксированного обьема .
 Инициализация page tables разделена на 2 части . 
 При старте системы инициализируются только первые 8 метров,остальное позже . 
 В файле arch/i386/kernel/head.S лежит функция startup_32() .
 Стартовый загрузочный адрес , по которому загружается ядро - 0x00100000.
 Первый мегабайт используется устройствами для работы с биосом .
 Инициализация page tables начинается со статического массива swapper_pg_dir , который
 размещается по адресу 0x00101000 . Затем инициализируются 2 таблицы  pg0 и pg1 .
 Если процессор поддерживает Page Size Extension (PSE) , 
 размер страницы будет установлен
 в 4 метра, а не в 4 килобайта , как обычно . 
 Остальную работу по инициализации page table
 выполняет paging_init.  Граф инициализации :
 
  При иницмализации page tables должна стать доступной 
  вся память в зоне ZONE_DMA и ZONE_NORMAL .
 При загрузке системы для каждой pgd_t , которая используется ядром , 
 выделит страницу PGD .
 Функция fixrange_init() выделяет память 
 для Advanced Programmable Interrupt Controller (APIC).
 Статический адрес swapper_pg_dir для  PGD  загружатеся в CR3 .
 Функция kmap_init() инициализирует PTE.
  Перевод виртуального адреса в физический должен быть максимально быстрым , 
  и это достигается с помощью глобального массива mem_map . 
  Физический адрес может быть получен из виртуального
 простым вычитанием PAGE_OFFSET : 
 /* from  */
  #define __pa(x)                   ((unsigned long)(x)-PAGE_OFFSET)
 
 /* from  */
  static inline unsigned long virt_to_phys(volatile void * address)
  {
          return __pa(address);
  }
  Ядро загружается по адресу 1MiB и берет на свои нужды 8 метров . 
 Ядро пытается зарезервировать 16 метров в ZONE_DMA , 
 и стартовый виртуальный адрес будет соответствовать физическому  0xC1000000.
 Таблица mem_map содержит индексы физических адресов - 
 Page Frame Number (PFN) - который получается
 путем сдвига виртуального адреса :
 #define virt_to_page(kaddr) (mem_map + (__pa(kaddr) >> PAGE_SHIFT))
  Для ускорения обращения к памяти , существует специальная таблица - 
  Translation Lookaside Buffer (TLB) -
 которая представляет из себя набор наиболее часто используемых адресов памяти . 
 Использование этой таблицы позволяет избегать многократного повторения 
 вычисления одних и тех же физических адресов ,
 которые уже имеются в этом буфере .
  Интеловские процессоры имеют встроенный 2-уровневый кеш . 
  Кеш 2-уровня больше , но медленнее , чем 1-й .
 Линукс использует кеш 1-го уровня . 
 Обращение к кешу быстрее на порядок , чем к памяти .
 
Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье