Редактирование при интерактивном вводе 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Редактирование при интерактивном вводе



Несколько особняком от других форм буферизации стоит использование буфера строки при вводе с клавиатуры.

Для пользователя привычно, что в процессе ввода числовых или строковых значений он может легко откорректировать ошибки ввода: «забить» неверный символ, вернуться в любое место вводимой строки и внести там изменения и т.п. При этом прикладная программа «не видит» процесса редактирования строки, она получает всю строку целиком после нажатия, например, клавиши Enter. Чтобы обеспечить возможность редактирования вводимой строки, используется буфер строки, выделяемый либо ОС, либо библиотекой времени выполнения конкретной системы программирования. Все редактирование выполняется над символами, которые помещаются в этот буфер подпрограммами ввода с клавиатуры. После нажатия Enter происходит либо копирование символов из буфера в массив, выделенный прикладной программой, либо передача этой программе указателя на буфер.

Кэширование дисков

Очень важной, специфической формой буферизации является кэширование [2]. Этот термин означает использование сравнительно небольшой по объему, но быстродействующей памяти для того, чтобы уменьшить количество обращений к более медленной памяти большого объема.

Идея кэширования основывается на так называемой гипотезе о локальности ссылок. Эта гипотеза заключается в следующем. Если в какой-то момент времени произошло обращение к определенному участку данных, то в ближайшее время можно с высокой вероятностью ожидать повторения обращений к тем же самым данным или же к соседним участкам данных. Конечно, локальность ссылок нельзя считать законом, однако практика показывает, что эта гипотеза оправдывается для подавляющего большинства программ.

В современных вычислительных системах может использоваться несколько уровней кэширования. В данном курсе не рассматривается аппаратный кэш процессора, позволяющий сократить число обращений к основной памяти за счет использования быстродействующих регистров. К работе ОС более прямое отношение имеет программное кэширование устройств произвольного доступа (дисковых накопителей). В этом случае гипотезу о локальности ссылок можно переформулировать более конкретно: если программа выполнила чтение или запись данных из некоторого блока диска, то весьма вероятно, что в скором будущем последуют еще операции чтения или записи данных из того же блока.

В роли быстродействующей памяти (кэша) здесь выступает массив буферов, размещенный в системной памяти. Каждый буфер состоит из заголовка и блока данных, соответствующего по размеру блоку (сектору) диска. Заголовок буфера содержит адрес блока диска, копия которого в данный момент содержится в буфере, и несколько флагов, характеризующих состояние буфера.

Когда система получает запрос на чтение или запись определенного блока данных диска, она прежде всего проверяет, не содержится ли в данный момент копия этого блока в одном из буферов кэша. Для этого требуется выполнить поиск по заголовкам буферов. Если блок найден в кэше, то обращение к диску выполняться не будет. Вместо этого данные читаются из буфера или, соответственно, записываются в буфер. В случае записи данных следует также в заголовке буфера отметить с помощью специального флага, что буфер стал «грязным», т.е. его содержимое не соответствует данным на диске.

Если требуемый блок диска не найден в кэше, то для него должен быть выделен буфер. Проблема в том, что общее количество буферов кэша ограничено. Чтобы отдать один из них под требуемый блок, надо «вытеснить» из кэша один из блоков, которые там хранились. При этом, если вытесняемый блок «грязный», то он должен быть «очищен», т.е. записан на диск. При вытеснении «чистого» блока никаких операций с диском выполнять не надо.

Какой из блоков, хранящихся в кэше, следует выбрать для вытеснения, чтобы сократить общее количество обращений к диску? Это крайне важный вопрос, и если он будет решаться неправильно, то вся работа системы может затормозиться из-за постоянных обращений к диску.

Имеется теоретически оптимальное решение данной задачи, которое заключается в следующем. Число обращений к диску будет минимально, если каждый раз выбирать для вытеснения тот блок данных, к которому в будущем дольше всего не будет обращений. К сожалению, воспользоваться этим правилом на практике невозможно, так как последовательность обращений к блокам диска непредсказуема. Данный теоретический результат полезен только как недостижимый идеал, с которым можно сравнивать результаты применения более реалистичных алгоритмов выбора.

Среди алгоритмов, используемых на практике, лучшим считается алгоритм LRU (Least Recently Used, в вольном переводе «давно не использовавшийся»). Он заключается в следующем: выбирать для вытеснения следует тот блок, к которому дольше всего не было обращений. Здесь как раз используется принцип локальности ссылок: раз обращений давно не было, то, вероятно, их и не будет в ближайшее время.

Как на практике реализуется выбор блока по правилу LRU? Очевидное решение – при каждом обращении к буферу записывать в его заголовке текущее время, а при выборе для вытеснения искать самую раннюю запись – слишком громоздко и медленно. Есть гораздо лучшая возможность.

