Linux随笔12-Linux操作系统启动流程以及systemd的服务管理命令systemctl,以及root密码重置的2种方法

1. 简述Linux操作系统启动流程(grub2/systemd)

Linux操作系统启动引导的过程,系统的控制权移交大致分为如下4步:

  1. BIOS POST,及BIOS加电自检。
  2. GRUB装载识别并装载内核。
  3. 内核识别根文件系统并启动systemd。
  4. systemd准备系统资源,最终完成系统启动,系统就绪。

在第一步中,从按下电源键主板BIOS开始加电自检(POST,即Power-On-Self-Test)并完成硬件初始化,如果在加电自检阶段失败,则无法启动系统,启动流程不会往后进行;当加电自检和硬件初始化操作完成,BIOS就会读取可引导设备的第一个扇区中的主引导记录(MBR),然后主引导记录中的引导装载器(GRUB/GRUB2)接管随后的启动流程。

在GRUB通过在其中记录的保存有/boot分区文件系统驱动(比如支持EXT系列文件系统、FAT文件系统、NTFS文件系统),至此,就可以识别/boot分区了。接下来通过定位/boot分区中的vmlinuz内核文件(自解压文件),将其装载到内存中,同时将随后的启动控制权移交给内核。在GRUB2以前的版本中,GRUB控制阶段可以被细分为3个过程(stage1, stage1.5, stage2)。GRUB2的官方说明中虽然并没有细分的3个阶段的说明,但是实际仍然是遵循的这个过程。

  1. stage 1阶段
    这个阶段GRUB会根据MBR(第一个扇区的前446个字节)中记录的内容定位到stage 1.5阶段的分区位置。而stage 1.5阶段位于MBR之后,到第一个分区之前的这块区域中。MBR中的内容记录的就是 “/usr/lib/grub/i386-pc/boot.img” 的内容,但实际查看两者内容并不完全一致(在446字节之后是64字节的分区表,共记录4个分区记录,每个分区记录占16个字节,最后2个字节为MBR的固定标识,为55AA)。
    关于上述文件内容与MBR的内容对比,如下所示:
    [root@c7u6-ha1 ~]# ls -lh /usr/lib/grub/i386-pc/boot.img
    -rw-r--r--. 1 root root 512 Nov  9  2018 /usr/lib/grub/i386-pc/boot.img
    [root@c7u6-ha1 ~]# hexdump -Cv -n 512 /usr/lib/grub/i386-pc/boot.img
    00000000  eb 63 90 00 00 00 00 00  00 00 00 00 00 00 00 00  |.c..............|
    00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
    00000050  00 00 00 00 00 00 00 00  00 00 00 80 01 00 00 00  |................|
    00000060  00 00 00 00 ff fa eb 05  f6 c2 80 74 05 f6 c2 70  |...........t...p|
    00000070  74 02 b2 80 ea 79 7c 00  00 31 c0 8e d8 8e d0 bc  |t....y|..1......|
    00000080  00 20 fb a0 64 7c 3c ff  74 02 88 c2 52 be 05 7c  |. ..d|<.t...R..||
    00000090  b4 41 bb aa 55 cd 13 5a  52 72 3d 81 fb 55 aa 75  |.A..U..ZRr=..U.u|
    000000a0  37 83 e1 01 74 32 31 c0  89 44 04 40 88 44 ff 89  |7...t21..D.@.D..|
    000000b0  44 02 c7 04 10 00 66 8b  1e 5c 7c 66 89 5c 08 66  |D.....f..\|f.\.f|
    000000c0  8b 1e 60 7c 66 89 5c 0c  c7 44 06 00 70 b4 42 cd  |..`|f.\..D..p.B.|
    000000d0  13 72 05 bb 00 70 eb 76  b4 08 cd 13 73 0d 5a 84  |.r...p.v....s.Z.|
    000000e0  d2 0f 83 de 00 be 85 7d  e9 82 00 66 0f b6 c6 88  |.......}...f....|
    000000f0  64 ff 40 66 89 44 04 0f  b6 d1 c1 e2 02 88 e8 88  |d.@f.D..........|
    00000100  f4 40 89 44 08 0f b6 c2  c0 e8 02 66 89 04 66 a1  |.@.D.......f..f.|
    00000110  60 7c 66 09 c0 75 4e 66  a1 5c 7c 66 31 d2 66 f7  |`|f..uNf.\|f1.f.|
    00000120  34 88 d1 31 d2 66 f7 74  04 3b 44 08 7d 37 fe c1  |4..1.f.t.;D.}7..|
    00000130  88 c5 30 c0 c1 e8 02 08  c1 88 d0 5a 88 c6 bb 00  |..0........Z....|
    00000140  70 8e c3 31 db b8 01 02  cd 13 72 1e 8c c3 60 1e  |p..1......r...`.|
    00000150  b9 00 01 8e db 31 f6 bf  00 80 8e c6 fc f3 a5 1f  |.....1..........|
    00000160  61 ff 26 5a 7c be 80 7d  eb 03 be 8f 7d e8 34 00  |a.&Z|..}....}.4.|
    00000170  be 94 7d e8 2e 00 cd 18  eb fe 47 52 55 42 20 00  |..}.......GRUB .|
    00000180  47 65 6f 6d 00 48 61 72  64 20 44 69 73 6b 00 52  |Geom.Hard Disk.R|
    00000190  65 61 64 00 20 45 72 72  6f 72 0d 0a 00 bb 01 00  |ead. Error......|
    000001a0  b4 0e cd 10 ac 3c 00 75  f4 c3 00 00 00 00 00 00  |.....<.u........|
    000001b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 24 12  |..............$.|
    000001c0  0f 09 00 52 be bd 7d 31  c0 cd 13 46 8a 0c 84 c9  |...R..}1...F....|
    000001d0  75 0f be da 7d e8 cc ff  eb 96 46 6c 6f 70 70 79  |u...}.....Floppy|
    000001e0  00 bb 00 70 8e c3 31 db  b8 01 02 b5 00 b6 00 cd  |...p..1.........|
    000001f0  13 72 d4 b6 01 b5 4f e9  f1 fe 00 00 00 00 55 aa  |.r....O.......U.|
    00000200
    [root@c7u6-ha1 ~]#
    接下来查看磁盘的第一个扇区的内容,即主引导记录MBR的内容。具体如下所示:
    [root@c7u6-ha1 ~]# hexdump -Cv -n 512 /dev/vda
    00000000  eb 63 90 10 8e d0 bc 00  b0 b8 00 00 8e d8 8e c0  |.c..............|
    00000010  fb be 00 7c bf 00 06 b9  00 02 f3 a4 ea 21 06 00  |...|.........!..|
    00000020  00 be be 07 38 04 75 0b  83 c6 10 81 fe fe 07 75  |....8.u........u|
    00000030  f3 eb 16 b4 02 b0 01 bb  00 7c b2 80 8a 74 01 8b  |.........|...t..|
    00000040  4c 02 cd 13 ea 00 7c 00  00 eb fe 00 00 00 00 00  |L.....|.........|
    00000050  00 00 00 00 00 00 00 00  00 00 00 80 01 00 00 00  |................|
    00000060  00 00 00 00 ff fa 90 90  f6 c2 80 74 05 f6 c2 70  |...........t...p|
    00000070  74 02 b2 80 ea 79 7c 00  00 31 c0 8e d8 8e d0 bc  |t....y|..1......|
    00000080  00 20 fb a0 64 7c 3c ff  74 02 88 c2 52 be 05 7c  |. ..d|<.t...R..||
    00000090  b4 41 bb aa 55 cd 13 5a  52 72 3d 81 fb 55 aa 75  |.A..U..ZRr=..U.u|
    000000a0  37 83 e1 01 74 32 31 c0  89 44 04 40 88 44 ff 89  |7...t21..D.@.D..|
    000000b0  44 02 c7 04 10 00 66 8b  1e 5c 7c 66 89 5c 08 66  |D.....f..\|f.\.f|
    000000c0  8b 1e 60 7c 66 89 5c 0c  c7 44 06 00 70 b4 42 cd  |..`|f.\..D..p.B.|
    000000d0  13 72 05 bb 00 70 eb 76  b4 08 cd 13 73 0d 5a 84  |.r...p.v....s.Z.|
    000000e0  d2 0f 83 de 00 be 85 7d  e9 82 00 66 0f b6 c6 88  |.......}...f....|
    000000f0  64 ff 40 66 89 44 04 0f  b6 d1 c1 e2 02 88 e8 88  |d.@f.D..........|
    00000100  f4 40 89 44 08 0f b6 c2  c0 e8 02 66 89 04 66 a1  |.@.D.......f..f.|
    00000110  60 7c 66 09 c0 75 4e 66  a1 5c 7c 66 31 d2 66 f7  |`|f..uNf.\|f1.f.|
    00000120  34 88 d1 31 d2 66 f7 74  04 3b 44 08 7d 37 fe c1  |4..1.f.t.;D.}7..|
    00000130  88 c5 30 c0 c1 e8 02 08  c1 88 d0 5a 88 c6 bb 00  |..0........Z....|
    00000140  70 8e c3 31 db b8 01 02  cd 13 72 1e 8c c3 60 1e  |p..1......r...`.|
    00000150  b9 00 01 8e db 31 f6 bf  00 80 8e c6 fc f3 a5 1f  |.....1..........|
    00000160  61 ff 26 5a 7c be 80 7d  eb 03 be 8f 7d e8 34 00  |a.&Z|..}....}.4.|
    00000170  be 94 7d e8 2e 00 cd 18  eb fe 47 52 55 42 20 00  |..}.......GRUB .|
    00000180  47 65 6f 6d 00 48 61 72  64 20 44 69 73 6b 00 52  |Geom.Hard Disk.R|
    00000190  65 61 64 00 20 45 72 72  6f 72 0d 0a 00 bb 01 00  |ead. Error......|
    000001a0  b4 0e cd 10 ac 3c 00 75  f4 c3 00 00 00 00 00 00  |.....<.u........|
    000001b0  00 00 00 00 00 00 00 00  e9 5f 09 00 00 00 80 20  |........._..... |
    000001c0  21 00 83 65 24 41 00 08  00 00 00 00 10 00 00 65  |!..e$A.........e|
    000001d0  25 41 82 ef 2c c3 00 08  10 00 00 00 20 00 00 ef  |%A..,....... ...|
    000001e0  2d c3 8e fe ff ff 00 08  30 00 00 f8 4f 02 00 00  |-.......0...O...|
    000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 55 aa  |..............U.|
    00000200
    
  2. stage 1.5
    这个阶段中存放的是core.img这个文件,其中包含了通用文件系统的驱动程序,比如EXT系列文件系统、FAT文件系统、NTFS文件系统等。通过将这个阶段的core.img装载到内存中,就可以识别到/boot分区的文件系统了。由于core.img中通常不包含lvm的软件以及相应的驱动,所以/boot分区不能安装在lvm逻辑卷上。另外,也不能将/boot分区格式化为XFS文件系统。这个阶段执行完成之后,就可以识别到/boot分区了。
  3. stage 2
    这个阶段中,GRUB就可以定位并能够识别/boot分区了,此时就可以将/boot分区中的内核文件vmlinuz装在到内存中,同时GRUB会将启动流程控制权移交给内核。由于此时,如果将根文件系统装在lvm逻辑卷上,格式化为EXT之外的其他文件系统,那么内核在这个阶段还不能识别根分区的文件系统。此时就需要借助/boot分区中的initramfs内存虚拟根文件系统中保存的对应驱动以及程序结合系统存储设备映射文件System.map识别根分区。

