2021年5月

探索一些未被充分利用但仍然有用的 Python 特性。

 title=

这是 Python 3.x 首发特性系列文章的第一篇。Python 3.0 于 2008 年首次发布,尽管它已经发布了一段时间,但它引入的许多特性都没有被充分利用,而且相当酷。这里有三个你应该知道的。

仅限关键字参数

Python 3.0 首次引入了仅限关键字参数参数的概念。在这之前,不可能指定一个只通过关键字传递某些参数的 API。这在有许多参数,其中一些参数可能是可选的函数中很有用。

请看一个特意设计的例子:

def show_arguments(base, extended=None, improved=None, augmented=None):
    print("base is", base)
    if extended is not None:
        print("extended is", extended)
    if improved is not None:
        print("improved is", improved)
    if augmented is not None:
        print("augmented is", augmented)

当阅读调用该函数的代码时,有时很难理解发生了什么:

show_arguments("hello", "extra")

    base is hello
    extended is extra

show_arguments("hello", None, "extra")

    base is hello
    improved is extra

虽然可以用关键字参数来调用这个函数,但这明显不是最好的方法。相反,你可以将这些参数标记为仅限关键字:

def show_arguments(base, *, extended=None, improved=None, augmented=None):
    print("base is", base)
    if extended is not None:
        print("extended is", extended)
    if improved is not None:
        print("improved is", improved)
    if augmented is not None:
        print("augmented is", augmented)

现在,你不能用位置参数传入额外的参数:

show_arguments("hello", "extra")
    ---------------------------------------------------------------------------

    TypeError                                 Traceback (most recent call last)

    <ipython-input-7-6000400c4441> in <module>
    ----> 1 show_arguments("hello", "extra")
   

    TypeError: show_arguments() takes 1 positional argument but 2 were given

对该函数的有效调用更容易预测:

show_arguments("hello", improved="extra")
    base is hello
    improved is extra

nonlocal

有时,函数式编程的人根据编写累加器的难易程度来判断一种语言。累加器是一个函数,当它被调用时,返回目前为止发给它的所有参数的总和。

在 3.0 之前,Python 的标准答案是:

class _Accumulator:
    def __init__(self):
        self._so_far = 0
    def __call__(self, arg):
        self._so_far += arg
        return self._so_far

def make_accumulator():
    return _Accumulator()

虽然我承认有些啰嗦,但这确实有效:

acc = make_accumulator()
print("1", acc(1))
print("5", acc(5))
print("3", acc(3))

这样做的输出结果将是:

1 1
5 6
3 9

在 Python 3.x 中,nonlocal 关键字可以用少得多的代码实现同样的行为。

def make_accumulator():
    so_far = 0
    def accumulate(arg):
        nonlocal so_far
        so_far += arg
        return so_far
    return accumulate

虽然累加器是人为的例子,但使用 nonlocal 关键字使内部函数拥有具有状态的的能力是一个强大的工具。

扩展析构

想象一下,你有一个 CSV 文件,每一行由几个元素组成:

  • 第一个元素是年份
  • 第二个元素是月
  • 其他元素是该月发表的全部文章数,每天一个条目

请注意,最后一个元素是 文章总数,而不是 每天发表的文章。例如,一行的开头可以是:

2021,1,5,8,10

这意味着在 2021 年 1 月,第一天发表了 5 篇文章。第二天,又发表了三篇文章,使总数达到 8 篇。第三天,又发表了两篇文章。

一个月可以有 28 天、30 天或 31 天。提取月份、日期和文章总数有多难?

在 3.0 之前的 Python 版本中,你可能会这样写:

year, month, total = row[0], row[1], row[-1]

这是正确的,但它掩盖了格式。使用扩展析构,同样可以这样表达:

year, month, *rest, total = row

这意味着如果该格式改为前缀了一个描述,你可以把代码改成:

_, year, month, *rest, total = row

而不需要在每个索引中添加 1

接下来是什么?

Python 3.0 和它的后期版本已经推出了 12 年多,但是它的一些功能还没有被充分利用。在本系列的下一篇文章中,我将会写另外三个。


via: https://opensource.com/article/21/5/python-30-features

