учебники, программирование, основы, введение в,

 

Устройства, терминалы и процессы

Устройства
В лекции 7 уже упоминалось, что для управления UNIX-системой довольно малого: выделить на машине линию, способную принимать и получать текстовые данные (по сути, поток байтов) и научить пользователя эти данные в линию вводить и читать их оттуда. Для такой цели стандартом предусмотрено оконечное устройство (оконечным оно названо потому, что на нем заканчивает путь передаваемая информация) или терминал.
Прежде чем говорить о терминалах, рассмотрим одно специфическое для UNIX понятие, которое нам понадобится в дальнейшем, - понятие устройства (device). Дело в том, что слово "устройство" в UNIX понимается двояко. С одной стороны - это внешнее устройство, то есть некая аппаратная часть компьютера, которая занимается обработкой или хранением данных. Жесткий диск, видеоадаптер, сетевой интерфейс, последовательный порт - все это примеры внешних устройств. Они как-то работают, и система может ими управлять сообразно потребностям; однако пользователю все тонкости работы с внешними устройствами на низком уровне вряд ли понадобятся. Если ему даже и нужен доступ к диску безо всякой файловой системы, то хорошо бы при этом не думать о том, в какие порты ввода/вывода нужно записывать определенные значения, потому что для разных типов оборудования эти значения будут различными. Вспомним, что унификация доступа - задача самой системы, а пользователю следует предоставлять универсальные команды, применимые к любым типам дисков и к любым видам дисковых контроллеров.
Таким образом, если посмотреть с другой стороны, т. е. с точки зрения пользователя, разнообразие устройств должно диктоваться только разнообразием их функциональности. Но и совсем различные устройства должны поддерживать простейшие способы передачи данных, вроде "открыть устройство-прочитать-закрыть" или "открыть устройство-записать-закрыть". Для этих операций уже придумана абстракция: файл. (Есть ли у понятия "файл" определение? Думается, что должно быть. Например, такое: "Файл - это именованная область данных".) Поэтому в UNIX точки доступа к устройствам (чаще говорят просто "устройства") размещаются в файловой системе: подобно файлам, устройства имеют имена, в них можно писать и можно из них читать. Иногда устройства называют еще файл-дырками, потому что наглядно можно себе представить устройство в виде дырки в файловой системе: все данные, которые мы записываем в такой файл, проваливаются в него, не занимая места на диске, прямо к драйверу, и читаем мы данные не с диска, а из дырки, куда их драйвер подкладывает. Традиционно устройства (или, что одно и то же, их файл-дырки) размещаются в UNIX в каталоге /dev (в некоторых системах, например, в Solaris, содержимое каталога /dev - лишь символьные ссылки на реальные устройства, тогда все сказанное ниже относится именно к ним), хотя создать устройство и пользоваться им можно в любом месте файловой системы, которая поддерживает файл-дырки.
Команда ls -l /dev покажет нам содержимое этого каталога (в некоторых системах - весьма внушительное). Не будем подробно рассказывать обо всем, что можно там увидеть (кое-что описано в лекции 10), но отметим, чем отличается формат выдачи ls для файл-дырки от того, что выводится для обычного файла. Вместо символа в самом начале строки ls пишет c или b, а в том месте, где ls показывала размер, стоят два числа. Буквы c и b обозначают символьное и блочное устройство соответственно (устройство, обмен данными с которым производится по одному символу или только блоками определенных размеров; это разделение чисто условное: в некоторых системах, например в FreeBSD, все устройства в конце концов сделали символьными), а два числа вместо размера - это так называемые старший и младший номера устройства (major device number, minor device number).
Старший номер устройства указывает, какой драйвер будет обрабатывать запросы на чтение из этого файла или запись в него, младший - к какому из нескольких внешних устройств или разделов идет обращение, а также один из возможных методов такого обращения. Например, в FreeBSD5.2 все звуковые устройства имеют старший номер 30, а младшие номера у них различны: 0 - /dev/mixer0 (микшер), 3 - /dev/dsp0.0 (оцифрованный звук), 4 - /dev/audio0.0 (звук в формате "sparc audio") и т. п. Отсюда следует, что называться устройства могут как угодно, потому что действительную информацию система получает именно из этих номеров.
Символьные ссылки
Вполне возможно, что ls -l /dev выдаст вам среди прочего нечто похожее на
lrwxr-xr-x  1 root   wheel  5 Jun 2 20:19 /dev/cdrom -> hdc
Что это за тип файла l, и что это за "->" ближе к концу строки? Так ls обозначает символьную ссылку - особый вид файла, не содержащего ничего, кроме имени другого файла. Когда ядру приходит запрос на открытие символьной ссылки, оно для начала подменяет ее имя именем файла, в ней находящимся. Если этот файл в свою очередь тоже символьная ссылка, операция повторяется до тех пор, когда очередное имя не окажется чем-то иным (например, файлом) или вообще будет отсутствовать (тогда произойдет ошибка). Из сказанного ls следует, что вместо /dev/cdrom будет взят файл /dev/hdc. Кстати, чтобы попросить ls проделать эту работу самостоятельно, можно добавить ключ -L, тогда для /dev/cdrom выведутся характеристики файл-дырки /dev/hdc. Конечно, создавать символьные ссылки может любой пользователь и в любом месте, где ему разрешено что-либо создавать. Вы можете запросто сказать что-нибудь вроде
$ cd
$ ln -s /tmp/mytmpdir tmp
причем /tmp/mytmpdir вовсе не обязан существовать! До тех пор пока вы не обращаетесь к этому файлу, его отсутствие никого не беспокоит. Можно теперь создать /tmp/mytmpdir
$ mkdir /tmp/mytmpdir
и бесцельная ссылка tmp в вашем домашнем каталоге начнет указывать на вновь созданный.
Индексные дескрипторы и жёсткие ссылки
Если символьная ссылка указывает на существующий файл, у того образуется как бы два имени - "настоящее" и "ссылочное". Так или иначе, мы всегда имеем дело с двумя различными объектами: специальным файлом-символьной ссылкой и любым (в том числе - отсутствующим) объектом файловой системы, на который эта ссылка указывает. На самом деле внутри одной файловой системы у каждого файла может быть сколько угодно "настоящих" имён в каких угодно каталогах!
Дело в том, что уникальный идентификатор файла - не имя, и не полный путь, а так называемый индексный дескриптор (i-node). Индексный дескриптор - это некое целое положительное число, присваиваемое каждому файлу в файловой системе (индекс) и служебная область данных, описывающая свойства этого файла (права доступа, тип, размещение на диске и т. п.). Имя же файла - не более, чем запись в каталоге, связывающая его и индексный дескриптор. Собственно, файловой системой в UNIX как раз и называется область диска, содержащая систематизированные при помощи индексных дескрипторов данные. При обращении к файлу ядро первым делом выясняет, в какой файловой системе он находится, а затем - какой inode ему соответствует. Таким образом, в разных файловых системах могут встречаться дескрипторы с одинаковыми индексами.
Такое дополнительное имя называется жёсткой ссылкой на файл (hard link), Собственно, все имена файла - жёсткие ссылки на него, они равноправны независимо от времени возникновения. Создать жёсткую ссылку на файл (завести ещё одно имя) можно с помощью команды ln (link). Параметры эта команда имеет такие же, как и cp, но копирования данных не производит, а только создаёт в соответствующем каталоге новое имя для существующего файла. В нашем примере мы создаём два файла, заводим жёсткую ссылку на первый и убеждаемся, что и содержимое, и значение i-node (первое поле команды ls -il) у файла first и third совпадают. Стоит обратить внимание и на третье поле этой команды - в нём как раз и показано количество имеющихся ссылок на файл.
$ echo "First file" > first
$ echo "Second file" > second
$ ln first third
$ ls -il first second third
706579 -rw-r--r-- 2 george staff 11 авг 25 23:35 first
709607 -rw-r--r-- 1 george staff 12 авг 25 23:35 second
706579 -rw-r--r-- 2 george staff 11 авг 25 23:35 third
$ cat first second third
First file
Second file
First file
$ rm first
$ ls -il second third     
709607 -rw-r--r-- 1 george staff 12 авг 25 23:35 second
706579 -rw-r--r-- 1 george staff 11 авг 25 23:35 third
Как видно из примера, удаление (rm) применяется к имени файла, и если на него было больше одной ссылки, удаляется только имя, а счётчик ссылок уменьшается на единицу. И только когда счётчик ссылок доходит до нуля, место, занимаемое файлом, освобождается. Кстати, если файл с единственным именем был открыт, и после этого удалён, с ним всё ещё можно работать - до тех пор, пока он не закрыт. При этом индексный дескриптор занят. А вот имени не задействовано ни одного. Таким методом пользуются программы, не желающие, чтобы в их временные файлы заглядывал кто бы то ни было.
Жёсткие ссылки возможно заводить только на файлы. Если представить себе жёсткую ссылку на каталог, помещённую в его собственный подкаталог, и вспомнить, что оба этих имени - "настоящие", то становится понятна вся опасность ссылок на каталог. В самом деле, когда команде ls -R остановиться при рекурсивном обходе такого каталога?

