Текст книги "Прикладные свободные программы и системы в школе"
Автор книги: Максим Отставнов
Жанр:
Программное обеспечение
сообщить о нарушении
Текущая страница: 5 (всего у книги 13 страниц)
Особенности прав на каталоги
Следующий пример может показаться контринтуитивным.
Рис. 1-42
У Алисы нет прав на запись в файл «файл». Тем не менее, она может удалить его командой «rm» (Рис. 1-42).
Но никакого парадокса в этом нет. Удаление файла не является изменением его содержания. Удаление файла – это изменение каталога, в котором он содержится и, соответственно, разрешение или запрещение удаления файла зависит не от прав на него, но от прав на каталог (мы помним, что каталог – это тоже файл).
Рис. 1-43
В примере на Рис. 1-43 Алиса создает каталог «каталог_1», создает в нем файл «файл_1», отнимает у владельца (себя) права на запись, тем не менее, удаляет его, затем создает такой же файл и отнимает у себя права на запись в этот каталог. После этого попытка удаления файла приводит к выводу сообщения о нехватке прав для совершения этой операции.
Соответственно, и создать файл в каталоге, прав записи на который у нее нет, она не сможет[33]33
Во многих системах крайне полезным оказывается определить особый режим для некоторых каталогов, при котором пользователь имеет право создавать в нем новые файлы, но удалять может лишь те, что принадлежат ему. Для этого стандарт предусматривает расширение набора прав, известное, как «липкий бит».
[Закрыть].
Обратите внимание, что отсутствие права записи в каталог не отнимает у Алисы права на изменение содержимого находящихся в нем файлов (Рис. 1-44).
Рис. 1-44
Это вполне логично, т.к. изменение содержимого никак не влияет на запись в каталоге. Однако здесь есть одна тонкость. Обратите внимание, что первая команда «ls -l» показала длину файла равной 0 байт (что естественно, т.к. этот файл создавался как пустой), а вторая – 4 байта. Разве информация о длине файла не является частью записи о нем в каталоге?
Вся правда о файлах
Дело в том, что понятие о файлах и их «нахождении» в каталоге выше давалось нами в несколько упрощенной форме. Если быть точными, каталог содержит не «файлы», а записи о файлах, вполне подобно тому, как библиотечный каталог содержит не книги, а записи о книгах (или библиографические карточки), а сами книги хранятся на полках[34]34
Именно поэтому метафора «папка» для каталога является неприемлемой.
[Закрыть]. Часть полей, выдаваемых командой «ls -l», относится к файлу как единице хранения («книге на полке»), а часть – к записи о нем в каталоге («библиографической карточке»).
Атрибутом записи о файле в каталоге является поле «имя».
Атрибутам файла как единицы хранения (его называют индексным узлом или и-узлом) соответствуют поля «тип и права», «количество указателей», «владелец», «группа-владелец», «размер», «время модификации».
Кстати говоря, поле «количество указателей» и содержит число «библиографических карточек» (записей в каталогах), соответствующих «книге» (и-узлу). Мы до сих пор имели дело только с и-узлами, которым соответствует одна запись (так обычно и бывает с файлами, создаваемыми пользователями), но так же, как книге могут соответствовать разные карточки (одна в предметном каталоге, другая в алфавитном каталоге названий, третья в алфавитном каталоге авторов...), на один и тот же и-узел могут ссылаться записи в разных каталогах (или разные записи в одном каталоге под разными именами). Создание и удаление дополнительных имен («ссылок») нами рассматриваться здесь не будет.
В то время, как правомочия чтения и записи на каталог вполне прозрачны (разрешение чтения позволяет прочитать список содержащихся в нем файлов (например, командой «ls»), а записи – модифицировать этот список, т.е. создавать и удалять содержащиеся в этом каталоге файлы), правомочие исполнения имеют для каталога особый смысл. Оно означает «право прохождения сквозь», т.е. право на обращение к файлам, содержащимся в каталоге и в его подкаталогах, даже если права на чтение самого каталога нет.
1.6 Процессы
Наряду с файлом, понятие процесса является важнейшим в концепции открытых операционных систем.
Процесс – это обладающая уникальным идентификатором единица исполняемого кода[35]35
Формальное определение стандарта гласит, что процесс – «Адресное пространство с одним или более витком, исполняющимся в нем, и системными ресурсами, необходимыми для исполнения этих витков». Виток (thread, поток) в свою очередь определяется как «поток управления». Понятие витка в этом курсе нам не понадобится, поскольку отдельные витки недоступны оператору оболочки. Хотя стандарт и определяет процесс через виток, понятие витка является менее фундаментальной позднейшей добавкой к концепции открытых ОС и, по мнению многих авторитетных аналитиков, является лишь средством повышения производительности.
[Закрыть] в памяти.
Подавая простую команду из оболочки, оператор дает ОС указание запустить другой процесс. В ходе исполнения процесс может порождать другие процессы и проходить целый ряд состояний, некоторые из которых будут ниже описаны. Сама оболочка также является процессом, порожденным, как правило, процессом регистрации в системе, который, в свою очередь, как правило, порождается особым инициализационным процессом.
Подобно файлам, процессы в своем отношении друг к другу могут быть представлены в виде иерархии (дерева). В отличие от иерархии файлов, ребра этого дерева представляют не отношения вложенности, но отношения порождения («родитель-ребенок»). Процесс не может появиться в системе иначе, нежели будучи порожденным другим процессом, за очевидным исключением «корневого» процесса, запускаемого самим ядром при загрузке системы. Само ядро не является процессом[36]36
В некоторых системах части кода ядра все же представлены в виде процессов, «усыновленных» инициализационным процессом.
[Закрыть].
Исследовать процессы можно стандартной командой «ps». Поданная без параметров, она выводит информацию о текущей оболочке и порожденных ею процессах.
Рис. 1-45
В выводе на Рис. 1-45 присутствуют четыре колонки. «PID» – это уникальный для системы идентификатор процесса (он устанавливается при порождении процесса и сохраняется неизменным до его завершения», «TTY» – терминал, с которого запущен процесс, «TIME» – время процесса (сумма квантов процессорного времени, потребленного процессом на момент «снимка» его состояния), «CMD» – команда, подача которой привела к порождению процесса.
В данном случае Алиса получила информацию о двух процессах: оболочке «bash» и внешней команде «ps»[37]37
Точность представления «снимка» в различных реализациях варьирует; так что не пугайтесь, если при подаче такой команды, допустим, ОС «Солярис», не увидите информации о «ps» – используемая при выводе таблица просто не успела обновиться.
[Закрыть].
Команда «ps -A» выводит информацию обо всех процессах в системе[38]38
Обычному пользователю предоставление информации о чужих процессах может быть ограничено по соображениям безопасности.
[Закрыть]. В примере на Рис. 1-46 мы, подав команду из эмулятора терминала, для наглядности использовали ключ «-A» вместе с ключом «-l» («эль»), задающим «длинный» формат вывода (с дополнительными полями) и нестандартным ключом «-H», представляющим с помощью отступов в поле «CMD» отношения между процессами (вывод немного сокращен).
Рис. 1-46
Несколько иной набор параметров процесса можно получить, использовав вместо ключа «-l» ключ «-w», а ключ «-o» позволяет вывести для каждого процесса произвольный набор параметров из числа поддерживаемых системой, указав их мнемонику в качестве аргумента этого ключа.
Стандартом определено пятнадцать параметров, к которым могут добавляться параметры, специфичные для конкретной системы. Мы разберем лишь некоторые из них.
UID – это идентификатор пользователя-владельца процесса. Как и у файла, у процесса есть владелец. В данном примере (при использовании ключа «-l») идентификатор выводится в числовом виде; если бы был задан ключ «-w», мы бы увидели, что числовому идентификатору 504 соответствует символический идентификатор «maksim», 505 – «alice». Числовой идентификатор 0 всегда соответствует главному пользователю «root».
Обычно UID наследуется от процесса-родителя. Исключение составляют процессы-оболочки, запускаемые программой регистрации – их UID соответствует идентификатору зарегистрировавшегося пользователя, хотя UID самой программы регистрации – 0.
Еще одно исключение – процессы, порожденные запуском программы из файла с установленным битом SUID. Их UID соответствует не породившему их процессу, а владельцу исполняемого файла. SUID (и подобный ему по эффекту бит GUID) – это мощный (и очень опасный) инструмент обхода системы распределения полномочий в ОС, поскольку позволяет пользователю запускать процессы с полномочиями выше собственных (в том числе, с полномочиями главного пользователя). Установить SUID бит может только главный пользователь. В аккуратно построенной и администрируемой системе количество программ с установленным SUID (и/или GUID) битом минимально.
В нашем примере этот механизм с очевидностью использован при запуске процесса «X» (Икс-сервер – основной компонент графической системы, предоставляющий в распоряжение Икс-клиентов (программ с графическим интерфейсом) виртуальный X-терминал, связанный с физическими видеоадаптером, клавиатурой, мышью и системным динамиком), чьим родителем является процесс «xinit» с UID равным 504. Существенно, что Икс-клиенты (процессы «blackbox», «soffice.bin», «mozilla-bin», «xterm») выполняются с обычным пользовательским UID.
PID, как мы уже знаем, это уникальный идентификатор процесса[39]39
Обратите внимание, что процессы, порожденные при подаче одинаковых команд (например «login» на tty1 и tty2 или «bash» на tty1, tty2 и pts/0), имеют разные идентификационные номера.
[Закрыть], а PPID – идентификатор его родителя. Обратите внимание на соответствие между PPID различных процессов в примере и расположением их в сформированном ключом «-H» «дереве».
TIME – время процесса – это совокупное количество процессорного времени, потребленного процессором на выполнение этого процесса за время его существования.
S – это состояние процесса. Запущенный процесс может находиться в одном из четырех стандартных состояний: «R» (выполняемый), «S» (ожидающий ввода-вывода), «T» (приостановленный – приостановку процессов мы обсудим ниже), «Z» («зомбированный», уже завершенный, но не успевший сообщить об этом процессу-родителю).
Итак, в примере на Рис. 1-46 мы видим:
находящийся в корне дерева процесс «init»;
два порожденных им процесса, не имеющих управляющего терминала: демон управления системой энергосбережения «apmd»[40]40
Собственно говоря, определить, что представляет собой тот или иной процесс, можно, подав команду «man» с именем команды из столбца CMD в качестве аргумента. В «ГНУ/Линукс» этот фокус не пройдет с квазипроцессами, которыми представлены некоторые компоненты ядра, поскольку они не имеют страниц руководства.
[Закрыть] и демон периодического исполнения заданий «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]41
На самом деле сигнал приостановки и клавиатурная комбинация Control-Z не описаны в стандарте, но вы найдете эту возможность практически в любой открытой ОС.
[Закрыть].
Рис. 1-49
После нажатия Control-Z оболочка выдает сообщение, состоящее из числа в квадратных скобках, слова «остановлен» («stopped» в стандартной локали) и введенной ранее команды (Рис. 1-49).
Число в квадратных скобках – это номер задания. Заданием является любая начавшая выполняться простая команда.
Состояние соответствующего процесса (колонка «S» в выводе «ps -l» (эль)) обозначено буквой «T», означающей, что процесс остановлен. Задание, соответствующее такому процессу, также называется остановленным.
Возобновить исполнение задания можно двумя способами. Команда «fg» возобновляет выполнение задания на переднем плане, а команда «bg» – на заднем плане (или в фоновом режиме)[42]42
В некоторых «доюниксовых» ОС понятие переднего и заднего планов выполнения было связано с приоритетами заданий или с выполнением одного из них в режиме «реального времени». В открытых ОС понятие приоритета и понятие плана выполнения взаимонезависимы.
[Закрыть]. Заданием переднего плана называется задание, завершения ведущего процесса (первого процесса, запущенного подачей команды) которого ожидает оболочка перед выводом очередного приглашения, и которое может свободно выводить данные на управляющий терминал и вводить их с терминала.
В любой момент времени на переднем плане каждого управляющего терминала находится не более одного задания. Заданий заднего плана может быть неограниченное[43]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]44
Например, сигналы об аппаратных или системно-программных ошибках, получение которых, как правило, приводит к аварийному завершению процесса-получателя с записью файла дампа памяти (core) в домашний каталог пользователя-хозяина процесса. Список известных системе сигналов (их число может приближаться к сотне) можно получить по команде «kill -l», но смысла указанных там идентификаторв, как правило, приходится доискиваться в технической документации.
[Закрыть]. Практически во всех системах реализован сигнал SIGSTOP, его отправка процессу переднего плана большинством современных оболочек осуществляется нажатием Control-Z, как описано выше.
На пользовательском уровне применяются обычно сигналы SIGTERM и SIGKILL. Отличие их в том, что при получении первого из них процесс по возможности завершается «чисто»: сбрасывает содержимое внутренних буферов в файлы и закрывает их, а второго – завершается немедленно. Второй используется обычно для «убиения» процесса, выполняющего ошибочную программу.
До сих пор Алиса отправляла сигналы по собственной инициативе. Отправляющим процессом выступали оболочка (при передаче сигналов процессу переднего плана нажатием клавиш) или команда «kill». Но передача сигналов (межпроцессная коммуникация) может осуществляться между любыми процессами, и является широко используемым в системном и прикладном программировании механизмом ОС. Как и при доступе к файлам, при доступе к процессам ОС руководствуется системой распределения полномочий. Стандартное поведение проще, чем в случае с файлами: процесс, запущенный обычным пользователем (не главным пользователем), может сигнализировать процессу, запущенному тем же пользователем, и не может сигнализировать процессу, которым «владеет» другой пользователь. В примере на Рис. 1-53 попытка Алисы «убить» процесс, принадлежащий другому пользователю, привела к сообщению об ошибке.
Рис. 1-53
Реализацией может быть определено более сложное поведение[45]45
На самом деле современные ОС ставят в соответствие процессам каталоги в фиктивной файловой структуре, смонтированной в каталоге «/proc/», и управляют доступом к процессам на основании правомочий, сопоставленных этим «каталогам» и входящим в них «файлам».
[Закрыть].
Программа может переопределить смысл сигналов, которые получает процесс (в частности, отменить завершение процесса), за исключением сигнала SIGKILL. Все стандартные команды ОС обрабатывают сигналы стандартным образом[46]46
Хотя экранные команды («more», «vi») перехватывают нажатие Control-C.
[Закрыть].
Если вам не удается завершить запущенный вами процесс подачей команды «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]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]48
Строго говоря, стандарт не говорит об этом прямо, но ограничивает набор символов стандартной локали семибитной латиницей, так что кроме американского или британского английского полностью реализовать стандартную локаль, не прибегая к транслитерации, можно разве что на латыни.
[Закрыть].
В ОС может быть также определено произвольное количество локалей, именуемых следующим образом: двухбуквенное ИСО-сокращение названия страны, за которым слитно следует знак подчеркивания «_» и – слитно же – двухбуквенное ИСО-сокращение названия языка. Далее могут следовать (также слитно) точка и наименование кодовой таблицы. Для русского языка и российских культурных особенностей значением локали будет «ru_RU.KOI8-R» или «ru_RU.ISO8859-5».
Текущую локаль можно узнать, подав команду «locale». Обычно всем переменным локали (кроме NLSPATH), перечисленным на Рис. 1-65, присваивается одно и то же значение (это можно сделать, установив значение всего лишь одной переменной, LC_ALL). Однако бывают и другие случаи: например, иностранный сотрудник или студент может предпочесть сообщения и диалоги на родном языке, а остальные национально-культурные параметры – соответствующими стране пребывания.
Рис. 1-65
Интернационализованной называется программа (включая стандартные команды), корректно изменяющая свое поведение в соответствии с переменными локали.
Локализованной для определенной локали называется программа, для которой существуют (если они необходимы) специфические языковые и культурные ресурсы.
Для длинных слов «localization» и «internationalization» иногда даже в формальных документах используются сокращения «l10n» и «i18n» (цифры между первой и последней буквой образуют число пропущенных букв).
Стандартный набор команд (и многие дополнительные программы), входящие в состав популярных дистрибутивов открытых систем, в массе своей интернационализованы, но с русской локализацией ситуация весьма неоднозначна. Вы столкнетесь с ситуациями, когда перевода того или иного ресурса (например, сообщений об ошибках и диагностики) для определенной программы не окажется – корректно интернационализованная программа «откатится» к системной локали «C»[49]49
Программы, входящие в утилиты ГНУ, составляющие большую часть системных программ в «ГНУ/Линукс», заметную – в ОС семейства «БСД» и часто используемые в других ОС, реализуют более продвинутую стратегию. Если определена переменная LANGUAGE, она будет интерпретироваться как список локалей (разделенных двоеточием) в порядке их предпочтения, в соответствии с которым программа будет искать локализационные ресурсы.
[Закрыть].
Вы также можете столкнуться и с некорректно интернационализованными программами (такое случается с прикладным кодом, перенесенным с альтернативных платформ, или разработанным без оглядки на локаль), которые игнорируют локаль, ведут себя странно или отказываются работать при локали, отличной от «C»[50]50
В последнем случае можно явным образом передать этой конкретной программе значение переменной LC_ALL, равное «C» или «POSIX».
[Закрыть].
К сожалению, при применении наиболее популярного сегодня «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]51
Встроенные команды исполняются самой оболочкой, без вызова внешних программ. Пятнадцать команд («break», «:», «continue», «.», «eval», «exec», «exit», «export», «readonly», «return», «set», «shift», «times», «trap», «unset») являются специальными встроенными, их должна реализовать любая оболочка. Кроме того, разработчик оболочки может по каким-либо соображениям реализовать любую другую команду (стандартную или нестандартную) как встроенную. Пользователю в большинстве случаев безразлично, является ли команда встроенной или внешней, но при начале использования новой оболочки полезно ознакомиться со списком встроенных в нее команд.
[Закрыть]). Чтобы оболочка нашла и запустила соответствующую программу, путь к ней (т.e. имя каталога, в котором содержится эта программа) должен содержаться в списке, составляющем переменную $PATH. Текущий каталог не исключение – чтобы поиск программы осуществлялся и в нем, он должен в явном виде присутствовать в этом списке[52]52
Это уточнение приведено специально для мигрантов с MS-DOS.
[Закрыть].
Рис. 1-66
В приведенном на Рис. 1-66 примере Алиса проверяет значение $PATH, затем добавляет в начало списка текущий каталог и убеждается, что значение переменной приняло искомую форму. (Этот пример демонстрирует особенности экранирования, выполняемого двойными кавычками. Они экранируют пробелы, но, в отличие от апострофов, не мешают раскрытию специальных символов и имен переменных).
Хотя стандартом не определены каталоги, в которых содержатся исполняемые файлы команд, в большинстве современных ОС основным таким каталогом является «/usr/bin/».
Добавление в PATH текущего каталога считается весьма легкомысленным; по крайней мере, суперпользователь никогда не должен этого делать[53]53
В текущем каталоге случайно или вследствие чьей-то «шутки» может оказаться исполняемый файл, совпадающий по имени со стандартной или административной командой системы, но выполняющий другую функцию.
[Закрыть]. Удобным является присутствие в PATH каталога «~/bin/» (в нашем примере «/home/alice/bin/»). В него пользователь может помещать собственные сценарии и другие программы.