Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 Kernels
 Boot 
 Memory 
 File system
 0.01
 1.0 
 2.0 
 2.4 
 2.6 
 3.x 
 4.x 
 Интервью 
 Kernel
 HOW-TO 1
 Ptrace
 Kernel-Rebuild-HOWTO
 Runlevel
 Linux daemons
 FAQ
NEWS
Последние статьи :
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
  SQL 30.07   
  JFS 10.06   
  B-trees 01.06   
 
TOP 20
 Part 4...355 
 Ethreal 1...285 
 Secure Programming for Li...278 
 Trees...259 
 2.0-> Linux IP Networking...226 
 Intel 386...208 
 B.Blunden 1...180 
 Steve Pate 3...176 
 Стивенс 9...172 
 Ethreal 4...171 
 Hansen 1...155 
 Part 3...149 
 Daniel Bovet 5...148 
 William Gropp...147 
 Advanced Bash Scripting G...147 
 UML 3...147 
 Ext4 FS...145 
 C++ Templates 3...145 
 Robbins 4...142 
 Robbins 3...139 
 
  01.05.2019 : 2719283 посещений 

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-го уровня . 
 Обращение к кешу быстрее на порядок , чем к памяти .
 
Оставьте свой комментарий !

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

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