图灵机启动顺序

电脑(现代计算机)的整个启动过程可以概括为:

  • 计算机通电。
  • CPU读取保存在主板上ROM芯片里的BIOS或UEFI程序(BootLoader)。
  • 该程序加载指定启动介质(包括从网络启动,但一般为本地硬盘),并从该介质启动操作系统。

为便于理解启动过程,有必要先了解一些相关的名词,如主板固件类型(第一节),硬盘分区方式(第二节),然后在前两节的基础上简单了解操作系统是如何被引导启动的,以及多系统引导的管理(该节尚未完成)。

目录

一、主板固件(BIOS与UEFI)

1.什么是BIOS

1.1 BIOS产生:

1.2 BIOS功能:

1.3 BIOS启动过程

2.什么是(U)EFI

2.1 产生:

2.2 (U)EFI启动过程

3.启用UEFI 的条件

二、硬盘分区结构(MBR与GPT)

1.什么是MBR:

1.1 含义

1.2 结构

1.3 关于MBR分区结构的小疑问

2.什么是GPT

2.1 含义

2.2 结构

2.3 关于GPT分区结构的小疑问

三、操作系统的引导过程

1.传统BIOS引导操作系统

2.UEFI启动操作系统

3.总结

四、多系统引导的管理(未完成)

五、linux init系统介绍

1、Sysvinit介绍

运行级别

Sysvinit执行顺序

Sysvinit优缺点

2、Upstart介绍

开发UpStart的缘由

UpStart的原理

3、Systemd介绍

Systemd的单元概念

Systemd的Target和运行级别

使用C/C++开发新的系统服务

Unit文件的编写

[Unit]参数

[install]参数

[service]参数

Systemd命令行工具的使用

Systemd命令和sysvinit命令的对照表

systemd电源管理命令


一、主板固件(BIOS与UEFI)

可以把UEFI 和 BIOS都理解成一种固件(即Firmware,固化在特定芯片里的程序,是硬件设备最底层的软件),其主要功能是为计算机提供最底层的、最直接的硬件设置和控制(当然包括启动操作系统)。而UEFI是传统BIOS升级替代品,功能更强,并兼容传统BIOS。

1.什么是BIOS

1.1 BIOS产生:

由于计算机启动是一个很矛盾的过程,即必须先运行程序,然后计算机才能启动,但是计算机不启动就无法运行程序!因此需要想法把一小段程序装进内存,然后计算机才能正常运行,这段程序称为BIOS(基本输入输出系统),保存在计算机主板上的一个芯片里(称为BIOS ROM),如下图。

 

BIOS芯片

通常BIOS会被认为是随着IBM PC(大名鼎鼎的商用电脑)的出现而产生的,实际上BIOS要早于IBM PC,早在1975年,BIOS的概念就在CP/M系统上出现。后来随着计算机硬件的发展,除了上面提到的主板BIOS(也叫系统BIOS)以外,其他硬件也有了BIOS程序(固件),如显卡BIOS,硬盘BIOS等,但我们一般提到的BIOS固件专门指主板ROM芯片里的BIOS程序(即系统BIOS)。

1.2 BIOS功能:

  • 检测硬件,又叫POST,就是看看你的硬件是否还正常的工作。
  • 初始化硬件,设置其基本状态,使得整个计算机达到所谓的"可用状态"(Ready State)。
  • 启动OS Loader加载操作系统。BIOS固定地加载存储器最前面扇区(LBA0)里的引导代码(即BootLoader),然后由BootLoader启动操作系统。
  • 在操作系统启动起来以后,一部分继续驻留内存,向操作系统以及其他软件提供基本的系统级的服务,如磁盘读写(INT13中断服务)等。
  • 修复硬件缺陷,如Intel CPU的SMM功能。

1.3 BIOS启动过程

  • 通电:CPU初始化,并执行BIOS程序。
  • 自检:BIOS程序启动开始POST自检(此阶段无法屏幕显示,只能通过主板扬声器报警),进行更完整的硬件(检查CPU/RAM/键盘/鼠标等)检测。
  • 其他初始化:初始化显卡和其他设备并显示到屏幕。
  • 加载MBR:BIOS提供中断服务,读取CMOS设置,读取第一个启动磁盘的MBR到内存让CPU执行(此后BIOS就撒手不管了),然后由MBR引导代码启动操作系统。

2.什么是(U)EFI

2.1 产生:

由于BIOS各种缺点(不支持2.2TB硬盘作为启动盘,安全与扩展性差等),英特尔公司从2000年开始,发明了可扩展固件接口(Extensible Firmware Interface),用以规范BIOS的开发。而支持EFI规范的BIOS也被称为EFI BIOS。之后为了推广EFI,业界多家著名公司共同成立了统一可扩展固件接口论坛(UEFI Forum),英特尔公司将EFI 1.1规范贡献给业界,用以制订新的国际标准UEFI规范。

 

UEFI芯片

UEFI是一种规范,不同厂商根据该规范对UEFI的实现并烧录到芯片里,该实现程序就称为UEFI固件(固件的英文为Firmware,可理解为固化在特定芯片里的软件程序,是硬件设备最底层的软件)。

