Chapter 6. How User Space Starts(第 6 章 用户空间如何启动)

The point where the kernel starts its first user-space process, init, is significant—not just because that’s where the memory and CPU are finally ready for normal system operation, but because that’s where you can see how the rest of the system builds up as a whole. Prior to this point, the kernel executes a well-controlled path of execution defined by a relatively small number of software developers. User space is far more modular. It’s much easier to see what goes into the user space startup and operation. For the adventurous, it’s also relatively easy to change the user space startup because doing so requires no low-level programming.

内核开始其第一个用户空间进程init的地方非常重要,不仅因为这是内存和CPU最终准备好进行正常系统操作的地方,还因为你可以看到整个系统是如何构建起来的。

在这一点之前,内核执行的是由相对较少的软件开发人员定义的良好控制的执行路径。

用户空间要模块化得多。

很容易看到用户空间启动和运行中涉及的内容。

对于冒险者来说,改变用户空间启动也相对容易,因为这不需要低级编程。

User space starts in roughly this order:

  1. init
  2. Essential low-level services such as udevd and syslogd
  3. Network configuration
  4. Mid- and high-level services (cron, printing, and so on)
  5. Login prompts, GUIs, and other high-level applications

用户空间大致按以下顺序开始:

  1. init
  2. 基本的低级服务,如udev和syslogd
  3. 网络配置
  4. 中高级服务(cron、打印等)
  5. 登录提示、GUI和其他高级应用程序

6.1 Introduction to init( init 简介)

The init program is a user-space program like any other program on the Linux system, and you’ll find it in /sbin along with many of the other system binaries. Its main purpose is to start and stop the essential service processes on the system, but newer versions have more responsibilities.

init程序是Linux系统中的一个用户空间程序,与系统中的其他程序一样,你可以在/sbin目录下找到它,与许多其他系统二进制文件一起。

它的主要目的是启动和停止系统上的必要服务进程,但较新的版本有更多的责任。

There are three major implementations of init in Linux distributions:

Linux发行版中有三个主要的init实现:

o System V init. A traditional sequenced init (Sys V, usually pronounced “sys-five”). Red Hat Enterprise Linux and several other distributions use this version.
o systemd. The emerging standard for init. Many distributions have moved to systemd, and most that have not yet done so are planning to move to it.
o Upstart. The init on Ubuntu installations. However, as of this writing, Ubuntu has also planned to migrate to systemd.

  • System V init。传统的顺序init(Sys V,通常发音为“sys-five”)。Red Hat Enterprise Linux和其他几个发行版使用这个版本。
  • systemd。正在兴起的init标准。许多发行版已经转向systemd,大多数尚未转向的发行版也计划转向它。
  • Upstart。Ubuntu安装中的init。然而,截至本文撰写时,Ubuntu也计划迁移到systemd。

There are various other versions of init as well, especially on embedded platforms. For example, Android has its own init. The BSDs also have their version of init, but you are unlikely to see them on a modern Linux machine. (Some distributions have also modified the System V init configuration to resemble the BSD style.)

还有其他各种各样的init版本,尤其是在嵌入式平台上。

例如,Android有自己的init。

BSD也有它们自己的init版本,但你不太可能在现代Linux机器上看到它们。

(一些发行版还修改了System V init配置以类似BSD风格。)

There are many different implementations of init because System V init and other older versions relied on a sequence that performed only one startup task at a time. Under this scheme, it is relatively easy to resolve dependencies. However, performance isn’t terribly good, because two parts of the boot sequence cannot normally run at once. Another limitation is that you can only start a fixed set of services as defined by the boot sequence: When you plug in new hardware or need a service that isn’t already running, there is no standardized way to coordinate the new components with init. systemd and Upstart attempt to remedy the performance issue by allowing many services to start in parallel thereby speeding up the boot process. Their implementations are quite different, though:

有许多不同的init实现,因为System V init和其他旧版本依赖于一次只执行一个启动任务的顺序。

在这种方案下,解决依赖关系相对容易。

然而,性能并不是特别好,因为引导序列的两个部分通常不能同时运行。

另一个限制是,你只能启动由引导序列定义的一组固定服务:当你插入新硬件或需要一个尚未运行的服务时,没有标准化的方法来协调新组件与init。

systemd和Upstart试图通过允许许多服务并行启动来解决性能问题,从而加快引导过程。

然而,它们的实现方式有很大的不同:

o systemd is goal oriented. You define a target that you want to achieve, along with its dependencies, and when you want to reach the target. systemd satisfies the dependencies and resolves the target. systemd can also defer the start of a service until it is absolutely needed.
o Upstart is reactionary. It receives events and, based on those events, runs jobs that can in turn produce more events, causing Upstart to run more jobs, and so on.

  • systemd是目标导向的。你定义一个目标,以及它的依赖关系和你想要达到目标的时间。systemd满足依赖关系并解决目标。systemd还可以推迟启动服务,直到绝对需要时再启动。
  • Upstart是反应式的。它接收事件,并根据这些事件运行作业,这些作业又可以产生更多的事件,导致Upstart运行更多的作业,依此类推。

The systemd and Upstart init systems also offer a more advanced way to start and track services. In traditional init systems, service daemons are expected to start themselves from scripts. A script runs a daemon program, which detaches itself from the script and runs autonomously. To find the PID of a service daemon, you need to use ps or some other mechanism specific to the service. In contrast, Upstart and systemd can manage individual service daemons from the beginning, giving the user more power and insight into exactly what is running on the system

systemd和Upstart init系统还提供了更高级的启动和跟踪服务的方式。

在传统的init系统中,服务守护进程被期望从脚本中启动自己。

一个脚本运行一个守护程序,守护程序从脚本中分离出来并自主运行。

要找到一个服务守护进程的PID,你需要使用ps或其他特定于该服务的机制。

相比之下,Upstart和systemd可以从一开始就管理单个服务守护进程,给用户更多的权力和了解系统上正在运行的内容。

Because the new init systems are not script-centric, configuring services for them also tends to be easier. In particular, System V init scripts tend to contain many similar commands designed to start, stop, and restart services. You don’t need all of this redundancy with systemd and Upstart, which allow you to concentrate on the services themselves, rather than their scripts

因为新的init系统不是以脚本为中心,所以为它们配置服务也更容易。

特别是,System V init脚本通常包含许多类似的命令,用于启动、停止和重新启动服务。

但是,使用systemd和Upstart,你不需要所有这些冗余,可以专注于服务本身,而不是它们的脚本。

Finally, systemd and Upstart both offer some level of on-demand services. Rather than trying to start all the services that may be necessary at boot time (as the System V init would do), they start some services only when needed. This idea is not really new; this was done with the traditional inetd daemon, but the new implementations are more sophisticated.

最后,systemd和Upstart都提供了一定程度的按需服务。

它们不会尝试在启动时启动所有可能需要的服务(像System V init那样),而是只在需要时启动一些服务。

这个想法并不是真正新的;传统的inetd守护进程就是这样做的,但新的实现更加复杂。

Both systemd and Upstart offer some System V backward compatibility. For example, both support the concept of runlevels

systemd和Upstart都提供了一些System V的向后兼容性。例如,它们都支持运行级的概念。

6.2 System V Runlevels(系统 V 运行级别)

At any given time on a Linux system, a certain base set of processes (such as crond and udevd) is running. In System V init, this state of the machine is called its runlevel, which is denoted by a number from 0 through 6. A system spends most of its time in a single runlevel, but when you shut the machine down, init switches to a different runlevel in order to terminate the system services in an orderly fashion and to tell the kernel to stop

在Linux系统上的任何时刻,都会运行一定的基本进程(如crond和udevd)。

在System V init中,这种机器的状态被称为运行级别,用数字0到6表示。系统大部分时间都在一个运行级别上运行,但当你关闭机器时,init会切换到另一个运行级别,以有序地终止系统服务并告诉内核停止运行。

You can check your system’s runlevel with the who -r command. A system running Upstart responds with something like this:

你可以用who -r命令检查系统的运行级别。

一个运行Upstart的系统会返回类似以下的结果:

$ who -r
run-level 2 2015-09-06 08:37

This output tells us that the current runlevel is 2, as well as the date and time that the runlevel was established.

这个输出告诉我们当前的运行级别是2,以及运行级别建立的日期和时间。

Runlevels serve various purposes, but the most common one is to distinguish between system startup, shutdown, single-user mode, and console mode states. For example, Fedora-based systems traditionally used runlevels 2 through 4 for the text console; a runlevel of 5 means that the system will start a GUI login.

运行级别有各种用途,但最常见的是区分系统启动、关闭、单用户模式和控制台模式。

例如,基于Fedora的系统传统上使用运行级别2到4来进行文本控制台操作;运行级别5表示系统将启动图形用户登录界面。

But runlevels are becoming a thing of the past. Even though all three init versions in this book support them, systemd and Upstart consider runlevels obsolete as end states for the system. To systemd and Upstart, runlevels exist primarily to start services that support only the System V init scripts, and the implementations are so different that even if you’re familiar with one type of init, you won’t necessarily know what to do with another.

但是,运行级别正在逐渐过时。

尽管本书中的所有三个init版本都支持运行级别,但systemd和Upstart认为运行级别作为系统的终止状态已经过时。

对于systemd和Upstart来说,运行级别主要存在于用于支持System V init脚本的服务启动,而且两者的实现方式差异很大,即使你熟悉一种init类型,你也不一定知道如何处理另一种类型的init。

6.3 Identifying Your init( 确定您的倡议)

Before proceeding, you need to determine your system’s version of init. If you’re not sure, check your system as follows:

在继续之前,您需要确定您的系统的init版本。如果您不确定,请按照以下方式检查您的系统:

o If your system has /usr/lib/systemd and /etc/systemd directories, you have systemd. Go to 6.4 systemd.
o If you have an /etc/init directory that contains several .conf files, you’re probably running Upstart (unless you’re running Debian 7, in which case you probably have System V init). Go to 6.5 Upstart.
o If neither of the above is true, but you have an /etc/inittab file, you’re probably running System V init. Go to 6.6 System V init.

  • 如果您的系统具有/usr/lib/systemd/etc/systemd目录,则表示您使用的是systemd。请转到6.4 systemd。
  • 如果您有一个包含多个.conf文件的/etc/init目录,则您可能正在运行Upstart(除非您运行的是Debian 7,那么您可能使用的是System V init)。请转到6.5 Upstart。
  • 如果以上两种情况都不成立,但您有一个/etc/inittab文件,则您可能正在运行System V init。请转到6.6 System V init。

If your system has manual pages installed, viewing the init(8) manual page should help identify your version.

如果您的系统安装了手册页,查看init(8)手册页应该有助于确定您的版本。

6.4 systemd(systemd)

The systemd init is one of the newest init implementations on Linux. In addition to handling the regular boot process, systemd aims to incorporate a number of standard Unix services such as cron and inetd. As such, it takes some inspiration from Apple’s launchd. One of its most significant features is its ability to defer the start of services and operating system features until they are necessary.

systemd init是Linux上最新的init实现之一。

除了处理常规的引导过程外,systemd还旨在整合一些标准的Unix服务,如cron和inetd。

因此,它在某种程度上受到了苹果的launchd的启发。

它最重要的特点之一是能够推迟服务和操作系统功能的启动,直到它们被需要为止。

There are so many systemd features that it can be very difficult to know where to start learning the basics.

systemd有很多功能,因此很难知道从哪里开始学习基础知识。

Let’s outline what happens when systemd runs at boot time:

让我们概述一下systemd在启动时的运行过程:

  1. systemd loads its configuration.
  2. systemd determines its boot goal, which is usually named default.target.
  3. systemd determines all of the dependencies of the default boot goal, dependencies of these dependencies,
    and so on.
  4. systemd activates the dependencies and the boot goal.
  5. After boot, systemd can react to system events (such as uevents) and activate additional components.
  6. systemd加载其配置。
  7. systemd确定其引导目标,通常被命名为default.target。
  8. systemd确定默认引导目标的所有依赖关系,以及这些依赖关系的依赖关系,以此类推。
  9. systemd激活依赖关系和引导目标。
  10. 引导完成后,systemd可以对系统事件(如uevents)做出反应,并激活其他组件。

When starting services, systemd does not follow a rigid sequence. As with other modern init systems, there is a considerable amount of flexibility in the systemd bootup process. Most systemd configurations deliberately try to avoid any kind of startup sequence, preferring to use other methods to resolve strict dependencies.

在启动服务时,systemd不会遵循严格的顺序。

与其他现代init系统一样,systemd的启动过程具有相当大的灵活性。

大多数systemd配置有意避免任何启动顺序,而是更倾向于使用其他方法来解决严格的依赖关系。

6.4.1 Units and Unit Types(单位和单位类型)

One of the most interesting things about systemd is that it does not just operate processes and services; it can also mount filesystems, monitor network sockets, run timers, and more. Each type of capability is called a unit type, and each specific capability is called a unit. When you turn on a unit, you activate it.

systemd 最有趣的一点是,它不仅能运行进程和服务,还能挂载文件系统、监控网络套接字、运行定时器等。

每一种功能都被称为一个单元类型,而每一种特定的功能都被称为一个单元。

当你打开一个单元时,你就激活了它。

Rather than describe all of the unit types (you’ll find them in the systemd(1) manual page), here’s a look at a few of the unit types that perform the boot-time tasks required in any Unix system:

这里不对所有单元类型进行描述(你可以在 systemd(1) 手册中找到它们),而是介绍几种执行任何 Unix 系统所需的启动时任务的单元类型:

o Service units. Control the traditional service daemons on a Unix system.
o Mount units. Control the attachment of filesystems to the system.
o Target units. Control other units, usually by grouping them.

o 服务单元。控制 Unix 系统中的传统服务守护进程。
o 挂载单元。控制文件系统与系统的连接。
o 目标单元。控制其他单元,通常是将它们分组。

The default boot goal is usually a target unit that groups together a number of service and mount units as dependencies. As a result, it’s easy to get a partial picture of what’s going to happen when you boot, and you can even create a dependency tree diagram with the systemctl dot command. You’ll find the tree to be quite large on a typical system, because many units don’t run by default.

默认启动目标通常是一个目标单元,它将许多服务和挂载单元作为依赖关系组合在一起。

因此,很容易就能了解启动时将发生的部分情况,甚至可以使用 systemctl dot 命令创建依赖关系树图。

在典型的系统中,由于许多单元默认情况下并不运行,因此依赖关系树会非常庞大。

Figure 6-1 shows a part of the dependency tree for the default.target unit found on a Fedora system. When you activate that unit, all of the units below it on the tree also activate.

图 6-1 显示了 Fedora 系统中 default.target 单元的部分依赖关系树。当您激活该单元时,它下面的所有单元也会激活。

Figure 6-1. Unit dependency tree

Figure 6-1. Unit dependency tree

图6-1 单元依赖树

6.4.2 systemd Dependencies(systemd 依赖关系)

Boot-time and operational dependencies are more complicated than they may seem at first because strict dependencies are too inflexible. For example, imagine a scenario in which you want to display a login prompt after starting a database server, so you define a dependency from the login prompt to the database server. However, if the database server fails, the login prompt will also fail due to that dependency, and you won’t even be able to log in to your machine to fix it.

启动时和运行时的依赖关系比起初看起来要复杂得多,因为严格的依赖关系过于死板。

例如,想象一种情况,你希望在启动数据库服务器后显示登录提示符,所以你定义了一个从登录提示符到数据库服务器的依赖关系。

然而,如果数据库服务器失败,由于这个依赖关系,登录提示符也会失败,你甚至无法登录到机器上进行修复。