作者:Moshe Zadka 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Android 12 发布,小米等设备可以提前测试

在今天的 Google I/O 大会上,谷歌预览展示了 Android 12,这是今年晚些时候 Android 设备的下一个重大更新。在 Android 12 中,谷歌引入了一种全新的设计语言 Material You。谷歌称,“Android 12 包括 Android 历史上最大的设计变化。……Android 12 比以往任何时候都更具表现力、更有活力、更有个性。”在 Android 12 中,也加强了私密和安全的设计,让你对哪些应用程序正在访问你的数据有更多的透明度,以及更多的控制。

Android 12 测试版现在可供下载,包括 Google Pixel、小米、OnePlus、OPPO、Vivo 的部分型号的设备可以提前测试体验。

除了某个厂家之外,很多国产厂家的手机也都能用上 Android 12 了。

搞瘫美国最大燃油管道的黑客组织解散,至少已获得 9000 万美元

黑客组织 DarkSide 攻击了美国最大的燃油管道运营商后让其声名大噪,也因此宣布团队解散。而据报道,数据显示黑客组织 DarkSide 在宣布解散前,其加密货币的账户中已经获得了至少价值 9000 万美元的比特币。平均来说,每个受害者支付了价值 190 万美元的比特币。其中大部分都被转移到加密货币交易所,并在那里兑换成法定货币。

勒索黑帮简直防不胜防,这也算是计算机普及后一个副作用吧。对抗的方式并不完全取决于技术,相应的法律打击可能更重要。

中国信通院发布首批开源供应商名录

中国信息通信研究院于 2021 年 1 月底开展了第一批开源供应商名录征集活动,本次发布的第一批开源供应商名录,共收录 26 家开源供应商。本次征集范围涵盖云计算、大数据、中间件、数据库、操作系统、开发框架和安全等领域,企业服务范围包括开源商业解决方案、开源云服务和开源服务。包括:北京青云、红帽软件、网易数帆、IBM、普华基础软件、优刻得、火山引擎等公司。

这些供应商里面有很多是耳熟能详的开源企业,发布这样的名单是指导意义的。

一个有趣的开源点对点音频流应用,它提供了一个简单的用户界面和强大的功能。

SonoBus: 跨平台音频流应用

如今,音频流服务在听音乐时非常受欢迎。然而,本地音乐集仍然是一种有用的方式,这不需要一直连接到互联网。

尽管流媒体音乐服务很方便,但你并不真正拥有这些音乐。因此,如果出现许可问题,该平台可能会删除你最喜欢的音乐,而你对此无能为力。

而有了本地音乐音乐集,你就不会遇到这个问题了。但是,你如何通过设备网络串流你本地的音乐,或者与一个小组分享?

SonoBus 可以成为解决这个问题的办法。不仅仅限于音乐,还包括任何音频,如与一群朋友远程练习音乐或合作制作音乐,为什么不呢?

让我们来看看它提供了什么。

SonoBus 的功能

SonoBus 使用起来比较简单,但提供的功能可能会让人震惊。因此,在继续使用之前,你可能想先知道它能让你做什么:

  • 能够连接到多个用户
  • 创建一个有可选密码的小组
  • 分享来自你的麦克风的音频输入
  • 分享来自文件的音频流
  • 支持单声道/立体声
  • 组内播放
  • 录制所有人的音频
  • 能够使个别用户或所有人都静音
  • 可以通过互联网或本地网络连接
  • 支持节拍器,用于协作制作音乐或远程练习课程
  • 支持高质量的音频,最高可达 256Kbps
  • 输入混音器
  • 支持声相
  • 支持有用的效果器(噪声门、压缩器和均衡器)
  • 可在 JACK 和 ALSA 下工作
  • 跨平台支持(Windows、macOS、Android、iOS 和 Linux)

虽然我试图提到所有的基本功能,但你可以在效果器的帮助下得到非常多的控制,来调整音量、质量、延迟,以及音频效果。

它最好的一点是跨平台支持,这使它成为任何用户群的有趣选择,而无论你出于什么原因要串流音频。

在 Linux 中安装 SonoBus

