S– это состояние процесса. Запущенный процесс может находиться в одном из четырех стандартных состояний: «R» (выполняемый), «S» (ожидающий ввода-вывода), «T» (приостановленный – приостановку процессов мы обсудим ниже), «Z» («зомбированный», уже завершенный, но не успевший сообщить об этом процессу-родителю).
   Итак, в примере на Рис. 1-46 мы видим:
   находящийся в корне дерева процесс «init»;
   два порожденных им процесса, не имеющих управляющего терминала: демон управления системой энергосбережения «apmd» 40и демон периодического исполнения заданий «crond»;
   порожденный процессом init процесс «login» с tty1 в качестве управляющего терминала;
   порожденный этим процессом «login» процесс «bash» – экземпляр оболочки также с tty1 в качестве управляющего терминала;
   порожденный процессом «bash» процесс «xinit» (это сценарий, запускающий компоненты графической среды);
   порожденные процессом «xinit» процессы «X» (это сервер оконной системы X, он запущен в качестве демона, т.е. без управляющего терминала) и «blackbox» (это менеджер окон графической среды);
   порожденные процессом «blackbox» процессы «soffice.bin» (это словарный процессор «OpenWriter», в котором набирается данный текст), «mozilla-bin» (браузер «Мозилла»), «xterm» (эмулятор текстового терминала). Они не имеют управляющего терминала;
   порожденный эмулятором терминала процесс оболочки «bash» с псевдотерминалом pts/0, назначенным при запуске эмулятора терминала, в качестве управляющего;
   порожденный этой оболочкой процесс «ps», который и осуществил приведенный в примере вывод;
   порожденный процессом «init» процесс «login» и порожденный им процесс «bash» на терминале tty2.
    Управление заданиями и сигнализация процессов
   В среде стандартной оболочки и команд открытой ОС запустить бесконечный процесс можно, введя команду «( while : ; do : ; done )», запускающую бесконечный цикл в подчиненном экземпляре оболочки (Рис. 1-47).
   Пока не нужно беспокоиться о понятности синтаксиса управляющих конструкций.
Рис. 1-47
   Если Алиса все сделала правильно, то сценарий сам по себе уже не остановится никогда (скорее всего, до разгрузки системы). Приглашения оболочки Алиса тоже уже не получит, поэтому даже не сможет выйти из системы.
   Справиться с этой ситуацией ей поможет клавиатурная комбинация Control-C. Как и комбинация Control-D, она не отображается на экране, но после ее нажатия Алиса получает приглашение оболочки и при помощи команды «ps» убеждается, что никаких процессов, кроме самой оболочки и «ps», под этой оболочкой не выполняется.
Рис. 1-48
   Клавиатурная комбинация Control-C побуждает драйвер терминала отправить сигнал нормального завершения выполняемому процессу (в данном случае, подчиненной оболочке).
   Клавиатурная комбинация Control-Z побуждает драйвер терминала отправить выполняемому процессу другой сигнал – приостановки 41.
Рис. 1-49
   После нажатия Control-Z оболочка выдает сообщение, состоящее из числа в квадратных скобках, слова «остановлен» («stopped» в стандартной локали) и введенной ранее команды (Рис. 1-49).
   Число в квадратных скобках – это номер задания. Заданием является любая начавшая выполняться простая команда.
   Состояние соответствующего процесса (колонка «S» в выводе «ps -l» (эль)) обозначено буквой «T», означающей, что процесс остановлен. Задание, соответствующее такому процессу, также называется остановленным.
    Возобновитьисполнение задания можно двумя способами. Команда «fg» возобновляет выполнение задания на переднем плане, а команда «bg» – на заднем плане(или в фоновом режиме) 42. Заданием переднего плана называется задание, завершения ведущего процесса(первого процесса, запущенного подачей команды) которого ожидает оболочка перед выводом очередного приглашения, и которое может свободно выводить данные на управляющий терминал и вводить их с терминала.
   В любой момент времени на переднем плане каждого управляющего терминала находится не более одного задания. Заданий заднего плана может быть неограниченное 43количество.