2.2 (U)EFI启动过程

  • SEC(安全性)阶段:通电,内存未初始化,CPU只能使用Cache来验证CPU、芯片组和主机驱动。
  • PEI(EFI前初始化)阶段:初始化一小部分低地址内存空间,CPU开始使用此内存初始化CPU、芯片组 和主板,随后EFI驱动载入内存。
  • DXE(驱动执行环境)阶段:此阶段内存、CPU(指CPU插槽上的物理CPU,非CPU核心)、PCI、USB、 SATA和Shell都会被初始化。
  • BDS(开机设备选择)阶段:此阶段用户可选择从检测到启动设备中选择启动设备。
  • TSL(临时系统载入)阶段:此阶段将由启动设备上的系统接手正式进入操作系统, 若BDS阶段选择UEFI Shell则会进入UEFI的简单命令行界面,可在此界面做简单维护和诊断。

3.启用UEFI 的条件

  • 电脑主板支持UEFI(2010年后的主板基本都支持)。
  • 操作系统支持UEFI(64位Windows基本都支持)
  • 磁盘具有 GUID 分区表(GPT)。
  • 磁盘分区表中必须有FAT分区。而特殊标志的FAT分区(即ESP分区,也叫EFI系统分区),该分区要作为第一个磁盘分区(进入系统会自动隐藏)。
  • 可启动的efi文件,一般为\EFI\BOOT\BOOTx64.EFI (注:不同平台文件名可能不同)。

从上面的启用条件发现,虽然要求支持UEFI的固件必须支持读取GPT磁盘类型中的FAT(12/16/32)文件系统格式,并可读取该FAT中的文件和执行*.efi的可执行文件。但是UEFI 规范并没有提到固件不能识别其他文件系统类型(如苹果Mac的HFS+)并从中加载启动装载程序(苹果Mac在这方面有与众不同的个性)。

二、硬盘分区结构(MBR与GPT)

目前硬盘有MBR与GPT两种分区方式。

1.什么是MBR:

1.1 含义

MBR磁盘分区是一种使用广泛的分区结构,它也被称为DOS分区结构(什么?感觉它与微软有关?是的),但它并不仅仅应用于Windows系统平台,也应用于Linux,基于X86的UNIX等系统平台。最早在1983年在IBM PC DOS 2.0中提出(微软最早与IBM合作的操作系统,但更早期的Unix系统没有这个概念,更多需要阅读操作系统发展简史)。它是存在于驱动器开始部分的一个特殊的启动扇区,磁盘的第一个扇区。这个扇区包含了已安装的操作系统的BootLoader和驱动器的分区信息。

注:'MBR'与'MBR分区结构'的区别:前者(狭义)专指主引导记录(磁盘第一扇区),后者(广义)是整个硬盘的组成结构(包括主引导记录扇区与其他扇区)。

1.2 结构

广义的MBR包含整个扇区(引导程序、分区表及分隔标识,即下图红色部分),狭义的MBR仅指引导程序(bootloader),这里统一按广义MBR的定义进行讨论。

 

MBR分区结构

①主引导代码(BootLoader):占446字节,由操作系统或特殊工具写入。
②硬盘分区表DPT:占64字节,用16字节表示一个分区信息(其中),因此只能容纳4个分区。后来随着磁盘空间越来越大,又发明了扩展分区和逻辑分区(为区分把原来的普通分区改称为主分区了)。
③结束标志:占MBR扇区最后2个字节,一直为"55 AA"(也称幻数Magic Number),表示该磁盘可启动。

注:引导代码(也叫魔术代码、神奇代码)种类较多这里不展开讨论,在第三节'操作系统的启动'会再次提起bootloader。

1.3 关于MBR分区结构的小疑问

  • MBR:磁盘第一个扇区的别名。
  • PBR:分区里第一个扇区的别名。
  • 磁盘为什么要分区?与磁盘的发展有关(很久以前就一个分区或没有分区的概念),更方便使用和管理磁盘吧。
  • 为什么要定义MBR?启动操作系统。
  • 为什么要定义PBR?由于MBR空间有限无法完成引导操作系统的工作。
  • 引导代码为什么这么神奇?使用汇编语言编写,直接操作底层硬件的代码程序。
  • 为什么存在多种引导代码?底层硬件不同,操作系统不同。
  • BIOS怎么知道MBR的地址?有一个硬件中断INT13会告诉BIOS外设的MBR地址。
  • 如何知道我的硬盘/U盘/光盘上有没有安装操作系统?
  • 为什么一个分区只能安装一个操作系统?安装操作系统时会向分区引导记录PBR写入引导代码,不同的操作系统会相互覆盖。

2.什么是GPT

2.1 含义

GUID分区表简称GPT,使用GUID分区表的磁盘称为GPT磁盘,是源自EFI标准的一种较新的磁盘分区表结构的标准。相对于MBR更为先进,因为GPT分区表头可自定义分区数量(而MBR最大4个)并且支持2TB以上的磁盘空间,在Windows系统中微软限定GPT最大128个分区。GPT分区结构只能被支持UEFI的计算机识别(BIOS无法识别)。

2.2 结构

