Операционная система UNIX

Робачевский Андрей М.

Глава 1.

Работа в операционной системе UNIX

 

 

Сегодня UNIX используется на самых разнообразных аппаратных платформах — от персональных рабочих станций до мощных серверов с тысячами пользователей. И прежде всего потому, что UNIX — это многозадачная многопользовательская система, обладающая широкими возможностями.

С точки зрения пользователя в операционной системе UNIX существуют два типа объектов: файлы и процессы. Все данные хранятся в виде файлов, доступ к периферийным устройствам осуществляется посредством чтения/записи в специальные файлы. Когда вы запускаете программу, ядро загружает соответствующий исполняемый файл, создает образ процесса и передает ему управление. Более того, во время выполнения процесс может считывать или записывать данные в файл. С другой стороны, вся функциональность операционной системы определяется выполнением соответствующих процессов. Работа системы печати или обеспечения удаленного доступа зависит от того, выполняются ли те или иные процессы в системе.

В этой главе мы познакомимся с пользовательской средой операционной системы UNIX; попробуем взглянуть на UNIX глазами обычного пользователя и администратора системы; не вдаваясь во внутреннюю архитектуру, обсудим, что такое файлы и файловая система, рассмотрим ее организацию и характеристики; с этих же позиций рассмотрим процесс в UNIX, его роль, атрибуты и жизненный цикл.

Мы также постараемся ответить на вопрос, что представляет собой пользователь UNIX как с точки зрения самой системы, так и с точки зрения администрирования; изучим сеанс работы в операционной системе и подробно остановимся на командном интерпретаторе shell — базовой рабочей среде пользователя; познакомимся с наиболее часто используемыми утилитами, неразрывно связанными с UNIX. В заключение постараемся сформулировать основные задачи администрирования этой операционной системы.

 

Файлы и файловая система

 

Файлы в UNIX играют ключевую роль, что не всегда справедливо для других операционных систем. Трудно отрицать значение файлов для пользователей, поскольку все их данные хранятся в виде файлов. Однако помимо этого, файлы в UNIX определяют привилегии пользователей, поскольку права пользователя в большинстве случаев контролируются с помощью прав доступа к файлам. Файлы обеспечивают доступ к периферийным устройствам компьютера, включая диски, накопители на магнитной ленте, CD-ROM, принтеры, терминалы, сетевые адаптеры и даже память. Для приложений UNIX доступ в дисковому файлу "неотличим" от доступа, скажем, к принтеру. Наконец, все программы, которые выполняются в системе, включая прикладные задачи пользователей, системные процессы и даже ядро UNIX, являются исполняемыми файлами.

Как и во многих современных операционных системах, в UNIX файлы организованы в виде древовидной структуры (дерева), называемой файловой системой (file system). Каждый файл имеет имя, определяющее его расположение в дереве файловой системы. Корнем этого дерева является корневой каталог (root directory), имеющий имя "/". Имена всех остальных файлов содержат путь — список каталогов (ветвей), которые необходимо пройти, чтобы достичь файла. В UNIX все доступное пользователям файловое пространство объединено в единое дерево каталогов, корнем которого является каталог "/". Таким образом, полное имя любого файла начинается с "/" и не содержит идентификатора устройства (дискового накопителя, CD-ROM или удаленного компьютера в сети), на котором он фактически хранится.

Однако это не означает, что в системе присутствует только одна файловая система. В большинстве случаев единое дерево, такое каким его видит пользователь системы, составлено из нескольких отдельных файловых систем, которые могут иметь различную внутреннюю структуру, а файлы, принадлежащие этим файловым системам, могут быть расположены на различных устройствах. Вопросы, связанные с объединением нескольких файловых систем в единое дерево, будут обсуждаться при рассмотрении внутреннего устройства файловой системы UNIX в главе 4.

Заметим, что имя файла является атрибутом файловой системы, а не набора некоторых данных на диске, который не имеет имени как такового. Каждый файл имеет связанные с ним метаданные (хранящиеся в индексных дескрипторах — inode), содержащие все характеристики файла и позволяющие операционной системе выполнять операции, заказанные прикладной задачей: открыть файл, прочитать или записать данные, создать или удалить файл. В частности, метаданные содержат указатели на дисковые блоки хранения данных файла. Имя файла в файловой системе является указателем на его метаданные, в то время как метаданные не содержат указателя на имя файла.

 

Типы файлов

 

В UNIX существуют 6 типов файлов, различающихся по функциональному назначению и действиям операционной системы при выполнении тех или иных операций над файлами:

□ Обычный файл (regular file)

□ Каталог (directory)

□ Специальный файл устройства (special device file)

□ FIFO или именованный канал (named pipe)

□ Связь (link)

□ Сокет

Обычный файл представляет собой наиболее общий тип файлов, содержащий данные в некотором формате. Для операционной системы такие файлы представляют собой просто последовательность байтов. Вся интерпретация содержимого файла производится прикладной программой, обрабатывающей файл. К этим файлам относятся текстовые файлы, бинарные данные, исполняемые программы и т.п.

Каталог . С помощью каталогов формируется логическое дерево файловой системы. Каталог — это файл, содержащий имена находящихся в нем файлов, а также указатели на дополнительную информацию — метаданные, позволяющие операционной системе производить операции над этими файлами. Каталоги определяют положение файла в дереве файловой системы, поскольку сам файл не содержит информации о своем местонахождении. Любая задача, имеющая право на чтение каталога, может прочесть его содержимое, но только ядро имеет право на запись в каталог.

На рис. 1.1 в качестве примера приведена структура каталога. По существу каталог представляет собой таблицу, каждая запись которой соответствует некоторому файлу. Первое поле каждой записи содержит указатель на метаданные (номер mode), а второе определяет имя файла.

Рис. 1.1. Структура каталога

Специальный файл устройства обеспечивает доступ к физическому устройству. В UNIX различают символьные (character) и блочные (block) файлы устройств. Доступ к устройствам осуществляется путем открытия, чтения и записи в специальный файл устройства.

Символьные файлы устройств используются для небуферизированного обмена данными с устройством, в противоположность этому блочные файлы позволяют производить обмен данными в виде пакетов фиксированной длины — блоков. Доступ к некоторым устройствам может осуществляться как через символьные, так и через блочные специальные файлы.

Как производится работа с периферийными устройствами, описано в главе 5.

FIFO или именованный канал — это файл, используемый для связи между процессами. FIFO впервые появились в System V UNIX, но большинство современных систем поддерживают этот механизм. Более подробно мы рассмотрим этот тип файлов при обсуждении системы межпроцессного взаимодействия в главе 3.

Связь . Как уже говорилось, каталог содержит имена файлов и указатели на их метаданные. В то же время сами метаданные не содержат ни имени файла, ни указателя на это имя. Такая архитектура позволяет одному файлу иметь несколько имен в файловой системе. Имена жестко связаны с метаданными и, соответственно, с данными файла, в то время как сам файл существует независимо от того, как его называют в файловой системе. Такая связь имени файла с его данными называется жесткой связью (hard link). Например, с помощью команды ln(1) мы можем создать еще одно имя (second) файла, на который указывает имя first (рис. 1.2).

Рис. 1.2. Структура файловой системы после выполнения команды ln(1). Жесткая связь имен с данными файла

$ pwd

/home/andrei

$ ln first /home/sergey/second

Жесткие связи абсолютно равноправны. В списках файлов каталогов, которые можно получить с помощью команды ls(1), файлы first и second будут отличаться только именем. Все остальные атрибуты файла будут абсолютно одинаковыми. С точки зрения пользователя — это два разных файла. Изменения, внесенные в любой из этих файлов, затронут и другой, поскольку оба они ссылаются на одни и те же данные файла. Вы можете переместить один из файлов в другой каталог — все равно эти имена будут связаны жесткой связью с данными файла. Легко проверить, что удаление одного из файлов (first или second) не приведет к удалению самого файла, т.е. его метаданных и данных (если это не специальный файл устройства).

По определению жесткие связи указывают на один и тот же индексный дескриптор inode. Поэтому проверить, имеют ли два имени файла жесткую связь, можно, вызвав команду ls(1) с ключом -i:

$ ls -i /home/andrei/first /home/sergey/second

12567 first

12567 second

Информацию о наличии у файла нескольких имен, связанных с ним жесткими связями, можно получить, просмотрев подробный листинг файлов с помощью команды /ls -l:

$ ls -l /home/sergey

...

-rw-r--r-- 2 andrei staff 7245 Jan 17 8:05 second

...

Во второй колонке листинга указано число жестких связей данного файла.

Сразу оговоримся, что жесткая связь является естественной формой связи имени файла с его метаданными и не принадлежит к особому типу файла. Особым типом файла является символическая связь, позволяющая косвенно адресовать файл. В отличие от жесткой связи, символическая связь адресует файл, который, в свою очередь, ссылается на другой файл. В результате, последний файл адресуется символической связью косвенно (рис. 1.3). Данные файла, являющегося символической связью, содержат только имя целевого файла.

Рис. 1.3. Символическая связь

Проиллюстрируем эти рассуждения на примере. Команда ln(1) с ключом -s позволяет создать символическую связь:

$ pwd

/home/andrei

$ ln -s first /home/sergey/symfirst

$ cd /home/sergey

$ ls -l

...

lrwxrwxrwx 1 andrei staff 15 Jan 17 8:05 symfirst->../andrei/first

Как видно из вывода команды ls(1), файл symfirst (символическая связь) существенно отличается от файла second (жесткая связь). Во-первых, фактическое содержимое файла symfirst отнюдь не то же, что и у файла first или second, об этом говорит размер файла — 15 байт. На самом деле в этом файле хранится не что иное как имя файла, на которую символическая связь ссылается — ../andrei/first — ровно 15 байт. Во-вторых, файл symfirst не содержит никаких ограничений на доступ (символы 2–10 в первой колонке).

Символическая связь является особым типом файла (об этом свидетельствует символ 'l' в первой позиции вывода ls(1)), и операционная система работает с таким файлом не так, как с обычным. Например, при выводе на экран содержимого файла symfirst появятся данные файла /home/andrei/first.

 

Сокеты

Сокеты предназначены для взаимодействия между процессами. Интерфейс сокетов часто используется для доступа к сети TCP/IP. В системах, ветви BSD UNIX на базе сокетов реализована система межпроцессного взаимодействия, с помощью которой работают многие системные сервисы, например, система печати. Мы подробнее познакомимся с сокетами в разделе "Межпроцессное взаимодействие в BSD UNIX" главы 3.

 

Структура файловой системы UNIX

 

Использование общепринятых имен основных файлов и структуры каталогов существенно облегчает работу в операционной системе, ее администрирование и переносимость. Эта структура используется в работе системы, например при ее инициализации и конфигурировании, при работе почтовой системы и системы печати. Нарушение этой структуры может привести к неработоспособности системы или отдельных ее компонентов.

Рис. 1.4 Типичная файловая система UNIX

Приведем краткое описание основных каталогов.

 

Корневой каталог

Корневой каталог "/" является основой любой файловой системы UNIX. Все остальные файлы и каталоги располагаются в рамках структуры, порожденной корневым каталогом, независимо от их физического местонахождения.

 

/bin

В каталоге /bin находятся наиболее часто употребляемые команды и утилиты системы, как правило, общего пользования.

 

/dev

Каталог /dev содержит специальные файлы устройств, являющиеся интерфейсом доступа к периферийным устройствам.

Каталог /dev может содержать несколько подкаталогов, группирующих специальные файлы устройств одного типа. Например, каталог /dev/dsk содержит специальные файлы устройств для доступа к гибким и жестким дискам системы.

 

/etc

В этом каталоге находятся системные конфигурационные файлы и многие утилиты администрирования. Среди наиболее важных файлов — скрипты инициализации системы. Эти скрипты хранятся в каталогах /etc/rc0.d, /etc/rc1.d, /etc/rc2.d и т.д, соответствующих уровням выполнения системы (run level), и управляются скриптами /etc/rc0, /etc/rc1, /etc/rc2 и т.д. Во многих версиях BSD UNIX указанные каталоги отсутствуют, и загрузка системы управляется скриптами /etc/rc.boot, /etc/rc и /etc/rc.local. В UNIX ветви System V здесь находится подкаталог default, где хранятся параметры по умолчанию многих команд (например, /etc/default/su содержит параметры для команды su(1M)). В UNIX System V большинство исполняемых файлов перемещены в каталог /sbin или /usr/sbin.

 

/lib

В каталоге /lib находятся библиотечные файлы языка С и других языков программирования. Стандартные названия библиотечных файлов имеют вид libx.a (или libx.so), где x — это один или более символов, определяющих содержимое библиотеки. Например, стандартная библиотека С называется libc.a, библиотека системы X Window System имеет имя libX11.a. Часть библиотечных файлов также находится в каталоге /usr/lib.

 

/lost+found

Каталог "потерянных" файлов. Ошибки целостности файловой системы, возникающие при неправильном останове UNIX или аппаратных сбоях, могут привести к появлению т.н. "безымянных" файлов — структура и содержимое файла являются правильными, однако для него отсутствует имя в каком-либо из каталогов. Программы проверки и восстановления файловой системы помещают такие файлы в каталог /lost+found под системными числовыми именами. Мы коснемся вопроса имен файлов далее в этой главе и, более подробно, в главе 4.

 

/mnt

Стандартный каталог для временного связывания (монтирования) физических файловых систем к корневой для получения единого дерева логической файловой системы. Обычно содержимое каталога /mnt пусто, поскольку при монтировании он перекрывается связанной файловой системой. Более подробно процесс монтирования и относящиеся к нему структуры данных ядра мы рассмотрим в главе 4.

 

/u или /home

Общеупотребительный каталог для размещения домашних каталогов пользователей. Например, имя домашнего каталога пользователя andrei будет, скорее всего, называться /home/andrei или /u/andrei. В более ранних версиях UNIX домашние каталоги пользователей размещались в каталоге /usr.

 

/usr

В этом каталоге находятся подкаталоги различных сервисных подсистем — системы печати, электронной почты и т.д. (/usr/spool), исполняемые файлы утилит UNIX (/usr/bin), дополнительные программы, используемые на данном компьютере (/usr/local), файлы заголовков (/usr/include), электронные справочники (/usr/man) и т.д.

 

/var

В UNIX System V этот каталог является заменителем каталога /usr/spool, используемого для хранения временных файлов различных сервисных подсистем — системы печати, электронной почты и т.д.

 

/tmp

Каталог хранения временных файлов, необходимых для работы различных подсистем UNIX. Обычно этот каталог открыт на запись для всех пользователей системы.

 

Владельцы файлов

Файлы в UNIX имеют двух владельцев: пользователя (user owner) и группу (group owner). Важной особенностью является то, что владелец- пользователь может не являться членом группы, владеющей файлом. Это дает большую гибкость в организации доступа к файлам. Совместное пользование файлами можно организовать практически для любого состава пользователей, создав соответствующую группу и установив для нее права на требуемые файлы. При этом для того чтобы некий пользователь получил доступ к этим файлам, достаточно включить его в группу- владельца, и наоборот — исключение из группы автоматически изменяет для пользователя права доступа к файлам.

Для определения владельцев файла достаточно посмотреть подробный листинг команды ls -l. Третья и четвертая колонки содержат имена владельца-пользователя и владельца-группы, соответственно:

1          2 3    4       5      6      7     8

-rw-r--r-- 1 andy group   235520 Dec 22 19:13 pride.tar

-rw-rw-r-- 1 andy student   3450 Nov 12 19:13 exams.quest

Владельцем-пользователем вновь созданного файла является пользователь, который создал файл. Порядок назначения владельца-группы зависит от конкретной версии UNIX. Например, в SCO UNIX владельцем-группой является первичная группа пользователя, создавшего файл, а в Digital UNIX владелец-группа наследуется от владельца группы — каталога, в котором создается файл.

Для изменения владельца файла используется команда chown(1). В качестве параметров команда принимает имя владельца-пользователя и список файлов, для которых требуется изменить данный атрибут. Например, следующая команда установит пользователя sergey владельцем файлов client.c и server.c:

$ chown sergey client.c server.c

