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

  • Аппаратная платформа Частота (в герцах)

  • Возможна ли операционная система без периодических отметок времени

  • 2 1 2 Глава 10

  • Этимология слова jiffy

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


    Скачать 3.09 Mb.
    НазваниеВторое издание
    Дата08.09.2019
    Размер3.09 Mb.
    Формат файлаpdf
    Имя файлаLav_Robert_Razrabotka_yadra_Linux_Litmir.net_264560_original_254.pdf
    ТипДокументы
    #86226
    страница28 из 53
    1   ...   24   25   26   27   28   29   30   31   ...   53
    Таблица 10.1. Значение частоты системного таймера
    Аппаратная платформа Частота (в герцах)
    alpha arm cris h8300
    i386
    ia64
    m68k m68knommu mips mips64
    parisc ppc ppc64
    s390
    sti spare sparc64
    um v850
    x86-64
    Идеальное значение параметра HZ
    Для аппаратной платформы i386, начиная с самых первых версий операционной системы Linux, значение частоты системного таймера было равно 100 Гц. Однако во время разработки ядер серии 2.5 это значение было увеличено до 1000 Гц, что
    (как всегда бывает в подобных ситуациях) вызвало споры. Так как в системе очень многое зависит от прерывания таймера, то изменение значения частоты системного таймера должно оказывать сильное влияние на систему. Конечно, как в случае боль- ших, так и в случае маленьких значений параметра HZ есть свои положительные и отрицательные стороны.
    Увеличение значения частоты системного таймера означает, что обработчик пре- рываний таймера выполняется более часто. Следовательно, вся работа, которую он делает, также выполняется более часто. Это позволяет получить следующие преиму- щества.
    • Прерывание таймера имеет большую разрешающую способность по времени,
    и следовательно, все событии, которые выполняются во времени, также имеют большую разрешающую способность.
    • Увеличивается точность выполнения событий во времени.
    2
    Эмулятор платформы IA-64 имеет частоту 32 Гц. Настоящая машина платформы IA-64 имеет часто- ту 1024 Гц.
    •••
    210 Глава 10 1024 100 100 100 1000 32 или 1024 2
    100 50, 100 или 1000 100 100 100 или 1000 100 1000 100 100 100 100 100 24, 100 или 122 1000

    Разрешающая способность увеличивается во столько же раз, во сколько раз воз- растает частота импульсов. Например, гранулярность таймеров при частоте импуль- сов 100 Гц равна 10 миллисекунд. Другими словами, все периодические события вы- полняются прерыванием таймера, которое генерируется с предельной точностью по времени, равной 10 миллисекунд, и большая точность
    3
    не гарантируется. При часто- те, равной 1000 Гц, разрешающая способность равна 1 миллисекунде, т.е. в 10 раз выше. Хотя ядро позволяет создавать таймеры с временным разрешением, равным
    1 миллисекунде, однако при частоте системного таймера в 100 Гц нет возможности гарантированно получить временной интервал, короче 10 миллисекунд.
    Точность измерения времени также возрастает аналогичным образом. Допустим,
    что таймеры ядра запускаются в случайные моменты времени, тогда в среднем тай- меры будут срабатывать с точностью по времени до половины периода прерывания таймера, потому что период времени таймера может закончиться в любой момент, а обработчик таймера может выполниться, только когда генерируется прерывание тай- мера. Например, при частоте 100 Гц описанные события в среднем будут возникать с точностью +/- 5 миллисекунд от желаемого момента времени. Поэтому ошибка из- мерения в среднем составит 5 миллисекунд. При частоте 1000 Гц ошибка измерения в среднем уменьшается до 0.5 миллисекунд — получает десятикратное улучшение.
    Более высокое разрешение и большая точность обеспечивают следующие преиму- щества.
    • Таймеры ядра выполняются с большим разрешением и с лучшей точностью
    (это позволяет получить много разных улучшений, некоторые из которых опи- саны дальше).
    • Системные вызовы, такие как poll () и select (), которые позволяют при же- лании использовать время ожидания (timeout) в качестве параметра, выполня- ются с большей точностью.
    • Измерения, такие как учет использования ресурсов или измерения времени ра- боты системы, выполняются с большей точностью.
    • Вытеснение процессов выполняется более правильно.
    Некоторые из наиболее заметных улучшений производительности — это улуч- шения точности измерения периодов времени ожидания при выполнении систем- ных вызовов p o l l () и s e l e c t (). Это улучшение может быть достаточно большим.
    Прикладная программа, которая интенсивно использует эти системные вызовы,
    может тратить достаточно много времени, ожидая на прерывания таймера, хотя в действительности интервал времени ожидания уже истек. Следует вспомнить, что средняя ошибка измерения времени (т.е. потенциально зря потраченное время) рав- на половине периода прерывания таймера.
    Еще одно преимущество более высокой частоты следования импульсов тайме- ра— это более правильное вытеснение процессов, что проявляется в уменьшении задержки за счет планирования выполнения процессов. Вспомним из материала главы 4, что прерывание таймера ответственно за уменьшение кванта времени вы-
    3
    Здесь имеется в виду не точность измерения, а точность в вычислительном плане. Точность из- мерения (в общенаучном смысле) — это статистическая мера повторяемости результата. В вычис- лительном (компьютерном) смысле точность— это количество значащих цифр, которые использу- ются для представления того или другого значения.
    Таймеры и управление временем 211
    полняющегося процесса. Когда это значение уменьшается до нуля, устанавливается флаг need_resched, и ядро активизирует планировщик как только появляется та- кая возможность. Теперь рассмотрим ситуацию, когда процесс в данный момент вы- полняется и у него остался квант времени, равный 2 миллисекундам. Это означает,
    что через 2 миллисекунды планировщик должен вытеснить этот процесс и запустить на выполнение другой процесс. К сожалению, это событие не может произойти до того момента, пока не будет сгенерировано следующее прерывание таймера. В са- мом худшем случае следующее прерывание таймера может возникнуть через 1/HZ
    секунд! В случае, когда параметр HZ=100, процесс может получить порядка 10 лиш- них миллисекунд. Конечно, в конце концов все будет сбалансировано и равнодоступ- ность ресурсов не нарушится, потому что все задания планируются с одинаковыми ошибками, и проблема состоит не в этом. Проблемы возникают из-за латентности,
    которую вносят задержки вытеснения процессов. Если задание, которое планирует- ся на выполнение, должно выполнить какие-нибудь чувствительные ко времени дей- ствия, как, например, заполнить буфер аудиоустройства, то задержка не допустима.
    Увеличение частоты до 1000 Гц уменьшает задержку планировщика в худшем случае до 1 миллисекунды, а в среднем — до 0.5 миллисекунды.
    Должна, однако, существовать и обратная сторона увеличения частоты систем- ного таймера, иначе она была бы с самого начала равна 1000 Гц (или даже больше).
    На самом деле существует одна большая проблема. Более высокая частота вызывает более частые прерывания таймера, что означает большие накладные затраты. Чем выше частота, тем больше времени процессор должен тратить на выполнение пре- рываний таймера. Это приводит не только к тому, что другим задачам отводится меньше процессорного времени, но и к периодическому трешингу (trashing) кэша процессора (т.е. кэш заполняется данными, которые не используются процессором).
    Проблема, связанная с накладными расходами, вызывает споры. Ясно, что переход от значения HZ=100 до значения HZ=1000 в 10 раз увеличивает накладные затраты,
    связанные с прерываниями таймера. Однако от какого реального значения наклад- ных затрат следует отталкиваться? Если "ничего" умножить на 10, то получится тоже "ничего". Решающее соглашение состоит в том, что по крайней мере для современ- ных систем, значение параметра HZ=1000 не приводит к недопустимым накладным затратам. Тем не менее для ядер серии 2.6 существует возможность скомпилировать ядро с другим значением параметра HZ
    4
    Возможна ли операционная система без периодических отметок времени
    Может возникнуть вопрос, всегда ли для функционирования операционной системы необходи- мо использовать фиксированное прерывание таймера? Можно ли создать операционную систе- му, в которой не используются периодические отметки времени? Да, можно, но результат будет
    не очень привлекательным.
    Нет строгой необходимости в использовании прерывания таймера, которое возникает с фик- сированной частотой. Вместо этого ядро может использовать динамически программируемый таймер для каждого ожидающего события. Такое решение сразу же приведет к дополнитель- ным накладным затратам процессорного времени в связи с обработкой событий таймера, по- этому лучшим решением будет использовать один таймер и программировать его так, чтобы он срабатывал тогда, когда должно наступить ближайшее событие.
    4
    В связи с ограничениями аппаратной платформы и протокола NTP, значение переменной HZ не может быть произвольным. Для платформы х86 значения 100, 500 и 1000 работают хорошо.
    2 1 2 Глава 10

    Когда обработчик таймера сработает, создается новый таймер для следующего события и так повторяется постоянно. При таком подходе не требуется периодическое прерывание таймера и нет необходимости в параметре HZ.
    Однако при указанном подходе необходимо решить две проблемы. Первая проблема — это как в таком случае реализовать концепцию периодических отметок времени, хотя бы для того,
    чтобы ядро могло отслеживать относительные интервалы времени. Эту проблему решить не сложно. Вторая проблема — это как избежать накладных затрат, связанных с управлением ди- намическими таймерами, даже при наличии оптимизации. Данную проблему решить сложнее.
    Накладные расходы и сложность реализации получаются настолько высокими, что в операцион- ной системе Linux такой подход решили не использовать. Тем не менее так пробовали делать, и результаты получаются интересными. Если интересно, то можно поискать в Интернет-архивах.
    Переменная j i f f i e s
    Глобальная переменная j i f f i e s содержит количество импульсов системного таймера, которые были получены со времени загрузки системы. При загрузке ядро устанавливает значение этого параметра в нуль и он увеличивается на единицу при каждом прерывании системного таймера. Так как в секунду возникает HZ прерыва- ний системного таймера, то за секунду значение переменной j i f f i e s увеличивается на HZ. Время работы системы (uptime) поэтому равно jiffies/HZ секунд.
    Этимология слова jiffy
    Происхождение слова jiffy (миг, мгновение) точно неизвестно. Считается, что фразы типа "in a
    jiffy" (в одно мгновение) появились в Англии в восемнадцатом веке. В быту термин jiffy [миг)
    означает неопределенный, но очень короткий промежуток времени.
    В научных приложениях слово jiffy используется для обозначения различных интервалов време- ни (обычно порядка 10 ms). В физике это слово иногда используется для указания интервала времени, который требуется свету, чтобы пройти определенное расстояние (обычно, фут, сан- тиметр, или расстояние, равное размеру нуклона).
    В вычислительной технике термин jiffy — это обычно интервал времени между двумя соседними импульсами системного таймера, которые были успешно обработаны. В электричестве jiffy —
    период переменного тока. В США jiffy— это 1/60 секунды.
    В приложении к операционным системам, в частности к Unix, jiffy— это интервал времени меж- ду двумя соседними успешно обработанными импульсами системного таймера. Исторически это значение равно 100 ms. Как уже было показано, интервал времени jiffy в операционной си- стеме Linux может иметь разные значения.
    Переменная j i f f i e s определена в файле < l i n u x / j i f f i e s . h > следующим обра- зом.
    extern unsigned long volatile jiffies;
    Определение этой переменной достаточно специфичное, и оно будет рассмотре- но более подробно в следующем разделе. Сейчас давайте рассмотрим пример кода ядра. Пересчет из секунд в значение переменной j i f f i e s можно выполнить следу- ющим образом.
    (секунды * HZ)
    Таймеры и управление временем 2 1 3

    Отсюда следует, что преобразование из значения переменной j i f f i e s в секунды можно выполнить, как показано ниже.
    (jiffies / HZ)
    Первый вариант встречается более часто. Например, часто необходимо устано- вить значение некоторого момента времени в будущем.
    unsigned long time_starnp = jiffies; /* сейчас */
    unsigned long next_tick = jiffies + 1; /* через один импульс таймера от текущего момента */
    unsigned long later = jiffies + 5*HZ; /* через пять секунд от текущего момента */
    Последний пример обычно используется при взаимодействии с пространством пользователя, так как в самом ядре редко используется абсолютное время.
    Заметим, что переменная j i f f i e s имеет тип unsigned long и использовать ка- кой-либо другой тип будет неправильным.
    Внутреннее представление переменной j i f f i e s
    Переменная j i f f i e s исторически всегда представлялась с помощью типа unsigned long и, следовательно, имеет длину 32 бит для 32-разрядных аппаратных платформ и 64 бит для 64-разрядных. В случае 32-разрядного значения переменной j i f f i e s и частоты появления временных отметок 100 раз в секунду, переполнение этой переменной будет происходить примерно каждые 497 дней, что является впол- не возможным событием. Увеличение значения параметра HZ до 1000 уменьшает пе- риод переполнения до 47.9 дней! В случае 64-разрядного типа переменной j i f f i e s ,
    переполнение этой переменной невозможно за время существования чего-либо при любых возможных значениях параметра HZ для любой аппаратной платформы.
    Из соображений производительности и по историческим причинам — в основ- ном, для совместимости с уже существующим кодом ядра — разработчики ядра пред- почли оставить тип переменной j i f f i e s — unsigned long. Для решения проблемы пришлось немного подумать и применить возможности компоновщика.
    Как уже говорилось, переменная jiffies определяется в следующем виде и имеет тип unsigned long.
    extern unsigned long volatile jiffies;
    Вторая переменная определяется в файле < l i n u x / j i f f i e s . h > в следующем виде.
    extern u64 jiffies_64;
    Директивы компоновщика ld (1), которые используются для сборки главного об- раза ядра (для аппаратной платформы х86 описаны в файле arch/i386/kernel/
    vmlinux.lds.S), указывают компоновщику, что переменную j i f f i e s необходимо совместить с началом переменной jiffies_64.
    Jiffies = jiffies_64;
    Следовательно, переменная j i f f i e s — это просто 32 младших разряда полной
    64-разрядной переменной jiffies_64. Так как в большинстве случаев переменная
    214 Глава 10

    5
    Необходима специальная функция, так как на 32-разрядных аппаратных платформах нельзя ато- марно обращаться к двум машинным словам 64-разрядного значения, Специальная функция, перед тем как считать значение, блокирует счетчик импульсов системного таймера с помощью блокиров- ки xtime_lock.
    Таймеры и управление временем 215
    j i f f i e s используется для измерения промежутков времени, то для большей части кода существенными являются только младшие 32 бит.
    В случае применения 64-разрядного значения, переполнение не может возник- нуть за время существования чего-либо. В следующем разделе будут рассмотрены проблемы, связанные с переполнением (хотя переполнение счетчика импульсов си- стемного таймера и не желательно, но это вполне нормальное и ожидаемое собы- тие). Код, который используется для управления ходом времени, использует все 64
    бит, и это предотвращает возможность переполнения 64-разрядного значения. На рис. 10.1 показана структура переменных j i f f i e s и j i f f i e s _ 6 4 .
    Переменная j i f f i e s _ 6 4 (и переменная j i f f i e s на 64-разрядной машине)
    Переменная j i f f i e s на 32-разрядной машине
    Рис. 10.1. Структура переменных jiffies и jiffies_64
    Код, который использует переменную j i f f i e s , просто получает доступ к трид- цати двум младшим битам переменной j i f f i e s _ 6 4 . Функция g e t _ j i f f i e s _ 6 4 ()
    может быть использована для получения полного 64-разрядного значения
    5
    . Такая не- обходимость возникает редко, следовательно большая часть кода просто продолжает считывать младшие 32 разряда непосредственно из переменной j i f f i e s .
    На 64-разрядных аппаратных платформах переменные j i f f i e s _ 6 4 и j i f f i e s просто совпадают. Код может либо непосредственно считывать значение перемен- ной j i f f i e s , либо использовать функцию g e t _ j i f f i e s _ 6 4 (), так как оба этих спо- соба позволяют получить аналогичный эффект.
    Переполнение переменной j i f f i e s
    Переменная j i f f i e s , так же как и любое целое число языка программирования
    С, после достижения максимально возможного значения переполняется. Для 32-раз- рядного беззнакового целого числа максимальное значение равно 2 3 2
    -1. Поэтому перед тем как счетчик импульсов системного таймера переполнится, должно прий- ти 4294967295 импульсов таймера. Если значение счетчика равно этому значению и счетчик увеличивается на 1, то значение счетчика становится равным нулю.
    Рассмотрим пример переполнения.
    unsigned long timeout = j i f f i e s + HZ/2; /* значение лимита времени равно 0.5 с */

    /* выполним некоторые действия и проверим, не слишком ли это много заняло времени . . . */
    if (timeout < jiffies) {
    /* мы превысили лимит времени — это ошибка ... */
    } else {
    /* мы не превысили лимит времени — это хорошо ... */
    }
    Назначение этого участка кода — установить лимит времени до наступления не- которого события в будущем, а точнее полсекунды от текущего момента. Код может продолжить выполнение некоторой работы — возможно, записать некоторые дан- ные в аппаратное устройство и ожидать ответа. После выполнения, если весь про- цесс превысил лимит установленного времени, код соответственным образом обра- батывает ошибку.
    В данном примере может возникнуть несколько потенциальных проблем, свя- занных с переполнением. Рассмотрим одну из них. Что произойдет, если перемен- ная j i f f i e s переполнится и снова начнет увеличиваться с нуля после того, как ей было присвоено значение переменной timeout? При этом условие гарантированно не выполнится, так как значение переменной j i f f i e s будет меньше, чем значение переменной timeout, хотя логически оно должно быть больше. По идее значение переменной j i f f i e s должно быть огромным числом, всегда большим значения пе- ременной timeout. Так как эта переменная переполнилась, то теперь ее значение стало очень маленьким числом, которое, возможно, отличается от нуля на несколько импульсов таймера. Из-за переполнения результат выполнения оператора if меняет- ся на противоположный!
    К счастью, ядро предоставляет четыре макроса для сравнения двух значений счетчика импульсов таймера, которые корректно обрабатывают переполнение счет- чиков. Они определены в файле следующим образом.
    #define time_after(unknown, known) ((long)(known) - (long)(unknown) < 0)
    #define time_before(unknown, known) ((long) (unknown) - (long)(known) < 0)
    #define time_after_eq(unknown, known) ((long) (unknown) - (long)(known) >= 0)
    #define time_before_eq(unknown, known) ((long) (known) - (long)(unknown) >= 0)
    Параметр unknown — это обычно значение переменной j i f f i e s , а параметр known — значение, с которым его необходимо сравнить.
    Макрос time_after (unknown, known) возвращает значение true, если момент времени unknown происходит после момента времени known, в противном случае возвращается значение false. Макрос time_before (unknown, known) возвраща- ет значение true, если момент времени unknown происходит раньше, чем момент времени known, в противном случае возвращается значение false. Последние два макроса работают аналогично первым двум, за исключением того, что возвращается значение "истинно", если оба параметра равны друг другу.
    Версия кода из предыдущего примера, которая предотвращает ошибки, связан- ные с переполнением, будет выглядеть следующим образом.
    unsigned long timeout = jiffies + HZ/2; /* значение лимита времени равно 0.5 с */
    1   ...   24   25   26   27   28   29   30   31   ...   53


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