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

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

前言

   在第 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 的这些库,因此,我们也将这部分程序安装到根文件系统中。我们只安装运行时使用的动态库及对应的运行时符号链接,当然,系统中并不一定会用到全部这些库,但是简单起见,这里全部安装了:

26eb7faf48314297995a31ed1ce04a47.png



(2)建立相关目录

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


ef3a02743e22c2fc49c7e340ef90f4cb.png


(3)构建程序 /sbin/init

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

8a4681a4d1e768134ef93b73764489bd.png


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

334c05a288451c907c0df77bafe07046.png


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

e1831c55321e0511216a0ed734d847bf.png



   其中,“\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 检查文件系统后,然后再以读写方式重新挂载根文件系统。这里我们忽略文件系统检查这一过程,直接以读写模式重新挂载根文件系统。


dc522f73c9388853ab1206e05ad29434.png


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


三、配置内核支持网络

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


四、启动udev

   前面我们将网卡驱动编译为模块,为了自动加载网卡驱动,需要启动 udev,为此需要修改 init 脚本:

51fb22a5fe9667a5a61c4e4835af8765.png


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


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

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

79a5cf4f76ed4bddd0a588a75bf2f07c.png



iproute2编译安装


六、安装并配置ssh服务

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


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


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


4c73cd769367aa116393719bfd750b53.png


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


0b7ebce01f99cd7d1dcc6a1d86cc41ed.png


七、安装 procps

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

eaeee1fc8415c51647ad2d1665a3ecfe.png



八、安装X窗口系统

   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 的安装过程比较烦琐和复杂,我们提供了一个安装脚本 build-X11.sh 。但是笔者建议读者尽量使用手动的方式安装,这样可以在思考和解决问题中不断提高。遇到自己实在解决不了的问题时再参考这个脚本,从而达到更好的学习效果。


1、安装 M4 宏定义

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


bbd225e05e6a9808f98fae99af8ac188.png


2、安装X协议和扩展

   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 中。


(14)字体扩展

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


(15)视频扩展

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


(16)复合扩展

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


(17)资源扩展

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


(18)直接图形访问扩展

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


1e8d874b5d6ba8177ccbffbc74787661.png


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 前需要安装 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,使用如下命令在宿主系统上安装这几个软件包:

482882b4b42abe070135688036e13db5.png



4、安装X服务器

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


fde28d2ce1cb75f200c30196c1f51eb0.png


5、安装GPU的2D驱动

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

9383303702daabbb839bf36d51db74d2.png



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

深度探索Linux操作系统 —— 构建根文件系统2:https://developer.aliyun.com/article/1598091