GPT分区结构

  • 保护MBR
    在GPT分区表的最开头,处于兼容性考虑仍然存储了一份传统的MBR(LBA0),这个MBR叫做保护性MBR(Protective MBR)。由分区表和结束标志组成,没有引导代码。而且分区表内只有一个分区表项(标识为EE),MBR工具无法识别和操作(从而起到保护作用),GPT工具则不使用它。
  • GPT头
    起始于磁盘的LBA1,通常也只占用这个单一扇区。其作用是定义分区表的位置和大小。GPT头还包含头和分区表的校验和,这样就可以及时发现错误。
  • 分区表
    分区表区域包含分区表项。这个区域由GPT头定义,一般占用磁盘LBA2~LBA33扇区。分区表中的每个分区项由起始地址、结束地址、类型值、名字、属性标志、GUID值组成。
  • 用户数据区
    就是用户使用的分区,也是用户进行数据存储的区域。分区区域的起始地址和结束地址由GPT头定义。
  • 分区表备份
    分区区域结束后就是分区表备份,其地址在GPT头备份扇区中有描述。分区表备份是对分区表32个扇区的完整备份。如果分区表被破坏,系统会自动读取分区表备份,也能够保证正常识别分区。
  • GPT头备份
    GPT头有一个备份,放在GPT磁盘的最后一个扇区,但这个GPT头备份并非完全GPT头备份,某些参数有些不一样。复制的时候根据实际情况更改一下即可。

注:扇区与LBA区别,在GPT分区中,每一个数据读写单元成为LBA(逻辑块地址),一个“逻辑块”相当于传统MBR分区中的一个“扇区”,之所以会有区别,是因为GPT除了要支持传统硬盘,还需要支持以NAND FLASH为材料的SSD硬盘(不像磁盘那样有磁片,而磁片又划分磁道和扇区来保存数据,因此闪存材料需要采用模拟扇区来保持统一性),这些硬盘的一个读写单元是2KB或4KB,所以GPT分区中干脆用LBA来表示一个基础读写块,当GPT分区用在传统硬盘上时,通常,LBA就等于扇区号,有些物理硬盘支持2KB或4KB对齐,此时LBA所表示的一个逻辑块就是2KB的空间。 为了方便,我们后面仍然将逻辑块称为扇区。

2.3 关于GPT分区结构的小疑问

  • MBR与GPT区别:支持的最大空间(2T),分区数(4主分区),安全性与扩展性。
  • 如何选择:GPT需要UEFI支持,如果电脑主板固件是UEFI类型肯定优先选择GPT分区结构。
  • 支持UEFI的操作系统:Linux(2000年开始)。Apple从 Mac OS X 10.4(代号Tiger)开始支持Intel版的EFI。微软的64位版Windows基本都支持UEFI。

  •  

  • Windows对UEFI的支持

三、操作系统的引导过程

简单的概括传统BIOS与UEFI主板引导操作系统过程如下:
BIOS:设备加电 -> BIOS初始化/自检 -> boot loader引导(扇区代码) -> OS操作系统。
UEFI:设备加电 -> UEFI平台 -> efi应用(硬盘文件) -> OS操作系统。

1.传统BIOS引导操作系统

理论上传统BIOS仅支持MBR硬盘启动(在操作系统支持的情况下,GPT类型的磁盘仅可做数据盘)。
具体启动过程:

 

MBR启动操作系统

  • 按下电源,主板通电,CPU重置。
  • 固化在主板ROM芯片里的BIOS程序就会被CPU加载到内存运行。
  • BIOS程序自检(POST)完毕以后加载COMS芯片里保存的参数。
  • 通过COMS的参数, BIOS程序加载指定(启动顺序)磁盘的第一个扇区(即主引导记录MBR)到内存里运行。
  • 不同的引导代码(BootLoader)引导操作系统流程略有不同,微软的BootLoader做法是加载分区表(DPT)中标记为活动标志的分区引导记录(PBR),PBR里的引导代码加载操作系统的引导文件到内存运行,例如 Windows 的 bootmgr。而Linux的BootLoader(如Gurb)则会分阶段加载,直接查找所有分区,加载第一个引导文件grldr(详情点击阅读)。
  • 当引导文件运行后,操作系统内核就被加载运行,完成从 BIOS 程序(准确的说是从MBR)中接手的引导流程。

传统的 BIOS比较低级,只读取指定磁盘最前面第一个扇区(MBR)里的代码,MBR里面的引导代码(BootLoader)如何启动操作系统BIOS是一无所知的。微软的BootLoader只是一条跳转语句,跳转到标记为活动分区的PBR(分区引导记录,类似于主引导记录,位于分区最前面的扇区)执行。由于BIOS并没有具体规定用户的第一个分区(我们平时说的C盘)从哪个位置(相对于MBR)开始,因此可以利用MBR后面的扇区空间保存更多的引导代码(实现更多的功能),比如GRUB4DOS占用16个扇区,因此改变BootLoader代码就可以实现多系统的引导,这样的软件也比较多,这里不一一列举了。也有人把MBR里的引导代码称为引导的第一阶段,其他扇区的引导代码称为第二阶段(实现更多功能,比如识别文件系统等)

2.UEFI启动操作系统

EFI基本启动流程(如下图):首先EFI初始化,EFI平台加载,设备驱动与EFI应用程序加载,boot manager启动,OS loader启动,OS启动。

 

