Искусство программирования для Unix
Шрифт:
В операционной системе Linux предусмотрена функция уведомления об изменениях файлов и каталогов [151] , и эти функции скопированы в некоторых версиях BSD, однако они еще не перенесены на другие Unix системы.
20.3.5. Конструкция системы управления задачами была плохо реализована
Не считая возможности приостанавливать процессы (что само по себе является тривиальным дополнением к планировщику, который мог бы быть сделан довольно безопасно), управление задачами предусмотрено для переключения терминала между несколькими процессами. К сожалению, при этом решается простейшая часть проблемы — определение нажатия клавиши. Вместе с тем сложные части, такие как сохранение и восстановление состояния экрана, передаются приложению.
151
Ищите
Действительно хорошая реализация данного средства была бы полностью невидимой для пользовательских процессов: без выделенных сигналов, без необходимости сохранять и восстанавливать режимы терминалов, без необходимости для приложений перерисовывать экран в случайные промежутки времени. Моделью должна быть виртуальная клавиатура, которая иногда подключается к реальной (и блокирует ее, если пользователь запрашивает ввод, когда она не подключена), а также виртуальный экран, который время от времени становится видимым на реальном экране (и может блокировать, а может не блокировать вывод, когда он невидимый), с системой, выполняющей мультиплексирование подобно мультиплексированию доступа к диску, процессору и другим ресурсам, но не влияющей на пользовательские программы в целом [152] .
152
Данный параграф основывается на аналитической статье Генри Спенсера, вышедшей в 1984 году. Он отметил, что управление задачами было необходимо и целесообразно точно учесть в POSIX.1 и последующих стандартах Unix, поскольку оно "просачивается" в каждую программу и, следовательно, должно быть продумано в любом интерфейсе "приложение-система". Отсюда и одобрение POSIX ошибочной конструкции, когда правильные решения "выходили за рамки", а следовательно, даже не рассматривались.
При правильной реализации потребовалось бы, чтобы tty-драйвер Unix не просто поддерживал буфер линии, но и полностью отслеживал текущее состояние экрана. Кроме того, потребовалось бы, чтобы сведения о типах терминалов были известны на уровне ядра (возможно, с помощью процесса демона), для того чтобы оно могло соответствующим образом выполнить восстановление, когда приостановленный процесс снова переводится в приоритетный режим. Последствия неправильной реализации заключаются в том, что ядро не способно отключить сеанс как задачу xterm или Emacs от одного терминала и подключить его к другому терминалу (тип которого мог бы отличаться).
Поскольку использование Unix сместилось в сторону X-дисплеев и эмуляторов терминалов, управление задачами стало сравнительно менее важным и этот вопрос уже не имеет прежней остроты. Однако удручает тот факт, что до сих пор не существует функции приостановления/подключения/отключения. Данная функция могла бы быть полезной для сохранения состояния терминальных сеансов между сеансами регистрации в системе.
Широко распространенная программа с открытым исходным кодом, которая называется screen(1), решает некоторые из этих проблем [153] . Однако поскольку пользователь должен ее вызвать явно, не гарантируется, что ее возможности будут присутствовать в каждом терминальном сеансе. Кроме того, код уровня ядра, который перекрывает ее в функциональной части, не был удален.
153
Web-страница проекта screen(1) — http://www.math.fu-berlin.de/~guckes/screen/.
20.3.6. В Unix API не используются исключительные ситуации
Язык С испытывает недостаток средств восстановления для обработки именованных исключительных ситуаций со связанными данными [154] . Таким образом, C-функции в Unix API сообщают об ошибках путем возвращения известного значения (обычно -1 или указатель на NULL-символ) и установки глобальной переменной errno.
Оглядываясь назад, становится ясно, что это является источником многих неочевидных ошибок. Программисты в спешке часто пренебрегают проверкой возвращаемых значений. Так как исключительные ситуации не обрабатываются, нарушается правило исправности; процесс выполнения программы
154
Для непрограммистов: обработка исключительных ситуаций — способ, с помощью которого программа прерывается в середине процедуры. Это не совсем то же, что выход, поскольку такой останов может быть обработан кодом ловушки во включающей его процедуре. Исключительные ситуации обычно используются для сигнализации об ошибках или неожиданных обстоятельствах, которые означают, что продолжение обычной работы нецелесообразно.
Отсутствие исключений также означает, что некоторые задачи, которые должны были бы быть простыми идиомами — как выход из обработчика сигналов по версии с сигналами Беркли-стиля — должны осуществляться с помощью сложного кода, который является причиной проблем переносимости и чреват ошибками.
Данная проблема может быть скрыта (и обычно скрывается) привязками Unix API в таких языках, как Python или Java, в которых есть исключительные ситуации.
Недостаток исключений фактически является индикатором проблемы с последующими более крупными последствиями. Слабая онтология типов в С делает проблематичным обмен данными между реализованными на нем языками более высокого уровня. Например, большинство современных языков имеют списки и словари как первичные типы данных. Однако, поскольку они не имеют канонического представления в С, попытки передавать списки между (например) Perl и Python являются неестественными и требуют большого количества связующего кода.
Существуют технологии, такие как CORBA, которые решают более крупную проблему, но они тяжеловесны и склонны задействовать большое количество преобразований во время выполнения.
20.3.7. Вызовы ioctl(2) и fcntl(2) являются препятствиями
Механизмы ioctl(2) и fcntl(2) обеспечивают способ написания перехватчиков (hooks) в драйверах устройств. Первоначальным историческим использованием ioctl(2) была установка параметров, таких как скорость передачи и количество фреймирующих битов в драйверах последовательных линий, отсюда и название ("I/O control"). Позднее вызовы ioctl были добавлены для других драйверов, a fcntl(2) был добавлен как перехватчик в файловую систему.
С годами вызовы
Основная проблема в данном случае та же, что и "большой блок байтов"; объектная модель Unix слабая и не оставляет естественного пространства для размещения многих вспомогательных операций. Разработчики получают сложный выбор из неудовлетворительных альтернатив. Вызовы
Пока не ясно, улучшится ли в будущем объектная модель Unix, а если улучшится, то каким образом. Если MacOS-подобные атрибуты файлов станут обычной функцией Unix, подстройка "магических" именованных атрибутов на драйверах устройств может взять на себя роль, которую в настоящее время играют вызовы
20.3.8. Модель безопасности Unix, возможно, слишком примитивна
Возможно, полномочия пользователя root слишком широки, и в Unix должны быть возможности более четкой градации полномочий или ACL (Access Control Lists — списки контроля доступа) для функций системного администрирования, чем один суперпользователь, который может все. Люди, которые принимают данную позицию, спорят, что многие системные программы имеют постоянные root- привилегии благодаря механизму setuid. Если даже одну из них можно будет взломать, то вторжения последуют везде.