问题描述:公司的一款设备在X11启动过程中,存在输入设备(触摸屏、触摸板,键盘)未能在xinput中显示出来,目前的解决方案是卸载再重新加载一下输入设备驱动。另外在测试的过程中还出现过一次关机提示X11 died的提示,需要从更高版本的Ubuntu中提取更高版本的X11及其依赖库,复测以上问题是否存在。
问题分析:
在前期调查过程中进行过一次重启测试,每次启动后脚本会将xinput中设备的连接情况保存下来。 保存的结果显示,约两百次重启测试中出现过一次未找到设备的信息提示。在出现错误的这次打印中,TouchScreen和Touchpad两个设备均未能成功连接,且驱动重新加载后设备又可以找到了。而除了这次重启以外,其余重启所产生的信息都没有任何问题。考虑到键盘等设备的热插拔,重启X server都可以让设备连接成功,因此排除出现包括物理链路、设备驱动,X server加载时机错误等问题的可能性。
Xorg分析:Xorg是根据X11协议实现的,而在X11协议中, 设备需要先与X server交互, 只有与X server交互成功才可以调用X client。因而我认为导致错误发生主要原因是 X server与设备的连接出现了问题, 原因应当出在设备与X server连接的过程中或者X 11程序有BUG。
解决方案: 升级X11
公司设备搭载的系统是Ubuntu20.04.2 LTS , Xorg的版本是1.20.9。
Ubuntu22.04 LTS中同样有Xorg,版本是1.21.1.3。因此,考虑将ubuntu22.04中的X11整个移植到ubuntu20.04.2中。
要想移植X11,首先要理清楚X11中包含哪些文件,与哪些文件有关联。下面将介绍一下X11整个系统在Ubuntu20.04+中的分布。
/bin目录中, Xorg、xinput 、xauth、xinit、xhost对于X11的运行起着较为重要的作用,简单的描述一下这5个程序:
Xorg: X server
xinput:用于查询输入设备的信息,同时可以使能或者禁用输入设备。
xauth:用于编辑和展示连接X server的授权信息。
xinit:X11系统初始化程序,主要完成X服务器的初始化设置。
xhost:X服务器访问控制工具,运行xhost命令前需要先启动本地的X-windows图形界面服务。
/lib目录中,与X11相关的重要目录有/lib/xorg,/lib/X11
/lib/xorg文件夹中包含了Xorg的驱动库
/lib/X11文件夹包含了x11性能比较程序和X11中的一个文本编辑器。
/etc/etc目录中包括了X11运行中所使用的脚本文件, 以及X11的配置文件。
/usr/share/X11中,包含X11运行时的支撑文件。 包括字体文件,RGB配置文件等等,这个目录与/etc/X11部分内容重合。
以上为较为重要的目录或文件,实际上/var目录下也有一些内容与X11有关联,这部分作为Todo事项等待后续研究。
在将设备原文件系统中相关的文件保存备份后,将22.04中的对应搬到设备的文件系统上。
然而并不是将文件搬运过来就可以正常运行高版本的X11。X11系统中很多文件的执行都是依赖系统中的库文件,Ubuntu22.04和Ubuntu20.04库文件必然有较大的差异。
在这些库文件中,最大的一个差异是glibc库。
随意在搜索引擎中搜索“glic是什么”都会看到这样的内容“ glibc是GNU发布的libc库,即c运行库。glibc是linux系统中最底层的api,几乎其它任何运行库都会依赖于glibc。”
X11同样如此,使用ldd命令查看Xorg、xinput 、xauth、xinit、xhost,会发现这些文件中均需要使用到库文件libc.so.6,并且这些文件需要的glibc版本为2.35。
而我司设备所搭载的Ubuntu22.04中,glibc版本为2.31,也就是说无法满足程序的运行要求,因此就需要在文件系统中再多安装一份glibc,下面介绍如何在系统中安装多个glibc。
前往https://ftp.gnu.org/gnu/glibc/ 中下载glibc2.35到文件系统中。
解压并进入文件夹中。创建build目录:mkdir build
进入build目录中:cd build
执行命令:
../configure --prefix=/opt --with-headers=/usr/include # prefix选项为glibc2.35的安装路径, headers为内核头文件路径,其余选项不用设置``
这一步有可能报错,报错内容如下:
```checking LD_LIBRARY_PATH variable... contains current directory
configure: error:
*** LD_LIBRARY_PATH shouldn't contain the current directory when
*** building glibc. Please change the environment variable
*** and run configure again.
这里报错的意思是指库文件环境变量不应该包含在当前路径下,因此只要把LD_LIBRARY_PATH
设置为空即可,具体命令为export LD_LIBRARY_PATH=
configure成功后,在build路径下 make && make install
安装glibc2.35。
安装过程中可能会遇到make无限循环的情况, 这种情况的因为系统时间不正常导致的,因此最好在编译glibc2.35前安装好ntpd调整好系统时间
这是简单有效的解决办法。系统时间调整好后,需要将glibc2.35所有源码的最后修改时间也调整为系统时间。
Todo:深入了解原理参照《驾驭Makefile》中编译死循环问题彻底调试_makefile 循环编译
调整完成后再次configure
和make && make install
, glibc2.35就会成功安装到/opt/glibc2.35下。
安装好了glibc2.35不代表Xorg1.21.1.3程序就可以正常运行。因为从Ubuntu22.04中移植过来得X11系统是在glibc2.35得环境中编译的,在编译过程中所使用的库文件和链接器都属于glibc2.35,因此要想让X11系统可以正常运行,需要改变X11系统中程序的链接器和库文件的位置。此时就需要引入工具 PatchELF。
引入patchelf在github主页中的介绍:
PatchELF is a simple utility for modifying existing ELF executables and libraries.
通过使用PatchELF,可以修改可执行程序的动态链接器和运行库的位置
# --set-interpreter /opt/glibc2.35/ld-linux.so.2设置xinput的动态链接器为glibc2.35的链接器
# --set-rpath /opt/glibc2.35/lib设置xinput的运行库位置为glibc2.35下lib目录
patchelf --set-interpreter /opt/glibc2.35/ld-linux.so.2 --set-rpath /opt/glibc2.35/lib /usr/bin/xinput
使用同样的方式将Xorg、xauth、xinit、xhost的动态链接器和lib库的位置都修改为glib2.35。
部分库文件也依赖于glibc的库文件,也需要使用PatchELF对其进行修改
# 库文件不需要动态链接器,只修改rpath即可,如
patchelf --set-rpath /opt/glibc2.35/lib /usr/opt/xinput
这部分修改后,执行命令Xorg。此时应当依然有大量库文件缺失,报错日志会保存在Xorg.*.log中,那么此时就可以根据日志的内容,从ubuntu22.04中确实的库文件依次复制到/opt/glibc2.35/lib目录下。
基本上所有缺失的库文件都在/usr/lib/x86_64-linux-gnu,因为Xorg执行过程中并不会一次把所有的报错都输出到日志中,因此这个过程需要重复进行多次,直到log文件搜索不到Error为止。
将确实的库都复制到/opt/glibc2.35/lib后,即可输入命令打开并测试X11系统是否成功运行。
# 开启X
$ Xorg &
# 打开X window
$ startx
命令执行后如果屏幕上出现白色命令行窗口,则代表X 11系统正常运行。使用Xorg -version查看Xorg版本,显示Xorg升级到了1.21.1.13
接下来需要处理的是QT程序相关的部分。
在这个阶段中,虽然Xorg.X.log文件中没有Error,但是在会发现一处程序运行fail的提示,显示的是libgl运行失败。
Todo: 使用LIBGL=verbose info, 会出现提示报错信息,根据报错信息修改即可。
输入 sh /etc/start_x.sh开启程序,会发现程序每次进度条跳转到7%以后就会再次跳回0%。
出现这样的原因是因为程序是在glibc2.31环境下编译的,而当前的X11环境已经发生了变化,因此也需要将程序的动态链接器和rpath进行修改。
patchelf --set-interpreter /opt/glibc2.35/ld-linux.so.2 --set-rpath /opt/glibc2.35/lib /usr/app/myapp
重新输入 sh /etc/start_x.sh开启程序,屏幕上会出现
This application failed to start because it could not find or load the Qt platform plugin "xcb"
in "".
Available platform plugins are: eglfs, linuxfb, minimal, minimalegl, offscreen, xcb, xcb.
Reinstalling the application may fix this problem.
这时可以通过修改环境变量的方式,开启QT的DEBUG模式。
export QT_DEBUG_PLUGINS=1
在这个模式中可以看到QT报错的打印信息, 信息中提示QT找不到它运行时依赖的库文件。
从/usr/lib/x86_64-linux-gnu中依次把打印中的库文件复制到/opt/glibc/lib中,再次启动QT,进度条顺序跑到了100%,程序得以成功运行。
测试
通过脚本自动跑重启的方式测试是否任然存在找不到键盘等输入设备的情况,结果显示1600次重启中,没有再出现找不到键盘鼠标的现象。X系统移植的很成功 O(∩_∩)O