UEFI启动流程

但由于UEFI规范只有要求标准,并没有具体的呈现方式,因此每家主板以及操作系统的引导过程也略有不同,这里以Windows系统为例。

  • 系统开机 - 上电自检。
  • UEFI 固件被加载,并由它初始化启动要用的硬件。
  • 固件读取其引导管理器以确定从何处(比如,从哪个硬盘及分区)加载哪个 UEFI 应用。
  • 固件按照引导管理器中的启动项目,加载UEFI 应用。即UEFI引导管理器加载FAT分区里的efi文件来启动操作系统。
  • 已启动的 UEFI 应用还可以启动其他应用(对应于 UEFI shell 或 rEFInd 之类的引导管理器的情况)或者启动内核及initramfs(对应于GRUB之类引导器的情况),这取决于 UEFI 应用的配置。

在 EFI 系统启动后,GUID 分区表(GPT)就会被识别,之后 EFI 系统读取全局NVRAM 变量,启动 Boot Loader 程序(根据启动顺序加载efi可执行文件),efi会加载操作系统内核。对于分区表格式为 MBR 分区表 的磁盘,EFI 系统会 先启动 CSM 兼容模式后按传统 BIOS 的步骤加载操作系统的内核。

 

UEFI启动操作系统(Windows)

在UEFI安装完操作系统后,Windows至少使用两个分区,一个叫做ESP分区(EFI SYSTEM PARTITION),用于存放启动文件,另一个则是BIOS下正常的系统分区,不同的是,BIOS下引导文件是winload.exe,UEFI下引导文件式winload.efi,两者都是pecoff格式的,但UEFI用的是各种固件接口,而BIOS使用的是中断。有时还会有一个MSR分区,不过这个分区并不重要,实验可以删除。
默认情况下,UEFI固件加载的启动文件路径是EFI\BOOT\bootx64.efi(bootia32.efi),而Windows会强制写入的启动项则会加载EFI\MICROSOFT\BOOT\bootmgfw.efi,虽然这两个文件其实是一模一样的文件,但这样在BDS阶段,固件就会自动引导Windows启动管理器,从而在多系统的情况下Windows能优先启动,Linux系统引导文件一般是grubx64.efi。

3.总结

UEFI之所以比BIOS强大,是因为UEFI本身已经相当于一个微型操作系统,其带来的便利之处在于:

  • 首先,UEFI已具备文件系统的支持,它能够直接读取FAT分区中的文件。
  • 其次,可开发出直接在UEFI下运行的应用程序,这类程序文件通常以efi结尾。既然UEFI可以直接识别FAT分区中的文件,又有可直接在其中运行的应用程序。那么完全可以将Windows安装程序做成efi类型应用程序,然后把它放到任意fat分区中直接运行即可,如此一来安装Windows操作系统这件过去看上去稍微有点复杂的事情突然就变非常简单了,就像在Windows下打开QQ一样简单。

而这些都是BIOS做不到的,因为BIOS下启动操作系统之前,必须从硬盘上指定扇区读取系统启动代码(包含在主引导记录中),然后从活动分区(只限微软)中引导启动操作系统。对扇区的操作远比不上对分区中文件的操作更直观更简单,所以在BIOS下引导安装Windows操作系统,我们不得不使用一些工具对设备进行配置以达到启动要求。而在UEFI下,这些统统都不需要,不再需要主引导记录,不再需要活动分区,不需要任何工具,只要复制安装文件到一个FAT32(主)分区/U盘中,然后从这个分区/U盘启动,安装Windows就是这么简单。

BIOS和UEFI的启动过程区别(本文已在前面用红色字体提过,总结如下):

  • BIOS把MBR(包含bootloader引导代码)读出来交给CPU执行,引导代码进一步(各家不同)去加载操作系统引导文件,引导文件启动操作系统内核。
  • UEFI是查找硬盘FAT分区里的\efi\boot\bootx64.efi(或bootia32.efi)文件,启动这个efi可执行程序(也叫UEFI应用),efi应用(各家不同)去加载操作系统引导文件,引导文件启动操作系统内核。据说苹果macOS较为特殊,引导与恢复等功能都集成在主板ROM里,EFI分区(即FAT分区)只用来升级系统。

四、多系统引导的管理(未完成)

五、linux init系统介绍

Linux操作系统的启动首先从BIOS开始,接下来进入bootloader,由bootloader载入内核,进行内核初始化。内核初始化的最后一步就是启动pid1init进程。init以守护进程方式存在,是系统的第一个进程,,是所有其他进程的祖先。

Init系统能够定义、管理和控制 init进程的行为。它负责组织和运行许多独立的或相关的始化工作(因此被称为init系统),从而让计算机系统进入某种用户预订的运行模式。

Linux初始化init系统包括:SysvinitUpstartSystemd,它们在Ubuntu系统下的演化如下:

  1. Ubuntu 6.10及以前版本使用Sysvinit。
  2. Ubuntu 14.10及以前版本使用Upstart,通过与Sysvinit并存。
  3. Ubuntu 15.04开始默认使用Systemd,不能与Sysvinit或Upstart并存

1、Sysvinit介绍

