2017年6月

在机器学习、计算机视觉以及高性能计算领域,充分利用显卡计算应用程序的能力已成为当前的热门。类似 OpenCL 的技术通过硬件无关的编程模型展现了这种能力,使得你可以编写抽象于不同体系架构的代码。它的目标是“一次编写,到处运行”,不管它是 Intel CPU、AMD 独立显卡还是 DSP 等等。不幸的是,对于日常程序员,OpenCL 的学习曲线陡峭;一个简单的 Hello World 程序可能就需要上百行晦涩难懂的代码。因此,为了减轻这种痛苦,Khronos 组织已经开发了一个称为 SYCL 的新标准,这是一个在 OpenCL 之上的 C++ 抽象层。通过 SYCL,你可以使用干净、现代的 C++ 开发出这些通用 GPU(GPGPU)应用程序,而无需拘泥于 OpenCL。下面是一个使用 SYCL 开发,通过并行 STL 实现的向量乘法事例:

#include <vector>
#include <iostream>

#include <sycl/execution_policy>
#include <experimental/algorithm>
#include <sycl/helpers/sycl_buffers.hpp>

using namespace std::experimental::parallel;
using namespace sycl::helpers;

int main() {
  constexpr size_t array_size = 1024*512;
  std::array<cl::sycl::cl_int, array_size> a;
  std::iota(begin(a),end(a),0);

  {
    cl::sycl::buffer<int> b(a.data(), cl::sycl::range<1>(a.size()));
    cl::sycl::queue q;
    sycl::sycl_execution_policy<class Mul> sycl_policy(q);
    transform(sycl_policy, begin(b), end(b), begin(b),
              [](int x) { return x*2; });
  }
}

为了作为对比,下面是一个通过 C++ API 使用 OpenCL 编写的大概对应版本(无需花过多时间阅读,只需注意到它看起来难看而且冗长)。

#include <iostream>
#include <array>
#include <numeric>
#include <CL/cl.hpp>

int main(){
    std::vector<cl::Platform> all_platforms;
    cl::Platform::get(&all_platforms);
    if(all_platforms.size()==0){
        std::cout<<" No platforms found. Check OpenCL installation!\n";
        exit(1);
    }
    cl::Platform default_platform=all_platforms[0];

    std::vector<cl::Device> all_devices;
    default_platform.getDevices(CL_DEVICE_TYPE_ALL, &all_devices);
    if(all_devices.size()==0){
        std::cout<<" No devices found. Check OpenCL installation!\n";
        exit(1);
    }

    cl::Device default_device=all_devices[0];
    cl::Context context({default_device});

    cl::Program::Sources sources;
    std::string kernel_code=
        "   void kernel mul2(global int* A){"
        "       A[get_global_id(0)]=A[get_global_id(0)]*2;"
        "   }";
    sources.push_back({kernel_code.c_str(),kernel_code.length()});

    cl::Program program(context,sources);
    if(program.build({default_device})!=CL_SUCCESS){
        std::cout<<" Error building: "<<program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(default_device)<<"\n";
        exit(1);
    }

    constexpr size_t array_size = 1024*512;
    std::array<cl_int, array_size> a;
    std::iota(begin(a),end(a),0);

    cl::Buffer buffer_A(context,CL_MEM_READ_WRITE,sizeof(int)*a.size());
    cl::CommandQueue queue(context,default_device);

    if (queue.enqueueWriteBuffer(buffer_A,CL_TRUE,0,sizeof(int)*a.size(),a.data()) != CL_SUCCESS) {
        std::cout << "Failed to write memory;n";
        exit(1);
    }

    cl::Kernel kernel_add = cl::Kernel(program,"mul2");
    kernel_add.setArg(0,buffer_A);

    if (queue.enqueueNDRangeKernel(kernel_add,cl::NullRange,cl::NDRange(a.size()),cl::NullRange) != CL_SUCCESS) {
        std::cout << "Failed to enqueue kernel\n";
        exit(1);
    }

    if (queue.finish() != CL_SUCCESS) {
        std::cout << "Failed to finish kernel\n";
        exit(1);
    }

    if (queue.enqueueReadBuffer(buffer_A,CL_TRUE,0,sizeof(int)*a.size(),a.data()) != CL_SUCCESS) {
        std::cout << "Failed to read result\n";
        exit(1);
    }
}

在这篇博文中我会介绍使用 SYCL 加速你 GPU 上的 C++ 代码。

GPGPU 简介

在我开始介绍如何使用 SYCL 之前,我首先给那些不熟悉这方面的人简要介绍一下为什么你可能想要在 GPU 上运行计算任务。如果已经使用过 OpenCL、CUDA 或类似的库,可以跳过这一节。

GPU 和 CPU 的一个关键不同就是 GPU 有大量小的、简单的处理单元,而不是少量(对于普通消费者桌面硬件通常是 1-8 个)复杂而强大的核。

CPU 架构

上面是一个 4 核 CPU 的简单漫画示意图。每个核都有一组寄存器以及不同等级的缓存(有些是共享缓存、有些不是),然后是主内存。

GPU 架构

在 GPU 上,多个小处理单元被组成一个执行单元。每个小处理单元都附有少量内存,每个执行单元都有一些共享内存用于它的处理单元。除此之外,还有一些 GPU 范围的内存,然后是 CPU 使用的主内存。执行单元内部的单元是 lockstep ,每个单元都在不同的数据片上执行相同的指令。

这可以使 GPU 同时处理大量的数据。如果是在 CPU 上,也许你可以使用多线程和向量指令在给定时间内完成大量的工作,但是 GPU 所能处理的远多于此。在 GPU 上一次性能够处理的数据规模使得它非常适合于类似图形(duh)、数学处理、神经网络等等。

GPGPU 编程的很多方面使得它和日常的 CPU 编程完全不同。例如,从主内存传输数据到 GPU 是很慢的真的很慢。会完全干掉你的性能使你慢下来。因此,GPU 编程的权衡是尽可能多地利用加速器的高吞吐量来掩盖数据来往的延迟。

这里还有一些不那么明显的问题,例如分支的开销。由于执行单元内的处理单元按照 lockstep 工作,使它们执行不同路径(不同的控制流)的嵌套分支就是个真正的问题。这通常通过在所有单元上执行所有分支并标记出无用结果来解决。这是一个基于嵌套级别的指数级的复杂度,这当然是坏事情。当然,有一些优化方法可以拯救该问题,但需要注意:你从 CPU 领域带来的简单假设和知识在 GPU 领域可能导致大问题。

