20.5. Управление переключением виртуальных консолей
Для того чтобы найти неиспользуемую VC (другими словами, консоль, на которую в данный момент не ссылается ни один открытый файловый дескриптор в процессах) и активизировать ее, используется команда управления вводом-выводом
VT_OPENQRY
.
retcode = ioctl(fd, VT_OPENQRY, &vtnum);
if ((retcode < 0) || (vtnum == -1)) {
perror("myapp: нет доступных виртуальных терминалов");
/* выполнить соответствующее действие */
}
Если в настоящее время используется менее 63 VC, и все из них заняты, то ядро автоматически выделяет память для новой VC [152] .
Для запуска переключения на другую VC (например, на ту свободную консоль, которую вы только что обнаружили) используется команда управления вводом-выводом
VT_ACTIVATE
. Если нужно подождать до тех пор, пока консоль не станет активной, применяется команда
VT_WAITACTIVE
. Смена консоли может занять некоторое время, возможно, несколько секунд. Это объясняется тем, что активизируемая консоль может находиться в графическом режиме, при этом содержимое экрана нужно реконструировать из памяти, выбрать из буфера обмена или восстановить каким-то другим способом, отнимающим немало времени [153] .
152
В большинстве остальных систем с виртуальными консолями или терминалами динамическое распределение для них не производится.
153
Отдельные системы (но не Linux) инициируют автоматическое переключение, если вызывается консоль, которая не исполняется в текущий момент.
ioctl(fd, VT_ACТIVATE, vtnum);
ioctl(fd, VT_WAITACTIVE, vtnum);
Для осуществления контроля над переключениями VC или для получения уведомлений о подобных переключениях необходимо предусмотреть надежные обработчики сигналов с
sigaction
, как обсуждалось в главе 12. Здесь мы применяем
SIGUSR1
и
SIGUSR2
; если нужно, вы можете использовать любые другие два сигнала, которые не предназначены для других целей (например,
SIGPROF
и
SIGURG
). Просто убедитесь, что выбранные вами сигналы удовлетворяют перечисленным ниже критериям.
• Они не требуются остальным системным функциям, особенно это касается тех сигналов, которые не могут быть перехвачены или проигнорированы.
• Они нигде не используются в вашем приложении для других целей.
• Они не представляют один и тот же сигнальный номер с двумя различными именами, как
SIGPOLL
и
SIGIO
(определения смотрите в
/usr/include/asm/signal.h
,
либо ограничьте себя использованием сигналов из табл. 12.1).
void relsig(int signo) {
/* выполнить соответствующее действие для освобождения VC */
}
void acqsig(int signo) {
/* выполнить соответствующее действие для запроса VC */
}
void setup_signals(void) { struct sigaction sact;
/* He маскировать никаких сигналов в то время,
* когда активизированы данные обработчики. */
sigemptyset(&sact.sa_mask);
/* Здесь может понадобиться добавление вызовов sigaddset,
* если существуют сигналы, которые нужно маскировать
* при переключении VC. */
sact.flags = 0;
sact.sa_handler = relsig;
sigaction(SIGUSR1, &sact, NULL);
sact.sa_handler = acqsig;
sigaction(SIGUSR2, &sact, NULL);
}
После этого потребуется изменить стандартный режим VC (
mode
) с
VT_AUTO
на
VT_PROGRESS
, пока консоль уведомляется об обработчиках сигналов путем установки
relsig
и
acqsig
.
void control_vc_switching(int fd) {
struct vt_mode vtmode;
vtmode.mode = VT_PROCESS;
vtmode.waitv = 1;
vtmode.relsig = SIGUSR1;
vtmode.acqsig = SIGUSR2;
vtmode.frsig = 0;
ioctl(fd, VT_SETMODE, &vtmode);
}
Обработчики сигналов, которые вызываются тогда, когда консоль находится в режиме
VT_PROCESS
, не должны соглашаться на переключение. Говоря более точно, обработчик
relsig
может отклонить разрешение на переключение VC. Обработчик
acqsig
, как правило, управляет процессом передачи консоли, но существует вероятность того, что он инициирует переключение на другую консоль. Будьте внимательны при кодировании обработчиков сигналов, чтобы избежать вызова любых нереентерабельных библиотечных функций. POSIX.1 устанавливает, что функции, перечисленные в табл. 12.2, являются реентерабельными. Значит, вы должны считать все остальные функции нереентерабельными, особенно, если хотите написать переносимую программу. Обратите внимание, в частности, на то, что
malloc
и
printf
являются нереентерабельными.
Ниже приведены примеры функций
relsig
и
acqsig
, выполняющих полезную работу. Особо следует отметить, что для функции