Главная страница
Навигация по странице:

  • Зона Описание физическая память

  • Таблица 11.1.

  • Второе издание


    Скачать 3.09 Mb.
    НазваниеВторое издание
    Дата08.09.2019
    Размер3.09 Mb.
    Формат файлаpdf
    Имя файлаLav_Robert_Razrabotka_yadra_Linux_Litmir.net_264560_original_254.pdf
    ТипДокументы
    #86226
    страница31 из 53
    1   ...   27   28   29   30   31   32   33   34   ...   53
    Ожидание в очереди wait queue в течение интервала времени
    В главе 4 рассматривалось, как контекст процесса в ядре может поместить себя в очередь ожидания для того, чтобы ждать наступления некоторого события, а затем вызвать планировщик, который выберет новое задание для выполнения. Если где-то в другом месте произойдет указанное событие, то вызывается функция wake_up ()
    для всех заданий, которые ожидают в очереди. Эти задания возвращаются к выпол- нению и могут продолжать работу.
    Иногда желательно ожидать наступления некоторого события или пока не прой- дет определенный интервал времени, в зависимости от того, что наступит раньше,
    В этом случае код должен просто вызвать функцию s c h e d u l e _ t i m e o u t () вместо функции schedule () после того, как он поместил себя в очередь ожидания. Задание будет возвращено к выполнению, когда произойдет желаемое событие или пройдет указанный интервал времени. Код обязательно должен проверить, почему он возвра- тился к выполнению — это может произойти потому, что произошло событие, про- шел интервал времени или был получен сигнал — после этого необходимо соответ- ственным образом продолжить выполнение.
    Время вышло
    В этой главе были рассмотрены понятия, связанные с представлением о времени в ядре и с тем, как при этом происходит управление абсолютным и относительным ходом времени. Были показаны отличия абсолютного и относительного времени, а также периодических и относительных событий. Далее были рассмотрены прерыва- ния таймера, импульсы таймера, константа HZ и переменная j i f f i e s .
    После этого было рассказано о том, как реализованы таймеры ядра и как их мож- но использовать в собственном коде ядра. В конце главы были представлены другие методы, которые разработчики могут использовать для учета времени.
    Большая часть кода ядра, который вам придется писать, требует понимания того,
    как время течет в ядре и как его отслеживать. С очень большой вероятностью, осо- бенно при разработке драйверов, вам необходимо будет иметь дело с таймерами ядра. Материал этой главы принесет практическую пользу.
    232 Глава 10

    11
    Управление
    памятью
    В
    ыделить память внутри ядра не так просто, как вне ядра. Это связано со мно- гими факторами. Главным образом, причина в том, что в ядре не доступны те элементы роскоши, которыми можно пользоваться в пространстве пользователя,
    В отличие от пространства пользователя, в ядре не всегда можно позволить себе лег- ко выделять память. Например, в режиме ядра часто нельзя переходить в состояние ожидания. Более того, в ядре не так просто справиться с ошибками, которые возни- кают при работе с памятью. Из-за этих ограничений и из-за необходимости, чтобы схема выделения памяти была быстрой, работа с памятью в режиме ядра становится более сложной, чем в режиме пользователя. Конечно, нельзя сказать, что выделение памяти в ядре — очень сложная процедура, однако скоро все будет ясно — просто это делается несколько по-другому.
    В этой главе рассматриваются средства, которые предназначены для выделения памяти внутри ядра. Перед изучением интерфейсов, предназначенных для выделе- ния памяти, необходимо рассмотреть, как ядро управляет памятью.
    Страницы памяти
    Ядро рассматривает страницы физической памяти как основные единицы управ- ления памятью. Хотя наименьшая единица памяти, которую может адресовать процессор, — это машинное слово, модуль управления памятью (MMU, Memory
    Management Unit) — аппаратное устройство, которое управляет памятью и отвечает за трансляцию виртуальных адресов в физические — обычно работает со страница- ми. Поэтому модуль MMU управляет таблицами страниц на уровне страничной дета- лизации (отсюда и название). С точки зрения виртуальной памяти, страница — это наименьшая значащая единица.
    Как будет показано в главе 19, "Переносимость", каждая аппаратная платформа поддерживает свой характерный размер страницы. Многие аппаратные платформы поддерживают даже несколько разных размеров страниц. Большинство 32-разряд- ных платформ имеют размер страницы, равный 4 Кбайт, а большинство 64-разряд- ных платформ— 8 Кбайт. Это значит, что на машине, размер страницы которой ра- вен 4 Кбайт, при объеме физической памяти, равном 1 Гбайт, эта физическая память разбивается на 262 144 страницы.

    Ядро сопоставляет каждой странице физической памяти в системе структуру struct page. Эта структура определена в файле следующим образом.
    struct page {
    page_flags_t flags;
    atomic_t _count;
    atornic_t _mapcount;
    unsigned long private;
    struct address_space *mapping;
    pgoff_t index;
    struct list_head lru;
    void *virtual;
    };
    Рассмотрим самые важные поля этой структуры. Поле flags содержит состоя- ние страницы. Это поле включает следующую информацию: является ли страни- ца измененной (dirty) или заблокированной (locked) в памяти. Значение каждого флага представлено одним битом, поэтому всего может быть до 32 разных флагов.
    Значения флагов определены в файле .
    Поле _count содержит счетчик использования страницы — т.е. сколько на эту страницу имеется ссылок. Когда это значение равно нулю, это значит, что никто не использует страницу, и она становится доступной для использования при новом вы- делении памяти. Код ядра не должен явно проверять значение этого поля, вместо этого необходимо использовать функцию page_count () , которая принимает ука- затель на структуру page в качестве единственного параметра. Хотя в случае неза- нятой страницы памяти значение счетчика _count может быть отрицательным (во внутреннем представлении), функция page_count () возвращает значение нуль для незанятой страницы памяти и положительное значение — для страницы, которая в данный момент используется. Страница может использоваться страничным кэшем
    (в таком случае ноле mapping указывает на объект типа address_space, который связан с данной страницей памяти), может использоваться в качестве частных дан- ных (на которые в таком случае указывает поле private) или отображаться в табли- цу страниц процесса.
    Поле v i r t u a l — это виртуальный адрес страницы. Обычно это просто адрес данной страницы в виртуальной памяти ядра. Некоторая часть памяти (называемая областью верхней памяти, high memory) не отображается в адресное пространство ядра (т.е. не входит в него постоянно). В этом случае значение данного поля равно
    NULL и страница при необходимости должна отображаться динамически. Верхняя память будет рассмотрена в одном из следующих разделов.
    Наиболее важный момент, который необходимо понять, это то, что структура page связана со страницами физической, а не виртуальной памяти. Поэтому то, чему соответствует экземпляр этой структуры, в лучшем случае, очень быстро изменяет- ся. Даже если данные, которые содержались в физической странице, продолжают существовать, то это не значит, что эти данные будут всегда соответствовать одной и той же физической странице памяти и соответственно одной и той же структу- ре раде, например из-за вытеснения страниц (swapping) или по другим причинам.
    Ядро использует эту структуру данных для описания всего того, что содержится в данный момент в странице физической памяти, соответствующей данной структуре.
    234 Глава 11

    Назначение этой структуры— описывать область физической памяти, а не данных,
    которые в ней содержатся.
    Ядро использует рассматриваемую структуру данных для отслеживания всех стра- ниц физической памяти в системе, так как ядру необходима информация о том, сво- бодна ли страница (т.е. соответствующая область физической памяти никому не вы- делена). Если страница не свободна, то ядро должно иметь информацию о том, чему принадлежит эта страница. Возможные обладатели: процесс пространства пользова- теля, данные в динамически выделенной памяти в пространстве ядра, статический код ядра, страничный кэш (page cache) и т.д.
    Разработчики часто удивляются, что для каждой физической страницы в системе создается экземпляр данной структуры. Они думают: "Как много для этого использу- ется памяти!" Давайте посмотрим, насколько плохо (или хорошо) расходуется адрес- ное пространство для хранения информации о страницах памяти. Размер структу- ры s t r u c t page равен 40 байт. Допустим, что система имеет страницы размером
    1 Кбайт, а объем физической памяти равен 128 Мбайт. Тогда все структуры раде в системе займут немного больше 1 Мбайт памяти — не очень большая плата за воз- можность управления всеми страницами физической памяти.
    Зоны
    В связи с ограничениями аппаратного обеспечения, ядро не может рассматривать все страницы памяти как идентичные. Некоторые страницы, в связи со значениями их физических адресов памяти, не могут использоваться для некоторых типов задач.
    Из-за этого ограничения ядро делит физическую память на зоны. Ядро использует зоны, чтобы группировать страницы памяти с аналогичными свойствами. В частно- сти, операционная система Linux должна учитывать следующие недостатки аппарат- ного обеспечения, связанные с адресацией памяти.
    • Некоторые аппаратные устройства могут выполнять прямой доступ к памяти
    (ПДП, DMA, Direct Memory Access) только в определенную область адресов.
    • На некоторых аппаратных платформах для физической адресации доступны большие объемы памяти, чем для виртуальной адресации. Следовательно, часть памяти не может постоянно отображаться в адресное пространство ядра.
    В связи с этими ограничениями, в операционной системе Linux выделяют три зоны памяти.
    • Z0NE_DMA. Содержит страницы, которые совместимы с режимом DMA.
    • ZONE_NORMAL. Содержит страницы памяти, которые отображаются в адресные пространства обычным образом.
    • ZONE_HIGHMEM. Содержит "верхнюю память", состоящую из страниц, которые не могут постоянно отображаться в адресное пространство ядра.
    Эти зоны определяются в заголовочном файле .
    То, как используется разделение памяти на зоны, зависит от аппаратной плат- формы. Например, для некоторых аппаратных платформ нет проблем с прямым до- ступом к памяти ни по какому адресу. Для таких платформ зона ZONE_DMA является пустой, и для всех типов выделения памяти используется зона ZONE_NORMAL.
    Управление памятью 235

    Операционная система разделяет страницы системной памяти на зоны, чтобы иметь пулы страниц для удовлетворения требований выделения памяти. Например,
    пул зоны ZONE_DMA дает возможность ядру удовлетворить запрос на выделение па- мяти, которая необходима для операций DMA. Если нужна такая память, ядро может просто выделить необходимое количество страниц из зоны ZONE_DMA. Следует обра- тить внимание, что зоны не связаны с аппаратным обеспечением— это логическое группирование, которое позволяет ядру вести учет страниц; памяти.
    Хотя некоторые запросы на выделение памяти могут требовать страницы из определенной зоны, это требование не обязательно может быть жестким. Например,
    выделение памяти для ПДП требует страницы из зоны ZONE DMA, а для обычного выделения памяти могут подойти страницы как из зоны ZONE_NORMAL, так и из зоны
    ZONE_DMA. Конечно, для удовлетворения запросов по обычному выделению памяти ядро будет стараться выделять страницы из зоны ZONE_NORMAL, чтобы сохранить страницы в зоне ZONE_DMA для случая, когда эти страницы действительно нужны,
    Если же наступает решающий момент (становится недостаточно памяти), то ядро может обратиться к любой доступной и подходящей зоне.
    1
    Некоторые некачественные устройства PCI также могут выполнять прямой доступ к памяти толь- ко к 24-битовом адресном пространстве. Но эти устройства работают не правильно.
    2
    Это не имеет ничего общего с верхней памятью в операционной системе DOS.
    2 3 6 Глава 11
    Зона Описание физическая память
    ZONE_DMA Страницы памяти, совместимые с ПДП < 16 Мбайт
    ZONE_NORMAL Нормально адресуемые страницы 16 - 896 Мбайт
    Z O N E _ H I G H M E M Динамически отображаемые страницы > 896 Мбайт
    Как противоположный пример можно привести платформу х86, для которой устройства ISA
    1
    не могут выполнять операции DMA в полном 32-разрядном простран- стве адресов, так как устройства ISA могут обращаться только к первым 16 Мбайт физической памяти. Следовательно, зона ZONE_DMA для платформы х8б содержит только страницы памяти с физическими адресами в диапазоне 0-16 Мбайт.
    Аналогично используется и зона ZONE_HIGHMEM. To, что аппаратная платформа может отображать и чего она не может отображать в адресное пространство ядра,
    отличается для разных аппаратных платформ. Для платформы х86 зона ZONE_
    HIGHMEM— это вся память, адреса которой лежат выше отметки 896 Мбайт. Для других аппаратных платформ зона ZONE_HIGHMEM пуста, так как вся память может непосредственно отображаться. Память, которая содержится в зоне ZONE_HIGHMEM,
    называется верхней памятью
    2
    (high memory). Вся остальная память в системе называет- ся нижней памятью (low memory).
    Зона ZONE_NORMAL обычно содержит все, что не попало в две предыдущие зоны памяти. Для аппаратной платформы х86, например, зона ZONE_NORMAL содержит всю физическую память от 16 до 896 Мбайт. Для других, более удачных аппаратных платформ, SONE_NORMAL— это вся доступная память. В табл. 11.1 приведен список зон для аппаратной платформы х86.
    Таблица 11.1. Зоны памяти для аппаратной платформы х86

    Каждая зона представлена с помощью структуры s t r u c t zone, которая опреде- лена в файле в следующем виде.
    struct zone {
    spinlock t lock;
    unsigned ]ong free_pages;
    unsigned long pages_min;
    unsigned long pages_low;
    unsigned long pages_high;
    unsigned long protection[MAX_NR_ZONES];
    spinlock_t lru_lock;
    struct list_head active_list;
    struct list_head inactive_list;
    unsigned long nr_scan_active;
    unsigned long nr_scan_inactive;
    unsigned long nr_active;
    unsigned long nr_inactive;
    int all_unreclaimable;
    unsigned long pages_scanned;
    int temp_priority;
    int prev_priority;
    struct free_area free_area[MAX_ORDER];
    wait_queue_head_t *wait_table;
    unsigned long wait_table_size;
    unsigned long wait_table_bits;
    struct per_cpu_pageset pageset[NR_CPUS];
    struct pglist_data *zone_pgdat;
    struct page *zone_mem_map;
    unsigned long zone_start_pfn;
    char *name;
    unsigned long spanned_pages;
    unsigned long prcsent_pages;
    };
    Эта структура большая, но в системе всего три зоны и соответственно три такие структуры. Рассмотрим наиболее важные поля данной структуры.
    Поле lock— это спин-блокировка, которая защищает структуру от параллельно- го доступа. Обратите внимание, что она защищает только структуру, а не страницы,
    которые принадлежат зоне. Для защиты отдельных страниц нет блокировок, хотя отдельные части кода могут блокировать данные, которые могут оказаться в указан- ных страницах.
    Поле free_pages — это количество свободных страниц в соответствующей зоне.
    Ядро старается поддерживать свободными хотя бы pages_min страниц зоны, если это возможно (например, с помощью вытеснения на диск).
    Поле name— это строка, оканчивающаяся нулем, которая содержит имя соответ- ствующей зоны (что не удивительно). Ядро инициализирует указанное поле при за- грузке системы с помощью кода, который описан п файле mm/page_alloc.с. Три зоны имеют имена "DMA", "Normal" и "HighMem".
    Управление памятью
    237

    Получение страниц памяти
    Теперь, имея некоторое понятие о том, как ядро упрапляет памятью с помощью страниц, зон и так далее, давайте рассмотрим интерфейсы, которые реализованы в ядре для того, чтобы выделять и освобождать память внутри ядра. Ядро предо- ставляет один низкоуровневый интерфейс для выделения памяти и несколько интер- фейсов для доступа к ней. Все эти интерфейсы выделяют память в объеме, кратном размеру страницы, и определены в файле < l i n u x / g f p . h > . Основная функция вы- деления памяти следующая.
    struct page * alloc_pages(unsigned int gfp_mask, unsigned int order)
    Данная функция позволяет выделить 2
    o r d e r
    (т.е. 1 << o r d e r ) смежных страниц
    (один непрерывный участок) физической памяти и возвращает указатель на структу- ру page, которая соответствует первой выделенной странице памяти. В случае ошиб- ки возвращается значение NULL. Параметр gfp_mask будет рассмотрен несколько позже. Полученную страницу памяти можно конвертировать в ее логический адрес с помощью следующей функции.
    void * page_address(struct page *page)
    Эта функция возвращает указатель на логический адрес, которому в данный мо- мент соответствует начало указанной страницы физической памяти. Если нет необ- ходимости в соответствующей структуре s t r u c t page, то можно использовать сле- дующую функцию.
    unsigned long __get_free_pages(unsigned int gfp_mask, unsigned int order)
    Эта функция работает так же, как и функция a l l o c _ p a g e s ( ) , за исключением того, что она сразу возвращает логический адрес первой выделенной страницы па- мяти. Так как выделяются смежные страницы памяти, то другие страницы просто следуют за первой.
    Если необходима всего одна страница памяти, то для этой цели определены сле- дующие функции-оболочки, которые позволяют уменьшить количество работы по набору кода программы.
    struct page * alloc_page(unsigned int gfp_mask)
    unsigned long __get_free_page(unsigned int gfp_mask)
    Эти функции работают так же, как и ранее описанные, по для них в качестве па- раметра o r d e r передается нуль (2 0
    = одна страница памяти).
    Получение страниц заполненных нулями
    Для того чтобы получаемые страницы памяти были заполнены нулями, необходи- мо использовать следующую функцию.
    unsigned long get_zeroed_page(unsigned int gfp_mask)
    Эта функция аналогична функции _ _ g e t _ f r e e _ p a g e () , за исключением того,
    что после выделения страницы памяти она заполняется нулями. Это полезно для страниц памяти, которые возвращаются в пространство пользователя, так как слу-
    238 Глава 11

    Освобождение страниц
    Для освобождения страниц, которые больше не нужны, можно использовать сле- дующие функции.
    void __free_pages(struct page *page, unsigned int order)
    void free_pages(unsigned long addr, unsigned int order)
    void free_page(unsigned long addr)
    Необходимо быть внимательными и освобождать только те страницы памяти, ко- торые вам выделены. Передача неправильного значения параметра page, addr или o r d e r может привести к порче данных. Следует помнить, что ядро доверяет себе.
    В отличие от пространства пользователя, ядро с удовольствием зависнет, если вы по- просите. Рассмотрим пример. Мы хотим выделить 8 страниц памяти.
    page = __get_free_pages(GFP_KERNEL, 3 ) ;
    if (!page) {
    /* недостаточно памяти: эту ошибку необходимо обработать самим! */
    return -ENOMEM;
    }
    /* переменная 'page' теперь содержит адрес первой из восьми страниц памяти*/
    free_pages(page, 3);
    /*
    * наши страницы памяти теперь освобождены и нам больше нельзя
    * обращаться по адресу, который хранится в переменной 'page'
    */
    Управление памятью 239
    чайный "мусор", который находится в страницах памяти, может оказаться не совсем случайным и случайно может содержать некоторые (например, секретные) данные.
    Все данные необходимо обнулить или очистить каким-либо другим образом перед тем, как возвращать информацию в пространство пользователя, чтобы при этом не пострадала безопасность системы. В табл. 11.2 приведен список всех низкоуровне- вых средств выделения памяти.
    1   ...   27   28   29   30   31   32   33   34   ...   53


    написать администратору сайта