Sysvinit就是System V风格的init系统,顾名思义,它源于System V系列UNIX。

运行级别

Sysvinit用术语runlevel来定义"预订的运行模式",默认的运行模式定义在/etc/inittab文件的initdefault项。如果没有默认的运行模式,那么用户将进入系统控制台,手动决定进入何种运行模式。

Sysvinit中运行模式描述了系统各种预订的运行模式。通常会有8种运行模式,即运行模式0-6和S。其中0表示关机,1表示单用户模式,3为命令行模式,5为GUI模式,6表示重启,1和S等往往用于系统故障之后的排错和恢复。可以看出每一种运行模式所作的初始化工作是不一样的。

Sysvinit执行顺序

  • /etc/rc.d/rc.sysinit
  • /etc/rc.d/rc 和/etc/rc.d/rcX.d/ (X 代表运行级别 0-6)
  • /etc/rc.d/rc.local
  • X Display Manager(可选)

首先,运行rc.sysinit以便执行一些重要的系统初始化任务。

然后,Sysvinit开始运行/etc/rc.d/rc脚本。根据不同的runlevel,rc脚本将执行/etc/rc.d/rcX.d(X就是runlevel)目录下的所有启动脚本。当所有的初始化脚本执行完毕。该目录下有多个脚本,为了保证系统正常关闭,脚本是要按照顺序执行的在该目录下所有以K开头的脚本都将在关闭系统时调用,字母K之后的数字定义了它们的执行顺序。

然后,Sysvinit运行/etc/rc.d/rc.local脚本。rc.local是Linux 留给用户进行个性化设置的地方。

Sysvinit优缺点

Sysvinit的优点:

  1. 是概念简单,开发人员只需要编写启动和停止脚本,概念非常清楚
  2. 确定的执行顺序,脚本严格按照启动数字的大小顺序执行,一个执行完毕再执行下一个,这非常有益于错误排查

Sysvinit的缺点:

  1. 串行地执行脚本导致Sysvinit运行效率较慢
  2. 对动态设备加载等Linux新特性支持不友好

2、Upstart介绍

开发UpStart的缘由

当Linux内核进入2.6时代时,系统支持热插拔功能,一旦新外设连接到系统,内核便可以自动实时地发现它们,并初始化这些设备,进而使用它们。这为便携式设备用户提供了很大的灵活性。

Sysvinit启动时必须一次性把所有可能用到的服务都启动起来,即使该设备没有连接,因此会造成浪费,比如为了管理打印任务,系统需要启动CUPS等服务。

UpStart基于事件机制,比如U盘插入USB接口后,udev得到内核通知,发现该设备,这就是一个新的事件。UpStart在感知到该事件之后触发相应的等待任务,比如处理/etc/fstab 中存在的挂载点。采用这种事件驱动的模式,upstart 完美地解决了即插即用设备带来的新问题。

UpStart相对于Sysvinit具有如下的优势:

  • 更快地启动系统
  • 当新硬件被发现时动态启动服务
  • 硬件被拔除时动态停止服务

UpStart的原理

Upstart的基本概念和设计清晰明确。UpStart主要的概念是job和event。Job就是一个工作单元,用来完成一件工作,比如启动一个后台服务,或者运行一个配置命令。每个Job都等待一个或多个事件,一旦事件发生,upstart就触发该 job 完成相应的工作。

Job包括包括TaskJobSeriveJobAbstractJob。其中SeriveJob代表后台服务进程,一旦开始运行就成为一个后台进程,由init进程管理。

事件是个非常抽象的概念,下面我罗列出一些常见的事件,希望可以帮助您进一步了解事件的含义:

  • 系统上电启动,init 进程会发送"start"事件
  • 根文件系统可写时,相应 job 会发送文件系统就绪的事件
  • 一个块设备被发现并初始化完成,发送相应的事件
  • 某个文件系统被挂载,发送相应的事件
  • 类似 atd 和 cron,可以在某个时间点,或者周期的时间点发送事件
  • 另外一个 job 开始或结束时,发送相应的事件
  • 一个磁盘文件被修改时,可以发出相应的事件
  • 一个网络设备被发现时,可以发出相应的事件
  • 缺省路由被添加或删除时,可以发出相应的事件

系统初始化的过程是在工作和事件的相互协作下完成的,可以大致描述如下:

系统初始化时,init 进程开始运行,init 进程自身会发出不同的事件,这些最初的事件会触发一些工作运行。每个工作运行过程中会释放不同的事件,这些事件又将触发新的工作运行。如此反复,直到整个系统正常运行起来。

UpStart是兼容SysvInit的runlevel的,通过触发执行/etc/init/rc.conf来执行/etc/rc$.d/目录下的所有脚本。

3、Systemd介绍

Systemd提供了和Sysvinit以及LSBinitscripts兼容的特性。系统中已经存在的服务和进程无需修改。这降低了系统向systemd 迁移的成本,使得Systemd替换现有初始化系统成为可能。

Systemd的启动速度更快,提供了比UpStart更激进的并行启动能力,采用了socket/D-Bus Activation等技术启动服务,提供按需启动的能力,只有在某个服务被真正请求的时候才启动它,当该服务结束,systemd 可以关闭它,等待下次需要时再次启动它。

