Добавлен: 29.10.2018
Просмотров: 47978
Скачиваний: 190
12.1. Природа проблемы проектирования
1061
продукт, который ни один из разработчиков не может даже надеяться понять целиком,
неудивительно, что результаты часто оказываются далеки от оптимальных.
Операционные системы не являются самыми сложными системами в мире. Авианосцы,
например, представляют собой значительно более сложные системы, но они намного
легче разделяются на отдельные подсистемы. Проектировщики туалетов на авианосце
не должны заниматься радарной системой. Эти две подсистемы почти не взаимодей-
ствуют. История не знает случаев, когда забитый туалет на авианосце служил причиной
запуска ракет. В операционной же системе файловая система часто взаимодействует
с системой памяти самым неожиданным и непредсказуемым образом.
Во-вторых, операционные системы должны учитывать параллелизм. В системе од-
новременно присутствует множество пользователей, работающих с множеством
устройств ввода-вывода. Управление несколькими параллельно выполняющимися
процессами существенно сложнее управления одной последовательной деятельно-
стью. Среди множества возникающих при этом проблем достаточно назвать хотя бы
состязания и тупиковые ситуации.
В-третьих, операционные системы должны учитывать наличие потенциально враж-
дебных пользователей, желающих вмешиваться в работу операционной системы или
выполнять запрещенные действия, например похищение чужих файлов. Операцион-
ная система должна предпринимать меры для предотвращения подобных действий со
стороны злонамеренных пользователей. Текстовые процессоры и фоторедакторы не
сталкиваются с подобными проблемами.
В-четвертых, несмотря на тот факт, что пользователи друг другу не доверяют, многим
из них бывает нужно использовать какую-либо информацию или ресурсы совместно
с определенной группой пользователей. Операционная система должна предоставлять
эту возможность, но таким образом, чтобы злоумышленник не смог воспользоваться
этими свойствами для своих целей. У прикладных программ подобных проблем тоже
нет.
В-пятых, операционные системы, как правило, живут довольно долго. Операционная
система UNIX используется уже более 40 лет, система Windows используется около
30 лет и не подает признаков того, что собирается уходить в историю. Соответственно
проектировщики операционной системы должны думать о том, как могут измениться
аппаратура и приложения в отдаленном будущем и как им следует к этому подго-
товиться. Системы, отражающие одно специфическое мировоззрение, как правило,
быстро сходят со сцены.
В-шестых, у разработчиков операционной системы на самом деле нет четкого пред-
ставления о том, как будет использоваться их система, поэтому они должны обеспечить
достаточную степень универсальности. При проектировании таких систем, как UNIX
или Windows, их использование для работы с веб-браузером или с потоковым видео
высокого разрешения не предполагалось, однако многие современные компьютеры
в основном только для этого и используются. Трудно себе представить проектиров-
щика морского судна, который не знал бы, что он проектирует: рыболовецкое судно,
пассажирское судно или военный корабль.
В-седьмых, от современных операционных систем, как правило, требуется переноси-
мость, что означает возможность работы на различных платформах. Они также должны
поддерживать сотни или даже тысячи устройств ввода-вывода, которые проектируются
совершенно независимо друг от друга. Например, операционная система должна рабо-
1062
Глава 12. Разработка операционных систем
тать на компьютерах как с прямым, так и с обратным порядком байтов. Второй пример
постоянно наблюдался в системе MS-DOS, когда пользователи пытались установить
звуковую карту и модем, использующие одни и те же порты ввода-вывода или линии
запроса прерывания. С такими проблемами, как конфликты различных частей аппара-
туры, приходится иметь дело в основном именно операционным системам.
Наконец, в-восьмых, при разработке операционных систем часто учитывается не-
обходимость совместимости с предыдущей версией операционной системы. Система
может иметь множество ограничений на длину слов, имена файлов и т. д., рассматри-
ваемых теперь проектировщиками как устаревшие, но от которых трудно избавиться.
Это напоминает переоборудование автомобильного завода под выпуск новых моделей
с условием сохранения существующих мощностей по выпуску старых моделей.
12.2. Разработка интерфейса
Итак, теперь должно быть ясно, что написание современной операционной системы
представляет собой непростую задачу. Но с чего начинается эта работа? Возможно,
лучше всего сначала подумать о предоставляемых операционной системой интер-
фейсах. Операционная система предоставляет набор служб, большую часть типов
данных (например, файлы) и множество операций с ними (например, read). Вместе
они формируют интерфейс пользователей системы. Обратите внимание на то, что
в данном контексте пользователями операционной системы являются программисты,
пишущие программы, которые используют системные вызовы, а не люди, запускающие
прикладные программы.
Кроме основного интерфейса системных вызовов у большинства операционных
систем есть дополнительные интерфейсы. Например, некоторым программистам не-
обходимо бывает написать драйвер устройства, чтобы добавить его в операционную
систему. Эти драйверы предоставляют определенные функции и могут обращаться
к определенным системным вызовам. Функции и вызовы определяют интерфейс, су-
щественно отличающийся от используемого прикладными программистами. Все эти
интерфейсы должны быть тщательно спроектированы, если разработчики системы
рассчитывают на успех.
12.2.1. Руководящие принципы
Существуют ли принципы , руководствуясь которыми можно проектировать интерфей-
сы? Мы надеемся, что такие принципы есть. Если выразить их в нескольких словах, то
это простота, полнота и возможность эффективной реализации.
Принцип 1: простота
Простой интерфейс легче понять и реализовать без ошибок. Всем разработчикам
систем следует помнить эту знаменитую цитату французского летчика и писателя
Антуана де Сент-Экзюпери: «Совершенство достигается не тогда, когда уже больше
нечего добавить, а когда больше нечего убавить».
Этот принцип утверждает, что лучше меньше, чем больше, по крайней мере, при-
менительно к операционным системам. Другими словами, этот принцип может быть
12.2. Разработка интерфейса
1063
выражен аббревиатурой KISS (Keep It Simple, Stupid), предлагающей программисту,
в чьих мыслительных способностях возникают сомнения, не усложнять систему.
Принцип 2: полнота
Разумеется, интерфейс должен предоставлять пользователям возможность выполнять
все, что им необходимо, то есть интерфейс должен обладать полнотой. В связи с этим на
ум приходит другая цитата, на этот раз это фраза, сказанная Альбертом Эйнштейном:
«Все должно быть простым, насколько это возможно, но не проще».
Другими словами, операционная система должна выполнять то, что от нее требуется,
но не более того. Если пользователю нужно хранить данные, операционная система
должна предоставлять для этого некий механизм. Если пользователям необходимо об-
щаться друг с другом, операционная система должна предоставлять механизм общения
и т. д. В своей речи по поводу получения награды Turing Award один из разработчиков
систем CTSS и MULTICS Фернандо Корбато объединил понятия простоты и полноты
и сказал: «Во-первых, важно подчеркнуть значение простоты и элегантности, так как
сложность приводит к нагромождению противоречий и, как мы уже видели, появлению
ошибок. Я бы определил элегантность как достижение заданной функциональности при
помощи минимума механизма и максимума ясности».
Ключевая идея здесь — минимум механизма. Другими словами, каждая функция,
каждый системный вызов должны нести собственную ношу. Когда член команды про-
ектировщиков предлагает расширить системный вызов или добавить новую функцию,
остальные разработчики должны спросить его: «Произойдет ли что-нибудь ужасное,
если мы этого не сделаем?». Если ответом будет: «Нет, но, возможно, когда-нибудь
кто-то посчитает эту функцию полезной», — поместите ее в библиотеку уровня пользо-
вателя, а не в операционную систему, даже если при этом она будет работать медленнее.
Не должна ставиться задача сделать каждую функцию быстрее пули. Цель заключается
в том, чтобы сохранить то, что Корбато назвал минимумом механизма.
Давайте кратко рассмотрим два примера из моего личного опыта: операционные си-
стемы MINIX и Amoeba. Для всех задач в системе MINIX до недавнего времени было
три системных вызова: send, receive и sendrec. Система структурирована в виде набора
процессов с менеджером памяти, файловой системой и каждым драйвером устройства,
представляющим собой отдельный планируемый процесс. В первом приближении все,
что делает ядро, — это планирование процессов и управление передачей сообщений
между ними. Соответственно необходимыми являются только два системных вызова:
send для отправки сообщения и receive, чтобы сообщение получить. Третий системный
вызов, sendrec, представляет собой просто оптимизацию, позволяющую послать со-
общение и запросить ответ всего за одно эмулированное прерывание. Все остальное
в системе выполняется с помощью обращения к какому-либо другому процессу (на-
пример, к процессу файловой системы или к дисковому драйверу). В самой последней
версии MINIX добавлены два дополнительных вызова, и оба для асинхронного обмена
данными. Вызов senda отправляет асинхронное сообщение. Ядро будет стремиться до-
ставить сообщение, но приложение его не ждет, оно продолжает выполнение. Кроме
этого, система для доставки кратких уведомлений использует вызов notify. Напри-
мер, ядро может уведомить драйвер устройства в пользовательском пространстве
о том, что произошло что-то, во многом похожее на прерывание. С уведомлением не
связано никакое сообщение. Когда ядро доставляет уведомление процессу, оно всего
1064
Глава 12. Разработка операционных систем
лишь изменяет состояние бита на имеющейся у каждого процесса битовой матрице на
противоположное, показывая: что-то случилось. Из-за предельной простоты уведом-
ление может осуществляться довольно быстро, и ядро не должно беспокоиться о том,
какое сообщение нужно доставить, если процесс получил одно и то же уведомление
дважды. Стоит отметить, что нынешнее все еще небольшое количество вызовов воз-
растает. Данный процесс неизбежен, и сопротивление бесполезно.
Разумеется, это всего лишь вызовы ядра. Запуск над ядром POSIX-совместимой систе-
мы требует реализации большого количество системных вызовов POSIX. Но красота
всего этого заключается в том, что все они отображаются на совсем небольшой набор
вызовов ядра. Имея (пока) такую простую систему, у нас есть шанс, что она такой
и останется.
Операционная система Amoeba устроена еще проще. У нее есть только один системный
вызов: выполнение вызова удаленной процедуры. Этот вызов отправляет сообщение
и ждет ответа. По существу, это то же самое, что и системный вызов sendrec в системе
MINIX. Все остальное строится на этом единственном вызове. Как бы то ни было,
но синхронный обмен данными вызывает другой вопрос, который будет рассмотрен
в разделе 12.3.
Принцип 3: эффективность
Третий руководящий принцип представляет собой эффективность реализации. Если
какая-либо функция (или системный вызов) не может быть реализована эффективно,
вероятно, ее не следует реализовывать. Программист должен интуитивно представлять
стоимость реализации и использования каждого системного вызова. Например, про-
граммисты, пишущие программы в системе UNIX, считают, что системный вызов lseek
дешевле системного вызова read, так как первый системный вызов просто изменяет
содержимое указателя, хранящегося в памяти, тогда как второй выполняет операцию
дискового ввода-вывода. Если интуиция подводит программиста, программист создает
неэффективные программы.
12.2.2. Парадигмы
Когда цели установлены, можно начинать проектирование. Можно начать, например,
со следующего: подумать, какой предстанет система перед пользователями. Один из
наиболее важных вопросов заключается в том, чтобы все функции системы хорошо
согласовывались друг с другом и обладали тем, что часто называют архитектурной
согласованностью
. При этом важно различать два типа пользователей операционной
системы. С одной стороны, существуют пользователи, взаимодействующие с при-
кладными программами, с другой — есть программисты, пишущие эти прикладные
программы. Первые большей частью имеют дело с графическим интерфейсом пользо-
вателя, тогда как последние в основном взаимодействуют с интерфейсом системных
вызовов. Если задача заключается в том, чтобы иметь единый графический интерфейс
пользователя, заполняющий всю систему, как, например, в системе Macintosh, то разра-
ботку следует начать отсюда. Если же цель состоит в том, чтобы обеспечить поддержку
различных возможных графических интерфейсов пользователя как в системе UNIX,
то в первую очередь должен быть разработан интерфейс системных вызовов. Начало
разработки системы с графического интерфейса пользователя представляет собой, по
12.2. Разработка интерфейса
1065
сути, проектирование сверху вниз. Вопрос заключается в том, какие функции будет
этот интерфейс иметь, как будет пользователь с ними взаимодействовать и как следует
спроектировать систему для их поддержки. Например, если большинство программ
отображает на экране значки, а затем ждет, когда пользователь щелкнет на них мышью,
это предполагает использование управляемой событиями модели для графического
интерфейса пользователя и, возможно, для операционной системы. В то же время если
экран в основном заполнен текстовыми окнами, то, вероятно, лучшей представляется
модель, в которой процессы считывают символы с клавиатуры.
Реализация в первую очередь интерфейса системных вызовов представляет собой
проектирование снизу вверх. Здесь вопросы заключаются в том, какие функции нуж-
ны программистам. В действительности для поддержки графического интерфейса
пользователя требуется не так уж много специальных функций. Например, оконная
система под названием X Windows, используемая в UNIX, представляет собой просто
большую программу на языке C, которая обращается к клавиатуре, мыши и экрану
с системными вызовами read и write. Оконная система X Windows была разработана
значительно позже операционной системы UNIX, но для ее работы не потребовалось
большого количества изменений в операционной системе. Это подтверждает тот факт,
что система UNIX обладает полнотой в достаточной степени.
Парадигмы интерфейса пользователя
Как для интерфейса уровня графического интерфейса пользователя , так и для интер-
фейса системных вызовов наиболее важный аспект заключается в наличии хорошей
парадигмы (иногда называемой метафорой или модельным представлением), обе-
спечивающей наглядный зрительный образ интерфейса. Многими графическими
интерфейсами пользователя используется парадигма WIMP (Windows, Icons, Menus,
Pointers — окна, пиктограммы, меню, указатели), обсуждавшаяся в главе 5. Эта пара-
дигма используется в интерфейсе для обеспечения согласованности таких идиом, как
щелчок и двойной щелчок мышью, перетаскивание и т. д. Часто к программам предъ-
являются дополнительные требования, такие как наличие строки меню с пунктами
Файл
,
Правка
и т. д., каждый из которых содержит хорошо знакомые пункты меню.
Таким образом, пользователи, знакомые с одной программой, легко могут освоить
другую программу.
Однако пользовательский интерфейс WIMP не является единственно возможным.
Планшетные компьютеры, смартфоны и некоторые ноутбуки используют сенсор-
ные экраны, позволяющие пользователям взаимодействовать с устройством бо-
лее непосредственным и интуитивно понятным образом. На некоторых карманных
компьютерах используется стилизованный интерфейс рукописного ввода. В специ-
ализированных мультимедийных устройствах может использоваться интерфейс
видеомагнитофона. И конечно же, совершенно иная парадигма используется при
голосовом вводе. Самое важное относится не столько к выбору парадигмы, сколько
к факту наличия единственной, самой важной парадигмы, объединяющей весь поль-
зовательский интерфейс.
Какая бы парадигма ни была выбрана, важно, чтобы все прикладные программы
использовали ее. Следовательно, проектировщики системы должны предоставить
разработчикам прикладных программ библиотеки и инструменты для доступа к про-
цедурам, обеспечивающим однородный внешний вид пользовательского интерфейса.