2017年12月

How to install software from source code

简介:这篇文章详细介绍了在 Linux 中怎么用源代码安装程序,以及怎么去卸载用源代码安装的程序。

Linux 发行版的一个最大的优点就是它的包管理器和相关的软件库。通过它们提供的资源和工具,你才能够以完全自动化的方式在你的计算机上下载和安装软件。

但是,尽管付出了很多的努力,包维护者仍然没法照顾好每种情况,也不可能将所有的可用软件都打包进去。因此,仍然存在需要你自已去编译和安装一个新软件的情形。对于我来说,到目前为止,最主要的原因是,我编译一些软件是我需要去运行一个特定的版本。或者是我想去修改源代码或使用一些想要的编译选项。

如果你也属于后一种情况,那你已经知道你应该怎么做了。但是,对于绝大多数的 Linux 用户来说,第一次从源代码中编译和安装一个软件看上去像是一个入门仪式:它让很多人感到恐惧;但是,如果你能克服困难,你将可能进入一个全新的世界,并且,如果你做到了,那么你将成为社区中享有特权的一部分人。

A. 在 Linux 中从源代码开始安装软件

这正是我们要做的。因为这篇文章的需要,我要在我的系统上安装 NodeJS 8.1.1。它是个完全真实的版本。这个版本在 Debian 仓库中没有:

sh$ apt-cache madison nodejs | grep amd64
    nodejs | 6.11.1~dfsg-1 | http://deb.debian.org/debian experimental/main amd64 Packages
    nodejs | 4.8.2~dfsg-1 | http://ftp.fr.debian.org/debian stretch/main amd64 Packages
    nodejs | 4.8.2~dfsg-1~bpo8+1 | http://ftp.fr.debian.org/debian jessie-backports/main amd64 Packages
    nodejs | 0.10.29~dfsg-2 | http://ftp.fr.debian.org/debian jessie/main amd64 Packages
    nodejs | 0.10.29~dfsg-1~bpo70+1 | http://ftp.fr.debian.org/debian wheezy-backports/main amd64 Packages

第 1 步:从 GitHub 上获取源代码

像大多数开源项目一样,NodeJS 的源代码可以在 GitHub:https://github.com/nodejs/node 上找到。

所以,我们直接开始吧。

The NodeJS official GitHub repository

如果你不熟悉 GitHubgit 或者提到的其它 版本管理系统包含了这个软件的源代码,以及多年来对该软件的所有修改的历史。甚至可以回溯到该软件的最早版本。对于开发者来说,保留它的历史版本有很多好处。如今对我来说,其中一个好处是可以得到任何一个给定时间点的项目源代码。更准确地说,我可以得到我所要的 8.1.1 发布时的源代码。即便从那之后他们有了很多的修改。

Choose the v8.1.1 tag in the NodeJS GitHub repository

在 GitHub 上,你可以使用 “branch” (分支)按钮导航到这个软件的不同版本。“分支” 和 “标签” 是 Git 中一些相关的概念。总的来说,开发者创建 “分支” 和 “标签” 来在项目历史中对重要事件保持跟踪,比如当他们启用一个新特性或者发布一个新版本时。在这里先不详细介绍了,你现在只需要知道我在找被标记为 “v8.1.1” 的版本。

The NodeJS GitHub repository as it was at the time the v8.1.1 tag was created

在选择了 “v8.1.1” 标签后,页面被刷新,最显著的变化是标签现在作为 URL 的一部分出现。另外,你可能会注意到文件改变日期也有所不同。你现在看到的源代码树是创建了 v8.1.1 标签时的代码。在某种意义上,你也可以认为像 git 这样的版本管理工具是一个时光穿梭机,允许你在项目历史中来回穿梭。

NodeJS GitHub repository download as a ZIP button

此时,我们可以下载 NodeJS 8.1.1 的源代码。你不要忘记去点那个建议的大的蓝色按钮来下载一个项目的 ZIP 压缩包。对于我来说,为讲解的目的,我从命令行中下载并解压这个 ZIP 压缩包。但是,如果你更喜欢使用一个 GUI 工具,不用担心,你可以取代下面的命令方式:

wget https://github.com/nodejs/node/archive/v8.1.1.zip
unzip v8.1.1.zip
cd node-8.1.1/

下载一个 ZIP 包就可以,但是如果你希望“像个专家一样”,我建议你直接使用 git 工具去下载源代码。它一点也不复杂 — 并且如果你是第一次使用该工具,它将是一个很好的开端,你以后将经常用到它:

# first ensure git is installed on your system
sh$ sudo apt-get install git
# Make a shallow clone the NodeJS repository at v8.1.1
sh$ git clone --depth 1 \
              --branch v8.1.1 \
              https://github.com/nodejs/node
sh$ cd node/

顺便说一下,如果你有任何问题,这篇文章的第一部分只是做一个总体介绍而已。后面,为了帮你排除常见问题,我们将基于 Debian 和基于 RedHat 的发行版更详细地解释。

不管怎样,在你使用 git 或者作为一个 ZIP 压缩包下载了源代码后,在当前目录下就有了同样的源代码文件:

sh$ ls
android-configure  BUILDING.md            common.gypi      doc            Makefile   src
AUTHORS            CHANGELOG.md           configure        GOVERNANCE.md  node.gyp   test
benchmark          CODE_OF_CONDUCT.md     CONTRIBUTING.md  lib            node.gypi  tools
BSDmakefile        COLLABORATOR_GUIDE.md  deps             LICENSE        README.md  vcbuild.bat

第 2 步:理解程序的构建系统

构建系统就是我们通常所说的“编译源代码”,其实,编译只是从源代码中生成一个可使用的软件的其中一个阶段。构建系统是一套工具,用于自动处置不同的任务,以便可以仅通过几个命令就能构建整个软件。

虽然概念很简单,实际上编译做了很多事情。因为不同的项目或者编程语言也许有不同的要求,或者因为编程者的好恶,或者因为支持的平台、或者因为历史的原因,等等等等 … 选择或创建另外一个构建系统的原因几乎数不清。这方面有许多种不同的解决方案。

NodeJS 使用一种 GNU 风格的构建系统。这在开源社区中这是一个很流行的选择。由此开始,你将进入一段精彩的旅程。

写出和调优一个构建系统是一个非常复杂的任务。但是,作为 “终端用户” 来说,GNU 风格的构建系统使用两个工具让他们免于此难:configuremake

configure 文件是个项目专用的脚本,它将检查目标系统的配置和可用功能,以确保该项目可以被构建,并最终吻合当前平台的特性。

一个典型的 configure 任务的重要部分是去构建 Makefile。这个文件包含了有效构建项目所需的指令。

另一方面,make 工具,这是一个可用于任何类 Unix 系统的 POSIX 工具。它将读取项目专用的 Makefile 然后执行所需的操作去构建和安装你的程序。

但是,在 Linux 的世界中,你仍然有一些定制你自己专用的构建的理由。

./configure --help

configure -help 命令将展示你可用的所有配置选项。再强调一下,这是非常的项目专用。说实话,有时候,在你完全理解每个配置选项的作用之前,你需要深入到项目中去好好研究。

不过,这里至少有一个标准的 GNU 自动化工具选项是你该知道的,它就是众所周知的 --prefix 选项。它与文件系统的层次结构有关,它是你软件要安装的位置。

第 3 步:文件系统层次化标准(FHS)

大部分典型的 Linux 发行版的文件系统层次结构都遵从 文件系统层次化标准(FHS)

这个标准说明了你的系统中各种目录的用途,比如,/usr/tmp/var 等等。

当使用 GNU 自动化工具 和大多数其它的构建系统 时,它会把新软件默认安装在你的系统的 /usr/local 目录中。这是依据 FHS 中 /usr/local 层级是为系统管理员本地安装软件时使用的,它在系统软件更新覆盖时是安全的。它也可以用于存放在一组主机中共享,但又没有放到 /usr 中的程序和数据”,因此,它是一个非常好的选择。