Терминалы
Упомянутый выше стандарт - документ, на наш взгляд, несколько однобокий: подробнейшим образом описывая логику передачи информации и поведения терминала в различных ситуациях, он очень мало говорит о логике и способах предъявления ее человеку. Задумывалось это следующим образом: терминал может общаться с пользователем как угодно: быть просто принтером с клавиатурой или довольно хитрым приспособлением, в котором можно подготовить и отредактировать целый блок текста. Стандарт описывает лишь то, как терминал будет общаться с компьютером. В самом деле, терминал системы IBM/370 (в нашей стране прозванный "EC-овским терминалом") как раз и был микрокомпьютером, в котором отдельно от основной системы можно было подготовить целый экран текста, затем нажатием кнопки Send отослать его машине (напоминает пакетный режим обработки заданий: слишком архаичная система IBM/370 плоховато справлялась с интерактивным режимом работы). Когда система "зависала", на этом терминале можно было в ожидании перезапуска да же играть в "змея" - программу, написанную на автокоде самого терминала.
Такой подход к оконечным устройствам оказался слишком дорогостоящим. К тому же производством терминалов занялись люди довольно далекие от компьютерной отрасли: изготовители печатных машинок. Первые терминалы выпускались для издательских систем, переведенных на цифровой способ работы. Это от механической печатной машинки достались нам символ и клавиша Tab: для набора таблиц имелась специальная рейка с подвижными ползунками-табулостопами; нажатие клавиши Tab приводило к тому, что каретка передвигалась вправо до очередного ползунка. Отсюда же и русское название клавиши Backspace - "забой": поскольку напечатанный на бумаге символ стереть нельзя, для удаления приходилось возвращать каретку на одно знакоместо назад и "забивать" этот символ черным прямоугольником-"марашкой".
Линия, к которой подключен терминал, передает данные последовательно. Последовательно, т. е. один за одним, вводит пользователь символы с клавиатуры. А вот экран терминала, как правило, представляет собой прямоугольник из знакомест (чаще всего - размером 80x24). Этот прямоугольник можно неплохо использовать для усиления наглядности выдачи. Если последовательно вывести на экран ********, * [Ok] * и ********, получится что-то вроде кнопки:
********
* [Ok] *
********
Управляющие символы и последовательности
Вообще, хотелось бы, не отказываясь от идеи последовательной передачи текстовых данных, уметь использовать все пространство экрана в личных целях: "рисовать" кнопки, меню, помещать выдачу в определенное место экрана, выделять символы и т. д. Для этого разными производителями терминалов было (увы, независимо друг от друга) изобретено понятие управляющего символа. Идея в том, что несколько передаваемых на экран терминала символов не отображаются как значки текста, зато, приняв такой символ, терминал что-нибудь делает.
Например, символ ^M (возврат каретки, carriage return) заставляет терминал переместить курсор в начало строки (курсор в терминале аналогичен каретке пишущей машинки: это то место, где будет напечатан следующий символ), а символ ^J (перевод строки, line feed) - переместить курсор на одну строку вниз. С CR и LF происходит немало путаницы. Это, к сожалению, обычное дело, если речь идет о внешнем устройстве под названием "терминал". Однако, если вообразить себе все возможные способности терминала (очистка областей экрана и строки, прокрутка всего экрана и отдельных прямоугольных "окон" внутри него, выделение текста подчеркиванием, яркостью и цветом, смена шрифта и т. д.), становится понятно, что набором из 32 управляющих символов (так в стандарте) обойтись не удастся.
Несколько слов об обозначении управляющих символов. Во-первых, можно указывать способ, которым определенный символ подается с клавиатуры. В документации принято писать ^буква для клавиатурной комбинации Ctrl+буква. При нажатии Ctrl старшие три бита ASCII-кода символа обнуляются, и символ с получившимся кодом передается в терминальную линию. Например, ^[ обозначает Escape (ASCII код 27), что соответствует нажатию клавиши Ctrl и [ (ASCII код 91). Иногда вместо ^буква используются обозначения, принятые в языке Си: универсальное, вида \0восьмеричный_ASCII_код, или специальные, такие как \n (от "newline", он же ^J и \012), \t ("tab", ^I и \011) или \E ("Escape", ^[ или \033).
Итак, на все функции терминала управляющих символов не хватает, и придется подумать об управляющей последовательности: специальный управляющий символ и несколько следующих за ним символов не отображаются на экране, а составляют команду. Тут-то производители печатных машинок вволю поиздевались над стандартами (точнее сказать, думать о них забыли; стандарт ANSI появился в 1979 году, когда было уже слишком поздно). У кого таким спецсимволом установлен Escape (^[), а у кого - нет. В некоторых терминалах есть управляющая последовательность позиционирования курсора, позволяющая выводить текст в определенное место экрана, а в некоторых - нет. Координаты могут начинаться с нуля, а могут - с единицы. Само собой, у разработчиков каждой модели терминала есть свое понимание того, из чего именно состоит каждая управляющая последовательность. Зачастую терминалы "говорят" на языках, ничем друг на друга не похожих. В лучшем случае управляющая последовательность для одного типа терминалов бессмысленна для другого, в худшем случае - перенастраивает его совершенно недопустимым образом.
Особенно прославился терминал Hazeltine, который считает управляющим обыкновенный текстовый символ ~, поэтому его нельзя на этот терминал выводить просто так. Примитивная последовательность очистки экрана для терминала Wyse 30 выглядит как ^[*, для VT100 - как ^[[2J, а для Hazeltine 1500 - ~^\. Управляющие последовательности называют еще Escape-последовательностями (esc-sequence), потому что чаще всего в них используется именно этот символ.
Между тем сама идея текстового интерфейса развивалась и захватывала все новые области применения, вдобавок к интерфейсу командной строки. Если терминал умеет хотя бы позиционировать курсор (то есть имеет управляющую последовательность, получив которую он переставляет курсор, а с ним и позицию вывода текста в заданное знакоместо экрана), то с помощью этой функции можно попробовать "рисовать" из символов списки, меню, кнопочки и прочие элементы интерфейса, основанного на выборе. В программах вроде Midnight Commander (или его предка - Norton Commander для ДОС) такой подход доказал свою эффективность и применимость для некоторых видов задач.
Для перемещения по текстовому полю весьма желательно использовать клавиши со стрелочками, с надписями Home, Page Up, F1 и т. п. На разных терминалах таких клавиш может быть очень много, а может не быть вообще. Более того, если уж мы отказались от интеллектуальности терминала, интерпретировать нажатие этих клавиш будет сама система. История повторяется: так же, как и выводимые на терминал управляющие последовательности, вводимые с терминала "стрелочки" - на самом деле тоже последовательности специальных символов. И опять каждый изготовитель ладит свое: в каждом виде терминала несимвольная клавиша может возвращать все, что заблагорассудится конструктору. Терминалы Wyse 30, VT100 и Hazeltine 2000 выводят при нажатии на стрелку вверх соответственно ^K, ^[OA и ~^L.
Преодолеть это можно естественным для проективной системы путем: разделить абстракцию и реализацию. Так называемые "умения", или характеристики терминала (terminal capabilities), любой уважающий себя программист должен использовать не напрямую в виде ESC-последовательностей, а косвенно, выбирая их по имени из таблицы характеристик терминалов. Таких таблиц в UNIX имеется два вида. Одна - из гнезда BSD, огромный текстовый файл под названием termcap (исторически он лежит в /etc, однако более правильно помещать его в /usr/share/misc, см. главу 13). Это более старый вариант таблицы характеристик; для быстрого чтения характеристик ее преобразуют иногда в формат BerkleyDB, однако исходный файл всегда текстовый.
Второй вид таблиц характеристик, terminfo, - из гнезда USG. Это целая база данных с нетекстовым форматом полей и специальными инструментами для чтения и записи. Каждому типу терминала соответствует отдельный файл описания, находящийся в подкаталоге /usr/share/lib/terminfo, имя которого соответствует первой букве имени терминала. Для работы с описанием терминала из terminfo необходима специальная библиотека и утилиты (в первую очередь infocmp и tic), ее использующие.
При появлении на горизонте очередного терминала с новыми прихотливыми возможностями этот терминал описывается в понятиях termcap или terminfo. Программа, работающая с терминалом, может использовать специальную библиотеку (она называется curses) и работать в прикладных терминах: строки, колонки, окна, рамки и т. п., независимо от типа терминала.

Терминальная линия
Подобно тому как понятие "устройство" обозначает и внешнее устройство-"железяку", и его логическое представление внутри системы, понятие "терминал" применяется и к описанным выше устройствам первого типа, и к специальным устройствам второго типа, именуемым tty. Подав команду ls /dev/*tty*, мы можем обнаружить подчас несметное количество устройств с именем, включающим в себя tty. Все эти устройства - терминалы, или терминальные линии (в о терминальной линии рассказывается достаточно подробно; мы же ограничимся довольно поверхностным рассмотрением, достаточным для того, чтобы у пользователя не возникало тупиковых ситуаций в работе). С точки зрения UNIX терминальные линии - не просто устройства, передающие байты: некоторые из этих байтов (например, уже рассмотренные нами в лекции 7 символы удаления) имеют специальное значение. В самом деле, при наборе текста в терминале символ удаления текущего ввода (обычно это ^U) обрабатывает сама система (читай: терминальная линия), а программа, которая ждет ввода данных, этого не замечает. Интерфейсом командной строки подсказан построчный режим работы терминальной линии: все, что вводится пользователем, обрабатывается ею и накапливается в специальном буфере, покуда не придет символ конца строки. После этого весь буфер передается ожидающей ввода программе (shell, например), а линия начинает заполнять новый.
Поведение терминальной линии не зависит от способностей терминала: это свойство самой системы, так называемый обработанный режим передачи данных (cooked mode). В некоторых случаях, например если программа хочет самостоятельно обрабатывать все вводимые символы, терминальную линию можно перевести в "сырой" режим (raw mode). Всевозможные настройки терминальной линии выдает команда stty -a (или stty all). В числе прочих настроек линии будут, например, настройки преобразования символов перевода строки (LF) и возврата каретки (CR) друг в друга при вводе и выводе. В UNIX принято, что строки в текстовом файле завершаются одним символом - New Line, NL. Этот символ обычно приравнивается к символу перевода строки, LF. Между тем на многих терминалах при выводе строки необходимы, как на печатной машинке, два символа: сначала - CR, потом - LF. Иногда терминалам достаточно одного символа, но CR. И наоборот, при вводе в клавиатуры клавиша Enter может посылать и LF, и CR, и CR+LF. Некоторые настройки stty определяют, что UNIX должен понимать как NL при вводе с клавиатуры и выводе на экран текущего терминала.
В выдаче stty -a мы обнаружим знакомые команды: ^C, ^D, ^U и т. д. Что они значат? Команда stty позволяет переопределить простейшие команды удаления вводимого текста: удаление символа (erase, как правило, это - ^H или ^?), слова (werase, как правило, ^W) и всей строки (kill, как правило, ^U). Зачем это бывает нужно? К примеру, терминал Volker Craig возвращает ^U при нажатии стрелки вправо. Хотелось бы, чтобы этот символ передавался программе, а не обрабатывался терминальной линией. Команда stty kill ^E переопределит команду удаления всей введенной строки на Ctrl+E. Обратите внимание: ^E в аргументах утилиты - это два символа, stty облегчает нам работу, соблюдая договоренность о представлении управляющих последовательностей.
Другая группа команд управляет самой терминальной линией: если, например, мы хотим приостановить вывод на экран довольно объемного текста (позабыв вовремя воспользоваться утилитой more или less), можно использовать команду stop (обычно ^S): она сообщит системе, что терминал временно не принимает данные, и очередная операция записи не завершится до тех пор, пока линия не получит команду start (^Q). Если некая программа уже прочла все данные из файла, то очередная операция чтения завершится с диагностикой EOF (End Of File), которую программа должна обработать. Если же чтение идет с терминала, что для самой программы неотличимо от чтения из файла, команда терминальной линии eof (обычно ^D) приведет к тому же результату.
Третья группа команд имеет особенное значение. Это способ подавать программе, обменивающейся данными с нашим терминалом, сигналы с клавиатуры. Как сказано в , сигнал - это способ обмена короткими сообщениями между процессами в UNIX. По логике работы сигнал на системном уровне напоминает прерывание на аппаратном. Процесс может игнорировать сигнал, перехватывать (обрабатывать его) или оставлять на обработку системе. Сигнал процессу может исходить от другого процесса, а может зародиться в недрах системы. Сигналов не очень много; для обозначения различных, с точки зрения отправителя сигнала, ситуаций используются разные сигналы. В частности, для полной остановки процесса используется команда терминальной линии intr (обычно это ^C), которая подает сигнал INT (от interrupt, прерывание) процессу, чей стандартный ввод связан с этим терминалом.
Процессы и сигналы
С конкретной терминальной линией может быть связан по вводу только один процесс. Этот процесс называется активным и, помимо свойства принимать сигналы с клавиатуры, единственный может считывать с нее данные. Остальные запускаемые пользователем процессы могут только выводить на терминал, вводить они должны откуда-нибудь еще. Эти процессы называются фоновыми. Если процесс, получивший сигнал INT, не станет его обрабатывать, система прекращает его работу. При этом активным становится родительский процесс, и т. д. вплоть до демона getty, который и есть первый процесс, связанный по вводу с терминальной линией. Более подробно о getty будет рассказано ниже, а про терминальные линии, группы процессов и пр. можно прочитать в.
Если необходимо остановить процесс во что бы то ни стало, следует использовать сигнал QUIT (quit, обычно ^\), который обычно не перехватывается. Процесс можно приостановить при помощи сигнала STOP (susp, ^Z). Тогда он временно остановится, перестанет быть активным и не будет работать до тех пор, пока не получит сигнал CONT (уже не с клавиатуры, потому что, перестав быть активным, он потеряет к ней доступ; многие командные интерпретаторы имеют команды fg и bg для того, чтобы приостановленный процесс продолжил работать как активный или как фоновый соответственно).
Кстати, отправкой сигналов процессам не с клавиатуры, а программным путем занимается утилита kill. Ей передается обязательный параметр - идентификатор процесса (PID), и необязательный - имя или номер сигнала. По умолчанию kill посылает сигнал TERM, но с ее помощью можно послать самый страшный сигнал, KILL, обрабатывать который никакая программа не вправе. Чем не повод для именования утилиты? Для того чтобы приостановить фоновый процесс с PID, например 1495, надо сказать kill -STOP 1495, а для того, чтобы процесс возобновил работу, - kill -CONT 1495. Во многих версиях UNIX существует утилита killall, которой вместо PID можно указать имя запущенной программы, но, во-первых, процессов, образованных запуском одной и той же программы, может быть несколько, и killall пошлет сигнал каждому, а во-вторых, в некоторых ветках UNIX (например, в Solaris), эта утилита игнорирует все параметры и... делает то, что можно в таком случае ожидать: убивает всех! Для того чтобы узнать PID процесса, иногда задействуют утилиту pidof, но это можно сделать, просто просмотрев выдачу ps - утилиты, выводящей таблицу процессов.
Как уже отмечалось, первой система связывает с терминалом системную утилиту getty, задача которой - определить тип терминала и параметры ввода/вывода, когда на линии возникнет активность, настроить линию и вызвать системную утилиту, которая займется пользовательскими делами (чаще всего - утилиту login для аутентификации пользователя). Настраивать линию не так-то просто, если к ней подсоединен модем, и действительным терминальным устройством может оказаться какой угодно терминал пользователя. Когда работа с линией закончена, модем "вешает трубку", а работающая с линией программа получает сигнал HUP (от Hang UP). При получении этого сигнала система должна забыть все старые настройки терминальной линии (пользователь-то отсоединился) и перезапустить getty. Правила запуска getty и других обработчиков терминальной линии указываются в /etc/inittab (для гнезда USG) или в /etc/ttys (для гнезда BSD). Это породило еще одну договоренность UNIX: если демон получает сигнал HUP, он должен перезапуститься и заново считать настройки; например, если мы изменили настроечный файл inetd, он заметит эти изменения после killall -HUP inetd.
Если в компьютере есть порт последовательной передачи данных (на IBM PC он обычно отвечает стандарту RS-232), он вполне подойдет для терминала. Поэтому в /dev будет соответствующее ему терминальное устройство с именем, содержащим подстроку tty: в Linux, например, оно будет носить имя ttyS0, а в FreeBSD - ttyd0. В FreeBSD, кстати, предусмотрено еще одно устройство, cuaa0, которое в действительности работает с тем же портом, но передаваемые данные не обрабатывает и сигналов не передает; одним словом, работает в "сыром" режиме. Редко когда последовательных портов у системы больше четырех (чаще их два). Откуда же тогда такой объем выдачи у ls /dev/*tty*?
Современная UNIX-система на основе IBM PC вообще не включает в себя такое внешнее устройство, как терминал. Да оно и незачем: в состав системного блока входят и клавиатура, и монитор, способный отображать графическую, а значит, и текстовую информацию. Правда, это вполне независимые устройства: нажатие клавиш на клавиатуре обрабатывает специальное устройство под названием контроллер клавиатуры, а монитор отображает состояние графического адаптера. Однако некая часть ядра системы, именуемая виртуальной консолью, выполняет функции терминала, работая с монитором и клавиатурой как с единым устройством. Под консолью в UNIX подразумевается терминал (возможно, не единственный), на который сама система выводит диагностику при работе и с которого ею можно управлять. Назначение виртуальной консоли в том, чтобы пользователь сидел за клавиатурой и монитором IBM PC как за терминалом, а UNIX "думал", что такой терминал у него всегда есть. Причем для удобства работы виртуальных консолей обычно организуется сразу несколько, нажатие в Linux или FreeBSD Alt+F6 приведет к тому, что вы окажетесь как бы перед терминалом с номером 6 и начнете работать с ним, а запущенная на устройстве /dev/tty6 (для FreeBSD - /dev/ttyv6) программа getty обнаружит активность именно на этом устройстве.

Псевдотерминалы
Большую часть терминалов-устройств в /dev составляют даже не виртуальные консоли, а так называемые псевдотерминалы. Это довольно курьезные устройства, не имеющие не только соответствующей аппаратной части, но даже и программной! UNIX управляется посредством терминала. Терминал не обязан быть аппаратным устройством (это мы видели на примере виртуальной консоли). На самом деле, любой источник адресованных системе команд в виде потока байтов может быть терминалом. Например, при установлении терминального соединения по сети роль дисплея и клавиатуры выполняет специальный демон (в последнее время для этого используется пакет Secure Shell, имя демона - sshd). Используя соответствующую утилиту (в нашем случае - ssh), пользователь подключается с одного компьютера к демону, запущенному на другом. Утилита читает с терминала пользователя и преобразует введенное сообразно сетевому протоколу SSH, после чего данные отсылаются демону. Демон преобразует полученное обратно в текстовый вид и записывает в специальное устройство по имени /dev/ptyp (точнее сказать, /dev/ptyp0, /dev/ptyp1 и т. д., так как для каждого такого сеанса связи необходимо свое устройство).
Устройство /dev/ptyp0 и остальные - очередной пример хитроумной файл-дырки: все, что записано в него, немедленно выводится на /dev/ttyp0, а все, что попадает в ttyp0, перенаправляется в ptyp0. И вот ttyp - уже полноценный терминал, с ним может быть связана по вводу какая-нибудь программа (скорее всего, shell), данные передаются в подготовленном виде, сигналы поступают и т. д. Нетрудно догадаться, что ttyp и есть псевдотерминал. Пара ptyp - ttyp напоминает канал ("|" из конвейера, описанного в лекции 11), с одним существенным отличием: эти устройства асимметричны, только ttyp работает как терминал: обрабатывает входной поток и подает сигналы.
Псевдотерминалами пользуется всякая программа, желающая заменить терминал: например, многочисленные варианты эмуляторов терминала для графической системы X Window (xterm, rxvt, gterm, konsole и т. д.). С одной стороны, обилие графических инструментов не может полноценно заменить командную строку, а с другой - диктует определенные принципы оформления. Поэтому для каждого вида рабочего стола в X Window существует своя программа, имеющая графическое окно, весьма похожее на терминал. Здесь, увы, возродилась традиция неразберихи в характеристиках: многие из них представляются системе как xterm (ортодоксальный эмулятор терминала для X Window), а в действительности слегка отличаются от него по управляющим последовательностям.
Кроме того, ptyp использует утилита screen, которая из одного канала передачи данных делает несколько терминалов (просто открывая несколько псевдотерминалов и переключаясь между ними; это удобно, если консоль в системе всего одна). У screen есть еще одно полезное свойство: его можно отсоединить от реального терминала, на котором он был запущен. При этом псевдотерминалы не освобождаются, и UNIX продолжает считать, что пользователь все еще работает в системе на нескольких терминалах, просто не вводит никаких команд. Впоследствии screen можно вновь присоединить к любому другому терминалу и продолжить работу.
Вы можете, скажем, поработать в screen за консолью системы и запустить там два сеанса shell: в одном поставить собираться из исходных текстов какой-нибудь большой проект (что иногда длится часами), а во втором вызвать текстовый редактор vi, да так и бросить. После чего вы отсоединяете screen от консоли (^A^D) и заканчиваете работу. Приехав вечером домой, вы, возможно, захотите проведать, как там идет сборка. Подключившись по Internet при помощи ssh к системе, вы присоединяете screen к текущему терминалу (screen -r; теперь он использует псевдотерминал, а не консоль, но какая системе разница?) и видите оба сеанса: в одном продолжается сборка, а в другом запущен и ничего не делает vi.
Во многих системах вы увидите изрядное количество ttyp и ptyp в /dev. Они заведены там впрок; программа, которая захочет открыть ptyp, получит первый, никем еще не занятый. Другие системы используют динамический метод выделения ptyp: очередное устройство создается, когда в нем возникает необходимость, и удаляется из /dev/, как только будет закрыто.

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г.Яндекс.Метрика