至此,内核就可以装载systemd程序,并将随后的启动控制权移交给systemd程序。
上述过程概括为如下图:
在这里插入图片描述

systemd是现在主流发行版的通用资源管理方式,systemd是所有其他进程的母进程,除了进程管理之外,还支持很多其他资源管理,比如挂载文件系统、启动和管理系统服务等等。systemd接管启动流程之后,首先会通过定义/etc/fstab挂在文件系统,此时就可以访问根目录中的/etc/下的配置文件了,包括其自身的配置文件 /etc/systemd/system/default.target,这个文件决定了系统的默认运行级别,该文件只是一个链接文件,其链接到系统启动之后的目标target上。

具体如下所示:

[root@c7u6-ha1 ~]# ll /etc/systemd/system/default.target
lrwxrwxrwx. 1 root root 37 Feb 18  2020 /etc/systemd/system/default.target -> /lib/systemd/system/multi-user.target
[root@c7u6-ha1 ~]# cat /etc/systemd/system/default.target
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it 
#  under the terms of the GNU Lesser General Public License as published by 
#  the Free Software Foundation; either version 2.1 of the License, or 
#  (at your option) any later version.

[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes
[root@c7u6-ha1 ~]# 

上述即为default.target的内容。
multi-user.target对应SystemV init中的运行级别为runleve 3,而graphical.target则对应于runleve 5。
关于各个运行级别,概括总结如下:

SystemV Runlevelsystemd targetsystem target aliasDescription
halt.targetHalts the system without powering it down.
0poweroff.targetrunlevel0.targetHalts the system and turns the power off.
Semergency.targetSingle user mode. No services are running; filesystems are not mounted. This is the most basic level of operation with only an emergency shell running on the main console for the user to interact with the system.
1rescue.targetrunlevel1.targetA base system including mounting the filesystems with only the most basic services running and a rescue shell on the main console.
2runlevel2.targetMultiuser, without NFS but all other non-GUI services running.
3multi-user.targetrunlevel3.targetAll services running but command line interface (CLI) only.
4runlevel4.targetUnused.
5graphical.targetrunlevel5.targetmulti-user with a GUI.
6reboot.targetrunlevel6.targetReboot
default.targetThis target is always aliased with a symbolic link to either multi-user.target or graphical.target. systemd always uses the default.target to start the system. The default.target should never be aliased to halt.target, poweroff.target, or reboot.target.

systemd接管启动流程之后的操作如下图所示:
在这里插入图片描述

上图中的sysinit.target和basic.target可以看作是启动过程中的检查点,虽然systemd支持资源的并行启动,但是总会有一些服务和资源对象需要在其他资源对象之前启动。当sysinit.target执行完成之后,就会执行basic.target这个资源,当这些先决资源启动完成之后,最后就是启动运行级别资源对象开启对应的用户接口了。

当systemd将系统资源准备完成,操作系统就完成了启动的整个流程。此时用户就可以登陆系统进行操作了。

2. systemctl管理命令

前面列出了systemd的几个运行级别,用于指定不同的用户操作环境。而systemd作为SystemV init脚本和Upstart的替代者,其存在就是用于管理系统资源和服务。其提供了丰富的特性,比如系统启动阶段的服务并行启动、按需激活必要的守护进程以及基于依赖关系的服务控制逻辑等。

2.1. systemd的unit介绍

systemd中引入了单元(unit)的概念,这些单元涉及到系统服务、监听套接字以及其他init系统相关的对象。要查看系统上systemd支持的单元都有哪些,可以执行命令systemctl -t help获得。在CentOS 7.6上有13个单元;而在CentOS 8.2上则减少到了11个。具体如下所示:

[root@LiuXianQiE ~]# cat /etc/redhat-release 
CentOS Linux release 8.3.2011
[root@LiuXianQiE ~]# systemctl -t help
Available unit types:
service
socket
target
device
mount
automount
swap
timer
path
slice
scope
[root@LiuXianQiE ~]# systemctl -t help | egrep -v unit | wc -l
11
[root@LiuXianQiE ~]# 
[root@c7u6-ha1 ~]# cat /etc/redhat-release 
CentOS Linux release 7.6.1810 (Core) 
[root@c7u6-ha1 ~]# systemctl -t help | egrep -v unit
service
socket
busname
target
snapshot
device
mount
automount
swap
timer
path
slice
scope
[root@c7u6-ha1 ~]# systemctl -t help | egrep -v unit | wc -l
13
[root@c7u6-ha1 ~]# 

从上述输出中可以看出,CentOS 8.x中比CentOS 7.x中少了snapshot以及busname这两个单元。
关于单元,一些简单的介绍总结如下表所示:

Unit TypeFile ExtensionDescription
Service unit.serviceA system service.
Target unit.targetA group of systemd units.
Automount unit.automountA file system automount point.
Device unit.deviceA device file recognized by the kernel.
Mount unit.mountA file system mount point.
Path unit.pathA file or directory in a file system.
Scope unit.scopeAn externally created process.
Slice unit.sliceA group of hierarchically organized units that manage system processes.
Snapshot unit.snapshotA saved state of the systemd manager
Socket unit.socketAn inter-process communication socket.
Swap unit.swapA swap device or a swap file
Timer unit.timerA systemd timer.

上述是一些类型的单元以及其扩展名。这些文件通常存放在一下三个地方,具体如下表所示:

DirectoryDescription
/usr/lib/systemd/system/Systemd unit files distributed with installed RPM packages.
/run/systemd/system/Systemd unit files created at run time.The directory takes precedence over the directory with installed service unit files.
/etc/systemd/systemSystemd unit files created by systemctl enable as well as unit files added for extending a service. This directory takes precedence over the directroy with runtime unit files.

上述三个路径中,通常将用户定义的单元(unit)放置在第一个路径中,即/usr/lib/systemd/system这个目录中。

而systemd自身的配置文件是/etc/systemd/system.conf,可以通过修改这个配置文件中的内容,来调整systemd的行为。比如默认的超时上限是90秒,通过修改这个配置文件中的字段 DefaultTimeoutStartSec 可以改变systemd的超时时间。比如改为 DefaultTimeoutStartSec=120 。就将超时值修改为了120秒。

2.2. systemd的主要特性

systemd提供的主要特性概括如下:

  • Socket-based activation:在系统启动的时候,systemd会为所有支持这个类型的服务创建监听套接字,并且在这些服务启动的时候将套接字传递给这些服务。这就可以使systemd可以实现并行启动服务,同时也使其能够在重启服务期间不会丢失掉发送给这个服务的信息:因为此时这个服务相关的套接字仍然是可以访问的,并且所有发送给这个服务的消息都会被放入其消息队列中。
    systemd中为基于套接字激活使用了.socket类型的unit。
  • Bus-based activation:使用D-Bus作为进程间通信的系统服务可以在客户端应用首次尝试与其建立连接通讯的时候按需启动,即有客户端的连接请求的时候才会激活服务。
    systemd为基于总线的激活方式使用.bus类型的unit。
  • Device-based activation:支持基于设备激活的系统服务可以在特定类型的硬件插入并且处于可用状态的时候被激活。
    systemd为基于设备的激活使用了.device类型的unit。
  • Path-based activation:支持基于路径激活的系统服务可以在系统的特定文件或者目录的状态信息发生更改的时候被激活。
    systemd为这个类型的服务使用了.path类型的unit。
  • Mount and automount point management:systemd会对系统的挂载点和自动挂载点进行监视。
    systemd为这类型使用了.mount和.automount这两个类型的unit。
  • Aggressive parallelization:因为使用了基于套接字的激活方式,所以systemd可以在相应的套接字出现的时候并行启动系统服务。通过与支持按需启动的系统服务相结合,并行激活可以显著的减少系统启动时间。
  • Transactional unit activation logic:在激活或者取消激活一个单元之前,systemd会对其依赖关系进行计算,并且创建临时事务,同时对这些事务的一致性进行验证。如果事务状态不一致,systemd会在报告错误信息之前尝试自动纠正这种情况,并自动移除非必要的任务。
  • Backwards compatibility with SysV init:Systemd可以向下兼容SysV init脚本。

2.3. systemctl命令管理系统服务

由于systemd是向下兼容SysV init的服务管理脚本的,所以在CentOS 7.x中仍然可以使用service命令以及chkconfig这两个命令对服务进行管理。而systemd提供的服务管理命令则是systemctl。这个命令可以对.service类型的unit执行start, stop, restart, enable, disable等操作。关于servicesystemctl命令的对比,详见下表:

servicesystemctlDescription
service name startsystemctl start name.serviceStarts a service.
service name stopsystemctl stop name.serviceStops a service.
service name restartsystemctl restart name.serviceRestarts a service.
service name condrestartsystemctl try-restart name.serviceRestarts a service only if it is running.
service name reloadsystemctl reload name.serviceReloads configuration.
service name statussystemctl status name.service
systemctl is-active name.service
Checks if a service is running.
service --status-allsystemctl list-units --type service --allDisplays the status of all services.

上述是service命令与systemctl命令的对比。
chkconfig命令与systemctl命令的对比结果如下表所示:

chkconfigsystemctlDescription
chkconfig name onsystemctl enable name.serviceEnables a service.
chkconfig name offsystemctl disable name.serviceDisables a service.
chkconfig --list namesystemctl status name.service
systemctl is-enabled name.service
Checks if a service is enabled.
chkconfig --listsystemctl list-unit-files --type serviceLists all services and checks if they are enabled.
chkconfig --listsystemctl list-dependencies --afterLists services that are ordered to start before the specified unit.
chkconfig --listsystemctl list-dependencies --beforeLists services that are ordered to start after the specified unit.

在使用systemctl命令管理服务的时候,一般会写上.service这个后缀,但是由于默认的服务都是以.service结尾,所以这个后缀是可以省略的。所以执行systemctl start nfs-server.servicesystemctl start nfs-server的效果是相同的。
另外,可以查看某个服务的详细信息,此时需要执行命令systemctl show name.service即可。如果要查看某一项信息,可以使用-p name进行指定。比如systemctl show nfs-server.service -p Names就可以查看nfs-server.service这个服务的Names这个信息,即这个服务有几个名字,是否有别名的存在,都可以被列出来。

要列出当前系统下所有已经装载的服务,可以执行命令systemctl list-units --type service。对于每一个.service类型的unit,这个命令会显示其完整的服务名称(UNIT)、该服务是否被装载(LOAD)、以及其高级(ACTIVE)和低级(SUB)单元文件的激活状态,以及该服务的短描述信息(DESCRIPTION)。
默认情况下,systemctl list-units这个命令只显示活动的单元,如果想要查看所有的单元,而不管其是否处于活动状态,需要加上-a/--all这个选项。此时的命令为systemctl list-units --type service --all
如果要查看系统上已经被使能的服务单元,可以执行命令systemctl list-unit-files --type service进行查看。对于每一个服务单元,该命令会显示服务单元的完整名称(UNIT FILE)、该服务单元是否处于使能状态(STATE)。

要查看服务的状态,可以执行命令systemctl status name.service进行查看。该命令的输出字段包括如下:

FieldDescription
LoadedInformation whether the service unit has been loaded, the absolute path to the unit file, and a note whether the unit is enabled.
ActiveInformation whether the service unit is running followed by a time stamp.
Main PIDThe PID of the corresponding system service followed by its name.
StatusAdditional information about the corresponding system service.
ProcessAdditional information about related processes.
CGroupAdditional information about related Control Groups(cgroups).

如果要验证服务单元是否处于活动状态,或者运行状态,可以执行命令systemctl is-active name.service进行查看。
如果要查看某个服务单元是否已经被使能,可以执行命令systemctl is-enabled name.service进行查看。
如果相关的服务单元处于运行状态或者是被使能状态,那么这两条命令的退出状态码都是0。

要查看某个服务单元的依赖关系,比如查看该服务是在哪些服务之后启动,可以执行命令systemctl list-dependencies --after name.service进行查看,换句话说,就是该命令会递归列出After=这个依赖关系中列出的单元(即该服务所依赖的服务);如果要查看该服务是在哪些服务之前启动,可以执行命令systemctl list-dependencies --before name.service进行查看,换句话说,这个命令会递归列出Before=中列出的单元(也就是依赖于该服务的单元有哪些)。

要启动一个服务单元,可以执行命令systemctl start name.service进行查看。

要停止一个服务单元,可以执行命令systemctl stop name.service进行查看。

要重启一个服务单元,可以执行命令systemctl restart name.service进行查看,该命令会在当前会话中停止指定的服务单元,然后立即启动该服务单元。重要的一点是,如果指定的服务单元此时并没有处于运行状态,那么这个重启的操作也会启动它。如果只想对已经运行的服务单元执行重启操作,而未运行的服务单元则不执行重启操作,那么可以执行命令systemctl try-restart name.service这个命令来完成。另外,某系服务允许在不中断服务执行的前提下重新装在配置文件,对于此类服务,可以执行命令systemctl reload name.service进行配置文件的重新装载。入果指定的服务单元并不支持这个操作,那么这个命令执行的时候会被忽略掉。如果不确定服务是否支持reload操作,那么可以执行命令systemctl reload-or-restart name.service进行重新装载配置文件或者重启的操作;如果只希望对已经运行的服务执行重新装载或者重启的操作,那么需要执行systemctl reload-or-try-restart name.service命令进行重新装载配置文件或者对已经运行的服务单元进行重启的操作。

要使能(即设置开机自动运行)服务单元,需要执行命令systemctl enable name.service来完成,这个命令会在/etc/systemd/system目录中创建对应服务单元的符号链接文件。如果想要相关服务的符号链接文件被重新创建,此时可以执行命令systemctl reenable name.service来完成。这个命令会首先禁用指定的服务(即删除相关服务的符号链接,然后再重新创建该链接)。如果对于未运行的服务单元,在使能其运行的同时,让其立即运行,此时可以执行命令systemctl enable --now name.service,表示让该服务开机自动运行,并且现在立即运行。

要禁用指定的服务(即将其设置为开机不自动运行),此时可以执行命令systemctl disable name.service。如果想要禁止某个服务被手动启动或者被其他服务启动,此时可以将其掩盖起来,执行命令systemctl mask name.service即可,这个命令的实际操作就是/etc/systemd/system/name.service创建为/dev/null的软链接。所以要允许指定的服务可以被手动启动或者被其他服务启动,那么就需要将掩盖拿掉,即执行命令systemctl unmask name.service即可。

2.4. systemctl与target

在RHEL/CentOS 7.x之前的发行版中,使用SysV init以及Upstart实现预定义的运行级别来表达特定的操作模式,其RHEL/CentOS5.x使用的是SysV init,在RHEL/CentOS 6.x中使用的则是Upstart。在RHEL/CentOS 7.x以及8.x中则是使用的systemd的target来替代此前的两者,实现类似运行级别来表达特定的操作模式的目的。

systemd的target表现为target单元,即以.target为结尾的文件,其唯一的目的就是将其他systemd的单元(unit)按照依赖关系链进行分组。比如 graphical.target 这个单元用于启动一个图形化的交互环境,其会启动比如GNOME Display Manager(gdm.service)这个系统服务、Account Service(accounts-daemon.service)这个服务,同时也会激活 multi-user.target 这个单元。类似的,multi-user.target 这个单元会启动一些其他必要的系统服务,比如NetworkManager(NetworkManager.service)或者D-Bus(dbus.service),同时激活 basic.target 这个单元。

关于各种运行级别,参见本篇博客的第一部分的内容。

要查看和切换运行级别,新老命令的对比如下表所示:

Old CommandNew CommandDescription
runlevelsystemctl list-units --type targetLists currently loaded target units.
telinit
runlevel
systemctl isolate name.targetChanges the current target.

要查看当前系统的默认target,执行命令systemctl get-default即可。要查看当前系统上已经装载的所有的target单元,执行命令systemctl list-units --type target即可。对于每一个target单元,这个命令都会显示完整的名字(UNIT)、该单元是否被装载(LOAD)、高级(ACTIVE)单元和低级(SUB)单元的激活状态以及该单元的简短描述(DESCRIPTION)。
默认情况下,systemctl list-units --target target只会列出活动的单元,如果想要列出所有的target单元,而不管其是否处于活动状态,那么此时就需要在命令行中加上选项--all/-a。此时的命令为systemctl list-units --type target --all即可。
如果要给当前系统设置默认的target,即设置默认运行级别,此时就需要执行命令systemctl set-default name.target。这个命令会将链接文件/etc/systemd/system/default.target指向/usr/lib/systemd/system/name.target,那么下次系统启动的时候,默认的运行级别就变成了name.target所指定的运行级别。
如果要在当前会话中临时修改target,那么就可以执行命令systemctl isolate name.target即可。比如将当前的target修改为营救模式,及rescue.target,执行命令systemctl isolate rescue.target即可,更简单的写法是systemctl rescue,这两种方式的效果相同。在切换到营救模式之后,系统会尝试挂在所有的本地文件系统,并且会启动一些重要的系统服务,但是并不会激活网卡接口,也不允许多用户登录,此时只允许root用户登录。在切换为营救模式之后,需要正确输入root用户的密码之后才能进入到营救模式。 另外,在切换到营救模式的时候,系统默认会给所有当前正处于登录状态的用户发送广播消息,如果要禁用这个广播消息,此时可以加上选项--no-wall即可,此时的命令为systemctl --no-wall rescue
如果要切换为紧急模式,即emergency.target单元,此时需要执行命令systemctl isolate emergency.target,或者更简短的形式systemctl emergency,这两种命令形式效果是一样的,都是切换到紧急模式。在紧急模式中,操作系统会将根分区挂载为只读形式,并且不会尝试挂载其他文件本地文件系统,同样不会激活网卡接口,并且只会启动一些必要的服务,同样不允许多用户登录。在切换为紧急模式之后,需要正确输入root用户密码之后才能进入到紧急模式。 另外,默认情况下,在切换为紧急模式的时候系统会给当前正处于登陆状态的用户发送广播消息。如果要禁用广播消息,可以加上选项--no-wall,此时的命令为systemctl isolate emergency --no-wall或者systemctl --no-wall emergency

2.5. systemctl命令实现系统关闭、挂起和冬眠

RHEL/CentOS 7.x开始,由systemd提供系统的电源管理命令。相关的电源管理命令如下表所示:

Old CommandNew CommandDescription
haltsystemctl haltHalts the system.
poweroffsystemctl poweroffPowers off the system.
rebootsystemctl rebootRestarts the system.
pm-suspendsystemctl suspendSuspends the system.
pm-hibernatesystemctl hibernateHibernates the system.
pm-suspend-hybridsystemctl hybrid-sleepHibernates and suspends the system.

如果要关闭运行的系统,可以使用传统的shutdown命令,比如shutdown -h now即可立即关闭系统。而使用systemctl命令进行系统关闭的操作,就需要执行命令systemctl poweroff即可,这个命令相当于直接长按主机的电源键进行关机。如果要通过关闭并停止系统,而不是通过类似按电源键关机的的效果,此时可以执行命令systemctl halt即可。默认情况下,这两个命令都会给登录用户发送广播消息,如果不想用户收到广播消息,可以在命令行中增加选项--no-wall即可。此时的命令为systemctl --no-wall poweroff或者systemctl --no-wall halt,这两者都可以。
如果不是立即关闭,实现延时关闭的效果,此时就需要将上述的命令改为选项模式,比如指定在几点几分关闭系统,此时可以执行命令systemctl --poweroff HH:MM即可,注意,此时poweroff这个命令已经变成长选项格式了,不要省略掉两个英文半角连字符。如果要指定在几分钟或者几个小时之后关闭系统,此时可以执行命令systemctl --halt +m,这条命令中的halt同样变为选项模式,不能省略两个英文半角连字符,同时+m表示m分钟后关闭系统。如果将+m替换为now,则等效于+0,表示立即关闭系统。对于上述尚未执行的挂起状态的关机命令,可以执行shutdown -c进行取消。

如果要重启系统,可以执行命令systemctl reboot即可,默认该命令会给所有已经登陆的用户发送广播消息,如果要禁用这个广播消息,此时可以加上命令行选项--no-wall即可,此时的命令为systemctl --no-wall reboot

如果要挂起系统,可以执行命令systemctl suspend即可。这个命令会将系统的状态信息保存到内存中,当后面切换回系统的时候,系统会直接从内存中回复此前保存的状态,而无需重新启动系统。由于系统的状态信息是保存在系统的内存中,而不是保存在磁盘中,所以恢复速度很快,但与此同时,也会导致当系统遭遇突然的断电时造成保存的状态丢失的情况。

如果要将系统冬眠,此时就需要执行命令systemctl hibernate。这个命令将系统状态保存到磁盘中,然后关闭系统。当你重新切回到系统的时候,操作系统会从硬盘中读取保存的状态完成恢复,而无需重新启动系统。由于系统状态信息是保存在硬盘中,而不是保存在内存中,所以恢复的时候速度会相对较慢 。
如果要冬眠并且挂起系统,可以执行命令systemctl hybrid-sleep命令即可。

2.6. 控制远程主机上的systemd

除了可以控制本地系统的systemd以及服务管理之外,systemctl命令还可以通过ssh协议与远程主机的systemd进行交互。需要确保远程主机上的sshd服务处于开启状态,此时就可以通过systemctl -H/--host形式的命令对远程主机进行操作了。具体命令为systemctl --host remote_user@remote_host command即可。具体如下面的示例所示:

[root@LiuXianQiE ~]# systemctl -H root@c7u6-ha1 is-active sshd.service
active
[root@LiuXianQiE ~]# systemctl -H root@c7u6-ha1 status sshd.service
● sshd.service - OpenSSH server daemon
   Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2021-06-07 11:12:56 CST; 13h ago
     Docs: man:sshd(8)
           man:sshd_config(5)
 Main PID: 2985
   CGroup: /system.slice/sshd.service
           └─1715308 /usr/sbin/sshd -D -oCiphers=aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes256-cbc,aes128-gcm@openssh.c>2985 /usr/libexec/gsd-power
lines 1-9/9 (END)

上述在远程主机上分别执行了两条命令,第一条命令查看远程主机上的sshd.service服务是否处于激活状态;第二条命令是查看远程主机上的sshd.service服务的状态信息。为了与上述的输出结果做对比,下面在本机上再查看一下sshd.service的状态信息,具体如下所示:

[root@LiuXianQiE ~]# systemctl status sshd.service
. sshd.service - OpenSSH server daemon
   Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
   Active: active (running) since Mon 2021-06-07 11:14:26 CST; 13h ago
     Docs: man:sshd(8)
           man:sshd_config(5)
 Main PID: 1715308 (sshd)
    Tasks: 1 (limit: 409082)
   Memory: 1.2M
   CGroup: /system.slice/sshd.service
           └─1715308 /usr/sbin/sshd -D -oCiphers=aes256-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes256-cbc,aes128-gcm@openssh.c>

Jun 07 11:14:26 LiuXianQiE systemd[1]: Starting OpenSSH server daemon...
Jun 07 11:14:26 LiuXianQiE sshd[1715308]: Server listening on 0.0.0.0 port 22.
Jun 07 11:14:26 LiuXianQiE sshd[1715308]: Server listening on :: port 22.
Jun 07 11:14:26 LiuXianQiE systemd[1]: Started OpenSSH server daemon.
lines 1-15/15 (END)

2.7. 创建以及修改systemd的unit文件

由于systemd的行为是由对应的单元(unit)文件定义的,而单元文件中包含了单元描述以及行为定义的指令,为了更出色的适应systemd的管理方式,管理员一般需要手动编辑以及创建单元文件。其中/etc/systemd/system这个目录用于保存系统管理员创建以及自定义的单元文件,但是实际上,更多的是直接在/usr/lib/systemd/system这个目录中进行单元文件的创建以及编辑自定义操作。

对于单元文件,可以通过目录增补额外的配置文件,比如要给 sshd.service 增补自定义配置选项,就需要创建 sshd.service.d/custom.conf 这个配置文件并在其中插入额外的配置指令。除此之外,还可以创建 sshd.service.wants/ 目录以及 sshd.service.requires/ 目录,这两个目录中包含了sshd服务所依赖服务的符号链接文件。这些符号链接文件是依据sshd服务的单元文件sshd.service中的[Install]选项自动创建出来的,或者是在运行时基于[Unit]选项自动创建出来的。当然,也可以手动创建这些目录和符号链接文件。

2.7.1. 理解单元文件(unit file)的结构

单元文件典型的结构由三部分组成:

  • [Unit] :指定不依赖于这个单元类型的通用选项,这些选项提供了单元的描述信息、指定单元的行为以及设置与其他单元的依赖关系。关于频繁使用的单元选项,参见下表"Table1: Important [Unit] Section Options"。
  • [unit type] :如果单元有特定类型的指令,那么这些指令会被分组到一个以这个单元类型命名的小节(section)下面。比如服务单元文件中包含 [Service] 这个小节。关于经常被用到的[Service]选项,参见下表"Table2: Important [Service] Section Options"。
  • [Install] :包含了systemctl enable命令以及systemctl disable命令所用的单元安装的信息。关于详细的[Install]选项,参见下表"Table3: Important [Install] Section options"。

Table1: Important [Unit] Section Options

Option[a]Description
DescriptionA meaningful description of the unit. This text is displayed for example in the output of the systemctl status command.
DocumentationProvides a list of URIs referencing documentation for the unit.
After[b]Defines the order in which units are started. The unit starts only after the units specified in After are active. Unlike Requires , After doesn’t explicitly activate the specified units. The Before option has the opposite functionality to After .
RequiresConfigures the dependencies on other units. The units listed in Requires are activated together with the unit. If any of the required units fail to start, the unit is not activated.
WantsConfigures weaker dependencies than Requires. If any of the listed units does not start successfully, it has no impact on the unit activation. This is the recommended way to establish custom unit dependencies.
ConfilictsConfigures negative dependencies, an opposite to Requires.

[a]: 关于[Unit]部分更详尽的配置选项,参见 **systemd.unit (5)**帮助手册,即man 5 systemd.unit
[b]: 多数情况下,只需要在[Unit]部分通过 After 以及 Before 就足以指定顺序依赖关系。如果与此同时也设置了 Wants 或者 Requires 指定需求的单元,那么仍然需要通过 After 以及 Before 指定顺序依赖关系。这是因为顺序以来关系和需求的单元之间彼此是相互独立的。

Table2: Important [Service] Section Options

Option[a]Description
TypeConfigures the unit process startup type that affects the functionality of ExecStart and related options. One of:
1. simple : The default value. The process started with ExecStart is the main process of the service.
2. forking : The process started with ExecStart spawns a child process that becomes the main process of the service. The parent process exits when the startup is complete.
3. oneshot : This type is similar to simple, but the process exits before starting consequent units.
4. dbus : This type is similar to simple, but consequent units are started only after the main process gains a D-Bus name.
5. notify : This type is similar to simple, but consequent units are started only after a notification message is sent via the sd_notify() function.
6. idle : Similar to simple, the acutal execution of the service binary is delayed until all jobs are finished, which avoids mixing the status output with shell output of services.
ExecStartSpecifies commands or scripts to be executed when the unit is started. ExecStartPre and ExecStartPost specify custom commands to be executed before and after ExecStart. Type=oneshot enables specifying multiple custom commands that are then executed sequentially.
ExecStopSpecifies commands or scripts to be executed when the unit is stopped.
ExecReloadSpecifies commands or scripts to be executed when the unit is reloaded.
RestartWith this option enabled, the service is restarted after its process exits, with the exception of a clean stop by the systemctl command.
RemainAfterExitIf set to True, the service is considered active even when all its processes exited. Default value is False. This option is especially useful if Type=oneshot is configured.

[a]: 要查看关于[Service]部分的完整选项列表,参见systemd.service (5) 的帮助手册,即执行命令man 5 systemd.service即可。

Table3: Important [Install] Section options

Option[a]Description
AliasProvides a space-separated list of additional names for the unit. Most systemctl commands, excluding systemctl enable, can use alias instead of the actual unit name.
RequiredByA list of units that depend on the unit. When this unit is enabled, the unit listed in RequiredBy gain a Require dependency on the unit.
WantedByA list of units that weakly depend on the unit. When this unit is enabled, the units listed in WantedBy gain a Want dependency on the unit.
AlsoSpecifies a list of units to be installed or uninstalled along with the unit.
DefaultInstanceLimited to instantiated units, this option specifies the default instance for which the unit is enabled.

[a]: 关于[Install]更详细的选项列表,参见systemd.unit (5) 的帮助手册,即执行命令man 5 systemd.unit即可。

关于这三部分的内容组织,以sshd.service单元文件的内容为例,具体如下图所示:
在这里插入图片描述上图的[Unit]部分指定了sshd服务的基本描述信息,以及这个服务的帮助文档获取方式;同时指定了该服务的以来关系,即sshd服务依赖network.target和sshd-keygen.target这两个单元,需要在这两个单元就绪之后才能启动sshd服务;也通过 Wants 显式声明了该服务需要ssh-keygen.target这个单元。
在上图的[Service]部分,指定了服务的进程启动类型受 ExecStart 选项的影响方式,notify的含义是该服务的进程被 ExecStart 启动之后以主进程的形式存在,随后的单元只是在通过 sd_notify() 函数发送了通知消息之后才会启动。ExecStart 指定了服务的启动命令,ExecReload 指定了重新装载配置文件时需要执行的操作(参见man 5 systemd.service的输出);KillMode 指定了进程应该以怎样的方式被关闭,process表示只有主进程会被杀掉(关于KillMode的帮助信息,参见man 5 systemd.kill的输出)。Restart表示在什么情况下需要重启该服务,on-failure的含义是当服务的进程返回非零退出状态码的时候,才会重启该服务的主进程(比如当服务的重新装载配置文件操作超时或者触发了看门狗超时的时候,该选项的更详细的信息,参见man systemd.service的输出);RestartSec表示重启服务之前的睡眠时间,以秒为单位,默认100秒。另外,还通过选项 EnvironmentFile 定义了两个环境变量文件,该选项指定的值应该是文件的绝对路径,或者通配符表达式,加上前缀英文半角连字符"-"的时候,表示如果指定的文件不存在,就不再尝试读取该文件,同时不会记录错误或者警告信息(关于该选项的更详细的信息,参见man 5 systemd.exec的输出)。
上图中[Install]部分指定了选项 WantedBy,表示在执行systemctl enable命令将该服务设置为开机自动运行的时候,该服务所依赖的其他单元,此处指定的值为 multi-user.target,表示在开机自动运行该服务之前,系统需要先运行 multi-user.target 这个单元之后,才会启动该服务。

2.7.2. 创建自定义的单元文件(unit files)

要创建自定义的服务单元文件,一般的步骤如下所示:

  1. 准备自定义服务的可执行文件。可以是自己写的脚本,也可以是软件提供者提供的可执行程序。如果有必要,可以准备PID文件,用来存放自定义服务的主进程的PID信息,也可以通过环境变量文件给自定义服务提供shell变量。请确保脚本或者程序具有执行权限,如果没有执行权限,需要执行chmod a+x给其增加执行权限。

  2. /etc/systemd/system/ 目录或者 /usr/lib/systemd/system/ 目录中创建自定义服务的单元文件(unit files),同时需要注意文件的权限。具体如下所示:

    创建自定义服务的单元文件:

    # touch /etc/systemd/system/name.service
    # chmod 644 /etc/systemd/system/name.service
    或者
    # touch /usr/lib/systemd/system/name.service
    # chmod 644 /usr/lib/systemd/system/name.service
    

    上述的两种形式都是可以的。

  3. 用文本编辑器打开前面创建的单元文件,并添加对应的服务配置选项。下面的简单示例如下:

    [Unit]
    Description=service_description
    After=network.target
    [Service]
    ExecStart=path_to_executable
    Type=forking
    PIDFile=path_to_pidfile
    [Install]
    WantedBy=default.target

    在上述的内容中,service_description 部分会显示在systemctl status命令输出的日志文件中。After 确保这个自定义服务在network.target启动之后才会运行,如果依赖多个其他单元,那么可以在 After 选项的后面通过空格将多个单元分隔。path_to_executable 代表了能够启动服务的脚本或者程序。Type=forking 代表 ExecStart 选项中启动的服务会调用fork()程序启动子进程,当子进程启动完成,并且所有的通信通道都设置完成之后,此时就应该退出父进程。此时子进程作为服务的主进程持续运行,如果使用了forking的形式,那么也需要提供 PIDFile 来保存服务的PID信息,以便systemd能够识别到这个服务的进程。WantedBy 这个自定义服务所依赖的其他单元。

  4. 通知systemd守护进程,有新的自定义服务被添加进来。

    以root身份执行如下命令:

    # systemctl daemon-reload
    # systemctl start name.service
    

    警告: 在每次增加了新的单元文件或者修改了既有的单元文件之后,都应该执行systemctl daemon-reload命令。否则执行systemctl start或者systemctl enable命令的时候就无法识别新增或修改的内容。

下面通过示例演示自定义服务的单元文件的步骤和过程。仍然以sshd这个服务为例,创建sshd服务的第二个单元文件,并使用不同的服务端口号。具体过程如下所示:

  1. 复制既有的sshd服务的配置文件

    [root@c7u6-ha1 ~]# cp /etc/ssh/sshd{,-second}_config
    [root@c7u6-ha1 ~]# ll /etc/ssh/sshd*_config
    -rw------- 1 root root 3908 Jun  8 14:14 /etc/ssh/sshd_config
    -rw------- 1 root root 3908 Jun  8 14:17 /etc/ssh/sshd-second_config
    [root@c7u6-ha1 ~]#
    
  2. 编辑上面的sshd-second_config这个配置文件,修改其中的服务端口号和PID文件

    [root@c7u6-ha1 ~]# vim /etc/ssh/sshd-second_config
    [root@c7u6-ha1 ~]# cat /etc/ssh/sshd-second_config | egrep '2212|PidFile'
    Port 2212
    PidFile /var/run/sshd-second.pid
    [root@c7u6-ha1 ~]#
    

    关于sshd_config的配置选项,详细信息参见 sshd_config (5) 的帮助信息,执行命令man 5 sshd_config即可。
    上面在第二个配置文件中指定的PID文件无需实现创建,在重启sshd服务的时候会自动创建这个PID文件。

  3. 拷贝既有的sshd.service单元文件

    [root@c7u6-ha1 ~]# cp /usr/lib/systemd/system/sshd{,-second}.service
    [root@c7u6-ha1 ~]# ll /usr/lib/systemd/system/sshd*.service
    -rw-r--r--. 1 root root 313 Apr 11  2018 /usr/lib/systemd/system/sshd-keygen.service
    -rw-r--r--  1 root root 373 Jun  8 14:25 /usr/lib/systemd/system/sshd-second.service
    -rw-r--r--. 1 root root 373 Apr 11  2018 /usr/lib/systemd/system/sshd.service
    -rw-r--r--. 1 root root 260 Apr 11  2018 /usr/lib/systemd/system/sshd@.service
    [root@c7u6-ha1 ~]#
    

    上述是将sshd.service直接在/usr/lib/systemd/system/这个目录中复制了一份,也可以将其复制到/etc/systemd/system/这个目录中。RHEL官方文档建议系统管理员应该将自定义的服务单元文件放置在/etc/systemd/system/这个目录中。

  4. 修改此前复制的sshd-second.service这个单元文件
    a. 修改Description选项

    Description=OpenSSH server second instance daemon
    b. 将sshd.service添加为该服务的依赖服务,即sshd-second.service需要在sshd.service之后启动
    After=syslog.target network.target auditd.service sshd.service
    c. 由于第一个sshd.service中已经包含了密钥生成,所以在第二个服务中需要将密钥生成部分移除
    ExecStartPre=/usr/sbin/sshd-keygen
    移除上面这一行
    d. 添加-f /etc/ssh/ssh-second_config这个参数
    ExecStart=/usr/sbin/sshd -D -f /etc/ssh/ssh-second_config $OPTIONS
    执行了上述修改之后,此时sshd-second.service文件的内容如下所示:

    [root@c7u6-ha1 ~]# cat /usr/lib/systemd/system/sshd-second.service
    [Unit]
    Description=OpenSSH server second instance daemon
    Documentation=man:sshd(8) man:sshd_config(5)
    After=network.target sshd-keygen.service sshd.service
    Wants=sshd-keygen.service
    
    [Service]
    Type=notify
    EnvironmentFile=/etc/sysconfig/sshd
    ExecStart=/usr/sbin/sshd -D -f /etc/ssh/sshd-second_config $OPTIONS
    ExecReload=/bin/kill -HUP $MAINPID
    KillMode=process
    Restart=on-failure
    RestartSec=42s
    
    [Install]
    WantedBy=multi-user.target
    [root@c7u6-ha1 ~]#
    

    至此,sshd-second.service这个单元文件就准备好了。

  5. 使能sshd-second.service,使其能够在下次开机的时候自动启动

    重新装载配置文件,并设置开机自动启动,具体如下所示:

    [root@c7u6-ha1 ~]# systemctl daemon-reload
    [root@c7u6-ha1 ~]# systemctl enable --now sshd-second.service
    Created symlink from /etc/systemd/system/multi-user.target.wants/sshd-second.service to /usr/lib/systemd/system/sshd-second.service.
    [root@c7u6-ha1 ~]#
    

    上述使用systemctl daemon-reload通知服务进程重新装载配置文件,也可以执行init q命令,具有同样的效果。做完上述设置之后,查看系统是否有2212端口处于监听状态。

    [root@c7u6-ha1 ~]# ss -ntl | egrep 2212
    LISTEN     0      128          *:2212                     *:*
    LISTEN     0      128         :::2212                    :::*
    [root@c7u6-ha1 ~]#
    

    在宿主机上开启另一个终端,并尝试使用上面的2212号端口登录虚拟机,具体如下所示:

    [root@LiuXianQiE ~]# ssh -p 2212 root@c7u6-ha1
    Last login: Tue Jun  8 14:19:19 2021 from 192.168.122.1
    [root@c7u6-ha1 ~]#
    

    从上述输出中可以看出,可以通过2212号端口正常登录虚拟机。

上述即为创建自定义单元文件的过程。

2.7.3. 修改既存单元文件

已经安装到系统上的服务,会将自带的默认单元文件存放在/usr/llib/systemd/system/这个目录中。一般不建议系统管理员直接修改这个目录中的单元文件,应该尽量将自定义修改放置在/etc/systemd/system/这个目录中。具体操作方式,取决于所要求更改的程度,不同的修改程度,对应于下面的两种方式:

  • 在/etc/systemd/system/中创建一个单元文件对应的目录,命名为name.unit.d的形式,然后将.conf结尾的配置文件放置在这个目录中,比如 /etc/systemd/system/httpd.service.d/httpd_supplementary.conf 这种形式。对于绝大多数情况,这种方式都是适用的,可以实现对既有单元文件的拓展,即增量的形式进行配置,这个配置文件中无需包含完整的配置信息,只需要包含增量或者需要修改的内容即可。

    使用这种方式的过程如下:

    # mkdir /etc/systemd/system/name.service.d/
    # touch /etc/systemd/system/name.service.d/config_name.conf
    

    然后将相应的补充的或者修改的配置选项写入到上述的config_name.conf这个文件中,其形式跟单元文件的内容一样,也包括[Unit], [Service]和[Install]三部分。

    # cat /etc/systemd/system/name.service.d/config_name.conf
    [Unit]
    Requires=new_dependency
    After=new_dependency
    [Service]
    Restart=always
    RestartSec=30
    

    上述就创建了补充的配置文件,在执行systemctl restart name.service之前,一定要先执行systemctl daemon-reload命令重新装载配置文件。
    下面以sshd.service这个单元文件为例,通过上述的方式自定义sshd服务。
    a. 创建服务对应的目录并在其中增加对应的配置文件

    [root@c7u6-ha1 ~]# mkdir /etc/systemd/system/sshd.service.d
    [root@c7u6-ha1 ~]# vim /etc/systemd/system/sshd.service.d/custom_supplementary.conf                                                             
    [root@c7u6-ha1 ~]# cat /etc/systemd/system/sshd.service.d/custom_supplementary.conf     
    [Unit]
    Description=Customize in /etc/systemd/system/sshd.service.d/custom-supplementary.conf                                                        
    [Service]
    ExecStartPost=/root/custom-sshd.sh
    

    b. 创建所需要的脚本,custom-sshd.sh

    [root@c7u6-ha1 ~]# vim custom-sshd.sh
    [root@c7u6-ha1 ~]# cat custom-sshd.sh 
    #!/bin/bash
    
    echo -e '\E\033[1;33mThis is test for customize exist sshd service\E\033[0m'
    [root@c7u6-ha1 ~]# 
    

    c. 重启服务并查看服务状态

    [root@c7u6-ha1 ~]# systemctl daemon-reload
    [root@c7u6-ha1 ~]# systemctl restart sshd.service
    Job for sshd.service failed because the control process exited with error code. See "systemctl status sshd.service" and "journalctl -xe" for details.
    [root@c7u6-ha1 ~]# chmod +x custom-sshd.sh 
    [root@c7u6-ha1 ~]# systemctl restart sshd.service
    [root@c7u6-ha1 ~]# systemctl status sshd.service
    . sshd.service - Customize in /etc/systemd/system/sshd.service.d/custom-supplementary.conf
       Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)
      Drop-In: /etc/systemd/system/sshd.service.d
               └─custom_supplementary.conf
       Active: active (running) since Tue 2021-06-08 16:15:43 CST; 3s ago
         Docs: man:sshd(8)
               man:sshd_config(5)
      Process: 6004 ExecStartPost=/root/custom-sshd.sh (code=exited, status=0/SUCCESS)
     Main PID: 6003 (sshd)
       CGroup: /system.slice/sshd.service
               └─6003 /usr/sbin/sshd -D
    
    Jun 08 16:15:43 c7u6-ha1 systemd[1]: Stopped Customize in /etc/systemd/system/sshd.service.d/custom-supplementary.conf.
    Jun 08 16:15:43 c7u6-ha1 systemd[1]: Starting Customize in /etc/systemd/system/sshd.service.d/custom-supplementary.conf...
    Jun 08 16:15:43 c7u6-ha1 sshd[6003]: Server listening on 0.0.0.0 port 22.
    Jun 08 16:15:43 c7u6-ha1 sshd[6003]: Server listening on :: port 22.
    Jun 08 16:15:43 c7u6-ha1 custom-sshd.sh[6004]: [58B blob data]
    Jun 08 16:15:43 c7u6-ha1 systemd[1]: Started Customize in /etc/systemd/system/sshd.service.d/custom-supplementary.conf.
    [root@c7u6-ha1 ~]# 
    

    从上述输出中可以看出,脚本需要加上执行权限,否则无法重启服务。
    另外,***/etc/systemd/system/目录中的对应服务的配置文件比/usr/lib/systemd/system/目录中的对应服务的优先级更高,从上述输出中可以看出,在配置文件中定义的Description的信息覆盖了以前的默认描述信息。

  • 从/usr/lib/systemd/system/这个目录中复制一份单元文件到/etc/systemd/system/这个目录中,并更改这个副本,这个拷贝的单元文件会忽略掉原始的单元文件。这种方式适用于忽略掉服务本身的设置,而将一些不受服务更新与否的配置写在这个单元文件中。注意:这种方式是存量扩展,即拷贝过来的单元文件需要包含服务正常运行所需要的完整内容,在此基础上进行增加或者修改。如果只保留增加或者修改的部分,删除掉其他部分,则此时会导致服务无法正常启动。因为这个单元文件的优先级比/usr/lib/systemd/system/httpd.service这个文件的优先级更高,所以会忽略掉默认的单元文件。

    如果想要使更改信息在更新软件包之后,仍然能保持生效,就需要将服务安装时的默认单元文件拷贝到/etc/systemd/system/这个目录中,具体如下所示:

    # cp /usr/lib/systemd/system/name.service /etc/systemd/system/name.service
    

    然后修改上述的name.service文件,修改完成之后,执行下面的操作重启服务即可。

    # systemctl daemon-reload
    # systemctl restart name.service
    

    下面以httpd.service为例进行示例:
    a. 将httpd.service文件从/usr/lib/systemd/system/目录中拷贝到/etc/systemd/system/这个目录中,并修改其内容:

    [root@c7u6-ha1 ~]# cp /usr/lib/systemd/system/httpd.service /etc/systemd/system                                                                 
    [root@c7u6-ha1 ~]# vim /etc/systemd/system/httpd.service
    [root@c7u6-ha1 ~]# cat /etc/systemd/system/httpd.service
    [Unit]
    Description=The Apache Service Configuration in /etc/systemd/system/httpd.service                                                               
    
    [Service]
    TimeoutStartSec=5
    

    b. 重启服务

    [root@c7u6-ha1 ~]# systemctl daemon-reload
    [root@c7u6-ha1 ~]# systemctl restart httpd.service
    Failed to restart httpd.service: Unit is not loaded properly: Invalid argument.                                                                 
    See system logs and 'systemctl status httpd.service' for details.
    [root@c7u6-ha1 ~]# systemctl status httpd.service
    ● httpd.service - The Apache Service Configuration in /etc/systemd/system/httpd.service                                                        
       Loaded: error (Reason: Invalid argument)
       Active: inactive (dead)
    
    Jun 08 16:33:19 c7u6-ha1 systemd[1]: httpd.service lacks both ExecStart= and ExecStop= setting. Refusing.                                       
    Jun 08 16:34:13 c7u6-ha1 systemd[1]: httpd.service lacks both ExecStart=    > and ExecStop= setting. Refusing.
    

    从上述输出中可以看出,提示缺少ExecStart以及ExecStop的相关选项,因为上面将拷贝过来的文件中的这部分内容都删除了,只留下了修改的或者增加的内容。这种方式是不可以的,必须要保证完整的内容,然后在其上进行修改才可以。
    重新拷贝文件,并对拷贝过来的文件增量修改:

    [root@c7u6-ha1 ~]# cp /usr/lib/systemd/system/httpd.service /etc/systemd/system/                                                                
    cp: overwrite ‘/etc/systemd/system/httpd.service’? y
    [root@c7u6-ha1 ~]# vim /etc/systemd/system/httpd.service
    [root@c7u6-ha1 ~]# cat /etc/systemd/system/httpd.service
    [Unit]
    Description=The Apache Configuration in /etc/systemd/system/httpd.service
    After=network.target remote-fs.target nss-lookup.target
    Documentation=man:httpd(8)
    Documentation=man:apachectl(8)
    
    [Service]
    Type=notify
    EnvironmentFile=/etc/sysconfig/httpd
    ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND
    ExecReload=/usr/sbin/httpd $OPTIONS -k graceful
    ExecStop=/bin/kill -WINCH ${MAINPID}
    TimeoutStartSec=5
    # We want systemd to give httpd some time to finish gracefully, but still want
    # it to kill httpd after TimeoutStopSec if something went wrong during the
    # graceful stop. Normally, Systemd sends SIGTERM signal right after the
    # ExecStop, which would kill httpd. We are sending useless SIGCONT here to give
    # httpd time to finish.
    KillSignal=SIGCONT
    PrivateTmp=true
    
    [Install]
    WantedBy=multi-user.target
    [root@c7u6-ha1 ~]# systemctl daemon-reload
    [root@c7u6-ha1 ~]# systemctl restart httpd.service
    [root@c7u6-ha1 ~]# systemctl status httpd.service
    . httpd.service - The Apache Configuration in /etc/systemd/system/httpd.service
       Loaded: loaded (/etc/systemd/system/httpd.service; disabled; vendor preset: disabled)
       Active: active (running) since Tue 2021-06-08 16:35:48 CST; 3s ago
         Docs: man:httpd(8)
               man:apachectl(8)
     Main PID: 6199 (httpd)
       Status: "Processing requests..."
       CGroup: /system.slice/httpd.service
               ├─6199 /usr/sbin/httpd -DFOREGROUND
               ├─6200 /usr/sbin/httpd -DFOREGROUND
               ├─6201 /usr/sbin/httpd -DFOREGROUND
               ├─6202 /usr/sbin/httpd -DFOREGROUND
               ├─6203 /usr/sbin/httpd -DFOREGROUND
               └─6204 /usr/sbin/httpd -DFOREGROUND
    
    Jun 08 16:35:48 c7u6-ha1 systemd[1]: Starting The Apache Configuration in /etc/systemd/system/httpd.service...
    Jun 08 16:35:48 c7u6-ha1 httpd[6199]: AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 192... message
    Jun 08 16:35:48 c7u6-ha1 systemd[1]: Started The Apache Configuration in /etc/systemd/system/httpd.service.
    Hint: Some lines were ellipsized, use -l to show in full.
    [root@c7u6-ha1 ~]# 
    

    至此,服务就可以正常工作了,并且Description的内容确实发生改变了。

