Alibaba Cloud Linux 2 LTS 快速启动优化实践

简介: ## 1. 概述 Alibaba Cloud Linux 2(原[Aliyun Linux 2](https://www.aliyun.com/product/alinux?spm=a2c4g.11186623.2.15.484034a3vucRFF),简称Alinux 2)是阿里云操作系统团队基于开源Linux内核4.19 LTS版本打造的一款针对云应用场景的下一代Linux OS发行,不仅提

1. 概述

Alibaba Cloud Linux 2(原Aliyun Linux 2,简称Alinux 2)是阿里云操作系统团队基于开源Linux内核4.19 LTS版本打造的一款针对云应用场景的下一代Linux OS发行,不仅提供Linux社区的最新增强功能,在提供云上最佳用户体验的同时,也针对阿里云基础设施做了深度的优化。今日Alinux 2 LTS 正式发布,是Alinux 2的一个重要里程碑。这标志着阿里云操作系统团队将为Alinux 2提供长期技术支持、稳定的更新、更好服务,为Alinux 2的用户提供更多保障。
Alinux 2 LTS 版本不仅增加了更多社区新功能的支持,对系统启动时间、运行时性能及稳定性都做了许多优化。更详细的更新优化可参考发布记录,推荐直接上手试用体验。
Alinux 2在快速启动优化上取得一些不错的效果,同时推出“Alinux 2 qboot快速启动版”镜像(公测中),内核部分启动性能提升40%:

1.png

这里分享一下Alinux 2 LTS在系统快速启动优化上的所做的一些实践。

2. Linux系统启动流程