/usr/local 层级以某种方式复制了根目录,你可以在 /usr/local/bin 这里找到可执行程序,在 /usr/local/lib 中找到库,在 /usr/local/share 中找到架构无关的文件,等等。

使用 /usr/local 树作为你定制安装的软件位置的唯一问题是,你的软件的文件将在这里混杂在一起。尤其是你安装了多个软件之后,将很难去准确地跟踪 /usr/local/bin/usr/local/lib 中的哪个文件到底属于哪个软件。它虽然不会导致系统的问题。毕竟,/usr/bin 也是一样混乱的。但是,有一天你想去卸载一个手工安装的软件时它会将成为一个问题。

要解决这个问题,我通常喜欢安装定制的软件到 /opt 子目录下。再次引用 FHS:

/opt 是为安装附加的应用程序软件包而保留的。

包安装在 /opt 下的软件包必须将它的静态文件放在单独的 /opt/<package> 或者 /opt/<provider> 目录中,此处 <package> 是所说的那个软件名的名字,而 <provider> 处是提供者的 LANANA 注册名字。”(LCTT 译注:LANANA 是指 The Linux Assigned Names And Numbers Authority。 )

因此,我们将在 /opt 下创建一个子目录,用于我们定制的 NodeJS 安装。并且,如果有一天我想去卸载它,我只是很简单地去删除那个目录:

sh$ sudo mkdir /opt/node-v8.1.1
sh$ sudo ln -sT node-v8.1.1 /opt/node
# What is the purpose of the symbolic link above?
# Read the article till the end--then try to answer that
# question in the comment section!

sh$ ./configure --prefix=/opt/node-v8.1.1
sh$ make -j9 && echo ok
# -j9 means run up to 9 parallel tasks to build the software.
# As a rule of thumb, use -j(N+1) where N is the number of cores
# of your system. That will maximize the CPU usage (one task per
# CPU thread/core + a provision of one extra task when a process
# is blocked by an I/O operation.

在你运行完成 make 命令之后,如果有任何的除了 “ok” 以外的信息,将意味着在构建过程中有错误。当我们使用一个 -j 选项去运行并行构建时,在构建系统的大量输出过程中,检索错误信息并不是件很容易的事。

在这种情况下,只能是重新开始 make,并且不要使用 -j 选项。这样错误将会出现在输出信息的最后面:

sh$ make

最终,编译结束后,你可以运行这个命令去安装你的软件:

sh$ sudo make install

然后测试它:

sh$ /opt/node/bin/node --version
v8.1.1

B. 如果在源代码安装的过程中出现错误怎么办?

我上面介绍的大多是你能在文档完备的项目的“构建指令”页面上看到。但是,本文的目标是让你从源代码开始去编译你的第一个软件,它可能要花一些时间去研究一些常见的问题。因此,我将再次重新开始一遍整个过程,但是,这次是在一个最新的、最小化安装的 Debian 9.0 和 CentOS 7.0 系统上。因此,你可能看到我遇到的错误以及我怎么去解决它。

从 Debian 9.0 中 “Stretch” 开始

itsfoss@debian:~$ git clone --depth 1 \
                             --branch v8.1.1 \
                             https://github.com/nodejs/node
-bash: git: command not found

这个问题非常容易去诊断和解决。去安装这个 git 包即可:

itsfoss@debian:~$ sudo apt-get install git
itsfoss@debian:~$ git clone --depth 1 \
                             --branch v8.1.1 \
                             https://github.com/nodejs/node && echo ok
[...]
ok
itsfoss@debian:~/node$ sudo mkdir /opt/node-v8.1.1
itsfoss@debian:~/node$ sudo ln -sT node-v8.1.1 /opt/node

现在没有问题了。

itsfoss@debian:~/node$ ./configure --prefix=/opt/node-v8.1.1/
WARNING: failed to autodetect C++ compiler version (CXX=g++)
WARNING: failed to autodetect C compiler version (CC=gcc)
Node.js configure error: No acceptable C compiler found!
        Please make sure you have a C compiler installed on your system and/or
        consider adjusting the CC environment variable if you installed
        it in a non-standard prefix.

很显然,编译一个项目,你需要一个编译器。NodeJS 是使用 C++ 语言 写的,我们需要一个 C++ 编译器。在这里我将安装 g++,它就是为这个目的写的 GNU C++ 编译器:

itsfoss@debian:~/node$ sudo apt-get install g++
itsfoss@debian:~/node$ ./configure --prefix=/opt/node-v8.1.1/ && echo ok
[...]
ok
itsfoss@debian:~/node$ make -j9 && echo ok
-bash: make: command not found

还差一个其它工具。同样的症状。同样的解决方案:

itsfoss@debian:~/node$ sudo apt-get install make
itsfoss@debian:~/node$ make -j9 && echo ok
[...]
ok
itsfoss@debian:~/node$ sudo make install
[...]
itsfoss@debian:~/node$ /opt/node/bin/node --version
v8.1.1

成功!

请注意:我将一次又一次地安装各种工具去展示怎么去诊断编译问题,以及展示怎么去解决这些问题。但是,如果你搜索关于这个主题的更多文档,或者读其它的教程,你将发现,很多发行版有一个 “meta-packages”,它包罗了安装一些或者全部的用于编译软件的常用工具。在基于 Debian 的系统上,你或许遇到过 build-essentials 包,它就是这种用作。在基于 Red Hat 的发行版中,它将是 “Development Tools” 组。

在 CentOS 7.0 上

[itsfoss@centos ~]$ git clone --depth 1 \
                               --branch v8.1.1 \
                               https://github.com/nodejs/node
-bash: git: command not found

命令没有找到?可以用 yum 包管理器去安装它:

[itsfoss@centos ~]$ sudo yum install git
[itsfoss@centos ~]$ git clone --depth 1 \
                               --branch v8.1.1 \
                               https://github.com/nodejs/node && echo ok
[...]
ok
[itsfoss@centos ~]$ sudo mkdir /opt/node-v8.1.1
[itsfoss@centos ~]$ sudo ln -sT node-v8.1.1 /opt/node
[itsfoss@centos ~]$ cd node
[itsfoss@centos node]$ ./configure --prefix=/opt/node-v8.1.1/
WARNING: failed to autodetect C++ compiler version (CXX=g++)
WARNING: failed to autodetect C compiler version (CC=gcc)
Node.js configure error: No acceptable C compiler found!

        Please make sure you have a C compiler installed on your system and/or
        consider adjusting the CC environment variable if you installed
        it in a non-standard prefix.

你知道的:NodeJS 是使用 C++ 语言写的,但是,我的系统缺少合适的编译器。Yum 可以帮到你。因为,我不是一个合格的 CentOS 用户,我实际上是在互联网上搜索到包含 g++ 编译器的包的确切名字的。这个页面指导了我:https://superuser.com/questions/590808/yum-install-gcc-g-doesnt-work-anymore-in-centos-6-4

[itsfoss@centos node]$ sudo yum install gcc-c++
[itsfoss@centos node]$ ./configure --prefix=/opt/node-v8.1.1/ && echo ok
[...]
ok
[itsfoss@centos node]$ make -j9 && echo ok
[...]
ok
[itsfoss@centos node]$ sudo make install && echo ok
[...]
ok
[itsfoss@centos node]$ /opt/node/bin/node --version
v8.1.1

再次成功!

C. 从源代码中对要安装的软件做一些改变

从源代码中安装一个软件,可能是因为你的分发仓库中没有一个可用的特定版本。或者因为你想去 修改 那个程序。也可能是修复一个 bug 或者增加一个特性。毕竟,开源软件这些都可以做到。因此,我将抓住这个机会,让你亲自体验怎么去编译你自己的软件。