相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
目录
相关文章
|
2天前
|
存储 安全 Linux
探索Linux操作系统的心脏:内核
在这篇文章中,我们将深入探讨Linux操作系统的核心—内核。通过简单易懂的语言和比喻,我们会发现内核是如何像心脏一样为系统提供动力,处理数据,并保持一切顺畅运行。从文件系统的管理到进程调度,再到设备驱动,我们将一探究竟,看看内核是怎样支撑起整个操作系统的大厦。无论你是计算机新手还是资深用户,这篇文章都将带你领略Linux内核的魅力,让你对这台复杂机器的内部运作有一个清晰的认识。
12 3
|
2天前
|
存储 数据挖掘 Linux
服务器数据恢复—Linux操作系统网站服务器数据恢复案例
服务器数据恢复环境: 一台linux操作系统服务器上跑了几十个网站,服务器上只有一块SATA硬盘。 服务器故障: 服务器突然宕机,尝试再次启动失败。将硬盘拆下检测,发现存在坏扇区
|
4天前
|
安全 Android开发 数据安全/隐私保护
构建未来:移动应用开发与操作系统的融合之道
在数字化浪潮中,移动应用和操作系统如同现代文明的双子星座。本文将探索它们之间的紧密联系,揭示如何通过技术创新和设计哲学的融合,共同塑造我们的数字生活。从用户体验到系统性能,我们将一窥这些看似简单却复杂的互动是如何影响我们日常生活的。
|
6天前
|
存储 Linux 索引
Linux 下最主流的文件系统格式——ext
【9月更文挑战第8天】硬盘被划分为若干相同大小的块(Block),默认大小为4K,便于灵活管理文件数据。文件数据分散存放于这些块中,提高了数据添加、删除和插入的便利性。
|
12天前
|
Java 开发工具 Android开发
移动应用开发之旅:探索移动操作系统与应用构建的奥秘
【8月更文挑战第33天】在数字时代的浪潮中,移动应用已成为我们日常生活的一部分。本文将带您深入理解移动操作系统的工作原理,并揭示如何在这个多姿多彩的平台上开发出引人入胜的应用。我们将从基础概念出发,逐步深入到高级编程技巧,最终通过一个实际的代码示例,展示如何将理论应用于实践。无论您是初学者还是有经验的开发者,这篇文章都将为您提供宝贵的见解和灵感。让我们一起踏上这场激动人心的移动应用开发之旅吧!
|
13天前
|
机器学习/深度学习 vr&ar Android开发
构建未来:移动应用开发与操作系统的革新之路
【9月更文挑战第1天】 在数字时代的浪潮中,移动应用和操作系统是推动技术前进的重要力量。本文将深入探讨移动应用开发的新趋势、移动操作系统的创新演进,以及它们如何共同塑造我们的未来生活。通过分析当前技术发展的现状和挑战,我们将一窥即将到来的技术变革,并理解这些变化对个人和社会的深远影响。
28 1
|
2天前
|
存储 缓存 Linux
Linux文件系统的功能规划
【9月更文挑战第12天】本文通过类比图书馆,形象地解释了文件系统的组织形式和管理方法。首先,文件系统需按块存储文件,并设有索引区方便查找。其次,热点文件应有缓存层提高效率,文件需分类存储以便管理。最后,Linux内核需记录文件使用情况,通过文件描述符区分不同文件,确保文件操作准确无误。
|
14天前
|
开发者 API 开发框架
Xamarin 在教育应用开发中的应用:从课程笔记到互动测验,全面解析使用Xamarin.Forms构建多功能教育平台的技术细节与实战示例
【8月更文挑战第31天】Xamarin 作为一款强大的跨平台移动开发框架,在教育应用开发中展现了巨大潜力。它允许开发者使用单一的 C# 代码库构建 iOS、Android 和 Windows 应用,确保不同设备上的一致体验。Xamarin 提供广泛的 API 支持,便于访问摄像头、GPS 等原生功能。本文通过一个简单的教育应用示例——课程笔记和测验功能,展示了 Xamarin 在实际开发中的应用过程。从定义用户界面到实现保存笔记和检查答案的逻辑,Xamarin 展现了其在教育应用开发中的高效性和灵活性。
24 0
|
14天前
|
人工智能 Java 物联网
构建未来:移动应用开发与操作系统的融合之旅
【8月更文挑战第31天】在数字时代的浪潮中,移动应用和操作系统成为我们日常生活和工作的基石。本文将深入探讨移动应用开发的核心概念、移动操作系统的架构,并通过一个实际的开发案例,展示如何将创新理念融入应用设计之中。我们将从基础出发,逐步深入到高级话题,旨在为读者提供一个全面而深刻的技术洞见。
|
14天前
|
网络协议 Linux Shell
探索Linux操作系统:从基础到高级编程
【8月更文挑战第31天】本文旨在为读者提供一条清晰的路径,从Linux操作系统的基础知识出发,逐步深入到高级编程技巧。我们将一起揭开Linux神秘的面纱,了解其内部工作原理,并通过实际代码示例加深理解。无论你是初学者还是有一定经验的开发者,这篇文章都将为你带来新的视角和技能提升。