Буферизация
6.7 Буферизация
Хотя блокировки и не влияют на результат выполнения процесса и не отражаются на его виртуальном времени, они сказываются на реальном времени его выполнения. Для интерактивных процессов они могут стать основным фактором, определяющим время реакции процесса. Невыгодны блокировки и для ОС, так как каждая блокировка - это переключение процессов, а следовательно, накладные расходы. Одним из способов, позволяющим избежать блокировок (или, по крайней мере, уменьшить их количество), является буферизация данных. Для устройства ввода-вывода назначается буферная область в оперативной памяти. Обмен данными происходит между процессом и буферной областью, а обмен между буферной областью и устройством выполняет ОС независимо от выполнения процесса. Если, например, выполнение системного вызова write будет включать в себя только пересылку данных в оперативной памяти, то блокировка процесса на время выполнения этого вызова не нужна. Буферизация, таким образом, сглаживает различия в скоростях работы производителя и потребителя информации и позволяет избежать излишних блокировок.
Особенно существенный эффект буферизация может дать при последовательном вводе-выводе, так как при вводе ОС может предсказать, какой блок информации понадобится следующим и произвести его упреждающее чтение в буфер. Еще один полезный эффект буферизации - ОС имеет возможность сгруппировать данные в буфере и производить обмен с внешним устройством большими блоками.
Богатые вариантами средства организации ввода-вывода с использованием буферизации были впервые реализованы в OS/360[15] и унаследованы следующими поколениями ОС мейнфреймов. При синхронной организации ввода-вывода (в OS/360 - "методы доступа с очередями") буферизация прозрачна для процесса, она полностью обеспечивается ОС. При асинхронной организации ("базисные методы доступа") процесс сам может организовать буферизацию. Устройству назначается буферная область, которая форматируется как буферный пул. В методах с очередями это назначение происходит автоматически, программист может только изменить размер пула, если его не устраивает принятый по умолчанию. В базисных методах есть специальные системные вызовы для выделения, освобождения, форматирования буферного пула и связывания пула с устройством. Буферизация не обязательно означает дополнительную пересылку данных в оперативной памяти. Методы с очередями предоставляют три альтернативных режима управления буферизацией:
- режим пересылки - данные пересылаются в оперативной памяти между рабочей областью процесса и буфером;
- режим указания - данные не пересылаются; при вводе процесс получает от ОС адрес буфера, содержащего введенные данные, и может использовать его как рабочую область, при выводе процесс получает от ОС адрес свободного выводного буфера и использует его как свою рабочую область, формируя в ней данные для вывода;
- режим подстановки - данные не пересылаются; при вводе процесс получает от ОС адрес заполненного вводного буфера, а свою рабочую область передает во вводной буферный пул; при выводе процесс передает в выводной пул свою рабочую область, заполненную выводными данными, а взамен получает свободный буфер из пула.
Два интересных примера буферизации мы возьмем из ОС Unix.
В Unix буферизация обмена с дисковыми накопителями (кэширование) является тотальной, через кэш проходят все данные, которыми ОС и процессы обмениваются с дисками, и под кэш отводится значительная часть оперативной памяти. Кэширование дает значительный эффект и при случайном (непоследовательном) обмене, характерном для многозадачной многопользовательской ОС. В дескрипторе каждого буфера в кеше имеются поля:
- состояние буфера (свободен/содержит правильную информацию/грязный/заблокирован/занят в операции ввода-вывода/ожидается каким-либо процессом);
- адрес на внешней памяти блока информации, содержащегося в буфере;
- указатели, связывающие буфера в хеш-очереди (см.дальше) и в список свободных.
Список свободных буферов организован как простая FIFO-очередь: ядро ОС выбирает буфер из головы списка, освобождающийся буфер включает в конец списка. Таким образом, буфер, попавший в список свободных еще некоторое время находится в этом списке, сохраняя свое содержимое, и может быть выбран из списка, если к нему будет обращение. При обращении к дисковому вводу-выводу основным параметром является адрес блока на внешней памяти. ОС прежде всего ищет в кеше соответствующий блоку буфер и только при неудачном поиске в кеше назначает свободный буфер и производит физическое обращение к диску. Для ускорения поиска в кеше все буфера распределяются по нескольким спискам, именуемым хеш-очередями. Номер списка определяется как результат весьма простой функции хеширования, аргументом которой является адрес на внешней памяти. Применение хеширования позволяет равномерно распределить буферы по хеш-очередям. Каждый буфер может входить только в одну хеш-очередь, но также может быть включен и в список свободных. Процесс, выдавший запрос на ввод-вывод, блокируется в случаях:
- если буфера нет в хеш-очереди и список свободных блоков пуст;
- если буфер есть в хеш-очереди, но он занят.
При освобождении блока разблокируются все процессы, ждущие освобождения этого или любого блока.
Все современные ОС (OS/2, Windows 95, Windows NT) в той или иной степени применяют тотальное кэширование при обмене с дисками.
Второй пример - буферизация ввода в драйвере терминала. Задача такой буферизации - обеспечить накопление данных и возможность упреждающего ввода с терминала. Для этой буферизации характерны: последовательный ввод, сравнительно небольшие объемы данных в буфере, непостоянный объем информации, содержащейся в буфере. Буфер представляет собой цепочку блоков, каждый из которых имеет постоянную длину N. В каждом блоке имеются следующие поля:
- указатель на следующий блок в цепочке;
- смещение первого символа в поле данных;
- смещение последнего символа в поле данных;
- поле данных для хранения N символов.