在我们回到 SYCL 之前,需要介绍一些术语。 主机 host 是主 CPU 运行的机器, 设备 device 是会运行你 OpenCL 代码的地方。设备可能就是主机,但也可能是你机器上的一些加速器、模拟器等。 内核 kernel 是一个特殊函数,它是在你设备上运行代码的入口点。通常还会提供一些主机设置好的缓存给它用于输入和输出数据。

回到 SYCL

这里有两个可用的 SYCL 实现:triSYCL,由 Xilinx 开发的实验性开源版本(通常作为标准的试验台使用),以及 ComputeCpp,由 Codeplay(我在 Codeplay 工作,但这篇文章是在没有我雇主建议的情况下使用我自己时间编写的) 开发的工业级实现(当前处于开发测试版)。只有 ComputeCpp 支持在 GPU 上执行内核,因此在这篇文章中我们会使用它。

第一步是在你的机器上配置以及运行 ComputeCpp。主要组件是一个实现了 SYCL API 的运行时库,以及一个基于 Clang 的编译器,它负责编译你的主机代码和设备代码。在本文写作时,已经在 Ubuntu 和 CentOS 上官方支持 Intel CPU 以及某些 AMD GPU。在其它 Linux 发行版上让它工作也非常简单(例如,我让它在我的 Arch 系统上运行)。对更多的硬件和操作系统的支持正在进行中,查看支持平台文档获取最新列表。这里列出了依赖和组件。你也可能想要下载 SDK,其中包括了示例、文档、构建系统集成文件,以及其它。在这篇文章中我会使用 SYCL 并行 STL,如果你想要自己在家学习的话也要下载它。

一旦你设置好了一切,我们就可以开始通用 GPU 编程了!正如简介中提到的,我的第一个示例使用 SYCL 并行 STL 实现。我们现在来看看如何使用纯 SYCL 编写代码。

#include <CL/sycl.hpp>

#include <array>
#include <numeric>
#include <iostream>

int main() {
      const size_t array_size = 1024*512;
      std::array<cl::sycl::cl_int, array_size> in,out;
      std::iota(begin(in),end(in),0);

      {
          cl::sycl::queue device_queue;
          cl::sycl::range<1> n_items{array_size};
          cl::sycl::buffer<cl::sycl::cl_int, 1> in_buffer(in.data(), n_items);
          cl::sycl::buffer<cl::sycl::cl_int, 1> out_buffer(out.data(), n_items);

          device_queue.submit([&](cl::sycl::handler &cgh) {
              constexpr auto sycl_read = cl::sycl::access::mode::read;
              constexpr auto sycl_write = cl::sycl::access::mode::write;

              auto in_accessor = in_buffer.get_access<sycl_read>(cgh);
              auto out_accessor = out_buffer.get_access<sycl_write>(cgh);

              cgh.parallel_for<class VecScalMul>(n_items,
                  [=](cl::sycl::id<1> wiID) {
                      out_accessor[wiID] = in_accessor[wiID]*2;
                  });
         });
     }
}

我会把它划分为一个个片段。

#include <CL/sycl.hpp>

我们做的第一件事就是包含 SYCL 头文件,它会在我们的命令中添加 SYCL 运行时库。

const size_t array_size = 1024*512;
std::array<cl::sycl::cl_int, array_size> in,out;
std::iota(begin(in),end(in),0);

这里我们构造了一个很大的整型数组并用数字 0array_size-1 初始化(这就是 std::iota 所做的)。注意我们使用 cl::sycl::cl_int 确保兼容性。

{
    //...
}

接着我们打开一个新的作用域,其目的为二:

  1. device_queue 将在该作用域结束时解构,它将阻塞,直到内核完成。
  2. in_bufferout_buffer 也将解构,这将强制数据传输回主机并允许我们从 inout 中访问数据。 cl::sycl::queue device_queue;

现在我们创建我们的命令队列。命令队列是所有工作(内核)在分发到设备之前需要入队的地方。有很多方法可以定制队列,例如说提供设备用于入队或者设置异步错误处理器,但对于这个例子默认构造器就可以了;它会查找兼容的 GPU,如果失败的话会回退到主机 CPU。

cl::sycl::range<1> n_items{array_size};

接下来我们创建一个范围,它描述了内核在上面执行的数据的形状。在我们简单的例子中,它是一个一维数组,因此我们使用 cl::sycl::range<1>。如果数据是二维的,我们就会使用 cl::sycl::range<2>,以此类推。除了 cl::sycl::range,还有 cl::sycl::ndrange,它允许你指定工作组大小以及越界范围,但在我们的例子中我们不需要使用它。

cl::sycl::buffer<cl::sycl::cl_int, 1> in_buffer(in.data(), n_items);
cl::sycl::buffer<cl::sycl::cl_int, 1> out_buffer(out.data(), n_items);

为了控制主机和设备之间的数据共享和传输,SYCL 提供了一个 buffer 类。我们创建了两个 SYCL 缓存用于管理我们的输入和输出数组。

      device_queue.submit([&](cl::sycl::handler &cgh) {/*...*/});

设置好了我们所有数据之后,我们就可以入队真正的工作。有多种方法可以做到,但设置并行执行的一个简单方法是在我们的队列中调用 .submit 函数。对于这个函数我们传递了一个运行时调度该任务时会被执行的“命令组伪函数”(伪函数是规范,不是我创造的)。命令组处理器设置任何内核需要的余下资源并分发它。

constexpr auto sycl_read = cl::sycl::access::mode::read_write;
constexpr auto sycl_write = cl::sycl::access::mode::write;

auto in_accessor = in_buffer.get_access<sycl_read>(cgh);
auto out_accessor = out_buffer.get_access<sycl_write>(cgh);

为了控制到我们缓存的访问并告诉该运行时环境我们会如何使用数据,我们需要创建访问器。很显然,我们创建了一个访问器用于从 in_buffer 读入,一个访问器用于写到 out_buffer

cgh.parallel_for<class VecScalMul>(n_items,
    [=](cl::sycl::id<1> wiID) {
         out_accessor[wiID] = in_accessor[wiID]*2;
    });

现在我们已经完成了所有设置,我们可以真正的在我们的设备上做一些计算了。这里我们根据范围 n_items 在命令组处理器 cgh 之上分发一个内核。实际内核自身是一个使用 work-item 标识符作为输入、输出我们计算结果的 lamda 表达式。在这种情况下,我们从 in_accessor 使用 work-item 标识符作为索引读入,将其乘以 2,然后将结果保存到 out_accessor 相应的位置。<class VecScalMul> 是一个为了在标准 C++ 范围内工作的不幸的副产品,因此我们需要给内核一个唯一的类名以便编译器能完成它的工作。

}

在此之后,我们现在可以访问 out 并期望看到正确的结果。

这里有相当多的新概念在起作用,但使用这些技术你可以看到这些能力和所展现出来的东西。当然,如果你只是想在你的 GPU 上执行一些代码而不关心定制化,那么你就可以使用 SYCL 并行 STL 实现。

SYCL 并行 STL

SYCL 并行 STL 是一个 TS 的并行化实现,它分发你的算法函数对象作为 SYCL 内核。在这个页面前面我们已经看过这样的例子,让我们来快速过一遍。

#include <vector>
#include <iostream>

#include <sycl/execution_policy>
#include <experimental/algorithm>
#include <sycl/helpers/sycl_buffers.hpp>

using namespace std::experimental::parallel;
using namespace sycl::helpers;

int main() {
  constexpr size_t array_size = 1024*512;
  std::array<cl::sycl::cl_int, array_size> in,out;
  std::iota(begin(in),end(in),0);

  {
    cl::sycl::buffer<int> in_buffer(in.data(), cl::sycl::range<1>(in.size()));
    cl::sycl::buffer<int> out_buffer(out.data(), cl::sycl::range<1>(out.size()));
    cl::sycl::queue q;
    sycl::sycl_execution_policy<class Mul> sycl_policy(q);
    transform(sycl_policy, begin(in_buffer), end(in_buffer), begin(out_buffer),
              [](int x) { return x*2; });
  }
}
  constexpr size_t array_size = 1024*512;
  std::array<cl::sycl::cl_int, array_size> in, out;
  std::iota(begin(in),end(in),0);

到现在为止一切如此相似。我们再一次创建一组数组用于保存我们的输入输出数据。

cl::sycl::buffer<int> in_buffer(in.data(), cl::sycl::range<1>(in.size()));
cl::sycl::buffer<int> out_buffer(out.data(), cl::sycl::range<1>(out.size()));
cl::sycl::queue q;

这里我们创建类似上个例子的缓存和队列。

sycl::sycl_execution_policy<class Mul> sycl_policy(q);

这就是有趣的部分。我们从我们的队列中创建 sycl_execution_policy,给它一个名称让内核使用。这个执行策略然后可以像 std::execution::parstd::execution::seq 那样使用。

transform(sycl_policy, begin(in_buffer), end(in_buffer), begin(out_buffer),
          [](int x) { return x*2; });

现在我们的内核分发看起来像提供了一个执行策略的 std::transform 调用。我们传递的闭包会被编译并在设备上执行,而不需要我们做其它更加复杂的设置。

当然,除了 transform 你可以做更多。开发的时候,SYCL 并行 STL 支持以下算法:

  • sort
  • transform
  • for_each
  • for_each_n
  • count_if
  • reduce
  • inner_product
  • transform_reduce

这就是这篇短文需要介绍的东西。如果你想和 SYCL 的开发保持同步,那就要看 sycl.tech。最近重要的开发就是移植 EigenTensorflow 到 SYCL ,为 OpenCL 设备带来引入关注的人工智能编程。对我个人而言,我很高兴看到高级编程模型可以用于异构程序自动优化,以及它们是怎样支持类似 HPXSkelCL 等更高级的技术。


via: https://blog.tartanllama.xyz/c++/2017/05/19/sycl/

作者:TartanLlama 译者:ictlyh 校对:wxy

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

学习 GRUB 引导加载程序是如何预备你的系统并启动操作系统内核的。

 title=

自从上个月为我的文章《Linux 引导和启动过程简介》做研究开始,我对更深入了解 GRUB2 产生了兴趣。这篇文章提供了配置 GRUB2 的简要介绍。为了简便起见,我大多数情况下会使用 GRUB 指代 GRUB2。

GRUB

GRUB 来自 GRand Unified Bootloader 的缩写。它的功能是在启动时从 BIOS 接管掌控、加载自身、加载 Linux 内核到内存,然后再把执行权交给内核。一旦内核开始掌控,GRUB 就完成了它的任务,也就不再需要了。

GRUB 支持多种 Linux 内核,并允许用户在启动时通过菜单在其中选择。我发现这是一种非常有用的工具,因为我有很多次遇到一个应用程序或者系统服务在特定内核版本下失败的问题。有好几次,引导到一个较旧的内核时就可以避免类似的问题。默认情况下,使用 yumdnf 进行更新时会保存三个内核 - 最新的以及两个比较旧的。在被包管理器删除之前所保留的内核数目可以在 /etc/dnf/dnf.conf/etc/yum.conf 文件中配置。我通常把 installonly_limit 的值修改为 9 以便保留 9 个内核。当我不得不恢复到低几个版本的内核时这非常有用。

GRUB 菜单

GRUB 菜单的功能是当默认的内核不是想要的时,允许用户从已经安装的内核中选择一个进行引导。通过上下箭头键允许你选中想要的内核,敲击回车键会使用选中的内核继续引导进程。

GRUB 菜单也提供了超时机制,因此如果用户没有做任何选择,GRUB 就会在没有用户干预的情况下使用默认内核继续引导。敲击键盘上除了回车键之外的任何键会停止终端上显示的倒数计时器。立即敲击回车键会使用默认内核或者选中的内核继续引导进程。

GRUB 菜单提供了一个 “ 救援 rescue ” 内核,用于故障排除或者由于某些原因导致的常规内核不能完成启动过程。不幸的是,这个救援内核不会引导到救援模式。文章后面会更详细介绍这方面的东西。

grub.cfg 文件

grub.cfg 文件是 GRUB 配置文件。它由 grub2-mkconfig 程序根据用户的配置使用一组主配置文件以及 grub 默认文件而生成。/boot/grub2/grub.cfg 文件在 Linux 安装时会初次生成,安装新内核时又会重新生成。

grub.cfg 文件包括了类似 Bash 脚本的代码以及一个按照安装顺序排序的已安装内核列表。例如,如果你有 4 个已安装内核,最新的内核索引是 0,前一个内核索引是 1,最旧的内核索引是 3。如果你能访问 grub.cfg 文件,你应该去看看感受一下它看起来是什么样。grub.cfg 太大也就没有包含在这篇文章中。

GRUB 配置文件

grub.cfg 的主要配置文件都在 /etc/grub.d 目录。该目录中的每个文件都包含了最终会整合到 grub.cfg 文件中的 GRUB 代码。这些配置文件的命名模式以排序方式设计,这使得最终的 grub.cfg 文件可以按正确的顺序整合而成。每个文件都有注释表明该部分的开始和结束,这些注释也是最终的 grub.cfg 文件的一部分,从而可以看出每个部分是由哪个文件生成。分隔注释看起来像这样:

### BEGIN /etc/grub.d/10_linux ###

### END /etc/grub.d/10_linux ###

