X window system(X11)
自从17年将开发主力系统切换为Linux之后,遇到了很多和X11相关的问题,因此就抽时间梳理了一下相关的内容。
X11
是一个协议,它规定了Xserver
和Xclient
通信的方式,给GUI程序提供了使用显示器,显卡,键盘,鼠标等设备的能力。
Xserver
是实现了X11服务端协议的进程,比如 Xorg
程序,它负责维护一个 Display
(=显示器+鼠标+键盘+显卡),允许 Xclient
程序使用这个 Display。它把它维护的 Display 包装成服务提供给 Xclient 程序使用,所以它是 server 端。既然是 server 端,它当然允许远程任意设备上的 Xclient 程序来连接它,使用它提供的显示服务。比如运行在 A 设备上的 firefox 进程,可以连接 B 设备的 Xserver,从而将界面显示在 B 设备上。
Xclient
是能够使用 X11 协议和 Xserver 通信的程序,比如常见的所有 Linux 上的 GUI 程序,firefox,chrome等。
在 AB 两台设备上使用 X11 的示意图如下,图上显示在 A 电脑上启动 firefox 程序,其界面显示在 B 电脑上。这里用两台电脑做演示是为了让你体会 程序的界面和程序的执行是独立的,当 A,B 合二为一的时候,原理不变。
在这个例子中,A 电脑提供了 firefox 程序运行所需要的 CPU,内存等资源,B 电脑提供了 firefox 程序界面需要的显示服务,包括显示器,键盘,鼠标。
X11支持扩展,其中最重要的一个扩展是 GLX
扩展,它允许X11客户端自行渲染客户端的内容(比如使用OpenGL),而不用将全部的渲染都经过X服务器。也是因为有了这个扩展,Linux上的应用才能使用GPU硬件加速的UI渲染。
手动启动Xserver
发行版系统的Xserver一般是由DM来启动的,可以查看相关DM的说明文档了解详细信息。
关于在发行版中 Xserver 的启动流程可以参考 Linux 桌面GUI及其启动流程。
单独的Xserver启动后你会看到一个全黑的屏幕,因为没有任何Xcient程序运行,所有的界面都需要Xclient程序来提供,发行版中的整个DE界面都是Xclient程序。
手动启动Xserver的方式由很多,下面一一介绍。
手动最直接启动Xserver的方法是直接使用 X
命令,下面是一个例子。
在 10 号本地display中启动X,显示在vt8中,最后一个Xclient退出后自动结束Xserver,2秒后在10号display中显示xterm程序,命令如下:
sudo X :10 -terminate vt8 & sleep 2 ; DISPLAY=:10 xterm
通过xinit程序启动Xserver:
sudo xinit /usr/bin/xterm -display :10 -- -terminate vt8 :10
注意:通过xinit 启动的Xclient默认在0号display显示。
如果xinit不添加任何参数,则会查找~目录下的.xinitrc脚本文件,具体查看man xinit。
通过startx脚本启动Xserver:
sudo startx /usr/bin/xterm -- -terminate vt8 :10
注意:通过startx启动的Xclient会在新启动的display中的显示。
startx 不一定要在sudo权限下运行,他可以处理权限相关内容,参见下文的 xauth
。
启动一个允许远程 Xclient
连接的 Xserver
首先使用 Ctrl+Shift+F1
切换到 tty1
。
然后运行以下命令:
startx /usr/bin/mate-session -- X :1 vt5 -listen tcp -terminate
以上命令中的
--
之前的参数是Xclient的参数,--
之后的参数是传给Xserver的参数。
然后在任意终端中运行xhost来添加连接权限:
DISPLAY=:1 xhost +
然后就可以在任意设备上运行Xclient程序并且显示在本机(192.168.1.4)上:
DISPLAY=192.168.1.4:1 firefox
为了方便调试XClient和Xserver,可以使用Xnest
命令来启动一个嵌套在当前Xserver中的虚拟Xserver,它会把其中运行的所有程序的X请求转发到当前的Xserver中:
startx /usr/bin/xterm -- /usr/bin/Xnest :10 -terminate
类似的功能也可以使用Xephyr
程序。
Xorg的启动日志在 ~/.local/share/xorg/Xorg.<Display>.log
文件中。
启动session的配置在 /user/share/xsession/*
目录下。
X11的配置在 /etc/X11/*
目录下。
任何的XServer启动后都会在 /tmp/.X11-***/
目录下创建一个相应display的文件,记录了Xserver的pid。同时会有一个/tmp/X***
的目录记录相应display的信息。可以通过这些来查看当前系统上启动的所有Xserver。
在X11中,所有选中的内容,都可以通过单击中键粘贴。
Xorg
提供的管理工具:
xdpyinfo
查看Display的信息。xwininfo
查看窗口信息,包括窗口树。xset
查看及设置Xserver的信息。xkill
结束一个Xclient。xwd
截图。xwud
查看截图.xrefresh
重绘所有Xclient。xrandr --query
查看当前的屏幕信息。-
xhost
用来设置当前的Xserver是否允许远程的xclient连接。如果要允许本机display 1显示远程的Xclient,则执行下面的命令,注意这会关闭在display1上连接认证,从而允许所有的设备连接display1。DISPLAY=:1 xhost +
以下工具常用来辅助X11程序的开发及测试,他们都“实现”了X11协议,可以理解为一个Xserver。
Xnest
创建一个窗口作为DISPLAY,它把其上运行的Xclient对Xserver的请求重定向到本机已有的Xserver上。Xephyr
类似Xnest
,但是不进行重定向,它自己实现了Xserver,会将Xclient显示在一个窗口中。
Xvnc
同时提供Xserver
和vnc server
,但是其中的Xserver不和物理Display关联,只能通过vnc client
来查看。
Xvnc :1 -rfbauth <passwd-file>
在Display1上启动Xvnc,并指定密钥文件,其他端可以通过 gvncviewer <ip>:1
来连接,可以看到系统登陆界面以及正常登陆DE。
X11vnc
提供 vnc server,当vncclient连接后可以看到本机的X11画面,并允许远程控制;同WinVNC;
x11vnc -display :1
在Display1上等待vnc连接,客户端使用gvncviewer <ip>:1
来连接,可以看到系统当前Display0的画面。
Xvfb 纯软件的 Xserver,不和物理Display关联,通过xwd可以截图查看内容。
xauth
X提供了 MIT-MAGICCOOKIE-1
协议用来实现基于 ~/.Xauthority
的安全认证。
为了让这种机制能够用于认证远程主机,又提供了 XDM-AUTHORIZATION-1
协议,主要是为了避免要把 MAGICCOOKIE
进行网络传输才能认证远程主机,从而提高安全性。
xauth
命令可以用于操作.Xauthority
文件;
xcookie
可以用于生成cookie字符串;
~/.Xauthority
文件(DM可能使用不同文件,可以通过Xorg的启动参数查看)记录了连接当前Xserver可以使用的cookie。在启动Xserver的时候通过 -auth 参数可以指定该文件的位置,只要指定了该文件则Xserver启动时就不需要是root权限。
~/.Xauthority
文件同时也是Xclient连接Xserver时需要使用的文件,它会获取其中的cookie用于认证。(它的位置也可以改变)
所以对于Xclient只要在本机中的~/.Xauthority
文件中写入了有了远程Xserver对应Xauthority文件中的cookie值,本机的Xclient就可以连接远程的Xserver了,这会忽略xhost
安全机制。
XDMCP(X Display Manager Control Protocol)
XDMCP
是一个独立于X11的协议,它用于支持用户登陆一个远程的session,可以配合Xephyr
作为远程登陆来使用。
一般来讲DM作为XDMCPserver
,而Xorg作为XDMCP的client,因此使用Xserver的-query
参数可以让X连接远程的session,可以显示远程的登陆界面,远程的WM,DE等。
大多数的DM默认这个功能是被关闭的,要配置DM开启XDMCPserver
才能用。
lightDM
lightDM
是目前Ubuntu,Manjaro-mate等系统默认使用的DM。
lightdm 配置文件的路径:
/usr/share/lightdm/
/etc/lightdm/
配置文件可以分功能分文件写,也可以写在一个文件中。
日志文件在 /var/log/lightdm
。
一般 lightdm
都是被init系统作为一个系统服务启动,可以通过service
或者systemctl
命令来控制lightdm
服务的启停。
这里有一份lightdm支持的配置选项列表:
https://askubuntu.com/questions/140471/is-there-a-list-of-all-the-possible-configuration-options-for-lightdm
其中xserver-allow-tcp
用于控制是否允许Xserver以监听tcp连接;
[XDMCPServer]
节点用于控制XDMCPServer;
[VNCServer]
节点用于控制VNCServer;
ArchWiki上的文档也值得一看: https://wiki.archlinux.org/index.php/LightDM
Display
display: 包括鼠标,键盘,显卡,显示器等设备的一个集合,每一个display都最多对应一个Xserver,Xclient可以连接到这些display,display的唯一标识称为 display specification(displayspec)
,格式如下:
host:display-number[.screen]
比如:
:0
使用本地unix/socket方式连接的 0 号 display.
localhost:2
使用tcp连接的本地 2 号display.
1.2.3.34:5.0
使用tcp连接的IP为1.2.3.34机器上的 5 号display 的 0 号 screen.
Xclient程序在启动的时候可以通过DISPLAY环境变量来指定要连接的display,比如:
DISPLAY=:3 firefox
在本地3号display上显示程序firefox。
每一个Xserver启动后都会占用一个TCP端口,用来监听客户端的连接,监听端口号为 6000+display
,比如,:0
号display的TCP端口号为6000+0
=6000,以此类推。
X11之所以还在v11,可能是因为它提供了较好的扩展机制,很多新功能都通过扩展实现了。
由于显卡相对于其他硬件太过复杂,几乎不可能定义一套通用的接口以供应用程序使用,因此,Linux给与Xserver完全管控显卡的能力,虽然部分显卡驱动提供一些内核模块来配合Xserver的工作。
Virtual Terminals(VT)
Linux提供virtual terminal(virtual console)
的机制,用来实现虚拟显卡。
每一个display同一时间只能和一个VT关联,每一个VT可以处于不同的模式,比如有的处于字符界面,有的处于GUI界面。用Ctrl+Alt+Fx
来切换VT,默认情况下Linux开机后就创建7个被占用的VT,VT1-VT7,默认的Xorg使用VT7。一般系统一共会创建63个VT,可以 ls /dev/ttyN
查看,每一个ttyN都是一个VT。
如果VT处于字符界面,可以通过 Alt+Fx
来切换到其他VT,或者通过 Alt+LeftArrow
来切换到上一个VT。
也可以通过chvt
或者switchto
命令来用命令切换VT。
使用tty命令可以查看当前使用的tty。
使用 openvt
命令可以在其他VT中运行指定程序。tty8-tty63默认情况下不会运行任何程序,因此切换过去是空白的字符界面。VT8默认是第一个没有被占用的终端(错误!)。init程序负责配置tty1-tty7,一般使用agetty
作为字符界面的登陆程序。
/dev/tty0
和/dev/tty
和/dev/console
指向当前tty,跟随当前tty变化而变化。通过ps命令可以查看进程所属的tty。
runlevel
可以通过内核参数来设定init启动的runlevel级别,也可以通过执行init x
来切换到指定的runlevel。一般情况下用户所在的runlevel是5
。
参考文档: