在第 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 删除一些运行时不需要的信息和符号表等;删除那些只是在编译时使用的头文件和静态库;等等。
上面讨论的都不是必须的,如果仅作为一个用于测试的系统,完全可以不必理会,下面是必须要做的几件事。
在前面编译 GCC 时,我们已经看到,GCC 也将部分底层函数封装到库中,很多程序会使用 GCC 的这些库,因此,我们也将这部分程序安装到根文件系统中。我们只安装运行时使用的动态库及对应的运行时符号链接,当然,系统中并不一定会用到全部这些库,但是简单起见,这里全部安装了:
在前面讨论从 initramfs 切换到根文件系统时,我们看到,切换程序将最初挂载到文件系统 rootfs 中的 /dev、/run、/proc 和 /sys 目录移动到真正的根文件系统,因此,我们需要在根文件系统上建立这几个目录。另外我们也为 root 用户建立一个属主 root 目录:
在内核初始化的最后,启动的第一个进程要装载用户空间的程序从而切入用户空间,通常这个程序是 /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 会跟随符号链接,所以我们采用先压缩、再复制的办法。
如果读者没有更改根文件系统中文件的属主和属组为 root ,那么更新 vita 系统的 /sbin/init 程序后,重启系统,我们来检查一下根文件系统是否以读写方式成功挂载了。
为了方便宿主系统与目标系统传输文件,并且可以从宿主系统登录到目标系统,目标系统需要支持网络。为此,需要配置目标系统的内核支持 TCP/IP 协议和网卡驱动。
前面我们将网卡驱动编译为模块,为了自动加载网卡驱动,需要启动 udev,为此需要修改 init 脚本:
这几条命令我们在讨论 initramfs 时已经见过了。事实上,这里启动 udev 服务,不仅是为了加载网卡驱动模块。initramfs 中往往只包含存储介质相关的驱动,而其他大量设备的驱动,大部分还是保存在根文件系统中,所以,在挂载了根文件系统后,需要重新模拟一遍热插拔,从根文件系统中加载相关设备的驱动模块。
在用户空间中,我们使用工具 ip 来配置网络,工具 ip 包含在软件包 iproute2 中。所以我们首先来编译安装软件包 iproute2 。
既然网络已经配置好了,一般情况下就不必再通过第三方系统(即虚拟机)更新 vita 系统了,可以直接通过网络和 vita 系统打交道了。当然如果是更新内核、initramfs 或者整个文件系统,还是要通过虚拟机系统的。我们在宿主系统和 vita 系统之间使用 ssh 服务进行通信。因此在这一节,我们为 vita 系统安装并配置 ssh 服务。
我们使用 ssh 协议的开源实现 openssh ,其依赖 zlib 和 openssl ,因此首先编译安装这两个软件包。
使用如下命令编译安装 zlib:
使用如下命令编译安装 openssl:
为了方便后面调试,我们在 vita 系统上安装 procps 。该软件包中包含了常用的一些工具,如 ps、kill 等。因为 vita 系统中没有安装 ncurses 库,为简单起见,我们只编译不需要使用 ncurses 库绘制界面的程序,这就是编译 procps 时传递参数 “–without-ncurses” 的目的。
UNIX 系统的主要目标就是多用户、多任务,而且允许多个用户远程登录并发执行任务。这种设计哲学同样被带到了 X 窗口系统中。X 的实现者将 X 设计为客户/服务器的架构,应用程序相当于客户端,它们不需要关心具体的显示和用户输入,而由 X 服务器负责管理显示设备和输入设备。应用程序只需要将请求,比如 “绘制一条直线从点A到点B” ,发送给 X 服务器,而由 X 服务器负责将其绘制到具体的显示设备上。X 服务器也会将用户的输入(包括鼠标、键盘等输入事件),转发给对应的应用。
X 将协议相关实现封装到了一个库中,开发者将这个库称为 Xlib 。后来因为效率问题,又开发了 xcb 来替代 Xlib 。Xlib 中封装的只是 X 的核心协议,X 使用扩展的方式扩充 X 协议,其他扩展协议可以在单独的库中实现。
作为类 UNIX 的图形系统的基础,X 的复杂是难以避免的。也恰恰是因为 X 的复杂,很多人提及 X 的安装就会谈虎色变。虽然 X 系统非常庞大,实际上它也是有章可循的。本节笔者就带领读者从头安装一个 X 窗口系统。鉴于 X 的安装过程比较烦琐和复杂,我们提供了一个安装脚本 。但是笔者建议读者尽量使用手动的方式安装,这样可以在思考和解决问题中不断提高。遇到自己实在解决不了的问题时再参考这个脚本,从而达到更好的学习效果。
X 定义了一些公用的 M4 宏,并将它们放在软件包 util-macros 中。X 的各个组件的配置脚本中将使用 M4 宏,因此我们首先来安装 M4 宏,方法如下:
X 包含了多种协议和扩展,为简单起见,Vita 系统不必全部安装。比如禁掉了记录事件的扩展 Record,支持扩展屏幕的协议 Xinerama 及用于屏保的 Screensaver,禁掉了已经过时的 DRI1 等。下面是 vita 系统安装的协议,安装这些协议时没有先后顺序要求。如果不要求 X 服务器支持 DRI2,那么可以安装更少的协议,比如去掉 glproto、dri2proto、damageproto等。
(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 扩展定义了 OpenGL 和 X 之间通信的协议。该扩展的定义在软件包 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 中。
(16)复合扩展 复合扩展是为了 X 支持窗口特效设计的扩展。在没有这个扩展之前,所有的在窗口上的绘制操作都 “实时” 显示在屏幕上。而复合扩展允许窗口可以先在离屏的区域进行绘制。复合扩展的定义在软件包 compositeproto 中。
(17)资源扩展 资源扩展定义了应用程序查询 X 服务器各种资源使用情况的协议。该扩展的定义在软件包 resourceproto 中。
(18)直接图形访问扩展 顾名思义,直接图形访问扩展也是为了直接访问图形硬件设计的协议,不过其功能非常有限,目前基本已经停止开发,但 vesa 驱动还在使用这个扩展。该扩展的定义在软件 xf86dgaproto 中。这些协议的配置安装都非常简单,而且安装命令完全相同。以 xproto 为例,安装命令如下:
在安装 X 服务器前,我们需要安装 X 服务器依赖的库、这些库依赖的库以及 X 服务器使用的工具和相关数据。注意,某些库是有安装顺序要求的,比如,libX11 需要在 libxkbfile 前安装,安装 libXfont 前需要先安装 freetype,libdrm、expat 需要在 Mesa 前安装等。读者按照下面的顺序安装即可。
(1)pixman pixman 是一个底层的像素操作的库,提供图形合成及光栅化等功能,是 X 中软件渲染的基础。
(2)xtrans xtrans 封装了网络传输的基本功能,从开发角度讲,是 X 服务器和应用程序之间进行通信的基础。X 服务器、libX11 等 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 前需要安装 libfontenc 和 freetype 。
(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、flex 和 bison,使用如下命令在宿主系统上安装这几个软件包:
万事俱备,现在我们开始安装X服务器,配置及安装命令如下:
如果只是在虚拟机上运行目标系统,安装vesa驱动即可,安装命令如下:
但是如果是在真实的机器上运行目标系统,最好安装相应GPU的2D驱动。在安装脚本 中,包含了安装Intel GPU的2D驱动的方法,读者如果需要,可以参考。PC上使用的GPU一般都符合VESA标准,所以在通常情况下,用vesa也能勉强驱动,但是vesa驱动很多特性不支持,比如硬件加速。
看到输入设备驱动,读者可能会有个疑问:内核中不是包括了各种设备的驱动吗?怎么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 模块。
X 服务器将建立一个套接字与应用程序进行通信,通常这个套接字被命名为 “/tmp/.X11-unix/X0” ,0 表示是第一个 X 服务器,如果再启动第二个 X 服务器,则为 “/tmp/.X11-unix/X1” 。除了建立套接字外,X 服务器还将在 /tmp 目录下建立一个锁文件,例如对于第一个 X 服务器,这个锁文件为 “/tmp/.X0-lock” 。另外,在前面编译时,我们指定 X 服务器将日志文件存放在 /var/log 目录下,因此,我们需要在根文件系统中建立这两个目录:
最初,X 服务器启动后将创建并显示鼠标指针。后来,X 的开发人员认为只有在应用程序明确表明需要与用户进行交互时,才应该显示鼠标指针。所以,这个默认行为发生了改变,在 X 服务器启动后,不再默认创建并显示鼠标指针,而是在第一个应用明确调用类似 XDefineCursor 这样的函数请求X服务器显示鼠标后,才显示鼠标指针。
但是 X 还是为用户留了余地,增加了一个命令行参数 “-retro” 。如果用户运行 X 服务器启动时即创建和显示鼠标,那么给 X 服务器传递这个参数即可。
在默认情况下,当最后一个 X 应用断开与 X 服务器的连接后,X 服务器默认自动重置。同样,X 也为这个行为提供了修正的机会,用户可以使用命令行参数 “-noreset” 关闭这个特性。vita 系统不需要这个特性,因此我们传递了 “-noreset” 参数给 X 服务器。
最后,使用如下命令运行 X 服务器:
在 X 服务器启动成功后,将创建一个根窗口,作为未来所有用户窗口的根。默认情况下,这个根窗口只以一个简单的灰色背景显示。并且我们看到,X 也按照我们的要求,创建并显示了鼠标指针。
我们使用 Xlib 编写一个简单的 X 程序来确认 X 服务器是否已经正常工作。这个程序非常简单,就是创建一个窗口,并在其上显示字符串 “Hello X Window!”,代码如下:
编译这个程序的 Makefile 如下:
注意环境变量DISPLAY的设置,其格式如下:
如果主机名(hostname)为空,则表示 X 服务器运行在本机。读者可以把 display 理解为一个 X 服务器,screen 这里无须解释。displaynumber 和 screennumber 均从 0 开始计数,如值为 “:0.0” 表示运行在本机的第一个 X 服务器接的第一块屏幕。vita系统只启动了一个 X 服务器,并且只接一块屏。所以自然将环境变量 DISPLAY 设置为 “:0.0” 。
如果读者是在真实机器上调试的,那么为了使 GPU 的 2D 驱动和 3D 驱动都可以正常工作,内核中还需要进行相关的配置,因为用户空间的 GPU 驱动是通过内核中的 DRM 访问 GPU 的。GPU 用户空间的驱动(2D 和 3D 驱动)和内核空间的驱动(DRM 模块)之间的关系如图 6-22 所示。
前面,我们使用 Xlib 编写了一个小程序。但是我们也看到,Xlib 是多么的原始,使用 X 提供的库编写一个如此简单的程序是多么的复杂,更别提具有复杂图形用户界面的程序了。所以先辈开发者们前赴后继,尝试在 Xlib 的基础上为 X 开发更高级的图形库,这些图形库通常被称为 Widget Libraries 或 Toolkits ,其中最著名的就是 GTK 和 QT 。这些图形库引入了控件的概念,极大简化了程序开发,也提高了开发效率。
我们选择 GTK 作为 vita 系统的图形库。这一节,我们就来编译安装 GTK。相比于安装 X ,图形库的安装过程相对要简单,但是我们也提供了一个编译脚本 。必要时,读者可以参考这个脚本。
GLib 是 GTK+ 和 GNOME 工程的基础底层核心程序库,是一个实用的轻量级的库,它提供常用的数据结构、相关的处理函数和一些运行时支承机制,如事件循环、线程、对象系统等。因此安装 GTK+ 前首先需要安装 GLib 。GLib 目前也由开发 GTK+ 的团队维护。
因为 GLib 提供的对象系统(GObject)可以绑定到多种语言,常见的如 C、Python、Ruby 等,因此,GLib 的对象系统借助库 libffi 处理不同语言间的函数调用。libffi 是专门设计的一个库,主要用于不同语言间的相互调用。因此,安装 GLib 前还需要安装 libffi 。
libffi 和 GLib 的编译安装命令如下:
ATK(Accessibility ToolKit)是 GTK 中实现辅助功能使用的库,包括辅助视觉、听觉、打字等。这个库也是别无选择,必须要安装的,安装命令如下:
图形库当然离不开图片格式处理的库,常用的图片格式有多种,比如 PNG、JPEG 等。但是为了简单起见,vita 系统只支持 PNG 图片格式。处理 PNG 图形格式的库是 libpng ,安装命令如下:
GTK 使用 GdkPixbuf 进行图片的渲染,是 GTK 图形库的基本依赖之一,是必须安装的,安装命令如下:
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 时已经安装,接下来只需安装 Fontconfig 和 Pango 。由于 Cairo 依赖于 Fontconfig ,而 Pango 又基于 Cairo 进行字体渲染,所以,这里的安装顺序看上去有点奇怪。我们先安装 Fontconfig,中间插播 Cairo,然后才安装 Pango。
安装 Fontconfig 的命令如下:
Cairo 是一个矢量图形库,GTK 使用其作为绘制后端。换句话说,GTK 的绘制动作由 Cairo 完成。看到这里,读者可能会非常困惑:X 上的应用不是由 X 服务器负责绘制吗?没错,暂且不提我们第 8 章讨论的 DRI。事实上,即使普通的 2D 应用也是可以自己绘制的,只不过,应用是将内容绘制在一个离屏的区域,但是最后还是要请求 X 服务器将绘制的内容显示到屏幕上。应用或者将绘制的内容复制到 X 服务器,或者使用 X 提供的 RENDER 扩展。当然,应用也可以将全部绘制请求 X 服务器完成,这就要看具体图形库采用的策略了。
安装 Cairo 的命令如下:
安装Pango的命令如下:
图形库当然是要接收用户输入的,X 输入扩展协议的实现是库 libXi,安装命令如下:
GTK 的基本依赖已经安装完成,只差完成最后一步了,安装GTK的命令如下:
至此,图形库 GTK 的安装过程已经全部完成,读者可以将 /vita/sysroot 目录下的文件系统更新到vita的根文件系统了。
更新了vita系统的根文件系统后,在运行使用 GTK 编写的程序前,我们还要在vita系统上为图形库做一点收尾工作。注意下面两个操作需要在使用安装了 GTK 图形库的根文件系统重启vita系统后进行。
(2)为库 GdkPixbuf 创建模块信息文件 在安装库 GdkPixbuf 时,我们看到,GdkPixbuf 使用模块的形式支持各图形格式。因此,在这个库初始化时,需要加载这些模块。但是这些模块存储在文件系统的什么位置,每个模块又支持什么图形格式等,诸如此类信息从哪里获取呢?为了提高加载速度,GdkPixbuf 没有去再次扫描每个模块,而是直接从系统的一个文件中读取,因此,我们需要为 GdkPixbuf 创建这个文件。
最后,我们使用一个简单的程序来测试我们的 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,就会发现字符不再是一个一个的 “方框” 了。
一、实验内容实验名称:Proc文件系统实验任务:学习和掌握proc文件系统的功能、工作原理及其应用实验目的:学习Linux内核、进程、存储和其他资源的一些重要特征。读/proc/stat文件,计算并显示系统CPU占用率和用户态CPU占用率。(编写一个程序使用/proc机制获得以及修改机器的各种资源参数。需要使用fopen(),fscanf(),printf())二、实验过程记录(一)实验过程1
第一节 文件管理的基本概念对信息存储的基本要求:能够存储大量的信息长期保存信息可以共享信息文件是通过OS来管理的,管理的内容包括文件的组织结构、命名、存取、使用、保护和实现方法。OS为系统管理者和用户提供了对文件的透明存取,即不必了解文件存放的物理机制和查找方法,只需要给定一个代表某段程序或数据的文件名称,文件系统就会自动地完成对该文件(与文件名称对应的文件)的指定操作。一、文件管理任务从研究角度
这个系列主要是对历年的考试题目中容易模糊的点进行汇总,其中很多内容也附带的了解析。这个系列的所有内容应该是全网最详细的内容了,希望可以帮助大家考试顺利。2024-042023-102023-042022-102022-042021-102021-042020-102020-082019-102019-04求三连!!感谢~~
内核的构建系统kbuild基于GNU Make,是一套非常复杂的系统。对于编译内核而言,一条make命令就足够
一般而言,桌面、服务器等通用系统都使用initramfs。部分嵌入式系统中,也会使用 initramfs,甚至有的使用 initramfs 作为最终的根
第17章 构建Linux根文件系统本章目标l 了解Linux的文件系统层次标准(FHS)l 了解根文件系统下各目录的作用l 掌握构建根
计算机领域中的桌面环境()其实是一种比喻的说法,即图形用户界面就像物理书桌一样,其上可以放置文件夹、文档等。桌
根文件系统(“/”)是存放运行、维护系统所必须的各种工具软件、库文件、脚本、配置文件和其他
《深度探索Linux系统:系统构建和原理解析》笔记——2.工具链构建 深度探索Linux操作系统 —— 编译过程分析深度探索Linux操作系统 —— 构建工具链 编译过程分为4个阶段,分别是:编译预处理、编译、汇编以及链接。每个阶段都涉及了若干工具,GNU将这些工具分别包含在3个软件包中:Binutils、GCC、Glibc。Binutils:GNU
Linux操作系统中的根文件系统是一个非常重要的概念,它包含了操作系统中的所有必要文件和目录,是系统启动时加载的第一个文件系统。而在Linux系统中,文件系统是一个非常核心的概念,它负责管理文件和目录的存储和访问,是Linux系统中最重要的组成部分之一。在Linux系统中,有多种不同的文件系统可供选择,其中最常用的是ext文件系统。除了ext文件系统之外,还有许多其他类型的文件系统,如xfs、
Linux 文件系统是红帽操作系统中的重要组成部分,而根文件系统则是其中的核心。在本文中,我们将探讨 Linux 文件系统以及根文件系统的概念和作用。首先,让我们了解一下 Linux 文件系统。简而言之,文件系统是一种用于组织和存储计算机上的文件和目录的方法。它通过在硬盘驱动器上分配空间来存储文件,然后使用文件系统管理数据的访问和组织。在 Linux 中,支持几种不同的文件系统类型,包括 ex
嵌入式 Linux根文件系统移植(三)——根文件系统构建一、busybox简介 BusyBox 是一个集成了一百多个最常用linux命令和工具的开源软件,是嵌入式系统开发中创建根文件系统的工具。BusyBox 包含了一些简单的工具,例如ls、cat和echo等等,还包含了一些更大、更复杂的工具,例grep
写在前面:为了帮助大家快速掌握Linux中的知识点,特别整理了一套知识笔记,搭配思维导图帮助大家理解记忆,作为福利免费分享出来!技术日新月异,不学习就是退步。基础篇Linux版本:一、文件系统安全文件系统简介在Linux操作系统中,文件系统采用梳妆的层次的目录结构,最顶层是根目录,用“/”表示,往下延伸其各级子目录目录说明/rootroot 文件系统是文件系统的顶级目录。它必
Linux操作系统文件系统一 、Linux文件结构 文件结构是文件存放在磁盘等存贮设备上的组织方法。主要体现在对文件和目录的组织上。 目录提供了管理文件的一个方便而有效的途径。 Linux使用标准的目录结构,在安装的时候,安装程序就已经为用户创建了文件系统和完整而固定的目录组成形式,并指定了每个目录的作用和其中的文件类型。 &n
Linux根文件系统(Root File System)是Linux操作系统中的一个重要组成部分。它是Linux系统启动过程中的第一个文件系统,也是其他文件系统的基础。在Linux操作系统中,根文件系统承载着操作系统核心文件、设备节点、配置文件以及其他必需文件等。本文将探讨Linux根文件系统的重要性和其所起的关键作用。根文件系统的重要性在于它是Linux系统启动的起点。在启动过程中,计算机首
如a/b/f,c/d/f,f文件同时存在两个目录下,f文件有多个路径名,两个目录项都指向f的inode节点。目录项可以表示一个目录或文件
文章目录文件系统的功能规划文件系统的基本组成一切皆文件目录项和目录是一个东西吗?那文件数据是如何存储在磁盘的呢?虚拟文件系统文件的物理结构文件块文件分配方式连续分配非连续空间存放方式链式分配隐式链接显式链接索引分配空闲空间的管理空闲表法空闲链
目录文章目录目录Linux 文件系统目录结构文件系统类型虚拟文件系统文件类型Linux 文件系统文件系统是文件存放在磁盘等存储设备上的组织方法。Linux 系统中每个分区都是一个文件系统,都有自己的目录层次结构。Linux 会将这些分属不同分区的、单独的文件系统按一定的方式形成一个系统的总的目录层次结构。一个操作系统的运行离不开对文件的操作,因此必然要拥有并维护自己的文件系统。目录结构和...
://blog..net/yaozhenguo2006/article/details/6761471构建根文件系统一. 根文件系统知识根文件系统是嵌入式linux系统三个重要组成部分之中的其中一个,其他两个是bootloader, kernel。在系统 中扮演者重要的角色。它是li...
目录 文章目录目录文件文件的构造文件的类型常规文件ASCII 码文件二进制文件设备文件字符设备文件块设备文件目录文件其他文件文件的属性文件的操作目录的操作 文件文件的构造文件的构造有多种方式。上图列出了...
不是题解不是教学!!! 11.10 CSES 1192 给定一个 \(01\) 网格图,求 \(0\) 的连通块个数。 遍历整个图,当遇到 \(0\) 时搜索并把联通的全部变为 \(1\),连通块数量加一。 CSES 1193 给定一个 \(01\) 网格图,问是否可以仅经过 \(0\) 从 \(A ...
电动车无钥匙解锁 在快节奏的都市生活中,电动车已成为许多人的首选出行工具。然而,传统的机械钥匙往往带来诸多不便:容易丢失、操作繁琐,甚至在雨天或匆忙时成为负担。 随着物联网和智能科技的快速发展,一键解锁方案应运而生。其中,手机APP蓝牙解锁和NFC刷卡解锁作为两大主流方式,不仅实现了无钥匙操作,还提 ...
控制器:1,deployment 无状态部署2,StatefulSet 有状态部署3,DaemonSet 守护进程部署4,Job 批处理5,CronJob 批处理Deployment:部署无状态应用管理Pod和ReplicaSet具有上线部署、副本设定、滚动升级、回滚等功能提供声明式更新,例如只更新一个新的Image应用场景:Web服务Sa
新一代大语言模型(如GPT-5)的架构变革导致传统提示技巧失效,核心机制包括“隐形路由器”的智能分发和“手术级精度”的指令执行。开发者需调整策略:通过结构化提示(如XML标签)明确任务复杂度以激活强模型,设计多步骤指令,并利用“完美循环”驱动模型自我迭代优化输出。这一转变要求从模糊对话转向精确指令设计,以释放新一代模型的潜力。
随着科技的不断发展进步,一些新的科技技术产品从众多产品里面脱颖而出,其中3D虚拟场景完美的参入各个行业中,增加了我们生活的体验感,和便捷性,感受科技的力量,其中我们可以通过vr虚拟现实制作软件进行设计虚拟场景,那么,vr虚拟现实制作软件可以用来制作什么呢?接下来小编就为大家详细的介绍,关于vr虚拟现实制作软件的用途。 vr虚拟现实制作软件可以用来制作什么没有接触过有关vr虚拟现实制作软件