不要修改这些文件,除非你是一个 GRUB 专家并明白更改会发生什么。无论如何,修改 grub.cfg 文件时你也总应该保留一个原始文件的备份。 40_custom41_custom 这两个特别的文件用于生成用户对 GRUB 配置的修改。你仍然要注意对这些文件的更改的后果,并保存一份原始 grub.cfg 文件的备份。

你也可以把你自己的文件添加到 /etc/grub.d 目录。这样做的一个可能的原因是为非 Linux 操作系统添加菜单行。要注意遵循命名规则,确保配置文件中额外的菜单选项刚好在 10_linux 条目之前或之后。

GRUB 默认文件

老版本 GRUB 的配置非常简单而明了,我只需要修改 /boot/grub/grub.conf 就可以了。对于新版本的 GRUB2,我虽然还可以通过更改 /boot/grub2/grub.cfg 来修改,但和老版本的 GRUB 相比,新版本相对更加复杂。另外,安装一个新内核时 grub.cfg 可能会被重写,因此任何修改都可能消失。当然,GNU.org 的 GRUB 手册确实有过直接创建和修改 /boot/grub2/grub.cfg 的讨论。

一旦你明白了如何做,更改 GRUB2 配置就会变得非常简单。我为之前的文章研究 GRUB2 的时候才明白这个。秘方就在 /etc/default 目录里面,一个自然而然称为 grub 的文件,它可以通过简单的终端命令操作。/etc/default 目录包括了一些类似 Google Chrome、 useradd、 和 grub 程序的配置文件。

/etc/default/grub 文件非常简单。这个 grub 默认文件已经列出了一些有效的键值对。你可以简单地更改现有键值或者添加其它文件中还没有的键。下面的列表 1 显示了一个没有更改过的 /etc/default/grub 文件。

GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' 
   /etc/system-release)"
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="rd.lvm.lv=fedora_fedora25vm/root 
   rd.lvm.lv=fedora_fedora25vm/swap 
   rd.lvm.lv=fedora_fedora25vm/usr rhgb quiet"
GRUB_DISABLE_RECOVERY="true"

列表 1:Fedora 25 一个原始 grub 默认文件。

GRUB 手册 5.1 章节包括了所有可以添加到该 grub 文件的键的信息。我只需要修改 grub 默认文件已经有的一些键值就够了。让我们看看这些键值以及一些在 grub 默认文件中没有出现的每个键的意义。

  • GRUB_TIMEOUT 这个键的值决定了显示 GRUB 选择菜单的时间长度。GRUB 提供了同时保存多个安装内核并在启动时使用 GRUB 菜单在其中选择的功能。这个键的默认值是 5 秒,但我通常修改为 10 秒使得有更多时间查看选项并作出选择。
  • GRUB_DISTRIBUTOR 这个键定义了一个从 /etc/system-release 文件中提取发行版本的 sed 表达式。这个信息用于生成出现在 GRUB 菜单中的每个内核发布版的文本名称,例如 “Fedora” 等。由于不同发行版之间 system-release 文件结构的差异,在你的系统中这个 sed 表达式可能有些不同。
  • GRUB_DEFAULT 决定默认引导哪个内核。如果是 saved,这代表最新内核。这里的其它选项如果是数字则代表了 grub.cfg 中列表的索引。使用索引号 3,就会总是加载列表中的第四个内核,即使安装了一个新内核之后也是。因此使用索引数字的话,在安装一个新内核后会加载不同的内核。要确保引导特定内核版本的唯一方法是设置 GRUB_DEFAULT 的值为想要内核的名称,例如 4.8.13-300.fc25.x86_64
  • GRUB_SAVEDEFAULT 通常,grub 默认文件中不会指定这个选项。当选择不同内核进行引导时,正常操作下该内核只会启动一次。默认内核不会改变。当其设置为 true 并和 GRUB_DEFAULT=saved 一起使用时,这个选项会保存一个不同内核作为默认值。当选择不同内核进行引导时会发生这种情况。
  • GRUB_DISABLE_SUBMENU 一些人可能会希望为 GRUB 菜单创建一个内核的层级菜单结构。这个键和 grub.cfg 中一些额外内核配置允许创建这样的层级结构。例如,主菜单中可能有 productiontest 子菜单,每个子菜单中包括了一些合适的内核。设置它为 false 可以启用子菜单。
  • GRUB_TERMINAL_OUTPUT 一些环境下可能需要或者必要将输出重定向到一个不同的显示控制台或者终端。默认情况下是把输出发送到默认终端,通常 console 等价于 Intel 系列个人电脑的标准输出。另一个有用的选择是在使用串行终端或者 Integrated Lights Out (ILO) 终端连接的数据中心或者实验室环境中指定 serial
  • GRUB_TERMINAL_INPUTGRUB_TERMINAL_OUTPUT 类似,可能需要或者必要重定向输入为串行终端或者 ILO 设备、而不是标准键盘输入。
  • GRUB_CMDLINE_LINUX 这个键包括了在启动时会传递给内核的命令行参数。注意这些参数会被添加到 grub.cfg 所有已安装内核的内核行。这意味着所有已安装的内核在启动时都会有相同的参数。我通常删除 rhgbquiet 参数以便我可以看到引导和启动时内核和 systemd 输出的所有内核信息消息。
  • GRUB_DISABLE_RECOVERY 当这个键的值被设置为 false,GRUB 菜单中就会为每个已安装的内核创建一个恢复条目。当设置为 true 时就不会创建任何恢复条目。但不管这个设置怎样,最后的内核条目总是一个 rescue 选项。不过在 rescue 选项中我遇到了一个问题,下面我会详细介绍。

还有一些你可能觉得有用但我没有在这里介绍的键。它们的描述可以在 GRUB 手册 2 的 5.1 章节找到。

生成 grub.cfg

完成所需的配置之后,就需要生成 /boot/grub2/grub.cfg 文件。这通过下面的命令完成。

grub2-mkconfig > /boot/grub2/grub.cfg

这个命令按照顺序使用位于 /etc/grub.d 的配置文件构建 grub.cfg 文件,然后使用 grub 默认文件的内容修改输出以便获得最终所需的配置。grub2-mkconfig 命令会尝试定位所有已安装的内核并在 grub.cfg 文件的 10_Linux 部分新建条目。它还创建一个 rescue 条目提供一个用于从 Linux 不能启动的严重问题中恢复的方法。

强烈建议你不要手动编辑 grub.cfg 文件,因为任何对该文件的直接修改都会在下一次安装新内核或者手动运行 grub2-mkconfig 时被重写。

问题