Unix boot-time tasks are fairly fault tolerant and can often fail without causing serious problems for standard services. For example, if a data disk for a system was removed but its /etc/fstab entry remained, the initial filesystem mount would fail. However, that failure typically wouldn’t seriously affect standard operating system operation.

Unix启动时的任务相当容错,并且通常可以在标准服务中出现问题时失败而不会造成严重问题。

例如,如果一个系统的数据磁盘被移除,但其/etc/fstab条目仍然存在,初始文件系统挂载将失败。

然而,这种失败通常不会对标准操作系统运行产生严重影响。

To accommodate the need for flexibility and fault tolerance, systemd offers a myriad of dependency types and styles. We’ll label them by their keyword syntax, and but we won’t go into details about configuration syntax until 6.4.3 systemd Configuration. Let’s first look at the basic types:

为了满足灵活性和容错性的需求,systemd提供了多种类型和风格的依赖关系。

我们将通过它们的关键字语法进行标记,但在6.4.3 systemd配置之前,我们不会详细介绍配置语法。

让我们首先看一下基本类型:

o Requires Strict dependencies. When activating a unit with a Requires dependency unit, systemd attempts to activate the dependency unit. If the dependency unit fails, systemd deactivates the dependent unit.
o Wants. Dependencies for activation only. Upon activating a unit, systemd activates the unit’s Wants dependencies, but it doesn’t care if those dependencies fail.
o Requisite. Units that must already be active. Before activating a unit with a Requisite dependency, systemd first checks the status of the dependency. If the dependency has not been activated, systemd fails on activation of the unit with the dependency.
o Conflicts. Negative dependencies. When activating a unit with a Conflict dependency, systemd
automatically deactivates the dependency if it is active. Simultaneous activation of two conflicting units fails.

o Requires 严格依赖关系。在激活具有Requires依赖关系的单元时,systemd尝试激活依赖单元。如果依赖单元失败,systemd会停用依赖单元。
o Wants 只用于激活的依赖关系。在激活单元时,systemd会激活该单元的Wants依赖关系,但不关心这些依赖关系是否失败。
o Requisite 必须已经激活的单元。在激活具有Requisite依赖关系的单元之前,systemd首先检查依赖的状态。如果依赖尚未激活,systemd在激活具有该依赖关系的单元时失败。
o Conflicts 负依赖关系。在激活具有Conflict依赖关系的单元时,如果该依赖关系处于激活状态,systemd会自动停用该依赖关系。两个相互冲突的单元同时激活会失败。

NOTE The Wants dependency type is especially significant because it does not propagate failures to other units. The systemd documentation states that this is the way you should specify dependencies if possible, and it’s easy to see why. This behavior produces a much more robust system, similar to that of a traditional init.

注意 Wants 依赖类型特别重要,因为它不会将失败传播给其他单元。systemd文档指出,如果可能的话,这是您应该指定依赖关系的方式,很容易理解为什么。这种行为产生了一个更加健壮的系统,类似于传统的init系统。

You can also attach dependencies “in reverse.” For example, in order to add Unit A as a Wants dependency to Unit B, you don’t have to add the Wants in Unit B’s configuration. Instead, you can install it as a WantedBy in Unit A’s configuration. The same is true of the RequiredBy dependency. The configuration for (and result of) a “By” dependency is slightly more involved than just editing a configuration file; see Enabling Units and the [Install] Section

您还可以“反向”附加依赖关系。例如,为了将单元A作为单元B的Wants依赖关系添加,您不必在单元B的配置中添加Wants。

相反,您可以将其安装为单元A配置中的WantedBy。RequiredBy依赖关系也是如此。

一个“By”依赖关系的配置(和结果)比仅仅编辑一个配置文件要稍微复杂一些,请参阅启用单元和[Install]部分。

You can view a unit’s dependencies with the systemctl command, as long as you specify a type of dependency, such as Wants or Requires:

您可以使用systemctl命令查看单元的依赖关系,只要指定依赖类型,例如Wants或Requires:

systemctl show -p type unit

Ordering(订购)

None of the dependency syntax that you’ve seen so far explicitly specifies order. By default, activating a unit with a Requires or Wants causes systemd to activate all of these dependencies at the same time as the first unit. This is optimal, because you want to start as many services as possible as quickly as possible to reduce boot time. However, there are situations when one unit must start after another. For instance, in the system that Figure 6-1 is based on, the default.target unit is set to start after multi-user.service (this order distinction is not shown in the figure).

迄今为止,您所见到的所有依赖语法都没有明确指定顺序。

默认情况下,通过Requires或Wants激活一个单元会导致systemd同时激活所有这些依赖项,与第一个单元同时进行。这是最优的,因为您希望尽快启动尽可能多的服务,以减少启动时间。

然而,在某些情况下,一个单元必须在另一个单元之后启动。

例如,在基于图6-1的系统中,默认的目标单元设置为在multi-user.service之后启动(此顺序区别未在图中显示)。

To activate units in a particular order, you can use the following dependency modifiers:

为了按特定顺序激活单元,您可以使用以下依赖修饰符:

o Before. The current unit will activate before the listed unit(s). For example, if Before=bar.target appears in foo.target, systemd activates foo.target before bar.target.
o After. The current unit activates after the listed unit(s).
Conditional Dependencies Several dependency condition keywords operate on various operation system states rather than systemd units. For example:
o ConditionPathExists=p: True if the (file) path p exists in the system.
o ConditionPathIsDirectory=p: True if p is a directory.
o ConditionFileNotEmpty=p: True if p is a file and it’s not zero-length.

o Before。当前单元将在列出的单元之前激活。例如,如果foo.target中出现Before=bar.target,systemd会在激活bar.target之前激活foo.target。
o After。当前单元在列出的单元之后激活。
条件依赖性。几个依赖条件关键字适用于各种操作系统状态,而不是systemd单元。例如:
o ConditionPathExists=p:如果系统中存在(文件)路径p,则为True。
o ConditionPathIsDirectory=p:如果p是一个目录,则为True。
o ConditionFileNotEmpty=p:如果p是一个文件且不为空,则为True。

If a conditional dependency in a unit is false when systemd tries to activate the unit, the unit does not activate, though this applies only to the unit in which it appears. Therefore, if you activate a unit that has a condition dependency as well as some other unit dependencies, systemd attempts to activate the unit dependencies regardless of whether the condition is true or false.

如果在systemd尝试激活单元时,单元中的条件依赖性为假,则不会激活该单元,尽管这仅适用于出现条件依赖性的单元。因此,如果您激活具有条件依赖性以及其他单元依赖性的单元,systemd会尝试激活单元依赖性,而不管条件是真还是假。

Other dependencies are primarily variations on the preceding. For example, the RequiresOverridable dependency is just like Requires when running normally, but it acts like a Wants dependency if a unit is manually activated. (For a full list, see the systemd.unit(5) manual page.)

其他依赖性主要是前述依赖性的变体。例如,RequiresOverridable依赖性在正常运行时与Requires完全相同,但如果手动激活单元,则会像Wants依赖性一样起作用。(有关完整列表,请参阅systemd.unit(5)手册页。)

Now that you’ve seen some of the a few pieces of the systemd configuration, let’s look at some actual unit files and how to work with them.

现在,您已经看到了一些systemd配置的一些片段,让我们来看一些实际的单元文件以及如何使用它们。

6.4.3 systemd Configuration(systemd 配置)

The systemd configuration files are spread among many directories across the system, so you typically won’t find the files for all of the units on a system in one place. That said, there are two main directories for systemd configuration: the system unit directory (globally configured, usually /usr/lib/systemd/system) and a system configuration directory (local definitions, usually /etc/systemd/system).

systemd的配置文件分布在系统的许多目录中,所以你通常不会在一个地方找到所有单元的文件。

话虽如此,systemd配置有两个主要目录:系统单元目录(全局配置,通常为/usr/lib/systemd/system)和系统配置目录(本地定义,通常为/etc/systemd/system)。

To prevent confusion, stick to this rule: Avoid making changes to the system unit directory because your distribution will maintain it for you. Make your local changes to the system configuration directory. So when given the choice between modifying something in /usr and /etc, always change /etc.

为了避免混淆,请遵循这个规则:避免对系统单元目录进行更改,因为你的发行版会为你维护它。将本地更改放在系统配置目录中。

所以当你在/usr和/etc之间做出选择时,总是选择更改/etc。

NOTE You can check the current systemd configuration search path (including precedence) with this command:

注意:你可以使用以下命令检查当前的systemd配置搜索路径(包括优先级):

systemctl -p UnitPath show

However, this particular setting comes from a third source: pkg-config settings. To see the system unit and configuration directories on your system, use the following commands:

然而,这个特定的设置来自第三方来源:pkg-config设置。要查看系统上的系统单元和配置目录,请使用以下命令:

$ pkg-config systemd –-variable=systemdsystemunitdir
$ pkg-config systemd --variable=systemdsystemconfdir

Unit Files(单位文件)

Unit files are derived from the XDG Desktop Entry Specification (for .desktop files, which are very similar to .ini files on Microsoft systems), with section names in brackets ([]) and variable and value assignments (options) in each section.

单元文件源于 XDG 桌面输入规范(用于 .desktop 文件,与微软系统上的 .ini 文件非常相似),括号([])中包含部分名称,每个部分中包含变量和值分配(选项)。

Consider the example unit file media.mount in /usr/lib/systemd/system, which is standard on Fedora installations. This file represents the /media tmpfs filesystem, which is a container directory for mounting removable media.

以 /usr/lib/systemd/system 中的单元文件 media.mount 为例,它是 Fedora 安装程序的标准配置。该文件表示 /media tmpfs 文件系统,它是挂载可移动媒体的容器目录。

[Unit]
Description=Media Directory
Before=local-fs.target
[Mount]
What=tmpfs
Where=/media
Type=tmpfs
Options=mode=755,nosuid,nodev,noexec

There are two sections here. The [Unit] section gives some details about the unit and contains description and dependency information. In particular, this unit is set to activate before the local-fs.target unit.

这里有两个部分。[Unit] 部分提供了有关单元的一些详细信息,包括描述和依赖信息。特别是,该单元设置为在 local-fs.target 单元之前激活。

The [Mount] section details the unit as being a mount unit, and it gives the details on the mount point, the type of filesystem, and the mount options as described in 4.2.6 Filesystem Mount Options. The What= variable identifies the device or UUID of the device to mount. Here, it’s set to tmpfs because this filesystem does not have a device. (For a full list of mount unit options, see the systemd.mount(5) manual page.)

[Mount] 部分详细说明了该单元作为挂载单元,并提供了有关挂载点、文件系统类型和挂载选项的详细信息,如 4.2.6 文件系统挂载选项中所述。

What= 变量标识要挂载的设备或设备的 UUID。

在这里,它设置为 tmpfs,因为该文件系统没有设备。(有关挂载单元选项的完整列表,请参阅 systemd.mount(5) 手册页。)

Many other unit configuration files are similarly straightforward. For example, the service unit file sshd.service enables secure shell logins:

许多其他单元配置文件同样简单明了。例如,服务单元文件 sshd.service 启用安全 shell 登录:

[Unit]
Description=OpenSSH server daemon
After=syslog.target network.target auditd.service
[Service]
EnvironmentFile=/etc/sysconfig/sshd
ExecStartPre=/usr/sbin/sshd-keygen
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target

Because this is a service target, you’ll find the details about the service in the [Service] section, including how to prepare, start, and reload the service. You’ll find a complete listing in the systemd.service(5) manual page (and in systemd.exec(5)), as well as in the discussion of process tracking in 6.4.6 systemd Process Tracking and Synchronization.

因为这是一个服务目标,你可以在[Service]部分找到有关服务的详细信息,包括如何准备、启动和重新加载服务。

你可以在systemd.service(5)手册页(以及systemd.exec(5)手册页)和6.4.6节systemd进程跟踪和同步的讨论中找到完整的列表。

Enabling Units and the [Install] Section(启用单元和[Install]部分)

The [Install] section in the sshd.service unit file is important because it helps us to understand how to use systemd’s WantedBy and RequiredBy dependency options. It’s actually a mechanism for enabling units without modifying any configuration files. During normal operation, systemd ignores the [Install] section. However, consider the case when sshd.service is disabled on your system and you would like to turn it on. When you enable a unit, systemd reads the [Install] section; in this case, enabling the sshd.service unit causes systemd to see the WantedBy dependency for multi-user.target. In response, systemd creates a symbolic link to sshd.service in the system configuration directory as follows:

sshd.service单元文件中的[Install]部分很重要,因为它帮助我们理解如何使用systemd的WantedBy和RequiredBy依赖选项。实际上,它是一种在不修改任何配置文件的情况下启用单元的机制。在正常操作中,systemd会忽略[Install]部分。然而,考虑一种情况,即sshd.service在您的系统上被禁用并且您想要启用它。当您启用一个单元时,systemd会读取[Install]部分;在这种情况下,启用sshd.service单元会使systemd看到对multi-user.target的WantedBy依赖。作为响应,systemd会在系统配置目录中创建一个指向sshd.service的符号链接,具体如下:

ln -s '/usr/lib/systemd/system/sshd.service' 
'/etc/systemd/system/multi-user.
target.wants/sshd.service'

