深度探索Linux操作系统 —— 构建根文件系统

在这里插入图片描述

系列文章目录


深度探索Linux操作系统 —— 编译过程分析
深度探索Linux操作系统 —— 构建工具链
深度探索Linux操作系统 —— 构建内核
深度探索Linux操作系统 —— 构建initramfs
深度探索Linux操作系统 —— 从内核空间到用户空间
深度探索Linux操作系统 —— 构建根文件系统
深度探索Linux操作系统 —— 构建桌面环境
深度探索Linux操作系统 —— Linux图形原理探讨


文章目录

  • 系列文章目录
  • 前言
  • 一、初始根文件系统
    • (1)安装GCC库
    • (2)建立相关目录
    • (3)构建程序 /sbin/init
  • 二、以读写模式重新挂载文件系统
  • 三、配置内核支持网络
  • 四、启动udev
  • 五、安装网络配置工具并配置网络
  • 六、安装并配置ssh服务
  • 七、安装 procps
  • 八、安装X窗口系统
    • 1、安装 M4 宏定义
    • 2、安装X协议和扩展
    • 3、安装 X 相关库和工具
    • 4、安装X服务器
    • 5、安装GPU的2D驱动
    • 6、安装X的输入设备驱动
    • 7、运行X服务器
    • 8、一个简单的X程序
    • 9、配置内核支持 DRM
  • 九、安装图形库
    • 1、安装 GLib 和 libffi
    • 2、安装ATK
    • 3、安装libpng
    • 4、安装GdkPixbuf
    • 5、安装Fontconfig
    • 6、安装Cairo
    • 7、安装Pango
    • 8、安装libXi
    • 9、安装GTK
    • 10、安装GTK图形库的善后工作
    • 11、一个简单的GTK程序
  • 十、安装字体


前言

    在第 3 章中,我们通过手工的方式展示了从零构建根文件系统的过程。在本章中,我们将构建一个相对完善的根文件系统,但是我们不再从零开始,毕竟一旦熟悉了原理后,余下的就是简单的重复了。第 2 章编译工具链时曾通过参数 “–with-sysroot” 指定了目标系统的文件安装的目录,后续所有的为目标系统编译的文件全部安装到了这个目录下。因此,在本章中,我们就基于这个目录下的文件构建运行在真实系统上的根文件系统。

    为了更高效地开发调试,我们首先打通了目标系统的网络,建立了宿主系统与目标系统的桥梁,包括配置内核支持网络协议以及网卡驱动,并安装了用户空间的网络配置工具。如此,我们就可以远程登录到目标系统上进行调试,并且可以动态更新文件(除内核和 initramfs 外)而不必再每次都重启系统。

    几乎所有的现代操作系统都提供图形用户界面,Linux 也不例外。麻省理工的开发者们为 UNIX 系统开发了 X 窗口系统(X Window System,简称 X 或者 X11)作为图形环境。除了 X 外,另外一个需要关注的图形环境是 Wayland 。虽然 Wayland 的目标是替代 X ,并且开源社区也支持 Wayland 向着这个方向发展,但是 Wayland 距广泛使用还有一段路要走。因此,在本章中,我们依然以目前广泛使用的 X 构建基础的图形环境,并安装了 GTK 作为更上层的图形库。事实上,Wayland 更像是 X 的一次整合或者重构,在第 8 章探讨 Linux 的图形原理时,我们会拿出一点篇幅讨论 Wayland ,在那里我们会看到,Wayland 和 X 之间并无本质区别。


一、初始根文件系统

    因为我们使用的是 vita 用户进行编译过程,所以 $SYSROOT 目录下的所有文件的属主和属组都是 vita ,如果对安全问题有顾虑,在最终将其作为根文件系统时,可以将该目录下的所有文件,包括目录的属主和属组,更改为 root

    另外,为简单起见,我们也没有考虑文件系统的大小。如果是为一个真实的系统制作根文件系统,那么可以考虑进行一些优化,比如对二进制文件和动态库使用命令 strip 删除一些运行时不需要的信息和符号表等;删除那些只是在编译时使用的头文件和静态库;等等。

    上面讨论的都不是必须的,如果仅作为一个用于测试的系统,完全可以不必理会,下面是必须要做的几件事。

(1)安装GCC库

    在前面编译 GCC 时,我们已经看到,GCC 也将部分底层函数封装到库中,很多程序会使用 GCC 的这些库,因此,我们也将这部分程序安装到根文件系统中。我们只安装运行时使用的动态库及对应的运行时符号链接,当然,系统中并不一定会用到全部这些库,但是简单起见,这里全部安装了:

在这里插入图片描述

(2)建立相关目录

    在前面讨论从 initramfs 切换到根文件系统时,我们看到,切换程序将最初挂载到文件系统 rootfs 中的 /dev、/run、/proc/sys 目录移动到真正的根文件系统,因此,我们需要在根文件系统上建立这几个目录。另外我们也为 root 用户建立一个属主 root 目录:

在这里插入图片描述

(3)构建程序 /sbin/init

    在内核初始化的最后,启动的第一个进程要装载用户空间的程序从而切入用户空间,通常这个程序是 /sbin 目录下的 init,因此我们要准备这个程序。为简单起见,我们也使用 shell 脚本编写:

在这里插入图片描述
    init 启动了一个交互式的 shell 。其中传递的参数 “-l” 是告诉 bash 以登录方式启动,这样可以使 bash 读取在 /etc/profile、~/.profile 等文件中定义的环境变量。同时要确保 init 程序具有可执行权限:

在这里插入图片描述
    为了让 shell 提示符看上去友好一些,更重要的是为了后面当从宿主系统远程登录到 vita 系统时,方便区分本地终端和登录到 vita 的终端,我们在全局范围的 profile 文件中定义了环境变量 PS1 来控制 shell 提示符的显示内容和风格:

在这里插入图片描述

    其中,“\u” 告诉 shell 显示当前用户名;“\w” 告诉shell显示完整的工作路径;我们将主机名直接硬编码为 vita,为了便于区分是本地的终端还是登入 vita 的终端;接下来我们给提示符加一点漂亮的颜色,“\e[” 与 “m” 之间的内容表示颜色值,在它们之外包围的 “[” 与 “]” 是保证其内的非打印字符,不占用任何空间。颜色设置的格式为 “[\e[F;B;Cm]” ,其中 F是前景色,B 是背景色,C 是一些表示特殊效果的代码,如下划线、闪烁等。

    具体到我们这个例子,其中 31 表示红色,因此,“用户名@vita” 将以红色显示;35 表示洋红,因此工作路径将以洋红色显示。最后,在提示符结束的位置,我们通过 “\e[0m” 将颜色值设定为零,也就是通知终端将前景、背景重置为它们的默认值,以使后续的文字以非彩色显示。

    接下来将 $SYSROOT 目录整个复制到虚拟机,因为命令 scp 会跟随符号链接,所以我们采用先压缩、再复制的办法。

二、以读写模式重新挂载文件系统

    一般在挂载文件系统之前,将使用工具 fsck 检查文件系统的一致性。如果文件系统中存在错误,则 fsck 会试图修复它们,但是这个过程要求文件系统没有挂载或以只读方式挂载。因此我们在 GRUB 的配置文件 grub.cfg 中经常看到内核的命令行参数中有这么一个字串 “ro” ,其是 “read only” 的简写,目的是告诉内核或 initramfs 最初以只读方式挂载根文件系统。在 Linux 系统进入用户空间、使用工具 fsck 检查文件系统后,然后再以读写方式重新挂载根文件系统。这里我们忽略文件系统检查这一过程,直接以读写模式重新挂载根文件系统。

在这里插入图片描述

    如果读者没有更改根文件系统中文件的属主和属组为 root ,那么更新 vita 系统的 /sbin/init 程序后,重启系统,我们来检查一下根文件系统是否以读写方式成功挂载了。

三、配置内核支持网络

    为了方便宿主系统与目标系统传输文件,并且可以从宿主系统登录到目标系统,目标系统需要支持网络。为此,需要配置目标系统的内核支持 TCP/IP 协议和网卡驱动。

四、启动udev

    前面我们将网卡驱动编译为模块,为了自动加载网卡驱动,需要启动 udev,为此需要修改 init 脚本:
在这里插入图片描述

    这几条命令我们在讨论 initramfs 时已经见过了。事实上,这里启动 udev 服务,不仅是为了加载网卡驱动模块。initramfs 中往往只包含存储介质相关的驱动,而其他大量设备的驱动,大部分还是保存在根文件系统中,所以,在挂载了根文件系统后,需要重新模拟一遍热插拔,从根文件系统中加载相关设备的驱动模块。

五、安装网络配置工具并配置网络

    在用户空间中,我们使用工具 ip 来配置网络,工具 ip 包含在软件包 iproute2 中。所以我们首先来编译安装软件包 iproute2 。

在这里插入图片描述

iproute2编译安装

六、安装并配置ssh服务

    既然网络已经配置好了,一般情况下就不必再通过第三方系统(即虚拟机)更新 vita 系统了,可以直接通过网络和 vita 系统打交道了。当然如果是更新内核、initramfs 或者整个文件系统,还是要通过虚拟机系统的。我们在宿主系统和 vita 系统之间使用 ssh 服务进行通信。因此在这一节,我们为 vita 系统安装并配置 ssh 服务。

    我们使用 ssh 协议的开源实现 openssh ,其依赖 zlibopenssl ,因此首先编译安装这两个软件包。

    使用如下命令编译安装 zlib:

在这里插入图片描述

使用如下命令编译安装 openssl:

在这里插入图片描述

七、安装 procps

    为了方便后面调试,我们在 vita 系统上安装 procps 。该软件包中包含了常用的一些工具,如 ps、kill 等。因为 vita 系统中没有安装 ncurses 库,为简单起见,我们只编译不需要使用 ncurses 库绘制界面的程序,这就是编译 procps 时传递参数 “–without-ncurses” 的目的。

在这里插入图片描述

八、安装X窗口系统

    UNIX 系统的主要目标就是多用户、多任务,而且允许多个用户远程登录并发执行任务。这种设计哲学同样被带到了 X 窗口系统中。X 的实现者将 X 设计为客户/服务器的架构,应用程序相当于客户端,它们不需要关心具体的显示和用户输入,而由 X 服务器负责管理显示设备和输入设备。应用程序只需要将请求,比如 “绘制一条直线从点A到点B” ,发送给 X 服务器,而由 X 服务器负责将其绘制到具体的显示设备上。X 服务器也会将用户的输入(包括鼠标、键盘等输入事件),转发给对应的应用。

    X 将协议相关实现封装到了一个库中,开发者将这个库称为 Xlib 。后来因为效率问题,又开发了 xcb 来替代 XlibXlib 中封装的只是 X 的核心协议,X 使用扩展的方式扩充 X 协议,其他扩展协议可以在单独的库中实现。

    作为类 UNIX 的图形系统的基础,X 的复杂是难以避免的。也恰恰是因为 X 的复杂,很多人提及 X 的安装就会谈虎色变。虽然 X 系统非常庞大,实际上它也是有章可循的。本节笔者就带领读者从头安装一个 X 窗口系统。鉴于 X 的安装过程比较烦琐和复杂,我们提供了一个安装脚本 build-X11.sh 。但是笔者建议读者尽量使用手动的方式安装,这样可以在思考和解决问题中不断提高。遇到自己实在解决不了的问题时再参考这个脚本,从而达到更好的学习效果。

1、安装 M4 宏定义

    X 定义了一些公用的 M4 宏,并将它们放在软件包 util-macros 中。X 的各个组件的配置脚本中将使用 M4 宏,因此我们首先来安装 M4 宏,方法如下:

在这里插入图片描述

2、安装X协议和扩展

    X 包含了多种协议和扩展,为简单起见,Vita 系统不必全部安装。比如禁掉了记录事件的扩展 Record,支持扩展屏幕的协议 Xinerama 及用于屏保的 Screensaver,禁掉了已经过时的 DRI1 等。下面是 vita 系统安装的协议,安装这些协议时没有先后顺序要求。如果不要求 X 服务器支持 DRI2,那么可以安装更少的协议,比如去掉 glprotodri2protodamageproto等。

(1)核心协议
    Xlib 中的绝大部分编程接口,如 XCreateWindow 、 XMapWindow 、XDrawRectange、XCopyArea 等都是由 X 核心协议定义的。核心协议的定义在软件包 xproto 中。

(2)基本扩展
    X 的基本扩展包括:DOUBLE-BUFFER(DBE)、DPMS、Extended-Visual-Information ( EVI ) 、 Generic Event Extension 、 LBX 、 MIT-SHM 、 MIT-SUNDRY-NONSTANDARD、Multi-Buffering、SECURITY、SHAPE、SYNC、TOG-CUP、XC-APPGROUP、XTEST。它们的定义在软件包 xextproto 中。

(3)键盘扩展
    键盘扩展定义了键盘的模型、布局,如对于不同的键盘模型,某个键值对应的字符。键盘扩展的定义在软件包 kbproto 中。

(4)输入扩展
    输入扩展是为一些特殊的输入设备定义的协议。通过这个扩展,输入设备可以模拟出与鼠标、键盘等核心输入设备相同格式的事件。输入扩展的定义在软件包 inputproto 中。

(5)XCB 协议
    鉴于 Xlib 的效率,开发者们开发了更高效的 XCB 来替代 Xlib。XCB 协议是用于这个库的协议,其以 XML 形式定义,并提供 python 程序将这些 XML 描述文件转换为相应的程序代码。XCB 协议的定义在软件包 xcb-proto 中。

(6)GLX 扩展
    GLX 扩展定义了 OpenGLX 之间通信的协议。该扩展的定义在软件包 glxproto 中。

(7)DRI2 扩展
    DRI2 扩展是 DRI 的第 2 个版本,定义了应用不通过 X 服务器直接使用硬件进行渲染的协议。DRI2 扩展的定义在软件包 dri2proto 中。

(8)XFixes 扩展
    从这个扩展的名字也可以看出,这个扩展其实是为解决 X 核心协议存在的各种限制的。该扩展的定义在软件包 fixesproto 中。

(9)Damage 扩展
    Damage 扩展是 X 服务器用来记录那些离屏的、发生了变化的绘制区域的协议。Damage 扩展的定义在软件包 damageproto 中。

(10)XC-MISC 扩展
    应用可以通过 XC-MISC 扩展获取 X 服务器可用的资源 ID,如 GetXIDRange、GetXIDList 等。该扩展的定义在软件包 xcmiscproto 中。

(11)BIG-REQUESTS 扩展
    BIG-REQUESTS 扩展提供了对大于 262140 字节的请求的支持。该扩展的定义在软件包 bigreqsproto 中。

(12)RANDR 扩展
    RANDR 扩展定义了动态调整屏幕尺寸、旋转屏幕以及镜像屏幕的协议。X 提供的工具 xrandr 就是这个协议的一个典型使用者。该扩展的定义在软件包 randrproto 中。

(13)RENDER 扩展
    RENDER 扩展是 X 使用的较新的渲染模型,用于合成多个绘制区域,相对于原始的通过复制进行合成的模型其更有效率。该扩展的定义在软件包 renderproto 中。

(14)字体扩展
    字体扩展定义了 X 中与字体处理相关的协议。字体扩展的定义在软件包 fontsproto 中。

(15)视频扩展
    视频扩展定义了 X 的视频输出相关的协议。该扩展的定义在软件包 videoproto 中。

(16)复合扩展
    复合扩展是为了 X 支持窗口特效设计的扩展。在没有这个扩展之前,所有的在窗口上的绘制操作都 “实时” 显示在屏幕上。而复合扩展允许窗口可以先在离屏的区域进行绘制。复合扩展的定义在软件包 compositeproto 中。

(17)资源扩展
    资源扩展定义了应用程序查询 X 服务器各种资源使用情况的协议。该扩展的定义在软件包 resourceproto 中。

(18)直接图形访问扩展
    顾名思义,直接图形访问扩展也是为了直接访问图形硬件设计的协议,不过其功能非常有限,目前基本已经停止开发,但 vesa 驱动还在使用这个扩展。该扩展的定义在软件 xf86dgaproto 中。这些协议的配置安装都非常简单,而且安装命令完全相同。以 xproto 为例,安装命令如下:

在这里插入图片描述

3、安装 X 相关库和工具

    在安装 X 服务器前,我们需要安装 X 服务器依赖的库、这些库依赖的库以及 X 服务器使用的工具和相关数据。注意,某些库是有安装顺序要求的,比如,libX11 需要在 libxkbfile 前安装,安装 libXfont 前需要先安装 freetype,libdrm、expat 需要在 Mesa 前安装等。读者按照下面的顺序安装即可。

(1)pixman
    pixman 是一个底层的像素操作的库,提供图形合成及光栅化等功能,是 X 中软件渲染的基础。

(2)xtrans
    xtrans 封装了网络传输的基本功能,从开发角度讲,是 X 服务器和应用程序之间进行通信的基础。X 服务器、libX11 等 X 的相关组件都要用到这个库。

(3)libXau
    libXau 是 X 服务器和应用程序之间认证授权使用的库。

(4)libX11、libxcb 和 libpthread-stubs
    libX11 是为应用程序提供的 X 协议的实现,应用程序使用 libX11 中提供的 API 和 X 服务器进行通信。
    因为 libX11 的效率问题,开发人员又开发了 libxcb 来替换 libX11 。而反过来,libX11 也基于 xcb 进行了改进,所以在安装 libX11 前,需要安装 libxcb。
    libxcb 依赖 libpthread-stubs , 因此在安装 libxcb 前需要先安装 libpthread-stubs

(5)libxkbfile、xkbcomp 和 xkeyboard-config
    这三个包都与键盘扩展相关。X 服务器根据键盘扩展,确定不同键盘模型的键盘的布局、键值到字符的转换等。键盘相关的数据就包含在 xkeyboard-config 中。
    而开发者将操作这些数据的功能封装在库 libxkbfile 中。
    xkbcomp 包中提供了同名的工具 xkbcomp,该工具根据键盘映射的描述,将键盘映射编译为 X 服务器可以识别的指定格式。

(6)libXfont、libfontenc 和 freetype
    这几个库都是与字体处理相关的。开发者将X使用的与字体相关的功能封装在库 libXfont 中。
    而 libXfont 使用 freetype 进行字体渲染,使用 libfontenc 处理字体编码。所以安装 libXfont 前需要安装 libfontencfreetype

(7)pciaccess
    早期版本的GPU的2D驱动,包括X服务器中的一些功能,不通过内核,而是直接访问PCI接口的GPU,这就是这个库的由来。现在虽然GPU驱动都通过内核访问GPU硬件了,但是X服务器中并没有清理得特别干净,还残存着对pciaccess库的依赖。
    库libdrm中也使用了部分pciaccess中的功能。比如通过读取PCI寄存器探测BIOS中给GPU分配的显存大小,libdrm借助的就是库pciaccess中的函数。

(8)libdrm
    用户空间的组件,如GPU的2D驱动和3D驱动、GLX扩展(包括X服务器端和Mesa端的实现部分)等,都需要通过内核的DRM模块访问GPU。为了方便用户空间的组件访问内核DRM模块,开发者开发了库libdrm。

(9)Mesa、expat、libXext、libXdamage和libXfixes
    如果配置X服务器支持DRI2,那么必须要安装Mesa,它是3D应用程序进行直接渲染的基础。
    Mesa中的DRI扩展使用Damage扩展告知X服务器绘制完成,因此需要安装libXdamage。
    Mesa中的DRI2扩展使用XFixes扩展中的如XFixesCreateRegion创建发生了改变的区域,也就是绘制发生的区域,因此也需要安装库libXfixes。
    而在安装扩展前,需要安装库libXext。它是所有扩展的公共库。

    另外,Mesa使用expat解析XML,所以安装Mesa前,还需要安装expat。

    在安装上述相关库之前,在宿主系统上还需安装几个辅助的软件包。一个是 xkeyboard-config 依赖的 intltool 。另外是 Mesa 依赖的 xutils-dev、flexbison,使用如下命令在宿主系统上安装这几个软件包:

在这里插入图片描述

4、安装X服务器

    万事俱备,现在我们开始安装X服务器,配置及安装命令如下:

在这里插入图片描述

5、安装GPU的2D驱动

如果只是在虚拟机上运行目标系统,安装vesa驱动即可,安装命令如下:

在这里插入图片描述

    但是如果是在真实的机器上运行目标系统,最好安装相应GPU的2D驱动。在安装脚本 build-X11.sh 中,包含了安装Intel GPU的2D驱动的方法,读者如果需要,可以参考。PC上使用的GPU一般都符合VESA标准,所以在通常情况下,用vesa也能勉强驱动,但是vesa驱动很多特性不支持,比如硬件加速。

6、安装X的输入设备驱动

    看到输入设备驱动,读者可能会有个疑问:内核中不是包括了各种设备的驱动吗?怎么X中还要安装设备驱动?没错,输入设备的驱动是在内核中,X 中的所谓输入设备的驱动 evdev 谈不上是一个驱动了,只不过大家习惯这么称呼而已。evdev 模块并不面向任何具体输入设备,它只不过是接收和解析内核发送到用户空间的输入事件。仔细观察图 6-12 所示的 Linux 输入子系统的架构,读者自然就会明白。

在这里插入图片描述

    操作系统将面对各种各样的输入设备,如鼠标、键盘、触摸屏、游戏手柄等。由于这些输入设备大部分不遵循统一的标准,所以导致应用程序,比如 X 将不得不处理来自各种输入设备的五花八门的输入事件。

    因此,内核中抽象了一个输入子系统。在输入子系统中,设备驱动面对各种各样具体的硬件设备,而输入事件经过事件处理模块处理后,将以统一的格式发送给用户空间的应用,用户空间的应用无需再为各种各样的输入事件格式疲于奔命。

    现在很多输入设备都使用 USB 接口,对于 USB 接口的输入设备,图 6-12 演化为图 6-13 所示。

在这里插入图片描述

    USB 设备通过主控制器连接到主机,所以内核需要驱动 USB 主控制器。USB 流行的一个主要原因就是具有统一的标准,所以对于 USB 接口的输入设备,它们使用统一的设备驱动,即图 6-13 中的 USB HID 驱动。

    通过上面的讨论可见,从操作系统的角度,安装 X 的输入设备驱动事实上有两件事需要做:一是需要配置内核的输入设备相关的驱动和模块;二是安装 X 的 evdev 模块。

7、运行X服务器

    X 服务器将建立一个套接字与应用程序进行通信,通常这个套接字被命名为 “/tmp/.X11-unix/X0” ,0 表示是第一个 X 服务器,如果再启动第二个 X 服务器,则为 “/tmp/.X11-unix/X1” 。除了建立套接字外,X 服务器还将在 /tmp 目录下建立一个锁文件,例如对于第一个 X 服务器,这个锁文件为 “/tmp/.X0-lock” 。另外,在前面编译时,我们指定 X 服务器将日志文件存放在 /var/log 目录下,因此,我们需要在根文件系统中建立这两个目录:

在这里插入图片描述
    为了使书中的截图不至于尺寸过大,笔者将vita系统的 X 服务器的分辨率设置为 “640×480” 。最初,X 服务器完全由用户通过书写配置文件的方式手动配置,在 udev 出现后,X 服务器采用了自动配置技术。但是 X 也给用户留有机会进行手动微调,并且用户手动配置的优先级还要更高。当然读者不必设置分辨率,由 X 服务器自动探测即可。通过 xorg.conf 设定分辨率的方法如下:

在这里插入图片描述
    最初,X 服务器启动后将创建并显示鼠标指针。后来,X 的开发人员认为只有在应用程序明确表明需要与用户进行交互时,才应该显示鼠标指针。所以,这个默认行为发生了改变,在 X 服务器启动后,不再默认创建并显示鼠标指针,而是在第一个应用明确调用类似 XDefineCursor 这样的函数请求X服务器显示鼠标后,才显示鼠标指针。

    但是 X 还是为用户留了余地,增加了一个命令行参数 “-retro” 。如果用户运行 X 服务器启动时即创建和显示鼠标,那么给 X 服务器传递这个参数即可。

    在默认情况下,当最后一个 X 应用断开与 X 服务器的连接后,X 服务器默认自动重置。同样,X 也为这个行为提供了修正的机会,用户可以使用命令行参数 “-noreset” 关闭这个特性。vita 系统不需要这个特性,因此我们传递了 “-noreset” 参数给 X 服务器。

    最后,使用如下命令运行 X 服务器:

Xorg -retro -noreset &

    在 X 服务器启动成功后,将创建一个根窗口,作为未来所有用户窗口的根。默认情况下,这个根窗口只以一个简单的灰色背景显示。并且我们看到,X 也按照我们的要求,创建并显示了鼠标指针。

8、一个简单的X程序

    我们使用 Xlib 编写一个简单的 X 程序来确认 X 服务器是否已经正常工作。这个程序非常简单,就是创建一个窗口,并在其上显示字符串 “Hello X Window!”,代码如下:

// /vita/build/hello_x/hello_x.c:#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <string.h>int main(int argc, char *argv[]) {Display *dpy;int screen_num;Window win;int x, y:unsigned int w, h;Atom atom_win_type, atom_win_type_normal;XEvent e;GC gc;char *s = "Hello x Window !";if (!(dpy = XOpenDisplay (NULL))) {fprintf(stderr, "Can't connect to X sever!\n");return -1;}screen_num = DefaultScreen(dpy) ;× = у = 20;w = DisplayWidth(dpy, screen_num) / 2;h = DisplayHeight (dpy, screen_num) / 2;win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy),x, y, w, h, 2, BlackPixel(dpy, screen_num),WhitePixel (dpy, screen num));XStoreName(dpy, win, "Hello X11");atom_win_type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);atom_win_type_normal = XInternAtom(dpy,"_NET_WM_WINDOW_TYPE_NORMAL", False);XChangeProperty(dpy, win, atom_win_type, XA_ATOM,32, PropModeReplace,(unsigned char *)&atom_win_type_normal, 1);XSelectInput (dpy, win, ExposureMask)gc = XCreateGC(dpy, win, 0, 0);XMapWindow(dpy, win);while (1) {XNextEvent (dpy, &e);switch (e.type) {case Expose:XDrawString(dpy, win, gc, 30, 30, s, strlen(s));break;}}
}

编译这个程序的 Makefile 如下:

# /vita/build/hello_x/Makefile:
LDFLAGS=`pkg-config --libs x11`
hello_x: hello_x.o
clean:rm -rf hello x *.o

    在登录到vita的终端中,使用如下命令启动X服务器,并运行应用程序 hello_x:

Xorg -retro &
export DISPLAY=:0.0
./hello_x &

注意环境变量DISPLAY的设置,其格式如下:

hostname: displaynumber.screennumber

    如果主机名(hostname)为空,则表示 X 服务器运行在本机。读者可以把 display 理解为一个 X 服务器,screen 这里无须解释。displaynumberscreennumber 均从 0 开始计数,如值为 “:0.0” 表示运行在本机的第一个 X 服务器接的第一块屏幕。vita系统只启动了一个 X 服务器,并且只接一块屏。所以自然将环境变量 DISPLAY 设置为 “:0.0” 。

9、配置内核支持 DRM

    如果读者是在真实机器上调试的,那么为了使 GPU2D 驱动和 3D 驱动都可以正常工作,内核中还需要进行相关的配置,因为用户空间的 GPU 驱动是通过内核中的 DRM 访问 GPU 的。GPU 用户空间的驱动(2D3D 驱动)和内核空间的驱动(DRM 模块)之间的关系如图 6-22 所示。

在这里插入图片描述

九、安装图形库

    前面,我们使用 Xlib 编写了一个小程序。但是我们也看到,Xlib 是多么的原始,使用 X 提供的库编写一个如此简单的程序是多么的复杂,更别提具有复杂图形用户界面的程序了。所以先辈开发者们前赴后继,尝试在 Xlib 的基础上为 X 开发更高级的图形库,这些图形库通常被称为 Widget LibrariesToolkits ,其中最著名的就是 GTKQT 。这些图形库引入了控件的概念,极大简化了程序开发,也提高了开发效率。

    我们选择 GTK 作为 vita 系统的图形库。这一节,我们就来编译安装 GTK。相比于安装 X ,图形库的安装过程相对要简单,但是我们也提供了一个编译脚本 build-gtk.sh 。必要时,读者可以参考这个脚本。

1、安装 GLib 和 libffi

    GLibGTK+GNOME 工程的基础底层核心程序库,是一个实用的轻量级的库,它提供常用的数据结构、相关的处理函数和一些运行时支承机制,如事件循环、线程、对象系统等。因此安装 GTK+ 前首先需要安装 GLibGLib 目前也由开发 GTK+ 的团队维护。

    因为 GLib 提供的对象系统(GObject)可以绑定到多种语言,常见的如 C、Python、Ruby 等,因此,GLib 的对象系统借助库 libffi 处理不同语言间的函数调用。libffi 是专门设计的一个库,主要用于不同语言间的相互调用。因此,安装 GLib 前还需要安装 libffi

    libffi 和 GLib 的编译安装命令如下:

在这里插入图片描述

2、安装ATK

    ATKAccessibility ToolKit)是 GTK 中实现辅助功能使用的库,包括辅助视觉、听觉、打字等。这个库也是别无选择,必须要安装的,安装命令如下:

在这里插入图片描述

3、安装libpng

    图形库当然离不开图片格式处理的库,常用的图片格式有多种,比如 PNG、JPEG 等。但是为了简单起见,vita 系统只支持 PNG 图片格式。处理 PNG 图形格式的库是 libpng ,安装命令如下:

在这里插入图片描述

4、安装GdkPixbuf

    GTK 使用 GdkPixbuf 进行图片的渲染,是 GTK 图形库的基本依赖之一,是必须安装的,安装命令如下:

在这里插入图片描述

5、安装Fontconfig

    Linux 最初在我国的程序员中流行时,有很多程序员热衷于 Linux 的美化,其中优化文字的显示是其中主要内容之一,至今在各个 Linux 论坛仍然可见 Linux 美化的身影。文本渲染比较烦琐,除了技术原因外,文本处理机制不断的发展变化,从最初的 X 的核心字体,到 X 的字体服务器,再到现在广泛采用的客户端渲染,也给这个本身就不是特别容易理解的领域增加了很多复杂性。

    凡是涉及字体相关的地方,我们经常看到如 Fontconfig、Freetype、Pango, 甚至更多,这些库在文本渲染中都担任什么角色?它们之间的关系又是什么?在我们埋头搭建系统时,还是要不时抬头看看路的。下面,我们就结合图 6-28 来简单地介绍一下文本的渲染。

在这里插入图片描述

(1)字符编码(character code)
    虽然我们在写程序时,直接使用可读的字符,但事实上,在程序内部,是用字符的编码来代表字符的。字符的编码有多种标准,比如 ISO-8859 系列编码,Unicode 编码以及我国的 GB18030 等。
    假设系统使用 UTF8 编码,当程序准备显示字符串 “你好Linux!” 时,程序中将以编码 “4F60 597D 4C 69…” 来记录这个字符串。

(2)字形(glyph
    字形是字的形体的简称,GB/T 16964《信息技术字型信息交换》中关于字形的的定义为:一个可以辨认的抽象的图形符号,它不依赖于任何特定的设计。

    这样解释读者可能依然会感到比较生疏,因为平时我们很少使用这个概念,但是提到字体,大家就一定比较熟悉了,因为操作系统中一定要安装字体文件的,否则是不能正确显示字符的。而所谓的这个字体文件,其实就是字形的集合。

    以 TrueType 字体文件为例,其中包含两个关键的数据结构:

    ◆ 一个是字形表,也称为 glyf 。字形表中每一项代表一个字形,使用字形索引访问其中的字形。TrueType 的字形表中,每个字形的描述并非如图 6-28 中的字形表(glyf)中显示的那样直观,字形表中描述的字形信息都是矢量的,字符的每一个笔画都是由多条曲线包围而形成的。一次曲线需要两个点来确定,二次需要三个点,三次就需要四个点。字体内部保存了这些点的坐标。

    ◆ 一个是字符编码到字形映射表(Character to Glyph Mapping),简称 cmap 。读者可能会有个疑问,cmap 中的第二列为什么不是字形,而是字形索引呢?原因是字体文件可能使用在不同的编码环境中,所以字体文件可能包含多个 cmap 表,比如 UTF8 对应一个 cmap 表,GB18030 对应另外一个 cmap 表。另外,一个字体文件中也可能不只包含一种字体。

(3)排版(layout)
    每每谈到文本渲染时,大家更多的关注在字体上,却往往忽略了文本的布局排版。实际上,文字的排版是重要而且复杂的。排版引擎需要将单个字符按照一定的间距美观的排列起来。

    除了处理字形信息外,由于世界上有多种文字体系,因此,文本可能是多种语言混合的。而且,还有像阿拉伯文、希伯来文这种文字体系是从右向左书写,更别提布局规则极其复杂的印度系文字。

    可见,排版引擎是一位真正的幕后英雄。而且,文本渲染的过程都是由排版引擎牵头开始的,不同的图形库可能使用不同的排版引擎,GTK 使用的排版引擎是 Pango

(4)确定字体
    在将字符编码转化为字符前,首先需要确定字体文件,否则巧妇也难为无米之炊。一个系统中可能安装了多个字体文件,因此,在众多的字体文件中要选择一个最合适的,这就是 Fontconfig 的主要任务之一。

(5)光栅化
    一旦字体确定后,Fontconfig 使用库 Freetype 提供的接口,确定字符编码对应的字形索引,依据的就是如 TrueType 字体文件中的 cmap 表。最后,Freetype 根据字形索引,从字体文件的字形表中获取描述字形的矢量信息,构建具体的字形,这个过程也叫光栅化。经过光栅化的字符编码,就是一普通图形了,接下来无论是显示到具体窗口中,还是进行其他处理,都与处理普通的图形完全相同。

    理解了各个库的作用后,下面我们开始安装这些库。

    Freetype 在前面安装 X 时已经安装,接下来只需安装 FontconfigPango 。由于 Cairo 依赖于 Fontconfig ,而 Pango 又基于 Cairo 进行字体渲染,所以,这里的安装顺序看上去有点奇怪。我们先安装 Fontconfig,中间插播 Cairo,然后才安装 Pango

安装 Fontconfig 的命令如下:

在这里插入图片描述

6、安装Cairo

    Cairo 是一个矢量图形库,GTK 使用其作为绘制后端。换句话说,GTK 的绘制动作由 Cairo 完成。看到这里,读者可能会非常困惑:X 上的应用不是由 X 服务器负责绘制吗?没错,暂且不提我们第 8 章讨论的 DRI。事实上,即使普通的 2D 应用也是可以自己绘制的,只不过,应用是将内容绘制在一个离屏的区域,但是最后还是要请求 X 服务器将绘制的内容显示到屏幕上。应用或者将绘制的内容复制到 X 服务器,或者使用 X 提供的 RENDER 扩展。当然,应用也可以将全部绘制请求 X 服务器完成,这就要看具体图形库采用的策略了。

    安装 Cairo 的命令如下:

在这里插入图片描述

7、安装Pango

安装Pango的命令如下:

在这里插入图片描述

8、安装libXi

图形库当然是要接收用户输入的,X 输入扩展协议的实现是库 libXi,安装命令如下:

在这里插入图片描述

9、安装GTK

    GTK 的基本依赖已经安装完成,只差完成最后一步了,安装GTK的命令如下:

在这里插入图片描述
    至此,图形库 GTK 的安装过程已经全部完成,读者可以将 /vita/sysroot 目录下的文件系统更新到vita的根文件系统了。

10、安装GTK图形库的善后工作

    更新了vita系统的根文件系统后,在运行使用 GTK 编写的程序前,我们还要在vita系统上为图形库做一点收尾工作。注意下面两个操作需要在使用安装了 GTK 图形库的根文件系统重启vita系统后进行。

(1)为 Pango 创建语系和模块对应关系的文件
    不同语系,对布局有不同的要求,全世界有各种各样的语系,如汉语、阿拉伯语、印度语等。Pango 采用模块化的方式提供对这些语系的支持。为了提高效率,在运行时,Pango 不会到文件系统中解析具体的模块,查看其支持的语系,而是直接读取/etc/pango目录下的文件pango.modules,其中记录了每个模块及其支持的语系。因此,我们需要为Pango创建文件pango.modules 。

(2)为库 GdkPixbuf 创建模块信息文件
    在安装库 GdkPixbuf 时,我们看到,GdkPixbuf 使用模块的形式支持各图形格式。因此,在这个库初始化时,需要加载这些模块。但是这些模块存储在文件系统的什么位置,每个模块又支持什么图形格式等,诸如此类信息从哪里获取呢?为了提高加载速度,GdkPixbuf 没有去再次扫描每个模块,而是直接从系统的一个文件中读取,因此,我们需要为 GdkPixbuf 创建这个文件。

11、一个简单的GTK程序

    最后,我们使用一个简单的程序来测试我们的 GTK 是否工作正常,程序代码如下:

在这里插入图片描述

编译该程序的Makefile文件如下:

在这里插入图片描述
    可见,同样是显示一个简单的窗口,使用 GTK 编写就简单多了,那些烦琐的细节已经实现在如 GTK 等这些图形库中。编译这个程序,并将其复制到 vita 系统并运行,步骤与程序 hello_x 完全相同。

十、安装字体

    对于基于 Xlib 编写的程序,一般简单的字符使用X中的内置字体就可以应付了。X的内置字体在 libXfont 中:

在这里插入图片描述

    其中 file_6x13 中记录的就是简单的点阵字体,又称位图字体,显然这个内置的点阵字体是把每一个字符都分成 6×23 个点,然后用每个点的虚实来表示字符的轮廓。

    这也是为什么前面在没有安装字体的情况下,使用 Xlib 编写的例子可以显示字符的原因。但是既然有内置的字体,那为什么使用 GTK 的程序不能显示字符呢?原因是 GTK 程序的字体是在客户端绘制的,客户端绘制完成后,将字形位图传给 X 服务器。而 GTK 中并没有像 libXfont 那样内置了字体,所以如果系统中没有安装字体,当然应用就找不到字体了。因此,我们需要安装字体。

    字体的安装非常简单,直接把字体文件复制到相关的目录下即可。但是安装在哪个目录下呢?前面我们已经看到,Linux 使用 Fontconfig 寻找字体,因此这个问题要问 Fontconfig 。没错,Fontconfig 在其配置文件中明确指明了其寻找字体文件的目录:

在这里插入图片描述

    这里,我们使用文泉驿字体,并将其安装到vita系统的 /usr/share/fonts 目录下,命令如下:

在这里插入图片描述

    安装完字体后,再次执行 gtk_hello,就会发现字符不再是一个一个的 “方框” 了。

   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/218351.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

媒体直播平台有哪些,活动直播如何扩大曝光?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体直播平台包括人民视频、新华社现场云、中国网、新浪新闻直播、搜狐视频直播、凤凰新闻直播、腾讯新闻直播等。活动直播想要扩大曝光&#xff0c;可以考虑以下方式&#xff1a; 1.选择…

海思平台isp之ccm标定

文章目录 1、raw图采集2、ccm标定2.1、标定参数配置2.2、标定效果优化2.2.1、优化方式一2.2.2、优化方式二2.2.3、优化方式三1、raw图采集 raw图采集步骤及标准,请参考文章 《海思平台isp之ccm标定》。2、ccm标定 2.1、标定参数配置 (1)图像基本参数 (2)黑电平设置 (…

spring boot 实现直播聊天室

spring boot 实现直播聊天室 技术方案: spring bootwebsocketrabbitmq 使用 rabbitmq 提高系统吞吐量 引入依赖 <dependencies><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.42&…

十六、YARN和MapReduce配置

1、部署前提 &#xff08;1&#xff09;配置前提 已经配置好Hadoop集群。 配置内容&#xff1a; &#xff08;2&#xff09;部署说明 &#xff08;3&#xff09;集群规划 2、修改配置文件 MapReduce &#xff08;1&#xff09;修改mapred-env.sh配置文件 export JAVA_HOM…

从零开始:前端架构师的基础建设和架构设计之路

文章目录 一、引言二、前端架构师的职责三、基础建设四、架构设计思想五、总结《前端架构师&#xff1a;基础建设与架构设计思想》编辑推荐内容简介作者简介目录获取方式 一、引言 在现代软件开发中&#xff0c;前端开发已经成为了一个不可或缺的部分。随着互联网的普及和移动…

智能科技企业网站搭建的作用是什么

随着科学技术快速提升&#xff0c;各种智能产品随之而来&#xff0c;每个赛道里都涌入了大量企业商家&#xff0c;有些热门产品更是广受关注&#xff0c;对企业来说&#xff0c;形象、品牌、信息等方面需要完美呈现到用户眼前&#xff0c;而网站无疑是很好的工具。 企业通过【…

打开软木塞,我们来谈谈葡萄酒泡泡吧

香槟是任何庆祝场合的最佳搭配。从婚礼和生日到单身派对和典型的周五晚上&#xff0c;这款气泡饮料是生活中特别聚会的受欢迎伴侣。 来自云仓酒庄品牌雷盛红酒分享你知道吗&#xff0c;你喜欢喝的那瓶香槟酒可能根本不是香槟&#xff0c;而是汽酒&#xff1f;你不是唯一一个认…

造型精致的冰精灵充电头,充电效率高安全可靠,居家出行皆可用

随着大家对手机的依赖度越来越高&#xff0c;快速充电已经成为必不可少的需求。快充当然少不了支持快充的充电器&#xff0c;现在市面上的快充头很多&#xff0c;安全性和便携性是我们选择时的重点关注方向&#xff0c;我目前用的是战飞ZEFi冰精灵&#xff0c;这款产品有着独特…

【LeetCode刷题】-- 161.相隔为1的编辑距离

161.相隔为1的编辑距离 方法&#xff1a;一次遍历 首先&#xff0c;我们要确认字符串的长度不会相差太远。如果长度差了2个或更多字符&#xff0c;那么 s 和 t 就不可能是一次编辑之差的字符串。 接下来&#xff0c;我们假设 s 的长度总是短于或等于 t 的长度。如果不是这样&…

【C语言】结构体内存对齐

目录 引入结构体 结构的声明 创建和初始化 内部元素的使用&#xff1b; 特殊声明&#xff1a; 结构体在内存中的对齐 练习&#xff1a; 引入结构体 C语言有各种数据类型&#xff0c;我们已经对一些数据类型很熟悉&#xff1a; 整型&#xff08;int&#xff09;- 存储整…

MAC IDEA Maven Springboot

在mac中&#xff0c;使用idea进行maven项目构建 环境配置如何运行maven项目1.直接在IDEA中运行2.使用jar打包后执行 如何搭建spring boot1.添加依赖2.创建入口类3.创建控制器4. 运行5.其他 环境配置 官网安装IDEA使用IDEA的创建新项目选择创建MAEVEN项目测试IDEA的MAVEN路径是…

Mybatis-plus介绍与入门

前言 MyBatis-Plus是在MyBatis基础上的一个增强工具库&#xff0c;旨在简化开发者的工作&#xff0c;提高开发效率&#xff0c;同时保留MyBatis的灵活性。使用 MyBatis-Plus 可以减少重复性的代码&#xff0c;简化常见的数据库操作 官方学习文档&#xff1a;MyBatis-Plus (bao…

phpstudy是什么?

PHPStudy 是一个集成环境工具&#xff0c;它将 PHP 开发所需的软件&#xff0c;如 Apache&#xff08;Web服务器&#xff09;、MySQL&#xff08;数据库服务器&#xff09;、PHP&#xff08;脚本语言&#xff09;等打包在一起&#xff0c;以便用户能够轻松安装和配置这些软件&a…

fl studio20中文内测版下载2024最新完美实现汉化

fl studio20是一款众所周知的水果编曲软件&#xff0c;能够剪辑、混音、录音&#xff0c;它的矢量界面能更好用在4K、5K甚至8K显示器上&#xff0c;还可以可以编曲、剪辑、录音、混音&#xff0c;让你的计算机成为全功能录音室&#xff0c;不论是在功能上面还是用户界面上都是数…

为了吃鸡苦练狙击,避免坑队友自己造一个狙击游戏!

引言 一文教会你造一个简易的狙击游戏。 说到狙击&#xff0c;相信大家都不陌生&#xff0c;无论是影视作品还是网络游戏&#xff0c;都经常能看到狙击枪的身影&#xff0c;最深刻的是它能够从百里之外&#xff0c;一枪爆头。 本文将介绍如何在Cocos Creator中造一个简易的狙…

真正可行的vue3迁移到nuxt3方法(本人亲测,完全避坑)

终于到了总结经验的时候了&#xff0c;这绝对是全网唯一、完全真正可行的干货。 在我看来&#xff0c;知识就是要拿来分享的&#xff0c;分享给他人也是在提高自己。我绝对不会搞什么订阅或者vip专栏来搞钱坑害各位&#xff0c; 因为我在csdn写文章最主要的目的是为了记录和总…

虚幻学习笔记13—C++静态和动态加载

一、前言 我们在蓝图中可以很方便的添加各种需要的组件&#xff0c;那么在C代码中要如何实现呢。在代码中分静态和动态加载&#xff0c;而无论静态和动态&#xff0c;加载的内容有资源和资源类&#xff0c;资源类通常为带资源的蓝图类。 二、实现 在实现静态或动态加载时&…

科技云报道:从数据到生成式AI,是该重新思考风险的时候了

科技云报道原创。 OpenAI“宫斗”大戏即将尘埃落定。 自首席执行官Sam Altman突然被董事会宣布遭解雇、董事长兼总裁Greg Brockman辞职&#xff1b;紧接着OpenAI员工以辞职威胁董事会要求Altman回归&#xff1b;再到OpenAI董事会更换成员、Altman回归OpenAI。 表面上看&…

OpenHarmony关于修改系统横屏导致启动视频显示不全问题解决

前言 OpenHarmony源码版本&#xff1a;4.0release 开发板&#xff1a;DAYU / rk3568 前段时间写的设置OpenHarmony启动视频&#xff0c;在竖屏状态下是正常的&#xff0c;但是横屏状态下显示不全。 链接直达&#xff1a;OpenHarmony 设备启动Logo和启动视频替换指南-CSDN博…

Java EE 多线程之线程安全的集合类

文章目录 1. 多线程环境使用 ArrayList1. 1 Collections.synchronizedList(new ArrayList)1.2 CopyOnWriteArrayList 2. 多线程环境使用队列2.1 ArrayBlockingQueue2.2 LinkedBlockingQueue2.3 PriorityBlockingQueue2.4 TransferQueue 3. 多线程环境使用哈希表3.1 Hashtable3.…