Заметки о Linux-консоли
3. Сколько бывает консолей

Алексей Федорчук
[email protected]

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

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

В отличие от FreeBSD, где максимально возможное число консолей определяется при конфигурировании ядра (и составляет для умолчального ядра GENERIC целых шестнадцать), консоли в Linux'е создаются (ло определенного предела) на лету, по мере возникновения в них необходимости. Типичный пример - запуск Иксов (или приложений, использующих SVGAlib). Каждый сеанс Иксов (а командой типа startx -- :# или xinit -- :# их можно запустить сколько угодно, вернее, на сколько хватит ресурсов) будет открыт в своей собственной виртуальной консоли, следующей по порядку после уже используемых.

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

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

За запуск процессов непосредственно после старта системы отвечает процесс init (/sbin/init), являющийся родительским (прямо или косвенно, через запуск пользовательских процессов) по отношению ко всем остальным процессам (во загнул-то!). А какие конкретно процессы он запускает - определяется его конфигурационным файлом, /etc/inittab.

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

c1:2:respawn:/sbin/agetty 38400 vc/1 linux
c2:2:respawn:/sbin/agetty 38400 vc/2 linux
c3:2:respawn:/sbin/agetty 38400 vc/3 linux
c4:2:respawn:/sbin/agetty 38400 vc/4 linux
c5:2:respawn:/sbin/agetty 38400 vc/5 linux
c6:2:respawn:/sbin/agetty 38400 vc/6 linux

То есть мы видим простую colon-separated таблицу о четырех полях и шести записях. Последнее, как нетрудно догадаться, соответствует "умолчальному" количеству виртуальных консолей. А значение полей - следующие: идентификатор записи, уровень (или уровни) выполнения (runlevels), для которого эта запись имеет силу, акция, выполняемая при этом, и собственно исполняемая команда (в данном случае - команда активизации консоли). Сразу оговорюсь, что в других системах значения всех полей, кроме третьего, могут быть несколько иными (или совсем другими).

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

Что такое уровень выполнения - вопрос в данном случае неуместный. Для наших сегодняшних целей достаточно знать, что это то состояние системы, в которое она приходит при нормальной загрузке по умолчанию. В моей системе такое умолчальное состояние достигается при уровне 2, в других здесь может стоять значение любого из доступных уровенй. Так как runlevel 0 соответствует останову системы, runlevel 1 зарезервирован за однопользовательским режимом, когда по определению активизируется только одна виртуальная консоль, а на runlevel 6 происходит перезагрузка системы, то доступными оказываются один из runlevels в диапазоне 2-5 или даже все они - в виде 2345.

Процедуру respawn в третьем поле можно, скорее всего, увидеть в /etc/inittab из любой Linux-системы. Она обеспечивает повторный вызов другого экземпляра того же процесса, который запускается командой из четвертого поля.

Команда же в четвертом поле - это обычно вариации на тему getty, что означает "получение терминала" (get tty). Она вызывает активизацию виртуальной консоли, указанной в качестве аргумента (в нашем примере vc/?) и запуск на ней программы login, обеспечивающей авторизацию пользователя в системе, после чего замешается программой, определенной в качестве его командной оболочки по умолчанию (login shell). Причем все эти программы (getty, login, пользовательский login shell) выполняются внутри одного и того же процесса. Хотя в ходе его протекания процесс этот меняет своего владельца - с root'а на того юзера, который авторизовался на этой консоли.

Завершение пользовательского сеанса работы приводит к смерти этого процесса, однако не вызывает деактивизации данной виртуальной консоли, так как процедура respawn обеспечивает "регенерацию" процесса getty с повторным вызовом программы login и (в случае авторизации) login shell. То есть всем определенным в /etc/inittab виртуальным консолям суждена вечная (до останова или переазгрузки системы) жизнь.

Еще несколько замечаний о содержимом четвертого поля. В приведенном примере использована команда /sbin/agetty (из пакета util-linux). Полный путь к ней указан не случайно - очевидно, что файл /etc/inittab задействуется системой раньше, чем считывается значение переменной $PATH из общесистемного (или, тем более, пользовательского) профильного файла типа /etc/profile. Однако сама по себе команда получения терминала в других системах может быть иной - например, /sbin/mingetty или что-нибудь подобное.

Первый аргумент команды обозначает скорость соединения по последовательной линии. Очевидно, что для виртуальной консоли никакого физического смысла он не имеет и спокойно мог бы быть опущен, что обычно и делается при использовании программы /sbin/mingetty (что и не удивительно, так как mingetty и приспособлена только для работы с вирутальными терминалами). Однако в /sbin/agetty попытка удалить этот аргумент приводит к ошибке (и, соответственно, невозможности загрузки). Так что лучше придерживаться той формы вызова getty, которая принята в вашем дистрибутиве.