Notice that the symbolic link is placed into a subdirectory corresponding to the dependent unit (multiuser.target in this case

请注意,符号链接被放置在与依赖单元对应的子目录中(在本例中是multi-user.target)。

The [Install] section is usually responsible for the the .wants and .requires directories in the system configuration directory (/etc/systemd/system). However, there are also .wants directories in the unit configuration directory (/usr/lib/systemd/system), and you may also add links that don’t correspond to [Install] sections in the unit files. These manual additions are a simple way to add a dependency without modifying a unit file that may be overwritten in the future (by a software upgrade, for instance).

[Install] 部分通常负责系统配置目录(/etc/systemd/system)中的 .wants 和 .requires 目录。然而,在单元配置目录(/usr/lib/systemd/system)中也有 .wants 目录,您也可以添加与单元文件中的 [Install] 部分不对应的链接。这些手动添加的链接是一种简单的方式,可以在不修改将来可能被覆盖的单元文件的情况下添加依赖关系(例如,软件升级)。

NOTE Enabling a unit is different from activating a unit. When you enable a unit, you are installing it into systemd’s configuration, making semipermanent changes that will survive a reboot. But you don’t always need to explicitly enable a unit. If the unit file has an [Install] section, you must enable it with systemctl enable; otherwise, the existence of the file is enough to enable it. When you activate a unit with systemctl start, you’re just turning it on in the current runtime environment. In addition, enabling a unit does not activate it.

注意:启用单元与激活单元是不同的。当您启用一个单元时,您正在将其安装到 systemd 的配置中,进行半永久性的更改,这些更改将在重启后保留。但您并不总是需要显式地启用一个单元。如果单元文件有一个 [Install] 部分,您必须使用 systemctl enable 命令来启用它;否则,文件的存在就足以启用它。当您使用 systemctl start 命令激活一个单元时,您只是在当前的运行时环境中启动它。此外,启用一个单元并不会激活它。

Variables and Specifiers(变量和指示符)

The sshd.service unit file also shows use of variables—specifically, the $OPTIONS and $MAINPID
environment variables that are passed in by systemd. $OPTIONS are options that you can pass to sshd when you activate the unit with systemctl, and $MAINPID is the tracked process of the service (see 6.4.6 systemd Process Tracking and Synchronization).

sshd.service 单元文件还显示了变量的使用,具体来说是 systemd 传递的 $OPTIONS 和 $MAINPID 环境变量。

$ OPTIONS 是您在使用 systemctl 激活单元时可以传递给 sshd 的选项,$MAINPID 是服务的跟踪进程(请参阅 6.4.6 systemd 进程跟踪和同步)。

A specifier is another variable-like feature often found in unit files. Specifiers start with a percent (%). For example, the %n specifier is the current unit name, and the %H specifier is the current hostname.

指示符是单元文件中常见的另一个类似变量的特性。

指示符以百分号(%)开头。例如,%n 指示符是当前单元的名称,%H 指示符是当前主机名。

NOTE
The unit name can contain some interesting specifiers. You can parameterize a single unit file in order to spawn multiple copies of a service, such as getty processes running on tty1, tty2, and so on. To use these specifiers, add the @ symbol to the end of the unit name. For getty, create a unit file named getty@.service, which allows you to refer to units such as getty@tty1 and getty@tty2. Anything after the @ is called the instance, and when processing the unit file, systemd expands the %I specifier to the instance. You can see this in action with the getty@.service unit files that come with most distributions running systemd.

注意:

单元名称可以包含一些有趣的指示符。

您可以通过参数化单个单元文件来生成多个服务的副本,例如在 tty1、tty2 等上运行的 getty 进程。要使用这些指示符,请在单元名称的末尾添加 @ 符号。

对于 getty,创建一个名为 getty@.service 的单元文件,这样您就可以引用 getty@tty1 和 getty@tty2 等单元。

@ 后面的任何内容都称为实例,当处理单元文件时,systemd 会将 %I 指示符扩展为实例。

在大多数运行 systemd 的发行版中,可以通过 getty@.service 单元文件看到这一点。

6.4.4 systemd Operation(systemd 操作)

You’ll interact with systemd primarily through the systemctl command, which allows you to activate and deactivate services, list status, reload the configuration, and much more.

您将主要通过systemctl命令与systemd进行交互,该命令允许您激活和停用服务,列出状态,重新加载配置等等。

The most essential basic commands deal with obtaining unit information. For example, to view a list of active units on your system, issue a list-units command. (This is actually the default command for systemctl, so you don’t really need the list-units part.):

最基本的命令涉及获取单元信息。例如,要查看系统上活动单元的列表,请发出list-units命令(实际上,这是systemctl的默认命令,所以您实际上不需要list-units部分):

$ systemctl list-units

The output format is typical of a Unix information-listing command. For example, the header and the line for media.mount would look like this:

输出格式是典型的Unix信息列表命令。

例如,标题和media.mount行将如下所示:

UNIT LOAD ACTIVE SUB JOB DESCRIPTION
media.mount loaded active mounted Media Directory

This command produces a lot of output, because a typical system has numerous active units, but it will still be abridged because systemctl truncates any really large unit names. To see the full names of the units, use the --full option, and to see all units (not just active), use the --all option.

该命令会产生大量输出,因为典型的系统有很多活动单元,但它仍然会被截断,因为systemctl会截断任何非常大的单元名称。要查看单元的完整名称,请使用--full选项,要查看所有单元(而不仅仅是活动单元),请使用--all选项。

A particularly useful systemctl operation is getting the status of a unit. For example, here is a typical status command and its output:

一个特别有用的systemctl操作是获取单元的状态。例如,这是一个典型的状态命令及其输出:

$ systemctl status media.mount
media.mount - Media Directory
 Loaded: loaded (/usr/lib/systemd/system/media.mount; static)
 Active: active (mounted) since Wed, 13 May 2015 11:14:55 -0800;
37min ago
 Where: /media
 What: tmpfs
 Process: 331 ExecMount=/bin/mount tmpfs /media -t tmpfs -o
mode=755,nosuid,nodev,noexec (code=exited, status=0/SUCCESS)
 CGroup: name=systemd:/system/media.mount

Notice that there is much more information output here than you would see on any traditional init system. You get not only the state of the unit but also the exact command used to perform the mount, its PID, and its exit status.

请注意,这里输出的信息比您在任何传统init系统上看到的信息要多得多。您不仅可以获得单元的状态,还可以获得执行挂载所使用的确切命令、其PID和退出状态。

One of the most interesting pieces of the output is the control group name. In the preceding example, the control group doesn’t include any information other than the name systemd:/system/media.mount because the unit’s processes have already terminated. However, if you get the status of a service unit such as NetworkManager.service, you’ll also see the process tree of the control group. You can view control groups without the rest of the unit status with the systemd-cgls command. You’ll learn more about control groups in 6.4.6 systemd Process Tracking and Synchronization.

输出中最有趣的部分之一是控制组名称。在前面的示例中,控制组除了名称systemd:/system/media.mount之外没有包含任何其他信息,因为单元的进程已经终止。但是,如果您获取服务单元(如NetworkManager.service)的状态,还将看到控制组的进程树。您可以使用systemd-cgls命令查看控制组而不包含单元状态的其他部分。有关控制组的更多信息,请参见6.4.6节systemd进程跟踪和同步。

The status command also displays recent information from the unit’s journal (a log that records diagnostic information for each unit). You can view a unit’s entire journal with this command:

状态命令还会显示单元日志(记录每个单元的诊断信息的日志)中的最新信息。您可以使用以下命令查看单元的完整日志:

$ journalctl _SYSTEMD_UNIT=unit

(This syntax is a bit odd because journalctl can access the logs of more than just a systemd unit.) To activate, deactivate, and restart units, use the systemd start, stop, and restart commands. However, if you’ve changed a unit configuration file, you can tell systemd to reload the file in one of two ways:

(这种语法有点奇怪,因为journalctl可以访问不仅仅是systemd单元的日志。)要激活、停用和重新启动单元,请使用systemd start、stop和restart命令。但是,如果您更改了单元配置文件,可以通过以下两种方式告诉systemd重新加载文件:

image.png

Requests to activate, reactivate, and restart units are known as jobs in systemd, and they are essentially unit state changes. You can check the current jobs on a system with

在 systemd 中,激活、重新激活和重启单元的请求被称为作业,它们本质上是单元状态的改变。你可以使用

$ systemctl list-jobs

If a system has been running for some time, you can reasonably expect there to be no active jobs on it because all of the activations should be complete. However, at boot time, you can sometimes log in fast enough to see some units start so slowly that they are not yet fully active. For example:

如果一个系统已经运行了一段时间,你可以合理地预期它不会有任何活动工作,因为所有的激活都应该已经完成。

不过,在启动时,有时登录速度会很快,以至于某些单元启动缓慢,尚未完全激活。例如

JOB UNIT TYPE STATE
 1 graphical.target start waiting
 2 multi-user.target start waiting
71 systemd-...nlevel.service start waiting
75 sm-client.service start waiting
76 sendmail.service start running
120 systemd-...ead-done.timer start waiting

In this case, job 76, the sendmail.service unit startup, is taking a really long time. The other listed jobs are in a waiting state, most likely because they’re all waiting for job 76. When sendmail.service finishes starting and becomes fully active, job 76 will complete, the rest of the jobs will also complete, and the job list will be empty.

在这种情况下,作业76,即sendmail.service单元的启动,花费了很长时间。其他列出的作业处于等待状态,很可能是因为它们都在等待作业76。当sendmail.service完成启动并完全激活时,作业76将完成,其余的作业也将完成,作业列表将为空。

NOTE The term job can be confusing, especially because Upstart, another init system described in this chapter, uses the word job to (roughly) refer to what systemd calls a unit. It’s important to remember that although a systemd job associated with a unit will terminate, the unit itself can be active and running afterwards, especially in the case of service units.

注意 术语“作业”可能会令人困惑,特别是因为本章中描述的另一个初始化系统Upstart使用“作业”一词(大致)指的是systemd所称的“单元”。重要的是要记住,尽管与一个单元相关联的systemd作业将终止,但该单元本身在服务单元的情况下可能仍然处于活动状态并继续运行。

See 6.7 Shutting Down Your System for how to shut down and reboot the system.

请参阅6.7关机和重启系统了解如何关闭和重启系统。

6.4.5 Adding Units to systemd(向systemd添加单元)

Adding units to systemd is primarily a matter of creating, then activating and possibly enabling, unit files. You should normally put your own unit files in the system configuration directory /etc/systemd/system so that you won’t confuse them with anything that came with your distribution and so that the distribution won’t overwrite them when you upgrade.

向systemd添加单元主要是创建、激活和可能启用单元文件的问题。通常应将自己的单元文件放在系统配置目录/etc/systemd/system中,以免与发行版提供的文件混淆,并且在升级时不会被发行版覆盖。

Because it’s easy to create target units that don’t do anything and don’t interfere with anything, you should try it. Here’s how to create two targets, one with a dependency on the other:

由于很容易创建不执行任何操作且不干扰任何内容的目标单元,建议您尝试一下。以下是创建两个目标的方法,其中一个依赖于另一个:

1. Create a unit file named test1.target:
2. [Unit] Description=test 1
3. Create a test2.target file with a dependency on test1.target:
4. [Unit]
5. Description=test 2
6. Wants=test1.target
7. Activate the test2.target unit (remember that the dependency in test2.target causes systemd to activate test1.target when you do this): 

    # systemctl start test2.target
7. Verify that both units are active:
8. # systemctl status test1.target test2.target
9. test1.target - test 1
10. Loaded: loaded (/etc/systemd/system/test1.target; static)
11. Active: active since Thu, 12 Nov 2015 15:42:34 -0800; 10s ago
12.
13. test2.target - test 2
14. Loaded: loaded (/etc/systemd/system/test2.target; static)
 Active: active since Thu, 12 Nov 2015 15:42:34 -0800; 10s ago

NOTE If your unit file has an [Install] section, “enable” the unit before activating it:

systemctl enable unit

注意 如果设备文件中有 [安装] 部分,请在激活前 "启用 "设备:

systemctl enable unit

Try this with the preceding example. Remove the dependency from test2.target and add an [Install] section to test1.target containing WantedBy=test2.target.

用前面的例子试试看。删除 test2.target 中的依赖关系,并在 test1.target 中添加包含 WantedBy=test2.target 的 [Install] 部分。

Removing Units(删除单元)

To remove a unit, follow these steps:

要删除单元,请按照以下步骤操作

  1. Deactivate the unit if necessary:
  2. 必要时停用单元:
# systemctl stop unit
  1. If the unit has an [Install] section, disable the unit to remove any dependent symbolic links:
  2. 如果设备有 [安装] 部分,则禁用设备以删除任何依赖的符号链接:
  3. # systemctl disable unit
  4. Remove the unit file, if you like.
  5. 如果需要,删除单元文件。

    6.4.6 systemd Process Tracking and Synchronization(systemd 进程跟踪和同步)

systemd wants a reasonable amount of information and control over every process that it starts. The main problem that it faces is that a service can start in different ways; it may fork new instances of itself or even daemonize and detach itself from the original process.

systemd希望对其启动的每个进程都能获得合理的信息和控制权。它面临的主要问题是,一个服务可以以不同的方式启动;它可以fork出新的实例,甚至可以守护进程化并与原始进程分离。

To minimize the work that a package developer or administrator needs to do in order to create a working unit file, systemd uses control groups (cgroups), an optional Linux kernel feature that allows for finer tracking of a process hierarchy. If you’ve worked with Upstart before, you know that you have to do a little extra work to figure out what the main process is for a service. In systemd, you don’t have to worry about how many times a process forks—just whether it forks. Use the Type option in your service unit file to indicate its startup behavior. There are two basic startup styles:

为了最大限度地减少软件包开发人员或管理员为创建一个可工作的单元文件所需的工作量,systemd使用控制组(cgroups),这是一个可选的Linux内核功能,允许更精细地跟踪进程层次结构。如果你之前使用过Upstart,你会知道你需要额外的工作来确定一个服务的主进程。在systemd中,你不需要担心进程fork的次数,只需要关注它是否fork。在你的服务单元文件中使用Type选项来指示其启动行为。有两种基本的启动方式:

o Type=simple The service process doesn’t fork.
o Type=forking The service forks, and systemd expects the original service process to terminate. Upon termination, systemd assumes that the service is ready.

  • Type=simple:服务进程不会fork。
  • Type=forking:服务进行fork,并且systemd期望原始服务进程终止。在终止后,systemd假设服务已准备就绪。

The Type=simple option doesn’t account for the fact that a service may take some time to set up, and systemd doesn’t know when to start any dependencies that absolutely require such a service to be ready. One way to deal with this is to use delayed startup (see 6.4.7 systemd On-Demand and Resource-Parallelized Startup). However, some Type startup styles can indicate that the service itself will notify systemd when it is ready:

Type=simple选项并不考虑一个服务可能需要一些时间来进行设置,而systemd不知道何时启动任何绝对需要此类服务准备就绪的依赖项。处理这个问题的一种方式是使用延迟启动(参见6.4.7 systemd按需和资源并行启动)。然而,某些Type启动方式可以表示服务本身将在准备就绪时通知systemd:

o Type=notify The service sends a notification specific to systemd (with the sd_notify() function call) when it’s ready.
o Type=dbus The service registers itself on the D-bus (Desktop Bus) when it’s ready.

  • Type=notify:服务在准备就绪时向systemd发送特定的通知(使用sd_notify()函数调用)。
  • Type=dbus:服务在准备就绪时在D-bus(桌面总线)上注册自己。

Another service startup style is specified with Type=oneshot; here the service process actually terminates completely when it’s finished. With such a service, you will almost certainly need to add a RemainAfterExit=yes option so that systemd will still regard the service as active even after its processes terminate.

另一种服务启动方式是通过Type=oneshot指定;在这种情况下,服务进程在完成后会完全终止。对于这样的服务,你几乎肯定需要添加RemainAfterExit=yes选项,以便systemd在其进程终止后仍将该服务视为活动状态。

Finally, there’s one last style: Type=idle. This simply instructs systemd not to start the service until there are no active jobs. The idea here is just to delay a service start until other services have started to keep the system load down, or to keep services from stepping on one another’s output. (Remember, once a service has started, the systemd job that started the service terminates.)

最后,还有一种类型:Type=idle。这仅仅是告诉systemd在没有活动作业时不要启动该服务。这里的想法是仅在其他服务启动后才延迟启动服务,以保持系统负载低,或者防止服务互相干扰输出。(记住,一旦服务启动,启动该服务的systemd作业就会终止。)

6.4.7 systemd On-Demand and Resource-Parallelized Startup(systemd 按需和资源并行启动)

One of systemd’s most significant features is its ability to delay a unit startup until it is absolutely needed. The setup typically works like this:

systemd最重要的功能之一是能够延迟启动一个单元,直到绝对需要它为止。通常的设置如下:

  1. You create a systemd unit (call it Unit A) for the system service that you’d like to provide, as normal.
  2. You identify a system resource such as a network port/socket, file, or device that Unit A uses to offer its services.
  3. You create another systemd unit, Unit R, to represent that resource. These units have special types such as socket units, path units, and device units.
  4. 创建一个systemd单元(称之为单元A),用于提供系统服务,与正常的方式一样。
  5. 识别一个系统资源,比如网络端口/套接字、文件或设备,单元A用来提供其服务。
  6. 创建另一个systemd单元,单元R,用来表示该资源。这些单元有特殊的类型,如套接字单元、路径单元和设备单元。

Operationally, it goes like this:

运行时,步骤如下:

  1. Upon activation of Unit R, systemd monitors the resource.
  2. When anything tries to access the resource, systemd blocks the resource, and the input to the resource is buffered.
  3. systemd activates Unit A.
  4. When the service from Unit A is ready, it takes control of the resource, reads the buffered input, and runs normally.
  5. 在激活单元R时,systemd监控该资源。
  6. 当有任何东西试图访问该资源时,systemd阻塞该资源,并将输入缓冲。
  7. systemd激活单元A。
  8. 当来自单元A的服务准备就绪时,它接管该资源,读取缓冲的输入,并正常运行。

There are a few concerns:

有几个注意事项:

o You must make sure that your resource unit covers every resource that the service provides. This normally isn’t a problem, as most services have just one point of access.
o You need to make sure your resource unit is tied to the service unit that it represents. This can be implicit or explicit, and in some cases, many options represent different ways for systemd to perform the handoff to the service unit.
o Not all servers know how to interface with the units that systemd can provide.

o 您必须确保资源单元涵盖了服务提供的每个资源。通常情况下,这不是问题,因为大多数服务只有一个访问点。
o 您需要确保资源单元与它所代表的服务单元有关联。这可以是隐式的或显式的,并且在某些情况下,许多选项表示systemd执行向服务单元交接的不同方式。
o 并非所有服务器都知道如何与systemd提供的单元进行接口。

If you already know what utilities like inetd, xinetd, and automount do, you’ll see that there are a lot of similarities. Indeed, the concept is nothing new (and in fact, systemd includes support for automount units). We’ll go over an example of a socket unit in An Example Socket Unit and Service. But let’s first take a look at how these resource units help you at boot time.

如果您已经了解了类似inetd、xinetd和automount等实用程序的功能,您会发现它们有很多相似之处。

事实上,这个概念并不新鲜(实际上,systemd包含了对automount单元的支持)。

我们将在《一个套接字单元和服务的示例》中介绍一个套接字单元的示例。

但首先,让我们看看这些资源单元如何在启动时帮助您。

Boot Optimization with Auxiliary Units(带辅助装置的启动优化)

A common style of unit activation in systemd attempts to simplify dependency order and speed up boot time. It’s similar to on-demand startup in that a service unit and an auxiliary unit represent the service unit’s offered resource, except that in this case systemd starts the service unit as soon as it activates the auxiliary unit.

systemd中常见的单元激活方式旨在简化依赖顺序并加快启动时间。

它类似于按需启动,其中一个服务单元和一个辅助单元代表服务单元提供的资源,不同之处在于在这种情况下,systemd在激活辅助单元时立即启动服务单元。

The reasoning behind this scheme is that essential boot-time service units such as syslog and dbus take some time to start, and many other units depend on them. However, systemd can offer a unit’s essential resource (such as a socket unit) very quickly, and then it can immediately activate not only the essential unit but also any units that depend on the essential resource. Once the essential unit is ready, it takes control of the resource.

这种方案背后的原因是,像syslog和dbus这样的必要启动服务单元需要一些时间来启动,并且许多其他单元依赖于它们。

然而,systemd可以非常快速地提供一个单元的必要资源(如套接字单元),然后它可以立即激活不仅是必要单元,还有任何依赖于必要资源的单元。

一旦必要单元准备就绪,它将控制该资源。

Figure 6-2 shows how this might work in a traditional system. In this boot timeline, Service E provides an essential Resource R. Services A, B, and C depend on this resource and must wait until Service E has started. When booting, the system takes quite a long time to get around to starting Service C.

图6-2展示了这在传统系统中的工作原理。在这个启动时间轴中,服务E提供了一个必要的资源R。

服务A、B和C依赖于这个资源,并且必须等待服务E启动。

在启动时,系统需要相当长的时间才能开始启动服务C。

Figure 6-2. Sequential boot timeline with a resource dependency

Figure 6-2. Sequential boot timeline with a resource dependency

图 6-2. 具有资源依赖性的顺序启动时间表

Figure 6-3 shows an equivalent systemd boot configuration. The services are represented by Units A, B, C, and E, and a new Unit R represents the resource that Unit E provides. Because systemd can provide an interface for Unit R while Unit E starts, Units A, B, C, and E can all be started at the same time. Unit E takes over for Unit R when ready. (An interesting point here is that Units A, B, and C may not need to explicitly access Unit R before they finish their startup, as Unit B in the figure demonstrates.)

图 6-3 显示了一个等效的 systemd 启动配置。单元 A、B、C 和 E 代表服务,新单元 R 代表单元 E 提供的资源。

由于 systemd 可以在单元 E 启动时为单元 R 提供接口,因此单元 A、B、C 和 E 可以同时启动。

当准备就绪时,单元 E 将接替单元 R。

(这里有趣的一点是,单元 A、B 和 C 在完成启动前可能不需要显式访问单元 R,如图中单元 B 所示)。

Figure 6-3. systemd boot timeline with a resource unit

Figure 6-3. systemd boot timeline with a resource unit

图 6-3. 使用资源单元的 systemd 启动时间线

NOTE When parallelizing startup like this, there is a chance that your system may slow down temporarily due to a large number of units starting at once.

注意 在进行类似的并行启动时,由于大量单元同时启动,系统可能会暂时变慢。

The takeaway is that, although you’re not creating an on-demand unit startup in this case, you’re using the same features that make on-demand startup possible. For common real-life examples, see the syslog and DBus configuration units on a machine running systemd; they’re very likely to be parallelized in this way.

这里要说明的是,虽然在这种情况下你并没有创建按需单元启动,但你使用的是使按需启动成为可能的相同功能。有关常见的实际例子,请参阅运行 systemd 的机器上的 syslog 和 DBus 配置单元;它们很可能以这种方式并行运行。

An Example Socket Unit and Service(套接字单元和服务示例)

We’ll now look at an example, a simple network echo service that uses a socket unit. This is somewhat advanced material, and you may not really understand it until you’ve read the discussion of TCP, ports, and listening in Chapter 9 and sockets in Chapter 10, so feel free to skip this and come back later

现在我们来看一个使用套接字单元的简单网络回声服务示例。这部分内容有些高深,在阅读过第 9 章中关于 TCP、端口和监听的讨论以及第 10 章中关于套接字的讨论之后,你可能才会真正理解,所以请跳过这部分内容,稍后再来看。

The idea behind this service is that when a network client connects to the service, the service repeats anything that the client sends back to the client. The unit will listen on TCP port 22222. We’ll call it the echo service and start with a socket unit, represented by the following echo.socket unit file:

这项服务背后的理念是,当网络客户端连接到服务时,服务会将客户端发送的任何内容重复发送给客户端。设备将监听 TCP 端口 22222。我们将其称为 echo 服务,并从一个套接字单元开始,用下面的 echo.socket 单元文件表示:

[Unit]
Description=echo socket
[Socket]
ListenStream=22222
Accept=yes

Note that there’s no mention of the service unit that this socket supports inside the unit file. So what is the corresponding service unit file?

请注意,单元文件中并没有提及该插座支持的服务单元。那么相应的服务单元文件是什么呢?

Its name is echo@.service. The link is done by naming convention; if a service unit file has the same prefix as a .socket file (in this case, echo), systemd knows to activate that service unit when there’s activity on the socket unit. In this case, systemd creates an instance of echo@.service when there’s activity on echo.socket.

它的名称是 echo@.service。这种链接是通过命名约定实现的;如果服务单元文件的前缀与 .socket 文件(本例中为 echo)相同,systemd 就会在套接字单元有活动时激活该服务单元。在这种情况下,当 echo.socket 上有活动时,systemd 就会创建 echo@.service 实例。

Here is the echo@.service unit file:

下面是 echo@.service 单元文件:

[Unit]
Description=echo service
[Service]
ExecStart=-/bin/cat
StandardInput=socket

NOTE If you don’t like the implicit activation of units based on the prefixes, or you need to create an activation mechanism between two units with different prefixes, you can use an explicit option in the unit defining your resource. For example, use Socket=bar.socket inside foo.service to have bar.socket hand its socket to foo.service.

注意:如果你不喜欢基于前缀的单位的隐式激活,或者你需要在具有不同前缀的两个单位之间创建一个激活机制,你可以在定义资源的单位中使用显式选项。例如,在 foo.service 中使用 Socket=bar.socket,以便 bar.socket 将其套接字传递给 foo.service。

To get this example service unit running, you need to start the echo.socket unit behind it, like this:

要运行这个示例服务单元,需要启动它后面的 echo.socket 单元,如下所示:

# systemctl start echo.socket

Now you can test the service by connecting to your local port 22222. When the following telnet command connects, type anything and press ENTER. The service repeats what you typed back to you:

现在可以连接本地端口 22222 测试服务。

当以下 telnet 命令连接后,键入任何内容并按 ENTER 键。服务会重复你输入的内容:

$ telnet localhost 22222

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hi there.
Hi there.

When you’re bored with this, press CTRL-] on a line by itself, and then CTRL-D. To stop the service, stop the socket unit:

当你觉得无聊时,在一行上按 CTRL-],然后按 CTRL-D。

要停止服务,请停止插座单元:

# systemctl stop echo.socket
Instances and Handoff(实例和交接)

Because the echo@.service unit supports multiple simultaneous instances, there’s an @ in the name (recall from Note that @ signifies parameterization). Why would you want multiple instances? The reason is that you may have more than one network client connecting to the service at the same time, and each connection should have its own instance.

因为echo@.service单元支持多个同时实例,所以名称中有一个@符号(请回忆一下@表示参数化的意思)。为什么需要多个实例?原因是您可能有多个网络客户端同时连接到服务,每个连接都应该有自己的实例。

In this case, the service unit must support multiple instances because of the Accept option in echo.socket. That option instructs systemd not only to listen on the port, but also to accept incoming connections and pass the incoming connections on to the service unit, with each connection a separate instance. Each instance reads data from the connection as standard input, but it doesn’t necessarily need to know that the data is coming from a network connection.

在这种情况下,由于echo.socket中的Accept选项,服务单元必须支持多个实例。该选项指示systemd不仅监听端口,还接受传入连接并将传入连接传递给服务单元,每个连接都是一个单独的实例。每个实例从连接中读取数据作为标准输入,但它不一定需要知道数据来自网络连接。

NOTE Most network connections require more flexibility than just a simple gateway to standard input and output, so don’t expect to be able to create network services with a service unit file like the echo@.service unit file shown here.

注意:大多数网络连接需要比简单的标准输入和输出网关更多的灵活性,因此不要指望能够像这里显示的echo@.service单元文件那样创建网络服务。

Although the service unit could do all of the work of accepting the connection, it wouldn’t have the @ in its name if it did. In that case, it would take complete control of the socket, and systemd wouldn’t attempt to listen on the network port again until the service unit has finished.

虽然服务单元可以完成接受连接的所有工作,但如果是这样,它的名称中就不会有@符号。在这种情况下,它将完全控制套接字,直到服务单元完成后,systemd才会再次尝试监听网络端口。

The many different resources and options for handoff to service units make it difficult to provide a categorical summary. Also, the documentation for the options is spread out over several manual pages. The ones to check for the resource-oriented units are systemd.socket(5), systemd.path(5), and systemd.device(5). One document that’s often overlooked for service units is systemd.exec(5), which contains information about how the service unit can expect to receive a resource upon activation.

对于交接到服务单元的许多不同资源和选项,很难提供一个分类总结。

此外,有关这些选项的文档分散在几个手册页面上。需要检查的资源导向单元的文档有systemd.socket(5)、systemd.path(5)和systemd.device(5)。

对于服务单元,经常被忽视的一个文档是systemd.exec(5),其中包含了关于服务单元如何在激活时接收资源的信息。

6.4.8 systemd System V Compatibility(systemd 系统 V 兼容性)

One feature that sets systemd apart from other newer-generation init systems is that it tries to do a more complete job of tracking services started by System V–compatible init scripts. It works like this:

一个将systemd与其他新一代init系统区分开来的特点是,它试图更完整地跟踪由System V兼容的init脚本启动的服务。其工作方式如下:

  1. First, systemd activates runlevel< N>.target, where N is the runlevel.
  2. For each symbolic link in /etc/rc< N>.d, systemd identifies the script in /etc/init.d.
  3. systemd associates the script name with a service unit (for example, /etc/init.d/foo would be foo.service).
  4. systemd activates the service unit and runs the script with either a start or stop argument, based on its name in rc< N>.d.
  5. systemd attempts to associate any processes from the script with the service unit.
  6. 首先,systemd激活runlevel< N>.target,其中N是运行级别。
  7. 对于/etc/rc< N>.d中的每个符号链接,systemd识别出/etc/init.d中的脚本。
  8. systemd将脚本名称与服务单元关联起来(例如,/etc/init.d/foo将变为foo.service)。
  9. systemd激活服务单元,并根据rc< N>.d中脚本的名称使用start或stop参数运行脚本。
  10. systemd尝试将脚本中的任何进程与服务单元关联起来。

Because systemd makes the association with a service unit name, you can use systemctl to restart the service or view its status. But don’t expect any miracles from System V compatibility mode; it still must run the init scripts serially, for example.

由于systemd将关联与服务单元名称,您可以使用systemctl来重新启动服务或查看其状态。但不要对System V兼容模式抱有太高期望;例如,它仍然必须按顺序运行init脚本。

6.4.9 systemd Auxiliary Programs(systemd 辅助程序)

When starting out with systemd, you may notice the exceptionally large number of programs in /lib/systemd. These are primarily support programs for units. For example, udevd is part of systemd, and you’ll find it there as systemd-udevd. Another, the systemd-fsck program, works as a middleman between systemd and fsck.

在开始使用systemd时,您可能会注意到/lib/systemd目录中异常庞大的程序数量。

这些程序主要是单位的支持程序。

例如,udevd是systemd的一部分,您会在那里找到它作为systemd-udevd。

另一个例子是systemd-fsck程序,它在systemd和fsck之间充当中间人。

Many of these programs exist because they contain notification mechanisms that the standard system utilities lack. Often, they simply run the standard system utilities and notify systemd of the results. (After all, it would be silly to try to reimplement all of fsck inside systemd.)

这些程序中的许多存在是因为它们包含了标准系统工具所缺少的通知机制。

通常情况下,它们只是运行标准系统工具并通知systemd结果。

(毕竟,在systemd内部重新实现所有的fsck将是愚蠢的。)

NOTE One other interesting aspect of these programs is that they are written in C, because one goal of systemd is to reduce the number of shell scripts on a system. There is some debate as to whether it’s a good idea to do so (after all, many of these programs could probably be written as shell scripts), but as long as everything works and does so reliably, securely, and reasonably quickly, there’s little reason to bother taking sides.

注意这些程序的另一个有趣之处是它们是用C编写的,因为systemd的一个目标是减少系统上的shell脚本数量。

关于是否这样做是个好主意存在一些争议(毕竟,许多这些程序可能可以用shell脚本编写),但只要一切正常且可靠、安全、相对快速地运行,就没有理由去纠结。

When you see a program in /lib/systemd that you can’t identify, see the manual page. There’s a good chance that the manual page will not only describe the utility but also describe the type of unit that it’s meant to augment.

当您在/lib/systemd中看到一个无法识别的程序时,请查看其手册页。

很有可能手册页不仅描述了该实用程序,还描述了它所用于扩展的单位类型。

If you’re not running (or interested in) Upstart, skip ahead to 6.6 System V init for an overview of the System V init process.

如果您不在运行(或对)Upstart感兴趣,请跳到6.6节System V init,了解System V init过程的概述。

6.5 Upstart(后起之秀)

The Upstart version of init revolves around jobs and events. Jobs are startup and runtime actions for Upstart to perform (such as system services and configuration), and events are messages that Upstart receives from itself or other processes (such as udevd). Upstart works by starting jobs in response to events.

Upstart版本的init围绕着作业(jobs)和事件(events)展开。

作业是Upstart执行的启动和运行时操作(例如系统服务和配置),而事件是Upstart从自身或其他进程接收到的消息(例如udev)。