我遇到一个如果没有意识到就可能导致严重后果的 GRUB2 问题。这个救援内核没有启动,反而启动了另外一个内核。我发现那是列表中索引为 1 的内核,也就是列表中的第二个内核。额外的测试发现不管使用原始的还是我生成的 grub.cfg 配置文件都会发生这个问题。我在虚拟机和真实硬件上都尝试过而且都发生了这个问题。我只测试了 Fedora 25,因此其它 Fedora 发行版本可能没有这个问题。

注意,从救援内核生成的 “recovery” 内核条目不能引导到维护模式。

我推荐将 grub 默认文件中 GRUB_DISABLE_RECOVERY 的值更改为 “false”,然后生成你自己的 grub.cfg。这会在 GRUB 菜单中为每个已安装的内核生成可用的恢复条目。这些恢复配置能像期望那样工作,从而从那些需要输入密码登录的内核条目中引导到运行级别 1,也就是进入(不需要密码的)单用户维护模式。你也可以按 Ctrl-D 继续正常的引导进入默认运行级别。

总结

GRUB 是引导 Linux 计算机到可用状态过程的一系列事件中,发生在 BIOS 之后的第一步。理解如何配置 GRUB 对于恢复或者处理多种类型的问题非常重要。

这么多年来我多次不得不引导到恢复或者救援模式以便解决多种类型的问题。其中的一些问题确实是类似 /etc/fstab 或其它配置文件中不恰当条目导致的引导问题,也有一些是由于应用程序或者系统软件和最新的内核不兼容的问题。硬件兼容性问题也可能妨碍特定的内核启动。

我希望这些信息能对你开启 GRUB 配置之旅有所帮助。

( 题图 : Internet Archive Book Images. Opensource.com 修改。 CC BY-SA 4.0)


作者简介:

David Both - David Both 是一个居住在 Raleigh,北卡罗来纳州的 Linux 和开源倡导者。他在 IT 界已经有超过 40 年,并在他工作的 IBM 执教 OS/2 20 多年。在 IBM 的时候,他在 1981 年开设了第一个最初 IBM 个人电脑的培训课程。他在红帽教授过 RHCE 课程并在 MCI Worldcom、 Cisco、 和北卡罗来纳州工作过。他已经在 Linux 和开源软件方面工作将近 20 年。


via: https://opensource.com/article/17/3/introduction-grub2-configuration-linux

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

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

备受关注的 LinuxCon 2017(北京)即将在一周后在北京首秀,而国内已经连续举办了 11 届的中国 Linux 内核开发者大会(CLK)也将在金秋十月的北京举办第 12 届。值此 Linux 界两大盛会举办之际,我特意收集了一些 Linux 内核方面的文章分享给大家。

让我们先以一篇漫画开端:《漫画赏析:Linux 内核到底长啥样》,这篇并不算严谨的漫画,来自极客漫画站 TurnOff.us,由 LCTT 翻译组进行汉化和点评,以有趣的方式向大众展示了内核里面都发生了些什么:

Linux 内核都有啥

当然, 作为非专业陈述,就不必深究细节了,但是这篇漫画成功地引起了诸多(伪)Linux 内核爱好者的兴趣。

如果你对 Linux 内核发生了兴趣,想要知道 Linux 内核是如何构建的,那这里也有一篇文章可以指导你,这是一篇由 GitHub 上 0xAX 写的一系列 Linux 内核文章中的一篇, LCTT 成员 @mudongliang 参与了组织翻译。

此系列我们还翻译了数篇数据结构方面的文章,如:双向链表基数树位数组,这些在你做内核开发和研究时肯定会用到。当然,Linus Torvalds 大神向来以对进入内核的代码审核严苛而著称,比如说,他曾经就如何写出具有 “good taste” 的代码而发表过演讲。

说起来,现在内核的变化太快了,简直是日新月异,比如说,我们就注意到 BPF 进入了 4.9 内核,它相当于 BSD 中的 DTrace 一样。另外,据闻 Linux 内核将新增一种异构内存管理,将会加快 Linux 上的机器学习处理能力。

这么多的新特性的涌现,背后代表着大量的代码和贡献人员的辛勤付出。据 2016 年度《Linux 内核开发》报告,自版本 3.18 于 2014 年 12 月 7 日发布以来,已合并了近 115000 个变更,这些贡献来自近 500 家公司的 5062 名开发人员。

当然,Linux 内核发展这么迅速,随着影响力的提升,也越来越引起各界的注意,比如说,华盛顿邮报就曾经批评 Linux “没有一个系统性的机制以在骇客之前发现和解决安全问题,或引入更新的防御技术”,“Linux 内核开发社区没有一个首席安全官”等等。针对这篇文章,LWN 上也有人对此进行了一些回应,并就一些问题进行了辨析和反思。

所以,现在 Linux 内核不仅仅需要更好的安全机制的出现和贡献者的努力,也需要解决 Linux 内核代码审查人员短缺问题

前面说了很多 Linux 内核开发人员更关注的话题, 对于普通的 Linux 用户来说,可能更关注的是如何在 CentOSUbuntu 上升级内核。不过,现在的内核已经支持升级后不重启了,对于某些内核补丁,可以热应用而不用重启。这对于生产环境中的 Linux 服务器很重要,比如 UbuntuOracle Linux 等发行版已经支持了。

作为 Linux 的使用者,尤其是 Linux 服务器的运维人员,密切监控 Linux 的各项性能指标也是必需的工作,无论是传统工具: top、ps、pstree、vmstatiostat,还是 htopnmonntopng 这样的新工具;而且不但有 cpustatCoreFreq 这样专门监控 CPU 的工具,也有各种大而全的全面监控系统,如 GlancesnetdataMunin。总之,用于监控的工具和系统不要太多了

那么,你喜欢 Linux ,喜欢研究下 Linux 内核么?如果是,那么这两场大会你一定要关注:

Play 商店又一次重新设计!这一版非常接近现在的设计,卡片结构让改变布局变得易如反掌。 [Ron Amadeo 供图]

周期外更新——谁需要一个新系统?

在安卓 4.2 和安卓 4.3 之间,谷歌进行了一次周期外更新,显示了有多少安卓可以不用经过费力的 OTA 更新而得到改进。得益于谷歌 Play 商店和 Play 服务,这些更新可以在不更新任何系统核心组件的前提下送达。

2013 年 4 月,谷歌发布了谷歌 Play 商店的一个主要设计改动。就如同在这之后的大多数重新设计,新的 Play 商店完全接受了 Google Now 审美,即在灰色背景上的白色卡片。操作栏基于当前页面内容部分更改颜色,由于首屏内容以商店的各部分为主,操作栏颜色是中性的灰色。导航至内容部分的按钮指向热门付费,在那下面通常是一块促销内容或一组推荐应用。