首先定义Linux系统启动,这里我们定义为从系统上电(虚拟机开启),到用户能够登陆(ssh login)为系统启动。通用Linux系统启动大致分为三个阶段:引导阶段(phase#1),内核启动阶段(phase#2)及用户态启动阶段(phase#3):

2.png

其中,

  • 对物理机产品,开机后运行固件中的BIOS程序,完成基本硬件初始化及上电自检(POST),通过后跳转至系统磁盘引导扇区;
  • 对虚拟机(Qemu + KVM)产品,Qemu运行后模拟BIOS,加载系统镜像文件虚拟出系统盘,跳转至系统盘引导扇区;
    下面来看看各阶段大致的启动流程。

2.1 Boot Loader

Bootloader是位于系统引导扇区的一段独立的系统程序,用于系统启动初期的硬件初始化,系统分区识别,系统内核加载及跳转执行。目前应用比较广泛的bootloader是用于通用系统的grub2和嵌入式系统的uboot。Grub2是多重引导器(multiboot),提供交互界面,默认配置下有5s交互超时,启动耗时较长。

2.2 Kernel

Bootloader加载Linux内核(一般为压缩内核vmlinuz)到内存,并运行内核自解压缩程序,解压后跳转至start_kernel,开始内核初始化流程:

22.png

2.3 User Space

Linux内核完成一系列初始化动作之后,开始运行init程序,创建PID为1的用户态进程,将系统控制权从内核态跳转到用户态。init进程会继续进行用户态启动流程,开启各种必要的,或是预先配置的系统服务,最后启动登陆服务,完成整个系统的启动。

23.png

Initrd与Switch Root

init是用户态程序,存放在系统根文件系统(rootfs)里。内核需要先挂载rootfs,才能运行init程序。通用Linux发行需要支持多种磁盘设备,多种文件系统,因此内核必须能够识别不同的磁盘设备,不同的文件系统。这需要内核预加载多种可能的磁盘设备驱动以及多种文件系统相关用户态工具软件才能正确识别rootfs。而这些驱动及用户态工具一般都存放在rootfs中,形成一个循环依赖。
为解决这个问题,initrd应运而生,将挂载rootfs必要的驱动,用户态工具以及其他需要预加载的代码从rootfs总抽取出来,并依照rootfs的文件结构,打包成一个小的rootfs,做成一个内存盘(ram disk)。内核在挂载最终的rootfs之前,先从内存中挂载initrd,加载必要的驱动后,先运行initrd中的init程序,挂载最终的rootfs。然后执行switch root动作,切换至最终的rootfs。
Alinux 2系统采用systemd来管理用户空间启动流程,systemd就是init程序,initrd使用压缩格式的initramfs文件。因此在加载initrd之前,内核需要先解压缩initramfs。

Cloud Init

Cloud init是云环境中的虚拟实例初始化配置工具,实例启动阶段能从多种数据源读取相关数据并据此对虚拟机进行配置,如用户密码,主机名,网络,用户数据等等一些配置。

3. 启动耗时画像

优化系统启动时间,自然需要先对系统启动画像,了解启动时间分布情况,找出系统启动耗时热点。

3.1 启动时间测量

Linux系统有如下常见的启动时间测量统计方法:

  • systemd-analyze
    systemd自带的启动分析工具,能够给出总的启动时间消耗,已经用户态服务启动耗时统计。

311.png

  • dmesg
    dmesg输出内核启动日志,时间戳能够帮助分析内核初始化各阶段耗时情况。配合-d选项计算出日志间的时间差,方便快速定位内核启动过程中耗时热点。

312.png

  • initcall_debug
    内核启动参数,开启后会统计内核各初始化函数的耗时情况,相比dmesg -d更加精确。
  • printk/trace_printk
    要分析一些启动热点的细化耗时情况时,手动增加一些printk/trace_printk探针能够帮助获取时间统计信息。
  • ftrace
    必要时也可开启内核早期ftrace功能,帮助分析热点耗时。不过需要注意开启ftrace后可能会导致函数延时增加,因此不宜参考ftrace得出函数绝对耗时,可以参照trace结果帮助分析热点函数的耗时逻辑。

还有其他一些时间测试方法,以及图形化画像工具,这里不一一介绍。

3.2 启动耗时热点分析

对Alinux 2系统启动画像后,按耗时排序,得到如下耗时热点:
(这里以2C8G虚拟机为例,内核耗时1000ms,总体耗时5000ms)

热点 耗时(ms) 内核启动占比 总启动占比
mem init ~35 3.5% 0.7%
ORC unwind init ~90 9% 1.8%
buddy init 250 25% 5%
console enable ~60 6% 1.2 %
initramfs unpack 250 25% 5%
free initmem 270 27% 5.4%
mouse probe 650 65% 13%
systemd initrd 600 N/A 12%
mount rootfs 200 N/A 4%
cloud init 2740 N/A 54.8%

可见:

  • 总体启动耗时中,一半以上的时间消耗在用户态cloud-init进程上;
  • 内核启动阶段,鼠标探测耗时占比较高。

4. 快速启动优化

4.1 启动优化方法

常用的启动优化方法大致如下:

  • 瘦身

    • 移除不必要的代码,如模块,服务等,缩减启动初始化步骤;
    • 移除不必要的测试,调式及打印
    • 精简共享库
  • 异步、并行

    • 将耗时动作从关键路径移除,延后执行
    • 将顺序动作并行化执行
  • 原地执行(XIP)

    • 多用于嵌入式系统
  • 定制化

    • 将通用初始化程序定制化
  • 算法优化

    • 改进算法,加速初始化时间

4.2 去initrd

从前面的启动耗时热点分析结果可以看出,initrd解压缩及initrd systemd耗时占Alinux 2启动较大比率。
421.png

Alinux 2系统主要面向云环境虚拟实例,系统盘设备基本固定为virtio-blk设备,根文件系统格式基本固定为ext4文件系统,应该不需要通过initrd来加载rootfs,可以去掉initrd,直接挂载系统磁盘,即对内核启动瘦身。

理论上会优化掉initramfs unpack(270) + initrd systemd(560) ~ 800ms的启动耗时。去掉initrd测试结果如下:
422.png
可见initrd systemd时间确实优化掉了,但总的启动时间并没有理论优化收益。原因是内核启动耗时增加了约400ms。进一步分析发现,启动耗时热点之一的mouse probe(600ms),去initrd之前是与initrd systemd并行执行的。
423.png
去掉initrd后,这部分时间就直接计入内核启动时间了。抵去优化掉的initramfs unpacking的200ms,内核实际增加了400ms左右。

因此,要最大化去initrd的优化收益,必须同时解决mouse probe的耗时。

4.3 延迟probe

通用Linux系统需要支持多种IO设备,而鼠标键盘是比较常用的输入设备,特别是鼠标,产品繁多,接口多样。系统启动过程中加载鼠标驱动后,需要扫描多种IO总线来探测鼠标设备,这一过程非常耗时。
依据前面提到的优化方法,我们有两种方案:

  1. 对云环境定制鼠标驱动,固定探测virtio设备;
  2. 将鼠标探测从启动关键路径剥离,延迟探测,与后面系统启动服务并行;

第一种方案需要重构相关代码,成本较高;而且定制化限制较多,无法与开源社区协作。因此需要思考第二种方法:延迟探测。一种简单可行的方法是将原本内置(built-in)的设备驱动重新编译为内核模块(kernel module),因内核模块存放在根文件系统,所以加载时机被动推迟到根文件系统挂载之后,此时内核已经启动完成,自然与用户态初始化进程并行执行。
测试结果如下:(注意这是优化后的内核本地测,cloud-init被禁用)
带initrd启动:
431.png
不带initrd启动:
432.png
可见,内核启动时间缩减约200ms,优化掉initrd systemd时间;鼠标设备探测延后至userspace初始化阶段,导致userspace启动时间略有增加。获得预期的启动时间优化。

4.4 内存初始化优化

内存初始化也是内核启动热点之一,特别是在大规格实例上,内存初始化耗时占比较高。图中为750GB实例内存初始化耗时:
meminit耗时近2s
441.png
buddy init耗时1.8s
442.png

内存初始化动作是在内核启动的关键路径上,优化思路是并行初始化。因内存初始化时机较早,系统多CPU还未初始化完成,所以需要将内存初始化延后至CPU初始化完成之后,采用多线程并行执行内存初始化。这部分工作社区已经完成,通过内核配置CONFIG_DEFERRED_STRUCT_PAGE_INIT来开启。
开启后,内存初始化延后,按NUMA node并行执行:
前半部耗时约0.2s
443.png
后半部耗时约1.3s
444.png

4.5 free initmem修复

Alinux 2 内核启动优化前有一个概率性的启动热点,free initmem到buddy系统时,会大概率(超过70%)出现200ms以上延时,dmesg日志显示如下,耗时超过200ms:

[    0.687494] rtc_cmos 00:00: setting system clock to 2020-03-03 15:09:38 UTC (1583248178)
[    0.915315] Freeing unused kernel image memory: 1836K

经分析,发现是社区已知问题,并在新内核已经修复 于是backport回Alinux 2 LTS内核,修复后耗时约5ms,基本消除这部分延时:

[    0.482477] rtc_cmos 00:00: setting system clock to 2020-03-03 15:01:41 UTC (1583247701)
[    0.487438] Freeing unused kernel image memory: 1856K

4.6 ORC unwind初始化

内核中有一些静态表,需要在内核初始化阶段排序,有些表体积较大,初始化耗时占比也不容小觑。如ORC unwind表格初始化排序,耗时约90ms:

[    0.087330] clocksource: refined-jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1910969940391419 ns
[    0.179718] random: get_random_bytes called from start_kernel+0x8b/0x563 with crng_init=0

这些静态表格是在内核构建阶段生成,因此可以将排序动作从内核初始化阶段移除,放到内核构建阶段完成,以节省内核初始化时间。经调查发现社区已经有类似的优化方案,异常处理表(exception table)排序移植到了内核构建阶段完成。于是对异常处理表改进,增加了ORC unwind表格构建阶段排序优化,系列patch已经合入主线。
优化后基本削减了这部分耗时:

[    0.037253] clocksource: refined-jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 1910969940391419 ns
[    0.040714] random: get_random_bytes called from start_kernel+0x8b/0x563 with crng_init=0

4.7 Cloud-init优化

Alinux 2采用systemd启动用户态系统服务进程,以实现最大化并行启动。在到达启动完成的ssh登陆状态前,依赖一系列必要的系统服务,其中cloud-init是关键链路中一个耗时热点,启动画像中可以看出cloud-init几乎占整个系统启动时间的一半,因此优化cloud-init能够获得较大的启动性能收益。
在cloud-init服务中,一个耗时的配置任务是用户密码配置,需要从metadata服务器获取账号密码,完成配置。Alinux 2 LTS 内核开启了 Qemu firmware configuration 功能,能够通过qemu透传一些诸如账号密码的配置到虚拟机内部,使得cloud-init能够本地读取配置信息,加快cloud-init配置动作。感谢阿里云镜像团队跟阿里云虚拟化团队共同努力,即将推出InnerPasswd功能,加速Alinux 2 LTS 实例的cloud-init配置,提升实例启动时间,敬请期待!

4.8 其它优化

另外,启动阶段的console输出也是一个相对耗时的动作,因为串行口的波特率是固定的,大量是输出会形成阻塞导致console enabled延时较大。例如:
开启console output,console耗时2.6s!:
481.png
配置内核参数quiet,关闭console output:
482.png

5. 下一步工作

虽然Alinux 2 LTS在启动优化已经取得了不错的效果,启动性能得到进一步提升,但仍然还有进一步挖掘的空间。特别是内存初始化这块,仍然是大规格实例启动热点。即便已经开启的deferred page init特性,但内存初始仍然限于node间并行,而node内并行初始化值得进一步挖掘,特别对当前ECS实例大都为单node实例(NUMA关闭)的场景下,理论上有更大的收益。
据了解社区已经有贡献者在着手进行相关工作,值得期待。

目录
相关文章
|
2月前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
95 4
|
1月前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
1月前
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####
|
1月前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
2月前
|
缓存 并行计算 Linux
深入解析Linux操作系统的内核优化策略
本文旨在探讨Linux操作系统内核的优化策略,包括内核参数调整、内存管理、CPU调度以及文件系统性能提升等方面。通过对这些关键领域的分析,我们可以理解如何有效地提高Linux系统的性能和稳定性,从而为用户提供更加流畅和高效的计算体验。
54 2
|
2月前
|
缓存 网络协议 Linux
深入探索Linux操作系统的内核优化策略####
本文旨在探讨Linux操作系统内核的优化方法,通过分析当前主流的几种内核优化技术,结合具体案例,阐述如何有效提升系统性能与稳定性。文章首先概述了Linux内核的基本结构,随后详细解析了内核优化的必要性及常用手段,包括编译优化、内核参数调整、内存管理优化等,最后通过实例展示了这些优化技巧在实际场景中的应用效果,为读者提供了一套实用的Linux内核优化指南。 ####
61 1
|
2月前
|
缓存 资源调度 安全
深入探索Linux操作系统的心脏——内核配置与优化####
本文作为一篇技术性深度解析文章,旨在引领读者踏上一场揭秘Linux内核配置与优化的奇妙之旅。不同于传统的摘要概述,本文将以实战为导向,直接跳入核心内容,探讨如何通过精细调整内核参数来提升系统性能、增强安全性及实现资源高效利用。从基础概念到高级技巧,逐步揭示那些隐藏在命令行背后的强大功能,为系统管理员和高级用户打开一扇通往极致性能与定制化体验的大门。 --- ###
89 9
|
2月前
|
算法 Unix Linux
深入理解Linux内核调度器:原理与优化
本文探讨了Linux操作系统的心脏——内核调度器(Scheduler)的工作原理,以及如何通过参数调整和代码优化来提高系统性能。不同于常规摘要仅概述内容,本摘要旨在激发读者对Linux内核调度机制深层次运作的兴趣,并简要介绍文章将覆盖的关键话题,如调度算法、实时性增强及节能策略等。
|
2月前
|
缓存 算法 Linux
Linux内核中的调度策略优化分析####
本文深入探讨了Linux操作系统内核中调度策略的工作原理,分析了不同调度算法(如CFS、实时调度)在多核处理器环境下的性能表现,并提出了针对高并发场景下调度策略的优化建议。通过对比测试数据,展示了调度策略调整对于系统响应时间及吞吐量的影响,为系统管理员和开发者提供了性能调优的参考方向。 ####
|
2月前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
223 8