无论你使用什么 Linux 发行版,你都可以轻松地安装 Snap 包Flatpak 包。如果你不想使用它们,你可以手动添加官方仓库来安装:

echo "deb http://pkg.sonobus.net/apt stable main" | sudo tee /etc/apt/sources.list.d/sonobus.list

sudo wget -O /etc/apt/trusted.gpg.d/sonobus.gpg https://pkg.sonobus.net/apt/keyring.gpg

sudo apt update && sudo apt install sonobus

你也可以通过其官方网站为你喜欢的平台下载它。

总结

SonoBus 是一个令人印象深刻的音频流应用,有很多潜在的用途,但它也有一些问题,可能不是每个人的完美解决方案。

例如,我注意到桌面应用占用大量的系统资源,所以这对较旧的系统来说可能是个问题。

另外,Play Store 上的安卓应用仍处于早期访问阶段(测试版)。在我的快速测试中,它工作符合预期,但我已经很久没有使用它了。因此,当依靠它进行跨平台会话时,可能会出现预期的小问题。

在任何情况下,它都适用于每种用例的大量功能。如果你还没有使用过,请试一试。


via: https://itsfoss.com/sonobus/

作者:Ankush Das 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

游戏、交易、预算、艺术、编程等等,这些都只是任何人都可以使用 Linux 的众多方式中的一种。

 title=

当我在度假时,我经常会去一家或者多家的二手书店。我经常能够找到我想读的一本好书,而且我总是以 “我在度假;我应该用这本书来犒劳自己” 来为不可避免的购买行为辩护。这很有效,我用这种方式获得了一些我最喜欢的书。但是,买一本好书在生活中很常见,这个理由经不起推敲。事实上,我不需要为买一本好书来找理由。事情都是这样的,我可以在任何时候做我想做的事。但不知何故,有一个理由似乎确实能让这个过程更有趣。

在我的日常生活中,我会收到很多关于 Linux 的问题。有时候我会不自觉地滔滔不绝地讲述开源软件的历史,或者共享资源的知识和利益。有时候,我会设法提到一些我喜欢的 Linux 上的特性,然后对这些好处进行逆向工程以便它们可以在其它的操作系统上享用。这些讨论经常是有趣且有益的,但只有一个问题:这些讨论都没有回答大家真正要问的问题。

当一个人问你关于 Linux 的问题时,他们经常希望你能够给他们一些使用 Linux 的理由。当然,也有例外。从来没有听过“Linux”的人们可能会问一些字面定义。但是当你的朋友或者同事吐露出他们对当前的操作系统有些不满意的时候,解释一下你为什么喜欢 Linux 可能更好,而不是告诉他们为什么 Linux 是一个比专有系统更好的选择。换句话说,你不需要销售演示,你需要的是度假照片(如果你是个书虫的话,也可以是度假时买的一本书)。

为了达到这个目的,下面是我喜欢 Linux 的 21 个原因,分别在 21 个不同的场合讲给 21 个不同的人。

游戏

 title=

说到玩电脑,最明显的活动之一就是玩游戏,说到玩游戏,我很喜欢。我很高兴花一个晚上玩一个 8 位的益智游戏或者 epic 工作室的一个 AAA 级游戏。其它时候,我还会沉浸在棋盘游戏或者角色扮演游戏(RPG)中。

这些我都是 在 Linux 系统的电脑上做的

办公

 title=

一种方法并不适合所有人。这对帽子和办公室工作来说都是如此。看到同事们被困在一个不适合他们的单一工作流程中,我感到很痛苦,我喜欢 Linux 鼓励用户找到他们喜欢的工具。我曾使用过的应用大到套件(例如 LibreOffice 和 OpenOffice),小到轻量级文字处理器(如 Abiword),再到最小的文本编辑器(利用 Pandoc 进行转换)。

不管我周围的用户被限制在什么范围内,我都可以 自由地使用可以在我的电脑上工作的最好的工具,并且以我希望的方式工作。

选择

 title=

