searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Linux TTY切换流程

2023-10-26 10:29:34
53
0

0 前言

    Linux桌面有时会卡死且快捷键“Ctrl + Alt + F2...F6”无法切换到字符终端的情况,本文梳理TTY的切换流程。

1 字符终端

    没有图形系统也是可以切换TTY的,此时它有内核独立完成:

kbd_keycode(unsigned int keycode, int down, int hw_raw)@linux-4.19.67/drivers/tty/vt/keyboard.c
|-->(*k_handler[type])(vc, keysym & 0xff, !down)
    |-->k_cons(struct vc_data *vc, unsigned char value, char up_flag)@linux-4.19.67/drivers/tty/vt/keyboard.c
	|-->if (up_flag)
	|	|-->return
	|-->set_console(value)

:服务器通常是不安装桌面的,其TTY切换默认就是上述流程;Linux桌面默认不会用上述流程,除非将图形关闭,例如

sudo systemctl stop lightdm # 立即生效,重启后无效

2 图形系统

    图形系统的其输入事件将会由图形服务器或合成器处理[1]:

kernel -> libevdev -+-> xf86-input-evdev -> X server -> X client		  # Xorg
		            |-> libinput -+-> Wayland compositor -> Wayland client        # Weston/Wayland compositor
			                      |-> xf86-input-libinput -> X server -> X client # xorg-xserver-1.16+

    例如Xorg切换TTY的流程如下:

dix_main()
|-->Dispatch()
	|-->ProcessInputEvents()@hw/xfree86/common/xf86Events.c
		|-->mieqProcessInputEvents()
			|-->mieqProcessDeviceEvent()
				|-->AccessXFilterPressEvent()
					|-->XkbProcessKeyboardEvent()
						|-->XkbHandleActions()
							|-->XkbActionGetFilter()
								|-->_XkbFilterSwitchScreen(XkbSrvInfoPtr xkbi, XkbFilterPtr filter, unsigned keycode, XkbAction *pAction)@xorg-server-1.20.8/xkb/xkbActions.c
                                    |-->DeviceIntPtr dev = xkbi->device
                                    |-->if (dev == inputInfo.keyboard)
                                    |	|-->return 0
                                    |-->if (filter->keycode == 0) // initial press
                                        |-->filter->keycode = keycode
                                        |-->filter->active = 1
                                        |-->filter->filterOthers = 0
                                        |-->filter->filter = _XkbFilterSwitchScreen
                                        |-->AccessXCancelRepeatKey(xkbi, keycode)
                                        |-->XkbDDXSwitchScreen(dev, keycode, pAction)
|<------------------------------------------|
|-->XkbDDXSwitchScreen(DeviceIntPtr dev, KeyCode key, XkbAction *act)@xorg-server-1.20.8/hw/xfree86/xkb/xkbVT.c
	|-->int scrnnum = XkbSAScreen(&act->screen)
	|-->if (act->screen.flags & XkbSA_SwitchApplication)
		|-->if (act->screen.flags & XkbSA_SwitchAbsolute)
		|	|-->xf86ProcessActionEvent(ACTION_SWITCHSCREEN, (void *) &scrnnum)
		|-->else
			|-->if (scrnnum < 0)
			|	|-->xf86ProcessActionEvent(ACTION_SWITCHSCREEN_PREV, NULL)
			|-->else
				|-->xf86ProcessActionEvent(ACTION_SWITCHSCREEN_NEXT, NULL)
|<------------------|
|
|-->xf86ProcessActionEvent(ActionEvent action, void *arg)@xorg-server-1.20.8/hw/xfree86/common/xf86Events.c
	|-->case ACTION_SWITCHSCREEN
    	|-->if (!xf86Info.dontVTSwitch && arg) 
        	|-->int vtno = *((int *) arg);
        	|-->if (vtno != xf86Info.vtno)
            	|-->xf86VTActivate(vtno)
                	|-->xf86VTActivate(int vtno)@xorg-server-1.20.8/hw/xfree86/os-support/shared/VTsw_usl.c
                    	|-->ioctl(xf86Info.consoleFd, VT_ACTIVATE, vtno) // 可见也是通过内核完成切换的
                        	|-->...
|<-----------------------------|
|-->vt_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)@linux-4.19.67/drivers/tty/vt/vt_ioctl.c
	| // ioctl(fd, VT_ACTIVATE, num) will cause us to switch to vt # num,
	| // with num >= 1 (switches to vt 0, our console, are not allowed, just
	| // to preserve sanity).
	|-->case VT_ACTIVATE
		|-->if (!perm)
		|	|-->return -EPERM
		|-->if (arg == 0 || arg > MAX_NR_CONSOLES)
		|	|-->ret =  -ENXIO
		|-->else
			|-->arg--
			|-->console_lock()
			|-->ret = vc_allocate(arg)
			|-->console_unlock()
			|-->if (ret)
			|	|-->break
			|-->set_console(arg)

3 总结

    由第2节可见,图形系统的TTY切换是由图形服务(X)或合成器(Wayland)实现的,因此Linux桌面的内核或Xorg异常都有可能导致无法切换TTY的情况,需要依次排查。

参考资料

[1]evdev(WIKIPEDIA)

0条评论
0 / 1000
李****海
15文章数
0粉丝数
李****海
15 文章 | 0 粉丝
李****海
15文章数
0粉丝数
李****海
15 文章 | 0 粉丝
原创