Изменение владельца-группы производится командой chgrp(1). Как и chown(1), в качестве параметров команда принимает имя владельца-группы и список файлов, для которых требуется изменить данный атрибут. Например, для установки группы staff в качестве владельца всех файлов текущего каталога, необходимо задать следующую команду:

$ chgrp staff *

Владение файлом определяет тот набор операций, который пользователь может совершить с файлом. Часть из них, такие как изменение прав доступа или владельца файла (табл. 1.1), может осуществлять только владелец (или суперпользователь), другие операции, такие как чтение, запись и запуск на выполнение (для исполняемых файлов) дополнительно контролируются правами доступа.

Таблица 1.1. Операции изменения владельцев файла

Операция Команда Имеет право выполнять
в системе BSD 4.x в системе SVR4
Изменение владельца-пользователя chown(1) суперпользователь владелец файла
Изменение владельца-группы chgrp(1) суперпользователь владелец файла только для группы, к которой сам принадлежит (в соответствии с POSIX)

 

Права доступа к файлу

В операционной системе UNIX существуют три базовых класса доступа к файлу, в каждом из которых установлены соответствующие права доступа:

User access (u) Для владельца-пользователя файла
Group access (g) Для членов группы, являющейся владельцем файла
Other access (о) Для остальных пользователей (кроме суперпользователя)

UNIX поддерживает три типа прав доступа для каждого класса: на чтение (read, обозначается символом на запись (write, обозначается символом w) и на выполнение (execute, обозначается символом x).

С помощью команды ls -l можно получить список прав доступа к файлу:

...

-rw-r--r-- 1 andy group 36482 Dec 22 19:13 report.txt.1

drwxr-xr-- 2 andy group    64 Aug 15 11:03 temp

-rwxr-xr-- 1 andy group  4889 Dec 22 15:13 a.out

-rw-r--r-- 1 andy group  7622 Feb 11 09:13 cont.c

...

Права доступа листинга отображаются в первой колонке (за исключением первого символа, обозначающего тип файла). Наличие права доступа обозначается соответствующим символом, а отсутствие — символом '-'. Рассмотрим, например, права доступа к файлу a.out:

Тип файла Права владельца- пользователя Права владельца- группы Права остальных пользователей
- rwx r-x r--
Обычный файл Чтение, запись, выполнение Чтение и выполнение Только чтение

Права доступа могут быть изменены только владельцем файла или суперпользователем (superuser) — администратором системы. Для этого используется команда chmod(1). Ниже приведен общий формат этой команды.

В качестве аргументов команда принимает указание классов доступа — владелец-пользователь, 'g' — владелец-группа, 'о' — остальные пользователи, 'а' — все классы пользователей), права доступа ('r' — чтение, 'w' — запись и 'x' — выполнение) и операцию, которую необходимо произвести ('+' — добавить, '-' — удалить и '=' — присвоить) для списка файлов file1, file2 и т.д. Например, команда

$ chmod g-wx ownfile

лишит членов группы-владельца файла ownfile права на запись и выполнение этого файла.

В одной команде можно задавать различные права для нескольких классов доступа, разделив их запятыми.

Приведем еще несколько примеров:

$ chmod a+w text Предоставить право на запись для всех пользователей
$ chmod go=r text Установить право на чтение для всех пользователей, за исключением владельца
$ chmod g+x-w runme Добавить для группы право на выполнение файла runme и снять право на запись
$ chmod u+w,og+r-w text1 text2 Добавить право записи для владельца, право на чтение для группы и остальных пользователей, отключить право на запись для всех пользователей, исключая владельца

Последний пример демонстрирует достаточно сложную установку прав доступа. Вы можете установить сразу все девять прав доступа, используя числовую форму команды chmod(1):

$ chmod 754 *

Число определяется следующим образом: нужно представить права доступа в двоичном виде (0 — отсутствие соответствующего права, 1 — его наличие) и каждую триаду, соответствующую классу доступа, в свою очередь преобразовать в десятичное число.

Владелец Группа Остальные
r w x r x r - -
1 1 1 1 0 1 1 0 0
7 5 4

Таким образом, приведенный пример эквивалентен следующей символьной форме chmod(1):

$ chmod u=rwx, g=rx, o=r *

Значение прав доступа различно для разных типов файлов. Для файлов операции, которые можно производить, следуют из самих названий прав доступа. Например, чтобы просмотреть содержимое файла командой cat(1), пользователь должен иметь право на чтение (r). Редактирование файла, т.е. его изменение, предусматривает наличие права на запись (w). Наконец, для того чтобы запустить некоторую программу на выполнение, вы должны иметь соответствующее право (x). Исполняемый файл может быть как скомпилированной программой, так и скриптом командного интерпретатора shell. В последнем случае вам также понадобится право на чтение, поскольку при выполнении скрипта командный интерпретатор должен иметь возможность считывать команды из файла. Все сказанное, за исключением, пожалуй, права на выполнение, имеющего смысл лишь для обычных файлов и каталогов, справедливо и для других типов файлов: специальных файлов устройств, именованных каналов, и сокетов. Например, чтобы иметь возможность распечатать документ, вы должны иметь право на запись в специальный файл устройства, связанный с принтером. Для каталогов эти права имеют другой смысл, а для символических связей они вообще не используются, поскольку контролируются целевым файлом.

Права доступа для каталогов не столь очевидны. Это в первую очередь связано с тем, что система трактует операции чтения и записи для каталогов отлично от остальных файлов. Право чтения каталога позволяет вам получить имена (и только имена) файлов, находящихся в данном каталоге. Чтобы получить дополнительную информацию о файлах каталога (например, подробный листинг команды ls -l), системе придется "заглянуть" в метаданные файлов, что требует права на выполнения для каталога. Право на выполнения также потребуется для каталога, в который вы захотите перейти (т.е. сделать его текущим) с помощью команды cd(1). Это же право нужно иметь для доступа ко всем каталогам на пути к указанному. Например, если вы установите право на выполнения для всех пользователей в одном из своих подкаталогов, он все равно останется недоступным, пока ваш домашний каталог не будет иметь такого же права.

Права r и x действуют независимо, право x для каталога не требует наличия права r, и наоборот. Комбинацией этих двух прав можно добиться интересных эффектов, например, создания "темных" каталогов, файлы которых доступны только в случае, если пользователь заранее знает их имена, поскольку получение списка файлов таких каталогов запрещено. Данный прием, кстати, используется при создании общедоступных архивов в сети когда некоторые разделы архива могут использоваться только "посвященными", знающими о наличии того или иного файла в каталоге. Приведем пример создания "темного" каталога.

$ pwd

Где мы находимся?

/home/andrei

$ mkdir darkroom

Создадим каталог

$ ls -l

Получим его атрибуты

...

-rwxr--r-- 2 andy group 65 Dec 22 19:13 darkroom

$ chmod a-r+x darkroom

Превратим его в "темный" каталог

$ ls -l

Получим его атрибуты

...

--wx--x--x 2 andy group 65 Dec 22 19:13 darkroom

$ cp file1 darkroom

Поместим в каталог darkroom некоторый файл

$ cd darkroom

Перейдем в этот каталог

$ ls -l darkroom

Попытаемся получить листинг каталога

##permission denied

Увы...

$ cat file1

ok

Тем не менее, заранее зная имя файла (file1), можно работать с ним (например, прочитать, если есть соответствующее право доступа)

Особого внимания требует право на запись для каталога. Создание и удаление файлов в каталоге требуют изменения его содержимого, и, следовательно, права на запись в этот каталог. Самое важное, что при этом не учитываются права доступа для самого файла. То есть для того, чтобы удалить некоторый файл из каталога, не обязательно иметь какие-либо права доступа к этому файлу, важно лишь иметь право на запись для каталога, в котором находится этот файл. Имейте в виду, что право на запись в каталог дает большие полномочия, и предоставляйте это право с осторожностью. Правда, существует способ несколько обезопасить себя в случае, когда необходимо предоставить право на запись другим пользователям, — установка флага Sticky bit на каталог. Но об этом мы поговорим чуть позже.

В табл. 1.2 приведены примеры некоторых действий над файлами и минимальные права доступа, необходимые для выполнения этих операций.

Таблица 1.2. Примеры прав доступа