开源最有价值的特性之一是用户在使用这些软件的时候是可以信任它的。这种信任来自于好友网络,他们可以阅读他们所使用的应用程序和操作系统的源代码。也就是说,即使你不知道源代码的好坏,你也可以在 开源社区 中结交一些知道的朋友。这些都是 Linux 用户在探索他们运行的发行版时建立的重要联系。如果你不信任构建和维护的发行版的社区,你可以去找其它的发行版。我们都是这样做的,这是有许多发行版可供选择的优势之一。

Linux 提供了可选择的特性。一个强大的社区,充满了真实的人际关系,结合 Linux 提供的选择自由,所有这些都让用户对他们运行的软件有信心。因为我读过一些源码,也因为我信任哪些维护我没读过的代码的人,所以我信任 Linux

预算

 title=

做预算并不有趣,但是很重要。我很早就认识到,在业余时间做一些不起眼的工作,就像我学会了一种 免费 的操作系统(Linux!)一样。预算不是为了追踪你的钱,而是为了追踪你的习惯。这意味着无论你是靠薪水生活,还是正在计划退休,你都应该 保持预算

如果你在美国,你甚至可以 用 Linux 来交税

艺术

 title=

不管你是画画还是做像素艺术、编辑视频 还是随性记录,你都可以在 Linux 上创建出色的内容。我所见过的一些最优秀的艺术作品都是使用一些非“行业标准”的工具随意创作出来的,并且你可能会惊讶于你所看到的许多内容都是基于同样的方式创造出来的。Linux 是一个不会被宣扬的引擎,但它是具有强大功能的引擎,驱动着独立艺术家和大型制作人。

尝试使用 Linux 来 创作一些艺术作品

编程

 title=

听着,用 Linux 来编程几乎是定论。仅次于服务器管理,开源和 Linux 是一个明显的组合。这其中有 许多原因,但我这里给出了一个有趣的原因。我在发明新东西时遇到了很多障碍,所以我最不希望的就是操作系统或者软件工具开发包(SDK)成为失败的原因。在 Linux 上,我可以访问一切,字面意义上的一切。

封包

 title=

当他们在谈编程的时候,没有人谈封包。作为一个开发者,你必须将你的代码提供给您的用户,否则你将没有任何用户。Linux 使得开发人员可以轻松地 发布应用程序,用户也可以轻松地 安装这些应用程序

令很多人感到惊讶的是 Linux 可以像运行本地程序一样运行许多 Windows 应用程序。你不应该期望一个 Windows 应用可以在 Linux 上执行。不过,许多主要的通用应用要么已经在 Linux 上原生存在,要么可以通过名为 Wine 的兼容层运行。

技术

 title=

如果你正在找一份 IT 工作,Linux 是很好的第一步。作为一个曾经为了更快地渲染视频而误入 Linux 的前艺术系学生,我说的是经验之谈。

尖端技术发生在 Linux 上。Linux 驱动着大部分的互联网、世界上最快的超级计算机以及云本身。现在,Linux 驱动着 边缘计算,将云数据中心的能力与分散的节点相结合,以实现快速响应。

不过,你不需要从最顶层开始。你可以学习在笔记本电脑或者台式机上自动完成任务,并通过一个 好的终端 远程控制系统。

Linux 对你的新想法是开放的,并且 可以进行定制

分享文件

 title=

无论你是一个新手系统管理员,还是仅仅是要将一个将文件分发给室友,Linux 都可以使 文件共享变得轻而易举

多媒体

 title=

在所有关于编程和服务器的讨论中,人们有时把 Linux 想象成一个充满绿色的 1 和 0 的黑屏。对于我们这些使用它的人来说,Linux 也能 播放你所有的媒体,这并不令人惊讶。

易于安装

 title=

以前从来没有安装过操作系统吗?Linux 非常简单。一步一步来,Linux 安装程序会手把手带你完成操作系统的安装,让你在一个小时内感觉到自己是个电脑专家。

来安装 Linux 吧!

试一试 Linux

 title=

如果你还没有准备好安装 Linux,你可以 试一试 Linux。不知道如何开始?它没有你想象的那么吓人。这里给了一些你 一开始需要考虑的事情。然后选择下载一个发行版,并想出你自己使用 Linux 的 21 个理由。