Upstart通过响应事件来启动作业。

To get an idea of how this works, consider the udev job for starting the udevd daemon. Its configuration file is typically /etc/init/udev.conf, which includes the following:

为了了解这个过程是如何工作的,可以考虑启动udev守护进程的udev作业。

它的配置文件通常是/etc/init/udev.conf,包含以下内容:

start on virtual-filesystems
stop on runlevel [06]

These lines mean that Upstart starts the udev job upon receiving the virtual-filesystems event, and it stops the job upon receiving a runlevel event with an argument of 0 or 6.

这几行的意思是,Upstart 在收到 virtual-filesystems 事件时启动 udev 作业,并在收到参数为 0 或 6 的 runlevel 事件时停止作业。

There are many variations on events and their arguments. For example, Upstart can react to events emitted in response to job status, such as the started udev event emitted by the udev job above. But before explaining jobs in detail, here’s a high-level overview of how Upstart works.

事件及其参数有很多变化。例如,Upstart 可以对响应作业状态的事件做出反应,如上述由 udev 作业发出的已启动 udev 事件。

在详细解释作业之前,我们先来了解一下 Upstart 的工作原理。

6.5.1 Upstart Initialization Procedure(启动初始化程序)

Upon startup, Upstart does the following:

在启动时,Upstart执行以下操作:

  1. Loads its configuration and the job configuration files in /etc/init.
  2. Emits the startup event.
  3. Runs jobs configured to start upon receiving the startup event.
  4. These initial jobs emit their own events, triggering more jobs and events.
  5. 加载其配置和位于/etc/init目录下的作业配置文件。
  6. 发出启动事件。
  7. 运行配置为在接收到启动事件后启动的作业。
  8. 这些初始作业会发出自己的事件,从而触发更多的作业和事件。

Upon finishing all jobs associated with a normal startup, Upstart continues to monitor and react to events during the entire system uptime.

在完成与正常启动相关的所有作业后,Upstart会继续监视并响应整个系统的运行时间内发生的事件。

Most Upstart installations run like this:

大多数Upstart安装都是以以下方式运行的:

  1. The most significant job that Upstart runs in response to the startup event is mountall. This job
    attaches all necessary local and virtual filesystems to the currently running system so that everything else can run.
  2. The mountall job emits a number of events, including filesystem, virtual-filesystems, local-filesystems, remote-filesystems, and all-swaps, among others. These events indicate that the important filesystems on the system are now attached and ready.
  3. In response to these events, Upstart starts a number of essential service jobs. For example, udev starts in response to the virtual-filesystems event, and dbus starts in response to the localfilesystems event.
  4. Among the essential service jobs, Upstart starts the network-interfaces job, usually in response to the local-filesystems event and udevd being ready.
  5. The network-interfaces job emits the static-network-up event.
  6. Upstart runs the rc-sysinit job in response to the filesystem and static-network-up events. This job is responsible for maintaining the system’s current runlevel, and when started for the first time without a runlevel, it switches the system to the default runlevel by emitting a runlevel event.
  7. Upstart runs most of the other startup jobs on the system in response to the runlevel event and the new runlevel.
  8. Upstart对启动事件的响应中最重要的作业是mountall。该作业会将所有必要的本地和虚拟文件系统附加到当前运行的系统,以便其他所有内容都能运行。
  9. mountall作业会发出多个事件,包括filesystem、virtual-filesystems、local-filesystems、remote-filesystems和all-swaps等。这些事件表示系统上的重要文件系统现在已经附加并准备就绪。
  10. 作为对这些事件的响应,Upstart会启动一些基本的服务作业。例如,udev会在虚拟文件系统事件发生时启动,dbus会在本地文件系统事件发生时启动。
  11. 在基本的服务作业中,Upstart会启动network-interfaces作业,通常是在本地文件系统事件和udev准备就绪时启动。
  12. network-interfaces作业会发出static-network-up事件。
  13. Upstart会在文件系统和static-network-up事件的响应中运行rc-sysinit作业。该作业负责维护系统的当前运行级别,并在第一次启动时没有运行级别时通过发出一个runlevel事件将系统切换到默认运行级别。
  14. Upstart会在runlevel事件和新的运行级别的响应中运行系统上的大多数其他启动作业。

The process can become complicated because it’s not always clear where events originate. Upstart emits only a few events, and the rest come from jobs. Job configuration files usually declare the events that they will emit, but the details of how the job emits the events are usually not in the Upstart job configuration files.

这个过程可能变得复杂,因为不总是清楚事件的来源。

Upstart只发出少量的事件,其余的事件都来自作业。

作业配置文件通常声明它们将要发出的事件,但作业如何发出事件的详细信息通常不在Upstart作业配置文件中。

To get to the bottom of things, you’ll often have to dig. For example, consider the static-network-up event. The network-interface.conf job configuration file says that it emits this event, but it doesn’t say where. It turns out that the event stems from the ifup command, which this job runs when initializing a network interface in the /etc/network/if-up.d/upstart script.

要深入了解情况,通常需要进行深入挖掘。

例如,考虑static-network-up事件。

network-interface.conf作业配置文件表示它会发出此事件,但没有说明事件的来源。

事实证明,该事件源自ifup命令,在初始化/etc/network/if-up.d/upstart脚本中运行此作业时。

NOTE Though all of this is documented (the ifup.d directory is in the interfaces(5) manual page referenced by the ifup(8) manual page), it can be challenging to find out how this all works just by reading the documentation. It’s usually faster to grep the event name in a lot of configuration files to see what comes up, then to try to piece everything back together from there.

注意:尽管所有这些都有文档记录(ifup.d目录在ifup(8)手册页引用的interfaces(5)手册页中有说明),但仅通过阅读文档来了解所有这些是有挑战性的。

通常更快的方法是在许多配置文件中使用grep命令搜索事件名称,然后从中尝试将所有内容拼凑在一起。

One issue with Upstart is that there’s currently no clear way to view events. You can turn its log priority to debug, which will cause it to log everything that comes in (typically to /var/log/syslog), but the copious amount of extraneous information in this file makes it difficult to determine an event’s context.

Upstart的一个问题是目前没有明确的方法来查看事件。

您可以将其日志优先级设置为debug,这将导致它记录所有收到的内容(通常记录在/var/log/syslog中),但是该文件中大量的无关信息使得很难确定事件的上下文。

6.5.2 Upstart Jobs

Each file in the Upstart /etc/init configuration directory corresponds to a job, and the main configuration file for each job has a .conf extension. For example, /etc/init/mountall.conf defines the mountall job.

Upstart /etc/init配置目录中的每个文件对应一个作业,每个作业的主配置文件都有一个.conf扩展名。例如,/etc/init/mountall.conf定义了mountall作业。

There are two primary kinds of Upstart jobs:

Upstart作业主要有两种类型:

o Task jobs. These are jobs with a clear end. For example, mountall is a task job because it terminates when finished mounting filesystems.
o Service jobs. These jobs have no defined stop. Servers (daemons) such as udevd, database servers, and web servers are all service jobs.

  • 任务作业。这些作业有明确的结束点。例如,mountall是一个任务作业,因为在完成挂载文件系统后会终止。
  • 服务作业。这些作业没有定义的停止点。诸如udevd、数据库服务器和Web服务器等服务器(守护进程)都属于服务作业。

A third kind of job is an abstract job. Think of this as a kind of virtual service job. Abstract jobs exist only in Upstart and start nothing by themselves, but they are sometimes used as management tools for other jobs because other jobs can start and stop based on the events coming from an abstract job.

第三种作业是抽象作业。可以将其视为一种虚拟的服务作业。抽象作业仅存在于Upstart中,并且本身不启动任何内容,但有时被用作其他作业的管理工具,因为其他作业可以根据来自抽象作业的事件进行启动和停止。

Viewing Jobs(查看作业)

You can view Upstart jobs and job status with the initctl command. To get an overview of what’s happening on your system, run:

可以使用initctl命令查看Upstart作业和作业状态。要获取系统上正在发生的情况概述,请运行:

$ initctl list

You’ll get a lot of output, so let’s just look at two sample jobs that might appear in a typical listing. Here’s a simple example of a task job status:

mountall stop/waiting

This indicates that the mountall task job has a status of stop/waiting, meaning that it’s not running. (Unfortunately, as of this writing, you can’t use the status to determine whether a job already ran or not because stop/waiting also applies to jobs that have never run.)

这表明 mountall 任务作业的状态是停止/等待,即没有运行。(遗憾的是,截至本文撰写时,您还不能使用状态来判断任务是否已运行,因为停止/等待也适用于从未运行过的任务)。

Service jobs that have associated processes appear in the status listing as follows:

有相关进程的服务任务在状态列表中显示如下:

tty1 start/running, process 1634

This line shows that the tty1 job is running and that process ID 1634 is performing the job. (Not all service jobs have associated processes.)

这一行显示了tty1作业正在运行,并且进程ID 1634正在执行该作业。

(并非所有的服务作业都有关联的进程。)

NOTE If you know a job’s name, you can view its status directly with initctl status job.

注意:如果您知道作业的名称,可以使用initctl status job命令直接查看其状态。

The status portion of the initctl output (e.g., stop/waiting) can be confusing. The left-hand side (before the /) is the goal, or what the job is supposed to be working toward, such as start or stop. The righthand side is the current job state, or what the job is doing right now, such as waiting or running. For example, in the preceding listing, the tty1 job has the status start/running, meaning that its goal is to start. The state of running indicates that it has started successfully. (For service jobs, the running state is nominal.)

initctl输出的状态部分(例如stop/waiting)可能会让人感到困惑。

左侧(斜杠之前)是目标,即作业应该朝着什么方向努力,例如start或stop。

右侧是当前作业状态,即作业当前正在做什么,例如waiting或running。

例如,在前面的列表中,tty1作业的状态是start/running,表示它的目标是启动。

running状态表示它已经成功启动。

(对于服务作业来说,running状态是正常的。)

The mountall case is a bit different because task jobs don’t remain running. The stop/waiting status usually indicates that the job started and completed its task. Upon completing its task, it moved from a start to a stop goal, and it is now waiting for further commands from Upstart.

mountall情况有些不同,因为任务作业不会保持运行状态。

stop/waiting状态通常表示作业已经启动并完成了任务。

完成任务后,它从start目标转变为stop目标,现在正在等待Upstart的进一步命令。

Unfortunately, as mentioned earlier, because jobs that have never started also have an Upstart stop/waiting status, you can’t really tell whether a job has run or never started unless you enable debugging and look at the logs, as described in 6.5.5 Upstart Logs.

不幸的是,正如前面提到的,由于从未启动的作业也具有Upstart的stop/waiting状态,因此除非您启用调试并查看日志(如6.5.5 Upstart日志中所述),否则无法确定作业是否运行或从未启动。

NOTE You won’t see jobs running on your system that were started with Upstart’s System V compatibility feature.

注意:使用Upstart的System V兼容性功能启动的系统上不会看到正在运行的作业。

Job State Transitions

There are many job states, but there’s a set way to move between them. For example, here’s how a typical job starts:

有许多工作状态,但在它们之间有一种固定的转换方式。

例如,以下是一个典型工作的开始过程:

  1. All jobs begin in the stop/waiting status.
  2. When a user or a system event starts a job, the job’s goal changes from stop to start.
  3. Upstart changes the job’s state from waiting to starting, so the status is now start/starting.
  4. Upstart emits a starting job event.
  5. The job performs whatever it needs to do for the starting state.
  6. Upstart changes the job’s state from starting to pre-start and emits the pre-start job event.
  7. The job works its way through several more states until it hits the running state.
  8. Upstart emits a started job event.
  9. 所有工作都从停止/等待状态开始。
  10. 当用户或系统事件启动一个工作时,工作的目标从停止变为启动。
  11. Upstart将工作的状态从等待改变为启动中,因此状态现在是启动/正在启动。
  12. Upstart发出一个启动工作事件。
  13. 工作执行启动状态所需的操作。
  14. Upstart将工作的状态从启动中改变为预启动,并发出预启动工作事件。
  15. 工作通过几个其他状态逐步进行,直到达到运行状态。
  16. Upstart发出一个已启动工作事件

Task termination involves a similar set of state changes and events. (See the upstart-events(7) manual page for details on all of the states and transitions in both goals.)

任务终止涉及一系列类似的状态变化和事件。

(有关目标中所有状态和转换的详细信息,请参阅upstart-events(7)手册页。)

6.5.3 Upstart Configuration

Let’s examine the two configuration files: one for the task job mountall and the other for the service job tty1. Like all Upstart configuration files, the configuration files are in /etc/init, and they are named mountall.conf and tty1.conf. The configuration files are organized into smaller pieces called stanzas. Each stanza starts with a leading keyword, such as description or start.

让我们来看一下两个配置文件:一个是用于任务作业mountall的配置文件,另一个是用于服务作业tty1的配置文件。

与所有Upstart配置文件一样,这些配置文件位于/etc/init目录下,分别命名为mountall.conf和tty1.conf。

配置文件被组织成称为段落的较小部分。

每个段落以一个关键词开头,例如description或start。

To get started, open the mountall.conf file on your system. Look for a line like this in the first stanza:

首先,打开您系统上的mountall.conf文件。在第一个段落中找到类似以下内容的行:

description "Mount filesystems on boot"

This stanza gives a short text description of the job.

这个段落提供了对作业的简短文本描述。

Next you’ll see a few stanzas describing how the mountall job starts:

接下来,您将看到几个描述mountall作业如何启动的段落:

start on startup
stop on starting rcS

Here, the first line tells Upstart to start the job upon receiving the startup event (the initial event that Upstart emits). The second line tells Upstart to terminate the job upon receiving the rcS event, when the system goes into single-user mode.

这里,第一行告诉Upstart在接收到启动事件(Upstart发出的初始事件)时启动作业。

第二行告诉Upstart在接收到rcS事件时终止作业,当系统进入单用户模式时。

The next two lines tell Upstart how the mountall job behaves:

接下来的两行告诉Upstart mountall作业的行为方式:

expect daemon
task

The task stanza tells Upstart that this is a task job, so the job should complete at some point. The expect stanza is tricky. It means that the mountall job will spawn a daemon that will operate independently of the original job script. Upstart needs to know this because it must know when the daemon terminates in order to correctly signal that the mountall job has terminated. (We’ll discuss this in more detail in Process Tracking and the Upstart expect Stanza.)

任务段落告诉Upstart这是一个任务作业,所以作业应该在某个时间点完成。

期望段落比较棘手。它意味着mountall作业将会生成一个独立于原始作业脚本的守护进程。

Upstart需要知道这一点,因为它必须知道守护进程何时终止,以便正确地发出mountall作业已终止的信号。

(我们将在进程追踪和Upstart期望段落的更多细节中讨论这一点。)

The mountall.conf file continues with several emits stanzas, indicating events that the jobs produce:

mountall.conf文件继续使用了几个发出事件的段落,指示作业产生的事件:

emits virtual-filesystems
emits local-filesystems
emits remote-filesystems
emits all-swaps
emits filesystem
emits mounting
emits mounted

NOTE As mentioned in 6.5.1 Upstart Initialization Procedure, even though these lines are present, this is not the actual source of the events. You’ll need to hunt through the job script to find them.

注意:正如在6.5.1 Upstart初始化过程中提到的那样,尽管这些行存在,但这并不是事件的实际来源。你需要查找作业脚本以找到它们。

You may also see a console stanza stating where Upstart should send the output:

您还可能会看到一个控制台段落,指示Upstart应该将输出发送到哪里:

console output

With the output parameter, Upstart sends the mountall job’s output to the system’s console.