Команда Смысл действия Минимальные права доступа
для обычного файла для каталога, содержащего файл
cd /u/andrei Перейти в каталог /u/andrei - x
ls /u/andrei/*.с Вывести все файлы с суффиксом .c этого каталога - r
ls -s /u/andrei/*.с Вывести дополнительную информацию об этих файлах (размер) - rx
cat report.txt Вывести на экран содержимое файла report.txt r x
cat >> report.txt Добавить данные в файл report.txt w x
runme.sh Выполнить программу runme x x
runme Выполнить скрипт командного интерпретатора runme.sh rx x
rm runme Удалить файл runme в текущем каталоге - xw

Итак, для выполнения операции над файлом имеют значение класс доступа, к которому вы принадлежите, и права доступа, установленные для этого класса. Поскольку для каждого класса устанавливаются отдельные права доступа, всего определено 9 прав доступа, по 3 на каждый класс.

Операционная система производит проверку прав доступа при создании, открытии (для чтения или записи), запуске на выполнение или удалении файла. При этом выполняются следующие проверки:

1. Если операция запрашивается суперпользователем, доступ разрешается. Никакие дополнительные проверки не производятся. Это позволяет администратору иметь неограниченный доступ ко всей файловой системе.

2. Если операция запрашивается владельцем файла, то:

 а) если требуемое право доступа определено (например, при операции чтения файла установлено право на чтение для владельца- пользователя данного файла), доступ разрешается,

 б) в противном случае доступ запрещается.

3. Если операция запрашивается пользователем, являющимся членом группы, которая является владельцем файла, то:

 а) если требуемое право доступа определено, доступ разрешается,

 б) в противном случае доступ запрещается.

4. Если требуемое право доступа для прочих пользователей (other) установлено, доступ разрешается, в противном случае доступ запрещается.

Система проводит проверки в указанной последовательности. Например, если пользователь является владельцем файла, то доступ определяется исключительно из прав владельца-пользователя, права владельца-группы не проверяются, даже если пользователь является членом владельца-группы. Чтобы проиллюстрировать это, рассмотрим следующее:

----rw-r-- 2 andy group 65 Dec 22 19:13 file1

Даже если пользователь andy является членом группы group, он не сможет ни прочитать, ни изменить содержимое файла file1. В то же время все остальные члены этой группы имеют такую возможность. В данном случае, владелец файла обладает наименьшими правами доступа к нему. Разумеется, рассмотренная ситуация носит гипотетический характер, поскольку пользователь andy в любой момент может изменить права доступа к данному файлу как для себя (владельца), так и для группы, и всех остальных пользователей в системе.

 

Дополнительные атрибуты файла

Мы рассмотрели основные атрибуты, управляющие доступом к файлу. Существует еще несколько атрибутов, изменяющих стандартное выполнение различных операций. Как и в случае прав доступа, эти атрибуты по- разному интерпретируются для каталогов и других типов файлов.

Дополнительные атрибуты также устанавливаются утилитой chmod(1), но вместо кодов 'r', 'w' или 'x' используются коды из табл. 1.3. Например, для установки атрибута SGID для файла file1 необходимо выполнить команду $ chmod g+s file1.

В табл. 1.3 приведены дополнительные атрибуты для файлов, и показано, как они интерпретируются операционной системой.

Таблица 1.3. Дополнительные атрибуты для обычных файлов

Код Название Значение
t Sticky bit Сохранить образ выполняемого файла в памяти после завершения выполнения
s Set UID, SUID Установить UID процесса при выполнении
s Set GID, SGID Установить GID процесса при выполнении
1 Блокирование Установить обязательное блокирование файла

Установка атрибута Sticky bit (действительное название — save text mode) редко используется в современных версиях UNIX для файлов. В ранних версиях этот атрибут применялся с целью уменьшить время загрузки наиболее часто запускаемых программ (например, редактора или командного интерпретатора). После завершения выполнения задачи ее образ (т.е. код и данные) оставались в памяти, поэтому последующие запуски этой программы занимали значительно меньше времени.

Атрибуты (или флаги) SUID и SGID позволяют изменить права пользователя при запуске на выполнение файла, имеющего эти атрибуты. При этом привилегии будут изменены (обычно расширены) лишь на время выполнения и только в отношении этой программы.

Обычно запускаемая программа получает права доступа к системным ресурсам на основе прав доступа пользователя, запустившего программу. Установка флагов SUID и SGID изменяет это правило, назначая права доступа исходя из прав доступа владельца файла. Таким образом, запущенный исполняемый файл, которым владеет суперпользователь, получает неограниченные права доступа к системным ресурсам, независимо от того, кто его запустил. При этом установка SUID приведет к наследованию прав владельца-пользователя файла, а установка SGID — владельца-группы.

В качестве примера использования этого свойства рассмотрим утилиту passwd(1), позволяющую пользователю изменить свой пароль. Очевидно, что изменение пароля должно привести к изменению содержимого определенных системных файлов (файла пароля /etc/passwd или /etc/shadow, или базы данных пользователей, если используется дополнительная защита системы). Понятно, что предоставление права на запись в эти файлы всем пользователям системы является отнюдь не лучшим решением. Установка SUID для программы passwd(1) (точнее, на файл /usr/bin/passwd — исполняемый файл утилиты passwd(1)) позволяет изящно разрешить это противоречие. Поскольку владельцем файла /usr/bin/passwd является суперпользователь (его имя в системе — root), то кто бы ни запустил утилиту passwd(1) на выполнение, во время работы данной программы он временно получает права суперпользователя, т. е. может производить запись в системные файлы, защищенные от остальных пользователей.

$ ls -lFa /usr/bin/passwd

-r-sr-sr-x 3 root sys 15688 Oct 25 1995 /usr/bin/passwd*

Понятно, что требования по безопасности для такой программы должны быть повышены. Утилита passwd(1) должна производить изменение пароля только пользователя, запустившего ее, и не позволять никакие другие операции (например, вызов других программ).

Блокирование файлов позволяет устранить возможность конфликта, когда две или более задачи одновременно работают с одним и тем же файлом. К этому вопросу мы вернемся в главе 4.

Однако вернемся к обсуждению дополнительных атрибутов для каталогов (табл. 1.4).

Таблица 1.4. Дополнительные атрибуты для каталогов

Код Название Значение
t Sticky bit Позволяет пользователю удалять только файлы, которыми он владеет или имеет права на запись
s Set GID, SGID Позволяет изменить правило установки владельца- группы создаваемых файлов, аналогично реализованному в BSD UNIX

При обсуждении прав доступа отмечалось, что предоставление права на запись в каталог дает достаточно большие полномочия. Имея такое право, пользователь может удалить из каталога любой файл, даже тот, владельцем которого он не является и в отношении которого не имеет никаких прав. Установка атрибута Sticky bit для каталога позволяет установить дополнительную защиту файлов, находящихся в каталоге. Из такого каталога пользователь может удалить только файлы, которыми он владеет, или на которые он имеет явное право доступа на запись, даже при наличии права на запись в каталог. Примером может служить каталог /tmp, который является открытым на запись для всех пользователей, но в котором может оказаться нежелательной возможность удаления пользователем чужих временных файлов.

Атрибут SGID также имеет иное значение для каталогов. При установке этого атрибута для каталога вновь созданные файлы этого каталога будут наследовать владельца-группу по владельцу-группе каталога. Таким образом для UNIX версии System V удается имитировать поведение систем версии BSD, для которых такое правило наследования действует по умолчанию.

Посмотреть наличие дополнительных атрибутов можно с помощью подробного списка файлов:

$ ls -l

...

drwxrwxrwt 5 sys  sys    367 Dec 19 20:29 /tmp

-r-sr-sr-x 3 root root 15688 Oct 25 1995  /usr/bin/passwd

...

Таблица 1.5. Операции изменения атрибутов файла

Операция Команда/системный вызов Кому разрешено
Изменение прав доступа chmod(1) владелец
Изменение дополнительного атрибута Sticky bit chmod(1) суперпользователь
Изменение дополнительного атрибута SGID chmod(1) владелец, причем его GID также должен совпадать с идентификатором группы файла

 

Процессы

 

Процессы в операционной системе UNIX играют ключевую роль. От оптимальной настройки подсистемы управления процессами и числа одновременно выполняющихся процессов зависит загрузка ресурсов процессора, что в свою очередь имеет непосредственное влияние на производительность системы в целом. Ядро операционной системы предоставляет задачам базовый набор услуг, определяемый интерфейсом системных вызовов. К ним относятся основные операции по работе с файлами, управление процессами и памятью, поддержка межпроцессного взаимодействия. Дополнительные функциональные возможности системы, т.е. услуги, которые она предоставляет пользователям, определяются активными процессами. От того, какие процессы выполняются в вашей системе, зависит, является ли она сервером базы данных или сервером сетевого доступа, средством проектирования или вычислительным сервером. Даже так называемые уровни выполнения системы (run levels), которые мы рассмотрим позже, представляют собой удобный способ определения группы выполняющихся процессов и, соответственно, функциональности системы.

 

Программы и процессы

Обычно программой называют совокупность файлов, будь то набор исходных текстов, объектных файлов или собственно выполняемый файл. Для того чтобы программа могла быть запущена на выполнение, операционная система сначала должна создать окружение или среду выполнения задачи, куда относятся ресурсы памяти, возможность доступа к устройствам ввода/вывода и различным системным ресурсам, включая услуги ядра.

Это окружение (среда выполнения задачи) получило название процесса. Мы можем представить процесс как совокупность данных ядра системы, необходимых для описания образа программы в памяти и управления ее выполнением. Мы можем также представить процесс как программу в стадии ее выполнения, поскольку все выполняющиеся программы представлены в UNIX в виде процессов. Процесс состоит из инструкций, выполняемых процессором, данных и информации о выполняемой задаче, такой как размещенная память, открытые файлы и статус процесса.

В то же время не следует отождествлять процесс с программой хотя бы потому, что программа может породить более одного процесса. Простейшие программы, например, команда who(1) или cat(1), при выполнении представлены только одним процессом. Сложные задачи, например системные серверы (печати, FTP, Telnet), порождают в системе несколько одновременно выполняющихся процессов.

Операционная система UNIX является многозадачной. Это значит, что одновременно может выполняться несколько процессов, причем часть процессов могут являться образцами одной программы.

Выполнение процесса заключается в точном следовании набору инструкций, который никогда не передает управление набору инструкций другого процесса. Процесс считывает и записывает информацию в раздел данных и в стек, но ему недоступны данные и стеки других процессов.

В то же время процессы имеют возможность обмениваться друг с другом данными с помощью предоставляемой UNIX системой межпроцессного взаимодействия. В UNIX существует набор средств взаимодействия между процессами, таких как сигналы (signals), каналы (pipes), разделяемая память (shared memory), семафоры (semaphores), сообщения (messages) и файлы, но в остальном процессы изолированы друг от друга.

 

Типы процессов

 

Системные процессы

Системные процессы являются частью ядра и всегда расположены в оперативной памяти. Системные процессы не имеют соответствующих им программ в виде исполняемых файлов и запускаются особым образом при инициализации ядра системы. Выполняемые инструкции и данные этих процессов находятся в ядре системы, таким образом они могут вызывать функции и обращаться к данным, недоступным для остальных процессов. Системными процессами являются: shed (диспетчер свопинга), vhand (диспетчер страничного замещения), bdfflush (диспетчер буферного кэша) и kmadaemon (диспетчер памяти ядра). К системным процессам следует отнести ink, являющийся прародителем всех остальных процессов в UNIX. Хотя init не является частью ядра, и его запуск происходит из исполняемого файла (/etc/init), его работа жизненно важна для функционирования всей системы в целом.

 

Демоны

Демоны — это неинтерактивные процессы, которые запускаются обычным образом — путем загрузки в память соответствующих им программ (исполняемых файлов), и выполняются в фоновом режиме. Обычно демоны запускаются при инициализации системы (но после инициализации ядра, подробнее см. главу 3) и обеспечивают работу различных подсистем UNIX: системы терминального доступа, системы печати, системы сетевого доступа и сетевых услуг и т.п. Демоны не связаны ни с одним пользовательским сеансом работы и не могут непосредственно управляться пользователем. Большую часть времени демоны ожидают пока тот или иной процесс запросит определенную услугу, например, доступ к файловому архиву или печать документа.

 

Прикладные процессы

К прикладным процессам относятся все остальные процессы, выполняющиеся в системе. Как правило, это процессы, порожденные в рамках пользовательского сеанса работы. С такими процессами вы будете сталкиваться чаще всего. Например, запуск команды ls(1) породит соответствующий процесс этого типа. Важнейшим пользовательским процессом является основной командный интерпретатор (login shell), который обеспечивает вашу работу в UNIX. Он запускается сразу же после вашей регистрации в системе, а завершение работы login shell приводит к отключению от системы.

Пользовательские процессы могут выполняться как в интерактивном, так и в фоновом режиме, но в любом случае время их жизни (и выполнения) ограничено сеансом работы пользователя. При выходе из системы все пользовательские процессы будут уничтожены.

Интерактивные процессы монопольно владеют терминалом, и пока такой процесс не завершит свое выполнение, пользователь не сможет работать с другими приложениями.

 

Атрибуты процесса

 

Процесс в UNIX имеет несколько атрибутов, позволяющих операционной системе эффективно управлять его работой, важнейшие из которых рассмотрены ниже.

 

Идентификатор процесса Process ID (PID)

Каждый процесс имеет уникальный идентификатор PID, позволяющий ядру системы различать процессы. Когда создается новый процесс, ядро присваивает ему следующий свободный (т. е. не ассоциированный ни с каким процессом) идентификатор. Присвоение идентификаторов происходит по возрастающий, т.е. идентификатор нового процесса больше, чем идентификатор процесса, созданного перед ним. Если идентификатор достиг максимального значения, следующий процесс получит минимальный свободный PID и цикл повторяется. Когда процесс завершает свою работу, ядро освобождает занятый им идентификатор.

 

Идентификатор родительского процесса Parent Process ID (PPID)

Идентификатор процесса, породившего данный процесс.

 

Приоритет процесса (Nice Number)

Относительный приоритет процесса, учитываемый планировщиком при определении очередности запуска. Фактическое же распределение процессорных ресурсов определяется приоритетом выполнения, зависящим от нескольких факторов, в частности от заданного относительного приоритета. Относительный приоритет не изменяется системой на всем протяжении жизни процесса (хотя может быть изменен пользователем или администратором) в отличие от приоритета выполнения, динамически обновляемого ядром.

 

Терминальная линия (TTY)

Терминал или псевдотерминал, ассоциированный с процессом, если такой существует. Процессы-демоны не имеют ассоциированного терминала.

 

Реальный (RID) и эффективный (EUID) идентификаторы пользователя

Реальным идентификатором пользователя данного процесса является идентификатор пользователя, запустившего процесс. Эффективный идентификатор служит для определения прав доступа процесса к системным ресурсам (в первую очередь к ресурсам файловой системы). Обычно реальный и эффективный идентификаторы эквивалентны, т.е. процесс имеет в системе те же права, что и пользователь, запустивший его. Однако существует возможность задать процессу более широкие права, чем права пользователя путем установки флага SUID, когда эффективному идентификатору присваивается значение идентификатора владельца исполняемого файла (например, администратора).

 

Реальный (RGID) и эффективный (EGID) идентификаторы группы

Реальный идентификатор группы равен идентификатору первичной или текущей группы пользователя, запустившего процесс. Эффективный идентификатор служит для определения прав доступа к системным ресурсам по классу доступа группы. Так же как и для эффективного идентификатора пользователя, возможна его установка равным идентификатору группы владельца исполняемого файла (флаг SGID).

Команда ps(1) (process status) позволяет вывести список процессов, выполняющихся в системе, и их атрибуты:

$ ps -ef | head -20

UID  PID  PPID С STIME  TTY   TIME CMD

root 0    0    0 Dec 17 ?     0:00 sched

root 1    0    0 Dec 17 ?     0:01 /etc/init -

root 2    0    0 Dec 17 ?     0:00 pageout

root 3    0    0 Dec 17 ?     7:00 fsflush

root 164  1    0 Dec 17 ?     0:01 /usr/lib/sendmail -bd -q1h

fed  627  311  0 Dec 17 pts/3 0:27 emiclock

fed  314  304  0 Dec 17 pts/4 0:00 /usr/local/bin/bash

fed  3521 512  0              0:01

Более подробное описание полей вывода команды ps(1) приведено далее в разделе "Основные утилиты UNIX".

 

Жизненный путь процесса

Процесс в UNIX создается системным вызовом fork(2). Процесс, сделавший вызов fork(2) называется родительским, а вновь созданный процесс — дочерним. Новый процесс является точной копией породившего его процесса. Как это ни удивительно, но новый процесс имеет те же инструкции и данные, что и его родитель. Более того, выполнение родительского и дочернего процесса начнется с одной и той же инструкции, следующей за вызовом fork(2). Единственно, чем они различаются — это идентификатором процесса PID. Каждый процесс имеет одного родителя, но может иметь несколько дочерних процессов.

Для запуска задачи, т.е. для загрузки новой программы, процесс должен выполнить системный вызов exec(2). При этом новый процесс не порождается, а исполняемый код процесса полностью замещается кодом запускаемой программы. Тем не менее окружение новой программы во многом сохраняется, в частности сохраняются значения переменных окружения, назначения стандартных потоков ввода/вывода, вывода сообщений об ошибках, а также приоритет процесса.

В UNIX запуск на выполнение новой программы часто связан с порождением нового процесса, таким образом сначала процесс выполняет вызов fork(2), порождая дочерний процесс, который затем выполняет exec(2), полностью замещаясь новой программой.

Рассмотрим эту схему на примере.

Допустим, пользователь, работая в командном режиме (в командном интерпретаторе shell) запускает команду ls(1). Текущий процесс (shell) делает вызов fork(2), порождая вторую копию shell. В свою очередь, порожденный shell вызывает exec(2), указывая в качестве параметра имя исполняемого файла, образ которого необходимо загрузить в память вместо кода shell. Код ls(1) замещает код порожденного shell, и утилита ls(1) начинает выполняться. По завершении работы ls(1) созданный процесс "умирает". Пользователь вновь возвращается в командный режим. Описанный процесс представлен на рис. 1.5. Мы также проиллюстрируем работу командного интерпретатора в примере, приведенном в главе 2.

Рис. 1.5. Создание процесса и запуск программы

Если сделать "отпечаток" выполняемых процессов, например командой ps(1), между указанными стадиями, результат был бы следующим:

Пользователь работает в командном режиме:

UID   PID PPID С  STIME    TTY   TIME CMD

user1 745 1    10 10:11:34 ttyp4 0:01 sh

Пользователь запустил команду ls(1), и shell произвел вызов fork(2):

UID   PID PPID С  STIME    TTY   TIME CMD

user1 745 1    10 10:11:34 ttyp4 0:01 sh

user1 802 745  14 11:00:00 ttyp4 0:00 sh

Порожденный shell произвел вызов exec(2):

UID   PID PPID С  STIME    TTY   TIME CMD

user1 745 1    10 10:11:34 ttyp4 0:01 sh

user1 802 745  12 11:00:00 ttyp4 0:00 ls

Процесс ls(1) завершил работу:

UID   PID PPID С  STIME    TTY   TIME CMD

user1 745 1    10 10:11:34 ttyp4 0:01 sh

Описанная процедура запуска новой программы называется fork-and-exec.

Однако бывают ситуации, когда достаточно одного вызова fork(2) без последующего exec(2). В этом случае исполняемый код родительского процесса должен содержать логическое ветвление для родительского и дочернего процессов.

Все процессы в UNIX создаются посредством вызова fork(2). Запуск на выполнение новых задач осуществляется либо по схеме fork-and-exec, либо с помощью exec(2). "Прародителем" всех процессов является процесс init(1М), называемый также распределителем процессов. Если построить граф "родственных отношений" между процессами, то получится дерево, корнем которого является init(1M). Показанные на рис. 1.6 процессы sched и vhand являются системными и формально не входят в иерархию (они будут рассматриваться в следующих главах).

Рис. 1.6. Типичное "дерево" процессов в UNIX

 

Сигналы

Сигналы являются способом передачи от одного процесса другому или от ядра операционной системы какому-либо процессу уведомления о возникновении определенного события. Сигналы можно рассматривать как простейшую форму межпроцессного взаимодействия. В то же время сигналы больше напоминают программные прерывания, — средство, с помощью которого нормальное выполнение процесса может быть прервано. Например, если процесс производит деление на 0, ядро посылает ему сигнал SIGFPE, а при нажатии клавиш прерывания, обычно или +, текущему процессу посылается сигнал SIGINT.

Для отправления сигнала служит команда kill(1):

kill sig_no pid

где sig_nо — номер или символическое название сигнала, a pid — идентификатор процесса, которому посылается сигнал. Администратор системы может посылать сигналы любым процессам, обычный же пользователь может посылать сигналы только процессам, владельцем которых он является (реальный и эффективный идентификаторы процесса должны совпадать с идентификатором пользователя). Например, чтобы послать процессу, который вы только что запустили в фоновом режиме, сигнал завершения выполнения SIGTERM, можно воспользоваться командой:

$ long_program &

Запустим программу в фоновом режиме

$ kill $!

По умолчанию команда kill(1) посылает сигнал SIGTERM; переменная $! содержит PID последнего процесса, запущенного в фоновом режиме

При получении сигнала процесс имеет три варианта действий для выбора:

1. Он может игнорировать сигнал. Не следует игнорировать сигналы, вызванные аппаратной частью, например, при делении на 0 или ссылке на недопустимые области памяти, так как дальнейшие результаты в отношении данного процесса непредсказуемы.

2. Процесс может потребовать действия по умолчанию. Как ни печально, обычно это сводится к завершению выполнения процесса.

3. Наконец, процесс может перехватить сигнал и самостоятельно обработать его. Например, перехват сигнала SIGINT позволит процессу удалить созданные им временные файлы, короче, достойно подготовиться к "смерти". Следует иметь в виду, что сигналы SIGKILL и SIGSTOP нельзя ни перехватить, ни игнорировать.

По умолчанию команда kill(1) посылает сигнал с номером 15 — SIGTERM, действие по умолчанию для которого — завершение выполнения процесса, получившего сигнал.

Иногда процесс продолжает существовать и после отправления сигнала SIGTERM. В этом случае можно применить более жесткое средство — послать процессу сигнал SIGKILL с номером (9), — поскольку этот сигнал нельзя ни перехватить, ни игнорировать:

$ kill -9 pid

Однако возможны ситуации, когда процесс не исчезает и в этом случае. Это может произойти для следующих процессов:

□ Процессы-зомби. Фактически процесса как такового не существует, осталась лишь запись в системной таблице процессов, поэтому удалить его можно только перезапуском операционной системы. Зомби в небольших количествах не представляют опасности, однако если их много, это может привести к переполнению таблицы процессов.

□ Процессы, ожидающие недоступные ресурсы NFS (Network File System), например, записывающие данные в файл файловой системы удаленного компьютера, отключившегося от сети. Эту ситуацию можно преодолеть, послав процессу сигнал SIGINT или SIGQUIT.

□ Процессы, ожидающие завершения операции с устройством, например, перемотки магнитной ленты.

Сигналы могут не только использоваться для завершения выполнения но и иметь специфическое для приложения (обычно для системных демонов) значение (естественно, это не относится к сигналам SIGKILL и SIGSTOP). Например, отправление сигнала SIGHUP серверу имен DNS named(1M) вызовет считывание базы данных с диска. Для других приложений могут быть определены другие сигналы и соответствующие им значения.

Более подробно сигналы мы рассмотрим в главах 2 и 3.

 

Устройства

 

Как уже отмечалось, UNIX "изолирует" приложения (а значит и пользователя) от аппаратной части вычислительной системы. Например, в имени файла отсутствует указатель диска, на котором этот файл расположен, а большая часть взаимодействия с периферийными устройствами неотличима от операций с обычными файлами.

UNIX предоставляет единый интерфейс различных устройств системы в виде специальных файлов устройств. Специальный файл устройства связывает прикладное приложение с драйвером устройства. Каждый специальный файл соответствует какому-либо физическому устройству (например, диску, накопителю на магнитной ленте, принтеру или терминалу) или т.н. псевдоустройству (например, сетевому интерфейсу, пустому устройству, сокету или памяти). Вся работа приложения с устройством происходит через специальный файл, а соответствующий ему драйвер обеспечивает выполнение операций ввода/вывода в соответствии с конкретным протоколом обмена данными с устройством.

Существует два типа специальных файлов устройств:

□ Файлы блочных устройств

□ Файлы символьных устройств

 

Файлы блочных устройств

Файлы блочных устройств служат интерфейсом к устройствам, обмен данными с которыми происходит большими фрагментами, называемыми блоками. При этом ядро операционной системы обеспечивает необходимую буферизацию. Примером физических устройств, соответствующих этому типу файлов, являются жесткие диски. Приведем фрагмент подробного списка файлов каталога /dev системы Digital UNIX, отражающий файлы для доступа к первому и второму разделам первого диска SCSI:

brw------- 1 root system 8, 1 Apr 18 11:03 /dev/rz0a

brw------- 1 root system 8, 1 Apr 18 13:15 /dev/rz0b

 

Файлы символьных устройств

Файлы символьных устройств используются для доступа к устройствам, драйверы которых обеспечивают собственную буферизацию и побайтную передачу данных. В качестве примера устройств с символьным интерфейсом можно привести терминалы, принтеры и накопители на магнитной ленте. Заметим, что одно и то же физическое устройство может иметь как блочный, так и символьный интерфейсы. Для блочных устройств такой интерфейс также называют интерфейсом доступа низкого уровня (raw interface). Так, для побайтного доступа к разделам диска, приведенным в предыдущем примере, используются соответствующие файлы:

crw------- 1 root system   8, 1 Apr 18 11:04 /dev/rrz0a

crw------- 1 root system   8, 1 Apr 18 13:15 /dev/rrz0b

crw-r----- 1 root system   13,1 Apr 18 18:08 /dev/kmem

crw-rw-rw- 1 root system   7, 0 Apr 18 15:30 /dev/ptyp0

crw-rw-rw- 1 root system   7, 1 Apr 18 15:20 /dev/ptyp1

crw-rw-rw- 1 bill terminal 3, 2 Apr 18 16:10 /dev/tty02

crw-rw-rw- 1 bin  terminal 3, 3 Apr 18 16:10 /dev/tty03

Последние три строки списка представляют интерфейс доступа к виртуальной памяти ядра и двум псевдотерминалам.

В поле размера файла (пятая колонка вывода команды ls(1)) у специальных файлов устройств выводятся два числа. Это так называемые старшее (major) и младшее (minor) числа. Часто драйвер обслуживает более одного устройства. При этом старшее число указывает ядру на конкретный драйвер (например, драйвер псевдо-терминалов), а младшее передается драйверу и указывает на конкретное устройство (например, конкретный псевдотерминал).

Интерфейс файловой системы для взаимодействия с устройством схематически представлен на рис. 1.7.

Рис. 1.7. Взаимодействие с устройством

 

Мнемоника названий специальных файлов устройств в файловой системе UNIX

Названия специальных файлов устройств в большой степени зависят от конкретной версии UNIX. Тем не менее в этих названиях присутствует общая логика, позволяющая даже в незнакомой системе определить, какие файлы отвечают за конкретные устройства. Например, имена файлов доступа к дисковым устройствам обычно содержат указание на тип диска, номер контроллера, логический номер устройства, раздел диска и т.д. По названию также легко определить, какой вид доступа предоставляет данный интерфейс (блочный или символьный).

В качестве примера рассмотрим специальный файл устройства для доступа к разделу диска в операционной системе Solaris:

/dev/dsk/c0t4d0s2

Данный файл предоставляет блочный интерфейс, а соответствующий ему символьный (или необработанный) файл имеет имя:

/dev/rdsk/c0t4d0s2

Файлы доступа к дисковым устройствам располагаются в специальных подкаталогах — /dev/dsk (для блочных устройств) и /dev/rdsk (для символьных устройств). Такая структура хранения специальных файлов характерна для систем UNIX версии System V.

Имя файла, характерное для систем версии SVR4, можно представить в общем виде:

c k t l d m s n

где k — номер контроллера, l — номер устройства (для устройств SCSI это идентификатор устройства ID), m — номер раздела, а n  — логический номер устройства (LUN) SCSI.

Таким образом файл устройства /dev/rdsk/c0t4d0s2 обеспечивает доступ к первому разделу (нумерация разделов начинается с 0) диска с ID=4, LUN=2 первого контроллера.

Такой формат имен файлов в версии SVR4 применяется для всех дисковых устройств и накопителей на магнитной ленте. Иногда для этих стандартных имен в файловой системе имеются символические связи с более простыми названиями. Например, в Solaris имя /dev/sd0a может использоваться вместо /dev/dsk/c0t3d0s, также обеспечивая доступ к устройству:

lrwxrwxrwx 1 root root 12 Oct 31 17:48 /dev/sd0a ->dsk/c0t3d0s

В SCO UNIX имеются специальные файлы с более простыми именами /dev/root, /dev/usr и т.п, которые предоставляют доступ к разделам диска с такими же именами (root, usr).

Более простая мнемоника обнаруживается в именах специальных файлов других устройств. Так, например, параллельный порт в большинстве систем имеет имя /dev/lpn, где n — номер порта (0, 1 и т.д.). Терминальные линии, подключенные к последовательным портам компьютера обозначаются как /dev/ttynn, где nn является идентификатором линии. В табл. 1.6 приведены примеры других специальных файлов устройств.

Таблица 1.6. Имена некоторых специальных файлов устройств

Общий вид имени Пример Описание устройства, доступ к которому обеспечивается через файл
/dev/rmtn /dev/rmt0 Накопитель на магнитной ленте
/dev/nrmtn /dev/nrmt0 Накопитель на магнитной ленте в режиме без перемотки назад по окончании работы
/dev/rstn /dev/rst1 SCSI-накопитель на магнитной ленте
/dev/cdn /dev/cd0 CD-ROM
/dev/cdrom
/dev/ttypn /dev/ttyp12 Псевдотерминал (подчиненный)
/dev/ptypn /dev/ptyp5 Псевдотерминал (мастер)
/dev/console Системная консоль
/dev/tty Синоним терминальной линии управляющего терминала для данного процесса
/dev/mem Физическая оперативная память
/dev/kmem Виртуальная память ядра
/dev/null Нулевое устройство — весь вывод на него уничтожается, а при попытке ввода с этого устройства возвращается 0 байтов
/dev/zero Нулевое устройство — весь вывод на него уничтожается, а ввод приводит к получению последовательности 0

 

Пользователи системы

 

Прежде чем вы сможете начать работу в UNIX, вы должны стать пользователем системы, т.е. получить имя, пароль и ряд других атрибутов.

С точки зрения системы, пользователь — не обязательно человек. Пользователь является объектом, который обладает определенными правами, может запускать на выполнение программы и владеть файлами. В качестве пользователей могут, например, выступать удаленные компьютеры или группы пользователей с одинаковыми правами и функциями. Такие пользователи называются псевдопользователями. Они обладают правами на определенные файлы системы и от их имени запускаются задачи, обеспечивающие ту или иную функциональность UNIX.

Как правило, большинство пользователей являются реальными людьми, которые регистрируются в системе, запускают те или иные программы, короче говоря, используют UNIX в своей работе.

В системе существует один пользователь, обладающий неограниченными правами. Это суперпользователь или администратор системы.

Каждый пользователь системы имеет уникальное имя (или регистрационное имя — login name). Однако система различает пользователей по ассоциированному с именем идентификатору пользователя или UID (User Identifier). Понятно, что идентификаторы пользователя также должны быть уникальными. Пользователь является членом одной или нескольких групп — списков пользователей, имеющих сходные задачи (например пользователей, работающих над одним проектом). Принадлежность к группе определяет дополнительные права, которыми обладают все пользователи группы. Каждая группа имеет уникальное имя (уникальное среди имен групп, имя группы и пользователя могут совпадать), но как и для пользователя, внутренним представлением группы является ее идентификатор GID (Group Identifier). В конечном счете UID и GID определяют, какими правами обладает пользователь в системе.

Вся информация о пользователях хранится в файле /etc/passwd. Это обычный текстовый файл, право на чтение которого имеют все пользователи системы, а право на запись имеет только администратор (суперпользователь). В этом файле хранятся пароли пользователей, правда в зашифрованном виде. Подобная открытость — недостаток с точки зрения безопасности, поэтому во многих системах зашифрованные пароли хранятся в отдельном закрытом для чтения и записи файле /etc/shadow.

Аналогично, информация о группах хранится в файле /etc/group и содержит списки пользователей, принадлежащих той или иной группе.

 

Атрибуты пользователя

Как правило, все атрибуты пользователя хранятся в файле /etc/passwd. В конечном итоге, добавление пользователя в систему сводится к внесению в файл /etc/passwd соответствующей записи. Однако во многих системах информация о пользователе хранится и в других местах (например, в специальных базах данных), поэтому создание пользователя простым редактированием файла /etc/passwd может привести к неправильной регистрации пользователя, а иногда и к нарушениям работы системы. Вместо этого при возможности следует пользоваться специальными утилитами, поставляемыми с системой. Более подробно мы поговорим об этом при обсуждении задач администрирования UNIX в конце этой главы.

Сейчас же наша задача — разобраться, какую информацию хранит система о пользователе. Для этого рассмотрим фрагмент файла /etc/passwd:

root:x:0:0000-Admin(0000):/:bin/bash

daemon:x:1:1:0000-Admin(0000):/:

bin:x:2:2:0000-Admin(0000):/usr/bin:

sys:x:3:3:0000-Admin(0000):/:

adm:x:4:4:0000-Admin(0000):/var/adm

lp:x:71:8:0000-lp(0000):/usr/spool/lp:

uucp:x:5:5:0000-uucp(0000):/usr/lib/uucp:

nobody:x:60001:60001:uid no body:/:

andy:x:206:101:Andrei Robachevsky:/home/andy:/bin/bash

Каждая строка файла является записью конкретного пользователя и имеет следующий формат:

name : passwd-encod : UID : GID : comments : home-dir : shell

— всего семь полей (атрибутов), разделенных двоеточиями.

Рассмотрим подробнее каждый из атрибутов:

name Регистрационное имя пользователя. Это имя пользователь вводит в ответ на приглашение системы login. Для небольших систем имя пользователя достаточно произвольно. В больших системах, в которых зарегистрированы сотни пользователей, требования уникальности заставляют применять определенные правила выбора имен.
passwd-encod Пароль пользователя в закодированном виде. Алгоритмы кодирования известны, но они не позволяют декодировать пароль. При входе в систему пароль, который вы набираете, кодируется, и результат сравнивается с полем passwd-encod . В случае совпадения пользователю разрешается войти в систему. Даже в закодированном виде доступность пароля представляет некоторую угрозу для безопасности системы. Поэтому часто пароль хранят в отдельном файле, а в поле passwd-encod ставится символ 'х' (в некоторых системах '!'). Пользователь, в данном поле которого стоит символ '*', никогда не сможет попасть в систему. Дело в том, что алгоритм кодирования не позволяет символу '*' появиться в закодированной строке. Таким образом, совпадение введенного и затем закодированного пароля и '*' невозможно. Обычно такой пароль имеют псевдопользователи.
UID Идентификатор пользователя является внутренним представлением пользователя в системе. Этот идентификатор наследуется задачами, которые запускает пользователь, и файлами, которые он создает. По этому идентификатору система проверяет пользовательские права (например, при запуске программы или чтении файла). Суперпользователь имеет UID=0, что дает ему неограниченные права в системе.
GID Определяет идентификатор первичной группы пользователя . Этот идентификатор соответствует идентификатору в файле /etc/group , который содержит имя группы и полный список пользователей, являющихся ее членами. Принадлежность пользователя к группе определяет дополнительные права в системе. Группа определяет общие для всех членов права доступа и тем самым обеспечивает возможность совместной работы (например, совместного использования файлов).
comments Обычно, это полное "реальное" имя пользователя. Это поле может содержать дополнительную информацию, например, телефон или адрес электронной почты. Некоторые программы (например, finger(1) и почтовые системы) используют это поле.
home-dir Домашний каталог пользователя. При входе в систему пользователь оказывается в этом каталоге. Как правило, пользователь имеет ограниченные права в других частях файловой системы, но домашний каталог и его подкаталоги определяют область файловой системы, где он является полноправным хозяином.
shell Имя программы, которую UNIX использует в качестве командного интерпретатора. При входе пользователя в систему UNIX автоматически запустит указанную программу. Обычно это один из стандартных командных интерпретаторов /bin/sh (Bourne shell), /bin/csh (С shell) или /bin/ksh (Korn shell), позволяющих пользователю вводить команды и запускать задачи. В принципе, в этом поле может быть указана любая программа, например, командный интерпретатор с ограниченными функциями (restricted shell), клиент системы управления базой данных или даже редактор. Важно то, что, завершив выполнение этой задачи, пользователь автоматически выйдет из системы. Некоторые системы имеют файл /etc/shells , содержащий список программ, которые могут быть использованы в качестве командного интерпретатора.

 

Пароли

Наличие пароля позволяет защитить ваши данные, а возможно (если вы — суперпользователь) и всю систему в целом. Уточним: наличие хорошего пароля, потому что неверно выбранный пароль — серьезная брешь в безопасности системы. Поэтому мы более подробно остановимся на основных рекомендациях по выбору пароля.

Назначить или изменить пароль можно командой passwd(1). Обычный пользователь может изменить свой пароль, администратор может назначить пароль любому пользователю.

Перед запуском программы passwd(1) стоит держать в голове общее правило выбора пароля: пароль должен хорошо запоминаться и быть трудным для подбора.

Не рекомендуется записывать пароль, его необходимо запомнить. Собственная фамилия, кличка любимой собаки, год и месяц рождения, безусловно, легки для запоминания, но такие пароли нетрудно подобрать. Многие системы предлагают пароль, сгенерированный самой системой. Предполагается, что он совершенно лишен какого-либо смысла, т.е. не содержит имен, названий и вообще каких-либо произносимых слов. Хотя система предлагает его в виде, удобном для запоминания, это не всегда помогает.

Если по правилам работы в вашей системе можно самостоятельно выбрать пароль, постарайтесь подобрать что-нибудь, что легче будет запомнить. Никогда не используйте примеры паролей, приводимые в книгах и руководствах (чтобы не было искушения, в этой книге примеры не приводятся).

Многие системы требуют, чтобы пароль удовлетворял следующим требованиям:

□ длина пароля не должна быть меньше шести символов;

□ пароль должен включать по крайней мере 2 алфавитных символа и одну цифру или специальный символ;

□ пароль должен содержать хотя бы 3 символа, не встречавшихся в вашем предыдущем пароле.

Пароли играют значительную роль в обеспечении безопасности системы. Общие рекомендации, адресованные прежде всего администраторам, можно свести к следующим:

1. В системе не должно существовать незащищенных пользовательских входов. Это относится как к пользовательским входам без пароля, так и ко входам пользователей, покинувших систему. Если пользователь длительное время не работает в системе, удалите его запись или хотя бы защитите его вход символом '*' в поле пароля.

2. Если ваша система допускает, установите минимальную длину пароля. В зависимости от требований безопасности в системе это число может варьироваться от 8 до 12.

3. Всегда меняйте пароль в следующих случаях:

 • если кто-либо узнал ваш пароль.

 • если пользователь больше не работает в вашей системе, все пароли, которые он знал, должны быть изменены.

 • если меняется администратор системы, должны быть изменены все системные пароли.

 • если у вас появилось подозрение, что файл паролей был считан по сети, будет разумным сменить все пароли в системе.

4. Пароль администратора должен периодически меняться, независимо от обстоятельств.

5. Это может показаться странным, но не стоит заставлять пользователей менять пароли чересчур часто. Скорее всего, в этом случае пользователь выберет не лучший пароль. Но менять пароли все же следует. Частота смены зависит от степени доступности вашей системы (изолированная станция, сервер с сетевым доступом, наличие сетевых экранов).

Не преуменьшайте роль паролей в системе.

 

Стандартные пользователи и группы

После установки UNIX обычно уже содержит несколько зарегистрированных пользователей. Перечислим основные из них (в разных версиях системы UID этих пользователей могут незначительно отличаться):

Имя Пользователь
root Суперпользователь, администратор системы, UID=0. Пользователь с этим именем имеет неограниченные полномочия в системе. Для него не проверяются права доступа, и таким образом он имеет все "рычаги" для управления системой. Для выполнения большинства функций администрирования требуется вход именно с этим именем. Следует отметить, что root — это только имя. На самом деле значение имеет UID. Любой пользователь с UID=0 имеет полномочия суперпользователя
adm Псевдопользователь, владеющий файлами системы ведения журналов
bin Обычно это владелец всех исполняемых файлов, являющихся командами UNIX
cron Псевдопользователь, владеющий соответствующими файлами, от имени которого выполняются процессы подсистемы запуска программ по расписанию
lp или lpd Псевдопользователь, от имени которого выполняются процессы системы печати, владеющий соответствующими файлами
news Псевдопользователь, от имени которого выполняются процессы системы телеконференций
nobody Псевдопользователь, используемый в работе NFS
uucp Псевдопользователь подсистемы UNIX-to-UNIX copy (uucp), позволяющей передавать почтовые сообщения и файлы между UNIX-хостами

Новая система также содержит ряд предустановленных групп. Поскольку группы, как правило, менее значимы, приведем лишь две категории:

Имя Группа
root или wheel Административная группа, GID=0
user или users или staff Группа, в которую по умолчанию включаются все обычные пользователи UNIX

 

Пользовательская среда UNIX

 

Сегодня характер работы в UNIX существенно отличается от того, каким он был, скажем, пятнадцать лет назад. Графический многооконный интерфейс, миллионы цветов, системы меню, техника drag-and-drop, — все это, казалось бы, стирает различия в работе с UNIX и, например, с Windows NT. Но взгляните внимательнее на экран монитора — и вы обязательно найдете хотя бы одно окно простого алфавитно-цифрового терминала.

Это — базовая пользовательская среда. Интерфейс командной строки может показаться безнадежно устаревшим, но в случае с UNIX это — самый непосредственный способ выполнения множества небольших задач администрирования. И программа, с которой вы рано или поздно столкнетесь, — командный интерпретатор shell. Поэтому здесь мы рассмотрим базовый пример работы в UNIX — использование командной строки интерпретатора shell.

 

Командный интерпретатор shell

Все современные системы UNIX поставляются по крайней мере с тремя командными интерпретаторами: Bourne shell (/bin/sh), С shell (/bin/csh) и Korn shell (/bin/ksh). Существует еще несколько интерпретаторов, например Bourne-Again shell (bash), со сходными функциями.

Командный интерпретатор занимает важное место в операционной системе UNIX, прежде всего, благодаря следующим обстоятельствам:

1. Первая программа, с которой по существу начинается работа пользователя, — shell. В UNIX реализуется следующий сценарий работы в системе (рис. 1.8):

 • При включении терминала активизируется процесс getty(1M), который является сервером терминального доступа и запускает программу login(1), которая, в свою очередь, запрашивает у пользователя имя и пароль.

 • Если пользователь зарегистрирован в системе и ввел правильный пароль, login(1) запускает программу, указанную в последнем поле записи пользователя в файле /etc/passwd. В принципе это может быть любая программа, но в нашем случае — это командный интерпретатор shell.

 • Shell выполняет соответствующий командный файл инициализации, и выдает на терминал пользователя приглашение. С этого момента пользователь может вводить команды.

 • Shell считывает ввод пользователя, производит синтаксический анализ введенной строки, подстановку шаблонов и выполняет действие, предписанное пользователем (это может быть запуск программы, выполнение внутренней функции интерпретатора) или сообщает об ошибке, если программа или функция не найдены.

• По окончании работы пользователь завершает работу с интерпретатором, вводя команду exit, и выходит из системы.

Рис. 1.8. Процессы, обеспечивающие вход пользователя в систему

2. Командный интерпретатор является удобным средством программирования. Синтаксис языка различных командных интерпретаторов несколько отличается, в качестве базового мы рассмотрим командный интерпретатор Bourne. С помощью shell вы можете создавать сложные программы, конструируя их, как из кирпичиков, из существующих утилит UNIX. Программы на языке shell часто называют скриптами или сценариями (script). Интерпретатор считывает строки из файла- скрипта и выполняет их, как если бы они были введены пользователем в командной строке.

3. Как уже упоминалось, при входе пользователя в систему запускается его инициализационный скрипт, выполняющий несколько функций: установку пути поиска программ, инициализацию терминала, определение расположения почтового ящика. Помимо этого может быть выполнен целый ряд полезных действий, — например, установка приглашения. Скорее всего вам придется "покопаться" в этом скрипте, по крайней мере, чтобы добавить необходимые пути поиска. Инициализационный скрипт находится в домашнем каталоге пользователя.

Для разных командных интерпретаторов используются различные скрипты инициализации:

Командный интерпретатор Скрипт инициализации
Bourne shell (sh) .profile
С shell (csh) .login и .cshrc
Korn shell (ksh) .profile и .kshrc
Bourne-Again shell (bash) .profile и .bashrc

Скрипты .profile и .login выполняются при первом входе в систему.

Скрипты .cshrc, .kshrc и .bashrc выполняются при каждом запуске интерпретатора.

4. Наконец, основная инициализация операционной системы происходит в результате выполнения скриптов shell. Если вам понадобится модифицировать процесс инициализации (например, добавить новый системный сервис), то придется заглянуть в эти скрипты.

 

Синтаксис языка Bourne shell

 

Любой из стандартных командных интерпретаторов имеет развитый язык программирования, позволяющий создавать командные файлы, или скрипты, для выполнения достаточно сложных задач. Следует, однако, иметь в виду, что shell является интерпретатором, он последовательно считывает команды из скрипта и выполняет их, как если бы они последовательно вводились пользователем с терминала. При таком характере работы трудно ожидать большой производительности от скриптов, однако их эффективность определяется простотой и наглядностью. Если же производительность программы играет главную роль, то самым эффективным средством по-прежнему остается язык программирования С.

В этом разделе приведены сведения о языке Bourne shell, достаточные, чтобы разобраться в системных скриптах и написать простейшие скрипты. Данное описание ни в коем случае не претендует на полное руководство по программированию на языке командного интерпретатора.

 

Общий синтаксис скрипта

Как уже было замечено, скрипт представляет собой обычный текстовый файл, в котором записаны инструкции, понятные командному интерпретатору. Это могут быть команды, выражения shell или функции. Командный интерпретатор считывает эти инструкции из файла и последовательно выполняет их.

Безусловно, как и в случае любого другого языка программирования, применение комментариев существенно облегчает последующее использование и модификацию написанной программы. В Bourne shell комментарии начинаются с символа '#':

# Этот скрипт выполняет поиск "мусора" (забытых временных

# файлов, файлов core и т.п.) в каталогах пользователей

Комментарии могут занимать не всю строку, а следовать после команды:

find /home -name core -print # Выполним поиск файлов core

Поскольку в системе могут существовать скрипты для различных интерпретаторов, имя интерпретирующей команды обычно помещается в первой строке следующим образом:

#!/bin/sh

В данном случае последующий текст скрипта будет интерпретироваться Bourne shell. Заметим, что при запуске скрипта из командной строки (для этого он должен обладать правом на выполнение — x), будет запущен новый командный интерпретатор, ввод команд для которого будет выполняться из файла скрипта.

 

Переменные

В командной строке или скрипте командного интерпретатора можно определить и использовать переменные. Значением переменной является строка, которая передается присвоением:

var= value

где var — имя переменной, a value — ее значение.

Значение переменной можно получить, используя знак. Например, вывести значение переменной name на экран можно с помощью команды echo следующим образом:

$ echo $name

Так же можно присвоить другой переменной (name1) значение переменной name:

$ name1=$name

Значение переменной можно присвоить иначе. Поскольку значение представляет собой строку, shell предоставляет удобный способ генерации строк из потока вывода команды. Синтаксис присвоения при этом следующий:

var=` command `

Так, например, где var — имя переменной, a command — название команды, команда pwd(1) выводит строку со значением текущего каталога:

$ pwd

/usr/home/andrei/test

Можно присвоить переменной cdir значение текущего каталога, которое сохранится в ней:

$ cdir=`pwd`

$ echo $cdir

/usr/home/andrei/test

$ cd /usr/bin

$ pwd

/usr/bin

$ cd $cdir

$ pwd

/usr/home/andrei/test

При использовании переменной, например var, командный интерпретатор подставляет вместо $var ее значение. Более сложные синтаксические конструкции получения значения переменной приведены в табл. 1.7.

Таблица 1.7. Способы получения значения переменной

$var Значение var; ничего, если переменная var не определена
${var} То же, но отделяет имя переменной var от последующих символов
${var:-string} Значение var, если определено; в противном случае — string. Значение var при этом не изменяется
${var:=string} То же, но если переменная var не определена, ей присваивается значение строки string
${var:?string} Если переменная var не определена, выводится строка string и интерпретатор прекращает работу. Если строка string пуста, то выводится сообщение var: parameter not set
${var:+string} Строка string, если переменная var определена, в противном случае — ничего

Приведем несколько примеров, используя команду echo:

$ var=user1

$ var1=user2

$ echo $var1

user2

$ echo ${var}l

user11

$ echo ${var1:+"do you want to redefine var?"}

do you want to redefine var?

Для нормальной работы в UNIX ряд переменных должен быть определен и зависит от тех приложений, с которыми вы работаете. Приведем несколько наиболее употребительных переменных:

Имя Описание Возможное значение
НОМЕ Каталог верхнего уровня пользователя /usr/'logname' [13]
PATH Поисковый путь /bin:/etc:/usr/bin:.
MAIL Имя почтового ящика /usr/spool/mail/'logname'
TERM Имя терминала ansi
PS1 Первичное приглашение shell #
PS2 Вторичное приглашение shell >

Начальное окружение вашего сеанса устанавливается программой login(1) исходя из записей в файле паролей, и имеет следующий вид:

Переменная окружения Поле файла паролей
HOME= домашний_каталог 6
LOGNAME= зарегистрированное_имя 1
PATH=/usr/bin: -
SHELL= интерпретатор_сеанса 7
MAIL=/var/mail/ зарегистрированное_имя 1
TZ= временная_зона определено системой

Переменная НОМЕ в основном используется в команде cd, которая служит для перехода в каталог:

$ pwd

/u/usr

$ cd some/new/directory

$ pwd

/u/usr/some/new/directorу

В результате текущим каталогом (команда pwd(1) выводит на терминал полное имя текущего каталога) становится /u/usr/some/new/directory. Вызов команды cd без параметра эквивалентен следующему вызову:

$ cd $HOME

который вернет вас в домашний каталог.

Переменная PATH служит для поиска командным интерпретатором запускаемых на выполнение программ, если их имя не содержит пути. Например, при запуске программы:

$ run

интерпретатор попытается найти файл run в каталогах пути поиска. В то же время при запуске программы run с указанием пути, переменная PATH использоваться не будет:

$ ./run

В последнем примере было задано относительное имя программы (относительно текущего каталога, обозначаемого точкой). Предполагается, что файл программы имеется в текущем каталоге, в противном случае shell выведет сообщение об ошибке.

Каталоги поиска в переменной PATH разделены символом ':'. Заметим, что текущий каталог поиска должен быть задан явно ('.'), shell не производит поиск в текущем каталоге по умолчанию.

Поиск запускаемых программ в текущем каталоге таит потенциальную опасность, поэтому для суперпользователя переменная PATH обычно инициализируется без '.'. Рассмотрим следующую ситуацию. Злоумышленник создает программу, наносящую вред системе (удаляющую файл паролей), помещает ее в каталог общего пользования, например в /tmp, открытый на запись всем пользователям системы, с именем ls. Известно, что в UNIX существует стандартная команда ls(1) (она обычно находится в каталоге /bin), выводящая на экран список файлов каталога. Допустим теперь, что администратор системы делает текущим каталог /tmp и хочет вывести список файлов данного каталога. Если текущий каталог ('.') расположен в пути поиска (переменной PATH) раньше каталога /bin, то выполнится программа, "подложенная" злоумышленником. Даже если текущий каталог указан последним в пути поиска, все равно существует вероятность, что вы захотите запустить команду, которая расположена в каталоге, не попавшем в переменную PATH, на самом деле вы можете запустить троянского коня.

Переменная MAIL определяет местоположение вашего почтового ящика, программы работы с электронной почтой используют эту переменную. Переменная MAIL инициализируется программой login(1).

Переменная TERM содержит имя терминала и используется программами для доступа к базе данных терминалов. Обычно это программы, обеспечивающие полноэкранный режим работы, цвета и системы меню (редакторы, различные пользовательские оболочки). Поскольку наборы команд работы с различными терминалами отличаются друг от друга, используется специальная база данных, где хранятся конкретные команды для конкретного терминала.

Переменные PS1 и PS2 устанавливают первичное и вторичное приглашения командного интерпретатора. Первичное приглашение указывает на готовность интерпретатора к вводу команд. Значение этой переменной устанавливается при исполнении скрипта (.profile) при входе пользователя в систему, и имеет вид "$" для обычных пользователей и "#" для суперпользователя. Однако вид приглашения легко изменить, соответствующим образом задав значение переменной PS1. Например, если вы хотите, чтобы в приглашении присутствовало имя хоста, на котором вы работаете, задайте значение PS1 следующим образом:

PS1=`uname -n">"

В этом случае, если имя вашей системы, например, telemak, при входе в систему командный интерпретатор выведет следующее приглашение:

telemak>

Вторичное приглашение появляется, если вы нажали клавишу , синтаксически не закончив ввод команды. Например:

$ while :      нажатие клавиши

> do           нажатие клавиши

> echo Привет! нажатие клавиши

> done         нажатие клавиши

После этого вы увидите слово "Привет!", выводимое на экран в бесконечном цикле. (Если вы все-таки воспроизвели этот пример, нажмите клавиши + или .)

Переменные, которые определены, являются внутренними переменными командного интерпретатора и не попадают в его окружение автоматически. Таким образом, они не могут быть использованы другими программами, запускаемыми из shell (окружение наследуется порожденными процессами). Для того чтобы поместить необходимые переменные в окружение shell и тем самым сделать их доступными для других приложений, эти переменные должны быть отмечены как экспортируемые. В этом случае при вызове какой-либо программы они автоматически попадут в ее окружение. Например, программа работы с электронной почтой получает имя файла — почтового ящика через переменную MAIL, программы, работающие с терминалом, например полноэкранный редактор, обращаются к базе данных терминалов, используя переменную TERM. Разработанная вами программа также может получать часть информации через переменные окружения. Для этого она должна использовать соответствующие функции (getenv(3C) и putenv(3C)), которые мы подробнее рассмотрим в следующей главе.

 

Встроенные переменные

Помимо переменных, определяемых явно, shell имеет ряд внутренних переменных, значения которых устанавливаются самим интерпретатором. Поскольку это внутренние переменные, имя переменной вне контекста получения ее значения не имеет смысла (т.е. не существует переменной #, имеет смысл лишь ее значение $#). Эти переменные приведены в табл. 1.8.

Таблица 1.8. Внутренние переменные shell

$1, $2, ... Позиционные параметры скрипта
$# Число позиционных параметров скрипта
$? Код возврата последнего выполненного процесса
$5 PID текущего shell
$! PID последнего процесса, запушенного в фоновом режиме
$* Все параметры, переданные скрипту. Передаются как единое слово, будучи заключенным в кавычки: "$*" = "$1 $2 $3 ..."
$@ Все параметры, переданные скрипту. Передаются как отдельные слова, будучи заключенным в кавычки: "$*" = "$1" "$2" "$3 ..."

Эти переменные редко используются при работе в командной строке, основная область их применения — скрипты. Рассмотрим несколько примеров.

Текст скрипта test1.sh:

#!/bin/sh

echo скрипт $0

echo $1 $2 $3

shift

echo $1 $2 $3

Запуск скрипта

$ ./test1.sh a1 a2 a3 a4 a5

скрипт ./test.sh

a1 a2 a3

a2 a3 a4

Переменные $1, $2, ... $9 содержат значения позиционных параметров — аргументов запущенного скрипта. В $1 находится первый аргумент (a1), в $2 — a2 и т.д. до девятого аргумента. При необходимости передать большее число аргументов, требуется использовать команду shift n, производящую сдвиг значений аргументов на n позиций (по умолчанию — на одну позицию). Приведенный скрипт иллюстрирует этот прием. В переменной $0 находится имя запущенного скрипта. Здесь наблюдается полная аналогия с массивом параметров argv[], передаваемом программе на языке С.

Значение $# равно числу позиционных параметров. Его удобно использовать при проверке соответствия числа введенных пользователем параметров требуемому.

Текст скрипта test2.sh:

#!/bin/sh

if [ $# -lt 2 ]

then

 echo usage: $0 arg1 arg2

 exit 1

fi

Запуск скрипта

$ test2.sh

usage: test2.sh arg1 arg2

$ test2.sh h1 h2

$

В данном примере использовано условное выражение if и проверка, которые мы рассмотрим ниже.

Код возврата последней выполненной задачи ($?) удобно использовать в условных выражениях. По правилам успешным завершением задачи считается код возврата, равный 0, ненулевой код возврата свидетельствует об ошибке. Код возврата скриптов генерируется с помощью команды exit n, где n — код возврата (см. предыдущий пример). В приведенном ниже примере определяется, зарегистрирован ли в системе пользователь с именем "sergey". Для этого программой grep(1) производится поиск слова sergey в файле паролей. В случае удачи grep(1) возвращает 0. Если слово не найдено, то grep(1) возвращает ненулевое значение, в данном случае это свидетельствует, что пользователь с именем sergey в системе не зарегистрирован.

Текст скрипта test3.sh:

#!/bin/sh

grep sergey /etc/passwd

if [ $? -ne 0 ]

then

 echo пользователь sergey в системе не зарегистрирован

fi

Каждый активный процесс в UNIX имеет уникальный идентификатор процесса, PID. Запуская скрипт, вы порождаете в системе процесс с уникальным PID. Значение PID сохраняется в переменной $$. Эту переменную удобно использовать в названиях временных файлов, поскольку их имена будут уникальными, например:

Текст скрипта test4.sh:

#!/bin/sh

tmpfile=/usr/tmp/tmp.$$

...

rm $tempfile

 

Перенаправление ввода/вывода

Каждая запущенная из командного интерпретатора программа получает три открытых потока ввода/вывода:

□ стандартный ввод

□ стандартный вывод

□ стандартный вывод ошибок

По умолчанию все эти потоки ассоциированы с терминалом. То есть любая программа, не использующая потоки, кроме стандартных, будет ожидать ввода с клавиатуры терминала, весь вывод этой программы, включая сообщения об ошибках, будет происходить на экран терминала. Большое число утилит, с которыми вам предстоит работать, используют только стандартные потоки. Для таких программ shell позволяет независимо перенаправлять потоки ввода/вывода. Например, можно подавить вывод сообщений об ошибках, установить ввод или вывод из файла и даже передать вывод одной программы на ввод другой.

В табл. 1.9 приведен синтаксис перенаправления ввода/вывода, а на рис. 1.9 схематически показаны примеры перенаправления потоков.

Таблица 1.9. Перенаправление потоков ввода/вывода

>file Перенаправление стандартного потока вывода в файл file
>>file Добавление в файл file данных из стандартного потока вывода
<file Получение стандартного потока ввода из файла file
p1 | p2 Передача стандартного потока вывода программы p1 в поток ввода p2
n>file Переключение потока вывода из файла с дескриптором n в файл file
n>>file To же, но записи добавляются в файл file
n>&m Слияние потоков с дескрипторами n и m
<<str "Ввод здесь": используется стандартный поток ввода до подстроки str. При этом выполняются подстановки метасимволов командного интерпретатора
<<\str To же, но подстановки не выполняются

Рис. 1.9. Пример перенаправления стандартных потоков ввода/вывода

Рассмотрим несколько примеров перенаправления потоков.

Запуск некой программы ведения журнала можно выполнить следующим образом:

$ logger >> file.log

При этом вывод программы logger будет записываться в конец файла file.log, сохраняя все предыдущие записи. Если файла file.log не существует, он будет создан. В отличие от этого, использование символа '>' указывает, что сначала следует очистить файл, а затем производить запись.

Стандартным потокам ввода, вывода и вывода ошибок присваиваются дескрипторы — числовые значения, являющиеся указателями на соответствующий поток. Они, соответственно, равны 0, 1 и 2. Перенаправлять потоки можно, используя эти числовые значения. Таким образом, предыдущему примеру эквивалентна следующая запись:

$ logger 1>>file.log

Чаще всего числовое значение дескриптора потока используется для потока ошибок. Например, чтобы подавить вывод ошибок, можно использовать следующую запись:

$ run 2>/dev/null

где /dev/null является псевдоустройством, удаляющим все введенные в него символы.

Командный интерпретатор предоставляет возможность слияния потоков. Например, при запуске команды

$ run_long_program >/dev/null 2>&1 &

сообщения об ошибках будут также выводиться в файл /dev/null. Символ '&' перед именем потока необходим, чтобы отличить его от файла с именем 1. Заметим, что изменение порядка двух перенаправлений потоков приведет к тому, что сообщения об ошибках будут по-прежнему выводиться на экран. Дело в том, что Shell анализирует командную строку слева направо, таким образом сначала будет осуществлено слияние потоков и оба будут указывать на терминал пользователя, а затем стандартный поток вывода (1) будет перенаправлен в файл /dev/null.

Передача потока вывода одной программы в поток ввода другой осуществляется с помощью конвейера '|' (программного канала). Программные каналы часто используются для фильтрации вывода некоторой команды:

$ ps - ef | grep myproc

позволяет получить информацию о конкретном процессе myproc. Утилита ps(1) выводит на экран информацию обо всех процессах в системе, программа grep(1) фильтрует этот поток, оставляя лишь строки, в которых присутствует слово myproc.

Можно усложнить задачу и попытаться получить идентификатор процесса myproc. Однако здесь нам не обойтись без других средств системы. В данном случае мы будем использовать интерпретатор awk(1):

$ ps -ef | grep myproc | awk '{ print $2 }'

Идея заключается в фильтрации второго поля записи о процессе myproc, содержащего идентификатор процесса (см. описание утилиты ps(1)).

Иногда возникает необходимость разместить поток ввода вместе с командой. Для этого используется выражение "ввод здесь". Проиллюстрируем его на примере:

$ at Dec 31 <

cat happy.new.year | elm -s"C Новым Годом"

[email protected]

!

По определению, команда at(1) устанавливает вызов команды, полученной ею со стандартного ввода (клавиатуры терминала), на определенное время (в данном случае — на 31 декабря каждого года). С помощью выражения "ввод здесь" мы явно задали вид этой команды, точнее комплекса команд: cat(1) передает текст поздравления программе elm(1), отвечающей за отправление сообщения электронной почты.

 

Команды, функции и программы

Все команды, которые вводятся в строке приглашения shell, относятся к одной из следующих категорий:

□ встроенные функции

□ функции shell, определенные пользователем

□ внешние программы и утилиты

Непосредственное отношение к shell имеют только первые две категории, а программы и утилиты являются обычными исполняемыми файлами.

Запуск встроенной функции не требует порождения нового процесса, поскольку эта функция реализована в самой программе shell (например, /bin/sh). Соответственно, встроенные функции shell выполняются быстрее всего. Рассмотрим важнейшие встроенные функции shell.

: Пустая команда. Код возврата всегда 0 (успех). Пустая команда удобна для создания бесконечных циклов, например:   while :   do ...  done
. runme Текущий командный интерпретатор выполняет команды, указанные в файле runme . При этом не происходит порождения нового shell, как в случае запуска на выполнение runme . Например, использование в скрипте команды . /usr/bin/include_script выполнит команды файла include_script, как если бы они являлись частью текущего скрипта.
break [ n ] Производит выход из цикла for или while . Если параметр n указан, происходит выход из n вложенных циклов  ps -ef | awk '{ print $1 " " $2}' | while read uid pid do if [$pid -eq $PID] then echo pid=$pid user=$uid break fi done
cd [dir] Осуществляет переход в каталог dir . Если параметр не указан, происходит переход в домашний каталог ($HOME)
echo [string] Строка string выводится на стандартное устройство вывода (терминал)
exec runme Выполняет программу runme , заменяя ею текущий командный интерпретатор. Например, если в login shell (командном интерпретаторе, запускаемом при регистрации пользователя в системе) мы вызовем exec ls , то после вывода имен файлов текущего каталога произойдет завершение работы в системе
exit [ n ] Завершает работу текущего интерпретатора (или скрипта) с кодом возврата n . По умолчанию код возврат равен 0
export [name1], [name2...] Помещает переменные, указанные в качестве аргументов, в окружение текущего shell, делая их тем самым экспортируемыми, т.е. доступными для запускаемых из интерпретатора программ
hash [-r] [ command , command ...] Для каждой команды, указанной в качестве аргумента, запоминается полный путь. Таким образом, при последующих вызовах этих команд поиск не производится. Ключ -r удаляет все ранее запомненные пути. Если команда hash вызвана без аргументов, то выводится информация о запомненных путях
jobs Если командный интерпретатор поддерживает управление заданиями, данная команда выводит список текущих заданий. См. раздел «Система управления заданиями», далее в этой главе
kill [- sig ] pid1 pid2 ... Посылает сигнал, определённый параметром sig , процессам, указанным параметрами pid . Параметр pid может быть либо идентификатором процесса, либо идентификатором задания, если поддерживается управление заданиями (в этом случае идентификатор должен предваряться символом '%' в соответствии синтаксисом системы управления заданиями). См. раздел «Система управления заданиями далее в этой главе
pwd Выводит имя текущего каталога
read var1 var2 ... Построчно считывает слова (т.е. группы символов, разделённые пробелами) из стандартного потока ввода, последовательно присваивая переменным var , указанным в качестве параметров значения, равные считанным словам. Если число слов в строке превышает число переменных, то последней переменной присваивается значение, равное остатку строки
return [ n ] Осуществляет выход из функции с кодом возврата n . По умолчанию возвращается код последней команды
set При задании без параметров выводит список определённых переменных
shift [ n ] Производит сдвиг позиционных параметров, хранящихся в $1, $2 и т.д. на n позиций. По умолчанию сдвиг производится на одну позицию
test Вычисляет условное выражение. Возвращает значение 0 — истина, или 1 — ложно. См раздел условные выражения далее в этой главе
times Выводит суммарное время использования процессора программами, запущенными из текущего командного интерпретатора
trap command sig1 sig2 ... Определяет команду command , которая будет выполнена при получении сигналов, указанных в качестве аргументов sig . См. раздел "Сигналы" ранее в этой главе
type name Показывает, как name будет интерпретироваться командным интерпретатором
ulimit Выводит или устанавливает значение пределов, ограничивающих использование задачей системных ресурсов (времени процессора, памяти, дискового пространства). Ограничения будут рассматриваться в главе 2
umask nnn Устанавливает маску прав доступа для вновь создаваемых файлов равной nnn
unset var1 var2 ... Удаляет переменные, указанные в качестве аргументов, из списка определенных переменных командного интерпретатора. Некоторые переменные, например PATH, PS1, PS2, не могут быть удалены
wait pid Ожидает завершения выполнения процесса с идентификатором pid и возвращает его код возврата

Пользователь может определить функцию командного интерпретатора и использовать ее как встроенную функцию shell. С другой стороны, функции мало отличаются от скриптов, включая синтаксис и передачу аргументов. Однако являясь частью shell, функции работают быстрее.

Синтаксис функции имеет следующий вид:

function() {

 command1

 command2

 ...

}

Как можно заметить, телом функции является обычный скрипт shell.

В качестве примера приведем функцию mcd, позволяющую отобразить в приглашении shell имя текущего каталога.

mcd() {

 cd $*

 PS=`pwd`

}

 

Подстановки, выполняемые командным интерпретатором

Прежде чем выполнить команду, указанную либо в командной строке, либо в скрипте, командный интерпретатор производит определенную последовательность действий:

1. Анализирует синтаксис команды. В случае, если обнаружена синтаксическая ошибка, выводится соответствующее сообщение. Естественно, shell анализирует командную строку в соответствии с синтаксисом собственного языка, а не семантику вызова конкретной команды, например, наличие тех или иных аргументов.

2. Производит подстановки, а именно:

 • Заменяет все указанные переменные их значениями. Например, если значение переменной var равно /usr/bin, то при вызове команды find $var -name sh -print переменная $var будет заменена ее значением. Другими словами, фактический запуск команды будет иметь вид:

find /usr/bin -name sh -print

 • Формирует списки файлов, заменяя шаблоны. При этом производится подстановка следующих шаблонов:

  * — соответствует любому имени файла (или его части), кроме начинающихся с символа '.',

  [abc] — соответствует любому символу из перечисленных (а или b или с),

  ? — соответствует любому одиночному символу.

3. Делает соответствующие назначения потоков ввода/вывода. Если в строке присутствуют символы перенаправления (>, <, >>, <<, |), shell производит соответствующее перенаправление потоков. Программный интерфейс ввода/вывода мы рассмотрим в разделе "Работа с файлами" следующей главы.

4. Выполняет команду, передавая ей аргументы с выполненными подстановками. При этом:

 • Если команда является функцией, определенной пользователем, вызывается функция.

 • В противном случае, если команда является встроенной командой shell, запускается встроенная команда.

 • В противном случае производится поиск программы в каталогах, указанных переменной $PATH, если имя команды задано без пути. Если имя команды задано явно, т.е. содержит элементы пути (относительный или абсолютный путь), производится запуск программы. В случае, если программа не найдена, выводится сообщение об ошибке.

Описанные подстановки, выполняемые интерпретатором, следует иметь в виду при запуске команд. Например, запуск команды rm приведет к удалению всех файлов данного каталога:

$ ls                    Вывести список файлов каталога

a.out client client.с

server server.с shmem.h

$ rm *                  Удалить файлы

$ ls

$                      Каталог пуст

Команда rm(1) без колебаний выполнит свою функцию, поскольку в качестве аргументов она получит обычный список файлов. Замену символа '*' на список всех файлов каталога произведет shell, и rm(1) трудно догадаться, что вы собираетесь удалить все файлы. Реальный же вызов rm(1) будет иметь вид:

rm a.out client client.с server server.с shmem.h

Точно так же запускаемые программы ничего не знают о перенаправлении потоков ввода/вывода, произведенных командным интерпретатором. Напомним, что перенаправление ввода/вывода возможно лишь для стандартных потоков ввода, вывода и сообщений об ошибках. Впрочем, большинство утилит UNIX используют только стандартные потоки.

 

Запуск команд

Как уже говорилось, запускаемые команды могут являться либо функциями, определенными пользователем, либо встроенными командами интерпретатора, либо исполняемыми файлами — прикладными программами и утилитами. В любом случае, синтаксис их вызова одинаков.

Если необходимо запустить сразу несколько команд, это можно сделать в одной строке, разделив команды символом ';'. Например:

$ pwd; date

Apr 18 1997 21:07

Заметим, что команды будут выполнены последовательно: сначала выполнится команда pwd(1), которая выведет имя текущего каталога, а затем date(1), которая покажет дату и время.

Можно запустить программу в фоновом режиме. В этом случае shell не будет ожидать завершения выполнения программы, а сразу выведет приглашение, и вы сможете продолжить работу в командном интерпретаторе. Для этого строку команды необходимо завершить символом '&':

$ find -name myfile.txt.1 -print >/tmp/myfile.list 2>/dev/null &

$

Пока утилита find(1) производит поиск файла с именем myfile.txt.1, сканируя файловую систему, вы сможете выполнить еще массу полезных дел, например, отправить почту или распечатать документ на принтере. Мы вернемся к этой схеме запуска программ далее в этой главе при обсуждении системы управления заданиями.

Наконец, командный интерпретатор предоставляет возможность условного запуска команд. Например, если необходимо выполнить команду только в случае успешного завершения предыдущей, следует воспользоваться следующей синтаксической конструкцией:

cmd1 && cmd2

В качестве примера рассмотрим поиск имени пользователя в файле паролей, и в случае успеха — поиск его имени в файле групп:

$ grep sergey /etc/passwd && grep sergey /etc/group

Успехом считается нулевой код возврата программы, неудачей — все другие значения.

Можно назначить выполнение команды только в случае неудачного завершения предыдущей. Для этого команды следует разделить двумя символами '|':

$ cmd1 || echo Команда завершилась неудачно

Приведенный синтаксис является упрощенной формой условного выражения. Командный интерпретатор имеет гораздо более широкие возможности проверки тех или иных условий, которые мы рассмотрим в следующем разделе.

 

Условные выражения

Язык Bourne shell позволяет осуществлять ветвление программы, предоставляя оператор if. Приведем синтаксис этого оператора:

if условие

then

 command1

 command2

 ...

fi

Команды command1 , command2 и т.д. будут выполнены, если истинно условие . Условие может генерироваться одной или несколькими командами. По существу, ложность или истинность условия определяется кодом возврата последней выполненной команды. Например:

if grep sergey /etc/passwd >/dev/null 2>&1

then

 echo пользователь sergey найден в файле паролей

fi

Если слово sergey будет найдено программой grep(1) в файле паролей (код возврата grep(1) равен 0), то будет выведено соответствующее сообщение.

Возможны более сложные формы оператора if.

set `who -r`

Установим позиционные параметры равными значениям полей вывода программы who(1)

if [ "$9" = "S" ]

Девятое поле вывода — предыдущий уровень выполнения системы; символ 'S' означает однопользовательский режим

then

 echo Система загружается

elif [ "$7" = "2" ]

Седьмое поле — текущий уровень

 echo Переход на уровень выполнения 2

else

 echo Переход на уровень выполнения 3

fi

Данный фрагмент скрипта проверяет уровень выполнения, с которого система совершила переход, и текущий уровень выполнения системы. Соответствующие сообщения выводятся на консоль администратора. В этом фрагменте условие генерируется командой test, эквивалентной (и более наглядной) формой которой является "[]". Команда test является наиболее распространенным способом генерации условия для оператора if.

 

Команда test

Команда test имеет следующий синтаксис:

test выражение

или

[ выражение ]

Команда вычисляет логическое выражение (табл. 1.10) и возвращает 0, если выражение истинно, и 1 в противном случае.

Таблица 1.10. Выражения, используемые в команде test

Выражения с файлами
-s file Размер файла file больше 0
-r file Для файла file разрешен доступ на чтение
-w file Для файла file разрешен доступ на запись
-x file Для файла file разрешено выполнение
-f file Файл file существует и является обычным файлом
-d file Файл file является каталогом
-с file Файл file является специальным файлом символьного устройства
-b file Файл file является специальным файлом блочного устройства
-р file Файл file является поименованным каналом
-u file Файл file имеет установленный флаг SUID
-g file Файл file имеет установленный флаг SGID
-k file Файл file имеет установленный флаг sticky bit
Выражения со строками
-z string Строка string имеет нулевую длину
-n string Длина строки string больше 0
string1 = string2 Две строки идентичны
string1 != string2 Две строки различны
Сравнение целых чисел
i1 -eq i2 i1 равно i2
i1 -ne i2 i1 не равно i2
i1 -lt i2 i1 строго меньше i2
i1 -le i2 i1 меньше или равно i2
i1 -gt i2 i1 строго больше i2
i1 -ge i2 i1 больше или равно i2

Более сложные выражения могут быть образованы с помощью логических операторов:

! выражение Истинно, если выражение ложно (оператор NOT)
выражение1 -а выражение2 Истинно, если оба выражения истинны (оператор AND)
выражение1 -o выражение2 Истинно, если хотя бы одно из выражений истинно (оператор OR)

Приведем несколько примеров использования выражений.

Фрагмент скрипта, используемый при регистрации нового пользователя. Скрипт проверяет наличие в домашнем каталоге инициализационного скрипта .profile и в случае его отсутствия копирует шаблон:

if [ ! -f $НОМЕ/.profile ]

then

 echo "файла .profile не существует - скопируем шаблон"

 cp /usr/lib/mkuser/sh/profile $НОМЕ/.profile

fi

Фрагмент скрипта, проверяющего наличие новой почты в почтовом ящике пользователя

if [ -s $MAIL ]

then

 echo "Пришла почта"

fi

Фрагмент скрипта инициализации системы — запуска "суперсервера" Internet inetd(1M). Если исполняемый файл /etc/inetd существует, он запускается на выполнение.

if [ -х /etc/inetd ]

then

 /etc/inetd

 echo "запущен сервер inetd"

fi

Фрагмент скрипта, анализирующий ввод пользователя, сохраненный в переменной ANSW. Если пользователь ввел 'N' или 'n', скрипт завершает свою работу.

if [ "$ANSW" = "N" -о "$ANSW" = "n" ]

then

 exit

fi

 

Циклы

Язык программирования Bourne shell имеет несколько операторов цикла. Приведем их синтаксис:

1) while условие

do

 command1

 command2

 ...

done

2) until условие

do

 command1

 command2

 ...

done

3) for var in список

do

 command1

 command2

 ...

done

С помощью оператора while команды command1 , command2 и т.д. будут выполняться, пока условие не станет ложным. Как и в случае с оператором if, условие генерируется кодом возврата команды, например, test .

В случае оператора until команды command1 , command2 и т.д. будут выполняться, пока условие не станет истинным.

Оператор for обеспечивает выполнение цикла столько раз, сколько слов в списке . При этом переменная var последовательно принимает значения, равные словам из списка. Список может формироваться различными способами, например как вывод некоторой команды (`имя_команды_формирующей_список`) или с помощью шаблонов shell.

В другой форме for, когда список отсутствует, переменная var принимает значения позиционных параметров, переданных скрипту.

Чтобы наглядно представить себе приведенные операторы, обратимся к конкретным примерам.

Например, скрипт монтирования всех файловых систем /etc/mounall для системы Solaris 2.5 включает в себя их проверку, исходя из данных, указанных в файле /etc/vfsck. При этом используется оператор while.

#

cat /etc/vfsck |

while read special fsckdev mountp fstype fsckpass automnt mntopts

# Построчно считывает записи файла vfsck и присваивает переменным spe-

# cial, fsckdev и т.д. значения соответствующих конфигурационных полей.

do

 case $special in

 '# ' * | '' ) # Игнорируем комментарии

  continue ;;

 '-') # Игнорируем строки, не требующие действия

  continue ;;

 esac

 # Последовательно проверяем файловые системы с помощью утилиты

 # /usv/sbin/fsck

 /usr/sbin/fsck -m -F $fstype $fsckdev >/dev/null 2>&1

 ...

done

Скрипт очистки давно не используемых файлов во временных каталогах (обычно он запускается при загрузке системы) использует оператор for.

for dir in /tmp /usr/tmp /home/tmp

do

 find $dir ! -type d -atime +7 -exec rm {} \;

done

При этом удаляются все файлы в указанных каталогах (/tmp, /usr/tmp и /home/tmp), последний доступ к которым осуществлялся более недели назад.

 

Селекторы

Оператор case предоставляет удобную форму селектора:

case слово in

шаблон1 )

 command

 ...

 ;;

шаблон2 )

 command

 ...

 ;;

*)

 command

 ...

 ;;

esac

Значение слово сравнивается с шаблонами, начиная с первого. Если совпадение найдено, то выполняются команды соответствующего раздела, который заканчивается двумя символами ';'. Шаблоны допускают наличие масок, которые были рассмотрены нами в разделе "Подстановки, выполняемые командным интерпретатором". Раздел с шаблоном '*' аналогичен разделу default в синтаксисе селектора switch языка С: если совпадения с другими шаблонами не произошло, то будут выполняться команды раздела '*)'. В качестве примера использования селектора приведем скрипт запуска и останова системы печати в SCO UNIX.

state=$1

set `who -r`

case $state in

'start')

 if [ $9 = "2" -o $9 = "3" ]

 then

  exit

 fi

 [ -f /usr/lib/lpshed ] && /usr/lib/lpshed

 ;;

'stop')

 [ -f /usr/lib/lpshut ] && /usr/lib/lpshut

 ;;

*)

 echo "usage $0 start|stop"

 ;;

esac

В случае, когда скрипт вызван с параметром start, будет произведен запуск системы печати. Если параметр скрипта — stop, то система печати будет остановлена. Запуск скрипта с любым другим параметром приведет к выводу сообщения об ошибке.

 

Ввод

Как мы уже видели, присвоение значений переменным может осуществляться явно или с помощью вывода некоторой программы. Команда read предоставляет удобный способ присвоить переменным значения, считанные из стандартного потока ввода. Это может быть строка, введенная пользователем или считанная из файла в случае перенаправления потока.

Команда read считывает строку из стандартного потока ввода и последовательно присваивает переменным, переданным в качестве параметров, значения слов строки. Если число слов в строке превышает число переменных, то в последней переменной будут сохранены все оставшиеся слова. Продемонстрируем это на простом примере:

Текст скрипта test5.sh:

#!/bin/sh

echo "input: "

while read var1 var2 var3

do

 echo var1=$var1

 echo var2=$var2

 echo var3=$var3

 echo "input: "

done

Запуск скрипта

$ test5.sh

input: пример работы команды read

var1=пример

var2=работы

var3=команды read

input: еще пример

var1=еще

var2=пример

var3=

input: ^D

$

В приведенном примере read в цикле считывает пользовательский ввод. Цикл завершается, когда достигнут конец файла (что эквивалентно пользовательскому вводу +), поскольку при этом read возвращает неудачу (код возврата равен 1) и while завершает работу. В первом цикле число введенных слов превышает количество переменных, поэтому значение переменной var3 состоит из двух слов. Во втором цикле значение var3 пусто.

 

Система управления заданиями

Командный интерпретатор может поддерживать управление заданиями. Для Bourne shell (/bin/sh), который мы рассматриваем, систему управления заданиями включает парный ему интерпретатор /bin/jsh. В остальном этот интерпретатор имеет те же возможности.

В системе управления заданиями каждая команда (простая или составная), которую пользователь запускает со своего терминала, называется заданием. Все задания могут выполняться либо в текущем режиме, либо в фоновом режиме, либо быть приостановлены. Задание в каждом из этих состояний обладает рядом характеристик:

Состояние задания Характеристики
Выполняется в текущем режиме Задание может считывать данные и выводить данные на терминал пользователя
Выполняется в фоновом режиме Заданию запрещен ввод с терминала. Возможность вывода на терминал определяется дополнительными установками
Приостановлено Задание не выполняется

Каждое задание при запуске получает уникальный идентификатор, называемый номером задания, который используется в командах системы управления. Синтаксис номера задания, применяемый в командах:

% jobid

где jobid может принимать следующие значения:

% или + Текущее задание — самое последнее запущенное или вновь запущенное задание
- Предыдущее задание (по отношению к текущему)
? строка Задание, для которого строка присутствует в командной строке запуска
n Задание с номером n
pref Задание, на которое можно уникально указать префиксом pref , например, команда ls(1) , запущенная в фоновом режиме, адресуется заданием %ls

Система управления заданиями позволяет использовать следующие дополнительные команды:

bg [% jobid ] Продолжает выполнение остановленного задания в фоновом режиме. Без параметра относится к текущему заданию.
fg [% jobid ] Продолжает выполнение остановленного задания в текущем режиме. Если задание jobid выполнялось в фоновом режиме, команда перемещает его в текущий режим.
jobs [-p | -l] [% jobid ... ] Выводит информацию об остановленных и фоновых заданиях с указанными номерами. Если последний аргумент опущен, выводится информация обо всех остановленных и фоновых заданиях. Приведенные ниже опции изменяют формат вывода: -l  Вывести идентификатор группы процессов и рабочий каталог. -р  Вывести только идентификатор группы процессов.
kill [-signo] % jobid Обеспечивает те же возможности, что и команда kill(1) , но по отношению к заданиям.
stop % jobid Останавливает выполнения фонового задания.
wait % jobid Ожидает завершения выполнения задания jobid и возвращает его код возврата.

Приведенный ниже пример иллюстрирует использование команд управления заданиями и не нуждается в комментариях:

$ inf.j &

[1] 9112

$ comm1 &

[2] 9113

$ jobs

[1] - Running inf.j

[2] + Running comm1

$ stop %1

$ jobs

[1] - Stopped (signal) inf.j

[2] + Running comm1

$ stop %%

$ jobs -1

[1] - 9112 Stopped (signal) inf.j (wd: /home/andy/SH//JOB)

[2] + 9113 Stopped (signal) comm1 (wd: /home/andy/SH/JOB)

$ bg %1

[1] inf.j &

$ jobs

[1] + Running inf.j

[2] - Stopped (signal) comm1

$ kill %1 %2

$ jobs

[1] + Done(208) inf.j

[2] - Done (208) comm1

$

 

Основные утилиты UNIX

 

В предыдущих разделах мы использовали некоторые утилиты UNIX. Ниже приводятся краткие характеристики утилит, выпавших из поля нашего зрения. Более подробно с различными утилитами можно познакомиться в электронном справочнике man(1).

 

Утилиты для работы с файлами

Поле [opt] содержит конкретные опции каждой утилиты.

cd [ dir ] Изменяет текущий каталог. При задании без параметра — производит переход в домашний каталог пользователя.
cmp [opt] file1 file2 Утилита cmp(1) сравнивает два файла, указанных в качестве аргументов. Если файлы одинаковы, никакого сообщения не выводится. В противном случае выводятся данные о первом несоответствии между этими файлами (в данном примере первое различие найдено в 13-м символе 4-й строки): $  cat file1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $  cat file2 1 2 3 4 5 6 diff1 7 8 9 10 11 12 13 14 15 diff2 $  cmp file1 file2 file1 file2 differ: char 13, line 4
diff [opt] file1 file2 Утилита diff(1) также сравнивает два файла и выводит список изменений, которые необходимо внести в содержимое этих файлов для того, чтобы преобразовать первый файл во второй. По существу, вывод утилиты diff(1) представляет собой команды редактора ed(1) , необходимые для преобразования file1 в file2 : $ diff file1 file2 3a4 > diff1 5c6, 7 < 11 12 13 14 15 ... > 11 12 13 14 15 diff2
cp [opt] file1 file2 cp [opt] file1 ... dir Утилита cp(1) служит для копирования файлов. При этом создается не жесткая связь, а новый файл: $  cp file1 file2 $  ls -li file1 file2 261425 -rw-r--r-- 1 andy user 49 Dec 24 12:58 file1 261427 -rw-r--r-- 1 andy user 49 Dec 24 13:13 file2
mv [opt]  file1 file2 mv [opt]  file1 ...  dir Утилита mv(1) изменяет имя файла. Если последний параметр является каталогом, то число аргументов утилит cp(1) или mv(1) может превышать 2. В этом случае будет производиться копирование или перемещение указанных файлов в каталог.
rm [opt]  file1 ... rmdir  dir1 ... Утилиты удаления файлов и каталогов. При этом удаляются только записи имен файлов в соответствующих каталогах, фактическое содержимое файла (метаданные и дисковые данные) будет удалено, если число жестких связей для файла станет равным 0.
ls [opt] [ file1 file2 ...] Без параметров утилита ls(1) выводит имена файлов текущего каталога. В качестве параметров можно задать имена каталогов, содержимое которых необходимо вывести, или имена файлов, информацию о которых нужно получить. Опции утилиты позволяют получить список различной информативности и формата.
ln [opt] source target Утилита ln(1) создает жесткую связь имени source с файлом, адресуемым именем target. При использовании опции -s будет создана символическая связь.
mkdir [-m mode] [-p] dir1 ... Создать каталог.
pwd Вывести имя текущего каталога.
fgrep [opt] < подстрока > file1 ... Утилиты поиска фрагментов текста в файлах. Могут использоваться в качестве фильтров в программных каналах. Для поиска подстроки в файлах можно использовать самую простую из утилит fgrep(1) (fast grep). Если подстрока поиска содержит пробелы или знаки табуляции, ее необходимо заключить в кавычки. Если подстрока уже содержит кавычки, их надо экранировать, поместив символ '\' непосредственно перед кавычками: $  fgrep "рассмотрим в разделе \"Создание процесса\"" chap* Если вы хотите сделать поиск нечувствительным к заглавным/строчным символам, используйте ключ -у . Для поиска строк, не содержащих указанную подстроку, используется ключ -v .
grep [opt] < рег_выражение > file1 ... egrep [opt]  < рег_выражение > file1 ... Утилиты grep(1) и egrep(1) позволяют производить более сложный поиск, например, когда вы не уверены в написании искомого слова, или хотите найти слова, расположенные в определенных местах файла. В этом случае в качестве подстроки поиска указывается регулярное выражение ( рег_выражение ). Например, чтобы произвести поиск слова "центр" в американском (center) и британском (centre) написании, можно задать следующую команду: $  grep "cent[er]" file или $  grep "cent[er][er]" file [er] является регулярным выражением, соответствующим либо символу 'е' , либо 'r' . Регулярное выражение должно быть заключено в кавычки для предотвращения интерпретации специальных символов командным интерпретатором shell.
cat [opt] file Утилиты просмотра содержимого файла. Команда cat file выводит содержимое файла file на экран терминала. Если у вас есть подозрение, что файл не текстовый, т.е. содержит "непечатные" символы, лучше запустить cat(1) с ключом -v . В этом случае вывод таких символов (которые, кстати, могут нарушить настройки вашего терминала) будет подавлен.
more [opt] file pg [opt] file Если размер файла велик и его содержимое не помещается в терминальном окне, удобнее будет воспользоваться утилитами pg(1) и more(1) , позволяющими выводить файл порциями.
head [-n] file tail [opt] file Посмотреть только начало (первые n строк) или конец (последние n строк) файла можно с помощью утилит head(1) и tail(1) , соответственно.
sort Для сортировки строк файла используется утилита sort(1) . Например, для сортировки текста в алфавитном порядке необходимо ввести следующую команду: $  sort -d file >sorted file Вы можете указать номер слова строки, по которому необходимо произвести сортировку (точнее, номер поля записи; по умолчанию записью является строка, а поля разделены пробелами). Например, для сортировки строк файла file Андрей Май Борис Январь Владимир Март по месяцам, можно использовать команду $  sort -M +1 file в результате получим: Борис Январь Владимир Март Андрей Май Опция -M определяет сортировку по месяцам (не по алфавиту), опция +1 указывает, что сортировку необходимо проводить по второму полю каждой строки.
cut Позволяет отфильтровать указанные поля строк файла. Разделитель полей указывается опцией -d< sep > . Например, чтобы получить реальные имена пользователей системы (пятое поле файла паролей), можно использовать следующую команду: $  cat /etc/passwd | cut -f5 -d: ... WWW Administrator Yuri Korenev Serge Smirnoff W3 group Konstantin Fedorov Andrei Robachevsky Sergey Petrov
wc file Позволяет вывести число строк, слов и символов текста файла.
find dir [opt] Выполняет поиск файла в файловой системе UNIX, начиная с каталога dir . Например, для вывода полного имени исполняемого файла командного интерпретатора Bourne shell введите команду: $  find / -name sh -print 2>/dev/null /usr/bin/sh /usr/xpg4/bin/sh /sbin/sh С помощью опции -name указывается имя искомого файла, а с помощью опции -print  — действие (вывести полное имя). С помощью find(1) можно производить поиск файлов по другим критериям, например, размеру, последнему времени модификации и т.д. Например, чтобы найти файлы с именем core (образ процесса, создаваемый при неудачном его завершении и используемый в целях отладки), последнее обращение к которым было, скажем, более месяца назад (скорее всего такие файлы не нужны пользователям и только "засоряют" файловую систему), можно задать команду: $  find / -name core -atime +30 -print /u/local/lib/zircon/lib/core /u/local/etc/httpd/data/zzmaps/core /home/amd/WORK/novosti/core /home/amd/WORK/access/core /home/guests/snell/core Если вы сторонник жесткого администрирования, то можно применить следующую команду: $  find / -name core -atime +30 -exec rm {} \; которая автоматически удалит все найденные файлы.
chown user file ... Изменяет владельца-пользователя указанных файлов.
chgrp group file ... Изменяет владельца-группу указанных файлов.
chmod mode file ... Изменяет права доступа и дополнительные атрибуты файлов.
file file1 ... Сканирует начало файла и пытается определить его тип. Если это текстовый файл (ASCII), file(1) пытается определить его синтаксис (текст, программа на С и т.д.). Если это бинарный файл, то классификация ведется по так называемому magic number, определения которого находятся в файле /etc/magic . $  file * nlc-2.2d.tar: tar archive report.doc: ascii text work: directory runme.c: с program text runme: ELF 32-bit MSB executable figure.gif: data

 

Утилиты для управления процессами

nice -[[-]n] command Утилита nice(1) применяется для запуска программы на выполнение с относительным приоритетом (nice number), отличным от принятого по умолчанию. Например, ввод команды: $  nice -10 big program приведет к запуску big program с большим значением nice. В UNIX чем больше значение nice number, тем меньший приоритет имеет процесс. Таким образом, при планировании выполнения процессов вероятность того, что ядро операционной системы выберет именно big_program для запуска, уменьшится. Как следствие, big_program станет выполняться дольше, но будет менее интенсивно потреблять процессорные ресурсы. Только администратор системы может повысить приоритет процесса (уменьшить значение nice number): $  nice - -10 job1
renice new_nice pid Утилита renice(1) позволяет изменять приоритет процесса во время его выполнения. Например, команда $ renice 5 1836 устанавливает значение nice number процесса с идентификатором 1836 равным 5. Как и в случае команды nice(1) , увеличить приоритет процесса может только администратор системы.
ps Утилита ps(1) выводит информацию о существующих процессах. При использовании различных опций она позволяет получить следующую информацию:
F статус процесса (системный процесс, блокировки в памяти и т.д.)
S состояние процесса (О — выполняется процессором, S — находится в состоянии сна, R — готов к выполнению, I — создается, Z — зомби)
UID идентификатор (имя) пользователя — владельца процесса
PID идентификатор процесса
PPID идентификатор родительского процесса
PRI текущий динамический приоритет процесса
NI значение nice number процесса
TTY управляющий терминал процесса ('?' — означает отсутствие управляющего терминала)
TIME суммарное время выполнения процесса процессором
STIME время создания процесса (может отличаться от времени запуска команды)
COMMAND имя команды, соответствующей процессу
kill [ signo ] pid1 , pid2 ... Посылает процессам с идентификаторами pid1 , pid2 и т.д. сигнал signo . Сигнал signo может быть указан как в числовой, так и в символьной форме. Команда kill -l выводит таблицу соответствия между символьными именами сигналов и их числовыми значениями: $  kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGIOT 7) SIGEMT 8) SIGFPE 9) SIGKILL 10) SIGBUS 11) SIGSEGV 12) SIGSYS 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGUSR1 ... Таким образом, следующие две команды эквивалентны: $  kill -9 18793 $  kill -SIGKILL 18793
at [opt] время_запуска Утилита at(1) считывает команды стандартного потока ввода и группирует их в задание at , которое будет выполнено в указанное пользователем время. Для выполнения задания будет запущен командный интерпретатор, в среде которого и будут исполнены команды. Например, следующая команда, позволит вам поздравить друга с днем рождения в назначенное время: $  at May 30 <<! cat birthday.txt | elm -s"C Днем Рождения!" [email protected] ! Вы можете добавить опцию -m, и после выполнения задания вам будет отправлено уведомление по электронной почте.

 

Об администрировании UNIX

 

Достаточно открыть оглавление любого "Руководства системного администратора" для UNIX, чтобы оценить то многообразие задач и проблем, с которыми приходится сталкиваться при обслуживании системы:

□ Настройка жизненно важных для пользователей подсистем, таких как файловая система, система печати и сетевая поддержка. Каждая из них, в свою очередь, может быть разделена на десятки подзадач.

□ Регистрация пользователей. Каждый новый пользователь добавляет "забот" администратору системы, но какой же UNIX без пользователей!

□ Постоянный мониторинг системы и борьба с авариями. Причем, как правило, неполадки возникают в самый неподходящий момент и там, где их совсем не ждешь. Здесь от администратора потребуется хорошее знание не только операционной системы, но и аппаратуры, на которой она работает.

□ Настройка производительности системы.

□ Обучение, наставление, "ссоры" и "примирения" с пользователями операционной системы, которую вы обслуживаете.

В этой книге вы не найдете практического руководства по администрированию системы. Вместо этого в следующих главах мы попытаемся взглянуть на UNIX изнутри, понять как устроена эта система и как она работает. Может быть после этого вы посмотрите на руководства другими глазами, а администрирование системы не сведется к простому заучиванию команд.

В качестве компенсации за отсутствие практического руководства предлагаю вашему вниманию перевод материала, найденный мною на одном из WWW-серверов Internet, в котором приведена забавная классификация системных администраторов.

Можно выделить четыре типа системных администраторов UNIX:

□ Технический бандит. Обычно в прошлом системный программист, вынужденный заниматься системным администрированием. Пишет скрипты на смеси языков интерпретатора Bourne shell, sed, С, awk, perl и APL.

□ Администратор-фашист. Обычно это законченный тунеядец (реже — бывшая ведьма-секретарша), вынужденный заниматься системным администрированием.

□ Маньяк. Стареющий хакер, обнаруживший, что ни Массада, ни Куба не собираются достойно оплачивать его услуги по компьютерному шпионажу, вследствие чего подавшийся в системные администраторы.

□ Идиот. Полный кретин или старый программист на Коболе, выбранный в системные администраторы комитетом, состоящим из таких же кретинов или старых программистов на Коболе.

Как определить, к какому типу принадлежит ваш системный администратор?

 

Ситуация 1. Нехватка дискового пространства

Технический бандит. Пишет набор скриптов для мониторинга использования дискового пространства, для сопровождения базы данных статистики использования диска, для прогнозирования будущего использования с помощью регрессионного анализа, для выявления пользователей, которые превысили стандартное отклонение от среднего значения и, наконец, для отправления нарушителям почтовых сообщений. Помещает скрипты под управление cron(1). В результате свободное дисковое пространство не увеличивается, поскольку "дисковые обжоры" обычно не читают почту.

Администратор-фашист. Помещает правила использования диска в сообщение дня motd. Активно пользуется квотированием дискового пространства. Не допускает никаких исключений, чем полностью останавливает деятельность разработчиков. Блокирует регистрацию пользователей, превысивших квоту.

Маньяк:

# cd /home

# rm -rf `du -s * | sort -rn | head -1 | awk '{print $2}'`

Идиот:

# cd /home

# cat `du -s * | sort -rn | head -1 | awk '{ printf "%s/*\n", $2}'` | compress

 

Ситуация 2. Избыточная загрузка процессора

Технический бандит. Пишет набор скриптов для мониторинга использования вычислительных ресурсов, для сопровождения базы данных статистики их использования, для выявления процессов, превысивших стандартное значение и для изменения приоритета таких процессов. Помещает скрипты под управление cron(1). В результате понижения приоритета офисной базы данных, предает ее забвению, ставя всю работу на грань срыва к немалой радости поклонников игры в

Администратор-фашист. Помещает правила использования вычислительных ресурсов в сообщение дня motd. Активно пользуется квотированием процессорных ресурсов. Не допускает никаких исключений, чем полностью останавливает деятельность разработчиков к немалой радости поклонников игры в xtrek.

Маньяк:

# kill -9 `ps -augxww | sort -rn +8 -9 | head -1 | awk '{print $2}'`

Идиот:

# compress -f `ps -augxww | sort -rn +8 -9 | head -1 | awk '{print $2}'`

 

Ситуация 3. Регистрация новых пользователей

Технический бандит. Пишет скрипт на языке Perl, создающий домашний каталог пользователя, определяющий непонятное окружение и помещающий записи в файлы /etc/passwd, /etc/shadow и /etc/group. Устанавливает на скрипт бит SUID и обязывает секретаршу обеспечить регистрацию новых пользователей. Поскольку обычно секретарша так и не может разобраться в разнице между и , ни один новый пользователь не зарегистрирован.

Администратор-фашист. Помещает правила регистрации пользователей в сообщение дня motd. Поскольку незарегистрированные пользователи не могут прочитать это сообщение, никто не выполняет бюрократических требований, и, как следствие, ни один новый пользователь не зарегистрирован.

Маньяк. "Если ты настолько глуп, что не можешь взломать машину и самостоятельно зарегистрироваться, тебе нечего делать в моей системе. В этом ящике и так слишком много придурков".

Идиот:

# cd /home; mkdir "Bob's home directory"

# echo "Bob Simon:gandalf:0:0::/dev/tty:compress -f" > /etc/passwd

 

Ситуация 4. Авария загрузочного диска

Технический бандит. Чинит диск. Обычно ему удается восстановить файловую систему прямо из приглашения загрузки. Если это не помогает, запускает микроядро, которое запускает на соседнем компьютере скрипт, копирующий на аварийную машину загрузочный код, переформатирующий диск и инсталлирующий операционную систему. Оставляет скрипт работать до конца уик-энда, а сам отправляется в поход в горы.

Администратор-фашист. Начинает расследование аварии. Отказывается исправить аварию до тех пор, пока виновный не найден, и с него не взыскана стоимость сломанного оборудования.

Маньяк. Извлекает диск. С помощью кузнечного молота пытается подогнать отдельные пластины. Звонит производителю. Во время установки нового диска и операционной системы наносит оскорбления присланному инженеру.

Идиот. Не замечает ничего необычного.

 

Ситуация 5. Слабая производительность сети

Технический бандит. Пишет скрипт для мониторинга сети, переписывает программное обеспечение, чем повышает производительность на 2%. Пожимает плечами, говорит: "Я сделал все, что мог", и отправляется в поход в горы.

Администратор-фашист. Помещает правила работы в сети в сообщение дня motd. Звонит в Беркли и в AT&T, приставая к ним, как установить сетевые квоты. Пытается уволить поклонников игры в xtrek.

Маньяк. Каждые два часа размыкает кабель Ethernet и ждет тайм-аута на сетевых соединениях.

Идиот:

# compress -f /dev/en0

 

Ситуация 6. "Глупые" вопросы пользователей

Технический бандит. Отвечает на вопросы в шестнадцатеричном или двоичном виде, иногда по-французски, пока пользователь не уходит.

Администратор-фашист. Блокирует вход пользователя в систему, пока тот не представит веские доказательства своей квалификации.

Маньяк:

# cat >> ~luser/.cshrc

alias vi 'rm \!*;unalias vi;grep -v BoZo ~/.cshrc > ~/.z;

mv -f ~/.z ~/~/cshrc'

^D

Идиот. Отвечает на все вопросы в меру своего понимания. Приглашает пользователя в группу администрирования системы.

 

Ситуация 7. Установка новой версии операционной системы

Технический бандит. Изучает исходные тексты новой версии и выбирает из них только то, что ему нравится.

Администратор-фашист. В первую очередь изучает законодательные акты против производителя, поставляющего программное обеспечение с ошибками.

Маньяк:

# uptime

1:33pm up 19 days, 22:49, 167 users, load average: 6.49, 6.45, 6.31

# wall

Итак, настало время установки новой версии. Займет несколько часов, и если нам повезет - управимся к 5-00. Мы работаем для вас!

^D

Идиот:

# dd if=/dev/rmt8 of=/vmunix

 

Ситуация 8. Пользователям необходима электронная телефонная книга

Технический бандит. Пишет программу на RDBMS, perl и Smalltalk. Отчаявшиеся пользователи возвращаются к использованию записных книжек.

Администратор-фашист. Устанавливает Oracle. Отчаявшиеся пользователи возвращаются к использованию записных книжек.

Маньяк. Предлагает пользователям хранить данные в едином сплошном файле и применять grep(1) для поиска телефонных номеров.

Идиот:

% dd ibs=80 if=/dev/rdisk001s7 | grep "Fred"

 

Заключение

Эта глава знакомит с пользовательской средой UNIX, а также с основными подсистемами этой операционной системы — файловой подсистемой, подсистемой управления процессами и памятью, и с подсистемой ввода/вывода.

Большое внимание уделено командному интерпретатору shell, и его языку программирования. Это, как вы убедились, достаточно мощный инструмент, который, в частности, используется при администрировании системы и конфигурации процесса инициализации UNIX. В конце главы приведены наиболее распространенные утилиты, которые можно найти в любой версии UNIX.