3. 重置root用户密码

关于root用户密码重置,有两种方式:第一种是通过live光盘进行修改; 第二种是通过修改grub信息实现修改,而这种方式又对应两种不同的内核参数,分别为rd.break和init指定交互的shell解释器。
这两种方式分别介绍如下,以Fedora 34为例:

3.1. 通过live光盘重置root用户密码

  1. 启动虚拟机,并在开始的时候选择按照提示进入boot菜单,如下图所示:
    在这里插入图片描述在上图中按下ESC键进入boot菜单,弹出boot菜单,如下图所示:
    在这里插入图片描述选择上图的第2项从光驱启动系统。在下图的引导界面中选择第一个,即"Start Fedora-Workstation-Live 34"
    在这里插入图片描述进入到下图所示的界面,选择"Try Fedora",具体如下图所示:
    在这里插入图片描述打开terminal,然后执行如下图所示操作,具体如下图所示:
    在这里插入图片描述2. 重启系统,即可输入重置后的密码进行登录
    在这里插入图片描述再上图中选择第一个,正常启动系统即可。
    在这里插入图片描述在上图中输入用户名root
    在这里插入图片描述在上图中输入重置后的密码,即可登录系统,在下图中打开终端,如下图所示:
    在这里插入图片描述在这里插入图片描述登录成功。