Systemd还提供如下等特性:

  • 和init比起来引导过程简化了很多
  • Systemd支持并发引导过程从而可以更快启动
  • 通过控制组来追踪进程,而不是PID
  • 优化了处理引导过程和服务之间依赖的方式
  • 支持系统快照和恢复
  • 监控已启动的服务;也支持重启已崩溃服务
  • 包含了systemd-login模块用于控制用户登录
  • 支持加载和卸载组件
  • 低内存使用痕迹以及任务调度能力
  • 记录事件的Journald模块和记录系统日志的syslogd模块

Systemd的单元概念

系统初始化需要执行的任务非常多。每一个任务都被Systemd 抽象为一个配置单元,即unit。当前单元类型如下:

  • service:代表一个后台服务进程,比如 mysqld。这是最常用的一类。
  • socket:此类配置单元封装系统和互联网中的一个 套接字 。当下,systemd 支持流式、数据报和连续包的 AF_INET、AF_INET6、AF_UNIX socket 。每一个套接字配置单元都有一个相应的服务配置单元 。相应的服务在第一个"连接"进入套接字时就会启动(例如:nscd.socket 在有新连接后便启动 nscd.service)。
  • device:此类配置单元封装一个存在于 Linux 设备树中的设备。每一个使用 udev 规则标记的设备都将会在 systemd 中作为一个设备配置单元出现。
  • mount:此类配置单元封装文件系统结构层次中的一个挂载点。Systemd 将对这个挂载点进行监控和管理。比如可以在启动时自动将其挂载;可以在某些条件下自动卸载。Systemd 会将/etc/fstab 中的条目都转换为挂载点,并在开机时处理。
  • automount:此类配置单元封装系统结构层次中的一个自挂载点。每一个自挂载配置单元对应一个挂载配置单元 ,当该自动挂载点被访问时,systemd 执行挂载点中定义的挂载行为。
  • swap: 和挂载配置单元类似,交换配置单元用来管理交换分区。用户可以用交换配置单元来定义系统中的交换分区,可以让这些交换分区在启动时被激活。
  • target:此类配置单元为其他配置单元进行逻辑分组。它们本身实际上并不做什么,只是引用其他配置单元而已。这样便可以对配置单元做一个统一的控制。这样就可以实现大家都已经非常熟悉的运行级别概念。比如想让系统进入图形化模式,需要运行许多服务和配置命令,这些操作都由一个个的配置单元表示,将所有这些配置单元组合为一个目标(target),就表示需要将这些配置单元全部执行一遍以便进入目标所代表的系统运行状态。 (例如:multi-user.target 相当于在传统使用 SysV 的系统中运行级别 5)
  • timer:定时器配置单元用来定时触发用户定义的操作,这类配置单元取代了 atd、crond 等传统的定时服务。
  • snapshot:与 target 配置单元相似,快照是一组配置单元。它保存了系统当前的运行状态。

Systemd的Target和运行级别

systemd使用目标(target)替代了运行级别的概念,提供了更大的灵活性,如您可以继承一个已有的目标,并添加其它服务,来创建自己的目标。通过target文件夹的命令也可以看出对应的runlevel:

Sysvinit运行级别Systemd目标备注
0poweroff.target关闭系统
1,srescue.target单用户模式
2,4multi-user.target多用户,非图形化
3multi-user.target多用户,非图形化
5graphical.target多用户,图形化
6reboot.target重启

使用C/C++开发新的系统服务

使用C/C++开发新的系统服务可能需要关注如下的内容:

  1. 后台服务进程代码不需要执行两次派生来实现后台精灵进程,只需要实现服务本身的主循环即可。
  2. 不要调用 setsid(),交给 systemd 处理
  3. 不再需要维护 pid 文件。
  4. Systemd 提供了日志功能,服务进程只需要输出到 stderr 即可,无需使用 syslog。
  5. 处理信号 SIGTERM,这个信号的唯一正确作用就是停止当前服务,不要做其他的事情。
  6. SIGHUP 信号的作用是重启服务。
  7. 需要套接字的服务,不要自己创建套接字,让 systemd 传入套接字。
  8. 使用 sd_notify()函数通知 systemd 服务自己的状态改变。一般地,当服务初始化结束,进入服务就绪状态时,可以调用它。

Unit文件的编写

服务配置单元文件以.service为文件名后缀,默认时存放在/lib/systemd/system/目录下,然后链接到/etc/systemd/system/对应的目录下。下面以sshd的为例/etc/system/system/sshd.service

1
2
3
4
5
6
7
8
9
10
11
12
[Unit]
Description=OpenSSH server daemon
[Service]
EnvironmentFile=/etc/sysconfig/sshd #设置环境变量
ExecStartPre=/usr/sbin/sshd-keygen
ExecStart=/usrsbin/sshd –D $OPTIONS
ExecReload=/bin/kill –HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=multi-user.target  #系统以该形式运行时,服务方可启动

文件分为三个小节,其中[Unit]段和[Install]段是所有Unit文件通用的,用于配置服务的描述、依赖和随系统启动方式,而[Service]断则是服务类型的Unit文件(后缀为.service)特有的,用于定义服务的具体管理和操作方法。

