显示服务(Display Server)
参考Linux 图形界面的显示原理是什么? - silaoA的回答 - 知乎,Linux 图形界面的显示原理是什么? - 韩朴宇的回答 - 知乎
大名鼎鼎的X窗口系统(X Window System,也可以叫做 X11 或者 X)是1980s由MIT提出的以位图图像显示图形化界面的一套软件规范及协议,之所以叫“X11”是因为目前是该协议迭代的第11个版本。可以在终端运行 sudo X -version
来查看:
1 | X.Org X Server 1.21.1.4 |
其架构如下图所示。
由于采用了C/S 架构,好处就是服务端和客户端不用在同一台电脑上,客户端可以基于网络通信。
为了方便编写 GUI 程序,一些图形库如 GTK、QT等,作为 X Window System的客户端工作。客户端将绘图请求发送给服务端,服务端操纵显卡或视频终端把位图图像绘制出来,并处理键盘鼠标的事件,发送给客户端(注意,和人进行交互的是服务端,这里可能有点难以理解。举个例子就是,当我们将鼠标向右移动,X Server会检测到鼠标的移动,然后告知X Client,经过X Client的运算,原来是要将鼠标指针向右移动25个像素。将这个结果传回X Server,X Server就操作硬件去绘图了)。
X Window System 毕竟只是一套协议,并不是程序实现。XFree86 是该协议的早期开源实现,而Xorg 则是这套协议使用最广的软件实现。
如果想启动图形页面,系统必定要运行Xorg服务,而启动Xorg的方式有两种,一种是借助xinit这个工具从终端中启动,另一种是通过显示管理器来启动(下面讲桌面环境时会详述)。
xinit
使用 startx
来寻找运行 X的参数。
-
X Server 的参数方面:
- 使用startx 后面带的参数
- 若无参数,则寻找用户家目录的文件
~/.xserverrc
- 若无上述两者,则使用
/etc/X11/xserverrc
- 若无上述三者,则单纯运行
/usr/bin/X
(即直接运行 X Server)
-
X Client 的参数方面
- 使用startx 后面带的参数
- 若无参数,则寻找用户家目录的文件
~/.xinitrc
- 若无上述两者,则使用
/etc/X11/xinit/xinitrc
- 若无上述三者,则单纯运行GUI程序
根据上述流程找到启动 X 所需要的参数后,接下来 startx
会调用 xinit
来启动 Xorg。
在没有参数的情况下,xinit
默认会寻找 /etc/X11/xinit/xinitrc
和 /etc/X11/xinit/xserverrc
这两个配置文件来初始化。
总之,xinit
的任务是来启动 X Server和X Client,而启动的参数由 startx
来寻找。
X Server的启动文件
X server 启动的脚本与参数是通过 /etc/X11/xinit/xserverrc
文件来启动的。不过如果你的系统中没有该文件,并且家目录下也没有 ~/.serverrc
的时候,系统默认会去运行 /usr/bin/X
这个指令。这也是 X server最原始的启动方式。
运行 X Server时,较早版本的系统 Xorg会去读取 /etc/X11/xorg.conf
这个配置文件,从CentOS 6以后,X Server在每次启动的时候会自动检测系统上面的显卡芯片、屏幕类型等等,然后自行搭配最佳的驱动程序,因此,/etc/X11/xorg.conf
这个文件已经不再被需要了。当然,你可以自己新建这个文件,一旦X Server检测到有这个文件存在,将会优先按照这个文件的配置来执行。
如果只是想要修改部分设置,并不是每个组件都要自行设置的话,可以在 /etc/X11/xorg.conf.d
目录下新建以 .conf
结尾的文件,将需要的额外项目加进去即可。
如果想要知道X Server具体用的什么设置,可以通过查看 /var/log/Xorg.0.log
日志来获取。
X Client 的启动文件
如果家目录下没有 ~/.xinitrc
,此时 X Client会默认把 /etc/X11/xinit/xinitrc
作为启动脚本。
在我的 CentOS7.9上,/etc/X11/xinit/xinitrc
的内容如下:
1 | !/bin/sh |
这个文件会将很多其他的文件参数引进来,包括 /etc/X11/xinit/xinitrc-common
与 /etc/X11/xinit/Xclients
等。
不过分析到最后,X client 最终是要去加载 GNOME 或者 KDE 等桌面环境而已。
关于X 和 Wayland
该部分有时间了再写
实验
多用户模式 muti-user.target
说了这么多理论,接下来我们通过几个实验来检测一下上面的理论。
测试的环境是在 CentOS 7.9 上面进行的:CentOS Linux release 7.9.2009 (Core)
。我这个环境安装的时候已经装起 GNOME 了,我们要在字符界面开始,因此首先要干掉 GNOME 或者开机的时候不启动它。
配置字符界面的方式可通过 cat /etc/inittab
来查看。
1 | # inittab is no longer used when using systemd. |
可以通过 systemctl get-default
来查看现在的 target。关于target,参考 Systemd中的 target 是什么?有什么用?如何使用? 这篇文章中也有关于 systemd 和 systemctl 的相关链接,不了解的来这里补补知识。
1 | [lsn@localhost sysconfig]$ systemctl get-default |
可以看到现在是图形用户界面模式,我们需要将其改为多用户模式,只需要 systemctl set-default multi-user.target
即可。
1 | [lsn@localhost sysconfig]$ systemctl set-default multi-user.target |
当然如果你安装的时候选择的是最小化安装(Minimal Installation),就没有上述的步骤,但是该安装需要自己手动安装一下 X。
1 | sudo yum group install "X Window System" |
tty 与 X 端口
本部分参考 如何在 Linux 中不使用功能键在 TTY 之间切换。
linux 默认有 7 个 tty,从 tty1~tty7。在字符界面下,要查看当前所在的 tty,只需要输入 tty
即可。
1 | [lsn@localhost ~]$ tty |
除了按快捷键 CTRL+ALT+F2
切换到 tty2 外,还可以用命令 chvt 2
切换。
要查看活动的虚拟控制台总数,运行
1 | [lsn@localhost ~]$ fgconsole |
要移除未使用的虚拟终端,只需要输入
1 | [lsn@localhost ~]$ deallocvt |
想要开启 X Server,只需要在 tty 中输入 X :0
即可,这个 :0
参数用来指定启动的接口,其中,X :0
会在 tty2 中开启,:0
对应 tty2,:1
对应 tty3,以此类推。
由于 X 是可以进行网络通信的,X Server默认会将 6000端口用作网络监听。hostname:0
对应 6000,hostname:1
对应6001,以此类推。
X 启动流程测试
当在 tty 中输入 X :0 &
后,X Server 会在 tty2 中启动,系统也会跳转到 tty2 中,但此时的 tty2 中是一片乌黑,因为此时虽然 X Server 启动了,但是并没有任何的 X Client 启动,一个 GUI 也没有。
我们需要通过按 CTRL+ALT+F1
切换到 tty1 中。
随后我们在 tty1 中输入 xclock -display :0 &
,然后再按 CTRL+ALT+F2
切换到 tty2 中,此时就会发现:
前面说过了,X Window System 是 C/S 架构的,X Client 和 X Server 可以通过网络通信。我们接下来做一个测试,再打开一个虚拟机,两个虚拟机的 ip 如下图所示:
那么此时有趣的事情来了,这里参考 快速理解 X server, DISPLAY 与 X11 Forwarding,如何让 X Server 和 X Client 分别处于不同的机器上运行呢?
这里,运行 X Server 的虚拟机的 ip 是 192.168.61.130,运行 X Client 的虚拟机的 ip 是 192.168.61.129。如果我在 192.168.61.129 上运行 xeyes
,但是让它在 192.168.61.130 上显示,该怎么做呢?很简单,在 192.168.61.129 中引入一个环境变量 export DISPLAY=192.168.61.130:0.0
就可以了!
那么这样做有什么用呢?或者说这样做的使用场景是什么呢? 想象一下,假如我现在需要训练一个神经网络模型,我的服务器性能非常强悍,是一个有着 20 块 A100 的集群。由于我用 PyCharm 用习惯了,但是服务器是又没有装桌面环境的,这个时候,我就可以在我的笔记本电脑中跑一个 X Server 服务,在服务器上跑 PyCharm,但是显示在我的笔记本电脑上。我直接像使用本地的 PyCharm 一样写代码、调试、运行,但是用的却是服务器上的 GPU 资源!
那么问题又来了,怎样在 Windows 的电脑上显示远程机器的 GUI 呢?点开上面的参考连接吧,里面有个 X11 Forwarding 正是你需要的,除此之外,有个开源软件叫 MobaXterm,将 X11 Forwarding 集成了,更方便使用,参考 远程显示(操作) 服务器 GUI 程序(图形化界面) (基于 X11 Forwarding + Centos + MobaXterm)。下图是一个我在做测试时的例子,good luck for you!
桌面环境
窗口管理器(Window Manger)
窗口管理器(Window Manger, WM)是为了实现一个屏幕上显示多个X程序,实现调整程序的大小,标题栏、最小化、最大化、关闭按钮等这些功能。如果没有 DM,那么一次只能运行一个 GUI 应用,而且分辨率是锁死的,显然不符合用户的使用习惯(当然一些特定的需求除外,后面还会提到)。
显示管理器(Display Manger)
显示管理器(Display Manger, DM)又叫登录管理器,用于开机后显示登录界面,并启动窗口管理器等X组件。没有显示管理器,Linux 开机后会直接显示命令行登录界面。
桌面环境(Desktop Environment)
把 WM、DM 以及文件管理器及其它应用,主题,图形库,通用的组件,进程间通信用的 Dbus 等等打包起来,就形成当今 Linux 下的桌面环境(Destop Environment)。
下表列出了一些常见的DE及其自带的 DM和WM:
桌面环境 | 显示(登录)管理器 | 窗口管理器 |
---|---|---|
GNOME | GDM | gnome-wm |
KDE | KDM,SDDM | Kwin |
XFCE | —— | XFWM |
LXDE | LXDM | Openbox |
还有国内的 Deepin 操作系统的系统桌面环境叫 DDE。
除了上面列出的,还有很多的 DM 和 WM。例如,常见的 DM 还有:LightDM,SDDM,XDM 等;常见的 WM 还有:DWM,i3WM,awesome,xmonad 等等。
需要指出的是,不一定非要使用桌面环境自带的显示管理器来加载桌面环境,比如 GNOME,也可以使用 LightDM 来加载桌面环境。
操作系统如何把桌面环境加载起来?
通过 xinit
上文提到,xinit 可以启动 X Server 和 X Client,那么很自然的想法就是通过执行 startx
来调用 xinit 运行 X Server,然后通过 X Client 配置文件 xinitrc
,在配置文件中启用窗口管理器。如果你用过 archlinux 的话, 一定对 DWM 等平铺式窗口管理器印象深刻吧哈哈哈。我当时配置 DWM 的时候还不懂为什么要用 startx
,在 .xinitrc
里写 exec dwm
,现在看来豁然开朗。
既然 GNOME 也是一种 X Client,那肯定也可以通过 .xinitrc
来启动。(//TODO 空了再来做启动的实验好了)
通过显示管理器
通过上文,我们可以梳理出一个大致的脉络:计算机上电,boot 引导操作系统,加载操作系统内核,通过 systemctl
来启动显示管理器服务, 由显示管理器将前端(窗口管理器和桌面环境)加载起来。
你可以使用命令 cat /etc/X11/default-display-manager
来查看当前系统使用的显示管理器,可以使用sudo dpkg-reconfigure gdm3
来切换系统的显示管理器。(//FIXME 如何知道系统现在有哪些显示管理器呢?)
本文以 SDDM 和 LightDM 为例,展示显示管理器是如何加载桌面环境的。
这部分内容参考 ubuntu下图形程序自启动的几种方法
大多数显示管理器会从 /usr/share/xsessions/
(对于 Wayland 来说该路径为 /usr/share/wayland-sessions
) 下面读取可用的 .desktop
文件。在安装各种窗口管理器时会在该目录下生成对应的 .desktop
文件。比如安装 ubuntu (ubuntu 应该是从 18.04 版本开始将默认的桌面环境换成了 GNOME)时,在 /usr/share/xsessions/
下生成了 ubuntu.desktop
,内容如下:
1 | [Desktop Entry] |
由于我的系统安装了两个桌面环境:GNOME 和 KDE,因此该路径下面还有 gnome-classic.desktop
、gnome-classic-xorg.desktop
、plasma.desktop
。
这些 ubuntu.desktop
与 linux 下其他的“快捷方式”并没有什么不同。
显示管理器实现自动登录的功能
如果我们想开机后不输入密码直接进入桌面环境,即让显示管理器自动登录,可以这么做。
对于 SSDM,需要新建 /etc/sddm.conf.d
路径并在该路径下新建 autologin.conf
文件,内容如下:
1 | [Autologin] |
其中的 User
即需要自动登录的用户名,Session
对应着 /usr/share/xsessions/plasma.desktop
。
SDDM 会通过加载该文件来覆盖本身的默认行为。
对于 LightDM 来说类似,新建 /etc/lightdm/lightdm.conf
:
1 | [Seat:*] |
因此,如果我们想实现窗口管理器登录后不去加载桌面环境,只是执行一个特定的GUI 应用,只需要将上面 autologin.conf
中的 Session=plasma
改成 Session=your_gui
即可(前提是在 /usr/share/xsessions/
下有 your_gui.desktop
文件)。