Все буферы кэша связываются в линейный список. В заголовке каждого буфера хранится ссылка на следующий по порядку списка буфер (фактически хранится индекс этого буфера в массиве буферов). При каждом обращении к блоку данных для чтения или записи выполняется также перемещение соответствующего буфера в конец списка. Это не означает перемещения данных, хранящихся в буфере, изменяются только несколько ссылок в заголовках.

В результате постоянного перемещения использованных блоков в конец списка буферов этот список оказывается отсортированным по возрастанию времени последнего обращения. В начале списка оказывается тот буфер, к данным которого дольше всего не было обращений. Он-то нам и нужен как кандидат на вытеснение.

На рис. 2‑3 показан массив буферов, связанный в список.

Рис. 2‑3

Теперь о «грязных» буферах. В каких случаях должна выполняться их «очистка», т.е. запись блока данных из кэш-буфера на диск? Можно назвать три таких случая.

· Выбор блока для вытеснения из кэша.

· Закрытие файла, к которому относятся «грязные» блоки. Общепринято, что при закрытии файла должно выполняться его сохранение на диске.

· Операция принудительной очистки всех буферов либо только буферов, относящихся к определенному файлу. Подобная операция может выполняться для повышения надежности хранения данных, как страховка от возможных сбоев. В ОС UNIX, например, очистка всех буферов традиционно выполняется каждые 30 с.

Следует признать, что кэширование операций записи на диск, в отличие от кэширования чтения, всегда создает определенную опасность потери данных. В случае случайного сбоя системы, отключения питания и т.п. может оказаться, что важная информация, которую следовало записать на диск, застряла в грязных буферах кэша и была поэтому потеряна.[3] Это неизбежная плата за значительное повышение производительности системы. Программы, требующие высокой надежности работы с данными (например, банковские программы), обычно записывают данные прямо на диск. При этом кэш либо не используется вообще, либо в кэш-буфер заносится копия данных, которая может пригодиться при последующих операциях чтения.

«Узким местом» кэширования дисков является поиск требуемого блока данных в кэше. Как было описано выше, для этого система просматривает заголовки буферов. Если кэш состоит из нескольких сотен буферов, время поиска будет ощутимо. Один из возможных приемов ускорения поиска, используемый в UNIX, показан на рис. 2‑4.

Рис. 2‑4

В UNIX каждый кэш-буфер может входить одновременно в два линейных списка. Один из них, называемый «списком свободных блоков», это знакомый нам LRU-список, используемый для определения блока, подлежащего вытеснению. Слово «свободный» не значит «пустой»; в данном случае это слово означает блок, не занятый в текущий момент в операции чтения/записи, выполняемой каким-нибудь процессом. Другой список называется «хеш-цепочкой» и используется для ускорения поиска нужного блока.

При записи в буфер данных, соответствующих некоторому блоку диска, номер хеш-цепочки, в которую будет помещен этот буфер, определяется как остаток от деления номера блока на N – количество хеш-цепочек. Для наглядности на рисунке принято значение N = 10. Таким образом, блоки с номерами 120, 40, 90 попадают в цепочку 0, блоки 91, 1, 71 – в цепочку 1 и т.д. Когда система ищет в кэше блок с определенным номером, она прежде всего по номеру блока определяет, в какой из хеш-цепочек этот блок должен находиться. Если блока нет в этой цепочке, то его вообще нет в кэше. Таким способом удается сократить поиск в лучшем случае в N раз (это если все цепочки окажутся одинаковой длины).

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

Еще одна особенность кэширования дисков в UNIX состоит в том, что при обнаружении в начале списка свободных блоков «грязных» буферов система запускает процессы их очистки, но не дожидается завершения этих процессов, а выбирает для вытеснения первый по списку чистый блок. После завершения очистки блоки возвращаются в начало списка свободных блоков, оставаясь первыми кандидатами на вытеснение.

Опережающее чтение.

В том случае, если обработка данных ведется последовательным образом (от начала файла к концу), кэширование не дает значительного эффекта. После того, как обработаны данные из одного блока, дальнейшее пребывание этого блока в кэш-буфере бесполезно. Значительно более полезной в этом случае может оказаться другая специальная форма буферизации, известная как опережающее чтение. Она заключается в том, что при обращении к некоторому блоку диска система, выполнив чтение требуемого блока, считывает затем еще несколько следующих за ним блоков. Если аппаратура позволяет выполнять операцию чтения одновременно с обработкой ранее прочитанных данных, то велика вероятность, что к моменту, когда следующий блок данных будет запрошен для обработки, этот блок уже окажется прочитанным.