在/etc/systemd/system 目录下还可以看到诸如*.wants 的目录,放在该目录下的配置单元文件等同于在[Unit]小节中的 wants关键字,即本单元启动时,还需要启动这些单元。比如您可以简单地把您自己写的 foo.service 文件放入 multi-user.target.wants 目录下,这样每次都会被默认启动了。

[Unit]参数

  • Description: 一段描述这个Unit文件的文字,通常只是简短的一句话。
  • Documentation:指定服务的文档,可以是一个或多个文档的URL路径。
  • Requires:依赖的其他Unit列表,列在其中的Unit模块会在这个服务启动的同时被启动。
  • Wants:与Requires相似,但只是在被配置的这个Unit启动时,触发启动列出的每个Unit模块,而不去考虑这些模块启动时候是否成功。
  • After:与Requires相似,但是在后面列出的所有模块启动完成以后,才会启动当前的服务。与Requires不同的是,After不会因为依赖程序在运行过程中停止运行,导致当前服务也停止。
  • Before:与After相反,在启动指定的任意一个模块之前,都会首先确保当前服务已经运行。
  • BindsTo:与Requires非常相似,但是一种更强的关联。启动这个服务时会同时启动列出的所有模块,当有模块启动失败时终止当前服务。反之,只要列出的模块全部启动以后,就会自动启动当前服务。并且,这些模块中有任意一个出现意外结束或重启,这个服务会跟着终止或重启。
  • PartOf:这是一个BindsTo作用的子集,仅在列出的任何模块失败或重启时,终止或重启当前服务,而不会随列出模块的启动而启动。
  • OnFailure:当这个模块启动失败时,就自动启动列出的每个模块。
  • Conflicts:与这个模块有冲突的模块,如果列出的模块中有已经在运行的,则会将已启动的冲突模块停止,并启动当前模块;反过来,冲突模块启动时会把当前模块停止。

上面的这些配置,除了Description外,其他都可以被添加多次。比如After参数,可以使用多个After参数,也可以在一行内使用空格分割,写多个依赖模块。

[install]参数

  • WantedBy:和前面Wants作用相似,但此处表示当前模块被依赖。
  • RequiredBy:和前面的Requires作用相似,但此处表示当前模块被依赖。
  • Also:当这个服务被enable/disable时,将自动enable/disable后面列出的每个模块。

[service]参数

服务生命周期控制相关的参数

  • Type:服务的类型,常用的有simple(默认类型)和forking,默认的simple类型可以适用于绝大多数场景,因此一般可以忽略者这个参数的配置。对于服务进程启动后通过fork系统调用创建子进程,然后关闭应用程序本身进程的情况,则应该将Type的值设置为forking;否则Systemd将不会跟踪子进程的行为,而认为服务已经退出。
  • RemainAfterExit:指为true或false(也可以写yes或no),默认为false。当配置为true时,Systemd只会负责启动服务进程,之后即便服务进程退出了,Systemd也仍然会认为这个服务还在运行中。这个配置主要是提供给一些并非常驻内存,而是启动注册后立即退出,然后等待消息按需启动的特殊类型服务使用的。
  • ExecStart:这个参数是几乎每个“.service”文件都会有的,指定服务启动的主要命令,在每个配置文件中只能使用一次.
  • ExecStartPre:指定在启动执行ExecStart命令前的准备工作,在同一个配置文件中可以有多个,所有命令会按照文件中书写的顺序依次被执行。
  • ExecStartPost:指定在启动执行ExecStart命令后的收尾工作,在同一个配置文件中也可有多个。
  • TimeoutSec:快速设置TimeoutStartSec和TimeoutStopSec参数成指定值。(另外,关于默认时间设定都在systemd配置文件中的DefaultTimeoutStartSec、DefaultTimeoutStopSec和DefaultRestartSec字段进行配置,如果这些字段缺省,DefaultTimeoutStartSec和DefaultTimeoutStopSec的默认指为90s,DefaultRestartSec默认为100ms)
  • TimeoutStartSec:启动服务时的等待秒数,如果超出这个时间服务仍然没有执行完所有的启动命令,则Systemd会认为服务自动失败。这一配置对于使用Docker容器托管的应用十分重要。由于Docker第一次运行时可能会需要从网络上下载服务的镜像文件,因此造成比较严重的延时,容易被Systemd误判断为启动失败而杀死。通常,对于这种服务,需要将TimeoutStartSec设置为0,关闭超时检测。
  • ExecStop:停止服务所需要执行的主要命令,在每个配置文件中只能够有一个。
  • ExecStopPost:指定在ExecStop命令执行后的收尾工作,在同一配置文件中可以有多个。
  • TimeoutStopSec:停止服务时的等待秒数,如果超过这个时间服务仍然没有停止,Systemd会使用SIGKILL信号强行干掉服务进程。
  • Restart:这个值用于指定在什么情况下需要重启服务进程。常用的值有:no、no-success、on-failure、on-abnormal、on-abort和always。默认值为no,即不会自动重启服务。这些不同的值分别表示在哪些情况下,服务会重新启动。
  • RestartSec:如果服务需要被重启,这个参数的值为服务被重启前的等待秒数。默认为100ms。
  • ExecReload:重新加载服务所需执行的主要命令。

