【转】deepin系统启动流程
<p>出自https://zhuanlan.zhihu.com/p/67118407</p><p>deepin系统整个的启动流程到底是怎么样子的?以前曾被同事缠问过类似的问题。遇到这种宏大而又不着边际的问题,我的回复往往是“你还太嫩,现在我告诉你还是会忘掉的,等你干上个两年,不用我说你就知道了”。我边敲着键盘,边佩服自己的聪明才智。</p>
<p>两年后……</p>
<p>这个小伙子长大了,并且坚定地又问了同样的问题。</p>
<p>我一愣神,脑海中不停浮现出一个声音“出来混,迟早是要还的”。想想也罢,是时候该把压箱底儿的货拿出来了,毕竟自封了“半吊子系统工程师”的title(虽然我自封的title还有很多:半吊子客服、半吊子产品经理、半吊子研发项目经理等等),不给你们露两手看看看来还真不行……</p>
<h2><strong>概览</strong></h2>
<p>deepin系统启动,从整体上看主要分为了硬件上电、内核引导、内核启动、系统初始化、图形界面等几个阶段。如果将这几个阶段分为两个部分,那么第一部分的硬件上电、内核引导、内核启动主要是“引导(boot)”,更偏向让内核可以启动;而第二部分的系统初始化、图形界面两个阶段主要的任务则是“初始化(initialize)”了,因为对于一个系统来说仅仅有内核跑起来是不行的,还要有各种各样的服务对系统的软硬件进行管理,这也是平常大家说发行版跟纯粹的GNU/Linux内核不是一个概念的原因之一。</p>
<p>下面我从一个软件开发者的角度说一下我对每个阶段的理解以及一些调试的方法。</p>
<h2><strong>硬件上电</strong></h2>
<p>既然说了是软件开发者的角度,这个部分对我来说基本上相当于黑盒子了。但是大体上我们仍然知道这个部分主要是:</p>
<ol>
<li>硬件上电</li>
<li><code>BIOS</code>/<code>UEFI</code></li>
<li><code>bootloader</code></li>
</ol>
<p>当你按下电源的那一刻起,电流就会“滋滋滋”的流向主板,启动<code>BIOS(Basic Input Output System)</code>系统。<code>BIOS</code>系统,顾名思义就是最直接跟硬件打交道的系统,因为有标准规定,所以输入输出设备的基本功能都是可以使用的,一些硬件的开关配置也可以在<code>BIOS</code>中进行操作。除此之外,<code>BIOS</code>还有两个重要的功能,一个是硬件自检;另外一个是加载引导。硬件自检这个跟作为一个”半吊子系统工程师“没什么关系,自不多说。加载引导的过程其实就是大家耳熟能详的<code>MBR</code>、小蝌蚪找妈…哦不…<code>MBR</code>中找<code>bootloader</code>了。</p>
<p>跟<code>BIOS</code>对应的<code>UEFI</code>,要说它们之间的区别,除了加载引导的方式不大一样以外。对我来说可能就是界面能够用鼠标点点点了吧,嗯……哈哈哈。</p>
<p>这里讲个段子,之前15.7搞启动时间优化的时候,测试的同学测试系统启动时间的优化情况,老是说效果不理想,我去看看吧,原来是他们测试系统启动是从硬件启动算起的,我说你们直接从内核引导开始计算,他还问我为什么。优化时间/(<code>BIOS</code>时间+<code>GRUB</code>时间+内核时间+图形时间) 跟 优化时间/(内核时间+图形时间)哪个大哪个小?我只能说这个测试同学的数学不大好……</p>
<h2><strong>内核引导</strong></h2>
<p><code>BIOS</code>在<code>MBR</code>中(或者<code>UEFI</code>在主板专有的存储设备中)找到<code>bootloader</code>并加载后,<code>bootloader</code>就会开始加载Linux内核并启动了。</p>
<h3><strong>GRUB引导</strong></h3>
<p>deepin系统默认的<code>bootloader</code>是<code>GRUB(GRand Unified Bootloader)</code>。其实我一直觉得这个名字挺恶心的,大神们果然都是重口味……<code>GRUB</code>并不需要按照什么规则去硬盘中找系统,而是根据<code>/boot/grub/grub.cfg</code>中的启动项加载内核、启动系统,而这个配置文件则是在系统安装或者手动执行<code>update-grub</code>这个命令的时候生成和更新。</p>
<p><code>update-grub</code>这个命令其实是对<code>grub-mkconfig</code>的一个包装,在非Debian系的发行版上是没有的。<code>grub-mkconfig</code>会执行的动作主要是:</p>
<ol>
<li>加载<code>/etc/default/grub</code>中的一些配置项。比如<code>GRUB_CMDLINE_LINUX_DEFAULT</code>配置项会控制Linux的boot param。</li>
<li>挨个执行<code>/etc/grub.d/</code>目录中的脚本,用来生成最终的<code>grub.cfg</code>文件。比如我们平常看到<code>update-grub</code>命令执行时输出的哪些启动项,其实就是<code>/etc/grub.d/03_os-prober</code>这个脚本里面执行<code>os-prober</code>这个工具产生的。</li>
</ol>
<p>在<code>GRUB</code>界面选择启动项,按<code>e</code>编辑启动项。除了普通的上下左右键移动光标,还可以使用基本的<code>Emacs</code>快捷键:</p>
<ul>
<li><code>Ctrl+N</code> 下一行</li>
<li><code>Ctrl+P</code> 上一行</li>
<li><code>Ctrl+B</code> 左移一个字符</li>
<li><code>Ctrl+F</code> 右移一个字符</li>
<li><code>Ctrl+A</code> 移动光标到行首</li>
<li><code>Ctrl+E</code> 移动光标到行尾</li>
</ul>
<p>编辑完成后按<code>Ctrl+X</code>按照编辑后的结果启动系统,但是编辑的结果不会保存,也就是说如果需要永久修改某个启动项,就要修改<code>grub.cfg</code>文件或者会影响<code>grub.cfg</code>生成的<code>/etc/default/grub</code>以及<code>/etc/grub.d/</code>中的脚本文件了。</p>
<p>对于<code>GRUB</code>,我们一般需要知道的就这么多,关于<code>GRUB</code>其他一些用法和知识,可以参考GRUB与系统引导这篇文章。</p>
<h3><strong>UEFI直接引导</strong></h3>
<p>在<code>UEFI</code>模式下,除了使用<code>GRUB</code>来引导内核以外,还可以通过<code>UEFI</code>直接引导内核(需要内核开启了<code>EFI Stub</code>支持),具体的配置方式见Debian Wiki EFI Stub。需要注意的一点是在使用<code>efibootmgr</code>创建启动项的时候,可能需要<code>-d</code>参数指定设备,否则可能会导致创建启动项失败。</p>
<h2><strong>内核启动</strong></h2>
<p>内核启动部分其实主要是想说<code>initrd</code>。</p>
<p><code>initrd</code>是一个小型的<code>rootfs</code>,这个<code>rootfs</code>保证了内核启动过程中所需要的内核模块和用户态工具。同时,它还需要为下一个阶段“系统初始化”做准备,也就是为init程序准备好真正的文件系统,并且启动<code>init</code>程序。</p>
<p>内核使用<code>initrd</code>启动的过程主要是:</p>
<ol>
<li>执行<code>init</code>脚本(这个不是上面说得<code>init</code>程序,而是生成好的<code>initramfs</code>中的<code>/init</code>这个文件。后面的步骤其实都发生在这个脚本的执行过程中);</li>
<li>解析内核启动参数,识别关键的如<code>debug</code>、<code>boot</code>、<code>break</code>等;</li>
<li>执行<code>/scripts/init-top/</code>中的脚本;</li>
<li>加载内核模块;</li>
<li>执行<code>/scripts/init-premount/</code>中的脚本;</li>
<li>执行<code>/scripts/$BOOT</code>脚本中的<code>mountroot</code>函数,其中的<code>$BOOT</code>参数就是第2步中识别到的<code>boot</code>参数指定的,自带的可选项有<code>local</code>(本地启动,默认)、<code>nfs</code>(比如PXE启动);</li>
<li>执行<code>/scripts/init-bottom/</code>中的脚本;</li>
<li>执行<code>init</code>程序。</li>
</ol>
<h3><strong>live系统</strong></h3>
<p>上面第6步提到<code>initramfs-tools</code>包自带有两种boot类型:<code>local</code>和<code>nfs</code>,我们使用live系统的时候的boot类型其实<code>live</code>。这个boot类型主要是<code>live-boot</code>这个包支撑起来的。</p>
<p>在启动live系统的时候,<code>/scripts/live</code>中的<code>mountroot</code>会调用<code>/lib/live/boot/</code>目录中的脚本设置根文件系统,包括挂在ISO和设置overlay等。</p>
<h3><strong>调试</strong></h3>
<p><code>initrd</code>启动阶段支持几个特殊的启动参数辅助调试:</p>
<ul>
<li><code>debug</code> 会开启initrd启动脚本中的调试模式;</li>
<li><code>break</code> 可以讲启动停止在某个阶段,例如<code>break=premount</code>就会在真正的根文件系统挂载前停掉启动流程,并给你一个<code>busybox</code>环境;可选<code>break</code>的阶段可以在<code>/scripts/init</code>脚本中看到,就是哪些使用<code>maybe_break</code>的行。</li>
</ul>
<h2><strong>系统初始化</strong></h2>
<p>从这个阶段开始,启动流程就算是进入我们真实的的系统中了,<code>init</code>程序会启动各种服务对系统进行配置,保证软硬件环境可以正常使用。</p>
<p>deepin系统下默认的<code>init</code>程序是<code>systemd</code>,这个庞然大物太过复杂,或许将来开个坑写一个系列才能说好……这里只简单说明一下跟系统启动流程相关的内容。一个是 <code>man bootup</code>中的一个图:</p>
<div class="highlight">
<div class="cnblogs_code">
<pre> local-fs-<span style="color: rgba(0, 0, 0, 1)">pre.target
</span>|<span style="color: rgba(0, 0, 0, 1)">
v
(various mounts and (various swap (various cryptsetup
fsck services...) devices...) devices...) (various low</span>-level (various low-<span style="color: rgba(0, 0, 0, 1)">level
</span>| | |<span style="color: rgba(0, 0, 0, 1)"> services: udevd, API VFS mounts:
v v v tmpfiles, random mqueue, configfs,
local</span>-<span style="color: rgba(0, 0, 0, 1)">fs.target swap.target cryptsetup.target seed, sysctl, ...) debugfs, ...)
</span>| | | | |<span style="color: rgba(0, 0, 0, 1)">
\__________________</span>|_________________ | ___________________|____________________/<span style="color: rgba(0, 0, 0, 1)">
\</span>|/<span style="color: rgba(0, 0, 0, 1)">
v
sysinit.target
</span>|<span style="color: rgba(0, 0, 0, 1)">
____________________________________</span>/|<span style="color: rgba(0, 0, 0, 1)">\________________________________________
</span>/ | | |<span style="color: rgba(0, 0, 0, 1)"> \
</span>| | | | |<span style="color: rgba(0, 0, 0, 1)">
v v </span>|<span style="color: rgba(0, 0, 0, 1)"> v v
(various (various </span>|<span style="color: rgba(0, 0, 0, 1)"> (various rescue.service
timers...) paths...) </span>| sockets...) |
| | | |<span style="color: rgba(0, 0, 0, 1)"> v
v v </span>|<span style="color: rgba(0, 0, 0, 1)"> v rescue.target
timers.target paths.target </span>|<span style="color: rgba(0, 0, 0, 1)"> sockets.target
</span>| | | |<span style="color: rgba(0, 0, 0, 1)">
v \_________________ </span>| ___________________/<span style="color: rgba(0, 0, 0, 1)">
\</span>|/<span style="color: rgba(0, 0, 0, 1)">
v
basic.target
</span>|<span style="color: rgba(0, 0, 0, 1)">
____________________________________</span>/|<span style="color: rgba(0, 0, 0, 1)"> emergency.service
</span>/ | | |
| | |<span style="color: rgba(0, 0, 0, 1)"> v
v v v emergency.target
display</span>-<span style="color: rgba(0, 0, 0, 1)"> (various system (various system
manager.service services services)
</span>| required <span style="color: rgba(0, 0, 255, 1)">for</span> |
|<span style="color: rgba(0, 0, 0, 1)"> graphical UIs) v
</span>| | multi-<span style="color: rgba(0, 0, 0, 1)">user.target
</span>| | |<span style="color: rgba(0, 0, 0, 1)">
\_________________ </span>| _________________/<span style="color: rgba(0, 0, 0, 1)">
\</span>|/<span style="color: rgba(0, 0, 0, 1)">
v
graphical.target</span></pre>
</div>
<p> </p>
<pre><code class="language-text"> </code></pre>
</div>
<p>基本涵盖了由<code>systemd</code>接管系统以后,<code>systemd</code>所管理的服务之间的一个依赖和基本先后顺序。</p>
<p>另外一个是<code>systemd-analyze</code>命令,通过<code>systemd-analyze plog > bootup.svg</code>可以更加直观的看到<code>systemd</code>各个服务在启动过程中启动的时间和所存在的时间。</p>
<h2><strong>图形界面</strong></h2>
<p>图形界面阶段是系统到达图形环境后的过程,这个部分主要包括:</p>
<ol>
<li><code>display-manager.service</code> 启动<code>lightdm</code>;</li>
<li><code>lightdm</code>启动<code>lightdm-deepin-greeter</code>;</li>
<li>用户输入密码后进入图形会话;</li>
<li><code>lightdm</code>执行<code>/usr/sbin/deepin-session</code>;</li>
<li><code>/usr/sbin/deepin-session</code>执行<code>/etc/X11/Xsession.d</code>中的脚本,并最终启动<code>/usr/bin/startdde</code>;</li>
<li><code>startdde</code>启动桌面环境的组件,系统启动完成。</li>
</ol>
<h2><strong>结尾</strong></h2>
<p>哦,对了,文章开头的故事纯属虚构,请勿对号入座 :P</p><br><br>
来源:https://www.cnblogs.com/libra13179/p/13935874.html
頁:
[1]