最近在设置Linux系统时,我想知道如何确保服务和其他单元的依赖项在这些依赖的服务和单元启动之前已经建立并运行。 具体来说,我需要更多有关systemd如何管理启动顺序的知识,尤其是在确定从本质上是并行系统中启动订单服务的过程中。

您可能知道SystemV(systemd的前身,正如我在本系列文章的第一篇中所解释的)通过使用SXX前缀命名启动脚本来对启动顺序进行排序,其中XX是从00到99的数字。然后,SystemV使用排序顺序按名称,并按所需的运行级别依次运行每个启动脚本。

但是systemd使用可以由sysadmin创建或修改的单位文件来定义子例程,这些例程不仅用于初始化,还用于常规操作。 在本系列的第三篇文章中,我解释了如何创建安装单元文件。 在第五篇文章中,我将演示如何创建其他类型的单元文件-一种在启动时运行程序的服务单元文件。 您还可以在单位文件中更改某些配置设置,并使用systemd日志查看更改在启动顺序中的位置。

制备

确保已从/etc/default/grub文件中的GRUB_CMDLINE_LINUX=行中删除了rhgb和quiet ,如本系列第二篇文章中所述。 这使您能够观察Linux启动消息流,本文中的一些实验将需要它。

该程序

在本教程中,您将创建一个简单的程序,使您可以在启动过程中在控制台上以及稍后在systemd日志中观察消息。

创建外壳程序/usr/local/bin/hello.sh并添加以下内容。 您希望确保结果在启动期间可见,并且在浏览systemd日记时可以轻松找到它。 您将使用“ Hello world”程序的版本,该程序周围带有一些小节,因此非常突出。 确保该文件是可执行文件,并具有root用户和组所有权,并具有700个安全权限 :

#!/usr/bin/bash 
     
     
# Simple program to use for testing startup configurations 
     
     
# with systemd. 
     
     
# By David Both 
     
     
# Licensed under GPL V2 
     
     
# 
     
     
echo 
     
     "###############################" 
     
     
echo 
     
     "######### Hello World! ########" 
     
     
echo 
     
     "###############################"

从命令行运行此程序以验证其是否正常运行:

[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     # hello.sh  
     
     
############################### 
     
     
######### Hello World! ######## 
     
     
############################### 
     
     
[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     #

该程序可以用任何脚本或编译语言创建。 基于Linux文件系统层次结构 (FHS), hello.sh程序也可以位于其他位置。 我将其放在/usr/local/bin目录中,以便可以轻松地从命令行运行它,而无需在键入命令时添加路径。 我发现我创建的许多Shell程序都需要从命令行和其他工具(例如systemd)运行。

服务单位文件

使用以下内容创建服务单元文件/etc/systemd/system/hello.service 。 该文件不需要是可执行文件,但是为了安全起见,它确实需要具有root和644或640权限的用户和组所有权:

# Simple service unit file to use for testing  
     
     
# startup configurations with systemd. 
     
     
# By David Both 
     
     
# Licensed under GPL V2 
     
     
# 
     
     

[ Unit 
     
     ] 
     
     
Description =My hello shell script
     
     

[ Service 
     
     ] 
     
     
Type =oneshot
     
     
ExecStart = 
     
     / usr 
     
     / local 
     
     / bin 
     
     / hello.sh 
     
     

[ Install 
     
     ] 
     
     
WantedBy =multi-user.target

通过查看服务状态,验证服务单元文件是否按预期执行。 任何语法错误将显示在这里:

[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     # systemctl status hello.service  
     
     

● hello.service - My hello shell script
     
     

     Loaded: loaded 
     
     ( 
     
     / etc 
     
     / systemd 
     
     / system 
     
     / hello.service; disabled; vendor preset: disabled 
     
     ) 
     
     

     Active: inactive 
     
     ( dead 
     
     ) 
     
     
[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     #

您可以多次运行此“ oneshot”服务类型,而不会出现问题。 oneshot类型适用于服务,其中由服务单元文件启动的程序是主要进程,并且必须在systemd启动任何相关进程之前完成。

有七种服务类型,您可以在systemd.service(5)手册页中找到每种服务的说明(以及服务单元文件的其他部分) 。 (您也可以在本文结尾处的资源中找到更多信息。)

像我一样好奇,我想看看错误可能是什么样的。 因此,我从Type=oneshot行中删除了“ o”,看起来像Type=neshot ,然后再次运行了命令:

[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     # systemctl status hello.service 
     
     

● hello.service - My hello shell script
     
     

     Loaded: loaded 
     
     ( 
     
     / etc 
     
     / systemd 
     
     / system 
     
     / hello.service; disabled; vendor preset: disabled 
     
     ) 
     
     

     Active: inactive 
     
     ( dead 
     
     ) 
     
     


May 06 08: 
     
     50 :09 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : 
     
     / etc 
     
     / systemd 
     
     / system 
     
     / hello.service: 
     
     12 : Failed to parse service 
     
     type , ignoring: neshot
     
     
[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     #

这些结果准确地告诉了我错误的出处,并且非常容易解决问题。

hello.service文件恢复其原始形式,该错误将继续存在。

尽管重新启动可以清除错误,但您不必这样做,因此我一直在寻找一种方法来清除此类永久性错误。 我遇到了服务错误,需要使用命令systemctl daemon-reload重置错误条件,但是在这种情况下不起作用。

可以用此命令修复的错误消息似乎总是有一条表明该效果的语句,因此您知道要运行它。

但是,建议您在更改单位文件或创建新文件后运行systemctl daemon-reload 。 这会通知systemd已经进行了更改,并且可以防止管理更改的服务或单元时出现某些类型的问题。 继续运行此命令。

纠正服务单元文件中的拼写错误后,简单的systemctl restart hello.service清除了该错误。 通过在hello.service文件中引入一些其他错误来进行hello.service以查看获得什么样的结果。

启动服务

现在您可以启动新服务并检查状态以查看结果。 尽管您可能在上一节中进行了重新启动,但是您可以根据需要多次启动或重新启动oneshot服务,因为该服务运行一次然后退出。

继续并启动服务(如下所示),然后检查状态。 根据您尝试错误的程度,您的结果可能与我的不同:

[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     # systemctl start hello.service  
     
     
[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     # systemctl status hello.service  
     
     

● hello.service - My hello shell script
     
     

     Loaded: loaded 
     
     ( 
     
     / etc 
     
     / systemd 
     
     / system 
     
     / hello.service; disabled; vendor preset: disabled 
     
     ) 
     
     

     Active: inactive 
     
     ( dead 
     
     ) 
     
     


May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org hello.sh 
     
     [ 
     
     842 
     
     ] : 
     
     ######### Hello World! ######## 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org hello.sh 
     
     [ 
     
     842 
     
     ] : 
     
     ############################### 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : hello.service: Succeeded.
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Finished My hello shell script.
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting My hello shell script...
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ############################### 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ######### Hello World! ######## 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ############################### 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : hello.service: Succeeded.
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Finished My hello shell script.
     
     
[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     #

注意,在status命令的输出中,systemd消息指示hello.sh脚本已启动且服务已完成。 您还可以查看脚本的输出。 该显示是根据服务的最新调用的日记条目生成的。 尝试多次启动该服务,然后再次运行status命令以了解我的意思。

您还应该直接查看日记的内容。 有多种方法可以做到这一点。 一种方法是指定记录类型标识符,在这种情况下为外壳程序脚本的名称。 这显示了先前重新引导以及当前会话的日志条目。 如您所见,我已经为这篇文章进行了一段时间的研究和测试:

[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     # journalctl -t hello.sh 
     
     
< snip 
     
     > 
     
     
-- Reboot 
     
     -- 
     
     

May 08 
     
     15 : 
     
     55 : 
     
     47 testvm1.both.org hello.sh 
     
     [ 
     
     840 
     
     ] : 
     
     ############################### 
     
     

May 08 
     
     15 : 
     
     55 : 
     
     47 testvm1.both.org hello.sh 
     
     [ 
     
     840 
     
     ] : 
     
     ######### Hello World! ######## 
     
     

May 08 
     
     15 : 
     
     55 : 
     
     47 testvm1.both.org hello.sh 
     
     [ 
     
     840 
     
     ] : 
     
     ############################### 
     
     
-- Reboot 
     
     -- 
     
     

May 08 
     
     16 :01: 
     
     51 testvm1.both.org hello.sh 
     
     [ 
     
     840 
     
     ] : 
     
     ############################### 
     
     

May 08 
     
     16 :01: 
     
     51 testvm1.both.org hello.sh 
     
     [ 
     
     840 
     
     ] : 
     
     ######### Hello World! ######## 
     
     

May 08 
     
     16 :01: 
     
     51 testvm1.both.org hello.sh 
     
     [ 
     
     840 
     
     ] : 
     
     ############################### 
     
     
-- Reboot 
     
     -- 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org hello.sh 
     
     [ 
     
     842 
     
     ] : 
     
     ############################### 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org hello.sh 
     
     [ 
     
     842 
     
     ] : 
     
     ######### Hello World! ######## 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org hello.sh 
     
     [ 
     
     842 
     
     ] : 
     
     ############################### 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ############################### 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ######### Hello World! ######## 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ############################### 
     
     
[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     #

要找到hello.service单元的systemd记录,可以在systemd上搜索。 您可以使用G + Enter键转到日记条目的末尾,然后向后滚动以找到您感兴趣的条目。使用-b选项仅显示最近一次启动的条目:

[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     # journalctl -b -t systemd 
     
     
< snip 
     
     > 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting SYSV: Late init script 
     
     for live image....
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Started SYSV: Late init script 
     
     for live image..
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : hello.service: Succeeded.
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Finished My hello shell script.
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     50 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting D-Bus System Message Bus...
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     50 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Started D-Bus System Message Bus.

我复制了其他一些日记条目,以使您对可能找到的内容有所了解。 此命令将生成与systemd有关的所有日记行,即我撰写本文时的109,183行。 那是要整理的大量数据。 您可以使用寻呼机的搜索工具(通常less ,也可以使用内置的grep功能。 -g (或--grep= )选项使用与Perl兼容的正则表达式:

[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     # journalctl -b -t systemd -g "hello" 
     
     
[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     # journalctl -b -t systemd -g "hello" 
     
     
-- Logs begin at Tue 
     
     2020 -05-05 
     
     18 : 
     
     11 : 
     
     49 EDT, end at Sun 
     
     2020 -05- 
     
     10 
     
     11 :01:01 EDT. 
     
     -- 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting My hello shell script...
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : hello.service: Succeeded.
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Finished My hello shell script.
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting My hello shell script...
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : hello.service: Succeeded.
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Finished My hello shell script.
     
     
[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     #

您可以使用标准的GNU grep命令,但是不会在第一行中显示日志元数据。

如果您不想仅查看与您的hello服务相关的日记条目,则可以通过指定时间范围来缩小范围。 例如,我将从测试VM的开始时间10:54:00开始,这是上述条目来自的分钟的开始。 请注意,-- --since=选项必须用引号引起来,并且该选项也可以表示为-S "<time specification>" 。

您的主机上的日期和时间将有所不同,因此请确保使用与日记中的时间匹配的时间戳:

[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     # journalctl --since="2020-05-10 10:54:00" 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     35 testvm1.both.org audit: BPF prog-id= 
     
     54 
     
     op =LOAD
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     35 testvm1.both.org audit: BPF prog-id= 
     
     55 
     
     op =LOAD
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting My hello shell script...
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ############################### 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ######### Hello World! ######## 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ############################### 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : hello.service: Succeeded.
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Finished My hello shell script.
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org audit 
     
     [ 
     
     1 
     
     ] : SERVICE_START 
     
     pid = 
     
     1 
     
     uid = 
     
     0 
     
     auid = 
     
     4294967295 
     
     ses = 
     
     4294967295 
     
     msg = 
     
     'unit=hello comm="systemd" exe="/usr/lib/systemd"' 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org audit 
     
     [ 
     
     1 
     
     ] : SERVICE_STOP 
     
     pid = 
     
     1 
     
     uid = 
     
     0 
     
     auid = 
     
     4294967295 
     
     ses = 
     
     4294967295 
     
     msg = 
     
     'unit=hello comm="systemd" exe="/usr/lib/systemd/"' 
     
     

May 
     
     10 
     
     10 : 
     
     56 :00 testvm1.both.org NetworkManager 
     
     [ 
     
     840 
     
     ] : 
     
     < error 
     
     > 
     
     [ 
     
     1589122560.0633 
     
     ] dhcp4 
     
     ( enp0s3 
     
     ) : error 
     
     -113 dispatching events
     
     

May 
     
     10 
     
     10 : 
     
     56 :00 testvm1.both.org NetworkManager 
     
     [ 
     
     840 
     
     ] : 
     
     < info 
     
     >  
     
     [ 
     
     1589122560.0634 
     
     ] dhcp4 
     
     ( enp0s3 
     
     ) : state changed bound - 
     
     > fail
     
     
< snip 
     
     >

since规范会跳过该时间之前的所有条目,但是在那之后,仍有许多不需要的条目。 您还可以使用“ until选项来剪裁您感兴趣的时间之后出现的条目。我想要事件发生时的一整分钟,仅此而已:

[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     # journalctl --since="2020-05-10 10:54:35" --until="2020-05-10 10:55:00" 
     
     
-- Logs begin at Tue 
     
     2020 -05-05 
     
     18 : 
     
     11 : 
     
     49 EDT, end at Sun 
     
     2020 -05- 
     
     10 
     
     11 :04: 
     
     59 EDT. 
     
     -- 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     35 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Reloading.
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     35 testvm1.both.org audit: BPF prog-id= 
     
     27 
     
     op =UNLOAD
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     35 testvm1.both.org audit: BPF prog-id= 
     
     26 
     
     op =UNLOAD
     
     
< snip 
     
     > 
     
     

ay 
     
     10 
     
     10 : 
     
     54 : 
     
     35 testvm1.both.org audit: BPF prog-id= 
     
     55 
     
     op =LOAD
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting My hello shell script...
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ############################### 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ######### Hello World! ######## 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ############################### 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : hello.service: Succeeded.
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Finished My hello shell script.
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org audit 
     
     [ 
     
     1 
     
     ] : SERVICE_START 
     
     pid = 
     
     1 
     
     uid = 
     
     0 
     
     auid = 
     
     4294967295 
     
     ses = 
     
     4294967295 
     
     msg = 
     
     'unit=hello comm="systemd" exe="/usr/lib/systemd>

May 10 10:54:45 testvm1.both.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg=' 
     
     unit =hello 
     
     comm = 
     
     "systemd" 
     
     exe = 
     
     "/usr/lib/systemd/>

lines 1-46/46 (END)

如果在这段时间内有很多活动,则可以使用以下选项的组合来进一步缩小结果数据流的范围:

[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     # journalctl --since="2020-05-10 10:54:35" --until="2020-05-10 10:55:00" -t "hello.sh" 
     
     
-- Logs begin at Tue 
     
     2020 -05-05 
     
     18 : 
     
     11 : 
     
     49 EDT, end at Sun 
     
     2020 -05- 
     
     10 
     
     11 : 
     
     10 : 
     
     41 EDT. 
     
     -- 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ############################### 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ######### Hello World! ######## 
     
     

May 
     
     10 
     
     10 : 
     
     54 : 
     
     45 testvm1.both.org hello.sh 
     
     [ 
     
     1380 
     
     ] : 
     
     ############################### 
     
     
[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     #

您的结果应该与我的相似。 从这一系列实验中,您可以看到该服务已正确执行。

重启-最后

到目前为止,您尚未重启安装服务的主机。 现在就这样做,因为毕竟,此方法是关于在启动时运行程序。 首先,您需要启用服务以在启动过程中启动:

[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     # systemctl enable hello.service  
     
     

Created symlink 
     
     / etc 
     
     / systemd 
     
     / system 
     
     / multi-user.target.wants 
     
     / hello.service → 
     
     / etc 
     
     / systemd 
     
     / system 
     
     / hello.service.
     
     
[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     #

请注意,该链接是在/etc/systemd/system/multi-user.target.wants目录中创建的。 这是因为服务单元文件指定了multi-user.target “需要”该multi-user.target 。

重新启动,并确保在启动过程中观看数据流以查看“ Hello world”消息。 等等...你没看到吗? 好吧,我也没有。尽管进展非常Swift,但我确实看到systemd的消息,它正在启动hello.service 。

查看自最新系统启动以来的日志。 您可以使用less寻呼机的搜索工具来查找“ Hello”或“ hello”。 我修剪了许多行数据,但留下了一些周围的日记帐分录,因此您可以感觉到与您的服务有关的分录在本地是什么样的:

[ root 
     
     @ testvm1 ~ 
     
     ] 
     
     # journalctl -b 
     
     
< snip 
     
     > 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Listening on SSSD Kerberos Cache Manager responder socket.
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Reached target Sockets.
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Reached target Basic System.
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting Modem Manager...
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting Network Manager...
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting Avahi mDNS 
     
     / DNS-SD Stack...
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Condition check resulted 
     
     in Secure Boot DBX 
     
     ( blacklist 
     
     ) updater being skipped.
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting My hello shell script...
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting IPv4 firewall with iptables...
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Started irqbalance daemon.
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org audit 
     
     [ 
     
     1 
     
     ] : SERVICE_START 
     
     pid = 
     
     1 
     
     uid = 
     
     0 
     
     auid = 
     
     4294967295 
     
     ses = 
     
     4294967295 
     
     msg = 
     
     'unit=irqbalance comm="systemd" exe="/usr/lib/sy>"' 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting LSB: Init script 
     
     for live image....
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting Hardware Monitoring Sensors...
     
     
< snip 
     
     > 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting NTP client 
     
     / server...
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting SYSV: Late init script 
     
     for live image....
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Started SYSV: Late init script 
     
     for live image..
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org audit 
     
     [ 
     
     1 
     
     ] : SERVICE_START 
     
     pid = 
     
     1 
     
     uid = 
     
     0 
     
     auid = 
     
     4294967295 
     
     ses = 
     
     4294967295 
     
     msg = 
     
     'unit=livesys-late comm="systemd" exe="/usr/lib/>"' 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org hello.sh 
     
     [ 
     
     842 
     
     ] : 
     
     ############################### 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org hello.sh 
     
     [ 
     
     842 
     
     ] : 
     
     ######### Hello World! ######## 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org hello.sh 
     
     [ 
     
     842 
     
     ] : 
     
     ############################### 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : hello.service: Succeeded.
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Finished My hello shell script.
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org audit 
     
     [ 
     
     1 
     
     ] : SERVICE_START 
     
     pid = 
     
     1 
     
     uid = 
     
     0 
     
     auid = 
     
     4294967295 
     
     ses = 
     
     4294967295 
     
     msg = 
     
     'unit=hello comm="systemd" exe="/usr/lib/systemd>"' 
     
     

May 
     
     10 
     
     10 : 
     
     37 : 
     
     49 testvm1.both.org audit 
     
     [ 
     
     1 
     
     ] : SERVICE_STOP 
     
     pid = 
     
     1 
     
     uid = 
     
     0 
     
     auid = 
     
     4294967295 
     
     ses = 
     
     4294967295 
     
     msg = 
     
     'unit=hello comm="systemd" exe="/usr/lib/systemd/>

May 10 10:37:50 testvm1.both.org audit: BPF prog-id=28 op=LOAD

<snip>

您可以看到systemd启动了hello.service单元,该单元运行了hello.sh Shell脚本,并将输出记录在日志中。 如果能够在引导过程中捕获它,那么您还将看到systemd消息表明它正在启动脚本,而另一条消息表明服务已成功。 通过查看上面数据流中的第一条systemd消息,您可以看到systemd在达到基本系统目标后不久就启动了服务。

但是我也希望看到启动时显示的消息。 有一种方法可以实现: hello.service添加到hello.service文件的[Service]部分:

StandardOutput =journal+console 
StandardOutput =journal+console

hello.service文件现在如下所示:

# Simple service unit file to use for testing  
     
     
# startup configurations with systemd. 
     
     
# By David Both 
     
     
# Licensed under GPL V2 
     
     
# 
     
     

[ Unit 
     
     ] 
     
     
Description =My hello shell script
     
     

[ Service 
     
     ] 
     
     
Type =oneshot
     
     
ExecStart = 
     
     / usr 
     
     / local 
     
     / bin 
     
     / hello.sh 
     
     
StandardOutput =journal+console
     
     

[ Install 
     
     ] 
     
     
WantedBy =multi-user.target

添加此行后,重新引导系统,并在引导过程中观察数据流在显示屏上滚动时的状态。 您应该在其小框中看到该消息。 启动序列完成后,您可以查看最近一次启动的日志并找到新服务的条目。

改变顺序

现在您的服务正在运行,您可以查看它在启动顺序中的开始位置,并尝试进行更改。 :重要的是要记住,systemd的意图是在平行内各主要指标开始尽可能多的服务和其他单位类型是很重要的basic.target , multi-user.target和graphical.target 。 您应该已经看到了最近一次引导的日志条目,该条目看起来应该类似于上面输出中的日志。

请注意,systemd到达目标基本系统后不久便开始了您的测试服务。 这是您在WantedBy行的服务单元文件中指定的WantedBy ,因此它是正确的。 更改任何内容之前,请列出/etc/systemd/system/multi-user.target.wants目录的内容,然后您会看到指向服务单元文件的符号(软)链接。 服务单元文件的[Install]部分指定哪个目标将启动该服务,然后运行systemctl enable hello.service命令在相应的“ target want”目录中创建链接:

hello.service - > / etc / systemd / system / hello.service 
hello.service - > / etc / systemd / system / hello.service

某些服务需要时启动basic.target ,和别人不需要启动,除非系统启动graphical.target 。 在这个实验中,该服务将不会在启动basic.target -assume你不需要它启动,直到graphical.target 。 因此,更改WantedBy行:

WantedBy =graphical.target 
WantedBy =graphical.target

一定要禁用hello.service和重新启用它来删除旧的链接,并添加在新一graphical.targets.wants目录。 我注意到,如果在更改所需的目标之前忘了禁用服务,则可以运行systemctl disable命令,链接将从两个“目标想要”目录中删除。 然后,我只需要重新启用服务并重新启动即可。

在开始服务的一个问题graphical.target是,如果主机靴子multi-user.target ,这项服务将不会自动启动。 如果该服务需要GUI桌面界面,那可能就是您想要的,但也可能不是您想要的。

使用-o short-monotonic选项查看graphical.target和多用户multi-user.target的日志条目,该选项在内核启动后以秒为单位显示秒数:

[ root @ testvm1 ~ ] # journalctl -b -o short-monotonic 
[ root @ testvm1 ~ ] # journalctl -b -o short-monotonic

multi-user.target一些结果:

[   
     
     17.264730 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting My hello shell script...
     
     
[   
     
     17.265561 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting IPv4 firewall with iptables...
     
     
< SNIP 
     
     > 
     
     
[   
     
     19.478468 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting LSB: Init script 
     
     for live image....
     
     
[   
     
     19.507359 
     
     ] testvm1.both.org iptables.init 
     
     [ 
     
     844 
     
     ] : iptables: Applying firewall rules: 
     
     [  OK  
     
     ] 
     
     
[   
     
     19.507835 
     
     ] testvm1.both.org hello.sh 
     
     [ 
     
     843 
     
     ] : 
     
     ############################### 
     
     
[   
     
     19.507835 
     
     ] testvm1.both.org hello.sh 
     
     [ 
     
     843 
     
     ] : 
     
     ######### Hello World! ######## 
     
     
[   
     
     19.507835 
     
     ] testvm1.both.org hello.sh 
     
     [ 
     
     843 
     
     ] : 
     
     ############################### 
     
     
< SNIP 
     
     > 
     
     
[   
     
     21.482481 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : hello.service: Succeeded.
     
     
[   
     
     21.482550 
     
     ] testvm1.both.org smartd 
     
     [ 
     
     856 
     
     ] : Opened configuration 
     
     file 
     
     / etc 
     
     / smartmontools 
     
     / smartd.conf
     
     
[   
     
     21.482605 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Finished My hello shell script.

以及有关graphical.target一些结果:

[   
     
     19.436815 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting My hello shell script...
     
     
[   
     
     19.437070 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting IPv4 firewall with iptables...
     
     
< SNIP 
     
     > 
     
     
[   
     
     19.612614 
     
     ] testvm1.both.org hello.sh 
     
     [ 
     
     841 
     
     ] : 
     
     ############################### 
     
     
[   
     
     19.612614 
     
     ] testvm1.both.org hello.sh 
     
     [ 
     
     841 
     
     ] : 
     
     ######### Hello World! ######## 
     
     
[   
     
     19.612614 
     
     ] testvm1.both.org hello.sh 
     
     [ 
     
     841 
     
     ] : 
     
     ############################### 
     
     
[   
     
     19.629455 
     
     ] testvm1.both.org audit 
     
     [ 
     
     1 
     
     ] : SERVICE_START 
     
     pid = 
     
     1 
     
     uid = 
     
     0 
     
     auid = 
     
     4294967295 
     
     ses = 
     
     4294967295 
     
     msg = 
     
     'unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success' 
     
     
[   
     
     19.629569 
     
     ] testvm1.both.org audit 
     
     [ 
     
     1 
     
     ] : SERVICE_STOP 
     
     pid = 
     
     1 
     
     uid = 
     
     0 
     
     auid = 
     
     4294967295 
     
     ses = 
     
     4294967295 
     
     msg = 
     
     'unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success' 
     
     
[   
     
     19.629682 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : hello.service: Succeeded.
     
     
[   
     
     19.629782 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Finished My hello shell script.

尽管具有graphical.target在单位文件“想要”时, hello.service约19.5 19.6秒,进入启动单元运行。 但hello.service开始在大约17.24秒multi-user.target并在图形目标19.43秒。

这是什么意思? 查看/etc/systemd/system/default.target链接。 则该文件显示,systemd第一内容启动默认的目标, graphical.target ,然后在拉multi-user.target :

[ root 
     
     @ testvm1 system 
     
     ] 
     
     # cat default.target 
     
     
#  SPDX-License-Identifier: LGPL-2.1+ 
     
     
# 
     
     
#  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 =Graphical Interface
     
     
Documentation =man:systemd.special 
     
     ( 
     
     7 
     
     ) 
     
     
Requires =multi-user.target
     
     
Wants =display-manager.service
     
     
Conflicts =rescue.service rescue.target
     
     
After =multi-user.target rescue.service rescue.target display-manager.service
     
     
AllowIsolate = 
     
     yes 
     
     
[ root 
     
     @ testvm1 system 
     
     ] 
     
     #

无论是开始与服务graphical.target或multi-user.target的hello.service约19.5 19.6秒,进入启动单元运行。 基于此和日志结果(尤其是使用单调输出的结果),您知道这两个目标是同时开始的。 从日志输出中再看一件事:

[   
     
     28.397330 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Reached target Multi-User System.
     
     
[   
     
     28.397431 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Reached target Graphical Interface.

这两个目标几乎同时完成。 这是一致的,因为graphical.target在拉multi-user.target和无法完成,直到multi.user target达到,即成品。 但是hello.service的完成要早于此。

这意味着这两个目标几乎同时启动。 如果您浏览日记帐分录,您将看到来自每个主要目标的各种目标和服务,大多数同时开始。 显然,在graphical.target启动之前,不需要完成multi-user.target 。 因此,简单地使用这些主要目标测序启动不工作得很好,虽然它可以保证需要的只有当他们认为单位开始有用graphical.target 。

在继续之前,请将hello.service单位文件还原为WantedBy=multi-user.target (如果尚未)。

确保在网络运行后启动服务

常见的启动顺序问题是确保设备在网络启动并运行后启动。 Freedesktop.org文章在网络启动后运行服务表示,对于何时将网络视为“启动”还没有真正的共识。 但是,本文提供了三种选择,而满足完全运行网络需求的一种选择是network-online.target 。 请注意, network.target是在关闭过程中使用的,而不是在启动过程中使用的,因此当您尝试对启动进行排序时,它对您没有任何好处。

在进行任何其他更改之前,请确保检查日志并确认hello.service单元在网络运行之前已启动。 您可以在日志中查找network-online.target进行检查。

您的服务实际上并不需要网络服务,但您可以将其用作提供服务的化身。

因为设置WantedBy=graphical.target不能确保在网络启动并运行后启动该服务,所以您需要另一种方法来确保它能够启动。 幸运的是,有一个简单的方法可以做到这一点。 将以下两行添加到hello.service单元文件的[Unit]部分:

After =network-online.target                                                                             
     
     
Wants =network-online.target

这两项都是进行这项工作所必需的。 重新启动主机,然后在日志中查找服务条目的位置:

[   
     
     26.083121 
     
     ] testvm1.both.org NetworkManager 
     
     [ 
     
     842 
     
     ] : 
     
     < info 
     
     >  
     
     [ 
     
     1589227764.0293 
     
     ] device 
     
     ( enp0s3 
     
     ) : Activation: successful, device activated.
     
     
[   
     
     26.083349 
     
     ] testvm1.both.org NetworkManager 
     
     [ 
     
     842 
     
     ] : 
     
     < info 
     
     >  
     
     [ 
     
     1589227764.0301 
     
     ] manager: NetworkManager state is now CONNECTED_GLOBAL
     
     
[   
     
     26.085818 
     
     ] testvm1.both.org NetworkManager 
     
     [ 
     
     842 
     
     ] : 
     
     < info 
     
     >  
     
     [ 
     
     1589227764.0331 
     
     ] manager: startup 
     
     complete 
     
     
[   
     
     26.089911 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Finished Network Manager Wait Online.
     
     
[   
     
     26.090254 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Reached target Network is Online.
     
     
[   
     
     26.090399 
     
     ] testvm1.both.org audit 
     
     [ 
     
     1 
     
     ] : SERVICE_START 
     
     pid = 
     
     1 
     
     uid = 
     
     0 
     
     auid = 
     
     4294967295 
     
     ses = 
     
     4294967295 
     
     msg = 
     
     'unit=NetworkManager-wait-online comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? termina>"' 
     
     
[   
     
     26.091991 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting My hello shell script...
     
     
[   
     
     26.095864 
     
     ] testvm1.both.org sssd 
     
     [ be 
     
     [ implicit_files 
     
     ] 
     
     ] 
     
     [ 
     
     1007 
     
     ] : Starting up
     
     
[   
     
     26.290539 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Condition check resulted 
     
     in Login and scanning of iSCSI devices being skipped.
     
     
[   
     
     26.291075 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Reached target Remote File Systems 
     
     ( Pre 
     
     ) .
     
     
[   
     
     26.291154 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Reached target Remote File Systems.
     
     
[   
     
     26.292671 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Starting Notify NFS peers of a restart...
     
     
[   
     
     26.294897 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : iscsi.service: Unit cannot be reloaded because it is inactive.
     
     
[   
     
     26.304682 
     
     ] testvm1.both.org hello.sh 
     
     [ 
     
     1010 
     
     ] : 
     
     ############################### 
     
     
[   
     
     26.304682 
     
     ] testvm1.both.org hello.sh 
     
     [ 
     
     1010 
     
     ] : 
     
     ######### Hello World! ######## 
     
     
[   
     
     26.304682 
     
     ] testvm1.both.org hello.sh 
     
     [ 
     
     1010 
     
     ] : 
     
     ############################### 
     
     
[   
     
     26.306569 
     
     ] testvm1.both.org audit 
     
     [ 
     
     1 
     
     ] : SERVICE_START 
     
     pid = 
     
     1 
     
     uid = 
     
     0 
     
     auid = 
     
     4294967295 
     
     ses = 
     
     4294967295 
     
     msg = 
     
     'unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success' 
     
     
[   
     
     26.306669 
     
     ] testvm1.both.org audit 
     
     [ 
     
     1 
     
     ] : SERVICE_STOP 
     
     pid = 
     
     1 
     
     uid = 
     
     0 
     
     auid = 
     
     4294967295 
     
     ses = 
     
     4294967295 
     
     msg = 
     
     'unit=hello comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success' 
     
     
[   
     
     26.306772 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : hello.service: Succeeded.
     
     
[   
     
     26.306862 
     
     ] testvm1.both.org systemd 
     
     [ 
     
     1 
     
     ] : Finished My hello shell script.
     
     
[   
     
     26.584966 
     
     ] testvm1.both.org sm-notify 
     
     [ 
     
     1011 
     
     ] : Version 2.4.3 starting

这确认hello.service单元在network-online.target之后启动。 这正是您想要的。 在启动过程中,您可能还会看到“ Hello World”消息。 还要注意,时间戳在启动时比以前晚了大约六秒钟。

定义开始顺序的最佳方法

本文探讨了Linux的启动过程,其中更详细地介绍了systemd和unit文件以及日志,并发现了将错误引入服务文件时会发生的情况。 作为系统管理员,我发现这种类型的实验可以帮助我了解程序或服务在中断时的行为,有意中断是在安全环境中学习的一种好方法。

正如这篇文章证明,只需添加一个服务单位要么在实验中multi-user.target或graphical.target在启动顺序没有定义自己的位置。 它仅确定单元是否作为图形环境的一部分启动。 现实情况是,在启动目标multi-user.target和graphical.target -和所有他们想要的和需要启动并联起来漂亮多了。 确保某个单元以特定顺序启动的最佳方法是确定该单元所依赖的单元,并将新单元配置为“所需”和“之后”该单元所依赖的单元。

资源资源

互联网上有大量有关systemd的信息,但是很多信息简洁,晦涩甚至是误导。 除了本文提到的资源之外,以下网页还提供了有关systemd启动的更详细和可靠的信息。

  • Fedora项目对systemd有很好的实用指南 。 它具有使用systemd配置,管理和维护Fedora计算机所需的几乎所有知识。
  • Fedora项目还有一个很好的备忘单 ,可以将旧的SystemV命令与可比的systemd命令进行交叉引用。
  • 有关systemd及其创建原因的详细技术信息,请查看Freedesktop.org 对systemd的描述 。
  • Linux.com的“更多系统乐趣”提供了更多高级系统信息和技巧 。

还有systemd的设计师和主要开发者Lennart Poettering撰写的一系列针对Linux sysadmin的技术性文章。 这些文章是在2010年4月至2011年9月之间撰写的,但它们现在和那时一样具有相关性。 关于systemd及其生态系统的许多其他优点都基于这些论文。

  • 重新思考PID 1
  • 系统管理员,第一部分
  • 系统管理员,第二部分
  • 系统管理员,第三部分
  • 管理员专用的systemd,第IV部分
  • 系统管理员,第五部分
  • 管理员专用的systemd,第VI部分
  • 管理员专用系统,第七部分
  • 管理员专用的系统化第八部分
  • 管理员专用的systemd,第IX部分
  • 适用于管理员的systemd,第X部分
  • 管理员专用系统,第十一部分

翻译自: https://opensource.com/article/20/5/manage-startup-systemd