Рис. 1-50
   В примере на Рис. 1-50 Алиса запускает сценарий «цикл», останавливает его нажатием Control-Z, затем возобновляет его выполнение на переднем плане командой «fg», снова останавливает, и затем возобновляет на заднем плане командой «bg». После этого Алиса сразу получает приглашение и, введя команду «ps -l», видит соответствующий выполнению сценария процесс «bash» (PID 2765) с состоянием «R» («выполняемый»).
   Нажатие клавиатурных комбинаций Control-C и Control-Z всегда вызывает передачу сигнала заданию переднего плана. Заданию заднего плана передавать сигнал можно только явно, для чего служит команда «kill». Указание в качестве ее единственного аргумента идентификатора процесса приводит к тому, что процессу передается сигнал «нормально завершиться» (это соответствует нажатию комбинации Control-C для задания переднего плана) (Рис. 1-51).
Рис. 1-51
   Подача команды «kill» с ключом «-s» и идентификатором сигналав качестве параметра этого ключа позволяет подать процессу произвольный сигнал. Стандартом определены восемь сигналов, перечисленных в таблице на Рис. 1-52.
Рис. 1-52
   Реализация может предусматривать большее их количество 44. Практически во всех системах реализован сигнал SIGSTOP, его отправка процессу переднего плана большинством современных оболочек осуществляется нажатием Control-Z, как описано выше.
   На пользовательском уровне применяются обычно сигналы SIGTERM и SIGKILL. Отличие их в том, что при получении первого из них процесс по возможности завершается «чисто»: сбрасывает содержимое внутренних буферов в файлы и закрывает их, а второго – завершается немедленно. Второй используется обычно для «убиения» процесса, выполняющего ошибочную программу.
   До сих пор Алиса отправляла сигналы по собственной инициативе. Отправляющим процессом выступали оболочка (при передаче сигналов процессу переднего плана нажатием клавиш) или команда «kill». Но передача сигналов (межпроцессная коммуникация) может осуществляться между любыми процессами, и является широко используемым в системном и прикладном программировании механизмом ОС. Как и при доступе к файлам, при доступе к процессам ОС руководствуется системой распределения полномочий. Стандартное поведение проще, чем в случае с файлами: процесс, запущенный обычным пользователем (не главным пользователем), может сигнализировать процессу, запущенному тем же пользователем, и не может сигнализировать процессу, которым «владеет» другой пользователь. В примере на Рис. 1-53 попытка Алисы «убить» процесс, принадлежащий другому пользователю, привела к сообщению об ошибке.
Рис. 1-53
   Реализацией может быть определено более сложное поведение 45.
   Программа может переопределить смысл сигналов, которые получает процесс (в частности, отменить завершение процесса), за исключением сигнала SIGKILL. Все стандартные команды ОС обрабатывают сигналы стандартным образом 46.
   Если вам не удается завершить запущенный вами процесс подачей команды «kill -s SIGKILL», значит, в системе возникли оченьсерьезные неполадки.
   Вы также можете столкнуться с ситуацией, когда программа переопределяет SIGINT, SIGSTOP и входит в бесконечный цикл (или ожидает события, наступления которого в обозримом будущем не предвидится). Запустив такую программу на переднем плане, вы не сможете завершить ее нажатием Control-C или приостановить нажатием Control-Z, а подать SIGKILL командой «kill» также будет невозможно, поскольку оболочка ожидает завершения процесса переднего плана.
   Простого выхода из этой ситуации нет, но обычно можно зарегистрироваться на другом терминале (включая виртуальную консоль) и «убить» хитрый процесс командой «kill -s SIGKILL». Если таким образом «завис» сеанс в окне виртуального терминала, его обычно можно «убить» средствами оконного менеджера (закрыв окно). Наконец, если вы работаете на последовательном терминале, можно попытаться выключить и снова включить его. Оболочка по выключении получит сигнал SIGHUP и «убьет» подчиненные себе процессы.
    Сложные команды и задания
   Чтобы запустить задание на заднем плане, не обязательно запускать его на переднем плане, приостанавливать и возобновлять командой «bg». Можно воспользоваться символом завершениякоманды «&» (читается «амперсенд») (Рис. 1-54).
Рис. 1-54
   Использование символа завершения «&» позволяет ввести в одной строке более одной команды (и, соответственно, запустить более одного задания), связав их этим символом (Рис. 1-55). Список заданий можно получить командой «jobs».
Рис. 1-55
   Знак «+» после номера задания обозначает «текущее» задание, то есть задание, которым можно управлять командами «bg» и «fg» без аргументов. Знаком «-» помечено «предыдущее» задание (которое станет текущим по завершению текущего). При переводе задания переднего плана на задний или запуске нового задания текущее задание (если оно есть) становится «предыдущим», а вновь запущенное – текущим.
   Если нужно возобновить исполнение задания на переднем или заднем плане, или перевести задание на передний план, можно воспользоваться командами «fg» и «bg», задав им аргумент, состоящий из «%» («процент»), и следующим за ним слитно номером задания (Рис. 1-56).
Рис. 1-56
   Другим символом завершения команды, также позволяющим подать более одной команды и инициировать более одного задания одной строкой является «;» (точка с запятой). Команда, завершенная этим символом, будет выполняться на переднем плане, а последующие команды (задания) будут выполняться после ее завершения.
   При подаче сложных команд (команд, включающих в себя более одной команды) их можно группировать с использованием круглых скобок. Сгруппированные скобками команды (вне зависимости от использованного символа завершения) образуют группу команд 47, выполняемых как одно задание (Рис. 1-57).
Рис. 1-57
   Кроме того, группировка бывает полезна, чтобы передать значение переменной сразу всем командам, или перенаправить ввод и/или вывод всех команд.
   Механизм управления заданиями (являющийся позднейшей добавкой к концепции открытых систем) чрезвычайно полезен при выполнении сложных работ с алфавитно-цифрового терминала. Сегодня операторы предпочитают при возможности запускать разные программы в разных окнах виртуальных терминалов в графической среде. Тем не менее, полезно хотя бы в общих чертах представлять, что это такое.
   Существуют также символы завершения команды «&&» и «||». Их действие связано с понятием кода завершения, возвращаемого каждой командой. Код завершения определяется программой, но обычно успешно выполнившаяся команда возвращает код «0» (ноль), а выполнившаяся с ошибкой – числовое значение кода ошибки. Явную работу с кодами завершения мы обсудим при введении элементов программирования оболочки, а здесь лишь упомянем, что символ завершения «&&» означает, что заданную за ним команду следует выполнить только в случае, если указанная перед ним команда выполнилась успешно, а символ «||» – наоборот, что «правую» команду следует выполнить только при ошибочном завершении «левой» (Рис. 1-58).
Рис. 1-58

1.7 Переменные

   До сих пор мы имели дело с параметрами, передававшимися команде в виде аргументов, следующих за именем команды. Команда интерпретирует аргументы исходя их их значений (так, большинство команд считает аргумент, начинающийся с дефиса, ключом) и их позиции (так, команды «cp» и «mv» последний операнд считают целевым файлом или каталогом, а предшествующие – источниками), поэтому аргументы называют еще позиционными параметрами.
   В открытых системах существует еще один механизм передачи параметров – переменные. В отличие от аргументов, переменные являются именованными параметрамии их семантика определяется не их позицией и значением, но именем.
Рис. 1-59
   В примере на Рис. 1-59 Алиса сначала подает команду «ls» с несуществующим файлом в качестве аргумента и получает сообщение об ошибке на русском языке. Затем она подает ту же команду, предварив ее конструкцией «LC_ALL=C», и получает сообщение о той же ошибке на английском языке.
   Конструкция, состоящая из имени переменнойи ее значения, разделенных знаком равенства («=») без промежутков, и является определением параметра-переменной для вызываемой команды. В данном случае определяется переменная «LC_ALL», которой присваивается значение «C». Переменная «LC_ALL» является одной из стандартных, ее значение определяет язык и другие национально-специфические особенности интерфейса (эту и несколько других переменных локалимы подробнее рассмотрим ниже).
   Передача переменной команде таким способом не оказывает никакого влияния на поведение последующих команд. Также она не оказывает влияния на поведение оболочки.
   Если нам нужно изменить подобным образом (получать сообщения на английском языке) самой оболочки, следует установить значение переменной такой же конструкцией из имени и значения переменной, разделенных знаком равенства, за которыми не следует никакой команды. Присвоение значения переменной командой само по себе не влияет на поведение вызываемых из оболочки команд, то есть эта переменная им не передается(Рис. 1-60).
Рис. 1-60
   Чтобы значение переменной передавалась всем вызываемым командам, ее следует сделать передаваемой ( экспортироватьее) командой «export» с именем переменной в качестве аргумента (см. Рис. 1-61).
Рис. 1-61
   Установленное значение (вне зависимости от того, экспортировано ли оно) сохраняется до конца сеанса работы с оболочкой, до его переустановки или до уничтожения переменной командой «unset» с именем переменной в качестве аргумента.
   Смысл механизма переменных как минимум двояк. Во-первых, крайне удобна возможность единообразного изменения поведения определенной группы команд, команд, поданных в течение определенного сеанса, или команд в сеансах определенного пользователя (механизм ключей для этого, очевидно, слишком громоздок). Во-вторых, язык оболочки – не только язык интерактивного взаимодействия с системой, но и императивный язык программирования.
   Установка значения переменной вполне соответствует оператору присвоения в большинстве интерпретируемых императивных языков программирования. Переменные в оболочке всегда имеют строчный тип, хотя семантика некоторых команд может накладывать ограничения на значения переменных.
   Запуская новый процесс (или группу процессов) при подаче команды, система передает ему копии значений всех экспортированных переменных, дополняя или заменяя их именованными параметрами, с которыми подана команда (если таковые имеются). Совокупность этих переменных называется окружениемпроцесса. Обратное наследование (передача процессом переменных родительскому) в открытых системах отсутствует.
   Переменная будет раскрытаоболочкой (подобно тому, как оболочка раскрывает значения специальных символов в именах файлов), если указать ее в любом месте любой команды в окружении фигурных скобок «{» и «}» предваренной знаком денежной единицы «$». В большинстве случаев (когда не возникает неоднозначности в интерпретации) фигурные скобки можно опустить (Рис. 1-62).
Рис. 1-62
   Получить список переменных, установленных в данный момент времени, можно командой «set» без аргументов, а список экспортированных переменных – командой «env» без аргументов. В типичной системе при запуске оболочки устанавливаются значения нескольких десятков переменных, большинство из которых сразу экспортируются. В примере на Рис. 1-63 эти списки сильно сокращены. С семантикой отдельных переменных мы познакомимся ниже.
Рис. 1-63
   Стандартом определен ряд переменных, имеющих фиксированную семантику, значения которых используются стандартными командами. Они представлены в таблице на Рис. 1-64. В большинстве случаев пользователю не нужно устанавливать их значения самостоятельно.