通过输出参数,Upstart 会将 mountall 作业的输出发送到系统控制台。

Now you’ll see the details of the job itself—in this case, with a script stanza:

现在,你将看到作业本身的详细信息--在本例中,是一个脚本节:

script
 . /etc/default/rcS
 [ -f /forcefsck ] && force_fsck="--force-fsck"
 [ "$FSCKFIX" = "yes" ] && fsck_fix="-fsck-fix"
 # set $LANG so that messages appearing in plymouth are translated
 if [ -r /etc/default/locale ]; then
 . /etc/default/locale
 export LANG LANGUAGE LC_MESSAGES LC_ALL
 fi
 exec mountall --daemon $force_fsck $fsck_fix
end script

This is a shell script (see Chapter 11), most of which is preparatory— setting locale and determining whether an fsck is necessary. The exec mountall command near the bottom of this script is where the real action happens. This command mounts the filesystems and emits the job’s events when finished.

这是一个shell脚本(见第11章),其中大部分是准备工作 - 设置区域设置并确定是否需要进行fsck。

在脚本底部附近的exec mountall命令是真正的操作发生的地方。

该命令在完成后挂载文件系统并发出作业的事件。

A Service Job: tty1(一个服务作业:tty1)

The service job tty1 is much simpler; it controls a virtual console login prompt. Its entire configuration file, tty1.conf, looks like this:

服务作业tty1要简单得多;它控制一个虚拟控制台登录提示符。

它的整个配置文件tty1.conf如下所示:

start on stopped rc RUNLEVEL=[2345] and (
 not-container or
 container CONTAINER=lxc or
 container CONTAINER=lxc-libvirt)
 stop on runlevel [!2345]
respawn
exec /sbin/getty -8 38400 tty1

The most complicated part of this job is actually when it starts, but for now, ignore the container lines and concentrate on this portion:

实际上,这项工作最复杂的部分是在开始时,但现在,请忽略容器线,将注意力集中在这一部分:

start on stopped rc RUNLEVEL=[2345]

This part tells Upstart to activate the job upon receiving a stopped rc event from Upstart when the rc task job has run and terminated. To make the condition true, the rc job must also set the RUNLEVEL environment variable to a value from 2 through 5 (see 6.5.6 Upstart Runlevels and System V Compatibility).

这部分告诉Upstart在接收到来自Upstart的停止rc事件时激活作业,当rc任务作业运行并终止时。

为了使条件成立,rc作业还必须将RUNLEVEL环境变量设置为2到5之间的值(参见6.5.6 Upstart运行级别和System V兼容性)。

NOTE Other jobs that start on runlevels aren’t so picky. For example, you might see this instead:

注意:其他在运行级别上启动的作业并不那么挑剔。例如,你可能会看到下面这样的情况:

start on runlevel [2345]

The only real difference between these last two start stanzas is timing; this example activates the job as soon as the runlevel is set, while the prior one waits until the System V stuff finishes.

这最后两个启动段落之间唯一的真正区别在于时间;这个例子在运行级别设置后立即激活作业,而前一个则等到System V的相关操作完成后才开始。

The container configuration is there because Upstart not only runs directly on top of the Linux kernel on real hardware, but it can also run in virtual environments or containers. Some of these environments do not have virtual consoles, and you don’t want to run getty on a console that doesn’t exist.

容器配置的原因是因为Upstart不仅直接在真实硬件上的Linux内核上运行,还可以在虚拟环境或容器中运行。

其中一些环境没有虚拟控制台,而你又不希望在不存在的控制台上运行getty。

Stopping the tty1 job is straightforward:

停止tty1作业很简单:

stop on runlevel [!2345]

This stop stanza tells Upstart to terminate the job whenever the run-level is not 2 through 5 (for example, during system shutdown).

只要运行级别不是 2 到 5(例如,在系统关机时),stop stanza 就会告诉 Upstart 终止作业。

The exec stanza at the bottom is the command to run:

底部的 exec stanza 是要运行的命令:

exec /sbin/getty -8 38400 tty1

This stanza is much like the script stanza that you saw for the mountall job, except that the tty1 job has no complicated setup to perform—it’s easy to start with a single line. In this case, we’re running the login prompt program getty on /dev/tty1, which is the first virtual console (the one you get when you press CTRL-ALT-F1 in graphics mode).

这一节与你在mountall作业中看到的脚本节非常相似,只是tty1作业没有复杂的设置要执行 - 只需要一行即可开始。

在这种情况下,我们在/dev/tty1上运行登录提示程序getty,它是第一个虚拟控制台(当你在图形模式下按下CTRL-ALT-F1时会出现)。

The respawn stanza instructs Upstart to restart the tty1 job if the job terminates. In this case, Upstart runs a new getty for a new login prompt when you log out of the virtual console.

respawn节指示Upstart在作业终止时重新启动tty1作业。

在这种情况下,当您从虚拟控制台注销时,Upstart会运行一个新的getty以获取新的登录提示。

Those are the basics of Upstart configuration. You’ll find much more detail in the init(5) manual page and online resources, but one stanza requires special attention. The expect stanza is discussed next.

这些是Upstart配置的基础知识。

您将在init(5)手册页面和在线资源中找到更多详细信息,但有一个节需要特别注意。

下面将讨论expect节。

Process Tracking and the Upstart expect Stanza(进程跟踪和Upstart的expect Stanza)

Because Upstart tracks processes in jobs once they’ve started (so that it can terminate and restart them efficiently), it wants to know which processes are relevant to each job. This can be a difficult task, because in the traditional Unix startup scheme, processes fork from others during startup to become daemons, and the main process for a job may start after one or two forks. Without proper process tracking, Upstart won’t be able to finalize its job startup, or it may track the incorrect PID for the job.

因为Upstart在作业启动后会跟踪进程(以便能够高效地终止和重新启动它们),所以它想要知道哪些进程与每个作业相关。

这是一项困难的任务,因为在传统的Unix启动方案中,进程在启动过程中从其他进程中分叉出来成为守护进程,而作业的主进程可能在一两次分叉后启动。

如果没有正确的进程跟踪,Upstart将无法完成作业的启动,或者可能跟踪到错误的作业进程ID。

You tell Upstart how a job behaves with the expect stanza. There are four basic possibilities:

使用expect stanza来告诉Upstart作业的行为方式。有四种基本情况:

o No expect stanza The main job process does not fork. Track the main process.
o expect fork The process forks once. Track the forked process.
o expect daemon The process forks twice. Track the second fork.
o expect stop The job’s main process will raise a SIGSTOP signal to indicate that it is ready. (This is rare.)

o 没有expect stanza 主作业进程不会分叉。跟踪主进程。
o expect fork 进程分叉一次。跟踪分叉出来的进程。
o expect daemon 进程分叉两次。跟踪第二次分叉出来的进程。
o expect stop 作业的主进程会发出SIGSTOP信号以表示它已准备好。(这种情况很少见。)

For Upstart and other modern versions of init, such as systemd, the ideal case is the first one (no expect stanza), because the main job process doesn’t have to include any of its own startup and shutdown mechanics.

对于Upstart和其他现代版本的init(如systemd)来说,理想情况是第一种情况(没有expect stanza),因为主作业进程不需要包含任何自己的启动和关闭机制。

In other words, it doesn’t need to bother with forking or detaching itself from a current terminal—nuisances that Unix systems developers have had to deal with for years.

换句话说,它不需要在当前终端上进行分叉或分离——这些是Unix系统开发人员多年来必须处理的麻烦事。

Many traditional service daemons already include debugging-style options that tell the main process to not fork. Examples include the Secure Shell daemon, sshd, and its -D option. A look at the /etc/init/ssh.conf startup stanzas reveals a simple configuration to start sshd, prevent rapid respawning, and eliminate spurious output to stderr:

许多传统的服务守护进程已经包含了告诉主进程不要分叉的调试选项。

例如,Secure Shell守护进程sshd及其-D选项。

查看/etc/init/ssh.conf启动stanza可以看到一个简单的配置,用于启动sshd,防止快速重启,并消除stderr上的杂散输出:

respawn
respawn limit 10 5
umask 022
# 'sshd -D' leaks stderr and confuses things in conjunction with 'console 
log'
console none
--snip--
exec /usr/sbin/sshd -D

Among jobs that require an expect stanza, expect fork is the most common. For example, here’s the startup portion of the /etc/init/cron.conf file:

在需要 expect stanza 的工作中,expect fork 是最常见的。

例如,下面是 /etc/init/cron.conf 文件的启动部分:

expect fork
respawn
exec cron

A simple job startup like this usually indicates a well-behaved, stable daemon.

像这样的简单工作启动通常表明守护进程很乖、很稳定。

NOTE It’s worth reading more about the expect stanza on the upstart.ubuntu.com site because it relates directly to process life span. For example, you can trace the life of a process and its system calls, including fork(), with the strace command.

注:值得在 upstart.ubuntu.com 网站上阅读更多有关 expect stanza 的内容,因为它与进程寿命直接相关。

例如,你可以使用 strace 命令跟踪进程的生命周期及其系统调用,包括 fork()。

6.5.4 Upstart Operation(Upstart操作)

In addition to the list and status commands described in 6.5.2 Upstart Jobs, you can also use the initctl utility to control Upstart and its jobs. You should read the initctl(8) manual page at some point, but for now let’s look at the essentials.

除了6.5.2 Upstart作业中描述的列表和状态命令之外,您还可以使用initctl实用程序来控制Upstart及其作业。

您应该在某个时间点阅读initctl(8)手册页,但现在让我们看一下基本操作。

To start an Upstart job, use initctl start:

要启动一个Upstart作业,请使用initctl start命令:

# initctl start job

To stop a job, use initctl stop:

要停止作业,请使用 initctl stop:

# initctl stop job

To restart a job:

要重新启动作业:

# initctl restart job

If you need to emit an event to Upstart, you can do it manually with:

如果需要向 Upstart 发送一个事件,可以手动执行:

# initctl emit event

You can also add environment variables to the emitted event by adding key=value parameters after event.

你也可以在事件后添加 key=value 参数,将环境变量添加到发出的事件中。

NOTE You can’t start and stop individual services that started via Upstart’s System V compatibility feature. See 6.6.1 System V init: Startup Command Sequence for more on how to do this in a System V init script.

注意:无法启动和停止通过 Upstart 的 System V 兼容功能启动的单个服务。有关在 System V init 脚本中如何执行此操作的更多信息,请参阅 6.6.1 System V init:启动命令序列。

There are many ways to disable an Upstart job so that it will not start at boot time, but the most maintainable one is to determine the name of the job’s configuration file (usually /etc/init/.conf ) and then create a new file called /etc/init/.override containing only the line:

有许多方法可以禁用 Upstart 作业,使其在启动时不会启动,但最可维护的方法是确定作业配置文件的名称(通常为 /etc/init/.conf),然后创建一个名为 /etc/init/.override 的新文件,其中只包含一行:

manual

Now the only way that the job will start is by running initctl start job.

现在,启动作业的唯一方法就是运行 initctl start job。

The primary advantage to this method is that it’s easily reversible. To reenable the job at boot, remove the .override file.

这种方法的主要优点是很容易逆转。要在启动时重新启用作业,只需删除 .override 文件。

6.5.5 Upstart Logs

There are two basic kinds of logs in Upstart: service job logs, and diagnostic messages that Upstart itself produces. Service job logs record the standard output and standard error of the scripts and daemons that run the services. These messages, recorded in /var/log/upstart, are in addition to the standard syslog messages that a service may produce. (You’ll learn more about syslog in Chapter 7.) It’s hard to categorize what goes into these logs because there are no standards, but the most common contents are startup and shutdown messages, as well as emergency error messages. Many services produce no messages at all because they send everything to syslog or their own logging facility.

在Upstart中有两种基本类型的日志:服务作业日志和Upstart本身生成的诊断消息。

服务作业日志记录运行服务的脚本和守护进程的标准输出和标准错误。

这些消息记录在/var/log/upstart中,并且是服务可能生成的标准syslog消息的补充。

(关于syslog的更多信息请参见第7章)。

很难对这些日志中包含的内容进行分类,因为没有标准,但最常见的内容是启动和关闭消息,以及紧急错误消息。

许多服务根本不产生任何消息,因为它们将所有内容都发送到syslog或它们自己的日志记录设施。

Upstart’s own diagnostic log can contain information about when it starts and reloads, as well as certain information about jobs and events. This diagnostic log goes to the kernel syslog utility. On Ubuntu, you’ll usually find this log in the /var/log/kern.log file and the catchall /var/log/syslog file.

Upstart自己的诊断日志可能包含有关启动和重新加载的信息,以及有关作业和事件的某些信息。

这个诊断日志发送到内核syslog实用程序。

在Ubuntu上,您通常会在/var/log/kern.log文件和万能的/var/log/syslog文件中找到这个日志。

That said, by default, Upstart logs little to nothing, so to see anything at all in the logs, you must change the Upstart log priority. The name of the default priority is message. To log events and job changes on a running system, change the log priority to info:

话虽如此,默认情况下,Upstart几乎不记录任何内容,所以要在日志中看到任何内容,您必须更改Upstart日志优先级。

默认优先级的名称是message。

要记录正在运行的系统上的事件和作业更改,请将日志优先级更改为info:

# initctl log-priority info

Keep in mind that this won’t be permanent and the priority will reset after a reboot. To have Upstart log everything when it starts, add a --verbose parameter as a boot parameter, as described in 5.5 GRUB Introduction.

请注意,这不是永久性的,重启后优先级将重置。

要让 Upstart 在启动时记录所有内容,可在启动参数中添加 --verbose 参数,如 5.5 GRUB 介绍中所述。

6.5.6 Upstart Runlevels and System V Compatibility(启动运行级别和系统 V 兼容性)

So far, we’ve touched upon a few places where Upstart supports the idea of System V runlevels and mentioned that it has the ability to run System V startup scripts as a job. Here’s a more detailed overview of how it works on Ubuntu systems:

到目前为止,我们已经涉及了Upstart支持System V运行级别的几个地方,并提到它能够将System V启动脚本作为作业运行的能力。

下面是关于它在Ubuntu系统上的更详细概述:

  1. The rc-sysinit job runs, usually after getting the filesystem and static-network-up events. Before it runs, there is no runlevel.
  2. The rc-sysinit job determines which runlevel to enter. Usually, the run-level is the default, but it can also parse an older /etc/inittab file or take the runlevel from a kernel parameter (in /proc/cmdline).
  3. The rc-sysinit job runs telinit to switch the runlevel. The command emits a runlevel event, specifying the runlevel in the RUNLEVEL environment variable.
  4. Upstart receives the runlevel event. A number of jobs are configured to start on the runlevel event paired with a certain runlevel, and Upstart sets these in motion.
  5. One of the runlevel-activated task jobs, rc, is responsible for running the System V start. In order to do so, the rc job runs /etc/init.d/rc, just as System V init would (see 6.6 System V init).
  6. Once the rc job terminates, Upstart can start a number of other jobs upon receiving the stopped rc event (such as the tty1 job in A Service Job: tty1).
  7. rc-sysinit作业在获取文件系统和静态网络连接事件后运行,通常是在这些事件之后运行。在它运行之前,还没有运行级别。
  8. rc-sysinit作业确定要进入的运行级别。通常情况下,运行级别是默认的,但也可以解析旧的/etc/inittab文件或从内核参数(在/proc/cmdline中)获取运行级别。
  9. rc-sysinit作业运行telinit来切换运行级别。该命令会发出一个运行级别事件,并在RUNLEVEL环境变量中指定运行级别。
  10. Upstart接收到运行级别事件。许多作业被配置为在特定运行级别的运行级别事件上启动,并且Upstart启动这些作业。
  11. 运行级别激活的任务作业之一,rc,负责运行System V启动。为了做到这一点,rc作业运行/etc/init.d/rc,就像System V init一样(参见6.6 System V init)。
  12. 一旦rc作业终止,Upstart可以在接收到停止的rc事件后启动其他一些作业(例如在A Service Job: tty1中的tty1作业)。

