切换虚拟终端
概述
linux 使用虚拟终端,这些虚拟终端对应/dev/tty1~/dev/tty8,它们由系统初始化时打开。
这个过程也在键盘的中断服务程序中完成。用户用 ALT+1..8 或 ALT+箭头来切换终端。每
个虚拟终端对应一个 tty_struct 和一个 termios 结构。Termios 结构存储输入输出及控制模式,
一些底层参数。Tty_struct 和终端的关系如同 task_struct 和进程的关系。一个 tty 被打开,
就对应一个 tty_stract,对终端的一切处理都围绕 tty_strct 进行。Termios 也被 tty_struct 的一
个指针指向。当然,tty_struct 和 termios 结构相对独立,tty 被完全关闭时,tty_struct 也就
不在存在,而 termios 依然存在。
另:
1 与虚拟终端相对的是实终端。它对应 /dev/tty0 或 /dev/console 二者设备号均为
4,0(2.0.35)系统初始时 tty 的初始化分早期和晚期。早期初始化支持实终端,晚期初始化
支持虚拟终端。此时/dev/tty0 或/dev/console 对应当前终端。
2 伪终端正如其名,与控制台终端除了最高层的输入输出功能类似外,底层的实现大相
径庭。
终端切换的流程:
want_console = nr;
mark_bh(CONSOLE_BH);
static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
handle_scancode(scancode);
(*key_handler[type])(keysym & 0xff, up_flag);
k_hand key_handler[]
键盘特殊输入的特殊处理函数数组。
static k_hand key_handler[16] = {
do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
do_meta, do_ascii, do_lock, do_lowercase, do_slock,
Keyboard_interrupt
Key_handler(do_cons)
Set_consle
Console_bh
Change_console
complete_change_console
Update_screen
do_ignore, do_ignore, do_ignore
};
do_cons()
改变终端的最上层函数
static void do_cons(unsigned char value, char up_flag)
{
if (up_flag)
return;
set_console(value);
}
set_console()
设置 want_console 为欲切换到的终端。设置 console 的 bottom half 标志。
extern inline void set_console(int nr)
{
want_console = nr;
mark_bh(CONSOLE_BH);
}
console_bh()
static void console_bh(void)
{
是否要切换 console
if (want_console >= 0) {
if (want_console != fg_console) {
change_console(want_console);
/* we only changed when the console had already
been allocated - a new console is not created
in an interrupt routine */
}
want_console = -1;
}
if (do_poke_blanked_console) { /* do not unblank for a LED change */
do_poke_blanked_console = 0;
poke_blanked_console();
}
}
change_console()
切换终端的实际动作。
void change_console(unsigned int new_console)
{
if ((new_console == fg_console) || (vt_dont_switch))
return;
if (!vc_cons_allocated(new_console))
return;
/*
If this vt is in process mode, then we need to handshake with
~~~~~~~~~
什么模式? 在此模式下,不能直接切换,要等待!
* that process before switching. Essentially, we store where that
* vt wants to switch to and wait for it to tell us when it's done
* (via VT_RELDISP ioctl).
*
* We also check to see if the controlling process still exists.
控制进程
* If it doesn't, we reset this vt to auto mode and continue.
^^^^^^^^^^
什么模式?
* This is a cheap way to track process control. The worst thing
* that can happen is: we send a signal to a process, it dies, and
* the switch gets "lost" waiting for a response; hopefully, the
* user will try again, we'll detect the process is gone (unless
* the user waits just the right amount of time :-) and revert the
* vt to auto control.
*/
VT_PROCESS 模式的处理。在此模式下,不能直接切换,要等待!
if (vt_cons[fg_console]-> == VT_PROCESS)
{
/*
* Send the signal as privileged - kill_proc() will
* tell us if the process has gone or something else
* is awry
*/
if (kill_proc(vt_cons[fg_console]->vt_pid,
vt_cons[fg_console]->,
1) == 0)
{
/*
* It worked. Mark the vt to switch to and
* return. The process needs to send us a
* VT_RELDISP ioctl to complete the switch.
*/
vt_cons[fg_console]->vt_newvt = new_console;
return;
}
/*
* The controlling process has died, so we revert back to
* normal operation. In this case, we'll also change back
* to KD_TEXT mode. I'm not sure if this is strictly correct
* but it saves the agony when the X server dies and the screen
* remains blanked due to KD_GRAPHICS! It would be nice to do
* this outside of VT_PROCESS but there is no single process
* to account for and tracking tty count may be undesirable.
*/
reset_vc(fg_console);
/*
* Fall through to normal (VT_AUTO) handling of the switch...
*/
}
在 KD_GRAPHICS+VT_AUTO 模式下忽略所有终端切换。
if (vt_cons[fg_console]->vc_mode == KD_GRAPHICS)
return;
complete_change_console(new_console);
}
相关函数与变量
判断新的虚拟终端是否存在。
int vc_cons_allocated(unsigned int i)//
{
return (i < MAX_NR_CONSOLES && vc_cons[i].d);
}
重设新的虚拟终端的 vc_date 结构。
void reset_vc(unsigned int new_console)//
{
vt_cons[new_console]->vc_mode = KD_TEXT;
kbd_table[new_console].kbdmode = VC_XLATE;
vt_cons[new_console]-> = VT_AUTO;
vt_cons[new_console]-> = 0;
vt_cons[new_console]-> = 0;
vt_cons[new_console]-> = 0;
vt_cons[new_console]-> = 0;
vt_cons[new_console]->vt_pid = -1;
vt_cons[new_console]->vt_newvt = -1;
reset_palette (new_console) ;
}
complete_change_console()
切换终端的实际动作。
void complete_change_console(unsigned int new_console)
{
unsigned char old_vc_mode;
if ((new_console == fg_console) || (vt_dont_switch))
return;
if (!vc_cons_allocated(new_console))
return;
last_console = fg_console;
/*
* If we're switching, we could be going from KD_GRAPHICS to
* KD_TEXT mode or vice versa, which means we need to blank or
* unblank the screen later.
*/
old_vc_mode = vt_cons[fg_console]->vc_mode;
根据 new_console 重设显示器。
update_screen(new_console);
/*
* If this new console is under process control, send it a signal
* telling it that it has acquired. Also check if it has died and
* clean up (similar to logic employed in change_console())
*/
if (vt_cons[new_console]-> == VT_PROCESS)
{
/*
* Send the signal as privileged - kill_proc() will
* tell us if the process has gone or something else
* is awry
*/
if (kill_proc(vt_cons[new_console]->vt_pid,
vt_cons[new_console]->,
1) != 0)
{
/*
* The controlling process has died, so we revert back to
* normal operation. In this case, we'll also change back
* to KD_TEXT mode. I'm not sure if this is strictly correct
* but it saves the agony when the X server dies and the screen
* remains blanked due to KD_GRAPHICS! It would be nice to do
* this outside of VT_PROCESS but there is no single process
* to account for and tracking tty count may be undesirable.
*/
reset_vc(new_console);
}
}
/*
* We do this here because the controlling process above may have
* gone, and so there is now a new vc_mode
*/
if (old_vc_mode != vt_cons[new_console]->vc_mode)
{
if (vt_cons[new_console]->vc_mode == KD_TEXT)
文本模式
do_unblank_screen();
else
图形模式
do_blank_screen(1);
}
/* Set the colour palette for this VT */
if (vt_cons[new_console]->vc_mode == KD_TEXT)
set_palette() ;
/*
* Wake anyone waiting for their VT to activate
*/
vt_wake_waitactive();
return;
}
相关函数与变量
#define vt_wake_waitactive() wake_up(&vt_activate_queue)
update_screen()
重设屏幕。
void update_screen(int new_console)
{
static int lock = 0;
lock 保证该函数不会被重入。
if (new_console == fg_console || lock)
return;
if (!vc_cons_allocated(new_console)) {
/* strange ... */
printk("update_screen: tty %d not allocated ??\n", new_console+1);
return;
}
lock = 1;
清除屏幕被选择部分。
clear_selection();
if (!console_blanked)
get_scrmem(fg_console);
else
console_blanked = -1; /* no longer of the form console+1 */
fg_console = new_console; /* this is the only (nonzero) assignment to fg_console */
/* consequently, fg_console will always be allocated */
set_scrmem(fg_console, 0); //
set_origin(fg_console);
set_cursor(fg_console);
set_leds();
recompute k_down[] and shift_state from key_down[] */
compute_shiftstate(); // linux/drivers/char/
lock = 0;
}
相关函数与变量
clear_selection(void) //
{
highlight_pointer(-1); /* hide the pointer */
if (sel_start != -1) {
highlight(sel_start, sel_end);
sel_start = -1;
}
}
int console_blanked = 0; //