在这里,我将在 NodeJS 源代码上做一个微小改变。然后,我们将看到我们的改变将被纳入到软件的编译版本中:

用你喜欢的 文本编辑器(如,vim、nano、gedit、 … )打开文件 node/src/node.cc。然后,尝试找到如下的代码片段:

   if (debug_options.ParseOption(argv[0], arg)) {
      // Done, consumed by DebugOptions::ParseOption().
    } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) {
      printf("%s\n", NODE_VERSION);
      exit(0);
    } else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
      PrintHelp();
      exit(0);
    }

它在 文件的 3830 行 附近。然后,修改包含 printf 的行,将它替换成如下内容:

      printf("%s (compiled by myself)\n", NODE_VERSION);

然后,返回到你的终端。在继续之前,为了对强大的 Git 支持有更多的了解,你可以去检查一下,你修改是文件是否正确:

diff --git a/src/node.cc b/src/node.cc
index bbce1022..a5618b57 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -3828,7 +3828,7 @@ static void ParseArgs(int* argc,
     if (debug_options.ParseOption(argv[0], arg)) {
       // Done, consumed by DebugOptions::ParseOption().
     } else if (strcmp(arg, "--version") == 0 || strcmp(arg, "-v") == 0) {
-      printf("%s\n", NODE_VERSION);
+      printf("%s (compiled by myself)\n", NODE_VERSION);
       exit(0);
     } else if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) {
       PrintHelp();

在你前面改变的那行之前,你将看到一个 “-” (减号标志)。而在改变之后的行前面有一个 “+” (加号标志)。

现在可以去重新编译并重新安装你的软件了:

make -j9 && sudo make install && echo ok
[...]
ok

这个时候,可能失败的唯一原因就是你改变代码时的输入错误。如果就是这种情况,在文本编辑器中重新打开 node/src/node.cc 文件并修复错误。

一旦你完成了新修改版本的 NodeJS 的编译和安装,就可以去检查你的修改是否包含到软件中:

itsfoss@debian:~/node$ /opt/node/bin/node --version
v8.1.1 (compiled by myself)

恭喜你!你对开源程序做出了你的第一个改变!

D. 让 shell 找到我们定制构建的软件

到目前为止,你可能注意到,我通常启动我新编译的 NodeJS 软件是通过指定到该二进制文件的绝对路径。

/opt/node/bin/node

这是可以正常工作的。但是,这样太麻烦。实际上有两种办法可以去解决这个问题。但是,去理解它们,你必须首先明白,你的 shell 定位可执行文件是通过在环境变量 PATH 中指定的目录里面查找的。

itsfoss@debian:~/node$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

在这个 Debian 系统上,如果你不指定一个精确的目录做为命令名字的一部分,shell 将首先在 /usr/local/bin 中查找可执行程序;如果没有找到,然后进入 /usr/bin 中查找;如果没有找到,然后进入 /bin查找;如果没有找到,然后进入 /usr/local/games 查找;如果没有找到,然后进入 /usr/games 查找;如果没有找到,那么,shell 将报告一个错误,“command not found”

由此,我们可以知道有两种方法去确保命令可以被 shell 访问到:将它(该二进制程序)增加到已经配置好的 PATH 目录中,或者将包含可执行程序的目录添加到 PATH 中。

从 /usr/local/bin 中添加一个链接

只是从 /opt/node/bin拷贝 NodeJS 二进制可执行文件到 /usr/local/bin 是一个错误的做法。因为,如果这么做,该可执行程序将无法定位到在 /opt/node/ 中的需要的其它组件。(软件以它自己的位置去定位它所需要的资源文件是常见的做法)

因此,传统的做法是去使用一个符号链接:

itsfoss@debian:~/node$ sudo ln -sT /opt/node/bin/node /usr/local/bin/node
itsfoss@debian:~/node$ which -a node || echo not found
/usr/local/bin/node
itsfoss@debian:~/node$ node --version
v8.1.1 (compiled by myself)

这一个简单而有效的解决办法,尤其是,如果一个软件包是由好几个众所周知的可执行程序组成的,因为,你将为每个用户调用的命令创建一个符号链接。例如,如果你熟悉 NodeJS,你知道应用的 npm 组件,也应该从 /usr/local/bin 做个符号链接。我把这个留给你做练习。

修改 PATH

首先,如果你尝试过前面的解决方案,请先移除前面创建的节点符号链接,去从一个干净的状态开始:

itsfoss@debian:~/node$ sudo rm /usr/local/bin/node
itsfoss@debian:~/node$ which -a node || echo not found
not found

现在,这里有一个改变你的 PATH 的魔法命令:

itsfoss@debian:~/node$ export PATH="/opt/node/bin:${PATH}"
itsfoss@debian:~/node$ echo $PATH
/opt/node/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games

简单说就是,我用环境变量 PATH 之前的内容前缀了一个 /opt/node/bin 替换了其原先的内容。因此,你可以想像一下,shell 将先进入到 /opt/node/bin 目录中查找可执行程序。我们也可以使用 which 命令去确认一下:

itsfoss@debian:~/node$ which -a node || echo not found
/opt/node/bin/node
itsfoss@debian:~/node$ node --version
v8.1.1 (compiled by myself)

鉴于 “符号链接” 解决方案是永久的,只要创建到 /usr/local/bin 的符号链接就行了,而对 PATH 的改变仅影响到当前的 shell。你可以自己做一些研究,如何做到对 PATH 的永久改变。给你一个提示,可以将它写到你的 “profile” 中。如果你找到这个解决方案,不要犹豫,通过下面的评论区共享给其它的读者!

E. 怎么去卸载刚才从源代码中安装的软件

因为我们定制编译的 NodeJS 软件全部在 /opt/node-v8.1.1 目录中,卸载它不需要做太多的工作,仅使用 rm 命令去删除那个目录即可:

sudo rm -rf /opt/node-v8.1.1

注意:sudorm -rf 是 “非常危险的鸡尾酒”!一定要在按下回车键之前多检查几次你的命令。你不会得到任何的确认信息,并且如果你删除了错误的目录它是不可恢复的 …

然后,如果你修改了你的 PATH,你可以去恢复这些改变。它一点也不复杂。

如果你从 /usr/local/bin 创建了一个符号链接,你应该去删除它们:

itsfoss@debian:~/node$ sudo find /usr/local/bin \
                                 -type l \
                                 -ilname "/opt/node/*" \
                                 -print -delete
/usr/local/bin/node

等等? 依赖地狱在哪里?

作为最终的讨论,如果你读过有关的编译定制软件的文档,你可能听到关于 依赖地狱 dependency hell 的说法。那是在你能够成功编译一个软件之前,对那种烦人情况的一个别名,你必须首先编译一个前提条件所需要的库,它又可能要求其它的库,而这些库有可能与你的系统上已经安装的其它软件不兼容。

发行版的软件包维护者的部分工作,就是实际去地解决那些依赖地狱,确保你的系统上的各种软件都使用了兼容的库,并且按正确的顺序去安装。

在这篇文章中,我特意选择了 NodeJS 去安装,是因为它几乎没有依赖。我说 “几乎” 是因为,实际上,它 依赖。但是,这些源代码的依赖已经预置到项目的源仓库中(在 node/deps 子目录下),因此,在你动手编译之前,你不用手动去下载和安装它们。

如果你有兴趣了解更多关于那个问题的知识和学习怎么去处理它。请在下面的评论区告诉我,它将是更高级别的文章的好主题!


作者简介:

充满激情的工程师,职业是教师,我的目标是:热心分享我所教的内容,并让我的学生自己培养它们的技能。你也可以在我的网站上联系到我。


via: https://itsfoss.com/install-software-from-source-code/

作者:Sylvain Leroux 译者:qhwdw 校对:wxy

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

Q:我正在使用基于 LXD(“Linux 容器”)的虚拟机。如何在 Linux 系统中启动时自动启动 LXD 容器?

当 LXD 在启动时运行,你就可以随时启动容器。你需要将 boot.autostart 设置为 true。你可以使用 boot.autostart.priority(默认值为 0)选项来定义启动容器的顺序(从最高开始)。你也可以使用 boot.autostart.delay(默认值 0)选项定义在启动一个容器后等待几秒后启动另一个容器。

语法

上面讨论的关键字可以使用 lxc 工具用下面的语法来设置:

$ lxc config set {vm-name} {key} {value}
$ lxc config set {vm-name} boot.autostart {true|false}
$ lxc config set {vm-name} boot.autostart.priority integer
$ lxc config set {vm-name} boot.autostart.delay integer

如何在 Ubuntu Linux 16.10 中让 LXD 容器在启动时启动?

输入以下命令:

$ lxc config set {vm-name} boot.autostart true

设置一个 LXD 容器名称 “nginx-vm” 以在启动时启动

$ lxc config set nginx-vm boot.autostart true

你可以使用以下语法验证设置:

$ lxc config get {vm-name} boot.autostart
$ lxc config get nginx-vm boot.autostart

示例输出:

true

你可以使用下面的语法在启动容器后等待 10 秒钟后启动另一个容器:

$ lxc config set nginx-vm boot.autostart.delay 10

最后,通过设置最高值来定义启动容器的顺序。确保 dbvm 容器首先启动,然后再启动 nginxvm。

$ lxc config set db_vm boot.autostart.priority 100
$ lxc config set nginx_vm boot.autostart.priority 99

使用下面的 bash 循环在 Linux 上查看所有配置值:

#!/bin/bash
echo 'The current values of each vm boot parameters:'
for c in db_vm nginx_vm memcache_vm
do
    echo "*** VM: $c ***"
    for v in boot.autostart boot.autostart.priority boot.autostart.delay
    do
        echo "Key: $v => $(lxc config get $c $v) "
    done
    echo ""
done

示例输出:

Fig.01: Get autostarting LXD containers values using a bash shell script


via: https://www.cyberciti.biz/faq/how-to-auto-start-lxd-containers-at-boot-time-in-linux/

作者:Vivek Gite 译者:geekpi 校对:wxy

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

How to install Arch Linux

简要说明:这一教程会教你如何用简单步骤安装 Arch Linux。

Arch Linux 是一个 x86-64 通用发行版,它流行于那些喜欢 DIY Linux 系统的用户和 Linux 铁杆粉丝当中。其默认的安装文件只含有一个最基本的系统,它希望使用者自己配置并使用 Arch 系统。其基于 KISS 原则( 使它保持简单、傻瓜 Keep It Simple, Stupid! ),Arch Linux 是一个专注于优雅、代码正确,精简而简洁的系统。

Arch 支持滚动发布模式,并且有自己的软件包管理器 —— pacman。为了提供一个先锐的操作系统,Arch 绝不会错失任何一个最新的源。实际上,它只提供了一个最小化的基本操作系统,使得你可以在低端硬件上安装 Arch,并且只安装你所必须的软件包。

同时,它也是最流行的从头开始学习 Linux 的操作系统之一。如果你想以 DIY 精神自己体验一番,你应该尝试一下 Arch Linux。 这被许多 Linux 用户视作核心的 Linux 体验。

在这篇文章里,我们会了解到如何安装、配置 Arch 并在其上安装桌面环境。

如何安装 Arch Linux

我们在这里讨论的安装方法是从你的电脑上完全删除已有的操作系统 ,而后在其上安装 Arch Linux。如果你想遵循这一教程安装 Arch,请确保你已经备份了所有文件,否则你就会失去它们。切切。

在你从 USB 上安装 Arch 之前,请确保你已经满足了以下条件:

安装 Arch Linux 的条件:

  • 一个兼容 x86\_64(例如 64 位)的机器
  • 最小 512M 内存(建议 2GB)
  • 最少 1GB 的磁盘空余空间(日常使用推荐 20GB)
  • 可以访问互联网
  • 至少有 2GB 存储空间的 USB 存储器
  • 熟悉 Linux 命令行

一旦你确认满足所有条件,就可以开始安装 Arch Linux 了。

第一步:下载 ISO 文件

你可以从官网上下载 ISO。安装 Arch Linux 需要一个至少有 512M 内存和 800M 磁盘空间,并兼容 x86\_64 (如 64 位)的机器。不过,建议至少有 2G 内存和 20G 磁盘空间,这样安装桌面环境时就不会遇到麻烦。

第二步:创建一个 Arch Linux 的现场版 USB 存储器

我们需要用你刚刚下载的 ISO 文件创建一个 Arch Linux 的 现场版 live USB 存储器。

如果你使用 Linux,你可以用 dd 命令来创建现场版 USB 存储器。 记得将下面的例子中的 /path/to/archlinux.iso 改成你的 ISO 文件的实际存储位置,/dev/sdx 改成你的磁盘设备号(例如 /dev/sdb)。你可以通过 lsblk 命令来了解你的设备信息。

dd bs=4M if=/path/to/archlinux.iso of=/dev/sdx status=progress && sync

在 Windows 下,有多种方法来创建现场版 USB 存储器。 推荐工具是 Rufus。我们之前已经有如何使用这一工具创建Antergos 现场版 USB 存储器的教程。因为 Antergos 发行版是基于 Arc h的,所以你可以使用同一教程。

步骤三:从现场版 USB 存储器上启动

一旦你已经创建了 Arch Linux 的现场版 USB 存储器,关闭你的电脑。插上你的 USB 存储器然后启动系统。在开机启动时,持续按 F2、F10 或 F1 之类的按键(根据你的电脑型号而定)进入启动设置。在这里,选择“从 USB 存储器或可移除设备启动”这一项。

一旦你选择了它,你会看到这样一个选项:

Arch Linux

选择“Boot Arch Linux (x86\_64)”。经过各种系统检查后,Arch Linux 会启动到 root 用户的命令行界面。

接下来的步骤包括磁盘分区、创建文件系统并挂载它。

第四步:磁盘分区

第一步就是给你的硬盘分区。单根分区是最简单的,就在它上面创建根分区(/)分区、交换分区和 home 分区。

我有一个 19G 的硬盘,我想在这儿安装 Arch Linux。为了创建分区,输入:

fdisk /dev/sda

n 创建新分区。按 p 创建主分区,然后选择分区号。

第一个扇区会被自动选择,你只要按回车键。在确定分区的最后一个扇区时,请输入这一分区的大小。

用相同的方法为 home 和交换分区创建两个分区,按 w 来保存修改并退出。

root partition

第四步:创建文件系统

因为我们已经有了三个分区,接下来就是创建文件系统来格式化分区。

我们用 mkfs 命令在根分区和 home 分区上创建文件系统,用 mkswap 创建交换分区。我们用 ext4 文件系统格式化磁盘。

mkfs.ext4 /dev/sda1
mkfs.ext4 /dev/sda3

mkswap /dev/sda2
swapon /dev/sda2

将这些分区挂载在根分区和 home 分区下:

mount /dev/sda1 /mnt
mkdir /mnt/home
mount /dev/sda3 /mnt/home

第五步:安装

我们已经创建分区并挂载了分区,开始安装最基本的软件包。基本的软件包包括了系统运行所必需的所有软件包。比如有 GNU BASH shell、文件压缩工具、文件系统管理工具、C 语言库、压缩工具、Linux 内核及其模块,类库、系统工具、USB 设备工具、Vi 文本编辑器等等。

pacstrap /mnt base base-devel

第六步:配置系统

生成一个 fstab 文件来规定磁盘分区、块设备,或者远程文件系统是如何挂载进文件系统中的。

genfstab -U /mnt >> /mnt/etc/fstab

进入 chroot 环境,这样可以为当前进程以及子进程切换当前根目录。

arch-chroot /mnt

一些需要与数据总线保持连接的 systemd 工具不能在 chroot 环境下使用,所以需要从当前环境退出。想要退出 chroot,就用下面的命令:

exit

第七步:设定时区

用下面这条命令设定时区:

ln -sf /usr/share/<时区信息>/<地区>/<城市> /etc/localtime

获取时区列表,输入:

ls /usr/share/zoneinfo

hwclock 命令设定硬件时钟:

hwclock --systohc --utc

第八步:设置地区

文件 /etc/locale.gen 在注释里包含了所有地区和系统语言设置。用 Vi 打开它,然后去掉你希望选择语言前面的注释。 我选择了 en_GB.UTF-8

现在用下面的命令在 /etc 文件夹里生成 关于地区的配置文件:

locale-gen
echo LANG=en_GB.UTF-8 > /etc/locale.conf
export LANG=en_GB.UTF-8

第九步 :安装 bootloader,设置主机名和 root 密码

创建 /etc/hostname 文件 然后添加一个对应的主机名:

127.0.1.1 myhostname.localdomain myhostname

我添加了 ItsFossArch 作为我的主机名:

echo ItsFossArch > /etc/hostname

然后也将它添加到 /etc/hosts

为了安装 bootloader 使用下面的命令:

pacman -S grub
grub-install /dev/sda
grub-mkconfig -o /boot/grub/grub.cfg

创建 root 密码,输入:

passwd

输入你想要的密码。

完成之后,更新你的系统。但很有可能你的系统已经是最新的,因为你下载的是最新的 ISO。

pacman -Syu

恭喜! 你已经安装了 Arch Linux 的命令行版本。

接下来,我们会了解到如何为 Arch 设置并安装一个桌面环境。我很喜欢 GNOME 桌面环境,所以在这儿也就选择了这个。

第十步:安装桌面(这一例子中是 GNOME)

在你安装桌面之前,你需要先设置网络。

你可以用下面的命令看见你的端口:

ip link

在我的电脑上,端口名是 enp0s3

将下面这一段加进文件中:

vi /etc/systemd/network/enp0s3.network

[Match]
name=en*
[Network]
DHCP=yes

保存并退出。重启网络来应用你刚才的改动。

systemctl restart systemd-networkd
systemctl enable systemd-networkd

将下面这两句话加进 /etc/resolv.conf

nameserver 8.8.8.8
nameserver 8.8.4.4

下一步是安装 X 环境。

输入下面的命令安装 Xorg,并将它作为显示服务器。

pacman -S xorg xorg-server

gnome 包含了基本的 GNOME桌面,gnome-extra 则包含 GNOME 应用、归档管理器、磁盘管理器、文本编辑器和其它的应用。

pacman -S gnome gnome-extra

最后一步是在 Arch 上开启 GDM 显示管理器。

systemctl start gdm.service
systemctl enable gdm.service

重启你的系统,你就会看见 GNOME 的登录界面。

Arch Linux 安装总结

我们在下面的视频中展示了一个由 Foss 读者 Gonzalo Tormo 提供的相似的安装方法(全屏观看,能更好的看清命令):

你也许意识到安装 Arch 不像安装 Ubuntu 一样简单。不过,只要有耐心,你一定可以安装好它,并且向全世界宣布你在用 Arch Linux。

Arch Linux 安装过程本身就是一个学习的机会。一旦安装完毕,我建议你参考它的 Wiki 去尝试其它的桌面环境,从而更深入了解这一操作系统。你可以探索它,发现它的强大之处。

如果你在安装 Arch 的过程中遇到任何问题,请在评论中给我们留言。


via: https://itsfoss.com/install-arch-linux/

作者:Ambarish Kumar 译者:wenwensnow 校对:wxy

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

本文假设你具备基本的 C 技能

Linux 完全在你的控制之中。虽然从每个人的角度来看似乎并不总是这样,但是高级用户喜欢去控制它。我将向你展示一个基本的诀窍,在很大程度上你可以去影响大多数程序的行为,它并不仅是好玩,在有时候也很有用。

一个让我们产生兴趣的示例

让我们以一个简单的示例开始。先乐趣,后科学。

random\_num.c:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(){
  srand(time(NULL));
  int i = 10;
  while(i--) printf("%d\n",rand()%100);
  return 0;
}

我相信,它足够简单吧。我不使用任何参数来编译它,如下所示:

gcc random_num.c -o random_num

我希望它输出的结果是明确的:从 0-99 中选择的十个随机数字,希望每次你运行这个程序时它的输出都不相同。

现在,让我们假装真的不知道这个可执行程序的出处。甚至将它的源文件删除,或者把它移动到别的地方 —— 我们已不再需要它了。我们将对这个程序的行为进行重大的修改,而你并不需要接触到它的源代码,也不需要重新编译它。

因此,让我们来创建另外一个简单的 C 文件:

unrandom.c:

int rand(){
    return 42; //the most random number in the universe
}

我们将编译它进入一个共享库中。

gcc -shared -fPIC unrandom.c -o unrandom.so

因此,现在我们已经有了一个可以输出一些随机数的应用程序,和一个定制的库,它使用一个常数值 42 实现了一个 rand() 函数。现在 …… 就像运行 random_num 一样,然后再观察结果:

LD_PRELOAD=$PWD/unrandom.so ./random_nums

如果你想偷懒或者不想自动亲自动手(或者不知什么原因猜不出发生了什么),我来告诉你 —— 它输出了十次常数 42。

如果先这样执行

export LD_PRELOAD=$PWD/unrandom.so

然后再以正常方式运行这个程序,这个结果也许会更让你吃惊:一个未被改变过的应用程序在一个正常的运行方式中,看上去受到了我们做的一个极小的库的影响 ……

等等,什么?刚刚发生了什么?

是的,你说对了,我们的程序生成随机数失败了,因为它并没有使用 “真正的” rand(),而是使用了我们提供的的那个 —— 它每次都返回 42

但是,我们告诉过它去使用真实的那个。我们编程让它去使用真实的那个。另外,在创建那个程序的时候,假冒的 rand() 甚至并不存在!

这句话并不完全正确。我们只能告诉它去使用 rand(),但是我们不能去选择哪个 rand() 是我们希望我们的程序去使用的。

当我们的程序启动后,(为程序提供所需要的函数的)某些库被加载。我们可以使用 ldd 去学习它是怎么工作的:

$ ldd random_nums
linux-vdso.so.1 => (0x00007fff4bdfe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f48c03ec000)
/lib64/ld-linux-x86-64.so.2 (0x00007f48c07e3000)

正如你看到的输出那样,它列出了被程序 random_nums 所需要的库的列表。这个列表是构建进可执行程序中的,并且它是在编译时决定的。在你的机器上的具体的输出可能与示例有所不同,但是,一个 libc.so 肯定是有的 —— 这个文件提供了核心的 C 函数。它包含了 “真正的” rand()

我使用下列的命令可以得到一个全部的函数列表,我们看一看 libc 提供了哪些函数:

nm -D /lib/libc.so.6

这个 nm 命令列出了在一个二进制文件中找到的符号。-D 标志告诉它去查找动态符号,因为 libc.so.6 是一个动态库。这个输出是很长的,但它确实在列出的很多标准函数中包括了 rand()

现在,在我们设置了环境变量 LD_PRELOAD 后发生了什么?这个变量 为一个程序强制加载一些库。在我们的案例中,它为 random_num 加载了 unrandom.so,尽管程序本身并没有这样去要求它。下列的命令可以看得出来:

$ LD_PRELOAD=$PWD/unrandom.so ldd random_nums
linux-vdso.so.1 =>  (0x00007fff369dc000)
/some/path/to/unrandom.so (0x00007f262b439000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f262b044000)
/lib64/ld-linux-x86-64.so.2 (0x00007f262b63d000)

注意,它列出了我们当前的库。实际上这就是代码为什么得以运行的原因:random_num 调用了 rand(),但是,如果 unrandom.so 被加载,它调用的是我们所提供的实现了 rand() 的库。很清楚吧,不是吗?

更清楚地了解

这还不够。我可以用相似的方式注入一些代码到一个应用程序中,并且用这种方式它能够像个正常的函数一样工作。如果我们使用一个简单的 return 0 去实现 open() 你就明白了。我们看到这个应用程序就像发生了故障一样。这是 显而易见的, 真实地去调用原始的 open()

inspect\_open.c:

int open(const char *pathname, int flags){
  /* Some evil injected code goes here. */
  return open(pathname,flags); // Here we call the "real" open function, that is provided to us by libc.so
}

嗯,不对。这将不会去调用 “原始的” open(...)。显然,这是一个无休止的递归调用。

怎么去访问这个 “真正的” open() 函数呢?它需要去使用程序接口进行动态链接。它比听起来更简单。我们来看一个完整的示例,然后,我将详细解释到底发生了什么:

inspect\_open.c:

#define _GNU_SOURCE
#include <dlfcn.h>

typedef int (*orig_open_f_type)(const char *pathname, int flags);

int open(const char *pathname, int flags, ...)
{
    /* Some evil injected code goes here. */

    orig_open_f_type orig_open;
    orig_open = (orig_open_f_type)dlsym(RTLD_NEXT,"open");
    return orig_open(pathname,flags);
}

dlfcn.h 是我们后面用到的 dlsym 函数所需要的。那个奇怪的 #define 是命令编译器去允许一些非标准的东西,我们需要它来启用 dlfcn.h 中的 RTLD_NEXT。那个 typedef 只是创建了一个函数指针类型的别名,它的参数等同于原始的 open —— 它现在的别名是 orig_open_f_type,我们将在后面用到它。

我们定制的 open(...) 的主体是由一些代码构成。它的最后部分创建了一个新的函数指针 orig_open,它指向原始的 open(...) 函数。为了得到那个函数的地址,我们请求 dlsym 在动态库堆栈上为我们查找下一个 open() 函数。最后,我们调用了那个函数(传递了与我们的假冒 open() 一样的参数),并且返回它的返回值。

我使用下面的内容作为我的 “邪恶的注入代码”:

inspect\_open.c (片段):

printf("The victim used open(...) to access '%s'!!!\n",pathname); //remember to include stdio.h!

要编译它,我需要稍微调整一下编译参数:

gcc -shared -fPIC  inspect_open.c -o inspect_open.so -ldl

我增加了 -ldl,因此,它将这个共享库链接到 libdl —— 它提供了 dlsym 函数。(不,我还没有创建一个假冒版的 dlsym ,虽然这样更有趣)

因此,结果是什么呢?一个实现了 open(...) 函数的共享库,除了它有 输出 文件路径的意外作用以外,其它的表现和真正的 open(...) 函数 一模一样。:-)

