C++ 程序员 Protocol Buffers 基础指南

Summary: 如何序列化和获取结构化的数据？Protocol buffers 是针对这个问题的一种灵活、高效、自动化的解决方案。

好久没见 C++ 的美文了

好文！费心了！

# C++ 程序员 Protocol Buffers 基础指南

这篇教程提供了一个面向 C++ 程序员关于 protocol buffers 的基础介绍。通过创建一个简单的示例应用程序，它将向我们展示：

* 在 `.proto` 文件中定义消息格式
* 使用 protocol buffer 编译器
* 使用 C++ protocol buffer API 读写消息

这不是一个关于在 C++ 中使用 protocol buffers 的全面指南。要获取更详细的信息，请参考 [Protocol Buffer Language Guide](https://developers.google.com/protocol-buffers/docs/proto) 和 [Encoding Reference](https://developers.google.com/protocol-buffers/docs/encoding)。

### 为什么使用 Protocol Buffers

我们接下来要使用的例子是一个非常简单的"地址簿"应用程序，它能从文件中读取联系人详细信息。地址簿中的每一个人都有一个名字、ID、邮件地址和联系电话。

如何序列化和获取结构化的数据？这里有几种解决方案：

* 以二进制形式发送/接收原生的内存数据结构。通常，这是一种脆弱的方法，因为接收/读取代码必须基于完全相同的内存布局、大小端等环境进行编译。同时，当文件增加时，原始格式数据会随着与该格式相关的软件而迅速扩散，这将导致很难扩展文件格式。
* 你可以创造一种 ad-hoc 方法，将数据项编码为一个字符串——比如将 4 个整数编码为 `12:3:-23:67`。虽然它需要编写一次性的编码和解码代码且解码需要耗费一点运行时成本，但这是一种简单灵活的方法。这最适合编码非常简单的数据。
* 序列化数据为 XML。这种方法是非常吸引人的，因为 XML 是一种适合人阅读的格式，并且有为许多语言开发的库。如果你想与其他程序和项目共享数据，这可能是一种不错的选择。然而，众所周知，XML 是空间密集型的，且在编码和解码时，它对程序会造成巨大的性能损失。同时，使用 XML DOM 树被认为比操作一个类的简单字段更加复杂。

Protocol buffers 是针对这个问题的一种灵活、高效、自动化的解决方案。使用 Protocol buffers，你需要写一个 `.proto` 说明，用于描述你所希望存储的数据结构。利用 `.proto` 文件，protocol buffer 编译器可以创建一个类，用于实现对高效的二进制格式的 protocol buffer 数据的自动化编码和解码。产生的类提供了构造 protocol buffer 的字段的 getters 和 setters，并且作为一个单元来处理读写 protocol buffer 的细节。重要的是，protocol buffer 格式支持格式的扩展，代码仍然可以读取以旧格式编码的数据。

<img src="content_image/album_201611_03_235451klrr358u5pz8trxu.png" alt="" title="">

### 在哪可以找到示例代码

示例代码被包含于源代码包，位于“examples”文件夹。可在[这里](https://developers.google.com/protocol-buffers/docs/downloads.html)下载代码。

### 定义你的协议格式

为了创建自己的地址簿应用程序，你需要从 `.proto` 开始。`.proto` 文件中的定义很简单：为你所需要序列化的每个数据结构添加一个<ruby> 消息 <rp>  （ </rp> <rt>  message </rt> <rp>  ） </rp></ruby>，然后为消息中的每一个字段指定一个名字和类型。这里是定义你消息的 `.proto` 文件 `addressbook.proto`。

```
package tutorial;

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phone = 4;
}

message AddressBook {
  repeated Person person = 1;
}

```

如你所见，其语法类似于 C++ 或 Java。我们开始看看文件的每一部分内容做了什么。

`.proto` 文件以一个 `package` 声明开始，这可以避免不同项目的命名冲突。在 C++，你生成的类会被置于与 `package` 名字一样的命名空间。

下一步，你需要定义<ruby> 消息 <rp>  （ </rp> <rt>  message </rt> <rp>  ） </rp></ruby>。消息只是一个包含一系列类型字段的集合。大多标准的简单数据类型是可以作为字段类型的，包括 `bool`、`int32`、`float`、`double` 和 `string`。你也可以通过使用其他消息类型作为字段类型，将更多的数据结构添加到你的消息中——在以上的示例，`Person` 消息包含了 `PhoneNumber` 消息，同时 `AddressBook` 消息包含 `Person` 消息。你甚至可以定义嵌套在其他消息内的消息类型——如你所见，`PhoneNumber` 类型定义于 `Person` 内部。如果你想要其中某一个字段的值是预定义值列表中的某个值，你也可以定义 `enum` 类型——这儿你可以指定一个电话号码是 `MOBILE`、`HOME` 或 `WORK` 中的某一个。

每一个元素上的 `= 1`、`= 2` 标记确定了用于二进制编码的唯一<ruby> “标签” <rp>  （ </rp> <rt>  tag </rt> <rp>  ） </rp></ruby>。标签数字 1-15 的编码比更大的数字少需要一个字节，因此作为一种优化，你可以将这些标签用于经常使用的元素或 `repeated` 元素，剩下 16 以及更高的标签用于非经常使用的元素或 `optional` 元素。每一个 `repeated` 字段的元素需要重新编码标签数字，因此 `repeated` 字段适合于使用这种优化手段。

每一个字段必须使用下面的修饰符加以标注：

* `required`：必须提供该字段的值，否则消息会被认为是<ruby> “未初始化的” <rp>  （ </rp> <rt>  uninitialized </rt> <rp>  ） </rp></ruby>。如果 `libprotobuf` 以调试模式编译，序列化未初始化的消息将引起一个断言失败。以优化形式构建，将会跳过检查，并且无论如何都会写入该消息。然而，解析未初始化的消息总是会失败（通过 `parse` 方法返回 `false`）。除此之外，一个 `required` 字段的表现与 `optional` 字段完全一样。
* `optional`：字段可能会被设置，也可能不会。如果一个 `optional` 字段没被设置，它将使用默认值。对于简单类型，你可以指定你自己的默认值，正如例子中我们对电话号码的 `type` 一样，否则使用系统默认值：数字类型为 0、字符串为空字符串、布尔值为 false。对于嵌套消息，默认值总为消息的“默认实例”或“原型”，它的所有字段都没被设置。调用 accessor 来获取一个没有显式设置的 `optional`（或 `required`） 字段的值总是返回字段的默认值。
* `repeated`：字段可以重复任意次数（包括 0 次）。`repeated` 值的顺序会被保存于 protocol buffer。可以将 `repeated` 字段想象为动态大小的数组。

你可以查找关于编写 `.proto` 文件的完整指导——包括所有可能的字段类型——在 [Protocol Buffer Language Guide](https://developers.google.com/protocol-buffers/docs/proto) 里面。不要在这里面查找与类继承相似的特性，因为 protocol buffers 不会做这些。

>
> **`required` 是永久性的**
>
>
> 在把一个字段标识为 `required` 的时候，你应该特别小心。如果在某些情况下你不想写入或者发送一个 `required` 的字段，那么将该字段更改为 `optional` 可能会遇到问题——旧版本的读者（LCTT 译注：即读取、解析旧版本 Protocol Buffer 消息的一方）会认为不含该字段的消息是不完整的，从而有可能会拒绝解析。在这种情况下，你应该考虑编写特别针对于应用程序的、自定义的消息校验函数。Google 的一些工程师得出了一个结论：使用 `required` 弊多于利；他们更愿意使用 `optional` 和 `repeated` 而不是 `required`。当然，这个观点并不具有普遍性。
>
>
>

### 编译你的 Protocol Buffers

既然你有了一个 `.proto`，那你需要做的下一件事就是生成一个将用于读写 `AddressBook` 消息的类（从而包括 `Person` 和 `PhoneNumber`）。为了做到这样，你需要在你的 `.proto` 上运行 protocol buffer 编译器 `protoc`：

1. 如果你没有安装编译器，请[下载这个包](https://developers.google.com/protocol-buffers/docs/downloads.html)，并按照 README 中的指令进行安装。
2. 现在运行编译器，指定源目录（你的应用程序源代码位于哪里——如果你没有提供任何值，将使用当前目录）、目标目录（你想要生成的代码放在哪里；常与 `$SRC_DIR` 相同），以及你的 `.proto` 路径。在此示例中：

```
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto

```

因为你想要 C++ 的类，所以你使用了 `--cpp_out` 选项——也为其他支持的语言提供了类似选项。

在你指定的目标文件夹，将生成以下的文件：

* `addressbook.pb.h`，声明你生成类的头文件。
* `addressbook.pb.cc`，包含你的类的实现。

### Protocol Buffer API

让我们看看生成的一些代码，了解一下编译器为你创建了什么类和函数。如果你查看 `addressbook.pb.h`，你可以看到有一个在 `addressbook.proto` 中指定所有消息的类。关注 `Person` 类，可以看到编译器为每个字段生成了<ruby> 读写函数 <rp>  （ </rp> <rt>  accessors </rt> <rp>  ） </rp></ruby>。例如，对于 `name`、`id`、`email` 和 `phone` 字段，有下面这些方法：（LCTT 译注：此处原文所指文件名有误，径该之。）

```
// name
inline bool has_name() const;
inline void clear_name();
inline const ::std::string& name() const;
inline void set_name(const ::std::string& value);
inline void set_name(const char* value);
inline ::std::string* mutable_name();

// id
inline bool has_id() const;
inline void clear_id();
inline int32_t id() const;
inline void set_id(int32_t value);

// email
inline bool has_email() const;
inline void clear_email();
inline const ::std::string& email() const;
inline void set_email(const ::std::string& value);
inline void set_email(const char* value);
inline ::std::string* mutable_email();

// phone
inline int phone_size() const;
inline void clear_phone();
inline const ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >& phone() const;
inline ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >* mutable_phone();
inline const ::tutorial::Person_PhoneNumber& phone(int index) const;
inline ::tutorial::Person_PhoneNumber* mutable_phone(int index);
inline ::tutorial::Person_PhoneNumber* add_phone();

```

正如你所见到，getters 的名字与字段的小写名字完全一样，并且 setter 方法以 `set_` 开头。同时每个<ruby> 单一 <rp>  （ </rp> <rt>  singular </rt> <rp>  ） </rp></ruby>（`required` 或 `optional`）字段都有 `has_` 方法，该方法在字段被设置了值的情况下返回 true。最后，所有字段都有一个 `clear_` 方法，用以清除字段到<ruby> 空 <rp>  （ </rp> <rt>  empty </rt> <rp>  ） </rp></ruby>状态。

数字型的 `id` 字段仅有上述的基本<ruby> 读写函数 <rp>  （ </rp> <rt>  accessors </rt> <rp>  ） </rp></ruby>集合，而 `name` 和 `email` 字段有两个额外的方法，因为它们是字符串——一个是可以获得字符串直接指针的`mutable_` 的 getter ，另一个为额外的 setter。注意，尽管 `email` 还没被<ruby> 设置 <rp>  （ </rp> <rt>  set </rt> <rp>  ） </rp></ruby>，你也可以调用 `mutable_email`；因为 `email` 会被自动地初始化为空字符串。在本例中，如果你有一个单一的（`required` 或 `optional`）消息字段，它会有一个 `mutable_` 方法，而没有 `set_` 方法。

`repeated` 字段也有一些特殊的方法——如果你看看 `repeated` 的 `phone` 字段的方法，你可以看到：

* 检查 `repeated` 字段的 `_size`（也就是说，与 `Person` 相关的电话号码的个数）
* 使用下标取得特定的电话号码
* 更新特定下标的电话号码
* 添加新的电话号码到消息中，之后你便可以编辑。（`repeated` 标量类型有一个 `add_` 方法，用于传入新的值）

为了获取 protocol 编译器为所有字段定义生成的方法的信息，可以查看 [C++ generated code reference](https://developers.google.com/protocol-buffers/docs/reference/cpp-generated)。

#### 枚举和嵌套类

与 `.proto` 的枚举相对应，生成的代码包含了一个 `PhoneType` 枚举。你可以通过 `Person::PhoneType` 引用这个类型，通过 `Person::MOBILE`、`Person::HOME` 和 `Person::WORK` 引用它的值。（实现细节有点复杂，但是你无须了解它们而可以直接使用）

编译器也生成了一个 `Person::PhoneNumber` 的嵌套类。如果你查看代码，你可以发现真正的类型为 `Person_PhoneNumber`，但它通过在 `Person` 内部使用 `typedef` 定义，使你可以把 `Person_PhoneNumber` 当成嵌套类。唯一产生影响的一个例子是，如果你想要在其他文件前置声明该类——在 C++ 中你不能前置声明嵌套类，但是你可以前置声明 `Person_PhoneNumber`。

#### 标准的消息方法

所有的消息方法都包含了许多别的方法，用于检查和操作整个消息，包括：

* `bool IsInitialized() const;` ：检查是否所有 `required` 字段已经被设置。
* `string DebugString() const;` ：返回人类可读的消息表示，对调试特别有用。
* `void CopyFrom(const Person& from);`：使用给定的值重写消息。
* `void Clear();`：清除所有元素为空的状态。

上面这些方法以及下一节要讲的 I/O 方法实现了被所有 C++ protocol buffer 类共享的<ruby> 消息 <rp>  （ </rp> <rt>  Message </rt> <rp>  ） </rp></ruby>接口。为了获取更多信息，请查看 [complete API documentation for Message](https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.message.html#Message)。

#### 解析和序列化

最后，所有 protocol buffer 类都有读写你选定类型消息的方法，这些方法使用了特定的 protocol buffer [二进制格式](https://developers.google.com/protocol-buffers/docs/encoding)。这些方法包括：

* `bool SerializeToString(string* output) const;`：序列化消息并将消息字节数据存储在给定的字符串中。注意，字节数据是二进制格式的，而不是文本格式；我们只使用 `string` 类作为合适的容器。
* `bool ParseFromString(const string& data);`：从给定的字符创解析消息。
* `bool SerializeToOstream(ostream* output) const;`：将消息写到给定的 C++ `ostream`。
* `bool ParseFromIstream(istream* input);`：从给定的 C++ `istream` 解析消息。

这些只是两个用于解析和序列化的选择。再次说明，可以查看 `Message API reference` 完整的列表。

>
> **Protocol Buffers 和面向对象设计**
>
>
> Protocol buffer 类通常只是纯粹的数据存储器（像 C++ 中的结构体）；它们在对象模型中并不是一等公民。如果你想向生成的 protocol buffer 类中添加更丰富的行为，最好的方法就是在应用程序中对它进行封装。如果你无权控制 `.proto` 文件的设计的话，封装 protocol buffers 也是一个好主意（例如，你从另一个项目中重用一个 `.proto` 文件）。在那种情况下，你可以用封装类来设计接口，以更好地适应你的应用程序的特定环境：隐藏一些数据和方法，暴露一些便于使用的函数，等等。**但是你绝对不要通过继承生成的类来添加行为。**这样做的话，会破坏其内部机制，并且不是一个好的面向对象的实践。
>
>
>

### 写消息

现在我们尝试使用 protocol buffer 类。你的地址簿程序想要做的第一件事是将个人详细信息写入到地址簿文件。为了做到这一点，你需要创建、填充 protocol buffer 类实例，并且将它们写入到一个<ruby> 输出流 <rp>  （ </rp> <rt>  output stream </rt> <rp>  ） </rp></ruby>。

这里的程序可以从文件读取 `AddressBook`，根据用户输入，将新 `Person` 添加到 `AddressBook`，并且再次将新的 `AddressBook` 写回文件。这部分直接调用或引用 protocol buffer 类的代码会以“`// pb`”标出。

```
#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h" // pb
using namespace std;

// This function fills in a Person message based on user input.
void PromptForAddress(tutorial::Person* person) {
  cout << "Enter person ID number: ";
  int id;
  cin >> id;
  person->set_id(id);   // pb
  cin.ignore(256, '\n');

  cout << "Enter name: ";
  getline(cin, *person->mutable_name());    // pb

  cout << "Enter email address (blank for none): ";
  string email;
  getline(cin, email);
  if (!email.empty()) { // pb
    person->set_email(email);   // pb
  }

  while (true) {
    cout << "Enter a phone number (or leave blank to finish): ";
    string number;
    getline(cin, number);
    if (number.empty()) {
      break;
    }

    tutorial::Person::PhoneNumber* phone_number = person->add_phone();  //pb
    phone_number->set_number(number);   // pb

    cout << "Is this a mobile, home, or work phone? ";
    string type;
    getline(cin, type);
    if (type == "mobile") {
      phone_number->set_type(tutorial::Person::MOBILE); // pb
    } else if (type == "home") {
      phone_number->set_type(tutorial::Person::HOME);   // pb
    } else if (type == "work") {
      phone_number->set_type(tutorial::Person::WORK);   // pb
    } else {
      cout << "Unknown phone type.  Using default." << endl;
    }
  }
}

// Main function:  Reads the entire address book from a file,
//   adds one person based on user input, then writes it back out to the same
//   file.
int main(int argc, char* argv[]) {
  // Verify that the version of the library that we linked against is
  // compatible with the version of the headers we compiled against.
  GOOGLE_PROTOBUF_VERIFY_VERSION;   // pb

  if (argc != 2) {
    cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
    return -1;
  }

  tutorial::AddressBook address_book;   // pb

  {
    // Read the existing address book.
    fstream input(argv[1], ios::in | ios::binary);
    if (!input) {
      cout << argv[1] << ": File not found.  Creating a new file." << endl;
    } else if (!address_book.ParseFromIstream(&input)) {    // pb
      cerr << "Failed to parse address book." << endl;
      return -1;
    }
  }

  // Add an address.
  PromptForAddress(address_book.add_person());  // pb

  {
    // Write the new address book back to disk.
    fstream output(argv[1], ios::out | ios::trunc | ios::binary);
    if (!address_book.SerializeToOstream(&output)) {    // pb
      cerr << "Failed to write address book." << endl;
      return -1;
    }
  }

  // Optional:  Delete all global objects allocated by libprotobuf.
  google::protobuf::ShutdownProtobufLibrary();  // pb

  return 0;
}

```

注意 `GOOGLE_PROTOBUF_VERIFY_VERSION` 宏。它是一种好的实践——虽然不是严格必须的——在使用 C++ Protocol Buffer 库之前执行该宏。它可以保证避免不小心链接到一个与编译的头文件版本不兼容的库版本。如果被检查出来版本不匹配，程序将会终止。注意，每个 `.pb.cc` 文件在初始化时会自动调用这个宏。

同时注意在程序最后调用 `ShutdownProtobufLibrary()`。它用于释放 Protocol Buffer 库申请的所有全局对象。对大部分程序，这不是必须的，因为虽然程序只是简单退出，但是 OS 会处理释放程序的所有内存。然而，如果你使用了内存泄漏检测工具，工具要求全部对象都要释放，或者你正在写一个 Protocol Buffer 库，该库可能会被一个进程多次加载和卸载，那么你可能需要强制 Protocol Buffer 清除所有东西。

### 读取消息

当然，如果你无法从它获取任何信息，那么这个地址簿没多大用处！这个示例读取上面例子创建的文件，并打印文件里的所有内容。

```
#include <iostream>
#include <fstream>
#include <string>
#include "addressbook.pb.h" // pb
using namespace std;

// Iterates though all people in the AddressBook and prints info about them.
void ListPeople(const tutorial::AddressBook& address_book) {    // pb
  for (int i = 0; i < address_book.person_size(); i++) {        // pb
    const tutorial::Person& person = address_book.person(i);    // pb

    cout << "Person ID: " << person.id() << endl;   // pb
    cout << "  Name: " << person.name() << endl;    // pb
    if (person.has_email()) {   // pb
      cout << "  E-mail address: " << person.email() << endl;   // pb
    }

    for (int j = 0; j < person.phone_size(); j++) { // pb
      const tutorial::Person::PhoneNumber& phone_number = person.phone(j);  // pb

      switch (phone_number.type()) {    // pb
        case tutorial::Person::MOBILE:  // pb
          cout << "  Mobile phone #: ";
          break;
        case tutorial::Person::HOME:    // pb
          cout << "  Home phone #: ";
          break;
        case tutorial::Person::WORK:    // pb
          cout << "  Work phone #: ";
          break;
      }
      cout << phone_number.number() << endl;    // ob
    }
  }
}

// Main function:  Reads the entire address book from a file and prints all
//   the information inside.
int main(int argc, char* argv[]) {
  // Verify that the version of the library that we linked against is
  // compatible with the version of the headers we compiled against.
  GOOGLE_PROTOBUF_VERIFY_VERSION;   // pb

  if (argc != 2) {
    cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
    return -1;
  }

  tutorial::AddressBook address_book;   // pb

  {
    // Read the existing address book.
    fstream input(argv[1], ios::in | ios::binary);
    if (!address_book.ParseFromIstream(&input)) {   // pb
      cerr << "Failed to parse address book." << endl;
      return -1;
    }
  }

  ListPeople(address_book);

  // Optional:  Delete all global objects allocated by libprotobuf.
  google::protobuf::ShutdownProtobufLibrary();  // pb

  return 0;
}

```

### 扩展 Protocol Buffer

或早或晚在你发布了使用 protocol buffer 的代码之后，毫无疑问，你会想要 "改善" protocol buffer 的定义。如果你想要新的 buffers 向后兼容，并且老的 buffers 向前兼容——几乎可以肯定你很渴望这个——这里有一些规则，你需要遵守。在新的 protocol buffer 版本：

* 你绝不可以修改任何已存在字段的标签数字
* 你绝不可以添加或删除任何 `required` 字段
* 你可以删除 `optional` 或 `repeated` 字段
* 你可以添加新的 `optional` 或 `repeated` 字段，但是你必须使用新的标签数字（也就是说，标签数字在 protocol buffer 中从未使用过，甚至不能是已删除字段的标签数字）。

（对于上面规则有一些[例外情况](https://developers.google.com/protocol-buffers/docs/proto#updating)，但它们很少用到。）

如果你能遵守这些规则，旧代码则可以欢快地读取新的消息，并且简单地忽略所有新的字段。对于旧代码来说，被删除的 `optional` 字段将会简单地赋予默认值，被删除的 `repeated` 字段会为空。新代码显然可以读取旧消息。然而，请记住新的 `optional` 字段不会呈现在旧消息中，因此你需要显式地使用 `has_` 检查它们是否被设置或者在 `.proto` 文件在标签数字后使用 `[default = value]` 提供一个合理的默认值。如果一个 `optional` 元素没有指定默认值，它将会使用类型特定的默认值：对于字符串，默认值为空字符串；对于布尔值，默认值为 false；对于数字类型，默认类型为 0。注意，如果你添加一个新的 `repeated` 字段，新代码将无法辨别它被留空（被新代码）或者从没被设置（被旧代码），因为 `repeated` 字段没有 `has_` 标志。

### 优化技巧

C++ Protocol Buffer 库已极度优化过了。但是，恰当的用法能够更多地提高性能。这里是一些技巧，可以帮你从库中挤压出最后一点速度：

* 尽可能复用消息对象。即使它们被清除掉，消息也会尽量保存所有被分配来重用的内存。因此，如果我们正在处理许多相同类型或一系列相似结构的消息，一个好的办法是重用相同的消息对象，从而减少内存分配的负担。但是，随着时间的流逝，对象可能会膨胀变大，尤其是当你的消息尺寸（LCTT 译注：各消息内容不同，有些消息内容多一些，有些消息内容少一些）不同的时候，或者你偶尔创建了一个比平常大很多的消息的时候。你应该自己通过调用 [SpaceUsed](https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.message.html#Message.SpaceUsed.details) 方法监测消息对象的大小，并在它太大的时候删除它。
* 对于在多线程中分配大量小对象的情况，你的操作系统内存分配器可能优化得不够好。你可以尝试使用 google 的 [tcmalloc](http://code.google.com/p/google-perftools/)。

### 高级用法

Protocol Buffers 绝不仅用于简单的数据存取以及序列化。请阅读 [C++ API reference](https://developers.google.com/protocol-buffers/docs/reference/cpp/index.html) 来看看你还能用它来做什么。

protocol 消息类所提供的一个关键特性就是<ruby> 反射 <rp>  （ </rp> <rt>  reflection </rt> <rp>  ） </rp></ruby>。你不需要针对一个特殊的消息类型编写代码，就可以遍历一个消息的字段并操作它们的值。一个使用反射的有用方法是 protocol 消息与其他编码互相转换，比如 XML 或 JSON。反射的一个更高级的用法可能就是可以找出两个相同类型的消息之间的区别，或者开发某种“协议消息的正则表达式”，利用正则表达式，你可以对某种消息内容进行匹配。只要你发挥你的想像力，就有可能将 Protocol Buffers 应用到一个更广泛的、你可能一开始就期望解决的问题范围上。

反射是由 [Message::Reflection interface](https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.message.html#Message.Reflection) 提供的。

---

via: <https://developers.google.com/protocol-buffers/docs/cpptutorial>

作者：[Google](https://developers.google.com/protocol-buffers/docs/cpptutorial) 译者：[cposture](https://github.com/cposture) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


新手指南：通过 Docker 在 Linux 上托管 .NET Core

想学习linux系统，能不能推荐一下书籍，或其他的学习方式，谢谢！

来自黑龙江哈尔滨的 Chrome Mobile 46.0|Android 6.0 用户

# 新手指南：通过 Docker 在 Linux 上托管 .NET Core

这篇文章基于我之前的文章 [.NET Core 入门](http://blog.scottlogic.com/2016/01/20/restful-api-with-aspnet50.html)。首先，我把 RESTful API 从 .NET Core RC1 升级到了 .NET Core 1.0，然后，我增加了对 Docker 的支持并描述了如何在 Linux 生产环境里托管它。

我是首次接触 Docker 并且距离成为一名 Linux 高手还有很远的一段路程。因此，这里的很多想法是来自一个新手。

### 安装

按照 <https://www.microsoft.com/net/core> 上的介绍在你的电脑上安装 .NET Core 。这将会同时在 Windows 上安装 dotnet 命令行工具以及最新的 Visual Studio 工具。

### 源代码

你可以直接到 [GitHub](https://github.com/niksoper/aspnet5-books/tree/blog-docker) 上找最到最新完整的源代码。

### 转换到 .NET CORE 1.0

自然地，当我考虑如何把 API 从 .NET Core RC1 升级到 .NET Core 1.0 时想到的第一个求助的地方就是谷歌搜索。我是按照下面这两条非常全面的指导来进行升级的：

* [从 DNX 迁移到 .NET Core CLI](https://docs.microsoft.com/en-us/dotnet/articles/core/migrating-from-dnx)
* [从 ASP.NET 5 RC1 迁移到 ASP.NET Core 1.0](https://docs.asp.net/en/latest/migration/rc1-to-rtm.html)

当你迁移代码的时候，我建议仔细阅读这两篇指导，因为我在没有阅读第一篇指导的情况下又尝试浏览第二篇，结果感到非常迷惑和沮丧。

我不想描述细节上的改变因为你可以看 GitHub 上的[提交](https://github.com/niksoper/aspnet5-books/commit/b41ad38794c69a70a572be3ffad051fd2d7c53c0)。这儿是我所作改变的总结：

* 更新 `global.json` 和 `project.json` 上的版本号
* 删除 `project.json` 上的废弃章节
* 使用轻型 `ControllerBase` 而不是 `Controller`， 因为我不需要与 MVC 视图相关的方法（这是一个可选的改变）。
* 从辅助方法中去掉 `Http` 前缀，比如：`HttpNotFound` -> `NotFound`
* `LogVerbose` -> `LogTrace`
* 名字空间改变： `Microsoft.AspNetCore.*`
* 在 `Startup` 中使用 `SetBasePath`（没有它 `appsettings.json` 将不会被发现）
* 通过 `WebHostBuilder` 来运行而不是通过 `WebApplication.Run` 来运行
* 删除 Serilog（在写文章的时候，它不支持 .NET Core 1.0）

唯一令我真正头疼的事是需要移动 Serilog。我本可以实现自己的文件记录器，但是我删除了文件记录功能，因为我不想为了这次操作在这件事情上花费精力。

不幸的是，将有大量的第三方开发者扮演追赶 .NET Core 1.0 的角色，我非常同情他们，因为他们通常在休息时间还坚持工作但却依旧根本无法接近靠拢微软的可用资源。我建议阅读 Travis Illig 的文章 [.NET Core 1.0 发布了，但 Autofac 在哪儿](http://www.paraesthesia.com/archive/2016/06/29/netcore-rtm-where-is-autofac/)？这是一篇关于第三方开发者观点的文章。

做了这些改变以后，我可以从 `project.json` 目录恢复、构建并运行 dotnet，可以看到 API 又像以前一样工作了。

### 通过 Docker 运行

在我写这篇文章的时候， Docker 只能够在 Linux 系统上工作。在 [Windows](https://docs.docker.com/engine/installation/windows/#/docker-for-windows) 系统和 [OS X](https://docs.docker.com/engine/installation/mac/) 上有 beta 支持 Docker，但是它们都必须依赖于虚拟化技术，因此，我选择把 Ubuntu 14.04 当作虚拟机来运行。如果你还没有安装过 Docker，请按照[指导](https://docs.docker.com/engine/installation/linux/ubuntulinux/)来安装。

我最近阅读了一些关于 Docker 的东西，但我直到现在还没有真正用它来干任何事。我假设读者还没有关于 Docker 的知识，因此我会解释我所使用的所有命令。

#### HELLO DOCKER

在 Ubuntu 上安装好 Docker 之后，我所进行的下一步就是按照 <https://www.microsoft.com/net/core#docker> 上的介绍来开始运行 .NET Core 和 Docker。

首先启动一个已安装有 .NET Core 的容器。

```
docker run -it microsoft/dotnet:latest

```

`-it` 选项表示交互，所以你执行这条命令之后，你就处于容器之内了，可以如你所希望的那样执行任何 bash 命令。

然后我们可以执行下面这五条命令来在 Docker 内部运行起来微软 .NET Core 控制台应用程序示例。

```
mkdir hwapp
cd hwapp
dotnet new
dotnet restore
dotnet run

```

你可以通过运行 `exit` 来离开容器，然后运行 `Docker ps -a` 命令，这会显示你创建的那个已经退出的容器。你可以通过上运行命令 `Docker rm <container_name>` 来清除容器。

#### 挂载源代码

我的下一步骤是使用和上面相同的 `microsoft/dotnet` 镜像，但是将为我们的应用程序以[数据卷](https://docs.docker.com/engine/tutorials/dockervolumes/1)的方式挂载上源代码。

首先签出有相关提交的仓库：

```
git clone https://github.com/niksoper/aspnet5-books.git
cd aspnet5-books/src/MvcLibrary
git checkout dotnet-core-1.0

```

现在启动一个容器来运行 .NET Core 1.0，并将源代码放在 `/book` 下。注意更改 `/path/to/repo` 这部分文件来匹配你的电脑：

```
docker run -it \
-v /path/to/repo/aspnet5-books/src/MvcLibrary:/books \
microsoft/dotnet:latest

```

现在你可以在容器中运行应用程序了！

```
cd /books
dotnet restore
dotnet run

```

作为一个概念性展示这的确很棒，但是我们可不想每次运行一个程序都要考虑如何把源代码安装到容器里。

#### 增加一个 DOCKERFILE

我的下一步骤是引入一个 Dockerfile，这可以让应用程序很容易在自己的容器内启动。

我的 Dockerfile 和 `project.json` 一样位于 `src/MvcLibrary` 目录下，看起来像下面这样：

```
FROM microsoft/dotnet:latest

# 为应用程序源代码创建目录
RUN mkdir -p /usr/src/books
WORKDIR /usr/src/books

# 复制源代码并恢复依赖关系
COPY . /usr/src/books
RUN dotnet restore

# 暴露端口并运行应用程序
EXPOSE 5000
CMD [ "dotnet", "run" ]

```

严格来说，`RUN mkdir -p /usr/src/books` 命令是不需要的，因为 `COPY` 会自动创建丢失的目录。

Docker 镜像是按层建立的，我们从包含 .NET Core 的镜像开始，添加另一个从源代码生成应用程序，然后运行这个应用程序的层。

添加了 Dockerfile 以后，我通过运行下面的命令来生成一个镜像，并使用生成的镜像启动一个容器（确保在和 Dockerfile 相同的目录下进行操作，并且你应该使用自己的用户名）。

```
docker build -t niksoper/netcore-books .
docker run -it niksoper/netcore-books

```

你应该看到程序能够和之前一样的运行，不过这一次我们不需要像之前那样安装源代码，因为源代码已经包含在 docker 镜像里面了。

#### 暴露并发布端口

这个 API 并不是特别有用，除非我们需要从容器外面和它进行通信。 Docker 已经有了暴露和发布端口的概念，但这是两件完全不同的事。

据 Docker [官方文档](https://docs.docker.com/engine/reference/builder/#/expose)：

>
> `EXPOSE` 指令通知 Docker 容器在运行时监听特定的网络端口。`EXPOSE` 指令不能够让容器的端口可被主机访问。要使可被访问，你必须通过 `-p` 标志来发布一个端口范围或者使用 `-P` 标志来发布所有暴露的端口
>
>
>

`EXPOSE` 指令只是将元数据添加到镜像上，所以你可以如文档中说的认为它是镜像消费者。从技术上讲，我本应该忽略 `EXPOSE 5000` 这行指令，因为我知道 API 正在监听的端口，但把它们留下很有用的，并且值得推荐。

在这个阶段，我想直接从主机访问这个 API ，因此我需要通过 `-p` 指令来发布这个端口，这将允许请求从主机上的端口 5000 转发到容器上的端口 5000，无论这个端口是不是之前通过 Dockerfile 暴露的。

```
docker run -d -p 5000:5000 niksoper/netcore-books

```

通过 `-d` 指令告诉 docker 在分离模式下运行容器，因此我们不能看到它的输出，但是它依旧会运行并监听端口 5000。你可以通过 `docker ps` 来证实这件事。

因此，接下来我准备从主机向容器发起一个请求来庆祝一下：

```
curl http://localhost:5000/api/books

```

它不工作。

重复进行相同 `curl` 请求，我看到了两个错误：要么是 `curl: (56) Recv failure: Connection reset by peer`，要么是 `curl: (52) Empty reply from server`。

我返回去看 docker run 的[文档](https://docs.docker.com/engine/reference/run/#/expose-incoming-ports)，然后再次检查我所使用的 `-p` 选项以及 Dockerfile 中的 `EXPOSE` 指令是否正确。我没有发现任何问题，这让我开始有些沮丧。

重新振作起来以后，我决定去咨询当地的一个 Scott Logic DevOps 大师 - Dave Wybourn（也在[这篇 Docker Swarm 的文章](http://blog.scottlogic.com/2016/08/30/docker-1-12-swarm-mode-round-robin.html)里提到过），他的团队也曾遇到这个实际问题。这个问题是我没有配置过 [Kestral](https://docs.asp.net/en/latest/fundamentals/servers.html#kestrel)，这是一个全新的轻量级、跨平台 web 服务器，用于 .NET Core 。

默认情况下， Kestrel 会监听 `http://localhost:5000`。但问题是，这儿的 `localhost` 是一个回路接口。

据[维基百科](https://en.wikipedia.org/wiki/Localhost)：

>
> 在计算机网络中，localhost 是一个代表本机的主机名。本地主机可以通过网络回路接口访问在主机上运行的网络服务。通过使用回路接口可以绕过任何硬件网络接口。
>
>
>

当运行在容器内时这是一个问题，因为 `localhost` 只能够在容器内访问。解决方法是更新 `Startup.cs` 里的 `Main` 方法来配置 Kestral 监听的 URL：

```
public static void Main(string[] args)
{
  var host = new WebHostBuilder()
    .UseKestrel()
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseUrls("http://*:5000") // 在所有网络接口上监听端口 5000
    .UseIISIntegration()
    .UseStartup<Startup>()
    .Build();

  host.Run();
}

```

通过这些额外的配置，我可以重建镜像，并在容器中运行应用程序，它将能够接收来自主机的请求：

```
docker build -t niksoper/netcore-books .
docker run -d -p 5000:5000 niksoper/netcore-books
curl -i http://localhost:5000/api/books

```

我现在得到下面这些相应：

```
HTTP/1.1 200 OK
Date: Tue, 30 Aug 2016 15:25:43 GMT
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Server: Kestrel

[{"id":"1","title":"RESTful API with ASP.NET Core MVC 1.0","author":"Nick Soper"}]

```

### 在产品环境中运行 KESTREL

[微软的介绍](https://docs.asp.net/en/latest/publishing/linuxproduction.html#why-use-a-reverse-proxy-server)：

>
> Kestrel 可以很好的处理来自 ASP.NET 的动态内容，然而，网络服务部分的特性没有如 IIS，Apache 或者 Nginx 那样的全特性服务器那么好。反向代理服务器可以让你不用去做像处理静态内容、缓存请求、压缩请求、SSL 端点这样的来自 HTTP 服务器的工作。
>
>
>

因此我需要在我的 Linux 机器上把 Nginx 设置成一个反向代理服务器。微软介绍了如何[发布到 Linux 生产环境下](https://docs.asp.net/en/latest/publishing/linuxproduction.html)的指导教程。我把说明总结在这儿：

1. 通过 `dotnet publish` 来给应用程序产生一个自包含包。
2. 把已发布的应用程序复制到服务器上
3. 安装并配置 Nginx（作为反向代理服务器）
4. 安装并配置 [supervisor](http://supervisord.org/)（用于确保 Nginx 服务器处于运行状态中）
5. 安装并配置 [AppArmor](https://wiki.ubuntu.com/AppArmor)（用于限制应用的资源使用）
6. 配置服务器防火墙
7. 安全加固 Nginx（从源代码构建和配置 SSL）

这些内容已经超出了本文的范围，因此我将侧重于如何把 Nginx 配置成一个反向代理服务器。自然地，我通过 Docker 来完成这件事。

### 在另一个容器中运行 NGINX

我的目标是在第二个 Docker 容器中运行 Nginx 并把它配置成我们的应用程序容器的反向代理服务器。

我使用的是[来自 Docker Hub 的官方 Nginx 镜像](https://hub.docker.com/_/nginx/)。首先我尝试这样做：

```
docker run -d -p 8080:80 --name web nginx

```

这启动了一个运行 Nginx 的容器并把主机上的 8080 端口映射到了容器的 80 端口上。现在在浏览器中打开网址 `http://localhost:8080` 会显示出 Nginx 的默认登录页面。

现在我们证实了运行 Nginx 是多么的简单，我们可以关闭这个容器。

```
docker rm -f web

```

### 把 NGINX 配置成一个反向代理服务器

可以通过像下面这样编辑位于 `/etc/nginx/conf.d/default.conf` 的配置文件，把 Nginx 配置成一个反向代理服务器：

```
server {
  listen 80;

  location / {
    proxy_pass http://localhost:6666;
  }
}

```

通过上面的配置可以让 Nginx 将所有对根目录的访问请求代理到 `http://localhost:6666`。记住这里的 `localhost` 指的是运行 Nginx 的容器。我们可以在 Nginx容器内部利用卷来使用我们自己的配置文件：

```
docker run -d -p 8080:80 \
-v /path/to/my.conf:/etc/nginx/conf.d/default.conf \
nginx

```

注意：这把一个单一文件从主机映射到容器中，而不是一个完整目录。

### 在容器间进行通信

Docker 允许内部容器通过共享虚拟网络进行通信。默认情况下，所有通过 Docker 守护进程启动的容器都可以访问一种叫做“桥”的虚拟网络。这使得一个容器可以被另一个容器在相同的网络上通过 IP 地址和端口来引用。

你可以通过<ruby> 监测 <rp>  （ </rp> <rt>  inspect </rt> <rp>  ） </rp></ruby>容器来找到它的 IP 地址。我将从之前创建的 `niksoper/netcore-books` 镜像中启动一个容器并<ruby> 监测 <rp>  （ </rp> <rt>  inspect </rt> <rp>  ） </rp></ruby>它：

```
docker run -d -p 5000:5000 --name books niksoper/netcore-books
docker inspect books

```

我们可以看到这个容器的 IP 地址是 `"IPAddress": "172.17.0.3"`。

所以现在如果我创建下面的 Nginx 配置文件，并使用这个文件启动一个 Nginx 容器， 它将代理请求到我的 API ：

```
server {
  listen 80;

  location / {
    proxy_pass http://172.17.0.3:5000;
  }
}

```

现在我可以使用这个配置文件启动一个 Nginx 容器（注意我把主机上的 8080 端口映射到了 Nginx 容器上的 80 端口）：

```
docker run -d -p 8080:80 \
-v ~/dev/nginx/my.nginx.conf:/etc/nginx/conf.d/default.conf \
nginx

```

一个到 `http://localhost:8080` 的请求将被代理到应用上。注意下面 `curl` 响应的 `Server` 响应头：

### DOCKER COMPOSE

在这个地方，我为自己的进步而感到高兴，但我认为一定还有更好的方法来配置 Nginx，可以不需要知道应用程序容器的确切 IP 地址。另一个当地的 Scott Logic DevOps 大师 Jason Ebbin 在这个地方进行了改进，并建议使用 [Docker Compose](https://docs.docker.com/compose/)。

概况描述一下，Docker Compose 使得一组通过声明式语法互相连接的容器很容易启动。我不想再细说 Docker Compose 是如何工作的，因为你可以在[之前的文章](http://blog.scottlogic.com/2016/01/25/playing-with-docker-compose-and-erlang.html)中找到。

我将通过一个我所使用的 `docker-compose.yml` 文件来启动：

```
version: '2'
services:
    books-service:
        container_name: books-api
        build: .

    reverse-proxy:
        container_name: reverse-proxy
        image: nginx
        ports:
         - "9090:8080"
        volumes:
         - ./proxy.conf:/etc/nginx/conf.d/default.conf

```

*这是版本 2 语法，所以为了能够正常工作，你至少需要 1.6 版本的 Docker Compose。*

这个文件告诉 Docker 创建两个服务：一个是给应用的，另一个是给 Nginx 反向代理服务器的。

### BOOKS-SERVICE

这个与 `docker-compose.yml` 相同目录下的 Dockerfile 构建的容器叫做 `books-api`。注意这个容器不需要发布任何端口，因为只要能够从反向代理服务器访问它就可以，而不需要从主机操作系统访问它。

### REVERSE-PROXY

这将基于 nginx 镜像启动一个叫做 `reverse-proxy` 的容器，并将位于当前目录下的 `proxy.conf` 文件挂载为配置。它把主机上的 9090 端口映射到容器中的 8080 端口，这将允许我们在 `http://localhost:9090` 上通过主机访问容器。

`proxy.conf` 文件看起来像下面这样：

```
server {
    listen 8080;

    location / {
      proxy_pass http://books-service:5000;
    }
}

```

这儿的关键点是我们现在可以通过名字引用 `books-service`，因此我们不需要知道 `books-api` 这个容器的 IP 地址！

现在我们可以通过一个运行着的反向代理启动两个容器（`-d` 意味着这是独立的，因此我们不能看到来自容器的输出）：

```
docker compose up -d

```

验证我们所创建的容器：

```
docker ps

```

最后来验证我们可以通过反向代理来控制该 API ：

```
curl -i http://localhost:9090/api/books

```

### 怎么做到的？

Docker Compose 通过创建一个新的叫做 `mvclibrary_default` 的虚拟网络来实现这件事，这个虚拟网络同时用于 `books-api` 和 `reverse-proxy` 容器（名字是基于 `docker-compose.yml` 文件的父目录）。

通过 `docker network ls` 来验证网络已经存在：

你可以使用 `docker network inspect mvclibrary_default` 来看到新的网络的细节：

注意 Docker 已经给网络分配了子网：`"Subnet": "172.18.0.0/16"`。`/16` 部分是无类域内路由选择（CIDR），完整的解释已经超出了本文的范围，但 CIDR 只是表示 IP 地址范围。运行 `docker network inspect bridge` 显示子网：`"Subnet": "172.17.0.0/16"`，因此这两个网络是不重叠的。

现在用 `docker inspect books-api` 来确认应用程序的容器正在使用该网络：

注意容器的两个别名（`"Aliases"`）是容器标识符（`3c42db680459`）和由 `docker-compose.yml` 给出的服务名（`books-service`）。我们通过 `books-service` 别名在自定义 Nginx 配置文件中来引用应用程序的容器。这本可以通过 `docker network create` 手动创建，但是我喜欢用 Docker Compose，因为它可以干净简洁地将容器创建和依存捆绑在一起。

### 结论

所以现在我可以通过几个简单的步骤在 Linux 系统上用 Nginx 运行应用程序，不需要对主机操作系统做任何长期的改变：

```
git clone https://github.com/niksoper/aspnet5-books.git
cd aspnet5-books/src/MvcLibrary
git checkout blog-docker
docker-compose up -d
curl -i http://localhost:9090/api/books

```

我知道我在这篇文章中所写的内容不是一个真正的生产环境就绪的设备，因为我没有写任何有关下面这些的内容，绝大多数下面的这些主题都需要用单独一篇完整的文章来叙述。

* 安全考虑比如防火墙和 SSL 配置
* 如何确保应用程序保持运行状态
* 如何选择需要包含的 Docker 镜像（我把所有的都放入了 Dockerfile 中）
* 数据库 - 如何在容器中管理它们

对我来说这是一个非常有趣的学习经历，因为有一段时间我对探索 ASP.NET Core 的跨平台支持非常好奇，使用 “Configuratin as Code” 的 Docker Compose 方法来探索一下 DevOps 的世界也是非常愉快并且很有教育意义的。

如果你对 Docker 很好奇，那么我鼓励你来尝试学习它 或许这会让你离开舒适区，不过，有可能你会喜欢它？

---

via: <http://blog.scottlogic.com/2016/09/05/hosting-netcore-on-linux-with-docker.html>

作者：[Nick Soper](http://blog.scottlogic.com/nsoper) 译者：[ucasFL](https://github.com/ucasFL) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


为什么 Chrome 又不支持我的 HTTP/2 网站了？

"插件好像好久没亮了"，那个插件叫什么名字？<br />
从头到翻到尾就没看到那个插件是什么…… (´・ω・`)

Debian jessie backports 已经提供 openssl 1.0.2 了。

第一句。。。

额……看到了，原来是，哈哈……

请教一下，想学linux，能不能推荐本书籍，或其他学习方式，谢谢

我也装上那个扩展试试。

鸟哥的linux私房菜基础篇,

# 为什么 Chrome 又不支持我的 HTTP/2 网站了？

昨晚偶尔清理 Chrome 插件时发现我的 “[HTTP/2 and SPDY indicator](https://chrome.google.com/webstore/detail/http2-and-spdy-indicator/mpbpobfflnpcgagjijhmgnchggcjblin)”插件好像好久没亮了。这个插件在你访问到一个支持 HTTP/2 （或之前的 SPDY 协议）的网站时会点亮，而我明明记得之前专门让 <https://linux.cn/> 支持了 HTTP/2 。

我的第一反应是不是这个插件有问题了？于是打开 Chrome 调试工具，然后发现，真的是请求和响应都是 HTTP/1.1 哎！

经过一番研究，原来是从 Chrome 51 开始，在 2016 年 5 月 31 日之前，对支持 NPN 协商协议的 HTTP/2 网站还会采用 HTTP/2 访问；而之后就只支持 ALPN 协商协议的 HTTP/2 网站了——而目前 ALPN 协议仅被鲜少有发行版支持 openssl-1.0.2 支持。

### 发生了什么？

#### 服务器端

我们知道，最初的 Web 访问协议是 HTTP/1，包括以前的 HTTP/1.0 和现在大部分网站采用的 HTTP/1.1（HTTP/0.9 是试验性协议，已经废弃）。但是随着 Web 应用越来越复杂，之前的 HTTP/1.x 协议就看起来不能满足日益庞杂的 Web 服务需求了。比如说，明文请求、请求复用等问题。因此，谷歌就开发了一个新的传输层协议，名为 SPDY。由于这个新的协议用了的人都说好，因此谷歌就把这个协议提交到了 IETF，然后大家觉得，SPDY 这名字不好听（SPDY 是谷歌的注册商标），就干脆叫 [HTTP/2](https://tools.ietf.org/html/rfc7540) 吧！

SPDY 协议是基于 SSL/TLS 的，谷歌开发了一个名为<ruby>   下一代协议协商  <rp>  （ </rp> <rt>  Next Protocol Negotiation </rt> <rp>  ） </rp></ruby>（NPN）的 SSL/TLS 扩展，用于在客户端连接服务器时协商是否采用 HTTP/2 协议。SPDY 协议是由 Web 服务器所实现支持的，而 NPN 则是由 OpenSSL 等 SSL 实现支持的。

但是，随着 SPDY 被提交到 IETF，然后变成了 HTTP/2 协议，谷歌也放弃了 SPDY 的开发，全力投入到了 HTTP/2 的开发中，之前所采用 NPN 也被一种新的协商协议 ALPN ——<ruby>   应用层协议协商  <rp>  （ </rp> <rt>  Application-Layer Protocol Negotiation </rt> <rp>  ） </rp></ruby>所替代。NPN 和 ALPN 是不兼容的，它们的主要不同是：

* NPN 是服务器发送所支持的协议列表，由客户端进行选择。而 ALPN 则是客户端发送该列表，由服务端选择。
* 在 NPN 中，最终的选择结果是在 Change Cipher Spec 之后发送给服务端的，也就是说是被加密了的。而在 ALPN 中，所有的协商都是明文的。

这样做的好处主要是安全性方面的考虑，但是这造成了一个问题就是，NPN 已经广泛地被 OpenSSL 支持，而 ALPN 则目前只有最新的 openssl-1.0.2 才支持。当前的几个主流 Linux 发行版的 OpenSSL 版本以及支持的协商协议如下：

| Linux 发行版 | OpenSSL 版本 | 所支持的协商协议 |
| --- | --- | --- |
| CentOS/Oracle Linux/RHEL 5.10+ | 0.9.8e | 不支持 |
| CentOS/Oracle Linux/RHEL 6.5+, 7.0+ | 1.0.1e | NPN |
| Ubuntu 12.04 LTS | 1.0.1 | NPN |
| Ubuntu 14.04 LTS | 1.0.1f | NPN |
| *Ubuntu 16.04 LTS* | *1.0.2g* | *ALPN 和 NPN* |
| Debian 7.0 | 1.0.1e | NPN |
| Debian 8.0 | 1.0.1k | NPN |

从上面我们可以看到，基本上所有的服务器级的 Linux 发行版都不支持 OpenSSL 及 ALPN，唯一支持的 Ubuntu 16.04 LTS 显然用的不会很多。不要小看这 0.0.1 的版本差异，对于别的软件来说这 0.0.1 的差异基本上可以忽略，但是对于 OpenSSL 来说，那就是两个版本代际。OpenSSL 是个相当底层的库，很多重要的软件都依赖于它，因此各个发行版在升级 OpenSSL 时采用的态度是相当保守，比如我们可以看看 CentOS 系统中有哪些软件使用了 OpenSSL：

```
$ lsof | grep libssl | awk '{print $1}' | sort | uniq
anvil
fail2ban
gdbus
gmain
httpd
postfix
mysqld
NetworkManager
nginx
php-fpm
puppet
sshd
sudo
tuned
zabbix_agent
```

没有经过足够的测试，Linux 发行版是不会在产品级（服务器级）的环境中随便升级的。为了解决旧版本（1.0.1）中的安全问题，他们宁可将新的版本（1.0.2）中安全修复移植回旧版本，也不会升级到有新功能的新版本（1.0.2），这就是你见到了各种 1.0.1e、1.0.1k 这样的版本号的原因。

当然，你可以自己编译一个最新 OpenSSL 替代你系统中的 openssl-1.0.1，但是我想你不会这样做的，是吧？

顺便提一句，NPN 和 ALPN 可以并存，但是会客户端会优先选择 ALPN。

#### 浏览器端（Chrome）

从 Chrome 51 开始，谷歌就去掉了对 SPDY 的支持，不过这不是个事，因为不但使用 SPDY 的 Web 服务器比较少，而且从 SPDY 升级到 HTTP/2 也很简单，这方面 Nginx、Apache 等服务器的配置都很简单。

但不幸的是，在 Chrome 51 中，谷歌也去掉了对 NPN 的支持！如果你的 Web 服务器使用的是 openssl-1.0.2 以下的版本，不支持 ALPN 协商，那么 Chrome 51 及以后版本就会以 HTTP/1 协议访问你的网站。

谷歌对放弃 NPN 支持做了一个[简短的解释](https://bugs.chromium.org/p/chromium/issues/detail?id=527066)，但是不管怎么说，NPN 协议在 Chrome 51 之后的版本不会再次回来了。而另一方面，OpenSSL 在 2016 年 12 月 31 日之后也不会继续发布 openssl-1.0.1 系列的新版本了，安全修复到此为止。

而在这种情况下，你原本支持 HTTP/2 的网站通过连接复用等 HTTP/2 所提供的新特性，在 Chrome 下访问取得了不错的体验，而现在又跌回了之前的残旧状态。

### 怎么办呢？

有几种办法：

#### 换浏览器

山不来就我，我去就山。Chrome 51+ 不支持带 NPN 的 HTTP/2 网站，作为浏览者，你可以使用其它的浏览器，比如 Safari、Edge 之类的。这样，你就可以用新的协议来访问世界上那 [10% 支持 HTTP/2 的 Web 服务器](https://w3techs.com/technologies/details/ce-http2/all/all)了。

但是，作为服务器运营者，你却不能忽视[高达 50% 以上的 Chrome 用户](https://www.netmarketshare.com/browser-market-share.aspx?qprid=0&qpcustomd=0)。

#### 换服务器

如上面所示，Ubuntu 16.04 LTS 是目前唯一官方支持 openssl-1.0.2 的 Linux 发行版，如果你一直采用 Ubuntu 做服务器，考虑一下升级吧。LTS 版本的支持期长达五年。

当然，在产品环境中，即便你是 Ubuntu 服务器，更新版本也是一件重大事宜，宜慎思之。

#### 重新编译

既然换服务器不是一个好的选择，那你还有一个方案，就是使用新的 openssl-1.0.2 源代码重新编译你的 Web 服务器，比如 nginx。

下面我简单介绍一下如何用 openssl-1.0.2 来编译 nginx。（1.0.2 系列的最新版本是 1.0.2j，当然你要非用 1.1.0，我也无话可说……）

首先下载并解压 openssl-1.0.2j：

```
# wget https://www.openssl.org/source/openssl-1.0.2j.tar.gz
# tar -zxvf openssl-1.0.2j.tar.gz
```

然后在编译 nginx 的时候使用 `--with-openssl=../openssl-1.0.2j` 选项以及你的其它选项：

```
./configure --with-openssl=../openssl-1.0.2j --with-http_v2_module --with-http_ssl_module
```

配置并编译之后，你可以用 `nginx -V`来看一下你的 nginx 中的 OpenSSL 版本。

这种自行编译的好处是灵活性高，但是你需要随时注意各个组件是否有严重的安全漏洞，并在出了修复版本之后重新编译。

#### 容器

除了自己编译之外，如果你的系统环境中已经有了容器支持，你还可以在容器中运行一个 Ubuntu 16.04 LTS，并将 Web 服务器运行在其中。

### 总结

以上就是 HTTP/2 和 Chrome 之间的故事，你准备去升级 HTTP/2 支持了吗？要知道相比 HTTP/2 的访问体验，你肯定不会想再回到 HTTP/1 了。


# 怎样在 RHEL、CentOS 和 Fedora 上安装 Git 及设置 Git 账号

对于新手来说，Git 是一个自由、开源、高效的分布式版本控制系统（VCS），它是基于速度、高性能以及数据一致性而设计的，以支持从小规模到大体量的软件开发项目。

Git 是一个可以让你追踪软件改动、版本回滚以及创建另外一个版本的目录和文件的软件仓库。

Git 主要是用 C 语言来写的，混杂了少量的 Perl 脚本和各种 shell 脚本。它主要在 Linux 内核上运行，并且有以下列举的卓越的性能：

* 易于上手
* 运行速度飞快，且大部分操作在本地进行，因此，它极大的提升了那些需要与远程服务器通信的集中式系统的速度。
* 高效
* 提供数据一致性检查
* 支持低开销的本地分支
* 提供非常便利的暂存区
* 可以集成其它工具来支持多种工作流

在这篇操作指南中，我们将介绍在 CentOS/RHEL 7/6 和 Fedora 20-24 Linux 发行版上安装 Git 的必要步骤以及怎么配置 Git，以便于你可以快速开始工作。

### 使用 Yum 安装 Git

我们将从系统默认的仓库安装 Git，并通过运行以下 [YUM 包管理器](/article-2272-1.html) 的更新命令来确保你系统的软件包都是最新的：

```
# yum update

```

接着，通过以下命令来安装 Git：

```
# yum install git

```

在 Git 成功安装之后，你可以通过以下命令来显示 Git 安装的版本：

```
# git --version

```

*检查 Git 安装的版本*

注意：从系统默认仓库安装的 Git 会是比较旧的版本。如果你想拥有最新版的 Git，请考虑使用以下说明来编译源代码进行安装。

### 从源代码安装 Git

开始之前，你首先需要从系统默认仓库安装所需的软件依赖包，以及从源代码构建二进制文件所需的实用工具：

```
# yum groupinstall "Development Tools"
# yum install gettext-devel openssl-devel perl-CPAN perl-devel zlib-devel
```

安装所需的软件依赖包之后，转到官方的 [Git 发布页面](https://github.com/git/git/releases)，抓取最新版的 Git 并使用下列命令编译它的源代码：

```
# wget https://github.com/git/git/archive/v2.10.1.tar.gz -O git.tar.gz
# tar -zxf git.tar.gz
# cd git-2.10.1/
# make configure
# ./configure --prefix=/usr/local
# make install
# git --version
```

*检查 Git 的安装版本*

**推荐阅读:** [Linux 下 11 个最好用的 Git 客户端和 Git 仓库查看器](http://www.tecmint.com/best-gui-git-clients-git-repository-viewers-for-linux/)。

### 在 Linux 设置 Git 账户

在这个环节中，我们将介绍如何使用正确的用户信息（如：姓名、邮件地址）和 `git config` 命令来设置 Git 账户，以避免出现提交错误。

注意：确保将下面的 `username` 替换为在你的系统上创建和使用的 Git 用户的真实名称。

你可以使用下面的 [useradd 命令](http://www.tecmint.com/add-users-in-linux/) 创建一个 Git 用户，其中 `-m` 选项用于在 `/home` 目录下创建用户主目录，`-s` 选项用于指定用户默认的 shell。

```
# useradd -m -s /bin/bash username
# passwd username

```

现在，将新用户添加到 `wheel` 用户组以启用其使用 `sudo` 命令的权限：

```
# usermod username -aG wheel

```

*创建 Git 用户账号*

然后通过以下命令使用新用户配置 Git：

```
# su username
$ sudo git config --global user.name "Your Name"
$ sudo git config --global user.email "you@example.com"
```

现在通过下面的命令校验 Git 的配置。

```
$ sudo git config --list
```

如果配置没有错误的话，你应该能够看到类似以下详细信息的输出：

```
user.name=username
user.email= username@some-domian.com
```

*在 Linux 设置 Git 用户*

### 总结

在这个简单的教程中，我们已经了解怎么在你的 Linux 系统上安装 Git 以及配置它。我相信你应该可以驾轻就熟。

---

via: <http://www.tecmint.com/install-git-centos-fedora-redhat/>

作者：[Aaron Kili](http://www.tecmint.com/author/aaronkili/) 译者：[OneNewLife](https://github.com/OneNewLife) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


使用命令行生成高强度密码

问题是我记不住哇！

所以有一种东西叫password manager，如：开源的KeePassX

这个 user-agent 是自己改得吧？Chromium 的版本号有那么高？

简单，抄下来，贴显示器旁。只要你的用户名不被别人知道就行。

可以很实用

openssl rand -base64 14<br />
这个不错，可以推荐使用vivaldi笔记功能，这样常用的密码可以记在旁边，如果你怕别人看到，可以使用密码管理器保存起来

一直在用第一个

# 使用命令行生成高强度密码

<img src="content_image/album_201611_07_121112icf4y744jucyvdvp.jpg" alt="" title="">

设置一个高强度的密码是非常重要的，这样才能够很好的保护自己的账号或者服务器以及确保自己的数据的安全。通常来说，一个高强度密码至少有 14 个字符，包括大小写字母、数字和特殊字符，并且要牢记永远不用那些字典中的单词。使用长密码比短密码要来的安全，因为密码越长越难猜测。在本文中，我将给你介绍几个不同方法，让你可以在 Linux 命令行下生成一个高强度密码。

### 使用 openssl 生成高强度密码

这里使用 openssl 的 rand 方法，它会生成一个 14 位字符的随机字符：

```
openssl rand -base64 14

```

### 使用 urandom 生成高强度密码

这里我们将使用 `tr` 条件来过滤 `/dev/urandom` 的输出，从而删掉那些不想要的字符，并打印出第一个出现的 14 位字符。

```
< /dev/urandom tr -dc A-Za-z0-9 | head -c14; echo

```

### 使用 pwgen 生成高强度密码

`pwgen` 是一个生成随机、无特殊含义但可以正常拼读的密码。

安装 `pwgen`，运行：

```
sudo apt-get install pwgen

```

安装好之后，使用以下命令来生成一个 14 位随机字符：

```
pwgen 14 1

```

你也可以使用以下标记：

* -c 或 --capitalize 生成的密码中至少包含一个大写字母
* -A 或 --no-capitalize 生成的密码中不含大写字母
* -n 或 --numerals 生成的密码中至少包含一个数字
* -0 或 --no-numerals 生成的密码中不含数字
* -y 或 --symbols 生成的密码中至少包含一个特殊字符
* -s 或 --secure 生成一个完全随机的密码
* -B 或 --ambiguous 生成的密码中不含<ruby> 易混淆字符 <rp>  （ </rp> <rt>  ambiguous characters </rt> <rp>  ） </rp></ruby>
* -h 或 --help 输出帮助信息
* -H 或 --sha1=path/to/file[#seed] 使用指定文件的 sha1 哈希值作为随机生成器
* -C 按列输出生成的密码
* -1 不按列输出生成的密码
* -v 或 --no-vowels 不使用任何元音，以免意外生成让人讨厌的单词

### 使用 gpg 生成高强度密码

我们也可以使用 `gpg` 工具来生成一个 14 位字符的密码：

```
gpg --gen-random --armor 1 14

```

### 其它方法

当然，可能还有很多方法可以生成一个高强度密码。比方说，你可以添加以下 bash shell 方法到 `~/.bashrc` 文件：

```
genpasswd() {
    strings /dev/urandom | grep -o '[[:alnum:]]' | head -n 14 | tr -d '\n'; echo
}

```

当你想要生成一个高强度的随机密码时，运行 `genpasswd` 就好了。

---

via: <https://www.rosehosting.com/blog/generate-password-linux-command-line/>

作者：[RoseHosting](www.rosehosting.com) 译者：[GHLandy](https://github.com/GHLandy) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


怎样在 CentOS 里下载 RPM 包及其所有依赖包

很不错的技巧

本地化最靠谱。所以自从开始使用Debian Gnu/Linux之后就没改过了。这个就是比其他版本好一点点！！！

本地化是什么意思？

Debian不是有光盤鏡像下載嗎？可以做成本地源～

很奇怪suse与redhat商业化源下载后给别人建立本地源怎么办？

厉害了！

最简单的方法是yum install PACKAGE, 系统给出提示的时候选择d，只下载不安装即可。

来自上海的 Internet Explorer 11.0|Windows 7 用户

这个只是下载了依赖的rpm，但是rpm所以来的lib包，如glibc等等，就下载不下来了……比较无语

# 怎样在 CentOS 里下载 RPM 包及其所有依赖包

前几天我尝试去创建一个仅包含我们经常在 CentOS 7 下使用的软件的本地仓库。当然，我们可以使用 curl 或者 wget 下载任何软件包，然而这些命令并不能下载要求的依赖软件包。你必须去花一些时间而且手动的去寻找和下载被安装的软件所依赖的软件包。然而，我们并不是必须这样。在这个简短的教程中，我将会带领你以两种方式下载软件包及其所有依赖包。我已经在 CentOS 7 下进行了测试，不过这些相同的步骤或许在其他基于 RPM 管理系统的发行版上也可以工作，例如 RHEL，Fedora 和 Scientific Linux。

### 方法 1 利用 “Downloadonly” 插件下载 RPM 软件包及其所有依赖包

我们可以通过 yum 命令的 “Downloadonly” 插件下载 RPM 软件包及其所有依赖包。

为了安装 Downloadonly 插件，以 root 身份运行以下命令。

```
yum install yum-plugin-downloadonly

```

现在，运行以下命令去下载一个 RPM 软件包。

```
yum install --downloadonly <package-name>

```

默认情况下，这个命令将会下载并把软件包保存到 `/var/cache/yum/` 的 `rhel-{arch}-channel/packageslocation` 目录，不过，你也可以下载和保存软件包到任何位置，你可以通过 `–downloaddir` 选项来指定。

```
yum install --downloadonly --downloaddir=<directory> <package-name>

```

例子:

```
yum install --downloadonly --downloaddir=/root/mypackages/ httpd

```

终端输出:

```
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: centos.excellmedia.net
 * epel: epel.mirror.angkasa.id
 * extras: centos.excellmedia.net
 * updates: centos.excellmedia.net
Resolving Dependencies
--> Running transaction check
---> Package httpd.x86_64 0:2.4.6-40.el7.centos.4 will be installed
--> Processing Dependency: httpd-tools = 2.4.6-40.el7.centos.4 for package: httpd-2.4.6-40.el7.centos.4.x86_64
--> Processing Dependency: /etc/mime.types for package: httpd-2.4.6-40.el7.centos.4.x86_64
--> Processing Dependency: libaprutil-1.so.0()(64bit) for package: httpd-2.4.6-40.el7.centos.4.x86_64
--> Processing Dependency: libapr-1.so.0()(64bit) for package: httpd-2.4.6-40.el7.centos.4.x86_64
--> Running transaction check
---> Package apr.x86_64 0:1.4.8-3.el7 will be installed
---> Package apr-util.x86_64 0:1.5.2-6.el7 will be installed
---> Package httpd-tools.x86_64 0:2.4.6-40.el7.centos.4 will be installed
---> Package mailcap.noarch 0:2.1.41-2.el7 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

=======================================================================================================================================
 Package Arch Version Repository Size
=======================================================================================================================================
Installing:
 httpd x86_64 2.4.6-40.el7.centos.4 updates 2.7 M
Installing for dependencies:
 apr x86_64 1.4.8-3.el7 base 103 k
 apr-util x86_64 1.5.2-6.el7 base 92 k
 httpd-tools x86_64 2.4.6-40.el7.centos.4 updates 83 k
 mailcap noarch 2.1.41-2.el7 base 31 k

Transaction Summary
=======================================================================================================================================
Install 1 Package (+4 Dependent packages)

Total download size: 3.0 M
Installed size: 10 M
Background downloading packages, then exiting:
(1/5): apr-1.4.8-3.el7.x86_64.rpm | 103 kB 00:00:01
(2/5): apr-util-1.5.2-6.el7.x86_64.rpm | 92 kB 00:00:01
(3/5): mailcap-2.1.41-2.el7.noarch.rpm | 31 kB 00:00:01
(4/5): httpd-tools-2.4.6-40.el7.centos.4.x86_64.rpm | 83 kB 00:00:01
(5/5): httpd-2.4.6-40.el7.centos.4.x86_64.rpm | 2.7 MB 00:00:09
---------------------------------------------------------------------------------------------------------------------------------------
Total 331 kB/s | 3.0 MB 00:00:09
exiting because "Download Only" specified

```

现在去你指定的目录位置下，你将会看到那里有下载好的软件包和依赖的软件。在我这种情况下，我已经把软件包下载到 `/root/mypackages/` 目录下。

让我们来查看一下内容。

```
ls /root/mypackages/

```

样本输出：

```
apr-1.4.8-3.el7.x86_64.rpm
apr-util-1.5.2-6.el7.x86_64.rpm
httpd-2.4.6-40.el7.centos.4.x86_64.rpm
httpd-tools-2.4.6-40.el7.centos.4.x86_64.rpm
mailcap-2.1.41-2.el7.noarch.rpm

```

正如你在上面输出所看到的, httpd软件包已经被依据所有依赖性下载完成了 。

请注意，这个插件适用于 `yum install/yum update`， 但是并不适用于 `yum groupinstall`。默认情况下，这个插件将会下载仓库中最新可用的软件包。然而你可以通过指定版本号来下载某个特定的软件版本。

例子:

```
yum install --downloadonly --downloaddir=/root/mypackages/ httpd-2.2.6-40.el7

```

此外，你也可以如下一次性下载多个包：

```
yum install --downloadonly --downloaddir=/root/mypackages/ httpd vsftpd

```

### 方法 2 使用 “Yumdownloader” 工具来下载 RPM 软件包及其所有依赖包

“Yumdownloader” 是一款简单，但是却十分有用的命令行工具，它可以一次性下载任何 RPM 软件包及其所有依赖包。

以 root 身份运行如下命令安装 “Yumdownloader” 工具。

```
yum install yum-utils

```

一旦安装完成，运行如下命令去下载一个软件包，例如 httpd。

```
yumdownloader httpd

```

为了根据所有依赖性下载软件包，我们使用 `--resolve` 参数：

```
yumdownloader --resolve httpd

```

默认情况下，Yumdownloader 将会下载软件包到当前工作目录下。

为了将软件下载到一个特定的目录下，我们使用 `--destdir` 参数：

```
yumdownloader --resolve --destdir=/root/mypackages/ httpd

```

或者，

```
yumdownloader --resolve --destdir /root/mypackages/ httpd

```

终端输出：

```
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: centos.excellmedia.net
 * epel: epel.mirror.angkasa.id
 * extras: centos.excellmedia.net
 * updates: centos.excellmedia.net
--> Running transaction check
---> Package httpd.x86_64 0:2.4.6-40.el7.centos.4 will be installed
--> Processing Dependency: httpd-tools = 2.4.6-40.el7.centos.4 for package: httpd-2.4.6-40.el7.centos.4.x86_64
--> Processing Dependency: /etc/mime.types for package: httpd-2.4.6-40.el7.centos.4.x86_64
--> Processing Dependency: libaprutil-1.so.0()(64bit) for package: httpd-2.4.6-40.el7.centos.4.x86_64
--> Processing Dependency: libapr-1.so.0()(64bit) for package: httpd-2.4.6-40.el7.centos.4.x86_64
--> Running transaction check
---> Package apr.x86_64 0:1.4.8-3.el7 will be installed
---> Package apr-util.x86_64 0:1.5.2-6.el7 will be installed
---> Package httpd-tools.x86_64 0:2.4.6-40.el7.centos.4 will be installed
---> Package mailcap.noarch 0:2.1.41-2.el7 will be installed
--> Finished Dependency Resolution
(1/5): apr-util-1.5.2-6.el7.x86_64.rpm | 92 kB 00:00:01
(2/5): mailcap-2.1.41-2.el7.noarch.rpm | 31 kB 00:00:02
(3/5): apr-1.4.8-3.el7.x86_64.rpm | 103 kB 00:00:02
(4/5): httpd-tools-2.4.6-40.el7.centos.4.x86_64.rpm | 83 kB 00:00:03
(5/5): httpd-2.4.6-40.el7.centos.4.x86_64.rpm | 2.7 MB 00:00:19

```

让我们确认一下软件包是否被下载到我们指定的目录下。

```
ls /root/mypackages/

```

终端输出：

```
apr-1.4.8-3.el7.x86_64.rpm
apr-util-1.5.2-6.el7.x86_64.rpm
httpd-2.4.6-40.el7.centos.4.x86_64.rpm
httpd-tools-2.4.6-40.el7.centos.4.x86_64.rpm
mailcap-2.1.41-2.el7.noarch.rpm

```

不像 Downloadonly 插件，Yumdownload 可以下载一组相关的软件包。

```
yumdownloader "@Development Tools" --resolve --destdir /root/mypackages/

```

在我看来，我喜欢 Yumdownloader 更胜于 Yum 的 Downloadonly 插件。但是，两者都是十分简单易懂而且可以完成相同的工作。

这就是今天所有的内容，如果你觉得这份引导教程有用，清在你的社交媒体上面分享一下去让更多的人知道。

干杯！

---

via: <https://www.ostechnix.com/download-rpm-package-dependencies-centos/>

作者：[SK](http://ostechnix.tradepub.com/free/w_make272/prgm.cgi?a=1) 译者：[LinuxBars](https://github.com/LinuxBars) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


Wire：一个极酷、专注于个人隐私的开源聊天应用程序已经来到了 Linux 上

Summary: 使 Wire 对于我们这些 FOSS（自由/开源软件）爱好者来说更加重要的是，几个月前 Wire 开源了。几个月下来我们见到了一个用于 Linux 的 beta 版本 Wire 桌面应用程序。

不错哟，telegram被墙很不爽，试试这个。

有伟大的墙，这东西只能看起耍

来自陕西西安的 Chrome Mobile 54.0|Android 6.0 用户

再好的IM, 没有使用者也没用, 天朝人民都被 深圳的企鹅绑架着. 我已不想挣扎了.

注重安全的东东不被qiang才怪～～～

悲伤的故事

这界面好像一般啊<br />
Electron的性能如何？

来自广东广州广东工贸职业技术学院的 Firefox 49.0|GNU/Linux 用户

试了一下，还没中文和表情包，要下载net4.5之类的东西

来自香港的 Internet Explorer 10.0|Windows 8 用户

支持开源软件

优秀的聊天软件的最悲剧就是，抵不过企鹅的用户基数，常联系的人，需要联系的人都不用，那是一点办法都没有

telegram被墙，其客户端开源，保障隐私，关键是服务端不清楚，也不安全，这个也如此

聊天软件没联系人其他方面做的再好有个卵用

支持。不喜欢qq的商业氛围。

有意思

# Wire：一个极酷、专注于个人隐私的开源聊天应用程序已经来到了 Linux 上

回到大约两年前，一些曾开发 [Skype](https://www.skype.com/en/) 的开发人员发行了一个漂亮的新聊天应用个程序：[Wire](https://wire.com/)。当我说它漂亮的时候，只是谈论它的“外貌”。Wire 具有一个许多其他聊天应用程序所没有的整洁优美的“外貌”，但这并不是它最大的卖点。

从一开始，Wire 就推销自己是[世界上最注重隐私的聊天应用程序](http://www.ibtimes.co.uk/wire-worlds-most-private-messaging-app-offers-total-encryption-calls-texts-1548964)。无论是文本、语音电话，还是图表、图像等基本的内容，它都提供端到端的加密。

WhatsApp 也提供‘端到端加密’，但是考虑一下它的所有者 [Facebook 为了吸引用户而把 WhatsApp 的数据分享出去](https://techcrunch.com/2016/08/25/whatsapp-to-share-user-data-with-facebook-for-ad-targeting-heres-how-to-opt-out/)。我不太相信 WhatsApp 以及它的加密手段。

使 Wire 对于我们这些 FOSS（自由/开源软件）爱好者来说更加重要的是，几个月前 [Wire 开源了](http://www.infoworld.com/article/3099194/security/wire-open-sources-messaging-client-woos-developers.html)。几个月下来我们见到了一个用于 Linux 的 beta 版本 Wire 桌面应用程序。

除了一个包装器以外，桌面版的 Wire 并没有比 web 版多任何东西。感谢 [Electron 开源项目](http://electron.atom.io/)提供了一种开发跨平台桌面应用程序的简单方式。许多其他应用程序也通过使用 Electron 为 Linux 带去了一个本地桌面应用程序，包括 [Skype](https://itsfoss.com/skpe-alpha-linux/)。

### WIRE 的特性：

在我们了解有关 Linux 版 Wire 应用程序的更多信息之前，让我们先快速看一下它的一些主要特性。

* 开源应用程序
* 针对所有类型内容的全加密
* 无广告，无数据收集，无数据分享
* 支持文本，语音以及视频聊天
* 支持群聊和群电话
* [音频过滤器](https://medium.com/colorful-conversations/the-tune-for-this-summer-audio-filters-eca8cb0b4c57#.c8gvs143k)（不需要吸入氦元素，只需要使用过滤器就可以用有趣的声音说话）
* 不需要电话号码，可以使用邮箱登录
* 优美、现代化的界面
* 跨平台聊天应用程序，iOS，Android，Web，Mac，Windows 和 Linux 客户机均有相应版本
* 欧洲法保护（欧洲法比美国法更注重隐私）

Wire 有一些更棒的特性，尤其是和[Snapchat](https://www.snapchat.com/)类似的音频过滤器。

### 在 Linux 上安装 WIRE

在安装 Wire 到 Linux 上之前，让我先警告你它目前还处于 beta 阶段。所以，如果你遇到一些故障，请不要生气。

Wire 有一个 64 位系统可使用的 .deb 客户端。如果你有一台 [32 位或者 64 位系统](https://itsfoss.com/32-bit-64-bit-ubuntu/)的电脑，你可以使用这些技巧来找到它。你可以从下面的链接下载 .deb 文件。

* [下载 Linux 版 Wire [Beta]](https://wire.com/download/)

如果感兴趣的话，你也可以看一看它的源代码：

* [桌面版 Wire 源代码](https://github.com/wireapp/wire-desktop)

这是 Wire 的默认界面，看起来像 [elementary OS Loki](https://itsfoss.com/tag/elementary-os-loki/):

你看，他们甚至还弄了一个机器人：）

你已经开始使用 Wire 了吗？如果是，你的体验是什么样的？如果没有，你将尝试一下吗？因为它现在是[开源的](https://itsfoss.com/tag/open-source)并且可以在 Linux 上使用。

---

via: <https://itsfoss.com/wire-messaging-linux/>

作者：[Abhishek Prakash](https://itsfoss.com/author/abhishek/)  译者：[ucasFL](https://github.com/ucasFL) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


通过 docker-compose 进行快速原型设计

一个`docker-compose up -d`被错打成`docker compose up -d`

谢谢！

# 通过 docker-compose 进行快速原型设计

在这篇文章中，我们将考察一个 Node.js 开发原型，该原型用于从英国三个主要折扣网店查找“Raspberry PI Zero”的库存。

我写好了代码，然后经过一晚的鼓捣把它部署在 Aure 上的 Ubuntu 虚拟机上。Docker 和 docker-compose 工具使得部署和更新过程非常快。

### 还记得链接指令（link）吗？

如果你已经阅读过 [Hands-on Docker tutorial](http://blog.alexellis.io/handsondocker)，那么你应该已经可以使用命令行链接 Docker 容器。通过命令行将 Node.js 的计数器链接到 Redis 服务器，其命令可能如下所示：

```
$ docker run -d -P --name redis1
$ docker run -d hit_counter -p 3000:3000 --link redis1:redis

```

现在假设你的应用程序分为三层：

* Web 前端
* 处理长时间运行任务的批处理层
* Redis 或者 mongo 数据库

通过`--link`的显式链接只是管理几个容器是可以的，但是可能会因为我们向应用程序添加更多层或容器而失控。

### 加入 docker-compose

*Docker Compose logo*

docker-compose 工具是标准 Docker 工具箱的一部分，也可以单独下载。 它提供了一组丰富的功能，通过纯文本 YAML 文件配置所有应用程序的部件。

上面的例子看起来像这样：

```
version: "2.0"
services:
  redis1:
    image: redis
  hit_counter:
    build: ./hit_counter
    ports:
     - 3000:3000

```

从 Docker 1.10 开始，我们可以利用网络覆盖（network overlays）来帮助我们在多个主机上进行扩展。 在此之前，链接仅能工作在单个主机上。 `docker-compose scale` 命令可以用来在需要时带来更多的计算能力。

>
> 查看 docker.com 上的 [docker-compose](https://docs.docker.com/compose/compose-file/) 参考
>
>
>

### 　真实工作示例：Raspberry PI 库存警示

*新的 Raspberry PI Zero v1.3 图片，由 Pimoroni 提供*

Raspberry PI Zero 嗡嗡作响 - 它是一个极小的微型计算机，具有 1GHz CPU 和 512MB RAM，可以运行完整的Linux、Docker、Node.js、Ruby 和其他许多流行的开源工具。 PI Zero 最好的优点之一就是它成本只有 5 美元。 这也意味着它销售的速度非常之快。

*如果你想在 PI 上尝试 Docker 和 Swarm，请查看下面的教程：[Docker Swarm on the PI Zero](http://blog.alexellis.io/dockerswarm-pizero/)*

### 原始网站：whereismypizero.com

我发现一个网页，它使用屏幕抓取以找出 4-5 个最受欢迎的折扣网店是否有库存。

* 网站包含静态 HTML 网页
* 向每个折扣网店发出一个 XMLHttpRequest 访问 /public/api/
* 服务器向每个网店发出 HTTP 请求并执行抓屏

每一次对 /public/api/ 的调用，其执行花 3 秒钟，而使用 Apache Bench（ab），我每秒只能完成 0.25 个请求。

### 重新发明轮子

零售商似乎并不介意 whereismypizero.com 抓取他们的网站的商品库存信息，所以我开始从头写一个类似的工具。 我尝试通过缓存和解耦 web 层来处理更多的抓取请求。 Redis 是执行这项工作的完美工具。 它允许我设置一个自动过期的键/值对（即一个简单的缓存），还可以通过 pub/sub 在 Node.js 进程之间传输消息。

>
> 复刻或者追踪放在 github 上的代码: [alexellis/pi*zero*stock](https://github.com/alexellis/pi_zero_stock)
>
>
>

如果之前使用过 Node.js，你肯定知道它是单线程的，并且任何 CPU 密集型任务，如解析 HTML 或 JSON 都可能导致速度放缓。一种缓解这种情况的方法是使用一个工作进程和 Redis 消息通道作为它和 web 层之间的连接组织。

* Web 层
	+ 使用 200 代表缓冲命中（该商店的 Redis 键存在）
	+ 使用 202 代表高速缓存未命中（该商店的 Redis 键不存在，因此发出消息）
	+ 因为我们只是读一个 Redis 键，响应时间非常快。
* 库存抓取器
	+ 执行 HTTP 请求
	+ 用于在不同类型的网店上抓屏
	+ 更新 Redis 键的缓存失效时间为 60 秒
	+ 另外，锁定一个 Redis 键，以防止对网店过多的 HTTP 请求。

```
version: "2.0"
services:
  web:
    build: ./web/
    ports:
     - "3000:3000"
  stock_fetch:
    build: ./stock_fetch/
  redis:
    image: redis

```

*来自示例的 docker-compose.yml 文件*

一旦本地正常工作，再向 Azure 的 Ubuntu 16.04 镜像云部署就轻车熟路，只花了不到 5 分钟。 我登录、克隆仓库并键入`docker-compose up -d`， 这就是所有的工作 - 快速实现整个系统的原型不会比这几个步骤更多。 任何人（包括 whereismypizero.com 的所有者）只需两行命令就可以部署新解决方案：

```
$ git clone https://github.com/alexellis/pi_zero_stock
$ docker-compose up -d

```

更新网站很容易，只需要一个`git pull`命令，然后执行`docker-compose up -d`命令，该命令需要带上`--build`参数。

如果你仍然手动链接你的 Docker 容器，请自己或使用如下我的代码尝试 Docker Compose：

>
> 复刻或者追踪在 github 上的代码： [alexellis/pi*zero*stock](https://github.com/alexellis/pi_zero_stock)
>
>
>

### 一睹测试网站芳容

目前测试网站使用 docker-compose 部署：[stockalert.alexellis.io](http://stockalert.alexellis.io/)

*预览于 2016 年 5 月 16 日*

---

via: <http://blog.alexellis.io/rapid-prototype-docker-compose/>

作者：[Alex Ellis](http://blog.alexellis.io/author/alex/) 译者：[firstadream](https://github.com/firstadream) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译，[Linux中国](https://linux.cn/) 荣誉推出


标志性文本编辑器 Vim 迎来其 25 周年纪念日

Live long & prosper!

8.0 版本怎么样？稳定么？

zan

# 标志性文本编辑器 Vim 迎来其 25 周年纪念日

让我们把时钟往回拨一点。不，别停…再来一点……好了！在 25 年前，当你的某些专家同事还在蹒跚学步时，Bram Moolenaar 已经开始为他的 Amiga 计算机开发一款文本编辑器。他曾经是 Unix 上的 vi 用户，但 Amiga 上却没有与其类似的编辑器。在三年的开发之后，1991 年 11 月 2 日，他发布了“<ruby> 仿 vi 编辑器 <rp>  （ </rp> <rt>  Vi IMitation </rt> <rp>  ） </rp></ruby>”（也就是 [Vim](http://www.vim.org/)）的第一个版本。

两年之后，随着 2.0 版本的发布，Vim 的功能集已经超过了 vi，所以这款编辑器的全称也被改为了“<ruby> vi 增强版 <rp>  （ </rp> <rt>  Vi IMproved </rt> <rp>  ） </rp></ruby>”。现在，刚刚度过 25 岁生日的 Vim，已经可以在绝大部分平台上运行——Windows、OS/2、OpenVMS、BSD、Android、iOS——并且已在 OS X 及很多 Linux 发行版上成为标配软件。它受到了许多赞誉，也受到了许多批评，不同组织或开发者也会围绕它来发起争论，甚至有些面试官会问：“你用 Emacs 还是 Vim？”Vim 已拥有自由许可证，它基于<ruby>   慈善软件证书  <rp>  （ </rp> <rt>  charityware license </rt> <rp>  ） </rp></ruby>（“帮助乌干达的可怜儿童”）发布，该证书与 GPL 兼容。

Vim 是一个灵活的、可扩展的编辑器，带有一个强大的插件系统，被深入集成到许多开发工具，并且可以编辑上百种编程语言或文件类型的文件。 在 Vim 诞生二十五年后，Bram Moolenaar 仍然在主导开发并维护它——这真是一个壮举！Vim 曾经在超过 20 年的时间里数次间歇中断开发，但在 2016 年 9 月，它发布了 [8.0 版本](/article-7766-1.html)，添加了许多新功能，为现代程序员用户提供了更多方便。很多年来，Vim 在官网上售卖 T 恤及 logo 贴纸，所得的销售款为支持 [ICCF](http://iccf-holland.org/)——一个帮助乌干达儿童的德国慈善机构做出了巨大贡献。Vim 所支持的慈善项目深受 Moolenaar 喜爱，Mooleaar 本人也去过乌干达，在基巴莱的一个儿童中心做志愿者。

Vim 在开源历史上记下了有趣的一笔：一个工程，在 25 年中，坚持由一个稳定的核心贡献者维护，拥有超多的用户，但很多人从未了解过它的历史。如果你想简单的了解 Vim，[请访问它的网站](http://www.vim.org/)，或者你可以读一读 [Vim 入门的一些小技巧](https://opensource.com/life/16/7/tips-getting-started-vim)，或者在 Opensource.com 阅读一位 [Vim 新用户的故事](https://opensource.com/business/16/8/7-reasons-love-vim)。

---

via: <https://opensource.com/life/16/11/happy-birthday-vim-25>

作者：[D Ruth Bavousett](https://opensource.com/users/druthb) 译者：[StdioA](https://github.com/StdioA) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何在 Arch Linux 的终端里设定 WiFi 网络

很实用的技巧性操作文章，希望大家收藏。

wifi-menu so easy

谢谢楼主，看完配置好了。一开始不能自动连接，每次报错，只能开机后再运行WiFi-menu，或者netctl。后来我认为是以前启动的netctl不对引起的，于是删除了所有的热点设置，重新配置一个wlp9s0-111，然后netctl  reenable   wlp9s0-111,netctl  restart  wlp9s0-111。就可以自动启动了。

来自黑龙江黑河的 MIUI Browser 10.7|Android 8.0 用户

# 如何在 Arch Linux 的终端里设定 WiFi 网络

如果你使用的是其他 Linux 发行版 而不是 Arch CLI，那么可能会不习惯在终端里设置 WiFi。尽管整个过程有点简单，不过我还是要讲一下。在这篇文章里，我将带领新手们通过一步步的设置向导，把你们的 Arch Linux 接入到你的 WiFi 网络里。

在 Linux 里有很多程序来设置无线连接，我们可以用 `ip` 和 `iw` 来配置因特网连接，但是对于新手来说有点复杂。所以我们会使用 `netctl` 命令，这是一个基于命令行的工具，用来通过配置文件来设置和管理网络连接。

注意：所有的设定都需要 root 权限，或者你也可以使用 `sudo` 命令来完成。

### 搜索网络

运行下面的命令来查看你的网络接口：

```
iwconfig

```

运行如下命令启用你的网络接口，如果没有启用的话：

```
ip link set  interface up

```

运行下面的命令搜索可用的 WiFi 网络。可以向下翻页来查看。

```
iwlist interface scan | less

```

**注意：** 命令里的 interface 是之前用 `iwconfig` 获取到的实际网络接口。

扫描完，如果不使用该接口可以运行如下命令关闭：

```
ip link set interface down

```

### 使用 netctl 配置 Wi-Fi：

在使用 `netctl` 设置连接之前，你必须先检查一下你的网卡在 Linux 下的兼容性。

运行命令：

```
lspci -k

```

这条命令是用来检查内核是否加载了你的无线网卡驱动。输出必须是像这样的：

如果内核没有加载驱动，你就必须使用有线连接来安装一下。这里是 Linux 无线网络的官方维基页面：<https://wireless.wiki.kernel.org/>。

如果你的无线网卡和 Linux 兼容，你可以使用 `netctl configuration`。

`netctl` 使用配置文件，这是一个包含连接信息的文件。创建这个文件有简单和困难两种方式。

#### 简单方式 – Wifi-menu

如果你想用 `wifi-menu`，必须安装 `dialog`。

1. 运行命令: `wifi-menu`
2. 选择你的网络

3. 输入正确的密码并等待

如果没有连接失败的信息，你可以用下面的命令确认下：

```
ping -c 3 www.google.com

```

哇！如果你看到正在 ping，意味着网络设置成功。你现在已经在 Arch Linux 下连上 WiFi 了。如果有任何问题，可以倒回去重来。也许漏了什么。

#### 困难方式

比起上面的 `wifi-menu` 命令，这种方式会难一点点，所以我叫做困难方式。在上面的命令里，网络配置会自动生成。而在困难方式里，我们将手动修改配置文件。不过不要担心，也没那么难。那我们开始吧！

1. 首先第一件事，你必须要知道网络接口的名字，通常会是 `wlan0` 或 `wlp2s0`，但是也有很多例外。要确认你自己的网络接口，输入 `iwconfig` 命令并记下来。

[](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/scan-wifi-networks-in-arch-linux-cli_orig.png)
2. 运行命令:

```
cd /etc/netctl/examples

```

在这个目录里，有很多不同的配置文件例子。
3. 拷贝将用到的配置文件例子到 `/etc/netctl/your_profile`

```
cp /etc/netctl/examples/wireless-wpa /etc/netctl/your_profile

```
4. 你可以用这个命令来查看配置文件内容: `cat /etc/netctl/your_profile`

5. 用 `vi` 或者 `nano` 编辑你的配置文件的下面几个部分：

```
nano /etc/netctl/your_profile

```

```
- `Interface`：比如说 `wlan0`
- `ESSID`：你的无线网络名字
- `key`：你的无线网络密码

```

**注意:**

如果你不知道怎么用 `nano`，打开文件后，编辑要修改的地方，完了按 `ctrl+o`，然后回车，然后按 `ctrl+x`。

### 运行 netctl

1. 运行命令：

```
cd /etc/netctl
ls

```

你一定会看到 `wifi-menu` 生成的配置文件，比如 `wlan0-SSID`；或者你选择了困难方式，你一定会看到你自己创建的配置文件。
2. 运行命令启动连接配置：`netctl start your_profile`。
3. 用下面的命令测试连接：

```
ping -c 3 www.google.com

```

输出看上去像这样：
4. 最后，你必须运行下面的命令：`netctl enable your_profile`。

```
netctl enable your_profile

```

这样将创建并激活一个 systemd 服务，然后开机时自动启动。然后欢呼吧！你在你的 Arch Linux 里配置好 wifi 网络啦。

### 其他工具

你还可以使用其他程序来设置无线连接：

iw：

1. `iw dev wlan0 link` – 状态
2. `iw dev wlan0 scan` – 搜索网络
3. `iw dev wlan0 connect your_essid` – 连接到开放网络
4. `iw dev wlan0 connect your_essid key your_key` - 使用 16 进制密钥连接到 WEP 加密的网络

wpa\_supplicant

* <https://wiki.archlinux.org/index.php/WPA_supplicant>

Wicd

* <https://wiki.archlinux.org/index.php/wicd>

NetworkManager

* <https://wiki.archlinux.org/index.php/NetworkManager>

### 总结

会了吧！我提供了在 **Arch Linux** 里接入 WiFI 网络的三种方式。这里有一件事我再强调一下，当你执行第一条命令的时候，请记住你的网络接口名字。在接下来搜索网络的命令里，请使用你的网络接口名字比如 `wlan0` 或 `wlp2s0`（上一个命令里得到的），而不是用 interface 这个词。如果你碰到任何问题，可以在下面的评论区里直接留言给我。然后别忘了在你的朋友圈里和大家分享这篇文章哦。谢谢！

---

via: <http://www.linuxandubuntu.com/home/how-to-setup-a-wifi-in-arch-linux-using-terminal>

作者：[Mohd Sohail](http://www.linuxandubuntu.com/contact-us.html) 译者：[zpl1025](https://github.com/zpl1025) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


这篇文章的作者肯定是被Linux驱动弄疯了，但是还是很政治正确的拍Linux马屁。----win万岁！

终于知道为什么有些驱动linux的源码烂遍大街，windows版的却找死了都找不到

# Linux 与 Windows 的设备驱动模型对比：架构、API 和开发环境比较

>
> 名词缩写：
>
>
> * <ruby> API 应用程序接口 <rp>  （ </rp> <rt>  Application Program Interface </rt> <rp>  ） </rp></ruby>
> * <ruby> ABI 应用系统二进制接口 <rp>  （ </rp> <rt>  Application Binary Interface </rt> <rp>  ） </rp></ruby>
>
>
>

设备驱动是操作系统的一部分，它能够通过一些特定的编程接口便于硬件设备的使用，这样软件就可以控制并且运行那些设备了。因为每个驱动都对应不同的操作系统，所以你就需要不同的 Linux、Windows 或 Unix 设备驱动，以便能够在不同的计算机上使用你的设备。这就是为什么当你雇佣一个驱动开发者或者选择一个研发服务商提供者的时候，查看他们为各种操作系统平台开发驱动的经验是非常重要的。

驱动开发的第一步是理解每个操作系统处理它的驱动的不同方式、底层驱动模型、它使用的架构、以及可用的开发工具。例如，Linux 驱动程序模型就与 Windows 非常不同。虽然 Windows 提倡驱动程序开发和操作系统开发分别进行，并通过一组 ABI 调用来结合驱动程序和操作系统，但是 Linux 设备驱动程序开发不依赖任何稳定的 ABI 或 API，所以它的驱动代码并没有被纳入内核中。每一种模型都有自己的优点和缺点，但是如果你想为你的设备提供全面支持，那么重要的是要全面的了解它们。

在本文中，我们将比较 Windows 和 Linux 设备驱动程序，探索不同的架构，API，构建开发和分发，希望让您比较深入的理解如何开始为每一个操作系统编写设备驱动程序。

### 1. 设备驱动架构

Windows 设备驱动程序的体系结构和 Linux 中使用的不同，它们各有优缺点。差异主要受以下原因的影响：Windows 是闭源操作系统，而 Linux 是开源操作系统。比较 Linux 和 Windows 设备驱动程序架构将帮助我们理解 Windows 和 Linux 驱动程序背后的核心差异。

#### 1.1. Windows 驱动架构

虽然 Linux 内核分发时带着 Linux 驱动，而 Windows 内核则不包括设备驱动程序。与之不同的是，现代 Windows 设备驱动程序编写使用 Windows 驱动模型（WDM），这是一种完全支持即插即用和电源管理的模型，所以可以根据需要加载和卸载驱动程序。

处理来自应用的请求，是由 Windows 内核的中被称为 I/O 管理器的部分来完成的。I/O 管理器的作用是是转换这些请求到<ruby> I/O 请求数据包 <rp>  （ </rp> <rt>  IO Request Packets </rt> <rp>  ） </rp></ruby>（IRP），IRP 可以被用来在驱动层识别请求并且传输数据。

Windows 驱动模型 WDM 提供三种驱动， 它们形成了三个层：

* <ruby> 过滤 <rp>  （ </rp> <rt>  Filter </rt> <rp>  ） </rp></ruby>驱动提供关于 IRP 的可选附加处理。
* <ruby> 功能 <rp>  （ </rp> <rt>  Function </rt> <rp>  ） </rp></ruby>驱动是实现接口和每个设备通信的主要驱动。
* <ruby> 总线 <rp>  （ </rp> <rt>  Bus </rt> <rp>  ） </rp></ruby>驱动服务不同的配适器和不同的总线控制器，来实现主机模式控制设备。

一个 IRP 通过这些层就像它们经过 I/O 管理器到达底层硬件那样。每个层能够独立的处理一个 IRP 并且把它们送回 I/O 管理器。在硬件底层中有硬件抽象层（HAL），它提供一个通用的接口到物理设备。

#### 1.2. Linux 驱动架构

相比于 Windows 设备驱动，Linux 设备驱动架构根本性的不同就是 Linux 没有一个标准的驱动模型也没有一个干净分隔的层。每一个设备驱动都被当做一个能够自动的从内核中加载和卸载的模块来实现。Linux 为即插即用设备和电源管理设备提供一些方式，以便那些驱动可以使用它们来正确地管理这些设备，但这并不是必须的。

模式输出那些它们提供的函数，并通过调用这些函数和传入随意定义的数据结构来沟通。请求来自文件系统或网络层的用户应用，并被转化为需要的数据结构。模块能够按层堆叠，在一个模块进行处理之后，另外一个再处理，有些模块提供了对一类设备的公共调用接口，例如 USB 设备。

Linux 设备驱动程序支持三种设备：

* 实现一个字节流接口的<ruby> 字符 <rp>  （ </rp> <rt>  Character </rt> <rp>  ） </rp></ruby>设备。
* 用于存放文件系统和处理多字节数据块 IO 的<ruby> 块 <rp>  （ </rp> <rt>  Block </rt> <rp>  ） </rp></ruby>设备。
* 用于通过网络传输数据包的<ruby> 网络 <rp>  （ </rp> <rt>  Network </rt> <rp>  ） </rp></ruby>接口。

Linux 也有一个硬件抽象层（HAL），它实际扮演了物理硬件的设备驱动接口。

### 2. 设备驱动 API

Linux 和 Windows 驱动 API 都属于事件驱动类型：只有当某些事件发生的时候，驱动代码才执行——当用户的应用程序希望从设备获取一些东西，或者当设备有某些请求需要告知操作系统。

#### 2.1. 初始化

在 Windows 上，驱动被表示为 `DriverObject` 结构，它在 `DriverEntry` 函数的执行过程中被初始化。这些入口点也注册一些回调函数，用来响应设备的添加和移除、驱动卸载和处理新进入的 IRP。当一个设备连接的时候，Windows 创建一个设备对象，这个设备对象在设备驱动后面处理所有应用请求。

相比于 Windows，Linux 设备驱动生命周期由内核模块的 `module_init` 和 `module_exit` 函数负责管理，它们分别用于模块的加载和卸载。它们负责注册模块来通过使用内核接口来处理设备的请求。这个模块需要创建一个设备文件（或者一个网络接口），为其所希望管理的设备指定一个数字识别号，并注册一些当用户与设备文件交互的时候所使用的回调函数。

#### 2.2. 命名和声明设备

##### 在 Windows 上注册设备

Windows 设备驱动在新连接设备时是由回调函数 `AddDevice` 通知的。它接下来就去创建一个<ruby> 设备对象 <rp>  （ </rp> <rt>  device object </rt> <rp>  ） </rp></ruby>，用于识别该设备的特定的驱动实例。取决于驱动的类型，设备对象可以是<ruby> 物理设备对象 <rp>  （ </rp> <rt>  Physical Device Object </rt> <rp>  ） </rp></ruby>（PDO），<ruby> 功能设备对象 <rp>  （ </rp> <rt>  Function Device Object </rt> <rp>  ） </rp></ruby>（FDO），或者<ruby> 过滤设备对象 <rp>  （ </rp> <rt>  Filter Device Object </rt> <rp>  ） </rp></ruby>（FIDO）。设备对象能够堆叠，PDO 在底层。

设备对象在这个设备连接在计算机期间一直存在。`DeviceExtension` 结构能够被用于关联到一个设备对象的全局数据。

设备对象可以有如下形式的名字 `\Device\DeviceName`，这被系统用来识别和定位它们。应用可以使用 `CreateFile` API 函数来打开一个有上述名字的文件，获得一个可以用于和设备交互的句柄。

然而，通常只有 PDO 有自己的名字。未命名的设备能够通过设备级接口来访问。设备驱动注册一个或多个接口，以 128 位全局唯一标识符（GUID）来标示它们。用户应用能够使用已知的 GUID 来获取一个设备的句柄。

##### 在 Linux 上注册设备

在 Linux 平台上，用户应用通过文件系统入口访问设备，它通常位于 `/dev` 目录。在模块初始化的时候，它通过调用内核函数 `register_chrdev` 创建了所有需要的入口。应用可以发起 `open` 系统调用来获取一个文件描述符来与设备进行交互。这个调用后来被发送到回调函数，这个调用（以及将来对该返回的文件描述符的进一步调用，例如 `read`、`write` 或`close`）会被分配到由该模块安装到 `file_operations` 或者 `block_device_operations`这样的数据结构中的回调函数。

设备驱动模块负责分配和保持任何需要用于操作的数据结构。传送进文件系统回调函数的 `file` 结构有一个 `private_data` 字段，它可以被用来存放指向具体驱动数据的指针。块设备和网络接口 API 也提供类似的字段。

虽然应用使用文件系统的节点来定位设备，但是 Linux 在内部使用一个<ruby> 主设备号 <rp>  （ </rp> <rt>  major numbers </rt> <rp>  ） </rp></ruby>和<ruby> 次设备号 <rp>  （ </rp> <rt>  minor numbers </rt> <rp>  ） </rp></ruby>的概念来识别设备及其驱动。主设备号被用来识别设备驱动，而次设备号由驱动使用来识别它所管理的设备。驱动为了去管理一个或多个固定的主设备号，必须首先注册自己或者让系统来分配未使用的设备号给它。

目前，Linux 为<ruby> 主次设备对 <rp>  （ </rp> <rt>  major-minor pairs </rt> <rp>  ） </rp></ruby>使用一个 32 位的值，其中 12 位分配主设备号，并允许多达 4096 个不同的设备。主次设备对对于字符设备和块设备是不同的，所以一个字符设备和一个块设备能使用相同的设备对而不导致冲突。网络接口是通过像 eth0 的符号名来识别，这些又是区别于主次设备的字符设备和块设备的。

#### 2.3. 交换数据

Linux 和 Windows 都支持在用户级应用程序和内核级驱动程序之间传输数据的三种方式：

* <ruby> 缓冲型输入输出 <rp>  （ </rp> <rt>  Buffered Input-Output </rt> <rp>  ） </rp></ruby>它使用由内核管理的缓冲区。对于写操作，内核从用户空间缓冲区中拷贝数据到内核分配的缓冲区，并且把它传送到设备驱动中。读操作也一样，由内核将数据从内核缓冲区中拷贝到应用提供的缓冲区中。
* <ruby> 直接型输入输出 <rp>  （ </rp> <rt>  Direct Input-Output </rt> <rp>  ） </rp></ruby> 它不使用拷贝功能。代替它的是，内核在物理内存中钉死一块用户分配的缓冲区以便它可以一直留在那里，以便在数据传输过程中不被交换出去。
* <ruby> 内存映射 <rp>  （ </rp> <rt>  Memory mapping </rt> <rp>  ） </rp></ruby> 它也能够由内核管理，这样内核和用户空间应用就能够通过不同的地址访问同样的内存页。

##### Windows 上的驱动程序 I/O 模式

支持缓冲型 I/O 是 WDM 的内置功能。缓冲区能够被设备驱动通过在 IRP 结构中的 `AssociatedIrp.SystemBuffer` 字段访问。当需要和用户空间通讯的时候，驱动只需从这个缓冲区中进行读写操作。

Windows 上的直接 I/O 由<ruby> 内存描述符列表 <rp>  （ </rp> <rt>  memory descriptor lists </rt> <rp>  ） </rp></ruby>（MDL）介导。这种半透明的结构是通过在 IRP 中的 `MdlAddress` 字段来访问的。它们被用来定位由用户应用程序分配的缓冲区的物理地址，并在 I/O 请求期间钉死不动。

在 Windows 上进行数据传输的第三个选项称为 `METHOD_NEITHER`。 在这种情况下，内核需要传送用户空间的输入输出缓冲区的虚拟地址给驱动，而不需要确定它们有效或者保证它们映射到一个可以由设备驱动访问的物理内存地址。设备驱动负责处理这些数据传输的细节。

##### Linux 上的驱动程序 I/O 模式

Linux 提供许多函数例如，`clear_user`、`copy_to_user`、`strncpy_from_user` 和一些其它的用来在内核和用户内存之间进行缓冲区数据传输的函数。这些函数保证了指向数据缓存区指针的有效，并且通过在内存区域之间安全地拷贝数据缓冲区来处理数据传输的所有细节。

然而，块设备的驱动对已知大小的整个数据块进行操作，它可以在内核和用户地址区域之间被快速移动而不需要拷贝它们。这种情况是由 Linux 内核来自动处理所有的块设备驱动。块请求队列处理传送数据块而不用多余的拷贝，而 Linux 系统调用接口来转换文件系统请求到块请求中。

最终，设备驱动能够从内核地址区域分配一些存储页面（不可交换的）并且使用 `remap_pfn_range` 函数来直接映射这些页面到用户进程的地址空间。然后应用能获取这些缓冲区的虚拟地址并且使用它来和设备驱动交流。

### 3. 设备驱动开发环境

#### 3.1. 设备驱动框架

##### Windows 驱动程序工具包

Windows 是一个闭源操作系统。Microsoft 提供 Windows 驱动程序工具包以方便非 Microsoft 供应商开发 Windows 设备驱动。工具包中包含开发、调试、检验和打包 Windows 设备驱动等所需的所有内容。

<ruby> Windows 驱动模型 <rp>  （ </rp> <rt>  Windows Driver Model </rt> <rp>  ） </rp></ruby>（WDM）为设备驱动定义了一个干净的接口框架。Windows 保持这些接口的源代码和二进制的兼容性。编译好的 WDM 驱动通常是前向兼容性：也就是说，一个较旧的驱动能够在没有重新编译的情况下在较新的系统上运行，但是它当然不能够访问系统提供的新功能。但是，驱动不保证后向兼容性。

##### Linux 源代码

和 Windows 相对比，Linux 是一个开源操作系统，因此 Linux 的整个源代码是用于驱动开发的 SDK。没有驱动设备的正式框架，但是 Linux 内核包含许多提供了如驱动注册这样的通用服务的子系统。这些子系统的接口在内核头文件中描述。

尽管 Linux 有定义接口，但这些接口在设计上并不稳定。Linux 不提供有关前向和后向兼容的任何保证。设备驱动对于不同的内核版本需要重新编译。没有稳定性的保证可以让 Linux 内核进行快速开发，因为开发人员不必去支持旧的接口，并且能够使用最好的方法解决手头的这些问题。

当为 Linux 写<ruby> 树内 <rp>  （ </rp> <rt>  in-tree </rt> <rp>  ） </rp></ruby>（指当前 Linux 内核开发主干）驱动程序时，这种不断变化的环境不会造成任何问题，因为它们作为内核源代码的一部分，与内核本身同步更新。然而，闭源驱动必须单独开发，并且在<ruby> 树外 <rp>  （ </rp> <rt>  out-of-tree </rt> <rp>  ） </rp></ruby>，必须维护它们以支持不同的内核版本。因此，Linux 鼓励设备驱动程序开发人员在树内维护他们的驱动。

#### 3.2. 为设备驱动构建系统

Windows 驱动程序工具包为 Microsoft Visual Studio 添加了驱动开发支持，并包括用来构建驱动程序代码的编译器。开发 Windows 设备驱动程序与在 IDE 中开发用户空间应用程序没有太大的区别。Microsoft 提供了一个企业 Windows 驱动程序工具包，提供了类似于 Linux 命令行的构建环境。

Linux 使用 Makefile 作为树内和树外系统设备驱动程序的构建系统。Linux 构建系统非常发达，通常是一个设备驱动程序只需要少数行就产生一个可工作的二进制代码。开发人员可以使用任何 [IDE](/article-7704-1.html)，只要它可以处理 Linux 源代码库和运行 `make` ，他们也可以很容易地从终端手动编译驱动程序。

#### 3.3. 文档支持

Windows 对于驱动程序的开发有良好的文档支持。Windows 驱动程序工具包包括文档和示例驱动程序代码，通过 MSDN 可获得关于内核接口的大量信息，并存在大量的有关驱动程序开发和 Windows 底层的参考和指南。

Linux 文档不是描述性的，但整个 Linux 源代码可供驱动开发人员使用缓解了这一问题。源代码树中的 Documentation 目录描述了一些 Linux 的子系统，但是有[几本书](http://xmodulo.com/go/linux_device_driver_books)介绍了关于 Linux 设备驱动程序开发和 Linux 内核概览，它们更详细。

Linux 没有提供设备驱动程序的指定样本，但现有生产级驱动程序的源代码可用，可以用作开发新设备驱动程序的参考。

#### 3.4. 调试支持

Linux 和 Windows 都有可用于追踪调试驱动程序代码的日志机制。在 Windows 上将使用 `DbgPrint` 函数，而在 Linux 上使用的函数称为 `printk`。然而，并不是每个问题都可以通过只使用日志记录和源代码来解决。有时断点更有用，因为它们允许检查驱动代码的动态行为。交互式调试对于研究崩溃的原因也是必不可少的。

Windows 通过其内核级调试器 `WinDbg` 支持交互式调试。这需要通过一个串行端口连接两台机器：一台计算机运行被调试的内核，另一台运行调试器和控制被调试的操作系统。Windows 驱动程序工具包包括 Windows 内核的调试符号，因此 Windows 的数据结构将在调试器中部分可见。

Linux 还支持通过 `KDB` 和 `KGDB` 进行的交互式调试。调试支持可以内置到内核，并可在启动时启用。之后，可以直接通过物理键盘调试系统，或通过串行端口从另一台计算机连接到它。KDB 提供了一个简单的命令行界面，这是唯一的在同一台机器上来调试内核的方法。然而，KDB 缺乏源代码级调试支持。KGDB 通过串行端口提供了一个更复杂的接口。它允许使用像 GDB 这样标准的应用程序调试器来调试 Linux 内核，就像任何其它用户空间应用程序一样。

### 4. 设备驱动分发

#### 4.1. 安装设备驱动

在 Windows 上安装的驱动程序，是由被称为为 INF 的文本文件描述的，通常存储在 `C:\Windows\INF` 目录中。这些文件由驱动供应商提供，并且定义哪些设备由该驱动程序服务，哪里可以找到驱动程序的二进制文件，和驱动程序的版本等。

当一个新设备插入计算机时，Windows 通过查看已经安装的驱动程序并且选择适当的一个加载。当设备被移除的时候，驱动会自动卸载它。

在 Linux 上，一些驱动被构建到内核中并且保持永久的加载。非必要的驱动被构建为内核模块，它们通常是存储在 `/lib/modules/kernel-version` 目录中。这个目录还包含各种配置文件，如 `modules.dep`，用于描述内核模块之间的依赖关系。

虽然 Linux 内核可以在自身启动时加载一些模块，但通常模块加载由用户空间应用程序监督。例如，`init` 进程可能在系统初始化期间加载一些模块，`udev` 守护程序负责跟踪新插入的设备并为它们加载适当的模块。

#### 4.2. 更新设备驱动

Windows 为设备驱动程序提供了稳定的二进制接口，因此在某些情况下，无需与系统一起更新驱动程序二进制文件。任何必要的更新由 Windows Update 服务处理，它负责定位、下载和安装适用于系统的最新版本的驱动程序。

然而，Linux 不提供稳定的二进制接口，因此有必要在每次内核更新时重新编译和更新所有必需的设备驱动程序。显然，内置在内核中的设备驱动程序会自动更新，但是树外模块会产生轻微的问题。 维护最新的模块二进制文件的任务通常用 [DKMS](http://xmodulo.com/build-kernel-module-dkms-linux.html) 来解决：这是一个当安装新的内核版本时自动重建所有注册的内核模块的服务。

#### 4.3. 安全方面的考虑

所有 Windows 设备驱动程序在 Windows 加载它们之前必须被数字签名。在开发期间可以使用自签名证书，但是分发给终端用户的驱动程序包必须使用 Microsoft 信任的有效证书进行签名。供应商可以从 Microsoft 授权的任何受信任的证书颁发机构获取<ruby> 软件出版商证书 <rp>  （ </rp> <rt>  Software Publisher Certificate </rt> <rp>  ） </rp></ruby>。然后，此证书由 Microsoft 交叉签名，并且生成的交叉证书用于在发行之前签署驱动程序包。

Linux 内核也能配置为在内核模块被加载前校验签名，并禁止不可信的内核模块。被内核所信任的公钥集在构建时是固定的，并且是完全可配置的。由内核执行的检查，这个检查严格性在构建时也是可配置的，范围从简单地为不可信模块发出警告，到拒绝加载有效性可疑的任何东西。

### 5. 结论

如上所示，Windows 和 Linux 设备驱动程序基础设施有一些共同点，例如调用 API 的方法，但更多的细节是相当不同的。最突出的差异源于 Windows 是由商业公司开发的封闭源操作系统这个事实。这使得 Windows 上有好的、文档化的、稳定的驱动 ABI 和正式框架，而在 Linux 上，更多的是源代码做了一个有益的补充。文档支持也在 Windows 环境中更加发达，因为 Microsoft 具有维护它所需的资源。

另一方面，Linux 不会使用框架来限制设备驱动程序开发人员，并且内核和产品级设备驱动程序的源代码可以在需要的时候有所帮助。缺乏接口稳定性也有其作用，因为它意味着最新的设备驱动程序总是使用最新的接口，内核本身承载较小的后向兼容性负担，这带来了更干净的代码。

了解这些差异以及每个系统的具体情况是为您的设备提供有效的驱动程序开发和支持的关键的第一步。我们希望这篇文章对 Windows 和 Linux 设备驱动程序开发做的对比，有助于您理解它们，并在设备驱动程序开发过程的研究中，将此作为一个伟大的起点。

---

via: <http://xmodulo.com/linux-vs-windows-device-driver-model.html>

作者：[Dennis Turpitka](http://xmodulo.com/author/dennis) 译者：[FrankXinqi &YangYang](https://github.com/FrankXinqi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


Summary:
更多：
- SLES for Raspberry Pi 支持树莓派 3
- Ubuntu Budgie 正式成为官方流派
- Mythbuntu Linux 宣告结束

# 一周开源新闻：Debian GNU/Linux 9 "Stretch" 将于 2017 年初发布

2016 年 11 月 5 日，Debian 发布经理 Emilio Pozuelo Monfort 和 Niels Thykier 高兴地[宣布](https://lists.debian.org/debian-devel-announce/2016/11/msg00002.html) Debian GNU/Linux 9 "Stretch" 操作系统进入冻结开发阶段。

在一个开发周期中，GNU/Linux 有多个里程碑，如 Alpha、Beta、RC、Beta Freeze、Feature Freeze，等等， Debian GNU/Linux 9 "Stretch" 即 Debian OS 的下一个重大发布，此版本已经是功能完善的版本。

"这意味着不再有新的库或包的大变化。不过之后几天还会有小的改变，我们尽量使其工作更流畅，所以请耐心等待最后的调整。"通知中写道。

按 Debian 开发周期，下一步是 2017 年 1 月 5 日进入 Soft Freeze 阶段。这意味着，此日期前所有更新必须纳入 Debian 测试计划中。

2017 年 2 月 5 日进入 Full Freeze 阶段，意味着所有更新需要获批。也意味着，此系统离最终发布不远了，预计是 2 月末或者 3 月初。

### SUSE Linux Enterprise Server for Raspberry Pi 3推出，支持蓝牙 & Wi-Fi

[SLES for Raspberry Pi](http://tinyurl.com/slespi) 是 SUSE Linux Enterprise Server 支持树莓派的单板计算机的官方版本，支持树莓派 2 和新的树莓派 3 设备。

SLES for the Raspberry Pi 支持 Broadcom BCM2837 64-bit A53 ARM 处理器，以及集成的蓝牙和 Wi-Fi。

### Ubuntu Budgie 成为 Ubuntu 官方流派发行版

在 11 月 8 日 Ubuntu 技术会议投票中，budgie-remix 被正式[接纳](https://budgie-remix.org/2016/11/08/its-official/)为 Ubuntu 官方流派发行版，称为 Ubuntu Budgie。

Ubuntu Budgie (之前称为 budgie-remix ）是使用 [Budgie](https://solus-project.com/budgie/) 作为默认桌面环境的 Ubuntu 发行版。它与其它Ubuntu 版本相似，只是更轻型，友好，简易。

*运行 Budgie Desktop 10.2.8 的Ubuntu Budgie 16.10*

### Mythbuntu Linux 正式宣布不再继续

而另一方面，与 Ubuntu Budgie 成功成为官方流派不同，2016 年 11 月 5 日，Mythbuntu GNU/Linux 团队[宣布](https://sites.google.com/a/mythbuntu.org/website/home/news/mythbuntusolongandthanksforallthefish)该项目中止，不再有新的发布。

团队建议希望使用 Mythbuntu 的用户安装 Xubuntu Linux 最新版本，然后增加 Mythbuntu PPA (Personal Package Archive)，提供最新 MythTV 发布和相关包。如有人想接管此项目，可通过官网联系原团队。

### 支持垂直面板的 Cinnamon 3.2 桌面环境现已可用

2016 年 11 月 7 日，[Cinnamon 3.2.0](https://github.com/linuxmint/Cinnamon/releases/tag/3.2.0) 桌面环境即将发布，用户如想提前体验可在[这里下载](http://linux.softpedia.com/get/Desktop-Environment/Window-Managers/Cinnamon-98277.shtml)。不过如果发现 bug 的话， Linux Mint 18.1 可能最终使用一个不同的 Cinnamon 3.2 版本，如 3.2.1 或 3.2.2。

*带有垂直面板的 Cinnamon 3.2*


Adobe 的新任首席信息官（CIO）对于开始一个新领导职位的建议

# Adobe 的新任首席信息官（CIO）对于开始一个新领导职位的建议

<img src="content_image/album_201611_10_135626jrr1s5exzgw0ytpg.png" alt="" title="">

我目前的几个月在一家十分受人尊敬的基于云的技术公司担任新的 CIO 一职。我的首要任务之一就是熟悉组织的人、文化和当务之急的事件。

作为这一目标的一部分，我访问了所有主要的网站。而在印度，上任不到两个月时，我被问道：“你打算做什么？你的计划是什么？” 我回答道，这个问题不会让经验丰富的 CIO 们感到吃惊：我现在仍然处于探索模式，我在做的主要是聆听和学习。

我从来没有在入职时制定一份蓝图说我要做什么。我知道一些 CIO 们拥有一本关于他要怎么做的”剧本“。他会煽动整个组织将他的计划付诸行动。

是的，在有些地方是完全崩坏了并无法发挥作用的情况下，这种行动可能是有意义的。但是，当我进入到一个公司时，我的策略是先开始一个探索的过程。我不想带入任何先入为主的观念，比如什么事应该是什么样子的，哪些工作和哪些是有冲突的，而哪些不是。

以下是我作为新任命的领导人的指导原则:

### 了解你的人

这意味着建立关系，它包括你的 IT 职员，你的客户和你的销售人员。他们的清单上最重要的是什么？他们想要你关注什么？什么产品受到好评？什么产品不好？客户体验是怎样的？了解如何帮助每一个人变得更好将决定你提供服务的方式。

如果你的部门就像我的一样，是分布在几层楼中的，你可以考虑召开一个见面会，利用午餐或者小型技术研讨会的时间，让大家可以进行自我介绍并讨论下他们正在进行的工作，如果他们愿意的话还可以分享一些他们的家庭小故事。如果你有一个公开的办公室政策，你得确保每一个人都很好的了解它了。如果你有跨国跨州的的员工，你应该尽快的去拜访一下。

### 了解你的产品和公司文化

来到 Adobe 后最让我震惊的是我们的产品组合是如此的广泛。我们有一个横贯三个云的提供解决方案和服务的平台——Adobe 创新云、文档云和营销云——和对每一个产品的丰富的组合。除非你去了解你的产品和学会如何为他们提供支持，否则你永远也不知道你的新公司有着多么多的机会。在 Adobe 我们给我们的零号客户使用许多数字媒体和数字市场解决方案，所以我们可以将我们的第一手经验分享给我们的客户。

### 了解客户

从很早开始，我们就收到客户的会面请求。与客户会面是一种很好的方式来启发你对 IT 机构未来的的思考，包括各种我们可以改进的地方，如技术、客户和消费者。

### 对未来的计划

作为一个新上任的领导者，我有一个全新的视角用以考虑组织的未来，而不会有挑战和障碍来干扰我。

CIO 们所需要做的就是推动 IT 进化到下一代。当我会见我的员工时，我问他们我们三到五年后的未来可以做什么，以便我们可以开始尽早定位。这意味着开始讨论方案和当务之急的事。

从那以后，它使领导小组团结在一起，所以我们能够共同来组建我们的下一代体系——它的使命、愿景、组织模式和操作规范。如果你开始从内而外的改变，那么它会渗透到业务和其他你所做的一切事情上。

贯穿整个过程，我对他人都表现一种开明的态度，这不是一个自上而下的命令。也许我对我们当前要做的事有一个自己的看法，但是我们必须使看法保持一致，我们是一个团队，我们应该共同找出我们需要做的事。

---

via: <https://enterprisersproject.com/article/2016/9/adobes-new-cio-shares-leadership-advice-starting-new-role>

作者：[Cynthia Stoddard](https://enterprisersproject.com/user/cynthia-stoddard) 译者：[Chao-zhi](https://github.com/Chao-zhi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


病毒过后，系统管理员投向了 Linux

Summary: 我爱上了 distro-hopping 和第一时间尝试新发行版的感觉。

用户还是要首先支持自由开源软件平台和支持自由软件的硬件平台系统。支持自由开源软件就是争取自己失去的用户自由和权利！！！

好老的文章啊，Mandrake GNU/Linux五年前就停止更新新版了，现在的Mageia GNU/Linux是这个项目的继承者，我尝试过Mageia，原汁原味的法兰西风情，默认KDE桌面，很华丽、流畅、友善的发行版，可喜对中文支持不是很友好，需要比较多的折腾，有兴趣的同学可以加入他们的中文本地化项目~

这文章是十年前的吧！现在你想让Windows 10蓝屏一次都不太容易。

好老的鸡汤文了

鸡汤随时喝着点，有助于身心健康，:D

哈哈，然而这篇真是 Opensource 最近发出来的。。。我承认里面提到的事情是很古老了。。

不不，上次因为连接kindle导致蓝屏让我重回了win7.这说明win10蓝屏也不像你说的那样不太容易。

不不，寝室的电脑还常常见他们蓝，不然就是“冰取之于水，而寒与水”:b

我爱上了 distro-hopping 和第一时间尝试新发行版的感觉。

# 病毒过后，系统管理员投向了 Linux

<img src="content_image/album_201611_10_143359vql2do0k2pm1ez25.jpg" alt="" title="">

我开源事业的第一笔，是我在 2001 年作为一名兼职系统管理员，为大学工作的时候。成为了那个以教学为目的，不仅仅在大学中，还在学术界的其他领域建立商业案例研究的小组的一份子。

随着团队的发展，渐渐地开始需要一个由文件服务、intranet 应用，域登录等功能构建而成的健壮的局域网。 我们的 IT 基础设施主要由跑着 Windows 98 的计算机组成，这些计算机对于大学的 IT 实验室来说已经太老了，就重新分配给了我们部门。

### 初探 Linux

一天，作为大学IT采购计划的一部分，我们部门收到了一台 IBM 服务器。 我们计划将其用作 Internet 网关，域控制站，文件服务器和备份服务器，以及 intranet 应用程序主机。

拆封后，我们注意到它附带了红帽 Linux 的 CD。 我们的 22 人团队（包括我）对 Linux 一无所知。 经过几天的研究，我找到了一位朋友的朋友，一位以 Linux RTOS （Linux 的实时操作系统领域）编程为生的人，求助他如何安装。

光看着那朋友用 CD 驱动器载入第一张安装 CD 并进入 Anaconda 安装系统，我的头都晕了。 大约一个小时，我们完成了基本的安装，但仍然没有可用的 internet 连接。

又花了一个小时的折腾才使我们连接到互联网，但仍没有域登录或 Internet 网关功能。 经过一个周末的折腾，我们可以让我们的 Windows 98 机器作为 Linux PC 的代理，终于构出了一个正常工作的共享互联环境。 但域登录还需要一段时间。

我们用龟速的电话调制解调器下载了 [Samba](https://www.samba.org/)，并手动配置它作为域控制站。文件服务也通过 NFS Kernel Server 开启了，随后为 Windows 98 的网络邻居创建了用户目录并进行了必要的调整和配置。

这个设置完美运行了一段时间，直到最终我们决定开始使用 Intranet 应用管理时间表和一些别的东西。 这个时候，我已经离开了该组织，并把大部分系统管理员的东西交给了接替我的人。

### 再遇 Linux

2004 年，我又重新装回了 Linux。我的妻子经营的一份独立员工安置业务，使用来自 Monster.com 等服务的数据来打通客户与求职者的交流渠道。

作为我们两人中的计算机好点的那个，在计算机和互联网出故障的时候，维修就成了我的分内之事。我们还需要用许多工具尝试，从堆积如山的简历中筛选出她每天必须看的。

Windows [BSoD](https://en.wikipedia.org/wiki/Blue_Screen_of_Death)（蓝屏） 早已司空见惯，但只要我们的付费数据是安全的，那就还算可以容忍。为此我将不得不每周花几个小时去做备份。

一天，我们的电脑中了毒，并且通过简单的方法无法清除。我们并不了解磁盘上的数据发生了些什么。当磁盘彻底挂掉后，我们插入了一周前的辅助备份磁盘，但是一周后它也挂了。我们的第二个备份直接拒绝启动。是时候寻求专业帮助了，所以我们把电脑送到一家靠谱的维修店。两天以后，我们被告知一些恶意软件或病毒已经将某些种类的文件擦除殆尽，其中包括我们的付费数据。

这是对我妻子的商业计划的一个巨大的打击，同时意味着丢失合同并耽误了账单。我曾短期出国工作，并在台湾的 [Computex 2004](https://en.wikipedia.org/wiki/Computex_Taipei) 购买了我的第一台笔记本电脑。 预装的是 Windows XP，但我还是想换成 Linux。 我知道 Linux 已经为桌面端做好了准备，[Mandrake Linux](https://en.wikipedia.org/wiki/Mandriva_Linux) （曼德拉草） 是一个很不错的选择。 我第一次安装就很顺利。所有工作都执行的非常漂亮。我使用 [OpenOffice](http://www.openoffice.org/) 来满足我写作，演示文稿和电子表格的需求。

我们为我们的计算机买了新的硬盘驱动器，并为其安装了 Mandrake Linux。用 OpenOffice 替换了 Microsoft Office。 我们依靠 Web 邮件来满足邮件需求，并在 2004 年的 11 月迎来了 [Mozilla Firefox](https://www.mozilla.org/en-US/firefox/new/)。我的妻子马上从中看到了好处，因为没有崩溃或病毒/恶意软件感染！更重要的是，我们告别了困扰 Windows 98 和 XP 的频繁崩溃问题。 她一直使用这个发行版。

而我，开始尝试其他的发行版。 我爱上了 distro-hopping （LCTT 译注：指在不同版本的 Linux 发行版之间频繁切换的 Linux 用户）和第一时间尝试新发行版的感觉。我也经常会在 Apache 和 NGINX 上尝试和测试 Web 应用程序，如 Drupal、Joomla 和 WordPress。现在我们 2006 年出生的儿子，在 Linux 下成长。 也对 Tux Paint，Gcompris 和 SMPlayer 非常满意。

---

via: <https://opensource.com/life/16/3/my-linux-story-soumya-sarkar>

作者：[Soumya Sarkar](https://opensource.com/users/ssarkarhyd) 译者：[martin2011qi](https://github.com/martin2011qi) 校对：[wxy](https://github.com/wxy)


怎样用 Tar 和 OpenSSL 给文件和目录加密及解密

杀鸡焉用牛刀，用 7z 会方便的多。

嗯，我也是喜欢用7z，很方便

7z -p123 -mhe -mx0 a test.7z a.txt

-p 123 密码

-mx 0 压缩级别：仅储存

不过话说回来，在安装或编译包受限的环境这不失为一种加密方法。<br />
P.S. 我在 Windows 中也利用类似的方法，使用本来用于数字证书方面的命令 certutil 实现对任意文件的 base64 编码/解码。这个命令其实主要是用在域控环境中，Workstation 上用途则受限制。

certuitl -encode demo.7z txt

type txt | clip

用这个命令可以方便的将几十 MiB 的二进制文件转换为文本文件，通过剪贴板经由 RDP 远程桌面协议复制到其他机器上。

sed '1d;$d' txt | base64 --decode

sed '1d;$d' txt | base64 --decode | xxd

在 linux 下的解码。 注意第一个命令组合没有考虑文字编码。

# 怎样用 Tar 和 OpenSSL 给文件和目录加密及解密

当你有重要的敏感数据的时候，给你的文件和目录额外加一层保护是至关重要的，特别是当你需要通过网络与他人传输数据的时候。

由于这个原因，我在寻找一个可疑在 Linux 上加密及解密文件和目录的实用程序，幸运的是我找到了一个用 tar（Linux 的一个压缩打包工具）和 OpenSSL 来解决的方案。借助这两个工具，你真的可以毫不费力地创建和加密 tar 归档文件。

在这篇文章中，我们将了解如何使用 OpenSSL 创建和加密 tar 或 gz（gzip，另一种压缩文件）归档文件：

牢记使用 OpenSSL 的常规方式是：

```
# openssl command command-options arguments

```

### 在 Linux 中加密文件

要加密当前工作目录的内容（根据文件的大小，这可能需要一点时间）：

```
# tar -czf - * | openssl enc -e -aes256 -out secured.tar.gz

```

上述命令的解释：

1. `enc` - openssl 命令使用加密进行编码
2. `-e` – 用来加密输入文件的 `enc` 命令选项，这里是指前一个 tar 命令的输出
3. `-aes256` – 加密用的算法
4. `-out` – 用于指定输出文件名的 `enc` 命令选项，这里文件名是 `secured.tar.gz`

### 在 Linux 中解密文件

要解密上述 tar 归档内容，使用以下命令。

```
# openssl enc -d -aes256 -in secured.tar.gz | tar xz -C test

```

上述命令的解释：

1. `-d` – 用于解密文件
2. `-C` – 提取内容到 `test` 子目录

下图展示了加解密过程，以及当你尝试执行以下操作时会发生什么：

1. 以传统方式提取 tar 包的内容
2. 使用了错误的密码的时候
3. 当你输入正确的密码的时候

*在 Linux 中加密和解密 Tar 归档文件*

当你在本地网络或因特网工作的时候，你可以随时通过加密来保护你和他人共享的重要文本或文件，这有助于降低将其暴露给恶意攻击者的风险。

我们研究了一种使用 OpenSSL（一个 openssl 命令行工具）加密 tar 包的简单技术，你可以参考它的<ruby> 手册页 <rp>  （ </rp> <rt>  man page </rt> <rp>  ） </rp></ruby>来获取更多信息和有用的命令。

---

via: <http://www.tecmint.com/encrypt-decrypt-files-tar-openssl-linux/>

作者：[Gabriel Cánepa](http://www.tecmint.com/author/gacanepa/) 译者：[OneNewLife](https://github.com/OneNewLife) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


在 Linux 下使用 TCP 封装器来加强网络服务安全

Summary: 你可以把这个工具看作是一个基于主机的访问控制列表，而且并不能作为你的系统的终极安全措施。通过使用一个防火墙和 TCP 封装器，而不是只偏爱其中的一个，你将会确保你的服务不会被出现单点故障。

不错，很有用

# 在 Linux 下使用 TCP 封装器来加强网络服务安全

在这篇文章中，我们将会讲述什么是 <ruby> TCP 封装器 <rp>  （ </rp> <rt>  TCP wrappers </rt> <rp>  ） </rp></ruby>以及如何在一台 Linux 服务器上配置他们来[限制网络服务的权限](/article-7719-1.html)。在开始之前，我们必须澄清 TCP 封装器并不能消除对于正确[配置防火墙](/article-4425-1.html)的需要。

就这一点而言，你可以把这个工具看作是一个[基于主机的访问控制列表](/article-3966-1.html)，而且并不能作为你的系统的[终极安全措施](http://www.tecmint.com/linux-server-hardening-security-tips/)。通过使用一个防火墙和 TCP 封装器，而不是只偏爱其中的一个，你将会确保你的服务不会被出现单点故障。

### 正确理解 hosts.allow 和 hosts.deny 文件

当一个网络请求到达你的主机的时候，TCP 封装器会使用 `hosts.allow` 和 `hosts.deny` （按照这样的顺序）来决定客户端是否应该被允许使用一个提供的服务。.

在默认情况下，这些文件内容是空的，或者被注释掉，或者根本不存在。所以，任何请求都会被允许通过 TCP 过滤器而且你的系统被置于依靠防火墙来提供所有的保护。因为这并不是我们想要的。由于在一开始我们就介绍过的原因，清确保下面两个文件都存在：

```
# ls -l /etc/hosts.allow /etc/hosts.deny

```

两个文件的编写语法规则是一样的：

```
<services> : <clients> [: <option1> : <option2> : ...]
```

在文件中，

1. `services` 指当前规则对应的服务，是一个逗号分割的列表。
2. `clients` 指被规则影响的主机名或者 IP 地址，逗号分割的。下面的通配符也可以接受：
	1. `ALL` 表示所有事物，应用于`clients`和`services`。
	2. `LOCAL` 表示匹配在正式域名中没有完全限定主机名（FQDN）的机器，例如 `localhost`。
	3. `KNOWN` 表示主机名，主机地址，或者用户是已知的（即可以通过 DNS 或其它服务解析到）。
	4. `UNKNOWN` 和 `KNOWN` 相反。
	5. `PARANOID` 如果进行反向 DNS 查找彼此返回了不同的地址，那么连接就会被断开（首先根据 IP 去解析主机名，然后根据主机名去获得 IP 地址）。
3. 最后，一个冒号分割的动作列表表示了当一个规则被触发的时候会采取什么操作。

你应该记住 `/etc/hosts.allow` 文件中允许一个服务接入的规则要优先于 `/etc/hosts.deny` 中的规则。另外还有，如果两个规则应用于同一个服务，只有第一个规则会被纳入考虑。

不幸的是，不是所有的网络服务都支持 TCP 过滤器，为了查看一个给定的服务是否支持他们，可以执行以下命令：

```
# ldd /path/to/binary | grep libwrap
```

如果以上命令执行以后得到了以下结果，那么它就可以支持 TCP 过滤器，`sshd` 和 `vsftpd` 作为例子，输出如下所示。

*查找 TCP 过滤器支持的服务*

### 如何使用 TCP 过滤器来限制服务的权限

当你编辑 `/etc/hosts.allow` 和 `/etc/hosts.deny` 的时候，确保你在最后一个非空行后面通过回车键来添加一个新的行。

为了使得 [SSH 和 FTP](http://www.tecmint.com/block-ssh-and-ftp-access-to-specific-ip-and-network-range/) 服务只允许 `localhost` 和 `192.168.0.102` 并且拒绝所有其他用户，在 `/etc/hosts.deny` 添加如下内容：

```
sshd,vsftpd : ALL
ALL : ALL
```

而且在 `/etc/hosts.allow` 文件中添加如下内容：

```
sshd,vsftpd : 192.168.0.102,LOCAL

```

这些更改会立刻生效并且不需要重新启动。

在下图中你会看到，在最后一行中删掉 `LOCAL` 后，FTP 服务器会对于 `localhost` 不可用。在我们添加了通配符以后，服务又变得可用了。

*确认 FTP 权限*

为了允许所有服务对于主机名中含有 `example.com` 都可用，在 `hosts.allow` 中添加如下一行：

```
ALL : .example.com

```

而为了禁止 `10.0.1.0/24` 的机器访问 `vsftpd` 服务，在 `hosts.deny` 文件中添加如下一行：

```
vsftpd : 10.0.1.

```

在最后的两个例子中，注意到客户端列表每行开头和结尾的**点**。这是用来表示 “所有名字或者 IP 中含有那个字符串的主机或客户端”

这篇文章对你有用吗？你有什么问题或者评论吗？请你尽情在下面留言交流。

---

via: <http://www.tecmint.com/secure-linux-tcp-wrappers-hosts-allow-deny-restrict-access/>

作者：[Gabriel Cánepa](http://www.tecmint.com/author/gacanepa/) 译者：[LinuxBars](https://linuxbar.org/) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


拥有开源项目部门的公司可以从四个方面获益

Summary: 在我的第一篇关于开源项目部门的系列文章中，我深入剖析了什么是开源项目部门，为什么你的公司需要一个开源项目部门。接着我又说到了谷歌是如何创建一种新的开源项目部门的。而这篇文章，我将阐述拥有一个开源项目部门的好处。

可以向360学习啊，免费的逼死一大票收费的

收费才是正常的盈利模式。钱是第一生产力。[无奈]

# 拥有开源项目部门的公司可以从四个方面获益

<img src="content_image/album_201611_11_124802k437372f00fg23ue.png" alt="" title="">

在我的第一篇关于<ruby> 开源项目部门 <rp>  （ </rp> <rt>  program office </rt> <rp>  ） </rp></ruby>的系列文章中，我深入剖析了[什么是开源项目部门，为什么你的公司需要一个开源项目部门](https://opensource.com/business/16/5/whats-open-source-program-office)。接着我又说到了[谷歌是如何创建一种新的开源项目部门的](https://opensource.com/business/16/8/google-open-source-program-office)。而这篇文章，我将阐述拥有一个开源项目部门的好处。

乍一看，非软件开发公司会更加热情的去拥抱开源项目部门的一个重要原因是他们并没有什么损失。毕竟，他们并不需要依靠这些软件产品来获得收益。比如，Facebook 可以很轻易的释放出一个 “分布式键值数据存储” 作为开源项目，是因为他们并没有售卖一个叫做 “企业级键值数据存储” 的产品。这回答了关于风险的问题，但是并没有回答他们如何通过向开源生态共献代码而获益的问题。让我们逐个来推测和探讨其中可能的原因。你会发现开源项目供应商的许多动机都是相同的，但是也有些许不同。

### 招聘

招聘可能是一个将开源项目部门推销给上层管理部门的最容易方法。向他们展示与招聘相关的成本，以及投资回报率，然后解释如何与天才工程师发展关系，从而与那些对这些项目感兴趣并且十分乐意在其中工作的天才开发者们建立联系。不需要我多说了，你懂的！

### 技术影响

曾几何时，那些没有专门从事软件销售的公司是难以直接对他们软件供应商的开发周期施加影响力的，尤其当他们并不是一个大客户时。开源完全改变了这一点，它将用户与供应商放在了一个更公平的竞争环境中。随着开源开发的兴起，任何人，假如他们愿意投入时间和资源的话，都可以将技术推向一个选定的方向。但是这些公司发现，虽然将投资用于开发上会带来丰硕的成果，但是总体战略的努力却更加有效——对比一下 bug 的修复和软件的构建——大多数公司都将 bug 的修复推给上游的开源项目，但是一些公司开始认识到通过更深层次的回报承诺和更快的功能开发来协调持久的工作，将会更有利于业务。通过开源项目部门模式，公司的职员能够从开源社区中准确嗅出战略重心，然后投入开发资源。

对于快速增长的公司，如 Google 和 Facebook，其对现有的开源项目提供的领导力仍然不足以满足业务的膨胀。面对激烈的增长和建立超大规模系统所带来的挑战，许多大型企业开始构建仅供内部使用的高度定制的软件栈。除非他们能说服别人在一些基础设施项目上达成合作。因此，虽然他们保持在诸如 Linux 内核，Apache 和其他现有项目领域的投资，他们也开始推出自己的大型项目。Facebook 发布了 Cassandra，Twitter 创造了 Mesos，并且甚至谷歌也创建了 Kubernetes 项目。这些项目已成为行业创新的主要平台，证实了该举措是相关公司引人注目的成功。（请注意，Facebook 在它需要创造一个新软件项目来解决更大规模的问题之后，已经在内部停止使用 Cassandra 了，但是，这时 Cassandra 已经变得流行，而 DataStax 公司接过了开发任务）。所有这些项目已经促使了开发商、相关的项目、以及最终用户构成的整个生态加速增长和发展。

没有与公司战略举措取得一致的开源项目部门不可能成功的。不这样做的话，这些公司依然会试图单独地解决这些问题，而且更慢。不仅拥有这些项目可以帮助内部解决业务问题，它们也帮助这些公司逐渐成为行业巨头。当然，谷歌成为行业巨头好多年了，但是 Kubernetes 的发展确保了软件的质量，并且在容器技术未来的发展方向上有着直接的话语权，并且远超之前就有的话语权。这些公司目前还是闻名于他们超大规模的基础设施和硅谷的中坚份子。鲜为人知，但是更为重要的是它们与技术生产人员的亲密度。开源项目部门凭借技术建议和与有影响力的开发者的关系，再加上在社区治理和人员管理方面深厚的专业知识来引领这些工作，并最大限度地发挥其影响力，

### 市场营销能力

与技术的影响齐头并进的是每个公司谈论他们在开源方面的努力。通过传播这些与项目和社区有关的消息，一个开源项目部门能够通过有针对性的营销活动来提供最大的影响。营销在开放源码领域一直是一个肮脏的词汇，因为每个人都有一个由企业营销造成的糟糕的经历。在开源社区中，营销呈现出一种与传统方法截然不同的形式，它会更注重于我们的社区已经在战略方向上做了什么。因此，一个开源项目部门不可能去宣传一些根本还没有发布任何代码的项目，但是他们会讨论他们创造什么软件和参与了其他什么举措。基本上，不会有“雾件（vaporware）”。

想想谷歌的开源项目部门作出的第一份工作。他们不只是简单的贡献代码给 Linux 内核或其他项目，他们更多的是谈论它，并经常在开源会议主题演讲。他们不仅仅是把钱给写开源代码的代码的学生，他们还创建了一个全球计划——“Google Summer of Code”，现在已经成为一种开源发展的文化试金石。这些市场营销的作用在 Kubernetes 开发完成之前就奠定了谷歌在开源世界巨头的地位。最终使得，谷歌在创建 GPLv3 授权协议期间拥有重要影响力，并且在科技活动中公司的发言人和开源项目部门的代表人成为了主要人物。开源项目部门是协调这些工作的最好的实体，并可以为母公司提供真正的价值。

### 改善内部流程

改善内部流程听起来不像一个大好处，但克服混乱的内部流程对于每一个开源项目部门都是一个挑战，不论是对软件供应商还是公司内的部门。而软件供应商必须确保他们的流程不与他们发布的产品重叠（例如，不小心开源了他们的商业售卖软件），用户更关心的是侵犯了知识产权（IP）法：专利、版权和商标。没有人想只是因为释放软件而被起诉。没有一个活跃的开源项目部门去管理和协调这些许可和其他法律问题的话，大公司在开源流程和管理上会面临着巨大的困难。为什么这个很重要呢？如果不同的团队释放的软件是在不兼容的许可证下，那么这不仅是一个坑爹的尴尬，它还将对实现最基本的目标改良协作产生巨大的障碍。

考虑到还有许多这样的公司仍在飞快的增长，如果无法建立基本流程规则的话，将可以预见到它们将会遇到阻力。我见过一个罗列着批准、未经批准的许可证的巨大的电子表格，以及指导如何（或如何不）创建开源社区而遵守法律限制。关键是当开发者需要做出决定时要有一个可以依据的东西，并且每次当开发人员想要为一个开源社区贡献代码时，可以不产生大量的法律开销，和效率低下的知识产权检查。

有一个活跃的开放源码项目部门，负责维护许可规则和源的贡献，以及建立培训项目工程师，有助于避免潜在的法律缺陷和昂贵的诉讼。毕竟，良好的开源项目合作可以减少由于某人没有看许可证而导致公司赔钱这样的事件。好消息是，公司已经可以较少的担心关于专有的知识产权与软件供应商冲突的事。坏消息是，它们的法律问题不够复杂，尤其是当他们需要直接面对软件供应商的阻力时。

你的组织是如何受益于拥有一个开源项目部门的？可以在评论中与我们分享。

*本文作者 John Mark Walker 是 Dell EMC 的产品管理总监，负责管理 ViPR 控制器产品及 CoprHD 开源社区。他领导过包括 ManageIQ 在内的许多开源社区。*

---

via: <https://opensource.com/business/16/9/4-big-ways-companies-benefit-having-open-source-program-offices>

作者：[John Mark Walker](https://opensource.com/users/johnmark) 译者：[chao-zhi](https://github.com/chao-zhi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


轻轻几个点击，在 AWS 和 Azure 上搭建 Docker 数据中心

# 轻轻几个点击，在 AWS 和 Azure 上搭建 Docker 数据中心

通过几个点击即可在 “AWS 快速起步”和“Azure 市场”上高效搭建产品级 Docker 数据中心。

通过 AWS 快速起步的 CloudFormation 模板和在 Azure 市场上的预编译模板来部署 Docker 数据中心使得比以往在公有云基础设施下的部署企业级的 CaaS Docker 环境更加容易。

Docker 数据中心 CaaS 平台为各种规模的企业的敏捷应用部署提供了容器和集群的编排和管理，使之更简单、安全和可伸缩。使用新为 Docker 数据中心预编译的云模板，开发者和 IT 运维人员可以无缝的把容器化的应用迁移到亚马逊 EC2 或者微软的 Azure 环境而无需修改任何代码。现在，企业可以快速实现更高的计算和运营效率，可以通过短短几步操作实现支持 Docker 的容器管理和编排。

### 什么是 Docker 数据中心？

Docker 数据中心包括了 <ruby> Docker 通用控制面板 <rp>  （ </rp> <rt>  Docker Universal Control Plane </rt> <rp>  ） </rp></ruby>（UCP），<ruby> Docker 可信注册库 <rp>  （ </rp> <rt>  Docker Trusted Registry </rt> <rp>  ） </rp></ruby>（UTR）和<ruby> 商用版 Docker 引擎 <rp>  （ </rp> <rt>  CS Docker Engine </rt> <rp>  ） </rp></ruby>，并带有与客户的应用服务等级协议相匹配的商业支持服务。

* Docker 通用控制面板（UCP），一种企业级的集群管理方案，帮助客户通过单个管理面板管理整个集群
* Docker 可信注册库（DTR）， 一种镜像存储管理方案，帮助客户安全存储和管理 Docker 镜像
* 商用版的 Docker 引擎

### 在 AWS 上快速布置 Docker 数据中心

秉承 Docker 与 AWS 最佳实践，参照 AWS 快速起步教程来，你可以在 AWS 云上快速部署 Docker 容器。Docker 数据中心快速起步基于模块化和可定制的 CloudFormation 模板，客户可以在其之上增加额外功能或者为自己的 Docker 部署修改模板。

* [AWS 的 Docker 数据中心应用说明](https://youtu.be/aUx7ZdFSkXU)

#### 架构

AWS Cloudformation 的安装过程始于创建 AWS 资源，这些 AWS 需要的资源包括：VPC、安全组、公有与私有子网、因特网网关、NAT 网关与 S3 bucket。

然后，AWS Cloudformation 启动第一个 UCP 控制器实例，紧接着，安装 Docker 引擎和 UCP 容器。它把第一个 UCP 控制器创建的根证书备份到 S3。一旦第一个 UCP 控制器成功运行，其他 UCP 控制器、UCP 集群节点和第一个 DTR 复制的进程就会被触发。和第一个 UCP 控制器节点类似，其他所有节点创建进程也都由商用版 Docker 引擎开始，然后安装并运行 UCP 和 DTR 容器以加入集群。两个弹性负载均衡器（ELB），一个分配给 UCP，另外一个为 DTR 服务，它们启动并自动完成配置来在两个可用区（AZ）之间提供弹性负载均衡。

除这些之外，如有需要，UCP 控制器和节点在 ASG 中启动并提供扩展功能。这种架构确保 UCP 和 DTR 两者都部署在两个 AZ 上以增强弹性与高可靠性。在公有或者私有 HostedZone 上，Route53 用来动态注册或者配置 UCP 和 DTR。

#### 快速起步模板的核心功能如下：

* 创建 VPC、不同 AZ 上的私有和公有子网、ELB、NAT 网关、因特网网关、自动伸缩组，它们全部基于 AWS 最佳实践
* 为 DDC 创建一个 S3 bucket，其用于证书备份和 DTR 映像存储（DTR 需要额外配置）
* 在客户的 VPC 范畴，跨多 AZ 部署 3 个 UCP 控制器
* 创建预配置正常检测的 UCP ELB
* 创建一个 DNS 记录并关联到 UCP ELB
* 创建可伸缩的 UCP 节点集群
* 在 VPC 范畴内，跨多 AZ 创建 3 个 DTR 副本
* 创建一个预配置正常检测的 DTR
* 创建一个 DNS 记录，并关联到 DTR ELB

* [下载 AWS 快速指南](https://s3.amazonaws.com/quickstart-reference/docker/latest/doc/docker-datacenter-on-the-aws-cloud.pdf)

### 在 AWS 使用 Docker 数据中心

1. 登录 [Docker Store](https://store.docker.com/login?next=%2Fbundles%2Fdocker-datacenter%2Fpurchase?plan=free-trial) 获取 [30 天免费试用](https://store.docker.com/login?next=%2Fbundles%2Fdocker-datacenter%2Fpurchase?plan=free-trial)或者[联系销售](https://goto.docker.com/contact-us.html)
2. 确认之后，看到提示“Launch Stack”后，客户会被重定向到 AWS Cloudformation 入口
3. 确认启动 Docker 的 AWS 区域
4. 提供启动参数
5. 确认并启动
6. 启动完成之后，点击输出标签可以看到 UCP/DTR 的 URL、缺省用户名、密码和 S3 bucket 的名称

* [Docker 数据中心需要 2000 美刀信用担保](https://aws.amazon.com/mp/contactdocker/)

### 在 Azure 使用 Azure 市场的预编译模板部署

在 Azure 市场上，Docker 数据中心是一个预先编译的模板，客户可以在 Azure 横跨全球的数据中心即起即用。客户可以根据自己需求从 Azure 提供的各种 VM 中选择适合自己的 VM 部署 Docker 数据中心。

#### 架构

Azure 部署过程始于输入一些基本用户信息，如 ssh 登录的管理员用户名（系统级管理员）和资源组名称。你可以把资源组理解为一组有生命周期和部署边界的资源集合。你可以在这个链接了解更多关于资源组的信息：<http://azure.microsoft.com/en-us/documentation/articles/resource-group-overview/> 。

下一步，输入集群详细信息，包括：UCP 控制器 VM 大小、控制器个数（缺省为 3 个）、UCP 节点 VM 大小、UCP 节点个数（缺省 1，最大值为 10）、DTR 节点 VM 大小、DTR 节点个数、虚拟网络名和地址（例如：10.0.0.1/19）。关于网络，客户可以配置 2 个子网：第一个子网分配给 UCP 控制器 ，第二个分配给 DTC 和 UCP 节点。

最后，点击 OK 完成部署。对于小集群，服务开通需要大约 15-19 分钟，大集群更久些。

#### 如何在 Azure 部署

1. 注册 [Docker 数据中心 30 天试用](https://store.docker.com/login?next=%2Fbundles%2Fdocker-datacenter%2Fpurchase?plan=free-trial)许可或者[联系销售](https://goto.docker.com/contact-us.html)
2. [跳转到微软 Azure 市场的 Docker 数据中心](https://azure.microsoft.com/en-us/marketplace/partners/docker/dockerdatacenterdocker-datacenter/)
3. [查看部署文档](https://success.docker.com/Datacenter/Apply/Docker_Datacenter_on_Azure)

---

通过注册获取 Docker 数据中心许可证开始，然后你就能够通过 AWS 或者 Azure 模板搭建自己的数据中心。

* [获取 30 天试用许可证](http://www.docker.com/trial)
* [通过视频理解 Docker 数据中心架构](https://www.youtube.com/playlist?list=PLkA60AVN3hh8tFH7xzI5Y-vP48wUiuXfH)
* [观看演示视频](https://www.youtube.com/playlist?list=PLkA60AVN3hh8a8JaIOA5Q757KiqEjPKWr)
* [获取 AWS 提供的部署 Docker 数据中心的 75 美元红包奖励](https://aws.amazon.com/quickstart/promo/)

了解有关 Docker 的更多信息：

* 初识 Docker? 尝试一下 10 分钟[在线学习课程](https://docs.docker.com/engine/understanding-docker/)
* 分享镜像，自动构建，或用一个[免费的 Docker Hub 账号](https://hub.docker.com/)尝试更多
* 阅读 [Docker 1.12 发行说明](https://docs.docker.com/release-notes/)
* 订阅 [Docker Weekly](https://www.docker.com/subscribe_newsletter/)
* 报名参加即将到来的 [Docker Online Meetups](http://www.meetup.com/Docker-Online-Meetup/)
* 参加即将发生的 [Docker Meetups](https://www.docker.com/community/meetup-groups)
* 观看 [DockerCon EU2015](https://www.youtube.com/playlist?list=PLkA60AVN3hh87OoVra6MHf2L4UR9xwJkv)视频
* 开始为 [Docker](https://docs.docker.com/contributing/contributing/) 贡献力量

---

via: <https://blog.docker.com/2016/06/docker-datacenter-aws-azure-cloud/>

作者：[Trisha McCanna](https://blog.docker.com/author/trisha/) 译者：[firstadream](https://github.com/firstadream) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


98% 的开发者在工作中使用了开源软件

Summary: Git 仓库管理软件 GitLab 进行了一项调查披露了一些关于开源接受度的有趣事实。针对开发人员群体的调查表明 98% 的开发者更喜欢在工作中使用开源。

很显然，参与这份调查的51%的人是前端，整天就知道在网上瞎转悠

不科学，竟然没有C/C++

我很好奇，那2%完全不用开源软件的都是用的什么

MS or Oracle 全套？

不知道，我不是开发者

# 98% 的开发者在工作中使用了开源软件

<img src="content_image/album_201611_12_163841xh9z0bclexlpt0n0.jpg" alt="developer using open source" title="developer using open source">

开源每天都会达到新的高度。但是一个新的研究表明超过 98% 的开发者在工作中使用开源工具。

Git 仓库管理软件 [GitLab](https://about.gitlab.com/2016/11/02/global-developer-survey-2016/) 进行了一项调查披露了一些关于开源接受度的有趣事实。针对开发人员群体的调查表明 98% 的开发者更喜欢在工作中使用开源，91% 选择在工作和个人项目中选择使用相同的开发工具。此外，92% 的人认为分布式版本控制系统（Git 仓库）在工作中很重要。

在所有的偏好编程语言中，JavaScript 占了 51% 的受访者比例。它后面是 Python、PHP、Java、Swift 和Objective-C。86% 的开发者认为安全是代码的主要判断标准。

GitLab 首席执行官兼联合创始人 Sid Sijbrandij 在一次声明中表示：“尽管过程驱动的开发技术在过去已经取得了成功，但开发人员正在寻找一种更自然的软件开发革新以促进项目生命周期内的协作和信息共享。”

这份报告来自 GitLab 在 7 月 6 日和 27 日之间对使用其存储库平台的 362 家初创企业和企业的 CTO、开发人员和 DevOps 专业人士的调查。

---

via: <http://opensourceforu.com/2016/11/98-percent-developers-use-open-source-at-work/>

作者：[JAGMEET SINGH](http://opensourceforu.com/author/jagmeet-singh/) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


用 dpkg 命令在 Debian 系的 Linux系统中管理软件包

Summary: dpkg 是一个可以安装、构建、删除及管理 Debian 软件包的命令行工具。

红帽系对应的是 rpm。

dpkg -l | grep linux-image

先列出所有已安裝packages然後再用grep擷取關鍵詞

gdebi path-to-.deb-file

命令列安裝.deb檔案

gdebi-gtk path-to-.deb-file

GUI安裝.deb檔案

dpkg 是一个可以安装、构建、删除及管理 Debian 软件包的命令行工具。

# 用 dpkg 命令在 Debian 系的 Linux系统中管理软件包

[dpkg](https://wiki.debian.org/Teams/Dpkg) 意即 <ruby> Debian 包管理器 <rp>  （ </rp> <rt>  Debian PacKaGe manager </rt> <rp>  ） </rp></ruby>。dpkg 是一个可以安装、构建、删除及管理 Debian 软件包的命令行工具。dpkg 将 Aptitude（首选而更用户友好）作为执行所有操作的前端界面。

<img src="content_image/album_201611_12_174052l7qt9x7pbx1zbrox.jpg" alt="" title="">

其它的一些工具如 dpkg-deb 和 dpkg-query 等也使用 dpkg 作为执行某些操作的前端。

现在大多数系统管理员使用 Apt、[Apt-Get](http://www.2daygeek.com/apt-get-apt-cache-command-examples/) 及 Aptitude 等工具，不用费心就可以轻松地管理软件。

尽管如此，必要的时候还是需要用 dpkg 来安装某些软件。其它的一些在 Linux 系统上广泛使用的包管理工具还有 [yum](/article-2272-1.html)、[dnf](/article-5718-1.html)、[apt-get](/article-4933-1.html)、[rpm](/article-7455-1.html)、[Zypper](/article-5606-1.html)、pacman、urpmi 等等。

现在，我要在装有 Ubuntu 15.10 的机器上用一些实例讲解最常用的 dpkg 命令。

### 1) dpkg 常见命令的语法及 dpkg 文件位置

下面是 dpkg 常见命令的语法及 dpkg 相关文件的位置，如果想深入了解，这些对你肯定大有益处。

```
### dpkg 命令的语法
$ dpkg -[command] [.deb package name]

$ dpkg -[command] [package name]

### dpkg 相关文件的位置
$ /var/lib/dpkg

### 这个文件包含了被 dpkg 命令（install、remove 等）所修改的包的信息
$ /var/lib/dpkg/status

### 这个文件包含了可用包的列表
$ /var/lib/dpkg/status

```

### 2) 安装/升级软件

在基于 Debian 的系统里，比如 Debian、Mint、Ubuntu 和 elementryOS，用以下命令来安装/升级 .deb 软件包。这里我要用 `atom-amd64.deb` 文件安装 Atom。要是已经安装了 Atom，就会升级它。要么就会安装一个新的 Atom。

```
### 安装或升级 dpkg 软件包
$ sudo dpkg -i atom-amd64.deb
Selecting previously unselected package atom.
(Reading database ... 426102 files and directories currently installed.)
Preparing to unpack atom-amd64.deb ...
Unpacking atom (1.5.3) over (1.5.3) ...
Setting up atom (1.5.3) ...
Processing triggers for gnome-menus (3.13.3-6ubuntu1) ...
Processing triggers for bamfdaemon (0.5.2~bzr0+15.10.20150627.1-0ubuntu1) ...
Rebuilding /usr/share/applications/bamf-2.index...
Processing triggers for desktop-file-utils (0.22-1ubuntu3) ...
Processing triggers for mime-support (3.58ubuntu1) ...

```

### 3) 从文件夹里安装软件

在基于 Debian 的系统里，用下列命令从目录中逐个安装软件。这会安装 `/opt/software` 目录下的所有以 .deb 为后缀的软件。

```
$ sudo dpkg -iR /opt/software
Selecting previously unselected package atom.
(Reading database ... 423303 files and directories currently installed.)
Preparing to unpack /opt/software/atom-amd64.deb ...
Unpacking atom (1.5.3) ...
Setting up atom (1.5.3) ...
Processing triggers for gnome-menus (3.13.3-6ubuntu1) ...
Processing triggers for bamfdaemon (0.5.2~bzr0+15.10.20150627.1-0ubuntu1) ...
Rebuilding /usr/share/applications/bamf-2.index...
Processing triggers for desktop-file-utils (0.22-1ubuntu3) ...
Processing triggers for mime-support (3.58ubuntu1) ...
```

### 4) 显示已安装软件列表

以下命令可以列出 Debian 系的系统中所有已安装的软件，同时会显示软件版本和描述信息。

```
$ dpkg -l
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                        Version                            Architecture Description
+++-===========================-==================================-============-================================================================
ii  account-plugin-aim          3.12.10-0ubuntu2                   amd64        Messaging account plugin for AIM
ii  account-plugin-facebook     0.12+15.10.20150723-0ubuntu1       all          GNOME Control Center account plugin for single signon - facebook
ii  account-plugin-flickr       0.12+15.10.20150723-0ubuntu1       all          GNOME Control Center account plugin for single signon - flickr
ii  account-plugin-google       0.12+15.10.20150723-0ubuntu1       all          GNOME Control Center account plugin for single signon
ii  account-plugin-jabber       3.12.10-0ubuntu2                   amd64        Messaging account plugin for Jabber/XMPP
ii  account-plugin-salut        3.12.10-0ubuntu2                   amd64        Messaging account plugin for Local XMPP (Salut)
.
.

```

### 5) 查看指定的已安装软件

用以下命令列出指定的一个已安装软件，同时会显示软件版本和描述信息。

```
$ dpkg -l atom
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name       Version   Architecture        Description
+++-==========-=========-===================-============================================
ii  atom       1.5.3     amd64               A hackable text editor for the 21st Century.

```

### 6) 查看软件安装目录

以下命令可以在基于 Debian 的系统上查看软件的安装路径。

```
$ dpkg -L atom
/.
/usr
/usr/bin
/usr/bin/atom
/usr/share
/usr/share/lintian
/usr/share/lintian/overrides
/usr/share/lintian/overrides/atom
/usr/share/pixmaps
/usr/share/pixmaps/atom.png
/usr/share/doc

```

### 7) 查看 deb 包内容

下列命令可以查看 deb 包内容。它会显示 .deb 包中的一系列文件。

```
$ dpkg -c atom-amd64.deb
drwxr-xr-x root/root         0 2016-02-13 02:13 ./
drwxr-xr-x root/root         0 2016-02-13 02:13 ./usr/
drwxr-xr-x root/root         0 2016-02-13 02:13 ./usr/bin/
-rwxr-xr-x root/root      3067 2016-02-13 02:13 ./usr/bin/atom
drwxr-xr-x root/root         0 2016-02-13 02:13 ./usr/share/
drwxr-xr-x root/root         0 2016-02-13 02:13 ./usr/share/lintian/
drwxr-xr-x root/root         0 2016-02-13 02:13 ./usr/share/lintian/overrides/
-rw-r--r-- root/root       299 2016-02-13 02:13 ./usr/share/lintian/overrides/atom
drwxr-xr-x root/root         0 2016-02-13 02:13 ./usr/share/pixmaps/
-rw-r--r-- root/root    643183 2016-02-13 02:13 ./usr/share/pixmaps/atom.png
drwxr-xr-x root/root         0 2016-02-13 02:13 ./usr/share/doc/
.
.
```

### 8) 显示软件的详细信息

以下命令可以显示软件的详细信息，如软件名、软件类别、版本、维护者、软件架构、依赖的软件、软件描述等等。

```
$ dpkg -s atom
Package: atom
Status: install ok installed
Priority: optional
Section: devel
Installed-Size: 213496
Maintainer: GitHub <atom@github.com>Architecture: amd64
Version: 1.5.3
Depends: git, gconf2, gconf-service, libgtk2.0-0, libudev0 | libudev1, libgcrypt11 | libgcrypt20, libnotify4, libxtst6, libnss3, python, gvfs-bin, xdg-utils, libcap2
Recommends: lsb-release
Suggests: libgnome-keyring0, gir1.2-gnomekeyring-1.0
Description: A hackable text editor for the 21st Century.
 Atom is a free and open source text editor that is modern, approachable, and hackable to the core.</atom@github.com>
```

### 9) 查看文件属于哪个软件

用以下命令来查看文件属于哪个软件。

```
$ dpkg -S /usr/bin/atom
atom: /usr/bin/atom

```

### 10) 移除/删除软件

以下命令可以用来移除/删除一个已经安装的软件，但不删除配置文件。

```
$ sudo dpkg -r atom
(Reading database ... 426404 files and directories currently installed.)
Removing atom (1.5.3) ...
Processing triggers for gnome-menus (3.13.3-6ubuntu1) ...
Processing triggers for bamfdaemon (0.5.2~bzr0+15.10.20150627.1-0ubuntu1) ...
Rebuilding /usr/share/applications/bamf-2.index...
Processing triggers for desktop-file-utils (0.22-1ubuntu3) ...
Processing triggers for mime-support (3.58ubuntu1) ...

```

### 11) 清除软件

以下命令可以用来移除/删除包括配置文件在内的所有文件。

```
$ sudo dpkg -P atom
(Reading database ... 426404 files and directories currently installed.)
Removing atom (1.5.3) ...
Processing triggers for gnome-menus (3.13.3-6ubuntu1) ...
Processing triggers for bamfdaemon (0.5.2~bzr0+15.10.20150627.1-0ubuntu1) ...
Rebuilding /usr/share/applications/bamf-2.index...
Processing triggers for desktop-file-utils (0.22-1ubuntu3) ...
Processing triggers for mime-support (3.58ubuntu1) ...

```

### 12) 了解更多

用以下命令来查看更多关于 dpkg 的信息。

```
$ dpkg -help
或
$ man dpkg

```

开始体验 dpkg 吧。

---

via: <http://www.2daygeek.com/dpkg-command-examples/>

作者：[MAGESH MARUTHAMUTHU](http://www.2daygeek.com/author/magesh/) 译者：[GitFuture](https://github.com/GitFuture) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出