Следующий аргумент - файл устройства активизируемой виртуальной консоли. При использовании devfs обязательно давать его именно в новой номенклатуре, вне зависимости от настроек демона devfsd (которые также вступают в силу после считывания /etc/inittab). Если devfs не используется, значение этого аргумента будет - tty1, tty2 и так далее.

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

Наконец, последний аргумент определяет тип "получаемого" командой getty терминала (в примере - стандартная linux-консоль). Он также не обязателен, однако дан мной для пущей определенности (да и вреда от него никакого). Кроме того, насколько я понимаю, указание типа обязательно, если в linux-консоли должен эмулироваться какой-либо иной терминал (вроде vt100 или vt220).

Теперь, зная, как описываются "изначальные" виртуальные консоли, легко догадаться, как изменить их количество. Для уменьшения оного (что несколько сэкономит ресурсы слабой машины) достаточно удалить (или закомментировать) соответствующее число строк. А для увеличения - создать должное количество новых по образу и подобию существующих, изменяя только идентификатор и имя файла устройств:

c7:2:respawn:/sbin/agetty 38400 vc/7 linux
c8:2:respawn:/sbin/agetty 38400 vc/8 linux

и так далее, вплоть до

c63:2:respawn:/sbin/agetty 38400 vc/63 linux

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

Заодно замечу, что сказанного достаточно только при использовании devfs. Если она не задействована, то нет гарантии, что файлы устройств tty7 и выше реально имеются в каталоге /dev. И тогда их нужно будет создать - с помощью сценария /dev/MAKEDEV или непосредственно командой mknod. В первом случе достаточно перейти в каталог /dev и запустить

$ .MAKEDEV -v tty7

ответом на что будет сообщение типа

create tty7 c 4 7 root:tty 666

знаменующее успех предприятия. Если же последует сообщение об ошибке (например, потому, что создание такого устройства не предусмотрено конкретным вариантом сценария MAKEDEV), придется обратиться к команде mknod, требующей указания типа устройства, старшего и младшего номеров, а также прав доступа - за деталями отправляю к man (1) mknod.

А мы тем временем вернемся к истинно толстовскому вопросу: сколько же виртуальных консолей человеку надо? Ответ на него зависит в первую очередь от преобладающей рабочей среды того человека - Иксы ли это или все-таки консоль.

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

А вот если стиль работы - преимущественно консольный, возможны варианты. Можно развести виртуальных консолей практически под каждую возможную задачу, и тогда 8-10 штук - вполне реально будут востребованы. До недавнего времени я придерживался именно такого подхода. И число консолей у меня иногда доходило до 11. Что требовало выработки четкой системы - какие задачи на каких консолях запускаются. А главное - неукоснительно этой системы придерживаться.

Для примера, моя система такова (это не пример для подражания, а просто возможный образец). Вне зависимости от числа консолей, последняя из них предназначается исключительно для запуска Иксов, а предпоследняя - для авторизации (при необходимости) в качестве root'а. Первые четыре консоли, напротив, служат для повседневной работы: на первой я пишу, на второй - смотрю ранее написанные тексты по теме (из которых надергиваю фрагменты с помощью мыши), на третьей запущен браузер (links), четвертая зарезервирована для выполнения необходимызх команд и (или) просмотра man-страниц.

Когда эта система окончательно устаканилась (а главное, следование ей дошло до рефлекторного уровня), я обнаружил, что перманентно мне довольно шести умолчальных консолей - четырех рабочих, одной для потенциального root'а и одной для Иксов. А необходимость в дополнительных терминалах возникает лишь время от времени, для кое-каких разовых акций (в основном копирования/перемещения файлов или просмотра каталогов). Или, напротив, для протяженного, но не требующего вмешательства, процесса, типа проигрывания mpeg-файлов (программой mpg123). И вот тут-то настало время вспомнить о возможности Linux'а (в отличие от FreeBSD) создавать виртуальные консоли на лету, для запуска конкретного процесса.

Очевидный способ открыть новую виртуальную консоль - запустить ту же программу из семейства getty, которая используется для инициации консолей умолчальных, например

$ agetty 38400 vc/9 login

Однако по ряду причин способ этот - не самый удобный. Тем более, что в пакете kbd этой цели служит штатная команда openvt (вероятно, и в console-tools существует аналогичное средство). Она имеет изрядное количество опций (о которых - см. man opennet), однако необходим ей лишь единственный аргумент - имя команды, которая будет исполнена на вновь активизированной консоли (при необходимости для этой команды не возбраняются собственные опции и аргументы). Так, в форме

