Как Linux работает с памятью.
Возникла мысль обратиться к прошлому , чтобы по крайней мере разобраться как все это развивалось (версия 0.1). Это помогло понять и современное ядро. В дальнейшем речь пойдет о ядрах серии 2.2 об изменениях в 2.4 будет сообщено особо. Не буду углубляться в тонкости функционирования защищенного режима процессора об этом написаны целые фолианты. Посмотрим только самую суть. Итак, в овнове всего лежат страницы памяти. В ядре они описываются структурой mem_map_t. typedef struct page { Уже тут наблюдается наворороченность. Множество всяких ссылок. Все они используются . Одна страница может находиться в разных списках , например и всписке страниц в страничном кеше и в списке страниц относящихся к отображенному в память файлу (inode).В структуре описывающей последний можно найти и обратную ссылку,что очень удобно. Все страницы адресуются глобальным указателем mem_map mem_map_t * mem_map Адресация страниц порисходит очень хитро. Если раньше в структуре page было отдельное поле указывающее на физический адрес (map_nr), то теперь он вычисляется static inline unsigned long
page_address(struct page * page) Свободные страницы хранятся в особой структуре free_area static struct free_area_struct free_area[NR_MEM_TYPES][NR_MEM_LISTS]; , где первое поле отвечает за тип области : Ядра, Пользователя, DMA и.т.д. И обрабатываются по очень хитрому алгоритму. Страницы делятся на свободные непрерывные обрасти размера 2 в степени x умноженной на размер страницы ( (2^x)*PAGE_SIZE ). Области одного размера лежат в одной области массива. .... Выделяет страницу функция get_free_pages(order). Она выделяет страницы составляющие область размера PAGE_SIZE*(2^order). Ищется область соответствующего размера или больше. Если есть только область большего размера то она делится на несколько маленьких и берется нужный кусок. Если свободных страниц недостаточно, то некоторые будут сброшены в область подкачки и процесс выделенения начнется снова. Возвращает страницу функция free_pages(struct page, order). Высвобождает страницы начинающиеся с page размера PAGE_SIZE*(2^order). Область возвращается в массив свободных обрастей в соответствующую позицию и после этого происходит попытка объединить несколько областей для создания одной большего размера. Отсутствие страницы в памяти обрабатыватся ядром особо. Страница может или вообще отсутствовать или находиться в области подкачки. Вот собственно и вся базовая работа с реальными страницами.Самое время вспомнить, что процесс работает все-каки с виртуальными адресами, а не с физическими. Преобразование происходит посредством вичислений, используя таблицы дескрипторов, и каталоги таблиц. Linux поддерживает 3 уровня таблиц: каталог таблиц первого уровня (PGD - Page Table Directory),каталог таблиц второго уровня (PMD - Medium Page Table Diractory), и наконец таблица дескрипторов (PTE - Page Table Entry). Реально конкректным процессором могут поддерживаться не все уровни, но запас позволяет поддерживать больше возможных архитектур (Intel имеет 2 уровня таблиц, а Alpha - целых 3 ). Преобразование виртуального адреса в физический происходит соответственно в 3 этапа. Берется указатель PGD, имеющийся в структуре описывающий каждый процесс, преобразуется в указатель записи PMD, а последний преобразуется в указатель в таблице дескрипторов PTE. И наконец к реальному адресу указывающему на начало страницы прибавляют смещение от ее начала.
page_dir = pgd_offset(vma->vm_mm, address);
Вообще-то все данные об используемой процессом памяти помещаются в структуре mm_struct struct mm_struct { Сразу замечаем, что помимо вполне понятных указателей на
начало данных (start_code, end_code ...) кода и стека есть
указатели на данные отображенных файлов (mmap). Это надо
сказать особенность Linux - тащить в себя все что только
можно. Может быть это и хорошо, но с другой стороны так
разбазариваться памятью (вспомним еще буфера ввода/вывода при
файловой системе, которые тоже будут кушать все новую память
пока она есть) Данный подход может негативно отразиться на
стабильности системы, ведь для запуска какого-то жизненно
необходимого процесса может потребоваться время на
освобождение лишних кешей. Простенькая проверка на потерю
свободной памяти: введите команду "cat /dev/mem >/image " и
посмотрите сколько свободной памяти после этого осталось. Если
вам это не нравится, то обратите взгляд на функцию
invalidate_inode_pages(* struct_inode), освобождающую
страничный кэш для данного файла. struct vm_area_struct {
/*
linked list of VM areas per task, sorted by address */
pgprot_t vm_page_prot; /*
AVL tree of VM areas per task, sorted by address */
/*
For areas with inode, the list inode->i_mmap, for shm
areas,
struct vm_operations_struct * vm_ops; struct vm_operations_struct {
Идея данной структуры возникла из идеи виртуальной файловой системы, поэтому все операции над виртуальными областями абстрактны и могут быть специфичными для разных типов памяти, например при отображении файлов операции чтения одни а при отображении памяти (через файл /dev/mem ) совершенно другие. Первоначально vm_area_struct появилась для обеспечения нужд отображения, но постепенно распространяется и на другие области. Что делать, когда требуется получить новую область памяти. Есть целых 3 способа. 1. Уже знакомый get_free_page() С каждой из двух процедур в ядре связаны еще по списку свободных/занятых областей, что еще больше усложняет понимание работы с памятью. (vmlist для vmalloc, kmem_cash для kmalloc) Что же в 2.4. Добавлена поддержка новой архитектуры памяти NUMA. В
противовес классической UMA память делится на зоны с разным
временем доступа к каждой из них . Это очень полезно и для
кластерных решений. В связи с этим появились новые обертки на
функции и найти суть стало еще сложнее. Появилась также
поддержка памяти до 64Гб. Но посмотрим - увидим. Ведь Linux развивается очень быстро
и не всегда предсказуемо. Stanislav Ievlev. |