Рис. 1-64
   Кроме того, стандарт не рекомендует произвольно переопределять еще ряд переменных: ARFLAGS, CC, CDPATH, CFLAGS, CHARSET, DEAD, EDITOR, ENV, EXINIT, FC, FCEDIT, FFLAGS, GET, GFLAGS, HISTFILE, HISTORY, HISTSIZE, IFS, LDFLAGS, LEX, LFLAGS, LINENO, LISTER, LPDEST, MAIL, MAILCHECK, MAILER, MAILPATH, MAILRC, MAKEFLAGS, MAKESHELL, MANPATH, MBOX, MORE, MSGVERB, PROC, OLDPWD, OPTARG, OPTERR, OPTIND, PAGER, PPID, PRINTER, PROCLANG, PROJECTDIR, RANDOM, SECONDS, TERMCAP, TERMINFO, USER, VISUAL, YACC, YFLAGS.Некоторые из них используются самой оболочкой, некоторые – стандартными командами, а некоторые – прикладными и инструментальными программами.
    Локаль, «и17я» и «л9я»
    Локалью(«locale» – «местонахождение») называется совокупность переменных, управляющих поведением оболочки, команд и других программ в части языковых и национально-культурных особенностей. Локалью также называются и значения, которые принимают эти переменные (кроме NLSPATH).
   В любой стандартной ОС определены (совпадающие) локали «POSIX» и «C». Эта локаль называется «системной» и во всех известных нам системах она соответствует соглашениям, принятым в США 48.
   В ОС может быть также определено произвольное количество локалей, именуемых следующим образом: двухбуквенное ИСО-сокращение названия страны, за которым слитно следует знак подчеркивания «_» и – слитно же – двухбуквенное ИСО-сокращение названия языка. Далее могут следовать (также слитно) точка и наименование кодовой таблицы. Для русского языка и российских культурных особенностей значением локали будет «ru_RU.KOI8-R» или «ru_RU.ISO8859-5».
   Текущую локаль можно узнать, подав команду «locale». Обычно всем переменным локали (кроме NLSPATH), перечисленным на Рис. 1-65, присваивается одно и то же значение (это можно сделать, установив значение всего лишь одной переменной, LC_ALL). Однако бывают и другие случаи: например, иностранный сотрудник или студент может предпочесть сообщения и диалоги на родном языке, а остальные национально-культурные параметры – соответствующими стране пребывания.
Рис. 1-65
    Интернационализованнойназывается программа (включая стандартные команды), корректно изменяющая свое поведение в соответствии с переменными локали.
    Локализованнойдля определенной локали называется программа, для которой существуют (если они необходимы) специфические языковые и культурные ресурсы.
   Для длинных слов «localization» и «internationalization» иногда даже в формальных документах используются сокращения «l10n» и «i18n» (цифры между первой и последней буквой образуют число пропущенных букв).
   Стандартный набор команд (и многие дополнительные программы), входящие в состав популярных дистрибутивов открытых систем, в массе своей интернационализованы, но с русской локализацией ситуация весьма неоднозначна. Вы столкнетесь с ситуациями, когда перевода того или иного ресурса (например, сообщений об ошибках и диагностики) для определенной программы не окажется – корректно интернационализованная программа «откатится» к системной локали «C» 49.
   Вы также можете столкнуться и с некорректно интернационализованными программами (такое случается с прикладным кодом, перенесенным с альтернативных платформ, или разработанным без оглядки на локаль), которые игнорируют локаль, ведут себя странно или отказываются работать при локали, отличной от «C» 50.
   К сожалению, при применении наиболее популярного сегодня «IBM PC-совместимого» оборудования (т.е. компьютеров на основе процессоров архитектуры IA-32) реальная локализация аппаратно-программной системы не сводится к установке локали. Для возможности ввода нелатинских символов необходимо назначить код переключения систем письменности какой-либо клавише. Кроме того, чтобы терминал отображал нелатинские символы в текстовом режиме, в видеоадаптер должен быть подгружен соответствующий шрифт (при применении эмулятора терминала в графическом режиме этой проблемы нет). Стандартного способа осуществления этих действий нет, каждая ОС решает эти задачи по-своему.
   Еще одна локализационная сложность связана с ограничением восьмибитной кодировки: размер одного набора символов ограничен 256 и, соответственно, за вычетом управляющих символов и символов псевдографики места в нем хватает только на обычную и расширенную латиницу, обычную латиницу и русскую кириллицу или обычную латиницу и украинскую кириллицу.
   Работать с текстами, включающими одновременно большее количество символов, можно, используя кодовые таблицы Юникод (стандарт ИСО/МЭК 10646) и кодировку UTF8. Однако не все ОС поддерживают корректную работу текстовых утилит при переменной длине символов. Кроме того, применять при этом вшитый знакогенератор видеоадаптера уже не представляется возможным, и для терминального режима нужен встроенный в драйвер программный знакогенератор. Проблемы, связанные с UTF8-локалью, должны быть решены в современных ОС в течение ближайших лет.
    Команда как файл и переменная $PATH
   Обратите внимание на переменную $PATH(у Алисы ее значение оказалось равным «/home/alice/bin:/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/usr/games»). Значение переменной $PATH интерпретируется оболочкой как список имен каталогов, разделенных двоеточиями. Когда оператор вводит команду, оболочка просматривает эти каталоги в поисках исполняемого файла с именем, совпадающим с именем введенной команды.
   Большинство стандартных команд ОС реализовано в виде отдельных программ (исключение составляют так называемые встроенные команды 51). Чтобы оболочка нашла и запустила соответствующую программу, путь к ней (т.e. имя каталога, в котором содержится эта программа) должен содержаться в списке, составляющем переменную $PATH. Текущий каталог не исключение – чтобы поиск программы осуществлялся и в нем, он должен в явном виде присутствовать в этом списке 52.
