Искусство программирования для Unix
Шрифт:
7.2.1. Передача задач специализированным программам
В простейшей форме взаимодействия программ, которая возможна благодаря малозатратному созданию дочерних процессов, одна программа вызывает другую для решения специализированной задачи. Поскольку вызванная программа часто задается как команда оболочки Unix через вызов system(3), данная операция часто называется вызовом программы с созданием подоболочки (shelling out). Вызываемая программа наследует управление клавиатурой и пользовательским дисплеем и выполняется до завершения. Когда она прекращает свою работу, вызывающая
Классическим случаем вызова с созданием подоболочки в Unix является вызов редактора из программы чтения почты или новостей. Разработчик, придерживающийся традиций Unix, не встраивает специально созданный редактор в программу, которая требует обычного текстового ввода. Вместо этого программист предоставляет пользователю возможность указать предпочтительный редактор, который будет вызываться при необходимости.
Специализированная программа обычно сообщается с родительским процессом через файловую систему, считывая или модифицируя файл (или файлы) с определенным расположением. Так работают вызываемые редакторами и почтовыми агентами программы.
В распространенном варианте данной модели специализированная программа может получать данные на свой стандартный ввод и вызываться с помощью функции С-библиотеки рореп(. . . , "w") или как часть сценария оболочки. Или она может отправлять данные на свой стандартный вывод и вызываться с помощью функции рореп (. . ., "г") или как часть сценария оболочки. (Если программа считывает стандартный ввод и записывает данные в стандартный вывод, то она выполняет эти операции в пакетном режиме, завершая все операции чтения до записи каких-либо данных.) Данный вид дочерних процессов обычно не называют процессами подоболочки. Для их обозначения не существует стандартного термина, однако такие программы можно называть "прикрепляемыми".
Ключевым моментом во всех описанных случаях является то, что специализированные программы во время работы не обмениваются данными с родительскими. Они имеют связанный протокол только в том случае, когда какая-либо программа (главная или подчиненная), принимающая ввод от другой, должна быть способна осуществлять синтаксический анализ ввода.
7.2.1.1. Учебный пример: пользовательский почтовый агент mutt
Пользовательский почтовый агент mutt является современным представителем наиболее важной традиции проектирования программ для обработки электронной почты в Unix. Данная программа имеет простой экранный интерфейс с одноклавишными командами для просмотра и чтения почты.
Если mutt используется для создания почтовых сообщений (либо если данная программа вызвана с адресом в качестве аргумента командной строки или с помощью одной из команд для создания ответного сообщения), то программа определяет значение переменной EDITOR, а затем генерирует имя временного файла. Значение данной переменной используется как команда, а имя временного файла как ее аргумент''. Когда запущенная таким образом программа прекращает свою работу, mutt возобновляет управление, предполагая, что временный файл содержит необходимый текст сообщения.
Данное соглашение в Unix соблюдается почти во всех программах создания почтовых сообщений и сообщений в группах новостей. И вследствие этого программистам, реализующим такие программы, не требуется писать
Важным вариантом такой стратегии является вызов с созданием подоболочки небольшой программы-посредника, передающей специальное задание уже запущенному экземпляру большой программы, такой как редактор или Web-браузер. Поэтому разработчики, на Х-диснлеях которых обычно уже имеется запущенный экземпляр редактора emacs, могут установить переменную EDITOR*emacsclient и в случае необходимости редактировать сообщение в mutt открывать данные буфера в emacs. Целью данного подхода является не экономия памяти или других ресурсов, а предоставление пользователю возможности объединять все редактирование в одном emacs-процессе (поэтому, например, при вырезании и вставке между буферами можно было переносить внутренние параметры emacs, такие как выделение шрифта).
7.2.2. Каналы, перенаправление и фильтры
После Кена Томпсона и Денниса Ритчи одной из наиболее важных фигур в истории создания Unix был, вероятно, Дуг Макилрой. Созданная им конструкция канала (pipe) в той или иной форме влияла на конструкцию операционной системы Unix, поддерживая зарождающуюся в ней философию "хорЛыего решения одной задачи" и способствуя большинству более поздних форм IPC в Unix (в частности, абстракции сокетов, применяемой для поддержки сетей).
Работа каналов определяется соглашением, согласно которому каждая программа изначально имеет доступные ей (по крайней мере) два ^Ьтока данных ввода-вывода: стандартный ввод и стандартный вывод (числовые дескрипторы файлов 0 и 1 соответственно). Многие программы могут быть написаны в виде фильтров (filters), которые последовательно считывают данные со стандартного ввода и записывают только в стандартный вывод.
Обычно такие потоки подключены к пользовательской клавиатуре и дисплею соответственно. Однако оболочки в операционной системе Unix обеспечивают универсальную поддержку операций перенаправления (redirection), которые подключают стандартный ввод и стандартный вывод к файлам. Поэтому команда
Is >foo
отправляет вывод команды ls(1) в файл с именем "foo". С другой стороны, команда
wc <foo
вынуждает утилиту для подсчета слов wc(1) принять на свой стандартный ввод данные из файла "foo" и отправить сведения о количестве символов/слов/строк на стандартный вывод.
Канал подключает стандартный вывод одной программы к стандартному вводу другой. Цепочка программ, соединенных таким способом, называется конвейером (pipeline). Команда
Is I wc
позволяет получить количество символов/слов/строк в списке файлов текущего каталога. (В данном случае, вероятно, действительно полезным будет только количество строк.)
Одним излюбленным конвейером был "be | speak" — "говорящий" калькулятор. Он "знал" названия чисел до вигинтеллиона (1063)
Дуг Макилрой.
Важно отметить, что все этапы конвейера работают одновременно. Каждый этап ожидает ввода на выходе из предыдущего этапа, но ни один этап не должен завершить работу до того, как следующий получит возможность запуститься. Важность этого свойства отмечена далее при рассмотрении интерактивного использования таких конвейеров как, например, отправка длинного вывода какой-либо команды утилите тоге( 1).