独立的内容部分有漂亮的颜色。 [Ron Amadeo 供图]

新的 Play 商店展现了谷歌卡片设计语言的真正力量,在所有的屏幕尺寸上能够拥有响应式布局。一张大的卡片能够和若干小卡片组合,大屏幕设备能够显示更多的卡片,而且相对于拉伸来适应横屏模式,可以通过在一行显示更多卡片来适应。Play 商店的内容编辑们也可以自由地使用卡片布局;需要关注的大更新可以获得更大的卡片。这个设计最终会慢慢渗透向其它谷歌 Play 内容应用,最后拥有一个统一的设计。

Hangouts 取代了 Google Talk,现在仍由 Google+ 团队继续开发。 [Ron Amadeo 供图]

Google I/O,谷歌的年度开发者会议,通常会宣布一个新的安卓版本。但是 2013 年的会议,谷歌只是发布了一些改进而没有系统更新。

谷歌宣布的大事件之一是 Google Talk 的更新,谷歌的即时消息平台。在很长一段时间里,谷歌随安卓附带四个文本交流应用:Google Talk,Google+ Messenger,信息(短信应用),Google Voice。拥有四个应用来完成相同的任务——给某人发送文本消息——对用户来说很混乱。在 I/O 上,谷歌结束了 Google Talk 并且从头开始创建全新的消息产品 Google Hangouts。虽然最初只是想替代 Google Talk,Hangouts 的计划是统一所有谷歌的不同的消息应用到统一的界面下。

Hangouts 的用户界面布局真的和 Google Talk 没什么大的差别。主页面包含你的聊天会话,点击某一项就能进入聊天页面。界面设计上有所更新,聊天页面现在使用了卡片风格来显示每个段落,并且聊天列表是个“抽屉”风格的界面,这意味着你可以通过水平滑动打开它。Hangouts 有已读回执和输入状态指示,并且群聊现在是个主要特性。

Google+ 是 Hangouts 的中心,所以产品的全名实际上是“Google+ Hangouts”。Hangouts 完全整合到了 Google+ 桌面站点。身份和头像直接从 Google+ 拉取,点击头像会打开用户的 Google+ 资料。和将浏览器换为 Google Chrome 类似,核心安卓功能交给了一个单独的团队——Google+ 团队,这是变得越发繁忙的安卓工程师的抗议的结果。随着 Google+ 团队的接手,安卓的主要即时通讯客户端现在成为一个持续开发的应用。它被放进了 Play 商店并且有稳定的更新频率。

新导航抽屉界面。 图片来自 developer.android.com

谷歌还给操作栏引入了新的设计元素:导航抽屉。这个抽屉显示为在左上角应用图标旁的三道横线。点击或从屏幕左边缘向右滑动,会出现一个侧边菜单目录。就像名字所指明的,这个是用来在应用内导航的,它会显示若干应用内的顶层位置。这使得应用首屏可以用来显示内容,也给了用户一致的、易于访问的导航元素。导航抽屉基本上就是个大号的菜单,可以滚动并且固定在左侧。


Ron Amadeo / Ron是Ars Technica的评论编缉,专注于安卓系统和谷歌产品。他总是在追寻新鲜事物,还喜欢拆解事物看看它们到底是怎么运作的。@RonAmadeo


via: http://arstechnica.com/gadgets/2016/10/building-android-a-40000-word-history-of-googles-mobile-os/23/

译者:alim0x 校对:wxy

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

自从 2010 年,已经有 29000 个学生完成了 Wiki Ed 这一项目。他们在维基百科上添加了 2500 万词条,相当于 85000 页纸张的内容。这相当于最新出版的 Britannica 百科全书中全部词条的 66%。Wiki Ed 的学生们最积极的时候,他们贡献了维基百科上 10% 的内容, 极大地补充了贫乏的学术板块。

为了了解更多关于这个项目的信息,我联络了 LiAnna Davis -- Wiki Ed 项目的负责人。他极富热情地同意来回答我的问题。

提示:Wiki 教育基金会 (Wiki Ed) 的平台是用自由软件搭建的,你可以在这里找到: WikiEdu Dashboard GitHub

Wiki Ed 这一项目是如何启动的?说说你的背景以及你是如何参与进这个项目的。

在2010年,维基基金会(简称 WMF,运营维基百科的非营利组织)注意到了一个趋势 -- 大学的教职人员如果本身也是维基词条的编辑者,会成功地将编辑维基词条作为一项任务交给了自己课堂里的学生。WMF 就此开展了一个试行项目试图解决这个问题:如果本身不编辑维基词条的教职人员支持课程中包含维基词条的编辑任务,他们是否可以通过维基百科实现教学呢?

我是这个团队的一员,在 2010 年被试行雇用,我对这个问题的回答是:“可以。” 在 2013年,WMF 将这个项目的美国分部和加拿大分部拆分,形成了一个新的非营利性组织 -- 维基教育基金会( Wiki Ed);自此我也从 WMF 到了Wiki Ed。自那以后我们便成为了一个独立组织,我们可以专注于这个项目并且将这个项目拓展开来 -- 起初 WMF 时期每年只有 75 个班级参与,而这个学期已经有 275 班级参与了 Wiki Ed。

人们总是觉得大学生对科技相关的一切事物都很敏感,尤其是互联网,但是你们的网站上写道,“大学本科学生可能具有高科技敏感度,但这并不代表他们具有数字信息素养。” 你可以稍微解释一下这句话吗?

仅仅因为一个人可以搞明白自己的 iPhone 如何使用不代表他们可以明辨他们在网上阅读的信息是否值得信赖。 斯坦福的一项研究 (“评估信息:公民在线推理的基石 November 22, 2016.”)在近期表明:学生们并不具有数字信息素养。然而,当学生们在撰写维基百科文章之时,他们必须这样。他们需要严格遵守维基百科的可信来源 规范,这些规范明确了维基百科上的任何信息都必须注明来源,这些来源必须是独立的,而且是可以追溯的事实。对于很多学生,这样注明来源还是第一次,多数人之前仅仅是通过谷歌搜索话题或者注明他们亲眼所见的第一手资料来进行资料的检索和查证。他们需要理解哪些资料可靠,哪些资料不可靠,从而成为足够成熟的电子信息消费者。

你想对那些声称“维基百科不是一个可靠的来源”的人说些什么?

维基百科是一本百科全书,根据在词典中的定义,它是一个第三手资料。当学生们开始读本科的时候,他们应该学会参考一手和二手资料,而不是第三手资料,因此学生不应该,且不能引用维基百科的内容。但是维基百科在研究之初会是一个不错的选择,它可以给你对这个话题一个广泛的认识,并且帮你发现页面底部那些你可以参考的文章来源。就像我们所鼓励的:“不要引用!用你的语言写!”

