当前位置: 首页 > Linux

干货分享-Systemd技术原理&实践(上)

时间:2023-04-07 03:30:04 Linux

优麒麟的程序员小弟在研究如何优化系统资源模块的时候,查阅了很多资料,发现没有一个能详细说明systemd的优点和原理的。中文介绍文章很清楚,所以自己下载了systemd的源码,根据资料整理了一篇关于systemd的详细文章,希望对研究systemd的Uker有所帮助。Systemd简介1systemd的由来关于systemd的由来,首先要从Linux的init程序说起。Linux系统启动过程中,内核初始化后,内核启动的第一个程序是init程序,路径为/sbin/init(软连接,链接到真正的init进程),其PID为1、它是系统中所有进程的“祖先”。Linux中的所有进程都是由init进程直接或间接创建和运行的。init进程作为守护进程存在,负责组织和运行系统。定义的操作模式,例如命令行模式或GUI模式。init程序的发展大致可以分为三个阶段:sysvinit->upstart->systemd。根据init进程的开发特点,可以简单理解为:sysvinit:init系统通过shell脚本串行启动系统服务,下一个进程必须等待上一个进程启动后才能启动,所以进程系统启动比较慢。upstart:在sysvinit的基础上,并行启动一些不相关的程序,提高启动速度,但有依赖的程序仍然串行启动。systemd:通过socket激活机制,所有程序无论是否有依赖都并行启动,根据系统启动的需要只启动相应的服务,最大限度地提高启动速度。目前,优麒麟操作系统采用systemd。systemd是systemdaemon的意思,意思是系统守护进程。它是由LennartPoettering开发的。它采用了更好的服务框架,并兼容旧的sysvinit。它的设计目的是为了克服sysvinit和upstart的不足,进一步提高启动速度。在目前的主流系统中,systemd的守护进程主要分为系统态(system)和用户态(user)。ps-ef可以看到systemd的daemon进程,如下:PID为1/sbin/init的进程是系统态的systemd,是软链接,指向真正的systemd路径,一般是在优麒麟操作系统中放在/lib/systemd/目录下:systemd是进程服务集合的总称,它包含了很多进程,负责控制,管理系统资源,包括systemd-login,负责创建、修改、删除用户登录相关信息;systemd-sleep控制系统休眠、睡眠状态切换等,在优麒麟操作系统下,它们主要集中在/lib/systemd/文件目录下:各个进程的主要用途可以阅读freedesktopsystemd手册:https://www.freedesktop.org/s...目前systemd占据了init程序的主导地位,大有一统天下之势。2systemd的主要功能systemd采用并行启动方式,提供按需启动方式:systemd在设计之初主要关注两点:少启动和多并行。少启动是指在开机阶段,systemd只启动一些系统启动必须的服务,其他更多的服务推迟到真正需要的时候,比如优麒麟的蓝牙和截图相关的服务,系统不会使用它开机时;优麒麟的U盘启动器相关服务可以等U盘连接后启动;如果系统没有连接网络,那么那些需要使用网络的相关服务也可以使用,不需要启动它,直到网络连接后的第一次连接,然后再启动它。更多的并行性意味着服务的启动不需要像sysvinit那样序列化,而是同时运行所有需要的服务,从而最大限度地利用系统cpu资源,因此总的启动时间最小化,systemd后面会介绍。如何实现所有服务的并行启动。使用cgroup跟踪管理进程的生命周期:cgroup是一个控制组,它是一个层次结构,类似于文件管理系统的结构。当一个进程创建子进程时,子进程会继承父进程的cgroup,就像子进程是在父进程的目录下创建的一样。当子进程创建另一个子进程时,子进程会继承前一个子进程的cgroup,相当于继承了父进程的cgroup。比如子进程是在上一个子进程的目录下创建的,这个子进程也是在父进程的目录下。通过这种机制,父进程可以与所有子进程关联起来并跟踪,当停止一个父进程时,可以通过查询cgroup找到所有关联的子进程,从而保证所有相关服务的干净停止。启动挂载和自动挂载:在系统启动过程中,systemd会在初始化时自行进行一些挂载,比如/sys目录和/run目录挂载,这些都是系统启动时至关重要的文件系统。systemd还可以实现动态挂载点的自动挂载,比如不需要经常使用的CD、U盘的挂载。只有当这些设备连接上时,systemd才会启动相应的服务,并临时挂载它们供访问其中,当这些设备拔掉时,这些挂载点会被取消,以节省资源。事务依赖管理:系统中有很多服务存在依赖关系。比如麒麟软件商店需要等待网络服务启动,lightdm与systemd的交互需要等待D-Bus启动,大部分服务也需要等待syslog完全启动并初始化..systemd使用Unit(配置单元)来管理这些服务的依赖关系,维护一个事务的一致性,保证所有相关服务不会相互依赖而造成死锁。后面会详细介绍Unit。日志:systemd自带journalctl命令可以查看系统保存的所有日志信息,并且可以支持一些参数对日志进行过滤,方便用户分析日志。其他:经过几代的更新,systemd实现了很多功能。甚至有人认为systemd管的太多了,导致有些服务不再需要了。例如,systemd添加了许多systemctl命令来管理系统电源;systemd还加入了watchdog机制,其他守护进程需要周期性的pingsystemd进程,否则会被视为失败并重启等。具体可以看设计者的博客http://0pointer.de/博客/项目。3systemd如何实现服务并行systemd的设计理念是并行启动所有服务,最大限度地利用硬件资源,提高启动时间。但是我们知道服务之间是有依赖关系的,客户端需要等待服务端启动后才能建立连接。比如前面提到的优麒麟操作系统中,所有的服务都需要等待syslog服务启动。系统如何工作?摆脱这个同步和序列化过程怎么样?systemd的设计者认为,对于传统的daemon来说,他们实际上等待另一个daemon提供的是socket的准备。他们需要的是一个文件系统套接字套接字描述符,这是他们唯一等待的东西。那么是否可以在不等待整个守护进程完全启动的情况下为连接更早地创建这些套接字描述符呢?答案是肯定的。在C语言中,当一个进程启动另一个进程时,一般会执行系统调用exec()。在调用exec()启动服务之前,systemd首先创建并激活与服务关联的监听套接字,然后执行()在服务启动时将套接字传递给它,所以在服务启动时套接字已经可用还在开始。这样,systemd就可以在第一步为所有服务创建套接字,然后在第二步一次性运行所有服务。如果一个服务需要依赖另一个服务,由于socket已经准备好,服务可以直接连接继续执行启动。如果遇到需要同步的请求必须等待阻塞,那么只会阻塞一个服务,而且只是一个服务的请求,不会影响其他服务。启动,这样服务之间就不需要启动序列化了。Linux内核提供了socketbuffer功能,帮助systemd实现最大的并行化。以syslog服务为例,优麒麟操作系统上的大部分服务在启动之初都会先进行日志相关的初始化配置。如果同时启动syslog服务和各种syslog客户端服务,由于syslog相关的socket在systemd执行exec()启动syslog之前已经创建并准备好,客户端可以直接连接syslogsocket,如果遇到syslogstart如果客户端向syslog发送请求消息,而syslog无法处理,则请求消息会通过内核socketbuffer的机制传递到syslogsocket的buffer中。只要缓冲区没有满,客户端不需要等待,继续执行;一旦syslog服务完全启动,它将使所有消息出列并处理它们;当另一种情况发生时,缓冲区已满,或者需要同步消息请求,虽然此时客户端要阻塞等待,但是只有一个客户端的一个请求被阻塞,直到服务器赶上并处理它。所以systemd先激活socket,再开始创建服务,这样可以并行启动所有的服务,依赖的管理就变得多余了,至少是次要的,因为从服务的角度来说,只要socket如果激活的话,其他服务是否启动没有任何区别。这种方法也使得程序更加健壮,因为无论服务可用还是不可用,甚至崩溃,套接字仍然可用,不会让客户端注意到连接丢失。4Systemd执行单元--单元介绍单元是systemd管理服务和资源的基本单元,可以简单理解为systemd启动后每次需要执行的服务,每次需要处理的资源是抽象成一个配置单元Unit,保存在一个Unit文件里面。例如,当用户登录优麒麟操作系统时,systemd会执行Unit文件systemd-login.service来启动登录服务,跟踪用户的session、进程、idle状态,并分配一个sliceunit给用户;当用户执行睡眠操作时,systemd会执行systemd-suspend.service文件的Unit启动systemd-sleep服务来执行系统睡眠操作。单元文件根据后缀可分为12种不同类型。Systemd内部为这12种类型的Unit定义了不同的全局模板。因此,systemd的执行过程是:首先找到对应的Unit文件,然后根据Unit文件的类型匹配对应的全局模板,然后根据这个模板解析Unit文件,最后执行Unit中的操作文件。接下来简单介绍一下这12种Unit文件类型:(1)service:这是最明显的unit类型,代表一个后台守护进程,可以启动、停止、重启、重新加载守护进程。它是最常用的单元文档类型。(2)socket:这个单元封装了一个套接字在文件系统或互联网上。目前systemd支持流、数据报和序列数据包类型的AF_INET、AF_INET6、AF_UNIX套接字。还支持经典FIFO作为传输。每个套接字单元都有一个匹配的服务单元,当第一个连接进入套接字时启动相应的服务,例如:nscd.socket在传入连接上启动nscd.service。(3)device:这个单元封装了Linux设备树中的一个设备。如果通过udev规则为此标记了设备,它将作为设备单元在systemd中公开。使用udev设置的属性可以用作配置源来设置设备单元依赖性。(4)mount:这个单元在文件系统层次结构中封装了一个挂载点。systemd监控所有挂载点,也可用于挂载或卸载挂载点。systemd会将/etc/fstab中的条目转换为挂载点并在启动时处理它们。(5)automount:该单元类型在文件系统层次结构中封装了一个自动挂载点。每个自动挂载单元都有一个匹配的挂载单元。当访问自动挂载点时,systemd将执行挂载点中定义的挂载行为。(6)target:该单元类型用于单元的逻辑分组:它自己不做任何事情,它只是引用其他单元,以便将它们一起控制。比如想让系统进入图形模式,就需要运行很多服务和配置命令。这些操作由配置单元一一表示。将所有这些配置单元组合成一个目标意味着您需要配置所有这些配置单元。执行一次进入目标所代表的系统运行状态。(7)snapshot:与目标单元类似,快照本身实际上不做任何事情,它们的唯一目的是引用其他单元。快照可用于保存或回滚init系统所有服务和单元的状态。例如,允许用户暂时进入特定状态,如“紧急shell”,终止当前服务,并提供返回到先前状态的简便方法。(8)swap:与mount配置单元类似,swap配置单元用于管理swap分区。用户可以使用交换配置单元在系统中定义交换分区,可以在开机时激活。(9)定时器:定时器配置单元,用于定时触发用户自定义的服务操作。它是一种基于计时器的服务激活。这种类型的配置单元取代了传统的定时服务,如atd和crond。(10)path:该类配置单元用于监听指定目录或文件的变化,并根据变化触发其他配置单元服务的运行。(11)scope:该单元主要表示从systemd外部创建的进程。(12)slice:该单元主要用于封装管理一组进程资源占用的控制组的slice单元,即主要用于cgroup,通过在cgroup中创建节点实现资源控制,一般包括范围和服务单位。下面通过蓝牙服务bluetooth.service介绍Unit文件的结构。Unit文件主要分为以下三大部分:Unit部分:这部分是所有Unit文件通用的,用于定义Unit的元数据、配置以及与其他Unit的关系,Description描述了Unit文件的信息,而Documentation表示指定服务的文档,Condition表示服务启动的条件,有些Unit还包含wants、before、after、require字段,表示服务的依赖关系。Install段:该段是所有Unit文件通用的,通常指定运行目标的目标,以便系统启动时服务自动运行。Wantedby、RequiredBy类似于Unit部分的Wants字段,表示依赖关系,Alias字段表示启动runtime时使用的别名。servicesegment:Service类型的Unit文件特有的字段,用于定义service的具体管理和执行动作。包括Type字段,定义了进程的行为,比如使用fork()启动,使用dbus启动等;ExecStart、ExecStartPre、ExecStartPos、ExecReload、ExecStop分别表示启动当前服务执行的命令、启动当前服务前执行的命令、启动当前服务后启动的命令、启动当前服务时执行的命令重启,当前服务停止时执行的命令。以上图为例,启动蓝牙服务的命令为/usr/lib/bluetooth/bluetoothd。更详细的相关字段说明请参考freedesktopman手册:https://www.freedesktop.org/s...https://www.freedesktop.org/software/systemd/man/systemd。service.html更好以KylinOS为例,Unit文件的主要存放路径如下:system:/etc/systemd/system/*/run/systemd/system/*/lib/systemd/system/*user:~/.config/systemd/user/*/etc/systemd/user/*$XDG_RUNTIME_DIR/systemd/user/*/run/systemd/user/*~/.local/share/systemd/user/*/usr/lib/systemd/user/*考虑长度问题,本期先介绍systemd的基础知识,下一期将带大家深入了解systemd在开机过程中的机制。