显示服务(Display Server)

参考Linux 图形界面的显示原理是什么? - silaoA的回答 - 知乎Linux 图形界面的显示原理是什么? - 韩朴宇的回答 - 知乎

大名鼎鼎的X窗口系统(X Window System,也可以叫做 X11 或者 X)是1980s由MIT提出的以位图图像显示图形化界面的一套软件规范及协议,之所以叫“X11”是因为目前是该协议迭代的第11个版本。可以在终端运行 sudo X -version 来查看:

1
2
3
4
5
6
7
8
X.Org X Server 1.21.1.4
X Protocol Version 11, Revision 0
Current Operating System: Linux lsn-Y9000X 6.2.0-37-generic #38~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov 2 18:01:13 UTC 2 x86_64
Kernel command line: BOOT_IMAGE=/@/boot/vmlinuz-6.2.0-37-generic root=UUID=adf7b9ab-203e-4441-8333-c7bb1a2ecfcc ro rootflags=subvol=@ quiet splash vt.handoff=7
xorg-server 2:21.1.4-2ubuntu1.7~22.04.2 (For technical support please see http://www.ubuntu.com/support)
Current version of pixman: 0.40.0
Before reporting problems, check http://wiki.x.org
to make sure that you have the latest version.

其架构如下图所示。

由于采用了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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/bin/sh
# Copyright (C) 1999 - 2005 Red Hat, Inc. All rights reserved. This
# copyrighted material is made available to anyone wishing to use, modify,
# copy, or redistribute it subject to the terms and conditions of the
# GNU General Public License version 2.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# Authors:
# Mike A. Harris <mharris@redhat.com>

# Mandatorily source xinitrc-common, which is common code shared between the
# Xsession and xinitrc scripts which has been factored out to avoid duplication
. /etc/X11/xinit/xinitrc-common

# The user may have their own clients they want to run. If they don't,
# fall back to system defaults.
if [ -f $HOME/.Xclients ]; then
exec $CK_XINIT_SESSION $SSH_AGENT $HOME/.Xclients || \
exec $CK_XINIT_SESSION $SSH_AGENT $HOME/.Xclients
elif [ -f /etc/X11/xinit/Xclients ]; then
exec $CK_XINIT_SESSION $SSH_AGENT /etc/X11/xinit/Xclients || \
exec $CK_XINIT_SESSION $SSH_AGENT /etc/X11/xinit/Xclients
else
# Failsafe settings. Although we should never get here
# (we provide fallbacks in Xclients as well) it can't hurt.
[ -x /usr/bin/xsetroot ] && /usr/bin/xsetroot -solid '#222E45'
[ -x /usr/bin/xclock ] && /usr/bin/xclock -geometry 100x100-5+5 &
[ -x /usr/bin/xterm ] && xterm -geometry 80x50-50+150 &
[ -x /usr/bin/twm ] && /usr/bin/twm
fi

这个文件会将很多其他的文件参数引进来,包括 /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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# inittab is no longer used when using systemd.
#
# ADDING CONFIGURATION HERE WILL HAVE NO EFFECT ON YOUR SYSTEM.
#
# Ctrl-Alt-Delete is handled by /usr/lib/systemd/system/ctrl-alt-del.target
#
# systemd uses 'targets' instead of runlevels. By default, there are two main targets:
#
# multi-user.target: analogous to runlevel 3
# graphical.target: analogous to runlevel 5
#
# To view current default target, run:
# systemctl get-default
#
# To set a default target, run:
# systemctl set-default TARGET.target
#

可以通过 systemctl get-default 来查看现在的 target。关于target,参考 Systemd中的 target 是什么?有什么用?如何使用? 这篇文章中也有关于 systemd 和 systemctl 的相关链接,不了解的来这里补补知识。

1
2
[lsn@localhost sysconfig]$ systemctl get-default 
graphical.target

可以看到现在是图形用户界面模式,我们需要将其改为多用户模式,只需要 systemctl set-default multi-user.target 即可。

1
2
3
[lsn@localhost sysconfig]$ systemctl set-default multi-user.target
Removed symlink /etc/systemd/system/default.target.
Created symlink from /etc/systemd/system/default.target to /usr/lib/systemd/system/multi-user.target.

当然如果你安装的时候选择的是最小化安装(Minimal Installation),就没有上述的步骤,但是该安装需要自己手动安装一下 X。

1
2
3
sudo yum group install "X Window System"
# 为了方便测试,还需要安装 xorg-x11-apps
sudo yum install xorg-x11-apps

tty 与 X 端口

本部分参考 如何在 Linux 中不使用功能键在 TTY 之间切换

linux 默认有 7 个 tty,从 tty1~tty7。在字符界面下,要查看当前所在的 tty,只需要输入 tty 即可。

1
2
[lsn@localhost ~]$ tty
/dev/tty1

除了按快捷键 CTRL+ALT+F2 切换到 tty2 外,还可以用命令 chvt 2 切换。

要查看活动的虚拟控制台总数,运行

1
2
[lsn@localhost ~]$ fgconsole
2

要移除未使用的虚拟终端,只需要输入

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
2
3
4
5
6
7
8
9
[Desktop Entry]
Name=Ubuntu
Comment=This session logs you into Ubuntu
Exec=env GNOME_SHELL_SESSION_MODE=ubuntu /usr/bin/gnome-session --session=ubuntu
TryExec=/usr/bin/gnome-shell
Type=Application
DesktopNames=ubuntu:GNOME
X-GDM-SessionRegisters=true
X-Ubuntu-Gettext-Domain=gnome-session-42

由于我的系统安装了两个桌面环境:GNOME 和 KDE,因此该路径下面还有 gnome-classic.desktopgnome-classic-xorg.desktopplasma.desktop

这些 ubuntu.desktop 与 linux 下其他的“快捷方式”并没有什么不同。

显示管理器实现自动登录的功能

如果我们想开机后不输入密码直接进入桌面环境,即让显示管理器自动登录,可以这么做。

对于 SSDM,需要新建 /etc/sddm.conf.d 路径并在该路径下新建 autologin.conf 文件,内容如下:

1
2
3
[Autologin]
User=lsn
Session=plasma

其中的 User 即需要自动登录的用户名,Session 对应着 /usr/share/xsessions/plasma.desktop

SDDM 会通过加载该文件来覆盖本身的默认行为。

对于 LightDM 来说类似,新建 /etc/lightdm/lightdm.conf

1
2
3
[Seat:*]
autologin-user=lsn
autologin-session=plasma

因此,如果我们想实现窗口管理器登录后不去加载桌面环境,只是执行一个特定的GUI 应用,只需要将上面 autologin.conf 中的 Session=plasma 改成 Session=your_gui 即可(前提是在 /usr/share/xsessions/ 下有 your_gui.desktop 文件)