如果这个强大的诀窍还没有说服你,是时候去尝试下面的这个示例了:

LD_PRELOAD=$PWD/inspect_open.so gnome-calculator

我鼓励你去看看自己实验的结果,但是简单来说,它实时列出了这个应用程序可以访问到的每个文件。

我相信它并不难想像为什么这可以用于去调试或者研究未知的应用程序。请注意,这个特定诀窍并不完整,因为 open() 并不是唯一一个打开文件的函数 …… 例如,在标准库中也有一个 open64(),并且为了完整地研究,你也需要为它去创建一个假冒的。

可能的用法

如果你一直跟着我享受上面的过程,让我推荐一个使用这个诀窍能做什么的一大堆创意。记住,你可以在不损害原始应用程序的同时做任何你想做的事情!

  1. 获得 root 权限。你想多了!你不会通过这种方法绕过安全机制的。(一个专业的解释是:如果 ruid != euid,库不会通过这种方法预加载的。)
  2. 欺骗游戏:取消随机化。这是我演示的第一个示例。对于一个完整的工作案例,你将需要去实现一个定制的 random()rand_r()random_r(),也有一些应用程序是从 /dev/urandom 之类的读取,你可以通过使用一个修改过的文件路径来运行原始的 open() 来把它们重定向到 /dev/null。而且,一些应用程序可能有它们自己的随机数生成算法,这种情况下你似乎是没有办法的(除非,按下面的第 10 点去操作)。但是对于一个新手来说,它看起来很容易上手。
  3. 欺骗游戏:让子弹飞一会 。实现所有的与时间有关的标准函数,让假冒的时间变慢两倍,或者十倍。如果你为时间测量和与时间相关的 sleep 或其它函数正确地计算了新的值,那么受影响的应用程序将认为时间变慢了(你想的话,也可以变快),并且,你可以体验可怕的 “子弹时间” 的动作。或者 甚至更进一步,你的共享库也可以成为一个 DBus 客户端,因此你可以使用它进行实时的通讯。绑定一些快捷方式到定制的命令,并且在你的假冒的时间函数上使用一些额外的计算,让你可以有能力按你的意愿去启用和禁用慢进或快进任何时间。
  4. 研究应用程序:列出访问的文件。它是我演示的第二个示例,但是这也可以进一步去深化,通过记录和监视所有应用程序的文件 I/O。
  5. 研究应用程序:监视因特网访问。你可以使用 Wireshark 或者类似软件达到这一目的,但是,使用这个诀窍你可以真实地控制基于 web 的应用程序发送了什么,不仅是看看,而是也能影响到交换的数据。这里有很多的可能性,从检测间谍软件到欺骗多用户游戏,或者分析和逆向工程使用闭源协议的应用程序。
  6. 研究应用程序:检查 GTK 结构 。为什么只局限于标准库?让我们在所有的 GTK 调用中注入一些代码,因此我们就可以知道一个应用程序使用了哪些组件,并且,知道它们的构成。然后这可以渲染出一个图像或者甚至是一个 gtkbuilder 文件!如果你想去学习一些应用程序是怎么管理其界面的,这个方法超级有用!
  7. 在沙盒中运行不安全的应用程序。如果你不信任一些应用程序,并且你可能担心它会做一些如 rm -rf / 或者一些其它不希望的文件活动,你可以通过修改传递到文件相关的函数(不仅是 open ,也包括删除目录等)的参数,来重定向所有的文件 I/O 操作到诸如 /tmp 这样地方。还有更难的诀窍,如 chroot,但是它也给你提供更多的控制。它可以更安全地完全 “封装”,但除非你真的知道你在做什么,不要以这种方式真的运行任何恶意软件。
  8. 实现特性zlibc 是明确以这种方法运行的一个真实的库;它可以在访问文件时解压文件,因此,任何应用程序都可以在无需实现解压功能的情况下访问压缩数据。
  9. 修复 bug。另一个现实中的示例是:不久前(我不确定现在是否仍然如此)Skype(它是闭源的软件)从某些网络摄像头中捕获视频有问题。因为 Skype 并不是自由软件,源文件不能被修改,这就可以通过使用预加载一个解决了这个问题的库的方式来修复这个 bug。
  10. 手工方式 访问应用程序拥有的内存。请注意,你可以通过这种方式去访问所有应用程序的数据。如果你有类似的软件,如 CheatEngine/scanmem/GameConqueror 这可能并不会让人惊讶,但是,它们都要求 root 权限才能工作,而 LD_PRELOAD 则不需要。事实上,通过一些巧妙的诀窍,你注入的代码可以访问所有的应用程序内存,从本质上看,是因为它是通过应用程序自身得以运行的。你可以修改这个应用程序能修改的任何东西。你可以想像一下,它允许你做许多的底层的侵入…… ,但是,关于这个主题,我将在某个时候写一篇关于它的文章。