服务上下文配置相关的参数

  • Environment:为服务添加环境变量,格式直接为Environment=“foo=bar”(看了一下Systemd的手册,这个参数所接受的格式有些奇葩,建议是直接“foo=bar”,取的时候使用${foo}进行获取)
  • EnvironmentFile:指定加载一个包含服务所需的环境变量列表的文件,文件中的每一行都是一个环境变量的定义。顺便提一下,建议使用的时候将=换成=-,如EnvironmentFile=-/etc/my.env,和=的区别是,使用=-时,假如/etc/my.env文件不在也不会报错。
  • Nice:服务的进程优先级,指越小优先级越高,默认为0,。其中-20为最高优先级,19为最低优先级。
  • WorkingDirectory:指定当前服务的工作目录。
  • RootDirectory:指定当前服务进程的根目录(/目录)。如果配置了这个参数,服务将无法访问指定目录外的任何文件。
  • User:指定运行服务的用户,会影响服务对本地文件系统的访问权限。
  • Group:指定运行服务的用户组,会影响服务对本地文件系统的访问权限。
  • MountFlags:这个值其实是服务的Mount Namespace的配置,会影响服务进程上下文中挂载点的信息,即服务是否会继承主机上已有的挂载点,以及如果服务运行时执行了挂载或卸载设备的操作,是否会真实地在主机上产生效果。可选值为shared、slave和private,具体作用如下表所示:
  • LimitCPU/LimitSTACK/LimitNOFILE/LimitNPROC等:限定服务可用的系统资源量,CPU、程序堆栈、文件句柄数量、子进程数量等

Systemd命令行工具的使用

systemd 的主要命令行工具是systemctl,可以替换servicechkconfig以及telinit命令的使用。

Systemd命令和sysvinit命令的对照表

Sysvinit命令Systemd命令备注
service foo startsystemctl start foo.service用来启动一个服务 (并不会重启现有的)
service foo stopsystemctl stop foo.service用来停止一个服务 (并不会重启现有的)
service foo restartsystemctl restart foo.service用来停止并启动一个服务
service foo reloadsystemctl reload foo.service当支持时,重新装载配置文件而不中断等待操作
service foo condrestartsystemctl condrestart foo.service如果服务正在运行那么重启它
service foo statussystemctl status foo.service汇报服务是否正在运行
ls /etc/rc.d/init.d/systemctl list-unit-files --type=service用来列出可以启动或停止的服务列表
chkconfig foo onsystemctl enable foo.service在下次启动时或满足其他触发条件时设置服务为启用
chkconfig foo offsystemctl disable foo.service在下次启动时或满足其他触发条件时设置服务为禁用
chkconfig foosystemctl is-enabled foo.service用来检查一个服务在当前环境下被配置为启用还是禁用
chkconfig –listsystemctl list-unit-files --type=service输出在各个运行级别下服务的启用和禁用情况
chkconfig foo –listls /etc/systemd/system/*.wants/foo.service用来列出该服务在哪些运行级别下启用和禁用
chkconfig foo –addsystemctl daemon-reload当您创建新服务文件或者变更设置时使用
telinit 3systemctl isolate multi-user.target (OR systemctl isolate runlevel3.target OR telinit 3)改变至多用户运行级别

systemd电源管理命令

命令操作
systemctl reboot重启机器
systemctl poweroff关机
systemctl suspend待机
systemctl hibernate休眠
systemctl hybrid-sleep混合休眠模式(同时休眠到硬盘并待机)

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Python 是一种流行的编程语言,它可以用于构建各种类型的应用程序,包括图灵机图灵机是一种理论模型,用于描述计算和自动化。它由英国数学家艾伦·图灵提出,被认为是计算机科学的重要基础。 在 Python 中,你可以使用基本的编程概念和数据结构来模拟图灵机的行为。例如,你可以使用条件语句、循环和变量来控制图灵机的状态转移和符号操作。你可以通过定义状态集合、符号集合、转移函数和停机状态来实现图灵机的功能。 以下是一个简单的 Python 代码示例,模拟了一个简化的图灵机: ```python # 定义图灵机的状态集合 states = {'q0', 'q1'} # 定义图灵机的符号集合 symbols = {'0', '1'} # 定义转移函数 transitions = { ('q0', '0'): ('q1', '1', 'R'), ('q1', '1'): ('q0', '0', 'L'), ('q1', '0'): ('q1', '1', 'R') } # 定义初始状态和输入串 initial_state = 'q0' input_string = '000111' # 初始化图灵机 current_state = initial_state tape = list(input_string) head_position = 0 # 模拟图灵机运行 while current_state != 'q1': symbol = tape[head_position] if (current_state, symbol) not in transitions: raise Exception('No transition defined for current state and symbol') new_state, new_symbol, move = transitions[(current_state, symbol)] tape[head_position] = new_symbol if move == 'R': head_position += 1 elif move == 'L': head_position -= 1 current_state = new_state # 输出最终的结果 output_string = ''.join(tape) print('Output:', output_string) ``` 请注意,这只是一个简化的示例,实际的图灵机可能更加复杂。在实际应用中,你可能需要使用更高级的编程技术和库来处理更复杂的图灵机模型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sunday_ding

一分钱也是爱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值