这样做可以让学生们代入知识生产者的身份,而不是知识的消费者……

一个教授在 Wiki Ed 项目中的参与是如何影响到他的学生的呢?

通过运用维基百科教学,导师们可以给学生们提供有价值的媒体素养、批判性思维、线上交流以及写作能力,不论他们在毕业之后会继续选择科研或是加入工作大军,这些极富价值的品质和技能都能帮助他们成功。这样做可以让学生们代入知识生产者的身份,而不是知识的消费者,而且这可以给予他们一个真正在这个世界做出改变的机会,而不是学生们到了学期末便会抛之脑后的生硬习题。

我们正在积极鼓励新的班级在春季学期加入这个项目。感兴趣的教职人员可以点击 维基教育基金会的教学页 由此开始。

一个教师会需要哪些职业素养来参与这个项目?学生又是需要哪些训练呢?

当你在维基教育基金会的教学页上注册的时候,你会发现 Wiki Ed 为新加入的教职人员提供了如何运用维基百科实现教学的在线指南。我们还有为学生提供的一系列的在线训练,根据他们不同的任务自动分配不同的指南(例如,如果你希望你的学生在文章中插入图片,他们会得到一个如何插入编辑图片的教学单元,如果你不需要,他们便不会得到这个单元的指南)。在部分十几个学科中,我们还有特定的指南书展示了这几个学科的特定编辑方式。除此之外,我们还有熟练维基百科编辑的工作人员帮助回答学生和导师们的问题。我们的系统已经在逐步扩大;我们在这个秋季学期已经支持了超过 6300 个学生,并且已经适应了与没有维基编辑经验的教职人员合作,因此这样就保证了没有人会因为资历不够而无法参与。

访问学者 Visiting Scholars ”这个项目是指?

我们正在试着建立学术界和维基百科之间的联系,而鼓励运用维基百科教学的项目只是其中的一环。在“ 访问学者 Visiting Scholars ”这一项目中,大学图书馆或者教学部门将公开他们的资料,并提供给一个缺乏资料的认证的维基百科编辑人员(被称作是“访问学者”)。通过大学这一渠道,“访问学者”们可以有机会使用这些资源来完善维基百科广泛领域中的文章。这是一个规模相对小却伟大的项目,因为我们花了很多时间来建立这样的联系,但是这些“学者”们都产出了许多真的很精彩的内容。

你的合作伙伴有谁?他们的参与如何影响到你现在的成功呢,或者在未来将会如何呢?

我们与那些将我们工作的价值看做对他们的学科的一种服务的学术机构合作。让我们面对这个现实:当人们想要获取关于一个话题的知识之时,他们不会去读那些同行审议过,并在这些机构发表的学术论文,他们会去维基百科。通过鼓励这些机构中的教职人员用维基百科教学,正可以完善维基百科的在这些学科中的内容和信息。我们的合作伙伴扩充了我们的潜在成员,并且让我们以合作身份大量完善了维基百科上特定学科的内容。

我们欢迎读者点击这个支持我们 的页面通过捐赠来支持我们的工作。

(题图:opensource.com)


作者简介:

Don Watkins 是一个教育家,专注教育技术,创业家,开源提倡者。教育心理学硕士、教育领导力硕士、Linux 系统管理员、思科认证网络支持工程师、Virtual Box 虚拟化认证。关注他: @Don\_Watkins 。


via: https://opensource.com/article/17/1/Wiki-Education-Foundation

作者:Don Watkins 译者:scoutydren 校对:Bestony

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

使用 7 条简单的 Git 命令开始你的软件开发之旅

你是否曾经想知道如何学好 Git?你长期以来都是跌跌撞撞地在使用 Git。最终,你总需要掌握它的窍门。这就是我写这篇文章的原因,我将带你去启蒙之旅。这儿是我关于如何加快 Git 学习过程的基本指南。我将介绍 Git 的实际情况以及我使用最多的 7 条 Git 命令。本文主要针对有兴趣的开发人员和大学新生,他们需要关于 Git 的介绍以及如何掌握基础知识。


你可以往前继续阅读整篇文章,或者只读 TLDR; 部分,尽管这将使我很受伤。

TLDR;

在学习 Git 的过程中,请养成下面这些步骤的习惯:

  1. 随时使用 git status
  2. 只更改那些你真正想更改的文件。
  3. git add -A 会是你的朋友。
  4. 随时使用命令 git commit -m "meaningful messages"
  5. 做任何推送(push)之前先使用命令 git pull,但是这需要在你提交过一些更改之后。
  6. 最后,git push推送提交的更改。

良宵莫辜负

对任何开发人员来说,通常第一步都是选择一个广泛使用的地方托管他或她的代码库。那就是,GitHub。它是一切有关代码的聚集地。要理解 GitHub 的概念,你先需要知道什么是 Git。

Git 是一款基于命令行的版本控制软件,在 Windows 和 Mac 系统上也有几款可用的桌面应用。 Git 由 Linux 之父 Linus Torvalds 开发,Linus Torvalds 还是是计算机科学中最有影响力的人物之一。因为这一优势,Git 已经成为绝大多数软件开发人员关于共享和维护代码的标准。这一大段话,让我们将其细细道来。正如它的名字所说,版本控制软件 Git 让你可以预览你写过的代码的所有版本。从字面上来说, 开发人员的每个代码库都将永远存储在其各自的仓库中,仓库可以叫做任何名字,从 pineappleexpress 都行。在此仓库开发代码的过程中,你将进行出无数次的更改,直到第一次正式发布。这就是版本控制软件如此重要的核心原因所在。它让作为开发人员的你可以清楚地了解对代码库进行的所有更改、修订和改进。从另外一个方面说,它使协同合作更容易,下载代码进行编辑,然后将更改上传到仓库。然而,尽管有了这么多好处,然而还有一件事可以锦上添花。你可以下载并使用这些文件,即使你在整个开发过程中什么事也没有做。

让我们回到文章的 GitHub 部分。它只是所有仓库的枢纽(hub),这些仓库可以存储在其中并在线浏览。它是一个让有着共同兴趣的人相聚的地方。

千里之行始于足下

OK,记住,Git 是一款软件,像任何其他软件一样,你首先需要安装它:

Git - 安装 Git,如果你希望从源代码安装 Git,你需要安装这些 Git 的依赖库: autotools —— 来自 git-scm.com

Tips:请点击上面的链接,然后按照说明开始。

