получить буфер для блока (алгоритм getblk);
if (данные в буфере правильные)
return буфер;
приступить к чтению с диска;
sleep (до завершения операции чтения);
return (буфер);
}
Рисунок 3.13. Алгоритм чтения дискового блока
Алгоритм записи содержимого буфера в дисковый блок (Рисунок 3.15) похож на алгоритм чтения. Ядро информирует дисковод о том, что есть буфер, содержимое которого должно быть выведено, и дисковод планирует операцию ввода-вывода блока. Если запись производится синхронно, вызывающий процесс приостанавливается, ожидая ее завершения и освобождая буфер в момент возобновления своего выполнения. Если запись производится асинхронно, ядро запускает операцию записи на диск, но не ждет ее завершения. Ядро освободит буфер, когда завершится ввод-вывод.
алгоритм breada /* чтение блока с продвижением */
входная информация:
(1) в файловой системе номер блока для немедленного считывания
(2) в файловой системе номер блока для асинхронного считывания
выходная информация: буфер с данными, считанными немедленно
{
if (первый блок отсутствует в кеше)
{
получить буфер для первого блока (алгоритм getblk);
if (данные в буфере неверные)
приступить к чтению с диска;
}
if (второй блок отсутствует в кеше)
{
получить буфер для второго блока (алгоритм getblk);
if (данные в буфере верные)
освободить буфер (алгоритм brelse);
else
приступить к чтению с диска;
}
if (первый блок первоначально находился в кеше)
{
считать первый блок (алгоритм bread);
return буфер;
}
sleep (до того момента, когда первый буфер будет содержать верные данные);
return буфер;
}
Рисунок 3.14. Алгоритм чтения блока с продвижением
Могут возникнуть ситуации, и это будет показано в следующих двух главах, когда ядро не записывает данные немедленно на диск. Если запись «откладывается», ядро соответствующим образом помечает буфер, освобождая его по алгоритму brelse, и продолжает работу без планирования ввода-вывода. Ядро записывает блок на диск перед тем, как другой процесс сможет переназначить буфер другому блоку, как показано в алгоритме getblk (случай 3). Между тем, ядро надеется на то, что процесс получает доступ до того, как буфер будет переписан на диск; если этот процесс впоследствии изменит содержимое буфера, ядро произведет дополнительную операцию по сохранению изменений на диске.
алгоритм bwrite /* запись блока */
входная информация: буфер
выходная информация: отсутствует
{
приступить к записи на диск;
if (ввод-вывод синхронный)
{
sleep (до завершения ввода-вывода);
освободить буфер (алгоритм brelse);
}
else
if (буфер помечен для отложенной записи)
пометить буфер для последующего размещения в «голове» списка свободных буферов;
}
Рисунок 3.15. Алгоритм записи дискового блока
Отложенная запись отличается от асинхронной записи. Выполняя асинхронную запись, ядро запускает дисковую операцию немедленно, но не дожидается ее завершения. Что касается отложенной записи, ядро отдаляет момент физической переписи на диск насколько возможно; затем по алгоритму getblk (случай 3) оно помечает буфер как «старый» и записывает блок на диск асинхронно. После этого контроллер диска прерывает работу системы и освобождает буфер, используя алгоритм brelse; буфер помещается в «голову» списка свободных буферов, поскольку он имеет пометку «старый». Благодаря наличию двух выполняющихся асинхронно операций ввода-вывода — чтения блока с продвижением и отложенной записи — ядро может запускать программу brelse из программы обработки прерываний. Следовательно, ядро вынуждено препятствовать возникновению прерываний при выполнении любой процедуры, работающей со списком свободных буферов, поскольку brelse помещает буферы в этот список.
3.5 ПРЕИМУЩЕСТВА И НЕУДОБСТВА БУФЕРНОГО КЕША
Использование буферного кеша имеет, с одной стороны, несколько преимуществ и, с другой стороны, некоторые неудобства.
• Использование буферов позволяет внести единообразие в процедуру обращения к диску, поскольку ядру нет необходимости знать причину ввода-вывода. Вместо этого, ядро копирует данные в буфер и из буфера, невзирая на то, являются ли данные частью файла, индекса или суперблока. Буферизация ввода-вывода с диска повышает модульность разработки программ, поскольку те составные части ядра, которые занимаются вводом-выводом на диск, имеют один интерфейс на все случаи. Короче говоря, упрощается проектирование системы.
• Система не накладывает никаких ограничений на выравнивание информации пользовательскими процессами, выполняющими ввод-вывод, поскольку ядро производит внутреннее выравнивание информации. В различных аппаратных реализациях часто требуется выравнивать информацию для ввода-вывода с диска определенным образом, т. е. производить к примеру двухбайтное или четырехбайтное выравнивание данных в памяти. Без механизма буферизации программистам пришлось бы заботиться самим о правильном выравнивании данных. По этой причине на машинах с ограниченными возможностями в выравнивании адресов возникает большое количество ошибок программирования и, кроме того, становится проблемой перенос программ в операционную среду UNIX. Копируя информацию из пользовательских буферов в системные буферы (и обратно), ядро системы устраняет необходимость в специальном выравнивании пользовательских буферов, делая пользовательские программы более простыми и мобильными.