3.2. 通过修改grub引导中的内核参数重置root用户密码

这里根据在grub引导菜单中使用的内核参数不同,又分为两种方式,这两种方式不拘泥于具体的发行版,只要是采用GRUB2引导的Linux系统,理论上都可以使用下面的两种方式实现root用户密码重置。分别如下:

3.2.1. 通过rd.break内核参数重置root用户密码

通过在启动的时候,出现GRUB的引导菜单的时候,按e键即可编辑内核选项。具体如下图所示:
在这里插入图片描述再上图按e键,进入下图所示界面:
在这里插入图片描述在上图的linux开始的行中,将末尾的rhgb quit去掉,然后增加rd.break,同时将原来的ro修改为rw,即将根分区以读写的方式挂载到/sysroot这个目录上,然后按下Ctrl+x键即可继续启动系统,具体如下图所示:
在这里插入图片描述在下图中按下Ctrl+d键,即可进入shell交互命令行,由于此时系统真正的根分区挂载在/sysroot上,所以需要将其以读写的方式重新进行挂载,然后执行命令chroot /sysroot切根,此时的上下文环境才是真正的根分区。然后在命令行中执行命令passwd root重置root用户密码。如果原来的系统没有开启SELinux的话,那么此时执行exit退出切根环境,并输入reboot命令重启的话,就可以实现密码重置的操作了。但由于我这个Fedora是新安装的系统,且并没有关闭SELinux,所以,还需要再执行一下touch /.autorelabel这个命令,提示SELinux重新加载标签。在下次重启的时候,如果系统的本地文件系统中文件很多,这个过程所需要的时间会比较长。重启之后,输入root用户名,就可以使用重置后的密码进行登录了。具体如下图所示:
在这里插入图片描述如果忘记上图中根分区被挂载到哪个目录下,可以在上图的命令行中执行命令mount,即可显示出根目录挂载的路径。上图重启完之后,输入root用户名和密码,就可以使用重置后的密码登录系统了。具体如下图所示:
在这里插入图片描述上图是SELinux打标签的过程。
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述至此,通过修改grub,增加rd.break这个内核参数实现的root密码重置操作就完成了。

