Contents

保存和还原Linux进程

1. 概述

当我们运行复杂的数学计算或渲染等长流程时,保存流程以供以后使用会很有帮助。在本教程中,我们将了解如何保存和恢复可以在多次重启时使用的进程。虽然大多数 Linux 发行版都支持完整的机器休眠,但我们主要专注于保存特定进程而不影响其他进程。

 在整个教程中,我们将使用可以帮助我们保留进程的不同工具,例如killcriu 。除此之外,我们还将了解如何将操作系统的状态保存在 VirtualBox 等虚拟机上。

2. 使用kill命令

如果您已经使用 Linux 一段时间,那么您很可能已经看到这个命令被大量使用。**我们使用kill命令向正在运行的进程发送信号。**这些信号提供了一种进程间通信的方式。例如,我们可以直接从终端发送信号来终止、停止或挂起另一个处理器的进程。

2.1. 基本用法

kill命令的基本语法非常简单:

$ kill -[SIGNAL] [PID]

**信号以“-”为前缀,后跟表示特定动作的信号代码或信号名称。**该信号后跟一个PID。Linux 中的每个进程都有一个进程 ID (PID),它唯一地标识了该机器上的进程。

在我们的例子中,我们有兴趣暂时暂停或停止该过程。一旦我们暂停该进程,我们应该能够稍后恢复该进程以继续其正常执行。幸运的是,kill命令提供了*-STOP-CONT*信号。

2.2. 停止和恢复进程

假设我们要停止一个占用大量 CPU 时间的长时间运行的进程,并且我们需要运行一个也需要大量 CPU 使用的短进程。我们可以使用*-STOP*信号暂时停止长进程:

$ kill -STOP 8763

运行命令后,我们可以注意到 CPU 使用率降低了。尽管该进程仍将驻留在主内存中,但 Linux 会随着时间的推移换出该进程,以便为内存中的其他进程腾出空间。

一旦我们准备好恢复进程,我们可以向进程发送*-CONT*信号:

$ kill -CONT 8763

我们应该知道,一旦我们关闭我们的机器,Linux 会从内存中清除所有进程,包括我们长期运行的进程。因此,如果我们想暂时停止和恢复我们的进程而不必将其保存在磁盘上,那么kill命令很有用。

但是,如果我们需要在重启过程中保持我们的进程,我们可能想看看我们接下来将介绍的其他工具。

3. 使用criu

criu是用于保存和恢复流程的最广泛使用的工具。与可用于相同目的的其他工具相比,它具有最多的功能。不仅如此,它还与最新的 Linux 内核保持同步并定期维护

** criu允许我们将进程或进程的一部分保存到我们的硬盘驱动器作为检查点。**检查点由文件组成,我们稍后可以使用这些文件从冻结点恢复进程。

3.1.安装

criu二进制包在大多数发行版的官方存储库中都可用*。*我们可以使用发行版附带的包管理器 来安装它:

# On Ubuntu, Debian
$ apt install criu
# On Fedora
$ yum install criu
# On Arch, Manjaro
$ pacman -S criu

如果官方仓库中没有这个包,我们可以参考criu wiki 从源代码编译。

3.2. 基本用法

在我们的机器上安装criu后,我们可以继续验证它:

$ criu -v 
Version: 3.16.1

**在使用criu之前,我们应该知道它只有在我们的 Linux 内核编译时启用了CONFIG_CHECKPOINT_RESTORE 选项才能工作。否则,criu会报错,进程将无法保存。**因此,我们可能需要使用所需的选项来编译内核。

现在,如果我们需要在内核配置中检查这个选项,我们可以简单地读取*/proc/zconfig.gz*文件:

$ zcat /proc/config.gz | grep CHECKPOINT
CONFIG_CHECKPOINT_RESTORE=y

现在,我们准备好使用criu转储进程了。有人可能想知道什么是倾销。好吧,转储进程是一种在内存中拍摄当时正在运行的进程的快照并将快照与其他信息一起保存在我们的磁盘上的方法

让我们看看如何从命令行使用criu转储进程:

$ criu dump -t [PID] -D /path/to/dir [OPTIONS]

我们使用转储选项指定转储操作。然后,我们使用*-t–tree选项写入 PID 。除此之外,我们还使用-D*选项指定放置检查点的目录。

**在转储进程之前,我们应该知道criu将转储进程及其子进程。**例如,如果我们从终端生成了一个进程,那么criu将尝试转储整个进程树。出于这个原因,我们应该使用setsid 从终端运行我们的命令行程序,这样它们就不会依赖于其他进程。

现在,当我们想从文件系统恢复我们的进程时,我们可以简单地使用检查点目录指定它:

$ criu restore -D /path/to/dir

criu将首先尝试使用以前的 PID 恢复进程,但如果该 PID 已在使用中,则恢复的进程将使用新的 PID 恢复。

3.3. 例子

例如,让我们创建一个包含无限循环的自定义bash  脚本。我们将向变量添加 10 并在每次循环迭代时打印它。让我们继续编写脚本然后运行它:

#!/bin/bash
NUM=0
while true; do
  NUM=$((NUM+10))
  echo "NUM: $NUM"
done

让我们使用setsid运行脚本:

$ setsid ./infinite-loop.sh

现在我们的脚本正在运行,让我们用pgrep 检查它的 PID :

$ pgrep infinite-loop.sh
2805

现在,让我们使用criu将进程保存到我们的磁盘:

$ criu dump -t $(pgrep infinite-loop.sh) -D ~/Documents/infinite-loop

让我们分解一下:

  • -t指定进程的PID,我们从pgrep命令通过命令替换得到
  • -D选项指示将存储无限循环检查点文件的目录

现在,让我们终止无限循环进程,以便我们可以从检查点文件中恢复它:

$ kill $(pgrep infinite-loop.sh)

现在,让我们通过运行带有restore选项的criu命令来恢复该过程:

$ criu restore -D ~/Documents/infinite-loop
NUM: 10030560
NUM: 10030570
NUM: 10030580
.
.
.

请注意,我们必须以 root 权限运行criu命令,否则我们将面临操作不允许错误。

4. 在虚拟机上保存状态

虚拟机提供了一个有用的选项来保存我们的分发状态,我们以后可以随时恢复。对于我们的示例,我们将使用VirtualBox ,因为它结构紧凑且可在大多数平台上使用。

设置 VirtualBox 后,让我们启动我们选择的发行版。登录后,继续按 ALT+F4 或标题栏上的关闭按钮。一旦我们尝试关闭虚拟机,我们将看到一个对话框。让我们检查“保存机器状态”单选框,然后单击“确定”:

/uploads/process_save_restore/1.jpg

VM 现在将为我们当前的操作系统保存当前状态,并在我们重新打开操作系统时恢复该状态。

VM 不一定冻结单个进程,而是冻结整个操作系统。但是,如果我们想冻结单个进程,那么我们可以在 VM 中运行的操作系统中使用criu 。不仅如此,我们还可以将冻结的进程移植到主机操作系统,假设它们运行相同的操作系统和相同的软件。