via: https://opensource.com/article/21/4/linux-reasons

作者:Seth Kenlon 选题:lujun9972 译者:ShuyRoy 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

“祝融号”火星车目前带宽只有每秒只能传 2 个字节

中国第一辆火星车“祝融号”已于本月 15 日成功登陆火星,但是一直没有公布拍摄的火星车和火星照片。这是因为火星车如果直接对地球传输数据,速度只有大约 16bps,也就是说,每秒钟只能传输 2 个字节,其主要用于传输一些关键传感器数据,便于地面人员判断工作状态。

天问一号的任务是一边部署登陆器、一边部署轨道器,因此在火星车登陆成功之后,依然需要 3 天的时间才能连接到轨道器。在部署成功后,“祝融号”通过轨道器,每次有大约 8-10 分钟的时间传输时间,大约可以传输 1.5GB 数据。

16bps 这个带宽,真是一个小水管啊。

OpenPrinting 正在开发上游 CUPS,苹果放弃了

早在 2007 年,苹果公司实际收购了开源的 CUPS 项目,但随后在 2019 年底,CUPS 的首席开发者 Michael Sweet 离开了苹果,继而 CUPS 的公开开发就近乎停止了。CUPS 的分支 OpenPrinting 现在正在努力实现 CUPS 2.4 的发布,支持各种新特性,现在实际上是新的 CUPS 上游。

Sweet 在本月的开放打印峰会上说,他现在与苹果公司签订了合同,会将 OpenPrinting 的重要错误修正推送到 Apple CUPS。苹果的 CUPS 会接受这些错误修复,但不会有兴趣在这个打印服务器进行功能开发。

CUPS 是 Linux 上非常重要的基础设施软件,而最终还是需要大厂资助才能继续发展。

安卓间谍软件过去一年中激增 48%

跟踪软件 Stalkerware 可以隐蔽地安装在个人电脑或移动设备上跟踪用户的活动,收集的数据包括他们的 GPS 位置、通话记录、联系人名单、短信通信、社交媒体使用、浏览器历史记录等等。它通常是由身边的人支付和使用的,而不是未知的威胁者。许多供应商将其产品作为保护儿童、雇员和妇女的手段,但并不意味着它不能被用来跟踪其他人。

本周,ESET 研究人员发布了安卓跟踪软件的检测数据,显示这些可疑的应用程序的使用率在 2019 年开始攀升,与 2018 年相比增加了五倍。而这种趋势在 2020 年仍在继续。

跟踪的“需求”比较多,现代人的隐私空间越来越狭窄了。

了解 systemd 是怎样决定服务启动顺序,即使它本质上是个并行系统。

 title=

最近在设置 Linux 系统时,我想知道如何确保服务和其他单元的依赖关系在这些依赖于它们的服务和单元启动之前就已经启动并运行了。我需要更多 systemd 如何管理启动程序的相关知识,特别是在本质上是一个并行的系统中如何是决定服务启动顺序的。

你可能知道 SystemV(systemd 的前身,我在这个系列的 第一篇文章 中解释过)通过 Sxx 前缀命名启动脚本来决定启动顺序,xx 是一个 00-99 的数字。然后 SystemV 利用文件名来排序,然后按照所需的运行级别执行队列中每个启动脚本。

但是 systemd 使用单元文件来定义子程序,单元文件可由系统管理员创建或编辑,这些文件不仅可以用于初始化时也可以用于常规操作。在这个系列的 第三篇文章 中,我解释了如何创建一个挂载单元文件。在第五篇文章中,我解释了如何创建一种不同的单元文件 —— 在启动时执行一个程序的服务单元文件。你也可以修改单元文件中某些配置,然后通过 systemd 日志去查看你的修改在启动序列中的位置。

准备工作

先确认你已经在 /etc/default/grub 文件中的 GRUB_CMDLINE_LINUX= 这行移除了 rhgbquiet,如同我在这个系列的 第二篇文章 中展示的那样。这让你能够查看 Linux 启动信息流,你在这篇文章中部分实验中需要用到。

程序

在本教程中,你会创建一个简单的程序让你能够在主控台和后续的 systemd 日志中查看启动时的信息。