Notice that although Upstart treats the runlevel no differently than any other event, many of the job configuration files on most Upstart systems refer to the runlevel.

请注意,尽管Upstart对待运行级别与其他事件没有任何区别,但大多数Upstart系统上的作业配置文件都与运行级别有关。

In any case, there is a critical point during boot when the filesystems are mounted and when most of the important system initialization is done. At this point, the system is ready to start higher-level system services such as graphical display managers and database servers. A runlevel event is handy for marking this point. You could configure Upstart to use any event as a trigger, though. One challenge comes when trying to determine which services start as Upstart jobs and which ones start in System V compatibility mode. The easiest way to find out is to look in your runlevel’s System V link farm (see 6.6.2 The System V init Link Farm). For example, if your runlevel is 2, look in /etc/rc2.d; anything there is likely running in System V compatibility mode.

无论如何,在启动过程中有一个关键点,即文件系统被挂载并完成大部分重要的系统初始化。

此时,系统已准备好启动更高级别的系统服务,如图形显示管理器和数据库服务器。

运行级别事件对于标记此点非常有用。

不过,您可以配置Upstart使用任何事件作为触发器。

当尝试确定哪些服务作为Upstart作业启动,哪些作为System V兼容模式启动时,可能会遇到一个挑战。

找出方法是查看运行级别的System V链接目录(参见6.6.2 System V init链接目录)。

例如,如果您的运行级别是2,请查看/etc/rc2.d;那里的任何内容都很可能是以System V兼容模式运行的。

NOTE One stumbling block may be the presence of dummy scripts in /etc/init.d. For any Upstart service job, there may also be a System V–style script for that service in /etc/init.d, but that script won’t do anything other than tell you that the service has been converted to an Upstart job. There also won’t be a link to the script in the System V link directory. If you run into a dummy script, find out the Upstart job name, and use initctl to control the job.

注意:一个障碍可能是/etc/init.d中存在虚拟脚本。对于任何Upstart服务作业,可能还有一个System V风格的脚本在/etc/init.d中,但该脚本除了告诉您该服务已转换为Upstart作业外,不会执行任何其他操作。

在System V链接目录中也不会有指向该脚本的链接。如果遇到虚拟脚本,请找出Upstart作业名称,并使用initctl来控制该作业。

6.6 System V init(系统 V 启动)

The System V init implementation on Linux dates to the early days of Linux; its core idea is to support an orderly bootup to different runlevels with a carefully sequenced process startup. Though System V is now uncommon on most desktop installations, you may encounter System V init in Red Hat Enterprise Linux, as well as in embedded Linux environments such as routers and phones.

Linux上的System V init实现可以追溯到Linux的早期阶段;其核心思想是通过仔细顺序的进程启动来支持有序的不同运行级别的启动。

虽然在大多数桌面安装中System V现在已经不常见,但在Red Hat Enterprise Linux以及嵌入式Linux环境(如路由器和手机)中,您可能会遇到System V init。

There are two major components to a typical System V init installation: a central configuration file and a large set of boot scripts augmented by a symbolic link farm. The configuration file /etc/inittab is where it all starts. If you have System V init, look for a line like the following in your inittab file:

典型的System V init安装有两个主要组件:一个中央配置文件和一个由符号链接组成的大型启动脚本集合。

配置文件/etc/inittab是一切的起点。

如果您使用System V init,请在inittab文件中查找类似下面这样的行:

id:5:initdefault: 

This indicates that the default runlevel is 5.

这表示默认运行级别为5。

All lines in inittab take the following form, with four fields separated by colons in this order:

inittab中的所有行都采用以下形式,按顺序由四个用冒号分隔的字段组成:

o A unique identifier (a short string, such as id in the previous example)
o The applicable runlevel number(s)
o The action that init should take (default runlevel to 5 in the previous example)
o A command to execute (optional)

o 唯一标识符(一个短字符串,例如前面示例中的id)
o 适用的运行级别号码
o init应该执行的操作(在前面示例中是将默认运行级别设置为5)
o 要执行的命令(可选)

To see how commands work in an inittab file, consider this line:

为了了解在inittab文件中命令的工作原理,考虑以下行:

l5:5:wait:/etc/rc.d/rc 5

This particular line is important because it triggers most of the system configuration and services. Here, the wait action determines when and how System V init runs the command: Run /etc/rc.d/rc 5 once when entering runlevel 5, then wait for this command to finish before doing anything else. To make a long story short, the rc 5 command executes anything in /etc/rc5.d that starts with a number (in the order of the numbers).

这一行很重要,因为它触发了大部分系统配置和服务。

在这里,wait操作确定System V init何时以及如何运行该命令:在进入运行级别5时运行一次/etc/rc.d/rc 5,然后等待该命令完成后再执行其他操作。

简而言之,rc 5命令执行以数字开头的/etc/rc5.d中的任何内容(按照数字的顺序)。

The following are some of the most common inittab actions in addition to initdefault and wait.

除了initdefault和wait之外,以下是一些常见的inittab操作。

respawn(重生)

The respawn action tells init to run the command that follows and, if the command finishes executing, to run it again. You’re likely to see something like this in an inittab file:

重生操作会告诉 init 运行后面的命令,如果命令执行完毕,则再次运行该命令。你可能会在 inittab 文件中看到类似的内容:

1:2345:respawn:/sbin/mingetty tty1

The getty programs provide login prompts. The line above is used for the first virtual console (/dev/tty1), which is the one you see when you press ALT-F1 or CTRL-ALT-F1 (see 3.4.4 Terminals: /dev/tty, /dev/pts/ , and /dev/tty). The respawn action brings the login prompt back after you log out.

getty程序提供登录提示。

上面的行用于第一个虚拟控制台(/dev/tty1),当您按下ALT-F1或CTRL-ALT-F1时,您会看到它(参见3.4.4 终端:/dev/tty、/dev/pts/ 和 /dev/tty)。

respawn操作在您注销后将登录提示带回。

ctrlaltdel

The ctrlaltdel action controls what the system does when you press CTRLALT-DEL on a virtual console. On most systems, this is some sort of reboot command, using the shutdown command (discussed in 6.7 Shutting Down Your System).

ctrlaltdel操作控制系统在虚拟控制台上按下CTRLALT-DEL时的操作。

在大多数系统上,这是一种重新启动命令,使用shutdown命令(在6.7 关闭系统中讨论)。

sysinit

The sysinit action is the first thing that init should run when starting, before entering any runlevels.

sysinit操作是init在启动之前应该运行的第一件事情,在进入任何运行级别之前。

NOTE For more available actions, see the inittab(5) manual page.

注意:更多可用的操作,请参阅inittab(5)手册页。

6.6.1 System V init: Startup Command Sequence(系统 V 启动 启动命令序列)

You are now ready to learn how System V init starts system services, just before it lets you log in. Recall this inittab line from earlier:

现在你可以学习 System V init 如何在让你登录之前启动系统服务了。

回想一下之前的 inittab 行:

l5:5:wait:/etc/rc.d/rc 5

This small line triggers many other programs. In fact, rc stands for run commands, which many people refer to as scripts, programs, or services. But where are these commands?

这个小行触发了许多其他程序。事实上,rc代表运行命令,许多人称之为脚本、程序或服务。但是这些命令在哪里呢?

The 5 in this line tells us that we’re talking about runlevel 5. The commands are probably either in /etc/rc.d/rc5.d or /etc/rc5.d. (Runlevel 1 uses rc1.d, runlevel 2 uses rc2.d, and so on.) For example, you might find the following items in the rc5.d directory:

这行中的5告诉我们我们正在谈论运行级别5。

这些命令可能在/etc/rc.d/rc5.d或/etc/rc5.d中。(运行级别1使用rc1.d,运行级别2使用rc2.d,依此类推。)

例如,您可能会在rc5.d目录中找到以下项目:

S10sysklogd S20ppp S99gpm
S12kerneld S25netstd_nfs S99httpd
S15netstd_init S30netstd_misc S99rmnologin
S18netbase S45pcmcia S99sshd
S20acct S89atd
S20logoutd S89cron

The rc 5 command starts programs in the rc5.d directory by executing the following commands in this sequence:

rc 5 命令按以下顺序执行,启动 rc5.d 目录中的程序:

S10sysklogd start
S12kerneld start
S15netstd_init start
S18netbase start
--snip--
S99sshd start

Notice the start argument in each command. The capital S in a command name means that the command should run in start mode, and the number (00 through 99) determines where in the sequence rc starts the command. The rc*.d commands are usually shell scripts that start programs in /sbin or /usr/sbin. Normally, you can figure out what a particular command does by viewing the script with less or another pager program.

请注意每个命令中的start参数。命令名称中的大写S表示该命令应以启动模式运行,而数字(00到99)确定了rc在序列中启动命令的位置。

rc*.d命令通常是启动/sbin或/usr/sbin中程序的shell脚本。

通常,您可以通过使用less或其他分页程序查看脚本来弄清楚特定命令的作用。

NOTE Some rc*.d directories contain commands that start with K (for “kill,” or stop mode). In this case, rc runs the command with the stop argument instead of start. You will most likely encounter K commands in runlevels that shut down the system.

注意:某些rc*.d目录包含以K开头(表示“kill”或停止模式)的命令。

在这种情况下,rc会使用stop参数而不是start来运行命令。

您很可能会在关闭系统的运行级别中遇到K命令。

You can run these commands by hand. However, you normally want to do so through the init.d directory instead of the rc * .d directories, which we’ll now describe.

您可以手动运行这些命令。但是,通常您希望通过init.d目录而不是rc*.d目录来执行这些命令,我们将在下面进行描述。

6.6.2 The System V init Link Farm(系统 V 启动链接农场)

The contents of the rc.d directories are actually symbolic links to files in yet another directory, init.d. If your goal is to interact with, add, delete, or modify services in the rc.d directories, you need to understand these symbolic links. A long listing of a directory such as rc5.d reveals a structure like this:

rc*.d目录的内容实际上是指向另一个目录init.d中的文件的符号链接。

如果你的目标是与rc*.d目录中的服务进行交互、添加、删除或修改,你需要理解这些符号链接。

像rc5.d这样的目录的长列表显示了以下结构:

lrwxrwxrwx . . . S10sysklogd -> ../init.d/sysklogd
lrwxrwxrwx . . . S12kerneld -> ../init.d/kerneld
lrwxrwxrwx . . . S15netstd_init -> ../init.d/netstd_init
lrwxrwxrwx . . . S18netbase -> ../init.d/netbase
--snip--
lrwxrwxrwx . . . S99httpd -> ../init.d/httpd
--snip--

A large number of symbolic links across several subdirectories such as this is called a link farm. Linux distributions contain these links so that they can use the same startup scripts for all runlevels. This convention is not a requirement, but it simplifies organization.

一个包含大量符号链接的目录结构,如此之多的链接被称为链接农场。

Linux发行版包含这些链接,以便可以为所有运行级别使用相同的启动脚本。

这种约定并非必需,但可以简化组织结构。

Starting and Stopping Services(启动和停止服务)

To start and stop services by hand, use the script in the init.d directory. For example, one way to start the httpd web server program manually is to run init.d/httpd start. Similarly, to kill a running service, you can use the stop argument (httpd stop, for instance).

要手动启动和停止服务,请使用init.d目录中的脚本。

例如,手动启动httpd Web服务器程序的一种方法是运行init.d/httpd start命令。

同样地,要停止正在运行的服务,可以使用stop参数(例如,httpd stop)。

Modifying the Boot Sequence(修改启动顺序)

Changing the boot sequence in System V init is normally done by modifying the link farm. The most common change is to prevent one of the commands in the init.d directory from running in a particular runlevel. You have to be careful about how you do this. For example, you might consider removing the symbolic link in the appropriate rc*.d directory. But beware: If you ever need to put the link back, you might have trouble remembering the exact name of the link. One of the best ways to do it is to add an underscore (_) at the beginning of the link name, like this:

在System V init中更改启动顺序通常是通过修改链接集合来完成的。

最常见的更改是阻止init.d目录中的某个命令在特定运行级别下运行。在执行此操作时需要小心。

例如,您可以考虑删除适当的rc*.d目录中的符号链接。

但要小心:如果您需要重新添加该链接,您可能会忘记链接的确切名称。

其中一种最好的方法是在链接名称的开头添加下划线(_),像这样:

# mv S99httpd _S99httpd

This change causes rc to ignore _S99httpd because the filename no longer starts with S or K, but the original name is still obvious.

这个改变导致rc忽略了_S99httpd,因为文件名不再以S或K开头,但原始名称仍然很明显。

To add a service, create a script like those in the init.d directory and then create a symbolic link in the correct rc*.d directory. The easiest way is to copy and modify one of the scripts already in init.d that you understand (see Chapter 11 for more information on shell scripts).

要添加一个服务,需要创建一个类似于init.d目录中的脚本,然后在正确的rc*.d目录中创建一个符号链接。

最简单的方法是复制并修改你已经理解的init.d中的一个脚本(有关shell脚本的更多信息,请参见第11章)。

When adding a service, choose an appropriate place in the boot sequence to start it. If the service starts too soon, it may not work, due to a dependency on some other service. For nonessential services, most systems administrators prefer numbers in the 90s, which puts the services after most of the services that came with the system.

在添加服务时,选择一个适当的启动顺序来启动它。

如果服务启动得太早,可能由于对其他服务的依赖而无法正常工作。

对于非必要的服务,大多数系统管理员喜欢使用90年代的数字,这样可以将服务放在系统自带服务之后。

6.6.3 run-parts(运行部件)

The mechanism that System V init uses to run the init.d scripts has found its way into many Linux systems, regardless of whether they use System V init. It’s a utility called run-parts, and the only thing it does is run a bunch of executable programs in a given directory, in some kind of predictable order. You can think of it as almost like a person who runs the ls command in some directory and then just runs whatever programs they see in the output.

System V init使用的机制已经在许多Linux系统中得到了应用,无论它们是否使用System V init。

这个实用程序被称为run-parts,它的唯一功能就是按照某种可预测的顺序运行给定目录中的一系列可执行程序。

你可以把它想象成一个人在某个目录中运行ls命令,然后按照输出中看到的运行相应的程序。

The default behavior is to run all programs in a directory, but you often have the option to select certain programs and ignore others. In some distributions, you don’t need much control over the programs that run. For example, Fedora ships with a very simple run-parts utility

默认行为是运行目录中的所有程序,但通常你可以选择运行某些程序并忽略其他程序。

在某些发行版中,你不需要对运行的程序有太多控制。

例如,Fedora附带了一个非常简单的run-parts实用程序。

Other distributions, such as Debian and Ubuntu, have a more complicated run-parts program. Their features include the ability to run programs based on a regular expression (for example, using the S[0-9]{2} expression for running all “start” scripts in an /etc/init.d runlevel directory) and to pass arguments to the programs. These capabilities allow you to start and stop System V runlevels with a single command.

其他发行版,如Debian和Ubuntu,有一个更复杂的run-parts程序。

它们的功能包括根据正则表达式运行程序(例如,使用S[0-9]{2}表达式来运行/etc/init.d运行级别目录中的所有“start”脚本)以及向程序传递参数。