这里仅是一些我想到的创意。我希望你能找到更多,如果你做到了 —— 通过下面的评论区共享出来吧!


via: https://rafalcieslak.wordpress.com/2013/04/02/dynamic-linker-tricks-using-ld_preload-to-cheat-inject-features-and-investigate-programs/

作者:Rafał Cieślak 译者:qhwdw 校对:wxy

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

 title=")

管理磁盘空间对系统管理员来说是一件重要的日常工作。一旦磁盘空间耗尽就需要进行一系列耗时而又复杂的任务,以提升磁盘分区中可用的磁盘空间。它也需要系统离线才能处理。通常这种任务会涉及到安装一个新的硬盘、引导至恢复模式或者单用户模式、在新硬盘上创建一个分区和一个文件系统、挂载到临时挂载点去从一个太小的文件系统中移动数据到较大的新位置、修改 /etc/fstab 文件的内容来反映出新分区的正确设备名、以及重新引导来重新挂载新的文件系统到正确的挂载点。

我想告诉你的是,当 LVM (逻辑卷管理)首次出现在 Fedora Linux 中时,我是非常抗拒它的。我最初的反应是,我并不需要在我和我的设备之间有这种额外的抽象层。结果是我错了,逻辑卷管理是非常有用的。

LVM 让磁盘空间管理非常灵活。它提供的功能诸如在文件系统已挂载和活动时,很可靠地增加磁盘空间到一个逻辑卷和它的文件系统中,并且,它也允许你将多个物理磁盘和分区融合进一个可以分割成逻辑卷(LV)的单个卷组(VG)中。

