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

  • Таблица 15.1

  • Режим ноутбука

  • Демоны bdflush и kupdated

  • Коротко о главном

  • Модуль "Hello,World!"

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


    Скачать 3.09 Mb.
    НазваниеВторое издание
    Дата08.09.2019
    Размер3.09 Mb.
    Формат файлаpdf
    Имя файлаLav_Robert_Razrabotka_yadra_Linux_Litmir.net_264560_original_254.pdf
    ТипДокументы
    #86226
    страница42 из 53
    1   ...   38   39   40   41   42   43   44   45   ...   53
    Буферный кэш
    В операционной системе Linux больше нет отдельного буферного кэша. В ядрах серии 2.2 существовало два отдельных кэша: страничный и буферный. В первом кэ- шировалисы страницы памяти, а в другом — буферы. Эти два кэша не были объедине- ны между собой. Дисковый блок мог находиться в обоих кэшах одновременно. Это требовало больших усилий по синхронизации двух кэшированных копий, не говоря уже о напрасной трате памяти.
    Так было в ядрах серии 2.2 и более ранних, но начиная с ядер Linux серии 2.4
    оба кэша объединили вместе. Сегодня существует только один дисковый кэш — стра- ничный кэш.
    Ядру все еще необходимо использовать буферы для того, чтобы представлять дис- ковые блоки в памяти. К счастью, буферы описывают отображение блоков на стра- ницы памяти, которые в свою очередь находятся в страничном кэше.
    336 Глава 15

    Демон pdflush
    Измененные (dirty, "грязные") страницы памяти когда-нибудь должны быть запи- саны на диск. Обратная запись страниц памяти выполняется в следующих двух слу- чаях.
    • Когда объем свободной памяти становится меньше определенного порога,
    ядро должно записать измененные данные обратно на диск, чтобы освободить память.
    • Когда несохраненные данные хранятся в памяти достаточно долго, то ядро должно их записать на диск, чтобы гарантировать, что эти данные не будут на- ходиться в Несохраненном состоянии неопределенное время.
    Эти два типа записи имеют разные цели. В более старых ядрах они выполнялись двумя разными потоками пространства ядра (см. следующий раздел). Однако в ядре
    2.6 эту работу выполняет группа (gang
    4
    ) потоков ядра pdflush, которые называются демонами фоновой обратной записи (или просто потоками pdflush). Ходят слухи,
    что название pdflush — это сокращение от "dirty page flush" ("очистка грязных стра- ниц"). Не обращайте внимание на это сомнительное название, давайте лучше более детально рассмотрим, для чего нужны эти процессы.
    Во-первых, потоки pdflush служат для записи измененных страниц на диск, ког- да объем свободной памяти в системе уменьшается до определенного уровня. Цель такой фоновой записи— освобождение памяти, которую занимают незаписанные страницы, в случае недостатка физических страниц памяти. Уровень, когда начи- нается обратная запись, может быть сконфигурирован с помощью параметра d i r - ty_background_ratio утилиты s y s c t l . Когда объем свободной памяти становится меньше этого порога, ядро вызывает функцию wakeup_bdf lush ()
    5
    для перевода в состояние выполнения потока pdflush, который пыполняет функцию обратной за- писи измененных страниц памяти background_writeout (). Эта функция получает один параметр, равный количеству страниц, которые функция должна попытаться записать на диск.
    Функция продолжает запись до тех пор, пока не выполнятся два следующих условия.
    • Указанное минимальное количество страниц записано на диск.
    • Объем свободной памяти превышает соответствующее значение параметра dirty_background_ratio.
    Выполнение этих условий гарантирует, что демон pdf lush выполнил свою рабо- ту по предотвращению нехватки памяти. Если эти условия не выполняются, то об- ратная запись может остановиться только тогда, когда демон p d f l u s h запишет на диск все несохраненные страницы и для него больше не будет работы.
    Во-вторых, назначение демона pdflush — периодически переходить в состояние выполнения (независимо от состояния нехватки памяти) и записывать на диск очень
    4
    Слово "gang" не является жаргонным. Этот термин часто используется в компьютерных науках,
    чтобы указать группу чего-либо, что может выполняться параллельно.
    5
    Да, название функции не совсем верное. Должно было бы быть wakeup_pdflush (). В следующем разделе рассказано, откуда произошло это название.
    Страничный кэш и обратная запись страниц 337
    давно измененные страницы памяти. Это гарантирует, что измененные страницы не будут находиться в памяти неопределенное время. При сбоях системы будут потеря- ны те страницы памяти, которые не были сохранены на диске, так как содержимое памяти после перегрузки не сохраняется. Следовательно, периодическая синхрони- зация страничного кэша с данными на диске является важным делом. При загрузке системы инициализируется таймер, периодически возвращающий к выполнению по- ток pdflush, который выполняет функцию wb_kupdate (). Эта функция выполняет обратную запись данных, которые были изменены более чем d i r t y _ e x p i r e _ c e n t i - seсs сотых секунды тому назад. После этого таймер снова инициализируется, чтобы сработать через d i r t y _ e x p i r e _ c e n t i s e c s сотых секунды. Таким образом потоки p d f l u s h периодически возвращаются к выполнению и записывают на диск все из- мененные страницы, данные в которых старше, чем указанный лимит.
    Системный администратор может установить эти значения с помощью каталога
    /proc/sys/vrn и утилиты s y s c t l . Втабл. 15.1 приведен список всех соответствую- щих переменных.
    Таблица 15.1. Параметры для настройки демона p d f l u s h
    Переменная Описание
    dirty_background_ratio dirty_expire_centisecs dirty_ratio dirty_writeback_centisecs laptop_mode
    Код потока p d f l u s h находится в файлах m m / p a g e - w r i t e b a c k . c и f s /
    f s - w r i t e b a c k . с .
    Режим ноутбука
    Режим ноутбука — это специальная политика обратной записи страниц с целью оптимизации использования батареи и продления срока ее работы. Это делается путем минимизации активности жестких дисков, чтобы они оставались в останов- ленном состоянии по возможности долго. Конфигурировать этот режим можно с по- мощью файла /proc/sys/vm/laptop_mode. По умолчанию в этом файле записано значение 0 и режим ноутбука выключен. Запись значения 1 в этот файл позволяет включить режим ноутбука.
    В режиме ноутбука существует всего одно изменение в выполнении обратной записи страниц. В дополнение к обратной записи измененных страниц; памяти,
    когда они становятся достаточно старыми, демон p d f l u s h также выполняет и все остальные операции дискового ввода-вывода, записывая все дисковые буферы на
    338 Глава 15
    Объем свободной оперативной памяти, при котором демон p d f l u s h начинает обратную запись незаписанных данных
    Время, в сотых долях секунды, в течение которого неза- писанные данные могут оставаться в памяти, перед тем как демон p d f l u s h не запишет их на диск при следующем периоде обратной записи
    Процент от общей оперативной памяти, соответствующий страницам памяти одного процесса, при котором начинает- ся обратная запись незаписанных данных
    • • i
    Насколько часто, в сотых долях секунды, процесс b d f l u s h возвращается к выполнению для обратной записи данных
    Переменная булевого типа, которая включает или выключает режим ноутбука (см. следующий раздел)
    диск. Таким образом демон p d f l u s h пользуется тем преимуществом, что диск уже запущен, а также он гарантирует, что в ближайшем будущем диск снова запущен не будет.
    Такое поведение имеет смысл, когда параметры d i r t y _ e x p i r e _ c e n t i s e c s и d i r t y _ w r i t e b a c k _ c e n t i s e c s установлены в большие значения, скажем 10 минут.
    При таких задержках обратной записи диск запускается не часто, а когда он все-таки запускается, то работа в режиме ноутбука гарантирует, что этот момент будет ис- пользован с максимальной эффективностью.
    Во многих поставках ОС Linux режим ноутбука автоматически включается и вы- ключается, при этом также могут изменяться и другие параметры демона pbflush,
    когда заряд батареи уменьшается. Такое поведение позволяет получать преимуще- ства от режима ноутбука при работе от батареи и автоматически возвращаться к нормальному поведению, когда машина включается в электрическую сеть.
    Демоны bdflush и kupdated
    В ядрах серий до 2.6 работа потоков p d f l u s h выполнялась двумя другими пото- ками ядра b d f l u s h и kupdated.
    Поток пространства ядра b d f l u s h выполнял фоновую обратную запись изменен- ных страниц, когда количество доступной памяти становилось достаточно малым.
    Также был определен ряд пороговых значений, аналогично тому как это делается для демона pdflush. Демон b d f l u s h возвращался к выполнению с помощью функ- ции wakeup_bdflush () , когда количество свободной памяти становилось меньше этих пороговых значений.
    Между демонами b d f l u s h и p d f l u s h существует два главных отличия. Первое состоит в том, что демон b d f l u s h был всего один, а количество потоков p d f l u s h может меняться динамически. Об этом более подробно будет рассказано в следую- щем разделе. Второе отличие состоит в том, что демон bdflush работал с буферами,
    он записывал на диск измененные буферы. Демон p d f l u s h работает со страница- ми, он записывает на диск целые измененные страницы памяти. Конечно, страницы памяти могут соответствовать буферам, но единицей ввода-вывода является целая страница памяти, а не один буфер. Это дает преимущество, поскольку работать со страницами памяти проще, чем с буферами, так как страница памяти — более общий и более часто используемый объект.
    Так как демон b d f l u s h выполнял обратную запись, только когда количество сво- бодной памяти очень сильно уменьшалось или количество буферов было очень боль- шим, то был введен поток ядра kupdated, который периодически выполнял обрат- ную запись измененных страниц памяти. Он использовался для целей, аналогичных функции wb_kupdate () демона pdflush.
    Потоки b d f l u s h и kupdated и их функциональность сейчас заменены потоками pdflush.
    Предотвращение перегруженности:
    для чего нужны несколько потоков
    Один из главных недостатков решения на основе демона b d f l u s h состоит в том,
    что демон b d f l u s h имел всего один поток выполнения. Это приводило к возмож- ности зависания демона при большом количестве операций обратной записи, когда
    Страничный кэш и обратная запись страниц 339
    один поток демона b d f l u s h блокировался на очереди запросов ввода-вывода пере- груженного устройства, в то время как очереди запросов других устройств могли быть в этот момент сравнительно свободными. Если система имеет несколько дис- ков и соответствующую процессорную мощность, то ядро должно иметь возмож- ность загрузить работой все диски. К сожалению, даже при большом количестве данных, для которых необходима обратная запись, демон b d f l u s h может оказаться загруженным работой с одной очередью и не сможет поддерживать все диски в на- груженном состоянии. Это происходит потому, что пропускная способность диском конечна и, к несчастью, очень низкая. Если только один поток выполняет обратную запись страниц, то он может проводить много времени в ожидании одного диска,
    так как пропускная способность диска ограничена. Для облегчения этой ситуации ядру необходима многопоточная обратная запись. В таком случае ни одна очередь запросов не может стать узким местом.
    В ядрах серии 2.6 эта проблема решается путем введения нескольких потоков pdflush. Каждый поток самостоятельно выполняет обратную запись страниц памя- ти на диск, что позволяет различным потокам p d f l u s h работать с разными очередя- ми запросов устройств.
    Количество потоков изменяется в процессе работы системы в соответствии с простым алгоритмом. Если все существующие потоки p d f l u s h оказываются заняты- ми в течение одной секунды, то создается новый поток pdflush. Общее количество потоков не может превышать значения константы MAX_PDFLUSH_THREADS, которая по умолчанию равна 8. И наоборот, если поток p d f l u s h находился в состоянии ожи- дания больше одной секунды, то он уничтожается. Минимальное количество потоков равно, по крайней мере, значению константы MIN_PDFLUSH_THREADS, что по умол- чанию соответствует 2. Таким образом, количество потоков p d f l u s h изменяется ди- намически в зависимости от количества страниц, для которых необходима обратная запись, и загруженности этих потоков. Если все потоки p d f l u s h заняты обратной записью, то создается новый поток. Это гарантирует, что ни одна из очередей запро- сов устройств не будет перегружена, в то время как другие очереди устройств не так загружены и в них тоже можно выполнять обратную запись. Если перегрузка предот- вращается, то количество потоков p d f l u s h уменьшается, чтобы освободить память.
    Вce это хорошо, но что если все потоки p d f l u s h зависнут в ожидании записи в одну и ту же перегруженную очередь? В этом случае производительность нескольких потоков p d f l u s h не будет выше производительности одного потока, а количество занятой памяти станет значительно большим. Чтобы уменьшить такой эффект, для потоков p d f l u s h реализован алгоритм предотвращения зависания (congestion avoi- dance). Потоки активно начинают обратную запись страниц для тех очередей, ко- торые не перегружены. В результате потоки p d f l u s h распределяют свою работу по разным очередям и воздерживаются от записи в перегруженную очередь. Когда все потоки p d f l u s h заняты работой и запускается новый поток, то это означает, что они действительно заняты.
    В связи с усовершенствованием алгоритмов обратной записи страниц, включая введение демона b d f l u s h , ядро серии 2.6 позволяет поддерживать в загруженном состоянии значительно большее количество дисков, чем в более старых версиях ядер. При активной работе потоки p d f l u s h могут обеспечить большую пропускную способность сразу для большого количества дисковых устройств.
    340 Глава 15

    Коротко о главном
    В этой главе был рассмотрен страничный кэш и обратная запись страниц. Было показано, как ядро выполняет все операции страничного ввода-вывода, как опера- ции записи откладываются с помощью дискового кэша и как данные записываются на диск с помощью группы потоков пространства ядра pdf lush.
    На основании материала последних нескольких глав вы получили устойчивое представление о том, как выполняется управление памятью и файловыми система- ми. Теперь давайте перейдем к теме модулей и посмотрим, ядро Linux обеспечивает модульную и динамическую инфраструктуру для загрузки кода ядра во время работы системы.
    Страничный кэш и обратная запись страниц 341

    16
    Модули
    Н
    есмотря на то что ядро является монолитным, в том смысле что все ядро вы- полняется в общем защищенном адресном домене, ядро Linux также является модульным, что позволяет выполнять динамическую вставку и удаление кода ядра в процессе работы системы. Соответствующие подпрограммы, данные, а также точки входа и выхода группируются в общий бинарный образ, загружаемый объект ядра,
    который называется модулем. Поддержка модулей позволяет системам иметь мини- мальное базовое ядро с опциональными возможностями и драйверами, которые компилируются в качестве модулей. Модули также позволяют просто удалять и пере- гружать код ядра, что помогает при отладке, а также дает возможность загружать драйверы по необходимости в ответ на появление новых устройств с функциями го- рячего подключения.
    В этой главе рассказывается о хитростях, которые стоят за поддержкой модулей в ядре, и о том, как написать свой собственный модуль.
    Модуль "Hello,World!"
    В отличие от разработки основных подсистем ядра, большинство из которых были уже рассмотрено, разработка модулей подобна созданию новой прикладной программы, по крайней мере в том, что модули имеют точку входа, точку выхода и находятся каждый в своем бинарном файле.
    Может показаться банальным, но иметь возможность написать программу, кото- рая выводит сообщение "Hello World!", и не сделать этого- просто смешно. Итак,
    леди и джентльмены, модуль "Hello, World!".
    /*
    * hello.с - модуль ядра Hello, World!
    /*
    #include
    #include
    #include
    /*
    * hello_init - функция инициализации, вызывается при загрузке модуля,
    * В случае успешной загрузки модуля возвращает значение нуль,
    * и ненулевое значение в противном случае.
    */
    static int hello_init(void)
    {
    printk(KERN_ALERT "I bear a charmed life.\n");
    return 0;
    }
    /*
    * hello_exit - функция завершения, вызывается при выгрузке модуля.
    */
    static void hello_exit (void)
    {
    printk(KERN_ALERT "Out, out, brief candle!\n");
    }
    module_init(hello_init);
    module_exit(hello_exit);
    MODULE_LICENSE{"GPL");
    MODULE_AUTHOR("Shakespeare");
    Это самый простой модуль ядра, который только может быть. Функция h e l l o _
    i n i t () регистрируется с помощью макроса module_init () в качестве точки входа в модуль. Она вызывается ядром при загрузке модуля. Вызов m o d u l e _ i n i t () — это не вызов функции, а макрос, который устанавливает значение своего параметра в ка- честве функции инициализации. Все функции инициализации должны соотпетство- вать следующему прототипу.
    int my_init(void);
    Так как функция инициализации редко вызывается за пределами модуля, ее обыч- но не нужно экспортировать и можно объявить с ключевым словом s t a t i c .
    Функции инициализации возвращают значение тина i n t . Если инициализация
    (или то, что делает функция инициализации) прошла успешно, то функция должна возвратить значение нуль. В случае ошибки возвращается ненулевое значение.
    В данном случае эта функция просто печатает сообщение и возвращает значе- ние нуль. В настоящих модулях функция инициализации регистрирует ресурсы, вы- деляет структуры данных и т.д. Даже если рассматриваемый файл будет статически скомпилирован с ядром, то функция инициализации останется и будет вызвана при загрузке ядра.
    Функция h e l l o _ e x i t () регистрируется в качестве точки выхода из модуля с помощью макроса module_exit (). Ядро вызывает функцию h e l l o _ e x i t (), когда модуль удаляется из памяти. Завершающая функция должна выполнить очистку ре- сурсов, гарантировать, что аппаратное обеспечение находится в непротиворечивом состоянии, и т.д. После того как эта функция завершается, модуль выгружается.
    Завершающая функция должна соответствовать следующему прототипу.
    void my_exit(void);
    Так же как и в случае функции инициализации, ее можно объявить как s t a t i c .
    344 Глава 16

    Если этот файл будет статически скомпилирован с образом ядра, то данная функ- ция не будет включена в образ и никогда не будет вызвана (так как если нет модуля,
    то код никогда не может быть удален из памяти).
    Макрос MODULE_LICENSE () позволяет указать лицензию на право копирования модуля. Загрузка в память модуля, для которого лицензия не соответствует GPL, при- ведет к установке в ядре флага t a i n t e d (буквально, испорченное). Этот флаг служит для информационных целей, кроме того, многие разработчики уделяют меньше вни- мания сообщениям об ошибках, в которых указан этот флаг. Более того, модули, у которых лицензия не соответствует GPL, не могут использовать символы, которые служат "только для GPL" (см. раздел "Экспортируемые символы" ниже в этой главе).
    Наконец, макрос MODULE_AUTHOR () позволяет указать автора модуля. Значение этого макроса служит только для информационных целей.
    Сборка модулей
    Благодаря новой системе сборки "kbuild", в ядрах серии 2.6 сборка модулей вы- полняется значительно проще, чем в старых сериях. Первое, что нужно сделать при сборке модулей, — это решить, где будет находиться исходный код модуля. Исходный код модуля необходимо правильно объединить с деревом исходных кодов ядра. Это можно сделать в виде заплаты или путем добавления в официальное дерево исходно- го кода ядра. Кроме этого, можно компилировать исходный код модуля отдельно от исходных кодов ядра.
    1   ...   38   39   40   41   42   43   44   45   ...   53


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