Дисковый кэш

Функции и принципы работы дискового кэша существенно отличаются от общих алгоритмов кэширования, обсуждавшихся в разд. Страничный обмен. Дело в том, что характер обращения к файлам обычно существенно отличается от обращений к областям кода и данных задачи. Например, компилятор С и макропроцессор ТЕХ рассматривают входные и выходные файлы как потоки данных. Входные файлы прочитываются строго последовательно и полностью, от начала до конца. Аналогично, выходные файлы полностью перезаписываются, и перезапись тоже происходит строго последовательно. Попытка выделить аналог рабочей области при таком характере обращений обречена на провал независимо от алгоритма, разве что рабочей областью будут считаться все входные и выходные файлы.
Тем не менее кэширование или, точнее, буферизация данных при работе с диском имеет смысл и во многих случаях может приводить к значительному повышению производительности системы. Если отсортировать механизмы повышения производительности в порядке их важности, мы получим следующий список.

  1. 1. Размещение в памяти структур файловой системы — каталогов, FAT пли таблицы инодов (эти понятия подробнее обсуждаются в главе И) и т. д. Это основной источник повышения производительности при использовании дисковых кэшей под MS/DR DOS.
  2. 2. Отложенная запись. Само по себе откладывание записи не повышает скорости обмена с диском, но позволяет более равномерно распределить по времени загрузку дискового контроллера.
  3. 3 Группировка запросов на запись. Система имеет пул буферов отложенной записи, который и называется дисковым кэшем. При поступлении запроса на запись, система выделяет буфер из этого пула и ставит его в очередь к драйверу. Если за время нахождения буфера в очереди в то же место на диске будет произведена еще одна запись, система может дописать данные в имеющийся буфер вместо установки в очередь второго запроса. Это значительно повышает скорость, если запись происходит массивами, не кратными размеру физического блока на диске.
  4. 4 Собственно кэширование. После того, как драйвер выполнил запрос, буфер не сразу используется повторно, поэтому какое-то время он содержит копию записанных или прочитанных данных. Если за это время произойдет обращение на чтение соответствующей области диска, система может отдать содержимое буфера вместо физического чтения.
  5. 5. Опережающее считывание. При последовательном обращении к данным чтение из какого-либо блока значительно повышает вероятность того, что следующий блок также будет считан. Теоретически опережающее чтение должно иметь тот же эффект, что и отложенная запись, т. е. обеспечивать более равномерную загрузку дискового канала и его работу параллельно с центральным процессором. На практике, однако, часто оказывается, что считанный с опережением блок оказывается никому не нужен, поэтому эффективность такого чтения заметно ниже, чем у отложенной записи.
  6. 6. Сортировка запросов по номеру блока на диске. По идее, такая сортировка должна приводить к уменьшению времени позиционирования головок чтения/записи (см. разд. Производительность жестких дисков). Кроме того, если очередь запросов будет отсортирована, это облегчит работу алгоритмам кэширования, которые производят поиск буферов по номеру блока.


Кэширование значительно повышает производительность дисковой подсистемы, но создает ряд проблем, причем некоторые из них довольно неприятного свойства.
Первая из проблем — та же, что и у отложенной записи. При использовании отложенной записи программа не знает, успешно ли завершилась физическая запись. При работе с дисками один из основных источников ошибок — физические ошибки диска. Однако многие современные файловые системы поддерживают так называемый hotfixing (горячую починку) — механизм, обеспечивающий динамическую замену "плохих" логических блоков на "хорошие", что в значительной мере компенсирует эту проблему.
Вторая проблема гораздо серьезнее и тоже свойственна всем механизмам отложенной записи: если в промежутке между запросом и физической записью произойдет сбой всей системы, то данные будут потеряны. Например, пользователь сохраняет отредактированный файл и. не дождавшись окончания физической записи, выключает питание — содержимое файла оказывается потеряно или повреждено. Другая ситуация, до боли знакомая всем пользователям DOS/Windows З.х/Windows 95: пользователь сохраняет файт и в это время система зависает — результат тот же. Аналогичного результата можно достичь, не вовремя достав дискету или другой удаляемый носите-ц, из привода (чтобы избежать этого, механика многих современных дисководов позволяет программно заблокировать носитель в приводе).
Очень забавно наблюдать, как пользователь, хотя бы раз имевший неприятный опыт общения с дисковым кэшем SMARTDRV, копирует данные с чужого компьютера на дискету. Перед тем, как извлечь ее из дисковода, он оглядывается на хозяина машины и с опаской спрашивает: "У тебя там никаких кэшей нет?". В эпоху MS DOS авторам доводилось наблюдать такое поведение у нескольких десятков людей.
Если откладывается запись не только пользовательских данных, но и модифицированных структур файловой системы, ситуация еще хуже: системный сбой может привести не только к потере данных, находившихся в кэше, но и к разрушению файловой системы, т. е. в худшем случае, к потере всех данных на диске.
Методы обеспечения целостности данных при системном сбое подробнее обсуждаются в разд. Устойчивость ФС к сбоям. Находившиеся в кэше данные при фатальном сбое гибнут всегда, но существуют способы избежать.повреждения системных структур данных на диске без отказа от использования отложенной записи.
Третья проблема, связанная с дисковым кэшем, — это выделение памяти под него. Уменьшение кэша приводит к снижению производительности дисковой подсистемы, увеличение же кэша отнимает память у пользовательских процессов. В системах с виртуальной памятью это может привести к увеличению дисковой активности за счет увеличения объема подкачки, что ведет к снижению как дисковой, так и общей производительности системы. Перед администратором системы встает нетривиальная задача: найти точку оптимума. Положение этой точки зависит от следующих параметров:

  • объема физической памяти;
  • скорости канала обмена с диском,
  • скорости центрального процессора;
  • суммы рабочих наборов программ, исполняющихся в системе;
  • интенсивности и характера обращений к диску.