Как правило, системе неизвестно, будет ли обработка файла вестись в режиме последовательного или произвольного доступа, поэтому часто используется та или иная комбинация кэширования с опережающим чтением. В Windows программа, открывающая файл, может указать системе, для какого способа доступа желательно оптимизировать механизм буферизации.

Идея опережающего чтения получила интересное развитие в Windows XP. В этой системе введен механизм опережающей загрузки данных (prefetch), который основан на автоматическом сборе и хранении статистики о том, какие файлы и каталоги используются в ходе загрузки ОС и при запуске конкретных приложений, а также какие данные читаются из этих файлов в первые минуты работы. При последующих загрузках ОС и запусках приложений система выполняет ожидаемые операции чтения еще до того, как они будут в действительности запрошены загружаемыми компонентами ОС или приложением. При этом система планирует порядок операций таким образом, чтобы сократить перемещения читающих головок и тем самым ускорить загрузку данных.

Драйверы устройств

Драйвер устройства – это системная программа, которая под управлением ОС выполняет все операции с конкретным периферийным устройством. Драйвер является как бы посредником между ОС и устройством. Перед драйверами стоят две одинаково важные, но трудно совместимые задачи:

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

· добиться максимально эффективного использования всех функциональных возможностей и особенностей конкретных устройств.

Возможность стандартными средствами работать с разными устройствами очень желательна с точки зрения архитектуры ОС и удобства программирования. Было бы крайне противно, если бы при написании прикладной программы нужно было заранее учитывать, какая модель принтера будет использоваться для выдачи результатов. Наоборот, в большинстве случаев прикладной программист даже не должен знать, будет ли это принтер или плоттер-графопостроитель, или же результаты будут отображаться на экране. Большие проблемы могли бы возникнуть и при замене одной модели принтера, диска, монитора на другую, если бы такая замена потребовала переписывать заново все программы, работающие с этим устройством. Другое дело, если все особенности устройства учитываются в одном-единственном месте, а именно – в драйвере этого устройства.

Разумеется, полностью скрыть все различия между устройствами невозможно. Никаким образом нельзя приравнять, скажем, диск к клавиатуре, и даже разные типы дисков похожи, но не совсем. Например, для дискет можно выполнить такую операцию, как проверка смены носителя (фактически при этом проверяется, открывался ли карман дисковода). Для жестких дисков эта операция не имеет смысла.

В большинстве ОС различаются, как минимум, два разных типа драйверов: для символьных и для блочных устройств.

Обращаясь к драйверу, ОС указывает функцию, которую требуется выполнить. Список этих функций общий для драйверов различных устройств, при этом каждый драйвер может реализовать только те функции, которые имеют смысл для данного устройства. Наиболее общими являются функции чтения данных, записи данных, инициализации устройства (эта функция вызывается системой один раз, сразу после загрузки), открытия и закрытия устройства (используются, когда символьное устройство открывается как файл). Для блочных устройств имеют смысл функции форматирования, поиска сектора. Для символьных устройств ввода – функция «неразрушающего ввода», т.е. проверки очередного символа без его изъятия из входного потока.

Для того, чтобы учесть все разнообразие возможных операций, в число функций драйвера вводят такую, как «выполнение специальных функций», и здесь уже для каждого устройства определен свой набор этих специальных функций.

Типичный драйвер устройства содержит, как минимум, три основных блока:

· заголовок драйвера;

· блок стратегии;

· блок прерываний.

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

В обязанность блока стратегии входит прием заявок на выполнение операции, ведение очереди заявок (в многозадачных системах, а также при асинхронных операциях, выполнения могут дожидаться несколько заявок), а также запуск операции и ее завершение.

Заявка на выполнение операции представляет собой стандартную запись, формируемую системой перед обращением к драйверу. Заявка содержит код требуемой функции драйвера и сведения об адресе данных в памяти и на устройстве, о количестве передаваемых данных. Заявка также содержит поле, в которое драйвер должен будет записать код завершения операции (обычно 0 – нормально выполненная операция, другие значения – коды ошибок).

Блок прерываний выполняет примерно тот алгоритм, который в п. 2.5.1 назывался вводом/выводом по прерываниям. Система вызывает этот блок, когда получает сигнал прерывания от устройства, обслуживаемого драйвером. Закончив выполнение заявки, блок прерываний возвращает управление блоку стратегии для завершения операции.

Помимо трех основных блоков, в разных ОС драйверы могут содержать, например, блок инициализации (он используется один раз при загрузке ОС, а затем может быть выгружен из памяти), блок изменения параметров драйвера и др.

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



Поделиться:


Последнее изменение этой страницы: 2017-02-05; просмотров: 274; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.218.55.14 (0.018 с.)