这些功能使您能够使用单个命令启动和停止System V运行级别。

You don’t really need to understand the details of how to use run-parts; in fact, most people don’t know that run-parts even exists. The main things to remember are that it shows up in scripts from time to time and that it exists solely to run the programs in a given directory.

实际上,您不需要了解如何使用run-parts的详细信息;

事实上,大多数人都不知道run-parts的存在。

主要需要记住的是它会偶尔出现在脚本中,并且它的存在只是为了运行给定目录中的程序。

6.6.4 Controlling System V init(控制系统 V 启动)

Occasionally, you’ll need to give init a little kick to tell it to switch runlevels, to reread its configuration, or to shut down the system. To control System V init, use telinit. For example, to switch to runlevel 3, enter:

有时候,你需要给init一个小小的推动,告诉它切换运行级别,重新读取配置文件或关闭系统。要控制System V init,请使用telinit命令。例如,要切换到运行级别3,输入以下命令:

# telinit 3

When switching runlevels, init tries to kill off any processes not in the inittab file for the new runlevel, so be careful when changing runlevels.

在切换运行级别时,init会尝试关闭任何未在新运行级别的inittab文件中的进程,因此在更改运行级别时要小心。

When you need to add or remove jobs, or make any other change to the inittab file, you must tell init about the change and cause it to reload the file. The telinit command for this is:

当你需要添加或删除作业,或对inittab文件进行任何其他更改时,必须告诉init这些变化并使其重新加载文件。用于此目的的telinit命令是:

# telinit q

You can also use telinit s to switch to single-user mode (see 6.9 Emergency Booting and Single-User Mode).

您也可以使用 telinit s 切换到单用户模式(参见 6.9 紧急启动和单用户模式)。

6.7 Shutting Down Your System(关闭系统)

init controls how the system shuts down and reboots. The commands to shut down the system are the same regardless of which version of init you run. The proper way to shut down a Linux machine is to use the shutdown command.

init 控制着系统关闭和重启的方式。无论运行哪个版本的 init,关闭系统的命令都是一样的。关闭 Linux 机器的正确方法是使用 shutdown 命令。

There are two basic ways to use shutdown. If you halt the system, it shuts the machine down and keeps it down. To make the machine halt immediately, run this:

使用 shutdown 有两种基本方法。如果你停止系统,它就会关闭机器并使其继续运行。要使机器立即停止,请运行以下命令:

# shutdown -h now

On most machines and versions of Linux, a halt cuts the power to the machine. You can also reboot the machine. For a reboot, use -r instead of -h.

在大多数机器和 Linux 版本中,停止运行会切断机器电源。您也可以重新启动机器。重启时,使用 -r 而不是 -h。

The shutdown process takes several seconds. You should never reset or power off a machine during this stage

关机过程需要几秒钟。

切勿在此阶段重置或关闭机器电源。

In the preceding example, now is the time to shut down. This argument is mandatory, but there are many ways to specify the time. For example, if you want the machine to shut down sometime in the future, you can use +n, where n is the number of minutes shutdown should wait before doing its work. (For other options, see the shutdown(8) manual page.)

在前面的示例中,现在是关机的时间。

这个参数是强制性的,但也有很多指定时间的方法。

例如,如果想让机器在未来某个时间关闭,可以使用 +n,其中 n 是 shutdown 在执行工作前应等待的分钟数。

(有关其他选项,请参阅 shutdown(8) 手册)。

To make the system reboot in 10 minutes, enter:

要使系统在 10 分钟后重启,请输入

# shutdown -r +10

On Linux, shutdown notifies anyone logged on that the machine is going down, but it does little real work. If you specify a time other than now, the shutdown command creates a file called /etc/nologin. When this file is present, the system prohibits logins by anyone except the superuser.

在Linux上,关机会通知所有登录用户机器即将关机,但实际上并没有做太多的工作。

如果你指定了一个非当前时间的关机时间,关机命令会创建一个名为/etc/nologin的文件。

当这个文件存在时,系统会禁止除超级用户外的任何用户登录。

When system shutdown time finally arrives, shutdown tells init to begin the shutdown process. On systemd, it means activating the shutdown units; on Upstart, it means emitting the shutdown events; and on System V init, it means changing the runlevel to 0 or 6. Regardless of the init implementation or configuration, the procedure generally goes like this:

当系统关机时间到达时,关机命令会告诉init开始关机过程。

在systemd上,这意味着激活关机单元;在Upstart上,这意味着发出关机事件;在System V init上,这意味着将运行级别更改为0或6。

无论init的实现方式或配置如何,关机过程通常如下所示:

  1. init asks every process to shut down cleanly.
  2. If a process doesn’t respond after a while, init kills it, first trying a TERM signal.
  3. If the TERM signal doesn’t work, init uses the KILL signal on any stragglers.
  4. The system locks system files into place and makes other preparations for shutdown.
  5. The system unmounts all filesystems other than the root.
  6. The system remounts the root filesystem read-only.
  7. The system writes all buffered data out to the filesystem with the sync program.
  8. The final step is to tell the kernel to reboot or stop with the reboot(2) system call. This can be done by init or an auxiliary program such as reboot, halt, or poweroff.
  9. init要求每个进程干净地关闭。
  10. 如果一个进程在一段时间后没有响应,init会发送TERM信号杀死它。
  11. 如果TERM信号不起作用,init会对任何未关闭的进程发送KILL信号。
  12. 系统锁定系统文件并做其他关机准备工作。
  13. 系统卸载除根文件系统外的所有文件系统。
  14. 系统将根文件系统重新挂载为只读。
  15. 系统使用sync程序将所有缓冲数据写入文件系统。
  16. 最后一步是使用reboot(2)系统调用告诉内核重新启动或停止。这可以由init或reboot、halt、poweroff等辅助程序完成。

The reboot and halt programs behave differently depending on how they’re called, which may cause confusion. By default, these programs call shutdown with the -r or -h options. However, if the system is already at a halt or reboot runlevel, the programs tell the kernel to shut itself off immediately. If you really want to shut your machine down in a hurry, regardless of any potential damage from a disorderly shutdown, use the -f (force) option.

reboot和halt程序的行为取决于它们的调用方式,这可能会引起混淆。

默认情况下,这些程序会使用-r或-h选项调用shutdown。

然而,如果系统已经处于停机或重启运行级别,这些程序会告诉内核立即关闭。

如果你真的想要快速关机,不考虑非正常关机可能造成的潜在损害,可以使用-f(强制)选项。

6.8 The Initial RAM Filesystem(初始 RAM 文件系统)

The Linux boot process is, for the most part, fairly straightforward. However, one component has always been somewhat confounding: initramfs, or the intitial RAM filesystem. Think of this as a little user-space wedge that goes in front of the normal user mode start. But first, let’s talk about why it exists

Linux的启动过程在大部分情况下都相当简单。

然而,有一个组件一直让人感到困惑:initramfs,即初始RAM文件系统。

可以将其看作是正常用户模式启动之前的一个小型用户空间楔子。

但首先,我们来谈谈它存在的原因。

The problem stems from the availability of many different kinds of storage hardware. Remember, the Linux kernel does not talk to the PC BIOS or EFI interfaces to get data from disks, so in order to mount its root filesystem, it needs driver support for the underlying storage mechanism. For example, if the root is on a RAID array connected to a third-party controller, the kernel needs the driver for that controller first. Unfortunately, there are so many storage controller drivers that distributions can’t include all of them in their kernels, so many drivers are shipped as loadable modules. But loadable modules are files, and if your kernel doesn’t have a filesystem mounted in the first place, it can’t load the driver modules that it needs.

问题源于许多不同类型的存储硬件的可用性。

请记住,Linux内核不会直接与PC BIOS或EFI接口通信以从磁盘获取数据,因此为了挂载其根文件系统,它需要底层存储机制的驱动程序支持。

例如,如果根文件系统位于连接到第三方控制器的RAID阵列上,则内核首先需要该控制器的驱动程序。

不幸的是,有这么多存储控制器驱动程序,发行版无法将它们全部包含在内核中,因此许多驱动程序作为可加载模块随发行版一起提供。

但是可加载模块是文件,如果您的内核一开始没有挂载文件系统,它就无法加载所需的驱动程序模块。

The workaround is to gather a small collection of kernel driver modules along with a few other utilities into an archive. The boot loader loads this archive into memory before running the kernel. Upon start, the kernel reads the contents of the archive into a temporary RAM filesystem (the initramfs), mounts it at /, and performs the user-mode handoff to the init on the initramfs. Then, the utilities included in the initramfs allow the kernel to load the necessary driver modules for the real root filesystem. Finally, the utilities mount the real root filesystem and start true init.

解决方法是将一小组内核驱动程序模块和一些其他实用程序打包成一个存档文件。

引导加载程序在运行内核之前将此存档加载到内存中。

启动时,内核将存档的内容读入临时的RAM文件系统(即initramfs),在/目录下挂载它,并将用户模式交接给initramfs上的init。

然后,initramfs中包含的实用程序允许内核加载真正的根文件系统所需的驱动程序模块。

最后,这些实用程序会挂载真正的根文件系统并启动真正的init。

Implementations vary and are ever evolving. On some distributions, the init on the initramfs is a fairly simple shell script that starts a udevd to load drivers, then mounts the real root and executes the init there. On distributions that use systemd, you’ll typically see an entire systemd installation there with no unit configuration files and just a few udevd configuration files.

实现方式各有不同且不断发展。

在某些发行版上,initramfs上的init是一个相当简单的shell脚本,它启动一个udev守护进程来加载驱动程序,然后挂载真正的根文件系统并在那里执行init。

在使用systemd的发行版中,您通常会看到一个完整的systemd安装,其中没有单元配置文件,只有一些udev配置文件。

One basic characteristic of the initial RAM filesystem that has (so far) remained unchanged since its inception is the ability to bypass it if you don’t need it. That is, if your kernel has all the drivers it needs to mount your root filesystem, you can omit the initial RAM filesystem in your boot loader configuration. When successful, eliminating the initial RAM file-system shortens boot time, usually by a couple of seconds. Try it yourself at boot time by using the GRUB menu editor to remove the initrd line. (It’s best not to experiment by changing the GRUB configuration file, as you can make a mistake that will be difficult to repair.) Recently, it has been a little more difficult to bypass the initial RAM filesystem because features such as mount-by-UUID may not be available with generic distribution kernels.

迄今为止,初始RAM文件系统的一个基本特征保持不变,即如果您不需要它,可以绕过它。

也就是说,如果您的内核具有挂载根文件系统所需的所有驱动程序,可以在引导加载程序配置中省略初始RAM文件系统。

成功后,省略初始RAM文件系统可以缩短启动时间,通常可以减少几秒钟。

您可以在引导时尝试使用GRUB菜单编辑器删除initrd行来验证。

最好不要尝试通过更改GRUB配置文件来进行实验,因为可能会犯一个难以修复的错误。

最近,绕过初始RAM文件系统变得稍微困难一些,因为通用发行版内核可能无法使用mount-by-UUID等功能。

It’s easy to see the contents of your initial RAM filesystem because, on most modern systems, they are simple gzip-compressed cpio archives (see the cpio(1) manual page). First, find the archive file by looking at your boot loader configuration (for example, grep for initrd lines in your grub.cfg configuration file). Then use cpio to dump the contents of the archive into a temporary directory somewhere and peruse the results. For example:

大多数现代系统的初始RAM文件系统的内容很容易查看,因为它们通常是简单的gzip压缩的cpio存档(请参阅cpio(1)手册页)。

首先,通过查看引导加载程序配置文件(例如,在grub.cfg配置文件中搜索initrd行)找到存档文件。

然后使用cpio将存档的内容转储到某个临时目录中,并查看结果。

例如:

$ mkdir /tmp/myinitrd
$ cd /tmp/myinitrd
$ zcat /boot/initrd.img-3.2.0-34 | cpio -i --no-absolute-filenames
--snip--

One particular piece of interest is the “pivot” near the very end of the init process on the initial RAM filesystem. This part is responsible for removing the contents of the temporary filesystem (to save memory) and permanently switch to the real root.

有一个特别引人注目的部分是在初始RAM文件系统(initramfs)的最后阶段的“转折点(pivot)”。

这部分负责清除临时文件系统的内容(以节省内存)并永久切换到真正的根目录。

You won’t typically create your own initial RAM filesystem, as this is a painstaking process. There are a number of utilities for creating initial RAM filesystem images, and your distribution likely comes with one. Two of the most common are dracut and mkinitramfs.

通常情况下,您不会自己创建初始RAM文件系统,因为这是一个费时的过程。

有许多用于创建初始RAM文件系统镜像的实用工具,您的发行版很可能自带其中之一。

其中两个最常见的工具是dracut和mkinitramfs。

NOTE The term initial RAM filesystem (initramfs) refers to the implementation that uses the cpio archive as the source of the temporary filesystem. There is an older version called the initial RAM disk, or initrd, that uses a disk image as the basis of the temporary filesystem. This has fallen into disuse because it’s much easier to maintain a cpio archive. However, you’ll often see the term initrd used to refer to a cpio-based initial RAM filesystem. Often, as in the preceding example, the filenames and configuration files will still contain initrd.

注意,术语初始RAM文件系统(initramfs)是指使用cpio存档作为临时文件系统的实现。

还有一个较旧的版本称为初始RAM磁盘(initrd),它使用磁盘映像作为临时文件系统的基础。

由于维护cpio存档要容易得多,因此这个版本已经不再使用。

然而,您经常会看到术语initrd用来指代基于cpio的初始RAM文件系统。

通常,如前面的示例中所示,文件名和配置文件仍会包含initrd。

6.9 Emergency Booting and Single-User Mode(紧急启动和单用户模式)

When something goes wrong with the system, the first recourse is usually to boot the system with a distribution’s “live” image (most distributions’ installation images double as live images) or with a dedicated rescue image such as SystemRescueCd that you can put on removable media. Common tasks for fixing a system include the following:

当系统出现问题时,通常的第一步是使用发行版的“live”镜像(大多数发行版的安装镜像也可以用作live镜像)或者专用的救援镜像(如SystemRescueCd),来引导系统。

修复系统的常见任务包括以下几点:

o Checking filesystems after a system crash
o Resetting a forgotten root password
o Fixing problems in critical files, such as /etc/fstab and /etc/passwd
o Restoring from backups after a system crash

  • 在系统崩溃后检查文件系统
  • 重置忘记的root密码
  • 修复关键文件(如/etc/fstab和/etc/passwd)中的问题
  • 在系统崩溃后从备份中恢复

Another option for booting quickly to a usable state is single-user mode. The idea is that the system quickly boots to a root shell instead of going through the whole mess of services. In the System V init, single-user mode is usually runlevel 1, and you can also enter the mode with an -s parameter to the boot loader. You may need to type the root password to enter single-user mode.

另一种快速引导到可用状态的选择是单用户模式。

其思想是系统快速引导到root shell,而不是经过整个服务的混乱过程。

在System V init中,单用户模式通常是运行级别1,你也可以通过引导加载程序的-s参数进入该模式。

进入单用户模式可能需要输入root密码。

The biggest problem with single-user mode is that it doesn’t offer many amenities. The network almost certainly won’t be available (and if it is, it will be hard to use), you won’t have a GUI, and your terminal may not even work correctly. For this reason, live images are nearly always considered preferable.

单用户模式最大的问题是它没有提供许多便利设施。

网络几乎肯定不可用(即使可用,也很难使用),你将没有图形界面,你的终端甚至可能无法正常工作。

因此,几乎总是认为使用live镜像更可取。


Xander
198 声望51 粉丝