При этом зависимость количества страничных отказов от объема памяти, доступной приложениям, имеет существенно нелинейный вид. Это же утверждение справедливо для связи между размером дискового кэша и соответствующей экономией обращений к диску. Таким образом, задача подбора оптимального размера кэша — это задача нелинейной оптимизации. Самое неприятное, что ключевой исходный параметр — характер обращений к диску — не количественный, а качественный; точнее сказать, его можно измерить лишь при помощи очень большого числа независимых количественных параметров.
Во многих ситуациях невозможно теоретически оценить положение оптимальной точки, и единственным способом оказывается эксперимент: прогон типичной для данной машины смеси заданий при различных объемах кэша. При этом нужно иметь возможность различать дисковую активность, связанную с обращениями к файлам и со страничным обменом. Большинство современных ОС предоставляют для этой цели различные инструменты системного мониторинга. Чаше, однако, объем кэша выставляется на глаз, а к дополнительной настройке прибегают, только если производительность оказывается слишком низкой.
Возникает вполне естественное желание возложить подбор размера кэша на саму систему, т. е. менять размер кэша динамически в зависимости от рабочей нагрузки. Кроме упрощения работы администратора, такое решение имеет еще одно большое преимущество: система начинает "автомагически" подстраиваться под изменения нагрузки.
Но далеко не все так просто. Если объем памяти в системе превосходит потребности прикладных программ, то динамический дисковый кэш может формироваться по очень простому "остаточному" принципу — все, что не пригодилось приложениям, отдается под кэш. Однако оперативная память до сих пор относительно дорога и представляет собой дефицитный ресурс, поэтому наибольший практический интерес представляет ситуация, когда памяти не хватает даже приложениям, не говоря уже о кэше. Тем не менее и в этой ситуации кэш некоторого объема бывает нужен.
Разумной политикой была бы подстройка кэша в зависимости от количества страничных отказов: если число отказов становится слишком большим, система уменьшает кэш; если же число отказов мало, а идут интенсивные обращения к диску, система увеличивает кэш. Получается саморегулирующаяся система с отрицательной обратной связью. Однако, если вдуматься, то видно, что вместо одной произвольной переменной (объема статического кэша) мы вынуждены ввести как минимум три:

  • количество страничных отказов, которое считается слишком большим;
  • количество отказов, которое считается достаточно малым;
  • величину, на которую следует увеличить или уменьшить кэш в этих случаях.

На практике, часто также вводятся параметры, ограничивающие минимальный и максимальный размеры кэша.
Оптимальные значения этих переменных зависят практически от тех же самых параметров, что и объем статического кэша, но подбор значений экспериментальным путем оказывается значительно сложнее, потому что вместо одномерной нелинейной оптимизации мы вынуждены занимать трехмерной нелинейной оптимизацией.
Кроме того, читатель, знакомый с теорией управления, должен знать чт неудачный подбор параметров у системы с отрицательной обратной связь может приводить к колебательному процессу вместо саморегуляции, в дм куссиях USENET news приводились примеры развития таких колебаний динамическом кэше системы Windows NT при компиляции большого проекта в условиях недостатка памяти.
Вполне возможно, что низкая производительность Windows NT/2000/XP на машинах с небольшим количеством памяти объясняется вовсе не низким качеством реализации и даже не секретным сговором между фирмой Microsoft и производителями оперативной памяти, а просто плохо сбалансированным динамическим кэшем.