卷管理也允许你去减少分配给一个逻辑卷的磁盘空间数量,但是,这里有两个要求,第一,卷必须是未挂载的。第二,在卷空间调整之前,文件系统本身的空间大小必须先被减少。

有一个重要的提示是,文件系统本身必须允许重新调整大小的操作。当重新提升文件系统大小的时候,EXT2、3 和 4 文件系统都允许离线(未挂载状态)或者在线(挂载状态)重新调整大小。你应该去认真了解你打算去调整的文件系统的详细情况,去验证它们是否可以完全调整大小,尤其是否可以在线调整大小。

即时扩展一个文件系统

在我安装一个新的发行版到我的生产用机器中之前,我总是喜欢在一个 VirtualBox 虚拟机中运行这个新的发行版一段时间,以确保它没有任何的致命的问题存在。在几年前的一个早晨,我在我的主要使用的工作站上的虚拟机中安装了一个新发行的 Fedora 版本。我认为我有足够的磁盘空间分配给安装虚拟机的主文件系统。但是,我错了,大约在安装到三分之一时,我耗尽了我的文件系统的空间。幸运的是,VirtualBox 检测到了磁盘空间不足的状态,并且暂停了虚拟机,然后显示了一个明确指出问题所在的错误信息。

请注意,这个问题并不是虚拟机磁盘太小造成的,而是由于宿主机上空间不足,导致虚拟机上的虚拟磁盘在宿主机上的逻辑卷中没有足够的空间去扩展。