$ openvt /bin/zsh

она активизирует ближайшую незадействованную консоль (например, 7-ю) и запустит на ней экземляр командной оболочки Z-Shell, команда

$ openvt login

выведет на следующей (скажем, 8-й) консоли предложение авторизоваться. А команда

$ openvt -l user_name

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

Номер активизируемой консоли можно задать явно, с помощью опции -c. То есть команда

$ openvt -c 63 mpg123 /path/*.mp3

начнет проигрывать на самой дальней консоли все mpeg-файлы из указанного каталога.

При открытии новой виртуальной консоли следует иметь ввиду, что она не будет полностью идентична по своим свойствам консолям "умолчальным". В частности, на нее автоматически не будет выведена "магическая последовательность", обеспечивающая активизацию так называемой карты соответствия (mapscreen), преобразующей кодировку клавиатурного ввода в кодировку экранного вывода. И потому, в случае стандартной для Linux русификации системы (ввод - KOI8, вывод - CP866) просмотреть в ней кириллическоий текст не удастся. Хотя и раскладка клавиатуры нашей новой консоли, и экранный ее шрифт будут вполне кириллическими. Впрочем, активизировать на ней mapscreen можно и вручную, но этому будет посвящена специальная заметка.

Во всех приведенных примерах текущей остается консоль, с которой была дана команда openvt (она назвается также управляющей консолью - это понятие нам потребуется в проследующих заметках). Чтобы одновременно с запуском процесса переключиться на консоль новообразованную, требуется опция -s. Однако и в том, и в другом случае в консоли, с которой запускалась команда openvt, возвращается приглашение командной строки, и в ней можно продолжать нормально работать. Хотя возможность эту можно и блокировать - с помощью опции -w командная строка в текущей (для openvt) консоли становится недоступной до завершения исполнения команды в новообразованной консоли.

Деактивация новообразованной консоли происходит либо естественным образом, по завершении запущенного на ней процесса (пример с запуском mpeg-проигрывателя), либо по принудительному выходу из работающей здесь программы (в примере с zsh и login - с помощью команды exit, а во втором случае также logout). Если команда openvt была дана с опцией -w, то есть блокировкой управляющей консоли, то из нее можно и прервать запущенный процесс - обычной клавишной косбинацией Control+C. При этом в примере с login никакой регенерации приглашения авторизоваться не происходит за отсутствием акции respawn.

Деактивированная тем или иным образом консоль как бы продолжает существоввать. То есть в нее можно перейти и даже посмотреть на остатки вывода выполнявшихся команд (хотя делать в ней, разумеется, ничего нельзя без повторной активации той же командой openvt). Для полного искоренения такой полуживой консоли предназначена специальная команда - deallocvt. В качестве аргументов ее выступают номера консолей, подлежащих окончательному истреблению. Без аргументов же она уничтожит все деактивированные консоли, то есть те, на которых в данный момент не исполняется никакого процесса. Разумеется, консолей умолчальных она не затронет - ведь на них в любом случае запущен процесс getty и порожденная им команда login.

Открывая консоли на лету, легко увлечься и перевалить за те 12, которые легко доступны по комбинации клавиш Alt+F# (к слову, количество активизированных консолей в данный момент можно посмотреть с помощью команды fgconsole). Как же получить доступ к этому консольному изобилию?

Во-первых, как уже говорилось, с помощью клавиши PrtScr в большинстве случаев можно как бы "пролистать" активизированные консоли, начиная с первой. Что, конечно, скучно, если требуется быстрый переход в консоль за номером 13... Во-вторых, к консолям с 13-й по 24-ю обычно возможен по комбинации клавиш Alt+Shift+F#. Однако, как уже было сказано ранее, оба эти способа возможны не всегда, так как зависят и от раскладки клавиатуры, и от ее типа. И к тому же не обеспечивают перехода на консоли с 25-й по 63-ю, буде таковые все же понадобятся.

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

$ chvt #

которая мгновенно перенесет нас в ту консоль, номер которой указан в качестве ее аргумента...

Открывать консоли с помощью openvt доступно только для root'а - это определяется правами доступа к файлам устройств неактивизированных консолей, хозяином которых является суперпользователь (активированная же консоль меняет хозяина на того пользователя, который в ней авторизовался). Поэтому любой запущенный на такой новообразованной консоли процесс также будет выполняться от лица администратора (если, конечно, это не процесс login с последующим входом в систему обычного пользователя). Такое положение не всегда желательно с точки зрения безопасности. Избежать же его можно с помощью иного способа активизации консолей, о которых я расскажу в следующей заметке.