3.2.2. 通过init内核参数重置root用户密码

这种方式整体流程跟上一种方式类似,只不过是使用的init这个内核参数,修改登录过程中的shell解释器。
在grub的引导菜单中,将linux开头的行中的ro修改为rw,然后去掉末尾的rhgb quiet,然后增加init=/sysroot/bin/bash,具体如下图所示:
在这里插入图片描述在上图中按下Ctrl+x继续启动系统,在下图中按下Ctrl+d进入到我们在内核中指定的shell解释器交互界面,如下图所示:
在这里插入图片描述进入到shell之后,执行chroot /sysroot命令切根,然后执行命令passwd root修改root用户的登录密码,如果原来的系统中没有开启SELinux,那么此时执行exit退出切根环境,并执行reboot重启即可完成root用户密码重置的操作。但是由于我的Fedora 34是新安装的系统,并未禁用SELinux,所以,此时必须要执行touch /.autorelabel命令给修改后的根分区中的配置文件重新打标签才可以使修改生效。如果省略掉这步,而系统又恰好开启了SELinux,那么上述的密码重置操作时不会生效的。具体如下图所示:
在这里插入图片描述SELinux给文件系统中的文件打标签。这个过程耗时长短取决于本地文件系统中文件的多少,文件越多,耗时越长。
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述至此,root用户密码重置的操作就完成了。

4. References

  1. An introduction to the Linux boot and startup processes
  2. systemd bootup
  3. File:GNU GRUB on MBR partitioned hard disk drives.svg
  4. Red_Hat_Enterprise_Linux-7-System_Administrators_Guide-en-US.pdf
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值