因为许多现在的发行版都缺省使用了逻辑卷管理,并且在我的卷组中有一些可用的空余空间,我可以分配额外的磁盘空间到适当的逻辑卷,然后即时扩展宿主机的文件系统。这意味着我不需要去重新格式化整个硬盘,以及重新安装操作系统或者甚至是重启机器。我不过是分配了一些可用空间到适当的逻辑卷中,并且重新调整了文件系统的大小 —— 所有的这些操作都在文件系统在线并且运行着程序的状态下进行的,虚拟机也一直使用着宿主机文件系统。在调整完逻辑卷和文件系统的大小之后,我恢复了虚拟机的运行,并且继续进行安装过程,就像什么问题都没有发生过一样。

虽然这种问题你可能从来也没有遇到过,但是,许多人都遇到过重要程序在运行过程中发生磁盘空间不足的问题。而且,虽然许多程序,尤其是 Windows 程序,并不像 VirtualBox 一样写的很好,且富有弹性,Linux 逻辑卷管理可以使它在不丢失数据的情况下去恢复,也不需要去进行耗时的安装过程。

LVM 结构

逻辑卷管理的磁盘环境结构如下面的图 1 所示。逻辑卷管理允许多个单独的硬盘和/或磁盘分区组合成一个单个的卷组(VG)。卷组然后可以再划分为逻辑卷(LV)或者被用于分配成一个大的单一的卷。普通的文件系统,如 EXT3 或者 EXT4,可以创建在一个逻辑卷上。

在图 1 中,两个完整的物理硬盘和一个第三块硬盘的一个分区组合成一个单个的卷组。在这个卷组中创建了两个逻辑卷和文件系统,比如,可以在每个逻辑卷上创建一个 EXT3 或者 EXT4 的文件系统。

lvm.png

图 1: LVM 允许组合分区和整个硬盘到卷组中

在一个主机上增加磁盘空间是非常简单的,在我的经历中,这种事情是很少的。下面列出了基本的步骤。你也可以创建一个完整的新卷组或者增加新的空间到一个已存在的逻辑卷中,或者创建一个新的逻辑卷。

增加一个新的逻辑卷

有时候需要在主机上增加一个新的逻辑卷。例如,在被提示包含我的 VirtualBox 虚拟机的虚拟磁盘的 /home 文件系统被填满时,我决定去创建一个新的逻辑卷,以存储包含虚拟磁盘在内的虚拟机数据。这将在我的 /home 文件系统中释放大量的空间,并且也允许我去独立地管理虚拟机的磁盘空间。

增加一个新的逻辑卷的基本步骤如下:

  1. 如有需要,安装一个新硬盘。
  2. 可选: 在硬盘上创建一个分区。
  3. 在硬盘上创建一个完整的物理卷(PV)或者一个分区。
  4. 分配新的物理卷到一个已存在的卷组(VG)中,或者创建一个新的卷组。
  5. 从卷空间中创建一个新的逻辑卷(LV)。
  6. 在新的逻辑卷中创建一个文件系统。
  7. /etc/fstab 中增加适当的条目以挂载文件系统。
  8. 挂载文件系统。

为了更详细的介绍,接下来将使用一个示例作为一个实验去教授关于 Linux 文件系统的知识。

示例

这个示例展示了怎么用命令行去扩展一个已存在的卷组,并给它增加更多的空间,在那个空间上创建一个新的逻辑卷,然后在逻辑卷上创建一个文件系统。这个过程一直在运行着和已挂载的文件系统上执行。

警告:仅 EXT3 和 EXT4 文件系统可以在运行和挂载状态下调整大小。许多其它的文件系统,包括 BTRFS 和 ZFS 是不能这样做的。

安装硬盘

如果在系统中现有硬盘上的卷组中没有足够的空间可以增加,那么可能需要去增加一块新的硬盘,然后创建空间增加到逻辑卷中。首先,安装物理硬盘,然后,接着执行后面的步骤。

从硬盘上创建物理卷

首先需要去创建一个新的物理卷(PV)。使用下面的命令,它假设新硬盘已经分配为 /dev/hdd

pvcreate /dev/hdd

在新硬盘上创建一个任意分区并不是必需的。创建的物理卷将被逻辑卷管理器识别为一个新安装的未处理的磁盘或者一个类型为 83 的 Linux 分区。如果你想去使用整个硬盘,创建一个分区并没有什么特别的好处,而且元数据所用的磁盘空间也能用做 PV 的一部分使用。

扩展已存在的卷组