完成了安装过程,很好。现在你需要在你的浏览器地址栏输入 github.com 访问该网站。如果你还没有帐号的话需要新创建一个帐号,这就是你的起舞之处。登录并创建一个新仓库,命名为 Steve ,没有什么理由,只是想要一个名为史蒂夫的仓库好玩而已。选中 “Initialize this repository with a README” 复选框并点击创建按钮。现在你有了一个叫做 Steve 的仓库。我相信你会为你自己感到自豪。

现在开始在使用 Git

现在是比较有趣的部分。你将把 Steve 克隆到你本地的机器上。可以把这个过程看作从 Github 上复制仓库到你的电脑上。点击 “clone or download” 按钮,你将看到一个类似下面这样的 URL:

https://github.com/yourGithubAccountName/Steve.git

复制这个 URL 并打开命令提示符窗口。现在输入并运行条命令:

git clone https://github.com/yourGithubAccountName/Steve.git

Abrakadabra!Steve 仓库已经被自动克隆到了你的电脑上。查看你克隆这个仓库的目录,你会看到一个叫做 Steve 的文件夹。这个本地的文件夹现在已经链接到了它的 “origin” ,也就是 GitHub 上的远程仓库。

记住这个过程,在你的软件开发工程人员的职业生涯中你一定会重复这个过程很多次的。完成所有这些准备工作之后,你就可以开始使用最普通且常用的 Git 命令了。

你现在已经开始在真实场景使用 Git 了

找到 Steve 目录并在该目录中打开命令提示符窗口,运行下面的命令:

git status

这会输出你的工作目录的状态,让你知道所有你编辑过的文件。这意味着它显示了远程库中和本地工作目录中之间的文件差异。status 命令被用来作为 commit 的模版,我将在这篇教程后面进一步谈论 commit 。简单的说,[git status][1] 告诉你你编辑过哪些文件,以给你一个你想要上传到远程库的概述。

但是,在你做任何上传之前,你首先需要做的是选择你需要发送回远程库的文件。使用下面命令完成:

git add

接着在 Steve 目录新建一个文本文件,可以取一个好玩的名字 pineapple.txt。在这个文件里面随便写些你想写的内容,返回命令提示符,然后再次输入 git status。现在,你将看到这个文件以红色出现在标记 “untracked files” 下面。

On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
  (use "git add <file>..." to include in what will be commited)

pineapple.txt

下一步就是将它添加到暂存区(staging)。暂存区可以看作是这样的一个环境:你做过的所有更改在提交时都将捆绑为一个更改而被提交。现在,你可以将这个文件加入暂存区:

git add -A

-A 选项意味着所有你更改过的文件都会被加到暂存区等待提交。然而, git add 非常灵活,它也可以像这样一个文件一个文件的添加:

git add pineapple.txt

这种方法让你有能力选择你想要暂存的每一个文件,而不用担心加入那些你不想改变的东西。

再次运行 git status,你会看到如下输出:

On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

new file:   pineapple.txt

准备好提交更改了吗?开始吧。

git commit -m "Write your message here"

Git commit 命令会将存储在暂存区中的文件和来自用户的用于描述更改的日志信息一起存储在一个新的地方。-m选项加入了写在双引号内的信息。

再次检查状态,你会看到:

On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)
nothing to commit, working directory clean

所有的更改现在都被加入到一次提交当中了,同时会有一条与你所做相关的信息。现在你可以用 git push 将这次提交推送到远程库 “origin”了。这条命令就像字面意义所说,它会把你提交的更改从本地机器上传到 GitHub 的远程仓库中。返回到命令提示符,然后运行:

git push

你会被要求输入你的 GitHub 帐号和密码,之后你会看到类似下面的这些内容:

Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 280 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/yourGithubUserName/Steve.git
   c77a97c..08bb95a  master -> master

就是这样。你已经成功上传了你本地的更改。看看你在 GitHub 上的仓库,你会看到它现在包含了一叫做 pineapple.txt 的文件。

如果你是一个开发小组的一员呢?如果他们都推送提交到 “origin”,将会发生什么?这就是 Git 真正开始发挥它的魔力的时候。你可以使用一条简单的命令轻松地将最新版本的代码库 pull 到你本地的机器上:

git pull

但是 Git 也有限制:你需要有相匹配的版本才能推送到 “origin”。这意味着你本地的版本需要和 origin 的版本大致一样。当你从 “origin” 拉取(pull)文件时,在你的工作目录中不能有文件,因为它们将会在这个过程中被覆盖。因此我给出了这条简单的建议。在学习 Git 的过程中,请养成下面这些步骤的习惯:

  1. 随时使用 git status
  2. 只更改那些你真正想更改的文件。
  3. git add -A 会是你的朋友。
  4. 随时使用命令 git commit -m "meaningful messages"
  5. 做任何推送(push)之前先使用命令 git pull,但是这需要在你提交过一些更改之后。
  6. 最后,git push推送提交的更改。

嘿!你还在看这篇文章吗?你已经看了很久了,休息一下吧!

休息好了吗?好的!让我们来处理一些错误。如果你不小心更改了一些你本不应该更改的文件后怎么办呢?不需要担心,只需要使用 git checkout。让我们在文件 pineapple.txt 里更改一些内容:在文件中加入一行,比方说,“Steve is mega-awesome!” 。然后保存更改并用 git status 检查一下:

On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

modified:   pineapple.txt

no changes added to commit (use "git add" and/or "git commit -a")

正如预料的那样,它已经被记录为一次更改了。但是,假如 Steve 实际上并不是很优秀呢?假如 Steve 很差劲呢?不用担心!最简单的还原更改的方式是运行命令:

git checkout -- pineapple.txt

现在你会看到文件已经恢复到了先前的状态。

但是假如你玩砸了呢?我是说,事情已经变得混乱,并且需要把所有东西重置到与 “origin” 一样的状态。也不需要担心,在这种紧急情况下我们可以享受 Git 的美妙之处:

git reset --hard

git reset 命令和 --hard 选项一起可以抛弃自上次提交以来的所有更改,有些时候真的很好用。


最后,我想鼓励你尽可能多地使用 Git。这是能够熟练使用它的最好学习方式。除此之外,养成阅读 Git 文档的习惯。一开始可能会有些云里雾里,但是过段时间后你就会明白它的窍门了。

希望你们(小伙子和姑娘们)读这篇文章的时候会和我写它时一样的开心。如果你认为这篇文章对别人有用,你尽可以与别人分享它。或者,如果你喜欢这篇文章,你可以在下面点下赞以便让更多的人看到这篇文章。


via: https://hackernoon.com/how-to-master-the-art-of-git-68e1050f3147

作者:Adnan Rahić 译者:zhousiyu325 校对:wxy

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