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 
 5.x 
 6.x 
 Интервью 
 Kernel
 HOW-TO 1
 Ptrace
 Kernel-Rebuild-HOWTO
 Runlevel
 Linux daemons
 FAQ
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 Linux Kernel 2.6...3653 
 Trees...490 
 Clickhouse...460 
 Go Web ...455 
 Ethreal 4...452 
 Максвелл 3...421 
 Ext4 FS...412 
 C++ Patterns 3...402 
 Rodriguez 6...397 
 William Gropp...390 
 Ethreal 1...385 
 Secure Programming for Li...382 
 Steve Pate 1...381 
 Gary V.Vaughan-> Libtool...381 
 Ethreal 3...371 
 Assembler...361 
 DevFS...359 
 Стивенс 9...353 
 Ulrich Drepper...349 
 Стивенс 10...324 
 
  01.01.2024 : 3621733 посещений 

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

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

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