Linux TTY切换流程

2023-10-26 10:29:34
53
0

0 前言

    Linux桌面有时会卡死且快捷键“Ctrl + Alt + F2...F6”无法切换到字符终端的情况,本文梳理TTY的切换流程。

1 字符终端

    没有图形系统也是可以切换TTY的,此时它有内核独立完成:

kbd_keycode(unsigned int keycode, int down, int hw_raw)@linux-4.19.67/drivers/tty/vt/keyboard.c
|-->(*k_handler[type])(vc, keysym & 0xff, !down)
    |-->k_cons(struct vc_data *vc, unsigned char value, char up_flag)@linux-4.19.67/drivers/tty/vt/keyboard.c
	|-->if (up_flag)
	|	|-->return
	|-->set_console(value)

:服务器通常是不安装桌面的,其TTY切换默认就是上述流程;Linux桌面默认不会用上述流程,除非将图形关闭,例如

sudo systemctl stop lightdm # 立即生效,重启后无效

2 图形系统

    图形系统的其输入事件将会由图形服务器或合成器处理[1]:

kernel -> libevdev -+-> xf86-input-evdev -> X server -> X client		  # Xorg
		            |-> libinput -+-> Wayland compositor -> Wayland client        # Weston/Wayland compositor
			                      |-> xf86-input-libinput -> X server -> X client # xorg-xserver-1.16+

    例如Xorg切换TTY的流程如下:

dix_main()
|-->Dispatch()
	|-->ProcessInputEvents()@hw/xfree86/common/xf86Events.c
		|-->mieqProcessInputEvents()
			|-->mieqProcessDeviceEvent()
				|-->AccessXFilterPressEvent()
					|-->XkbProcessKeyboardEvent()
						|-->XkbHandleActions()
							|-->XkbActionGetFilter()
								|-->_XkbFilterSwitchScreen(XkbSrvInfoPtr xkbi, XkbFilterPtr filter, unsigned keycode, XkbAction *pAction)@xorg-server-1.20.8/xkb/xkbActions.c
                                    |-->DeviceIntPtr dev = xkbi->device
                                    |-->if (dev == inputInfo.keyboard)
                                    |	|-->return 0
                                    |-->if (filter->keycode == 0) // initial press
                                        |-->filter->keycode = keycode
                                        |-->filter->active = 1
                                        |-->filter->filterOthers = 0
                                        |-->filter->filter = _XkbFilterSwitchScreen
                                        |-->AccessXCancelRepeatKey(xkbi, keycode)
                                        |-->XkbDDXSwitchScreen(dev, keycode, pAction)
|<------------------------------------------|
|-->XkbDDXSwitchScreen(DeviceIntPtr dev, KeyCode key, XkbAction *act)@xorg-server-1.20.8/hw/xfree86/xkb/xkbVT.c
	|-->int scrnnum = XkbSAScreen(&act->screen)
	|-->if (act->screen.flags & XkbSA_SwitchApplication)
		|-->if (act->screen.flags & XkbSA_SwitchAbsolute)
		|	|-->xf86ProcessActionEvent(ACTION_SWITCHSCREEN, (void *) &scrnnum)
		|-->else
			|-->if (scrnnum < 0)
			|	|-->xf86ProcessActionEvent(ACTION_SWITCHSCREEN_PREV, NULL)
			|-->else
				|-->xf86ProcessActionEvent(ACTION_SWITCHSCREEN_NEXT, NULL)
|<------------------|
|
|-->xf86ProcessActionEvent(ActionEvent action, void *arg)@xorg-server-1.20.8/hw/xfree86/common/xf86Events.c
	|-->case ACTION_SWITCHSCREEN
    	|-->if (!xf86Info.dontVTSwitch && arg) 
        	|-->int vtno = *((int *) arg);
        	|-->if (vtno != xf86Info.vtno)
            	|-->xf86VTActivate(vtno)
                	|-->xf86VTActivate(int vtno)@xorg-server-1.20.8/hw/xfree86/os-support/shared/VTsw_usl.c
                    	|-->ioctl(xf86Info.consoleFd, VT_ACTIVATE, vtno) // 可见也是通过内核完成切换的
                        	|-->...
|<-----------------------------|
|-->vt_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)@linux-4.19.67/drivers/tty/vt/vt_ioctl.c
	| // ioctl(fd, VT_ACTIVATE, num) will cause us to switch to vt # num,
	| // with num >= 1 (switches to vt 0, our console, are not allowed, just
	| // to preserve sanity).
	|-->case VT_ACTIVATE
		|-->if (!perm)
		|	|-->return -EPERM
		|-->if (arg == 0 || arg > MAX_NR_CONSOLES)
		|	|-->ret =  -ENXIO
		|-->else
			|-->arg--
			|-->console_lock()
			|-->ret = vc_allocate(arg)
			|-->console_unlock()
			|-->if (ret)
			|	|-->break
			|-->set_console(arg)

3 总结

    由第2节可见,图形系统的TTY切换是由图形服务(X)或合成器(Wayland)实现的,因此Linux桌面的内核或Xorg异常都有可能导致无法切换TTY的情况,需要依次排查。

参考资料

[1]evdev(WIKIPEDIA)

文章来自个人专栏
云桌面
14 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0