Рис. 1-66
   В приведенном на Рис. 1-66 примере Алиса проверяет значение $PATH, затем добавляет в начало списка текущий каталог и убеждается, что значение переменной приняло искомую форму. (Этот пример демонстрирует особенности экранирования, выполняемого двойными кавычками. Они экранируют пробелы, но, в отличие от апострофов, не мешают раскрытию специальных символов и имен переменных).
   Хотя стандартом не определены каталоги, в которых содержатся исполняемые файлы команд, в большинстве современных ОС основным таким каталогом является «/usr/bin/».
   Добавление в PATH текущего каталога считается весьма легкомысленным; по крайней мере, суперпользователь никогда не должен этого делать 53. Удобным является присутствие в PATH каталога «~/bin/» (в нашем примере «/home/alice/bin/»). В него пользователь может помещать собственные сценарии и другие программы.
   Команду, соответствующую программе, чей код размещен вне путей, перечисленных в PATH, можно издать, указав полное или относительное имя файла (например, команда «./моя_программа -o» запустит программу, содержащуюся в файле «моя_программа» в текущем каталоге). Файл должет быть исполняемым.

1.8 Конвейер

   Помимо использования символов завершения («&», «;», «&&» и «||») и скобок «(» и «)», в открытых ОС имеется еще один механизм объединения простых команд в группу – конвейер 54.
   В отличие от групп команд, объединенных упомянутыми символами завершения и независимых друг от друга, команды, входящие в конвейер, связаны передачей данных.
   В следующем примере Алисе по каким-то причинам нужно получить пронумерованный список файлов в одном из каталогов. У команды «ls» нет соответствующего ключа, хотя есть ключ «-1» (единица), позволяющий осуществить вывод списка в «коротком» формате по одному файлу на строку. Но в системе имеется стандартная команда «nl», выводящая строки ввода, предваренные их номерами.
   Алиса может перенаправить вывод команды «ls -1» во временный файл, затем перенаправить ввод команды «nl» из того же файла и, наконец, удалить его (Рис. 1-67).
Рис. 1-67
   Это достаточно громоздко; кроме того, в вывод попало и имя самого временного файла, что в планы Алисы не входило 55.
   Конвейер – это соединение двух или более команд символом «|» («вертикальная черта», «пайп»). При связывании команд конвейером, вывод указанной слева становится вводом указанной справа, безкаких-либо временных файлов (Рис. 1-68).
Рис. 1-68
   В конвейер могут быть связаны и более двух команд. В примере на Рис. 1-69 Алиса передает, как и ранее, вывод команды «ls» команде «nl», а вывод «nl» передает команде «sort -r», которая (с этим ключом) выводит свой ввод, отсортированный в обратном порядке.