创建一个 shell 程序 /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 ~]#

这个程序可以用任意脚本或编译语言实现。hello.sh 程序可以被放在 Linux 文件系统层级结构(FHS)上的任意位置。我把它放在 /usr/local/bin 目录下,这样它可以直接在命令行中执行而不必在打命令的时候前面带上路径。我发现我创建的很多 shell 程序需要从命令行和其他工具(如 systemd)运行。

服务单元文件

创建服务单元文件 /etc/systemd/system/hello.service,写入下述内容。这个文件不一定是要可执行的,但是为了安全起见,它需要 root 的用户和组所有权和 644640 权限。

# 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”(单发)类型的服务多次而不会有问题。此类服务适用于服务单元文件启动的程序是主进程,必须在 systemd 启动任何依赖进程之前完成的服务。

共有 7 种服务类型,你可以在 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 文件来看看会得到怎样的结果。

启动服务

现在你已经准备好启动这个新服务,通过检查状态来查看结果。尽管你可能之前已经重启过,你仍然可以启动或重启这个单发服务任意次,因为它只运行一次就退出了。

继续启动这个服务(如下所示),然后检查状态。你的结果可能和我的有区别,取决于你做了多少试错实验。

[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 ~]#

从状态检查命令的输出中我们可以看到,systemd 日志表明 hello.sh 启动然后服务结束了。你也可以看到脚本的输出。该输出是根据服务的最近调用的日志记录生成的,试试看多启动几次这个服务,然后再看状态命令的输出就能理解我所说的。

你也应该直接查看日志内容,有很多种方法可以实现。一种办法是指定记录类型标识符,在这个例子中就是 shell 脚本的名字。它会展示前几次重启和当前会话的日志记录。如你所见,我已经为这篇文章做了挺长一段时间的研究测试了。

[root@testvm1 ~]# journalctl -t hello.sh
<剪去>
-- 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
<剪去>
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 的日志内容 —— 当我写这篇时是 109183 行。这是一个需要整理的大量数据。你可以使用页面的搜索功能,通常是 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 服务的日志记录,你可以指定时间来缩小范围。举个例子,我将在我的测试虚拟机上以 10:54:00 为开始时间,这是上述的日志记录开始的分钟数。注意 --since= 的选项必须加引号,这个选项也可以写成 -S "某个时间"

日期和时间可能在你的机器上有所不同,所以确保使用能匹配你日志中的时间的时间戳。

[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
<剪去>

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
<剪去>
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 所“需要”的。

重启机器,确保能在启动阶段观察数据流,这样你能看到 “Hello world” 信息。等等……你看见了么?嗯,我看见了。尽管它很快被刷过去了,但是我确实看到 systemd 的信息显示它启动了 hello.service 服务。

看看上次系统启动后的日志。你可以使用页面搜索工具 less 来找到 “Hello” 或 “hello”。我裁剪了很多数据,但是留下了附近的日志记录,这样你就能感受到和你服务有关的日志记录在本地是什么样子的:

[root@testvm1 ~]# journalctl -b
<剪去>
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...
<剪去>
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
<剪去>

你可以看到 systemd 启动了 hello.service 单元,它执行了 hello.sh 脚本并将输出记录在日志中。如果你能在启动阶段抓到它,你也应该能看见,systemd 信息表明了它正在启动这个脚本,另外一条信息表明了服务成功。通过观察上面数据流中第一条 systemd 消息,你会发现 systemd 在到达基本的系统目标后很快就启动了你的服务。

但是我想看见信息在启动阶段也被打印出来。有一种方法可以做到:在 hello.service 文件的 [Service] 段中加入下述行:

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.targetmulti-user.targetgraphical.**target)中并行启动尽可能多的服务和其他的单元类型。你应该刚刚看过最近一次开机的日志记录,它应该和上面我的日志看上去类似。

注意,systemd 在它到达到基本系统目标(basic.target)后不久就启动了你的测试服务。这正是你在在服务单元文件的 WantedBy 行中指定的,所以它是对的。在你做出修改之前,列出 /etc/systemd/system/multi-user.target.wants 目录下的内容,你会看到一个指向服务单元文件的软链接。服务单元文件的 [Install] 段指定了哪一个目标会启动这个服务,执行 systemctl enable hello.service 命令会在适当的 targets.wants 路径下创建软链接。

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

