本文共 14529 字,大约阅读时间需要 48 分钟。
Linux操作系统的开机过程是这样的,即从BIOS开始,然后进入Boot Loader,再加载系统内核,然后内核进行初始化,最后启动初始化进程。初始化进程作为Linux系统的第一个进程,它需要完成Linux系统中相关的初始化工作,为用户提供合适的工作环境。RHEL 7、CentOS7等linux发行版系统已经替换掉了熟悉的初始化进程服务System V init,正式采用全新的systemd初始化进程服务。systemd初始化进程服务采用了并发启动机制,开机速度得到了不小的提升。
systemd即为system daemon,是linux下的一种init软件,由Lennart Poettering带头开发,并在LGPL 2.1及其后续版本许可证下开源发布,开发目标是提供更优秀的框架以表示系统服务间的依赖关系,并依此实现系统初始化时服务的并行启动,同时达到降低Shell的系统开销的效果,最终代替现在常用的System V与BSD风格init程序。
systemd是一个专用于 Linux 操作系统的系统与服务管理器。当作为启动进程(PID=1)运行时,它将作为初始化系统运行,也就是启动并维护各种用户空间的服务。 为了与传统的 SysV 兼容,如果将 systemd 以 init 名称启动,并且"PID≠1",那么它将执行 telinit 命令并将所有命令行参数原封不动的传递过去。 这样对于普通的登录会话来说,无论是调用 init 还是调用 telinit 都是等价的。当作为系统实例运行时,systemd将会按照system.conf配置文件以及system.conf.d配置目录中的指令工作;当作为用户实例运行时,systemd 将会按照user.conf配置文件 以及 user.conf.d配置目录中的指令工作。systemd将各种系统启动和运行相关的对象,表示为各种不同类型的单元(unit),并提供了处理不同单元之间依赖关系的能力。大部分单元都静态的定义在单元文件中,但是有少部分单元则是动态自动生成的:其中一部分来自于其他传统的配置文件(为了兼容性),而另一部分则动态的来自于系统状态或可编程的运行时状态。单元既可以处于活动(active)状态,也可以处于停止(inactive)状态,当然也可以处于启动中(activating)或停止中(deactivating)的状态。还有一个特殊的失败(failed)状态,意思是单元以某种方式失败了(进程崩溃了、或者触碰启动频率限制、或者退出时返回了错误代码、或者遇到了操作超时之类的故障)。当进入失败(failed)状态时,导致故障的原因将被记录到日志中以方便日后排查。需要注意的是,不同的单元可能还会有各自不同的"子状态",但它们都被映射到上述五种状态之一。systemd的优点是功能强大、使用方便,缺点是体系庞大、非常复杂。事实上,现在还有很多人反对使用systemd,理由就是它过于复杂,与操作系统的其他部分强耦合,违反"keep simple, keep stupid"的Unix哲学。与以前的发行版使用的System V风格init相比,systemd采用了以下新技术:(1) 采用Socket激活式与总线激活式服务,以提高相互依赖的各服务的并行运行性能;(2)用Cgroups代替PID来追踪进程,以此即使是两次fork之后生成的守护进程也不会脱离systemd的控制。
无论怎样,RHEL 7系统选择systemd初始化进程服务已经是一个既定事实,因此也没有了“运行级别”这个概念,Linux系统在启动时要进行大量的初始化工作,比如挂载文件系统和交换分区、启动各类进程服务等,这些都可以看作是一个一个的单元(Unit),systemd用目标(target)代替了System V init中运行级别的概念,这两者的区别如下所示。表1:System V init和systemd的区别System V init运行级别 | systemd目标名称 | 作用 |
---|---|---|
0 | runlevel0.target -> poweroff.target | 关机 |
1 | runlevel1.target -> rescue.target | 单用户模式 |
2 | runlevel2.target -> multi-user.target | 等同于级别3 |
3 | runlevel3.target -> multi-user.target | 多用户的文本界面 |
4 | runlevel4.target -> multi-user.target | 等同于级别3 |
5 | runlevel5.target -> graphical.target | 多用户的图形界面 |
6 | runlevel6.target -> reboot.target | 重启 |
emergency | emergency.target | 紧急shell |
每个Unit都有一个配置文件,告诉Systemd怎么启动这个Unit。Systemd默认从目录/etc/systemd/system/读取配置文件,但是里面存放的大部分文件都是符号链接,指向目录/usr/lib/systemd/system/或者/lib/systemd/system/,Unit的配置文件都存放在这个目录里面。
例如:将系统默认的运行模式修改为“多用户,无图形”,可直接用ln命令把多用户模式目标文件链接到/etc/systemd/system/目录ln -sf /lib/systemd/system/multi-user.target /etc/systemd/system/default.target
在RHEL6及之前的发行版系统是使用service、chkconfig等命令来管理系统服务,在RHEL7系统中是使用systemctl命令来管理服务的。我们来看一下它们之间的区别。表2:服务的启动、重启、停止等常用命令对比 System V init命令 | systemctl命令 | 作用 |
---|---|---|
service foo start | systemctl start foo.service | 启动服务 |
service foo restart | systemctl restart foo.service | 重启服务 |
service foo stop | systemctl stop foo.service | 停止服务 |
service foo reload | systemctl reload foo.service | 重新加载配置文件(不停止服务) |
service foo status | systemctl status foo.service | 查看服务状态 |
表3:设置服务开机启动、不启动等常用命令对比
System V init命令 | systemctl命令 | 作用 |
---|---|---|
chkconfig foo on | systemctl enable foo.service | 开机自动启动 |
chkconfig foo off | systemctl disable foo.service | 开机不自动启动 |
chkconfig foo | systemctl is-enabled foo.service | 查看服务是否为自动启动 |
chkconfig --list | systemctl list-unit-files | 查看各个服务的启动与禁用情况 |
(1)systemctl是systemd的主命令,用于管理系统和服务。
重启系统systemctl reboot
关闭系统,切断电源systemctl poweroff
CPU停止工作systemctl halt
暂停系统systemctl suspend
让系统进入冬眠状态systemctl hibernate
让系统进入交互式休眠状态systemctl hybrid-sleep
启动进入救援状态(单用户状态)systemctl rescue
(2)systemd-analyze命令用于查看启动耗时。查看系统启动耗时systemd-analyze
查看每个服务的启动耗时systemd-analyze blame
显示瀑布状的系统启动过程流systemd-analyze critical-chain
显示指定服务的启动流systemd-analyze critical-chain atd.service
(3)hostnamectl命令用于查看当前主机的信息。显示当前主机的信息hostnamectl
设置主机名。hostnamectl set-hostname xuad1
(4)localectl命令用于查看本地化设置。查看本地化设置localectl
设置本地化参数。 localectl set-locale LANG=zh_CN.UTF-8localectl set-keymap zh_CN
(5)timedatectl命令用于查看当前系统时区设置。
查看当前时区设置timedatectl
显示所有可用的时区timedatectl list-timezones
设置当前时区 timedatectl set-timezone Asia/Shanghaitimedatectl set-time YYYY-MM-DDtimedatectl set-time HH:MM:SS
(6)loginctl命令用于查看当前登陆的用户。
列出当前sessionloginctl list-sessions
列出当前登录用户loginctl list-users
列出显示指定用户的信息loginctl show-user root
Systemd可以管理所有系统资源。不同的资源统称为Unit(单位),Unit一共分成12种。
Service unit:系统服务Target unit:多个Unit构成的一个组Device Unit:硬件设备Mount Unit:文件系统的挂载点Automount Unit:自动挂载点Path Unit:文件或路径Scope Unit:不是由Systemd启动的外部进程Slice Unit:进程组Snapshot Unit:Systemd快照,可以切回某个快照Socket Unit:进程间通信的socketSwap Unit:swap文件Timer Unit:定时器
systemctl list-units命令可以查看系统的所有Unit信息。
列出已启动的Unitsystemctl list-units
列出所有Unit,包括没有找到配置文件的或者启动失败的systemctl list-units --all
列出所有没有启动的Unitsystemctl list-units --all --state=inactive
列出所有启动失败的Unitsystemctl list-units --failed
列出所有正在运行的、类型为service的Unitsystemctl list-units --type=service
systemctl status命令用于查看系统状态和单个Unit的状态。显示系统状态systemctl status
显示单个 Unit 的状态sysystemctl status httpd.service
显示远程主机的nginx服务的状态systemctl -H root@192.168.2.204 status nginx.service
除了status外,systemctl还提供了三个查询状态的简单方法,主要供脚本内部的判断语句使用。显示某个 Unit 是否启动systemctl is-active application.service
显示某个 Unit 是否启动失败systemctl is-failed application.service
显示某个 Unit 服务是否开机启动systemctl is-enabled application.service
systemctl常用命令立即启动一个服务systemctl start httpd.service
立即停止一个服务systemctl stop httpd.service
重启一个服务systemctl restart httpd.service
杀死一个服务的所有子进程systemctl kill httpd.service
重新加载一个服务的配置文件systemctl reload httpd.service
重载所有修改过的配置文件systemctl daemon-reload
显示某个 Unit 的所有底层参数systemctl show httpd.service
显示某个 Unit 的指定属性的值systemctl show -p CPUShares httpd.service
设置某个 Unit 的指定属性systemctl set-property httpd.service CPUShares=500
Unit的后缀名.service可以省略,例如启动ssh的命令systemctl start sshd.service可以写成systemctl start sshd。Unit之间存在依赖关系,A依赖于B,就意味着systemd在启动A的时候,同时会去启动B。systemctl list-dependencies命令列出一个Unit的所有依赖。systemctl list-dependencies nginx.service
上面命令的输出结果之中,有些依赖是Target类型,默认不会展开显示。如果要展开Target,就需要使用--all参数。systemctl list-dependencies --all nginx.service
systemctl list-unit-files命令用于列出所有Unit的状态。列出所有Unit状态systemctl list-unit-files
列出指定类型的Unit状态systemctl list-unit-files --type=service
Unit的四种状态 enabled:开机启动disabled:不开机启动static:该Unit没有[Install]部分(无法执行),只能作为其他Unit的依赖masked:该Unit被禁止开机启动
如果修改了某个服务的配置文件,就要重新加载配置,然后重新启动,否则修改不会生效。
systemctl daemon-reloadsystemctl restart httpd.service
系统运行模式target相关命令
查看当前系统的所有Targetsystemctl list-unit-files --type=target
查看一个 Target 包含的所有Unitsystemctl list-dependencies multi-user.target
查看启动时的默认Targetsystemctl get-default
设置启动时的默认Targetsystemctl set-default multi-user.target
切换Target时,默认不关闭前一个Target启动的进程,systemctl isolate命令改变这种行为,关闭前一个Target里面所有不属于后一个Target的进程systemctl isolate multi-user.target
systemd统一管理所有Unit的启动日志,可以只用journalctl一个命令查看所有日志(内核日志和应用日志),它的配置文件是/etc/systemd/journald.conf。
查看所有日志(默认情况下 ,只保存本次启动的日志)journalctl
查看内核日志(不显示应用日志)journalctl -k
查看系统本次启动的日志 journalctl -bjournalctl -b -0
查看上一次启动的日志(需更改设置)
journalctl -b -1
查看指定时间的日志 journalctl --since="2012-10-30 18:17:16"journalctl --since "20 min ago"journalctl --since yesterdayjournalctl --since "2015-01-10" --until "2015-01-11 03:00"journalctl --since 09:00 --until "1 hour ago"
显示尾部的最新10行日志
journalctl -n
显示尾部指定行数的日志journalctl -n 20
实时滚动显示最新日志journalctl -f
查看指定服务的日志journalctl /usr/lib/systemd/systemd
查看指定进程的日志journalctl _PID=1
查看某个路径的脚本的日志journalctl /usr/bin/bash
查看指定用户的日志journalctl _UID=33 --since today
查看某个 Unit 的日志 journalctl -u nginx.servicejournalctl -u nginx.service --since today
实时滚动显示某个 Unit 的最新日志
journalctl -u nginx.service -f
合并显示多个 Unit 的日志journalctl -u nginx.service -u php-fpm.service --since today
查看指定优先级(及其以上级别)的日志,共有8级 0: emerg1: alert2: crit3: err4: warning5: notice6: info7: debug
journalctl -p err -b
journalctl --no-pager
以 JSON 格式(单行)输出journalctl -b -u nginx.service -o json
以 JSON 格式(多行)输出,可读性更好journalctl -b -u nginx.service -o json-pretty
显示日志占据的硬盘空间journalctl --disk-usage
指定日志文件占据的最大空间journalctl --vacuum-size=1G
指定日志文件保存多久journalctl --vacuum-time=1years
前面已经讲过,配置一个服务开机启动的命令是systemctl enable <服务名>.service,实际上只是在/etc/systemd/system/目录下做了个文件链接而已。
systemctl enable httpd.service等同于ln -s '/usr/lib/systemd/system/httpd.service' '/etc/systemd/system/httpd.service'
Unit的配置文件的内容,一般会有以下三个区块,具体含义如下
[Unit]区块通常是配置文件的第一个区块,用来定义Unit的元数据,以及配置与其他Unit的关系,它的主要字段如下:Description:简单描述Documentation:服务的启动文件和配置文件Requires:当前Unit依赖的其他Unit,如果它们没有运行,当前Unit会启动失败Wants:与当前Unit配合的其他Unit,如果它们没有运行,不影响当前Unit的启动BindsTo:与Requires类似,它指定的Unit如果退出,会导致当前Unit停止运行Before:如果该字段指定的Unit也要启动,那么必须在当前Unit之后启动After:如果该字段指定的Unit也要启动,那么必须在当前Unit之前启动Conflicts:这里指定的Unit不能与当前Unit同时运行Condition...:当前Unit运行必须满足的条件,否则不会运行Assert...:当前Unit运行必须满足的条件,否则会报启动失败
[Service]区块配置,只有Service类型的Unit才有这个区块,它的主要字段如下:
Type:定义启动时的进程行为,它有以下几种值。Type=simple:默认值,执行ExecStart指定的命令,启动主进程Type=forking:以fork方式从父进程创建子进程,之后父进程会退出,子进程成为主进程Type=oneshot:一次性进程,Systemd会等当前服务退出,再继续往下执行Type=dbus:当前服务通过D-Bus启动Type=notify:当前服务启动完毕,会通知Systemd,再继续往下执行Type=idle:若有其他任务,则其他任务执行完毕,当前服务才会运行ExecStart:启动当前服务的命令ExecStartPre:启动当前服务之前执行的命令ExecStartPost:启动当前服务之后执行的命令ExecReload:重启当前服务时执行的命令ExecStop:停止当前服务时执行的命令ExecStopPost:停止当其服务之后执行的命令RestartSec:自动重启当前服务间隔的秒数Restart:定义何种情况Systemd会自动重启当前服务,可能的值包括always(总是重启)、on-success、on-failure、on-abnormal、on-abort、on-watchdogTimeoutSec:定义Systemd停止当前服务之前等待的秒数Environment:指定环境变量
[Install]通常是配置文件的最后一个区块,用来定义运行模式(Target)、Unit别名等设置,以及是否开机启动,它的主要字段如下:
WantedBy:它的值是一个或多个Target,当前Unit激活时(enable)时,符号链接会放入/etc/systemd/system目录下面以Target名+.wants后缀构成的子目录中RequiredBy:它的值是一个或多个Target,当前Unit激活时,符号链接会放入/etc/systemd/system目录下面以Target名+.required后缀构成的子目录中Alias:当前Unit可用于启动的别名Also:当前Unit激活(enable)时,会被同时激活的其他Unit
Unit配置文件的完整字段清单,请参考官方文档。
systemctl cat命令可以用来查看配置文件,那么我们来看一下sshd配置文件的内容,分析下它每项配置的含义是什么。systemctl cat sshd.service
[Unit]Description=OpenSSH server daemon # 当前服务的简单描述Documentation=man:sshd(8) man:sshd_config(5) # sshd是启动脚本,sshd_config是配置文件After=network.target sshd-keygen.service # 启动ssh服务之前会先启动这两个UnitWants=sshd-keygen.service # 此Unit启动成功与否不影响ssh服务的正常启动[Service]Type=notify # ssh服务启动成功后会通知systemd,再启动其他依赖服务EnvironmentFile=/etc/sysconfig/sshd # 指定ssh服务的环境参数配置文件ExecStart=/usr/sbin/sshd -D $OPTIONS # 启动ssh服务执行的命令ExecReload=/bin/kill -HUP $MAINPID # 重启ssh服务执行的命令KillMode=process # process表示只停止主进程,不停止子进程Restart=on-failure # 进程非正常退出时,包括信号终止和超时,会重启服务RestartSec=42s # 上面Restart重启之前需要等待42秒再重启[Install]WantedBy=multi-user.target # ssh服务所在的系统运行模式
[Unit]区块:启动顺序与依赖关系
After字段定义哪些服务在ssh服务启动之前启动,Before字段定义哪些服务在ssh服务启动之后启动,After和Before字段只涉及启动顺序,不涉及依赖关系。举例来说,某web应用需要postgresql数据库储存数据,在配置文件中只定义了postgresql在该web应用之前启动,而没有定义依赖关系,由于某种原因postgresql需要重新启动,在postgresql停止期间,该web应用就会无法建立数据库连接。设置依赖关系,需要使用Wants字段和Requires字段。Wants字段表示sshd.service与sshd-keygen.service之间属于“弱依赖”关系,即sshd-keygen.service启动失败或者停止运行,不影响sshd.service的运行。Requires字段则表示“强依赖”关系,即如果该服务启动失败或者停止运行,那么sshd.service也不会启动。注意:Wants字段与Requires字段只涉及依赖关系,与启动顺序无关,默认情况下是同时启动的。[Service]区块:启动、停止、重启行为EnvironmentFile字段:指定ssh服务的环境参数配置文件,该文件内部的key=value键值对,可以用$key的形式,在当前配置文件中获取。ExecStart字段定义了启动ssh服务的命令是/usr/sbin/sshd -D $OPTIONS,其中的变量$OPTIONS就来自EnvironmentFile字段定义的环境参数配置文件。所有的启动设置前面都可以加上一个符号(-),表示“抑制错误”,即发生错误也不影响其他命令的执行。比如,EnvironmentFile=-/etc/sysconfig/sshd表示即使/etc/sysconfig/sshd文件不存在,也不会抛出错误。Type字段定义启动类型,Type=notify表示ssh服务启动后会向Systemd发出通知信号,然后Systemd再启动其他服务。举个例子,笔记本电脑启动时,要把触摸板关掉,配置文件可以这样写。[Unit]Description=Switch-off Touchpad[Service]Type=oneshotExecStart=/usr/bin/touchpad-off[Install]WantedBy=multi-user.target
Type设置为oneshot时,就表示这个服务只要运行一次就够了,不需要长期运行。
如果关闭后,将来某个时候还想打开,配置文件修改如下。[Unit]Description=Switch-off Touchpad[Service]Type=oneshotExecStart=/usr/bin/touchpad-off startExecStop=/usr/bin/touchpad-off stopRemainAfterExit=yes[Install]WantedBy=multi-user.target
RemainAfterExit字段设置为yes,表示进程退出后,服务仍然保持执行。这样的话,一旦使用systemctl stop命令停止服务,ExecStop定义的命令就是执行,从而重新开启触摸板。
KillMode字段设为process表示只停止主进程,不停止任何子进程,即子进程打开的ssh session仍然保持连接。这个设置不太常见,但对ssh服务很重要,否则你停止服务的时候,会连自己打开的ssh session一起杀掉。KillMode字段可以设置的值如下:control-group(默认值):当前控制组里面的所有子进程,都会被杀掉process:只杀主进程mixed:主进程将收到SIGTERM信号,子进程收到SIGKILL信号none:没有进程会被杀掉,只是执行服务的stop命令。
Restart设为on-failure表示任何意外的失败,都将重启ssh服务。如果ssh服务正常停止(比如执行systemctl stop sshd.service命令),ssh服务就不会重启。
Restart字段可以设置的值如下:no(默认值):退出后不会重启on-success:只有正常退出时(退出状态码为0),才会重启on-failure:非正常退出时(退出状态码非0),包括被信号终止和超时,才会重启on-abnormal:只有被信号终止和超时,才会重启on-abort:只有在收到没有捕捉到的信号终止时,才会重启on-watchdog:超时退出,才会重启always:不管是什么退出原因,总是重启
对于需要守护的进程,推荐设为on-failure;对于那些允许发生错误退出的服务,可以设为on-abnormal。
RestartSec表示Systemd重启服务之前,需要等待的秒数。[Install]区块:定义服务启动的系统运行模式,即该服务在哪个运行模式下开机启动。WantedBy定义服务所在的Target,WantedBy=multi-user.target指的是ssh服务所在的Target是multi-user.target(多用户命令行模式)。当我们执行systemctl enable sshd.service命令时,sshd.service的一个符号链接,就会放在/etc/systemd/system目录下面的multi-user.target.wants子目录之中。Systemd有默认启动的Target,multi-user.target(多用户命令行模式)。上图的结果表示,在multi-user.target.wants目录下面的所有服务,都将开机启动,这就是为什么systemctl enable命令能设置开机启动的原因。查看 multi-user.target 包含的所有服务systemctl list-dependencies multi-user.target
切换到另一个 targetshutdown.target 就是关机状态systemctl isolate shutdown.target
前面已经讲过systemd的系统运行目标有哪些,我们经常用到的有两个:一个是multi-user.target,表示多用户命令行状态;另一个是graphical.target,表示图形用户状态,它依赖于multi-user.target。官方文档有一张非常清晰的Target依赖关系图。 Target的配置文件存放在/lib/systemd/system/目录下,我们来看一下multi-user.target的内容。
systemctl cat multi-user.target
Requires=basic.target:要求basic.target一起运行。Conflicts字段:冲突字段,如果rescue.service或rescue.target正在运行,multi-user.target就不能运行,反之亦然。After:表示multi-user.target在basic.target、rescue.service、rescue.target之后启动,如果它们有启动的话。AllowIsolate:允许使用systemctl isolate命令切换到multi-user.target。注意:修改配置文件以后,需要重新加载配置文件,然后重新启动相关服务。重新加载配置文件systemctl daemon-reload
重启相关服务systemctl restart foo
我们首先编写一个xuad.service配置文件,功能为启动、重启、停止nginx服务,放在/usr/lib/systemd/system/目录下,内容如下:
vim /usr/lib/systemd/system/xuad.service
[Unit]Description=xuad server[Service]Type=forkingPIDFile=/run/nginx.pidExecStartPre=/usr/bin/rm -f /run/nginx.pidEnvironmentFile=/etc/sysconfig/xuadExecStart=/usr/sbin/nginxExecStartPost=/bin/echo $OPTIONSExecReload=/bin/kill -s HUP $MAINPIDKillSignal=SIGQUITExecStopPost=/bin/echo stop[Install]WantedBy=multi-user.target
接下来再编写一个xuad文件,放在/etc/sysconfig/目录下,内容如下:
OPTIONS=Start
这样我们相当于定义了一个xuad服务。首先确认nginx是否启动。systemctl status nginx.service
通过上图可以看到运行状态为inactive,很明显nginx未运行。接下来我们来启动xuad.service这个服务,前面有讲过.service可以省略。 systemctl start xuadsystemctl status xuad # 查看xuad服务状态很明显xuad服务已经成功启动,并且成功打印Start字符串,接下来我们确认一下nginx是否已经启动。
ps aux | grep nginx
通过上图可以确认nginx已经成功启动。接下来我们重启xuad.service这个服务,看看结果如何。 systemctl restart xuadsystemctl status xuad # status只能看到启动成功后的状态journalctl -u xuad.service -f # 通过日志来看重启结果通过上图我们看到成功打印stop和Start两个字符串,说明重启nginx成功。最后我们停止xuad.service,看看结果如何。
systemctl stop xuadsystemctl status xuad运行状态已经变成inactive(未运行),并且成功打印stop字符串,说明停止nginx成功。这时执行ps aux | grep nginx应该看不到nginx的进程了。本文所有命令参考文献:在此感谢这位作者,谢谢!
转载于:https://blog.51cto.com/andyxu/2122109