在这个示例中,我将扩展一个已存在的卷组,而不是创建一个新的;你可以选择其它的方式。在物理磁盘已经创建之后,扩展已存在的卷组(VG)去包含新 PV 的空间。在这个示例中,已存在的卷组命名为:MyVG01。

vgextend /dev/MyVG01 /dev/hdd
创建一个逻辑卷

首先,在卷组中从已存在的空余空间中创建逻辑卷。下面的命令创建了一个 50 GB 大小的 LV。这个卷组的名字为 MyVG01,然后,逻辑卷的名字为 Stuff。

lvcreate -L +50G --name Stuff MyVG01
创建文件系统

创建逻辑卷并不会创建文件系统。这个任务必须被单独执行。下面的命令在新创建的逻辑卷中创建了一个 EXT4 文件系统。

mkfs -t ext4 /dev/MyVG01/Stuff
增加一个文件系统卷标

增加一个文件系统卷标,更易于在文件系统以后出现问题时识别它。

e2label /dev/MyVG01/Stuff Stuff
挂载文件系统

在这个时候,你可以创建一个挂载点,并在 /etc/fstab 文件系统中添加合适的条目,以挂载文件系统。

你也可以去检查并校验创建的卷是否正确。你可以使用 dflvsvgs 命令去做这些工作。

在 LVM 文件系统中调整逻辑卷大小

从 Unix 的第一个版本开始,对文件系统的扩展需求就一直伴随,Linux 也不例外。随着有了逻辑卷管理(LVM),现在更加容易了。

  1. 如有需要,安装一个新硬盘。
  2. 可选: 在硬盘上创建一个分区。
  3. 在硬盘上创建一个完整的物理卷(PV)或者一个分区。
  4. 分配新的物理卷到一个已存在的卷组(VG)中,或者创建一个新的卷组。
  5. 从卷空间中创建一个新的逻辑卷(LV),或者用卷组中部分或全部空间扩展已有的逻辑卷。
  6. 如果创建了新的逻辑卷,那么在上面创建一个文件系统。如果对已有的逻辑卷增加空间,使用 resize2fs 命令来增大文件系统来填满逻辑卷。
  7. /etc/fstab 中增加适当的条目以挂载文件系统。
  8. 挂载文件系统。

示例

这个示例展示了怎么用命令行去扩展一个已存在的卷组。它会给 /Staff 文件系统增加大约 50GB 的空间。这将生成一个可用于挂载的文件系统,在 Linux 2.6 内核(及更高)上可即时使用 EXT3 和 EXT4 文件系统。我不推荐你用于任何关键系统,但是这是可行的,我已经成功了好多次;即使是在根(/)文件系统上。是否使用自己把握风险。

警告:仅 EXT3 和 EXT4 文件系统可以在运行和挂载状态下调整大小。许多其它的文件系统,包括 BTRFS 和 ZFS 是不能这样做的。

安装硬盘

如果在系统中现有硬盘上的卷组中没有足够的空间可以增加,那么可能需要去增加一块新的硬盘,然后创建空间增加到逻辑卷中。首先,安装物理硬盘,然后,接着执行后面的步骤。

从硬盘上创建物理卷

首先需要去创建一个新的物理卷(PV)。使用下面的命令,它假设新硬盘已经分配为 /dev/hdd

pvcreate /dev/hdd

在新硬盘上创建一个任意分区并不是必需的。创建的物理卷将被逻辑卷管理器识别为一个新安装的未处理的磁盘或者一个类型为 83 的 Linux 分区。如果你想去使用整个硬盘,创建一个分区并没有什么特别的好处,而且元数据所用的磁盘空间也能用做 PV 的一部分使用。

增加物理卷到已存在的卷组

在这个示例中,我将使用一个新的物理卷来扩展一个已存在的卷组。在物理卷已经创建之后,扩展已存在的卷组(VG)去包含新 PV 的空间。在这个示例中,已存在的卷组命名为:MyVG01。

vgextend /dev/MyVG01 /dev/hdd
扩展逻辑卷

首先,在卷组中从已存在的空余空间中创建逻辑卷。下面的命令创建了一个 50 GB 大小的 LV。这个卷组的名字为 MyVG01,然后,逻辑卷的名字为 Stuff。

lvcreate -L +50G --name Stuff MyVG01
扩展文件系统

如果你使用了 -r 选项,扩展逻辑卷也将扩展器文件系统。如果你不使用 -r 选项,该操作不行单独执行。下面的命令在新调整大小的逻辑卷中调整了文件系统大小。

resize2fs /dev/MyVG01/Stuff

你也可以去检查并校验调整大小的卷是否正确。你可以使用 dflvsvgs 命令去做这些工作。

提示

过去几年来,我学习了怎么去做让逻辑卷管理更加容易的一些知识,希望这些提示对你有价值。

  • 除非你有一个明确的原因去使用其它的文件系统外,推荐使用可扩展的文件系统。除了 EXT2、3、和 4 外,并不是所有的文件系统都支持调整大小。EXT 文件系统不但速度快,而且它很高效。在任何情况下,如果默认的参数不能满足你的需要,它们(指的是文件系统参数)可以通过一位知识丰富的系统管理员来调优它。
  • 使用有意义的卷和卷组名字。
  • 使用 EXT 文件系统标签

我知道,像我一样,大多数的系统管理员都抗拒逻辑卷管理。我希望这篇文章能够鼓励你至少去尝试一个 LVM。如果你能那样做,我很高兴;因为,自从我使用它之后,我的硬盘管理任务变得如此的简单。

关于作者

David Both 是一位 Linux 和开源软件的倡导者,住在 Raleigh, North Carolina。他在 IT 行业工作了 40 多年,在 IBM 工作了 20 多年。在 IBM 期间,他在 1981 年为最初的 IBM PC 编写了第一个培训课程。他曾教授红帽的 RHCE 课程,并在 MCI Worldcom、Cisco和 North Carolina 工作。他已经使用 Linux 和开源软件工作了将近 20 年。


via: https://opensource.com/business/16/9/linux-users-guide-lvm

作者:David Both 译者:qhwdw 校对:wxy

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

时值年末,我们 Linux 中国在北上广成组织了一次线下沙龙,来自北上广成等多个城市的朋友们欢聚在这四个城市,成功地将友谊从线上连接到了线下。

这次沙龙,是分别在北上广成四个城市分别举办的,并在期间多个城市进行了视频互联。大家俨如多年未见的好友,畅聊了开源、Linux、互联网、区块链、运维、开发、安全等等各种话题。老王我这次参与了北京的沙龙,并全程口若悬河谈了许多话题;据闻上海、广州和成都也都聊得非常尽兴。

这次参与的人员当中,很多人都是 Linux 中国多年的成员,其中不少人是 LCTT 成员。当然,也有许多新朋友。其中据闻有一位最小参与者才上初二!

下面分享一些各地的沙龙精彩照片:

北京沙龙

北京沙龙,老王在说“其一”

上海沙龙

广州沙龙

巴适的成都沙龙

左一棣琦在给译作签名,北京沙龙是妹子最多的一处

上海沙龙,左一是上海召集人瑾萱

上海沙龙合影

北京沙龙合影——新 Linux 中国“黑帮”

北京沙龙在吃羊蝎子

总之,这次沙龙聚会,取得了超乎预期的成功,本次参会的很多小伙伴们也得到由人邮出版社傅主编赞助的书籍(其中有棣琦翻译的《Linux 二进制分析》,北京站更有荣幸得到译者亲自签名!)。

让我们期待,一个月以后的沙龙再次举办,也希望下次我们可以有更多的城市共同举办,并且让更多的同学来参与,并讲演自己的知识和见解!

欢迎希望参与线下沙龙的同学,加微信群助手 “#群主”(微信号:china\_linux)好友,并对她说“沙龙”,获得进入微信群的邀请。