某些服务需要在 basic.target 阶段启动,其他则没这个必要,除非系统正在启动 graphical.target。这个实验中的服务不会在 basic.target 期间启动 —— 假设你直到 graphical.target 阶段才需要它启动。那么修改 WantedBy 这一行:

WantedBy=graphical.target

一定要先禁用 hello.service 再重新启用它,这样可以删除旧链接并且在 graphical.targets.wants 目录下创建一个新的链接。我注意到如果我在修改服务需要的目标之前忘记禁用该服务,我可以运行 systemctl disable 命令,链接将从两个 targets.wants 目录中删除。之后我只需要重新启用这个服务然后重启电脑。

启动 graphical.target 下的服务有个需要注意的地方,如果电脑启动到 multi-user.target 阶段,这个服务不会自动启动。如果这个服务需要 GUI 桌面接口,这或许是你想要的,但是它同样可能不是你想要的。

-o short-monotonic 选项来查看 graphical.targetmulti-user.target 的日志,展示内核启动几秒后的日志,精度为微秒级别:

[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...
<剪去>
[   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]: ###############################
<剪去>
[   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...
<剪去>
[   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.

尽管单元文件的 WantedBy 部分包含了 graphical.targethello.service 单元在启动后大约 19.5 或 19.6 秒后运行。但是 hello.servicemulti-user.target 中开始于 17.24 秒,在 graphical target 中开始于 19.43 秒。

这意味着什么呢?看看 /etc/systemd/system/default.target 这个链接。文件内容显示 systemd 先启动了默认目标 graphical.target,然后 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 比这个完成的早的多。

这一切表明,这两个目标几乎是并行启动的。如果你查看日志,你会发现各种目标和来自这类主要目标的服务大多是平行启动的。很明显,multi-user.target 没有必要在 graphical.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” 消息在启动阶段出现。还需要注意的是,在启动时记录出现的时间戳比之前要晚了大约 6 秒。

定义启动序列的最好方法

本文章详细地探讨了 Linux 启动时 systemd 和单元文件以及日志的细节,并且发现了当错误被引入单元文件时候会发生什么。作为系统管理员,我发现这类实验有助于我理解程序或者服务出故障时的行为,并且在安全环境中有意破坏是一种学习的好方法。

文章中实验结果证明,仅将服务单元添加至 multi-user.target 或者 graphical.target 并不能确定它在启动序列中的位置。它仅仅决定了一个单元是否作为图形环境一部分启动。事实上,启动目标 multi-user.targetgraphical.target 和所有它们的 Wants 以及 Required 几乎是并行启动的。确保单元在特定位置启动的最好方法是确定它所依赖的单元,并将新单元配置成 WantAfter 它的依赖。

资源

网上有大量的关于 systemd 的参考资料,但是大部分都有点简略、晦涩甚至有误导性。除了本文中提到的资料,下列的网页提供了跟多可靠且详细的 systemd 入门信息。

Fedora 项目有一篇切实好用的 systemd 入门,它囊括了几乎所有你需要知道的关于如何使用 systemd 配置、管理和维护 Fedora 计算机的信息。

Fedora 项目也有一个不错的 备忘录,交叉引用了过去 SystemV 命令和 systemd 命令做对比。

关于 systemd 的技术细节和创建这个项目的原因,请查看 Freedesktop.org 上的 systemd 描述。

Linux.com 的“更多 systemd 的乐趣”栏目提供了更多高级的 systemd 信息和技巧。

此外,还有一系列深度的技术文章,是由 systemd 的设计者和主要开发者 Lennart Poettering 为 Linux 系统管理员撰写的。这些文章写于 2010 年 4 月至 2011 年 9 月间,但它们现在和当时一样具有现实意义。关于 systemd 及其生态的许多其他好文章都是基于这些文章:


via: https://opensource.com/article/20/5/manage-startup-systemd

作者:David Both 选题:lujun9972 译者:tt67wq 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出