浅谈配置文件格式

Summary: 流行的配置文件格式有若干种，每种都有其自身优势。从中找到最适合你的格式吧！

流行的配置文件格式有若干种，每种都有其自身优势。从中找到最适合你的格式吧！

>
> 流行的配置文件格式有若干种，每种都有其自身优势。从中找到最适合你的格式吧！
>
>
>

![](/data/attachment/album/202109/29/134955py5ypl58dgplyx4x.jpg "Computer screen with files or windows open")

计算机上有数以千计的配置文件。你可能永远不会直接与其中的大部分文件打交道，但它们确实散落在你的 `/etc` 以及 `~/.config`、`~/.local`、`/usr` 文件夹中。还有一些可能在 `/var`，甚至 `/opt` 文件夹中。如果无意中打开过或更改过它们，你就可能会有疑问：为什么有些配置文件看起来是某一种格式，而另一些则是看起来完全不同的格式？

存储配置是一项很灵活的任务，因为只要开发人员知道他们的代码是如何将数据存入文件的，他们就可以轻松编写代码来根据需要提取数据。然而，科技行业非常青睐有详细文档的标准化事物，因此多年来出现了几种比较普遍的格式用来简化配置任务。

### 为什么我们需要配置文件

配置文件对于现代计算来说很重要。它们使你能够自定义与应用程序交互的方式，或自定义应用程序与系统内其他程序的交互方式。有了配置文件，每当你启动某个应用程序时，它都会有“记忆”，记录了你喜欢如何去使用该程序。

配置文件的结构可以很简单，而且通常确实也很简单。例如，如果你要编写一个应用程序，程序唯一需要知道的是其用户的偏好名字，那么它的唯一配置文件就可以只包含一个词：用户名。就像下面这样：

```
Tux

```

但通常应用程序需要追踪的不仅仅是一条信息，因此配置文件通常会有一个键和一个值：

```
NAME='Tux'
SPECIES='Penguin'

```

即使没有编程经验，你也可以想象出代码如何解析这些数据。这里有两个简单的例子，一个使用 [awk 命令](https://opensource.com/article/20/9/awk-ebook)，另一个使用 [grep 命令](https://opensource.com/downloads/grep-cheat-sheet)。两个例子都是只关注包含 `NAME` “键”的行，并返回出现在等号 (`=`) 之后的“值”：

```
$ awk -F'=' '/NAME/ { print $2; }' myconfig.ini
'Tux'
$ grep NAME fake.txt | cut -d'=' -f2
'Tux'

```

同样的原则适用于任何编程语言和任何配置文件。只要你有统一的数据结构，就可以在需要的时候编写简单的代码来提取和解析它。

### 选择格式

为了保证普遍有效性，配置文件最重要的一点是它们是一致的和可预测的。你绝对不会想做这样的事：以保存用户首选项的名义，将信息随意存储到文件中，然后花好几天时间逆向工程，来找到最终出现在文件中的随机信息。

流行的配置文件格式有若干种，每种格式都有自己的优势。

#### INI

INI 文件采用了键值对的格式：

```
[example]
name=Tux
style=widgety,fidgety
enabled=1

```

这种简单的配置风格很直观，只要你别选择使用糟糕的键名（比如用 `unampref` 这样的神秘键名来代替 `name`）就好。这些键值对很容易解析和编辑。

除了键和值之外，INI 格式还可以分 <ruby> 节 <rt>  section </rt></ruby>。在下列示例代码中，`[example]` 和 `[demo]` 就是配置文件中的两个节：

```
[example]
name=Tux
style=widgety,fidgety
enabled=1

[demo]
name=Beastie
fullscreen=1

```

这几个配置语句解析起来有点复杂，因为有两个 `name` 键。想象一下，一个粗心的程序员在这个配置文件中查询 `name`，结果总是返回 `Beastie`，因为这是文件中对 `name` 的最后一个定义值。在解析这样的文件时，开发人员必须加倍小心地在各节中搜索键，这可能会很棘手，具体取决于用来解析该文件的语言。然而，它仍然是一种很流行的格式，大多数语言都会有一个现成的库来帮助程序员解析 INI 文件。

#### YAML

[YAML 文件](https://www.redhat.com/sysadmin/yaml-beginners) 是结构化列表，可以包含值或者键值对：

```
---
Example:
  Name: 'Tux'
  Style:
   - 'widgety'
    - 'fidgety'
  Enabled: 1

```

YAML 格式很流行，部分原因是它看起来很整洁。数据要放置到相对其上层数据的特定位置，除此之外没有太多其他语法。然而，对于某些人来说的这种特色，在其他人眼中可能就是一个问题。许多开发人员不愿使用 YAML，正是因为它很看重本质上 *不存在* 的东西。如果你在 YAML 中缩进错误，YAML 解析器可能会将你的文件视为无效文件，即使不视为无效，返回的数据也可能是错误的。

大多数语言都有 YAML 解析器，并且有很好的开源 YAML linters（验证语法的应用程序）来帮你确保 YAML 文件的完整性。

#### JSON

JSON 文件在技术上来说是 YAML 的子集，因此其数据结构是相同的，尽管其语法完全不同：

```
{
  "Example": {
    "Name": [
      "Tux"
    ],
    "Style": [
      "widgety",
      "fidgety"
    ],
    "Enabled": 1
  }
}

```

JSON 在 JavaScript 程序员中很流行，这并不奇怪，因为 JSON 全称为<ruby> JavaScript 对象符号 <rt>  JavaScript Object Notation </rt></ruby>。由于与 Web 开发密切相关，JSON 是 Web API 的常见输出格式。大多数编程语言都有解析 JSON 的库。

#### XML

XML 使用标签作为键，将配置值围绕起来：

```
<example>
  <name>Tux</name>
  <style priority="user">widgety</style>
  <style priority="fallback">fidgety</style>
  <enabled>1</enabled>
</example>

```

XML 经常被 Java 程序员使用，Java 有一套丰富的 XML 解析器。虽然 XML 以非常严格而著称，但同时也非常灵活。与有一系列特定标签的 HTML 不同，XML 中可以随意发明自己的标签。只要始终坚持相同的构建规则，并有一个良好的库来解析它，你就可以准确而轻松地提取数据。

有一些很好的开源 linter 可以帮你验证 XML 文件，并且大多数编程语言都提供用于解析 XML 的库。

#### 二进制格式

Linux 以纯文本配置为傲。这样做的优点是可以使用 [cat](https://opensource.com/article/19/2/getting-started-cat-command) 等基本工具查看配置数据，甚至可以使用你 [最喜欢的文本编辑器](https://opensource.com/article/21/2/open-source-text-editors) 来编辑配置。

但是，某些应用程序使用二进制格式配置，就意味着数据以某种非自然语言的格式进行编码。这些文件通常需要一个特殊的应用程序（通常是它们要配置的应用程序）来解释它们的数据。你无法查看这些文件，至少无法以任何有意义的方式查看，并且无法在其宿主应用程序之外编辑它们。选用二进制格式的一些原因如下：

* **速度：** 程序员可以使用自定义符号在二进制配置文件中的某些点注册特定的信息位。提取数据时不涉及搜索，因为所有内容都已标注了索引。
* **大小：** 文本文件可能会变大，如果选择压缩文本文件，实际上是在将其转换为二进制格式。二进制文件可以通过编码技巧变得更小（文本文件也是如此，但在某些时候，你的优化会使数据变得晦涩，以至于文件也成了二进制文件）。
* **晦涩：** 一些程序员甚至不希望人们查看他们的配置文件，因此将它们编码为二进制数据。这通常只会让用户感到沮丧，并不是使用二进制格式的好理由。

如果必须使用二进制格式进行配置，请使用已作为开放标准存在的格式，例如 [NetCDF](https://www.unidata.ucar.edu/software/netcdf/)。

### 找到有效的配置格式

配置格式帮助开发人员存储应用程序所需的数据，并帮助用户存储他们希望应用程序如何操作的偏好项。对于应该使用什么格式的问题，可能没有错误的答案，只要你觉得所使用的语言能很好地支持就可以。在开发应用程序时，查看可用格式，用一些样例数据建模，查看和评估你的编程语言提供的库和实用程序，然后选择你觉得最合适的一种格式吧。

---

via: <https://opensource.com/article/21/6/what-config-files>

作者：[Seth Kenlon](https://opensource.com/users/seth) 选题：[lujun9972](https://github.com/lujun9972) 译者：[unigeorge](https://github.com/unigeorge) 校对：[wxy](https://github.com/wxy)

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


微软 Exchange 的一个开源替代方案

Summary: 开源用户现在有了一个强大的、功能齐全的群件选择。

开源用户现在有了一个强大的、功能齐全的群件选择。

>
> 开源用户现在有了一个强大的、功能齐全的群件选择。
>
>
>

![](/data/attachment/album/202109/29/141404pesw8xgh8oohwhjh.jpg "Working on a team, busy worklife")

多年来，微软 Exchange 作为一个平台牢牢统治着群件环境。然而，在 2020 年末，一个奥地利的开源软件开发商推出了 [grommunio](https://grommunio.com/en/)，这是一个群件服务器和客户端，其外观和感觉让 Exchange 和 Outlook 用户感到很熟悉。

grmmunio 项目可以很好地替代 Exchange。开发者以与微软相同的方式将组件连接到平台上，它们支持 RPC （远程过程调用）与 HTTP 协议。据开发者介绍，grommunio 还包括许多常见的群件接口，如 IMAP、POP3、SMTP、EAS（Exchange ActiveSync）、EWS（Exchange Web Services）、CalDAV 和 CardDAV。有了这样广泛的支持，grommunio 可以顺利地整合到现有的基础设施中。

用户会注意到对 Outlook、Android 和 iOS 客户端来说几乎没有区别。当然，作为开源软件，它也支持其他客户端。由于集成了原生的 Exchange 协议，Outlook 和智能手机与 grommunio 的通信就像与微软 Exchange 服务器一样。日常的企业用户可以继续使用他们现有的客户端，而 grommunio 服务器则在后台安静地运行。

### 不仅仅是邮件

除了邮件功能外，grommunio 界面还提供了日历系统。可以直接在日历显示中或在一个新标签中点击创建约会。这很直观，正如你对现代工具的期望。用户可以创建、管理和分享日历以及地址簿。私人联系人或普通联系人都支持，而且你可以与同事分享一切。

任务管理在左边的下拉菜单中显示任务列表，它们可以有一个所有者和多个合作者。你可以为每个任务指定截止日期、类别、附件和其他属性。笔记可以以同样的方式被管理并与其他团队成员共享。

### 聊天、视频会议和文件同步

除了现代群件的所有标准功能外，grommunio 还提供聊天、视频会议和文件同步功能。它为企业实现了大规模的全面整合，具有极高的性能。对于开源的推动者来说，这是一个简单的选择，对于系统管理员来说，这是一个强大的选择。因为 grommunio 的目标是整合而不是重新发明，所以所有的组件都是标准的开源工具。

![Screenshot of grommunio meeting space](/data/attachment/album/202109/29/141414b6abaz3e6zgnc3g2.png "grommunio meeting space")

*用于高级视频会议的 Jitsi 集成（Markus Feilner, [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)）*

在 grommunio 会议功能的背后是 [Jitsi](https://opensource.com/article/20/5/open-source-video-conferencing)，它以熟悉的用户界面顺利地集成到 grommunio 的用户界面中。完全集成和集中管理的聊天功能是基于 [Mattermost](https://opensource.com/education/16/3/mattermost-open-source-chat)。

![Screenshot of grommunio's town square for chat](/data/attachment/album/202109/29/141414nq2317pfkx3jqjz4.png "grommunio's town square for chat")

*用于聊天的 Mattermost（Markus Feilner，[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)）*

[ownCloud](https://owncloud.com/) 承诺提供企业级的文件共享和同步，在点击“文件”按钮后开始。

![Screenshot of grommunio file sharing space](/data/attachment/album/202109/29/141415a1mpe9o5lb17ml27.png "Owncloud for file synchronization and exchange")

*用于文件同步和交换的 ownCloud（Markus Feilner，[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)）*

grommunio 项目有一个强大的管理界面，包括角色、域和组织管理、预测性监测和自助服务门户。基于 shell 的向导指导管理员完成安装和从微软 Exchange 迁移数据。开发团队正在不断努力，以实现更好的整合和更集中的管理，并随之为管理员提供更好的工作流程。

![Screenshot of grommunio dashboards](/data/attachment/album/202109/29/141415z0b0rz5fcb75bl9s.png "Screenshot of grommunio dashboards")

*grommunio 的管理界面（Markus Feilner, [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)）*

### 探索 grommunio

grommunio 项目的目标很高，但它的开发者已经付出了努力，这一点很明显。一家专门从事税务咨询的德国托管服务机构最近宣布，他们的客户可以使用 grommunio，这是一个德国数据保护法特别严厉的行业。grommunio 项目做得很好：将现有的、成功的概念干净地结合在一起，实现了开放、安全和符合隐私的通信。

---

via: <https://opensource.com/article/21/9/open-source-groupware-grommunio>

作者：[Markus Feilner](https://opensource.com/users/mfeilner) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

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


硬核观察 #409 Stack Overflow 的“复制粘贴键盘”

Summary: 更多：• 广告主投诉 Chrome 取消 Cookie 影响行业营收 • AI 判断一副价值 660 万英镑的名画是赝品

但也要防范该AI模型是否被植入了后门，毕竟后门攻击可以让模型在特定的输入时输出开发者选择的结果

艺术作品也需要打码吗? 貌似知乎是可以发的?

表情符号被过滤掉了。。。

emmmmmmm

emmmmmmm

![](/data/attachment/album/202109/29/165145xbgbhuoubhr33bb5.jpg)

### Stack Overflow 的“复制粘贴键盘”

![](/data/attachment/album/202109/29/165156dwqv4jtcgw9wwr5c.jpg)

Stack Overflow 今年的愚人节的[恶作剧](https://stackoverflow.blog/2021/03/31/the-key-copy-paste/)说，它将限制对其网站的免费访问，除非人们购买“钥匙”，一个带有打开 Stack Overflow、复制和粘贴的按钮的设备。但是大家似乎对这个键盘很感兴趣，于是在一位键盘爱好者和定制键盘制造商 Drop 的帮助下，Stack Overflow 设计了一把真正的键盘，并开始以 [29 美元](https://drop.com/buy/stack-overflow-the-key-macropad)的价格出售。

>
> 老王点评：这样的键盘，你想不想要一块？你值得拥有~
>
>
>

### 广告主投诉 Chrome 取消 Cookie 影响行业营收

![](/data/attachment/album/202109/29/165220um7gwkbgmyzg1dta.jpg)

代表广告主、出版商和科技公司的行业组织“开放网络运动”今日向欧盟投诉谷歌，称谷歌 Chrome 浏览器取消 Cookie 的计划是一种反垄断行为，将严重影响广告主和出版商的营收。谷歌去年年初宣布，未来两年内将逐步淘汰对第三方 Cookie 的支持，并计划推广其“隐私沙箱”技术。但出版商和广告技术公司纷纷抱怨称，谷歌所谓的“隐私沙箱”将限制他们收集网络用户信息的能力，从而影响他们提供更具价值广告的能力。

>
> 老王点评：套用一句俗话，“和尚摸得，我也摸得”。各位广告商在想，凭啥你谷歌一个人可以摸，不让我们摸。
>
>
>

### AI 判断一副价值 660 万英镑的名画是赝品

![](/data/attachment/album/202109/29/165246je8cz7o6mmdm68m8.jpg)

英国国家美术馆最重要的藏品之一鲁本斯的画《参孙与大利拉》，是 40 年前在佳士得拍卖会上以当时创纪录的价格 250 万英镑购买的，相当于今天的 660 万英镑。批评家们长期以来一直认为这幅画并非真正出自鲁本斯之手。而现在，一系列采用突破性人工智能技术的科学测试得出了[结论](https://www.theguardian.com/artanddesign/2021/sep/26/was-famed-samson-and-delilah-really-painted-by-rubens-no-says-ai)。在与 148 幅无争议的鲁本斯画作进行比较后，它不是鲁本斯的原创作品的概率为 91.78%，而与之对比的是，另外一幅英国国家美术馆收藏的画作则有 98.76% 的概率是真品。

>
> 老王点评：AI 技术的进步，往往能做到很多人类做不到和做不好的事情，我认为这才是 AI 真正该发挥作用的领域。
>
>
>


5 个可以替代 Zoom 的开源软件

Summary: 试试这些开源视频会议服务之一。

试试这些开源视频会议服务之一。

>
> 试试这些开源视频会议服务之一。
>
>
>

![](/data/attachment/album/202109/30/114413ylalu7dcl5au575v.jpg "Two people chatting via a video conference app")

我最近参加了 [实用开源信息（POSI）](https://opensource.org/posicfp) 会议，它是在一个自由开源的视频会议平台上举行的。当我参加了一系列关于开源软件的实际用途的精彩讲座时，我意识到视频会议在过去几年里已经变得非常普遍。

如果说开源做了什么，那就是提供了选择，现在越来越多的工人有了远程工作的自由，在你的连接方式上有一个选择是非常有意义的。

有时，你需要一个全功能的视频会议应用，其中包括审核、演示模式和分组讨论室，而其他时候，你想做的只是给朋友打一个快速电话，以便看到对方的脸。

### Jitsi

[Jitsi](http://jitsi.org) 是一个简单、随意、但强大的视频通话平台。你可以自己托管它，也可以在 [meet.jit.si](http://meet.jit.si) 的公共实例上使用它。它有可定制的 URL，可以很容易地与你想见面的朋友分享链接、通话中的聊天、管理控制，和通话录音。它的开发非常活跃，每年都会测试和发布一大批新功能。它是 [Opensource.com](http://Opensource.com) 用于我们每周会议的平台。

### Signal

[Signal](https://signal.org) 已经是一个流行的以安全为重点的聊天应用，最近它又增加了 [团体视频通话](https://support.signal.org/hc/en-us/articles/360052977792-Group-Calling-Voice-or-Video-with-Screen-Sharing) 的功能。视频通话非常适合简单的会议，而且因为你只能与你的 Signal 联系人列表中的其他人见面，所以不用担心你的视频通话会议上有不速之客。当你尝试定位你应该参与开会的虚拟房间时，也没有来回的“电话标签”。这一切都发生在 Signal 中，因此无需猜测。

Signal 本身是非常直观的，视频功能非常适合其现有惯例。简而言之，只要你的联系人在使用 Signal，这就是一个不费力的视频通话平台。这是我用于个人联系的应用，我经常使用其视频通话功能与朋友和家人联系。

### P2p.chat

[P2p.chat](https://p2p.chat/) 是这些中最简单的一个，无论是设计还是实现。通过 WebRTC 工作，p2p.chat 是一个 Web 应用，允许你直接连接到你正在呼叫的人，而不需要托管服务器。p2p.chat 的界面并不多，但这也是其吸引力的另一部分。没有管理控制或演示模式，因为 p2p.chat 在很大程度上是科幻片中承诺的“视频电话”：与远方的人进行轻松的人对人（或人们对人们）视频通话。

你可以使用自定义的 URL 来动态地创建一个会议空间，所以它们相对容易记忆（除了小的随机部分）和输入。我和不在 Signal 上的朋友使用 p2p.chat，它从未让我失望过。

### BigBlueButton

如果你需要严格的管理控制和极端的灵活性，[BigBlueButton](https://bigbluebutton.org/) 是你正在寻找的解决方案，它专为教室、会议和演讲而设计。有了 BigBlueButton，你可以让所有与会者静音，阻止和踢走一个与会者，创建分组讨论室，创建协作式白板，共享屏幕，进行演讲，以及记录会议。与会者可以“举起手”表示注意，并将他们的状态设定为非语言交流方式。它很容易使用，但它是一个严肃的平台，适用于重点和非常大的群体。我参加过一些使用 BigBlueButton 的技术会议，包括 [实用开源信息（POSI）](https://opensource.org/posicfp)会议。

### Wire

[Wire](https://wire.com/en/) 是寻找托管视频聊天和群件客户端的企业客户的绝佳选择。它是 [AGPL](https://opensource.org/licenses/AGPL-3.0) 许可，这个开源项目可用于桌面和服务器、安卓和 iOS。它具有视频通话、发信和文件共享的功能，因此，即使是远程会议，基本上也有亲自开会的所有便利。你可以在有限的时间内免费试用 Wire，然后为你的公司购买一份支持合同。另外，你也可以自己托管它。

### 开源视频聊天

没有理由满足于由你可能不完全信任的公司托管的专有视频通话。今天可用的开源选项对于与你的职业和个人生活中的所有人保持联系是非常好的。下次你想和朋友见面时，不妨试试这些解决方案之一。

---

via: <https://opensource.com/article/21/9/alternatives-zoom>

作者：[Seth Kenlon](https://opensource.com/users/seth) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

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


用 Foreman 管理 CentOS Stream

我个人还是觉得Debian Gnu/Linux的全功能组件ISO对于用户最方便！！！尤其在非互联网环境下！！！

>
> 这个例子让我们看到了在 Foreman 中管理和配置 CentOS Stream 内容的许多选项。
>
>
>

![](/data/attachment/album/202109/30/133541s3e3s31yzmnugl1s.jpg "Puzzle pieces coming together to form a computer screen")

2021 年 12 月，CentOS 8 将达到生命终点，被 CentOS Stream 取代。CentOS Stream 和 CentOS 之前的迭代之间的主要变化之一是没有小版本。Centos Stream 采用了一个连续的发布周期。从今年年初开始，Foreman 社区的开发者开始看到 CentOS Stream 由于持续发布而提供的更早的错误检测和补丁的好处。我们不再需要等待下一个版本来利用最新的变化和错误修复。[一位资深的 Linux 社区爱好者](https://twitter.com/Det_Conan_Kudo/status/1337366036023218177?s=20) 指出，此举也使 RHEL 开发者比以往更接近 FOSS 社区。

然而，如果你是一个拥有数百或数千台服务器的管理员，你可能想控制新的软件包何时被添加到特定的服务器。如果你正在寻找一个免费的开源工具，帮助你确保生产服务器的稳定性，同时允许你安全地从 Centos Stream 中拉入最新的变化用于开发和测试，这就是 Foreman 可以帮助你的地方。有了 Foreman，你可以在生命周期环境的各个阶段管理你的 Centos Stream 内容。

### Foreman 介绍

Foreman 是一个完整的物理和虚拟服务器的生命周期管理工具。有了 Foreman，系统管理员有能力轻松实现重复性任务的自动化，快速部署应用程序，并主动管理内部或云中的服务器。Foreman 为<ruby> 配备 <rt>  provisioning </rt></ruby>管理、配置管理和监控提供了企业级解决方案。由于其插件架构，Foreman 可以以无数种方式进行扩展。使用 Katello 插件，你可以把 Foreman 作为一个完整的<ruby> 内容管理 <rt>  content management </rt></ruby>工具来管理 CentOS Stream，以及其他许多内容类型。

通过 Foreman 和 Katello，你可以准确地定义你希望每个环境包含哪些软件包。例如，生产环境可能使用已被验证为稳定的软件包，而开发环境可能需要最新、最先进的软件包版本。你还可以跨生命周期环境推广<ruby> 内容视图 <rt>  content view </rt></ruby>。让我们来看看 Foreman 是如何完成这个任务的。

我们在这篇文章中使用了网页用户界面，但 Foreman 也有一个强大的 CLI 和 API。Katello 插件为 Pulp 项目提供了一个工作流和网页用户界面，你可以在 [这篇文章](https://opensource.com/article/20/8/manage-repositories-pulp) 中了解更多。我们在这里也提供了一个简单的工作流程，但是 Foreman 和 Katello 项目提供了许多不同的配置选项来满足你的具体需求。

本文假设 Foreman 和 Katello 已经安装完毕。关于如何安装的更多信息，请参阅 [Katello 安装手册](https://docs.theforeman.org/3.0/Installing_Server_on_Red_Hat/index-katello.html)。

### 创建一个产品

第一步是在 Foreman 中创建一个<ruby> 产品 <rt>  product </rt></ruby>。该产品的功能是作为一个内部标签来存储 CentOS Stream 存储库。

1. 在 Foreman 网页用户界面，导航到“<ruby> 内容 <rt>  Content </rt></ruby> > <ruby> 产品 <rt>  Products </rt></ruby>”，并点击“<ruby> 创建产品 <rt>  Create Product </rt></ruby>”。
2. 在“<ruby> 名称 <rt>  Name </rt></ruby>”字段中，为产品输入一个名称。Foreman会根据你输入的“<ruby> 名称 <rt>  Name </rt></ruby>”自动完成“<ruby> 标签 <rt>  Label </rt></ruby>”字段，以后不能再更改。

### 将 CentOS Stream 存储库添加到产品中

现在你有了一个产品，你可以使用 AppStream 和 BaseOS 存储库的 URL，并将它们添加到你的新产品中。

1. 在 Foreman 网页用户界面中，导航到 “<ruby> 内容 <rt>  Content </rt></ruby> > <ruby> 产品 <rt>  Products </rt></ruby>”，选择你要使用的产品，然后点击 “<ruby> 新存储库 <rt>  New Repository </rt></ruby>”。
2. 在“<ruby> 名称 <rt>  Name </rt></ruby>”字段中，为存储库输入一个名称；例如，“Centos8StreamBaseOS”。Foreman 会根据你输入的“<ruby> 名称 <rt>  Name </rt></ruby>”，自动完成“<ruby> 标签 <rt>  Label </rt></ruby>”字段。
3. 从“<ruby> 类型 <rt>  Type </rt></ruby>”列表中，选择存储库的类型，然后选择“Yum”。
4. 在 “URL” 字段中，输入 CentOS Stream Baseos 存储库的 URL，作为源： `http://mirror.centos.org/centos/8-stream/BaseOS/x86_64/os/`。
5. 选择“<ruby> 下载规则 <rt>  Download Policy </rt></ruby>”列表。默认的是“<ruby> 按需 <rt>  On Demand </rt></ruby>”，这意味着 Katello 将只下载元数据。如果你想下载所有的软件包，请改成“<ruby> 即时 <rt>  Immediate </rt></ruby>”，它可以下载所有的软件包，可能会达到 20-30GB。
6. 确保“<ruby> 与镜像同步 <rt>  Mirror on Sync </rt></ruby>”复选框被选中。这个选项确保在同步过程中，不再是上游存储库的一部分的内容被删除。
7. 点击“<ruby> 保存 <rt>  Save </rt></ruby>”。

重复这些步骤，添加 AppStream 存储库及其 URL，例如，`http://mirror.centos.org/centos/8-stream/AppStream/x86_64/os/`。确保你使用最近的官方 CentOS 镜像来代替它。

要执行立即同步，在你的产品窗口，点击“<ruby> 立即同步 <rt>  Sync Now </rt></ruby>”。最初的同步可能需要一些时间。你可以从“<ruby> 内容 <rt>  Content </rt></ruby> > <ruby> 同步状态 <rt>  Sync Status </rt></ruby>”查看同步状态。

同步完成后，你可以在“<ruby> 主机 <rt>  Hosts </rt></ruby> > <ruby> 操作系统 <rt>  Operating System </rt></ruby>”中查看新的 CentOS Stream 操作系统。请随意编辑名称和描述以满足你的要求。

如果你打算使用 Ansible 或 Puppet 等配置管理软件，Foreman 会自动创建一个操作系统报告。你可以在“<ruby> 管理 <rt>  Administe </rt></ruby> > <ruby> 设置 <rt>  Settings </rt></ruby> > <ruby> 忽略操作系统状况 <rt>  Ignore facts for operating system </rt></ruby>”中关闭这个选项。重命名操作系统以匹配配置管理软件中的名称是一个好主意。例如，对于 Puppet，这将是“CentOS 8”。

### 定义你的基础设施的生命周期环境

应用程序生命周期是 Foreman 的内容管理功能的一个核心概念。应用程序的生命周期定义了一个特定的系统和它的软件在特定阶段的状况。例如，一个应用程序的生命周期可能很简单，你可能只有一个“开发”阶段和“生产”阶段。Foreman 提供了一些方法来以可控的方式定制每个应用生命周期阶段，以适应你的规范。

在这一点上，你必须创建你的生命周期环境路径。

1. 在 Foreman 网页用户界面中，导航到“<ruby> 内容 <rt>  Content </rt></ruby> > <ruby> 生命周期环境 <rt>  Lifecycle Environments </rt></ruby>”。
2. 点击“<ruby> 新环境路径 <rt>  New Environment Path </rt></ruby>”，开始一个新的应用生命周期。
3. 在“<ruby> 名称 <rt>  Name </rt></ruby>”字段中，为你的环境输入一个名称。
4. 在“<ruby> 描述 <rt>  Description </rt></ruby>”字段中，为你的环境输入一个描述。
5. 点击“<ruby> 保存 <rt>  Save </rt></ruby>”。
6. 根据你的需要添加尽可能多的环境路径。例如，你可以创建“dev”、“test”、“stage” 和 “production” 环境。要添加这些环境，点击“添加新环境”，完成“<ruby> 名称 <rt>  Name </rt></ruby>”和“<ruby> 描述 <rt>  Description </rt></ruby>”字段，并从“<ruby> 优先环境 <rt>  Prior Environment* </rt></ruby>”列表中选择先前的环境，这样你就可以按照你预期使用的顺序将它们串联起来。

### 创建和发布一个内容视图

在 Foreman 中，“<ruby> 内容视图 <rt>  Content View </rt></ruby>”是你的存储库在某个特定时间点的快照。内容视图提供了隔离软件包版本到你想保留的状态的机制。内容视图有很多可配置的功能，你可以用它来进一步细化。为了本教程的目的，让我们保持简单。

1. 在 Foreman 网页用户界面中，导航到“<ruby> 内容 <rt>  Content </rt></ruby> > <ruby> 内容视图 <rt>  Content View </rt></ruby>”，并点击“<ruby> 创建新视图 <rt>  Create New View </rt></ruby>”。
2. 在“<ruby> 名称 <rt>  Name </rt></ruby>”字段中，为视图输入一个名称。Foreman 会根据你输入的名称自动完成“<ruby> 标签 <rt>  Label </rt></ruby>”字段。
3. 在“<ruby> 描述 <rt>  Description </rt></ruby>”字段中，输入视图的描述。
4. 单击“<ruby> 保存 <rt>  Save </rt></ruby>”以创建内容视图。
5. 在新的内容视图中，点击“<ruby> Yum 内容 <rt>  Yum Contents </rt></ruby> > <ruby> 添加存储库 <rt>  Add Repositories </rt></ruby>”，在“<ruby> 存储库选择 <rt>  Repository Selection </rt></ruby>”区域，点击“<ruby> 添加 <rt>  Add </rt></ruby>”。对于 BaseOS 和 Appstream 存储库，选择你想包括的软件包，然后点击“<ruby> 添加存储库 <rt>  Add Repositories </rt></ruby>”。
6. 点击“<ruby> 发布新版本 <rt>  Publish New Version </rt></ruby>”，在“<ruby> 描述 <rt>  Description </rt></ruby>”区域，输入关于版本的信息以记录变化。
7. 单击“<ruby> 保存 <rt>  Save </rt></ruby>”。

当你点击“<ruby> 发布新版本 <rt>  Publish New Version </rt></ruby>”时，你创建了一个你已同步的所有内容的快照。这意味着你订阅此内容视图的每台服务器将只能访问与此生命周期环境相关的内容视图中的软件包版本。

每一个新的内容视图和后续版本都会首先发布到库环境，然后你可以在那里推广到其他环境。

### 跨生命周期环境推广内容

如果你已经测试了新的软件包，并且确信一切都很稳定，你可以把你的内容视图推广到另一个生命周期环境中。

1. 导航到“<ruby> 内容 <rt>  Content </rt></ruby> > <ruby> 内容视图 <rt>  Content Views </rt></ruby>”，选择你想推广的内容视图。
2. 点击内容视图的“<ruby> 版本 <rt>  Versions </rt></ruby>”标签。
3. 选择你想推广的版本，并在“<ruby> 操作 <rt>  Action </rt></ruby>”栏中，点击“<ruby> 推广 <rt>  Promote </rt></ruby>”。
4. 选择你要推广内容视图的环境，并点击“<ruby> 推广版本 <rt>  Promote Version </rt></ruby>”。
5. 再次点击“<ruby> 推广 <rt>  Promote </rt></ruby>”按钮。这次选择生命周期环境，例如，“Test”，然后单击“<ruby> 推广版本 <rt>  Promote Version </rt></ruby>”。
6. 最后，再次点击“<ruby> 推广 <rt>  Promote </rt></ruby>”按钮。例如，选择“Production”环境并点击“<ruby> 推广版本 <rt>  Promote Version </rt></ruby>”。

被分配到该特定环境的服务器现在可以从一套更新的软件包中提取。

### 创建一个激活密钥

为了将 CentOS Stream 服务器注册到你在特定生命周期中定义的内容，你必须创建一个激活密钥。激活密钥是一种与服务器共享凭证的安全方法。这使用了一个叫做“<ruby> 订阅管理器 <rt>  subscription-manager </rt></ruby>的工具来订阅 CentOS Stream 服务器的内容。

当你创建了激活密钥后，将 CentOS Stream 订阅添加到激活密钥中。

1. 在 Foreman 网页用户界面中，导航到“<ruby> 内容 <rt>  Content </rt></ruby> > <ruby> 激活密钥 <rt>  Activation keys </rt></ruby>”，并点击“<ruby> 创建激活密钥 <rt>  Create Activation Key </rt></ruby>”。
2. 在“<ruby> 名称 <rt>  Name </rt></ruby>”栏中，输入激活密钥的名称。
3. 在“<ruby> 描述 <rt>  Description </rt></ruby>”栏中，输入激活密钥的描述。
4. 从“<ruby> 环境 <rt>  Environment </rt></ruby>”列表中，选择要使用的环境。
5. 从“<ruby> 内容视图 <rt>  Content View </rt></ruby>”列表中，选择你刚才创建的内容视图。
6. 点击“<ruby> 保存 <rt>  Save </rt></ruby>”。

### 从 Foreman 管理的内容中创建一个 CentOS Stream 主机

现在一切都准备好了。随着你创建的内容包含在内容视图中，并在整个生命周期中推广，你现在可以准确地用你想使用的内容来配置主机，并订阅你想让它们接收的更新。

要在 Foreman 中创建一个主机，请导航到“主机 > 创建主机”。

1. 在“<ruby> 名称 <rt>  Name </rt></ruby>”字段中，为主机输入一个名称。
2. 单击“<ruby> 组织 <rt>  Organization </rt></ruby>”和“<ruby> 位置 <rt>  Location </rt></ruby>”选项卡，以确保配置环境自动设置为当前环境。
3. 从“<ruby> 部署在 <rt>  Deploy On </rt></ruby>”列表中，选择“<ruby> 裸金属 <rt>  Bare Metal </rt></ruby>”。
4. 单击“<ruby> 操作系统 <rt>  Operating System </rt></ruby>”选项卡。
5. 从“<ruby> 架构 <rt>  Architectures </rt></ruby>”列表中，选择“x86\_64”。
6. 从“<ruby> 操作系统 <rt>  Operating System </rt></ruby>”列表中，选择“CentOS\_Stream 8”。
7. 勾选“<ruby> 构建模式 <rt>  Build Mode </rt></ruby>”框。
8. 对于“<ruby> 媒体选择 <rt>  Media Selection </rt></ruby>”，选择“<ruby> 同步的内容 <rt>  Synced Content </rt></ruby>”来使用你之前同步的 CentOS Stream 内容。
9. 从“<ruby> 同步的内容 <rt>  Synced Content </rt></ruby>”列表中，确保选择 “CentOS Stream”。
10. 从“<ruby> 分区表 <rt>  Partition Table </rt></ruby>”列表中，对于这个演示，选择默认的 “Kickstart”，但有许多可用的选项。
11. 在“<ruby> Root 密码 <rt>  Root Password </rt></ruby>”栏中，为你的新主机输入一个 root 密码。
12. 点击“<ruby> 接口 <rt>  Interface </rt></ruby>”标签，并点击“<ruby> 编辑 <rt>  Edit </rt></ruby>”，并添加一个 “<ruby> Mac 地址 <rt>  Mac address </rt></ruby>”。
13. 点击“<ruby> 参数 <rt>  Parameters </rt></ruby>”标签，并确保存在一个提供激活密钥的参数。如果没有，添加一个激活密钥。
14. 点击“<ruby> 提交 <rt>  Submit </rt></ruby>”以保存主机条目。

现在，新的主机处于构建模式，这意味着当你打开它时，它将开始安装操作系统。

如果你导航到“<ruby> 主机 <rt>  Hosts </rt></ruby> > <ruby> 内容主机 <rt>  Content Hosts </rt></ruby>”，你可以看到你的主机所订阅的订阅、生命周期环境和内容视图的全部细节。

这个例子只是对你在 Foreman 中管理和配置 CentOS Stream 内容的众多选项的一个小窥视。如果你想了解更多关于如何管理 CentOS Stream 版本，控制你的服务器可以访问的内容，以及控制和保护你的基础设施的稳定性的详细信息，请查看 [Foreman 内容管理](https://docs.theforeman.org/master/Content_Management_Guide/index-foreman.html) 文档。当所有 CentOS Stream 内容在你的控制之下时，你可以创建和注册 Centos Stream，只使用你指定的内容。有关配备的更多详细信息，请参见 [Foreman 配备](https://docs.theforeman.org/master/Provisioning_Guide/index-foreman.html) 文档。如果你有任何问题、反馈或建议，你可以在 <https://community.theforeman.org/> 找到 Foreman 社区。

---

via: <https://opensource.com/article/21/9/centos-stream-foreman>

作者：[Melanie Corr](https://opensource.com/users/melanie-corr) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


硬核观察 #410 国内知名“开源软件”织梦 CMS 向用户追索 5800 元授权费

Summary: 更多：• Google 律师称必应上最常见的搜索词是“Google” • 微软五年前就知道 Exchange 的 Autodiscover 缺陷

![](/data/attachment/album/202109/30/150549tiimpzmexb9dmmdy.jpg)

### 国内知名“开源软件”织梦 CMS 向用户追索 5800 元授权费

![](/data/attachment/album/202109/30/150600psrxkxx4wllzehsf.jpg)

织梦 CMS（DedeCMS）是国内使用广泛的一个内容管理系统，近日它发出一则通告称，“除个人非营利网站外，均需购买 DedeCMS 商业使用授权，授权费为人民币 5800 元”。要求除个人非营利网站外，在 2021 年 10 月 25 日 24 时前必须购买授权。据称，织梦 CMS 官方已经通过 AI 取证机器完成了全网的材料取证，并且组建了一个庞大的律师团队。很多人一直将织梦 CMS 视为“开源的免费软件”。但实际上它并不是开源软件，也不是自由软件，甚至并不是免费软件——因为据其[授权](http://www.desdev.cn/service-dedecms.html)协议，只允许“个人非营利网站”免费使用，这种授权协议显然并非 OSI 认可的“开源许可证”。

>
> 老王点评：要注意，织梦 CMS 不是开源软件，请大家绕道而行。
>
>
>

### Google 律师称必应上最常见的搜索词是“Google”

![](/data/attachment/album/202109/30/150627sj3yzc6kqec57jfc.jpg)

多年来，Google 一直因其在网络搜索领域的主导地位而受到抨击。本周，Google 提出一个证据，必应上最常见的搜索词显然是“[Google](https://www.androidauthority.com/google-bing-search-3032917/)”。Google 用这个角度来证明其客户选择使用 Google 搜索而不是竞争对手，不是因为它是其设备上的默认选项。必应是微软的搜索引擎，是该公司的 Edge 浏览器以及 Windows 的默认选项。目前，这家科技巨头正因其行为在欧盟面临 50 亿美元的巨额罚款。欧盟针对 Google 的案件裁定，Google“不公平地”在 Android 手机上推送其搜索引擎，将其作为这些设备的默认选项，以迫使竞争对手退出。

>
> 老王点评：在一个搜索引擎输入另外一个搜索引擎的名字，我不知道你们在想什么。
>
>
>

### 微软五年前就知道 Exchange 的 Autodiscover 缺陷

![](/data/attachment/album/202109/30/150638kf6hbsijisy4202y.jpg)

之前我们[报道](/article-13812-1.html)过 Exchange 的 Autodiscover 缺陷，它会泄露用户凭证。但是其实微软早在 5 年前就知道这个缺陷，并坚称这不是问题，也没有给出修复。2016 年就有人给微软安全响应中心发邮件，指出了该缺陷，“基本上，我已经发现以纯文本方式获取 Exchange（因此也包括活动目录）用户密码是非常容易的”。微软[承认](https://www.theregister.com/2021/09/27/microsoft_exchange_autodiscover/)有这种情况，但“确定它不是一个安全问题”，而是让用户在向服务器“发送请求之前，确保它是值得信赖的”。

>
> 老王点评：问题是，客户在向这些服务器发送请求时，是不知道有这些默认服务器的，即便是发送后也不知道。
>
>
>


在 Ubuntu Linux 上安装 AnyDesk

个人感觉没RustDesk好

个人感觉没RustDesk好

个人感觉没RustDesk好

>
> 这个初学者的教程讨论了在基于 Ubuntu 的 Linux 发行版上安装 AnyDesk 的 GUI 和终端方法。
>
>
>

![](/data/attachment/album/202110/01/165758y9aaiix7yu88ayui.jpg)

[AnyDesk](https://anydesk.com/en) 是一个流行的远程桌面软件，可用于 Linux、Windows、BSD、macOS 和移动平台。

有了 AnyDesk，你可以用它远程访问其他电脑，或者让别人远程访问你的系统。不是每个人都可以访问它，因为需要两台设备都使用 AnyDesk。你必须接受传入的连接和/或提供一个安全连接的密码。

这对于向朋友、家人、同事甚至客户提供技术支持很有帮助。

在本教程中，我将向你展示在 Ubuntu 上安装 AnyDesk 的图形和命令行两种方法。你可以根据自己的喜好使用这两种方法。这两种方法都会在你的 Ubuntu 系统上安装相同的 AnyDesk 版本。

同样的方法应该适用于 Debian 和其他基于 Debian 和 Ubuntu 的发行版，如 Linux Mint，Linux Lite 等。

>
> **非 FOSS 警告！**
>
>
> AnyDesk 不是开源软件。这里涉及它是因为它在 Linux 上可用，而文章的重点是 Linux。
>
>
>

### 方法 1：使用终端在 Ubuntu 上安装 AnyDesk

在你的系统上 [打开终端程序](https://itsfoss.com/open-terminal-ubuntu/)。你需要一个像 `wget` 这样的工具来 [在终端下载文件](https://itsfoss.com/download-files-from-linux-terminal/)，使用以下命令：

```
sudo apt update
sudo apt install wget

```

下一步是下载 AnyDesk 仓库的 GPG 密钥，并将其添加到你的系统的可信密钥中。这样，你的系统就会信任来自这个[外部仓库](https://itsfoss.com/adding-external-repositories-ubuntu/)的软件。

```
wget -qO - https://keys.anydesk.com/repos/DEB-GPG-KEY | sudo apt-key add -

```

你可以暂时忽略关于 `apt-key` 命令的废弃警告。下一步是将 AnyDesk 仓库添加到系统的仓库源中：

```
echo "deb http://deb.anydesk.com/ all main" | sudo tee /etc/apt/sources.list.d/anydesk-stable.list

```

更新包缓存，这样你的系统就能通过新添加的仓库了解到新应用的可用性。

```
sudo apt update

```

现在，你可以安装 AnyDesk 了：

```
sudo apt install anydesk

```

完成后，你可以从系统菜单或从终端本身启动 AnyDesk：

```
anydesk

```

你现在可以享受 AnyDesk 了。

![AnyDesk running in Ubuntu](/data/attachment/album/202110/01/165804j4vaipseiva7eaed.png)

### 方法 2：在 Ubuntu 上以图形方式安装 AnyDesk

如果你不习惯使用命令行，不用担心。你也可以不进入终端安装 AnyDesk。

你可以从 AnyDesk 官网下载 Ubuntu 上的 AnyDesk：

* [下载 Linux 上的 Anydesk](https://anydesk.com/en/downloads/linux)

你会看到一个“Download Now”的按钮。点击它。

![Download AnyDesk](/data/attachment/album/202110/01/165804v8tzol77sp5so1s2.jpg)

当你点击下载按钮时，它会给你提供各种 Linux 发行版的选项。选择 Ubuntu 的那个：

![Download the appropriate file](/data/attachment/album/202110/01/165805qp45zhcq4ss95qhi.jpg)

它将下载 AnyDesk 的 DEB 文件。[安装 DEB 文件](https://itsfoss.com/install-deb-files-ubuntu/)很简单。要么双击它，要么右击并使用软件安装打开。

![Right click on deb file and open with software center](/data/attachment/album/202110/01/165805vznqmvaqycz1csfm.png)

软件中心应用将被打开，你可以在那里安装它。

![Installing AnyDesk in Ubuntu software center](/data/attachment/album/202110/01/165805x9ii1cdwwdci3vi3.png)

安装后，在系统菜单中搜索它并从那里开始。

![AnyDesk installed in Ubuntu](/data/attachment/album/202110/01/165806pfxw85s5f10xx888.png)

这就好了。不是太难，是吗？

我不打算展示使用 AnyDesk 的步骤。我想你已经对这个问题有了一些了解。如果没有，请参考 [这篇文章](https://support.anydesk.com/Access)。

#### 故障排除提示

当我试图从系统菜单中运行 AnyDesk 时，它没有启动。于是，我从终端启动它，它显示了这个错误：

```
[email protected]:~$ anydesk
anydesk: error while loading shared libraries: libpangox-1.0.so.0: cannot open shared object file: No such file or directory

```

如果你看到 “[error while loading shared libraries](https://itsfoss.com/solve-open-shared-object-file-quick-tip/)” 信息，你要安装它所报错的软件包。在我的例子中，我是这样做的：

```
sudo apt install libpangox-1.0-0

```

这解决了我的问题，我希望它也能为你解决。

如果你有任何与此主题相关的问题，请在评论区告诉我。

---

via: <https://itsfoss.com/install-anydesk-ubuntu/>

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

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


如何在 Linux 上使用 BusyBox

>
> BusyBox 是一个开源（GPL）项目，提供了近 400 个常用命令的简单实现。
>
>
>

![](/data/attachment/album/202110/01/185208x6ckkmvi0silk3vk.jpg "bash logo on green background")

我们很容易认为 Linux 的命令是理所当然的。当你安装 Linux 时，它们与系统捆绑在一起，而我们常常不问为什么它们会在那里。一些基本的命令，如 [cd](https://opensource.com/article/21/8/navigate-linux-directories)、[kill](https://opensource.com/article/18/5/how-kill-process-stop-program-linux) 和 echo，并不总是独立的应用程序，而是实际上内置于你的 shell 中。其他如 [ls](https://opensource.com/article/19/7/master-ls-command)、[mv](https://opensource.com/article/19/8/moving-files-linux-depth) 和 [cat](https://opensource.com/article/19/2/getting-started-cat-command) 是核心工具包（通常是 GNU `coreutils`）的一部分。但在开源的世界里，总是有一些替代品，其中最有趣的是 [BusyBox](https://www.busybox.net)。

### Linux 中的 BusyBox 简介

BusyBox 是一个开源（GPL）项目，提供近 400 个常用命令的简单实现，包括 `ls`、`mv`、`ln`、`mkdir`、`more`、`ps`、`gzip`、`bzip2`、`tar` 和 `grep`。它还包含了编程语言 `awk`、流编辑器 `sed`、文件系统检查工具 `fsck`、`rpm` 和 `dpkg` 软件包管理器，当然还有一个可以方便的访问所有这些命令的 shell（`sh`）。简而言之，它包含了所有 POSIX 系统需要的基本命令，以执行常见的系统维护任务以及许多用户和管理任务。

事实上，它甚至包含一个 `init` 命令，可以作为 PID 1 启动，以作为所有其它系统服务的父进程。换句话说，BusyBox 可以作为 [systemd](https://opensource.com/article/20/4/systemd)、OpenRC、sinit、init 和其他初始化系统的替代品。

BusyBox 非常小。作为一个可执行文件，它不到 1MB，所以它在 [嵌入式](https://opensource.com/article/21/3/rtos-embedded-development)、[边缘计算](https://opensource.com/article/17/9/what-edge-computing) 和 [物联网](https://opensource.com/article/21/3/iot-measure-raspberry-pi) 领域很受欢迎，因为这些场景的存储空间是很宝贵的。在容器和云计算的世界里，它作为精简的 Linux 容器镜像的基础镜像也很受欢迎。

### 极简主义

BusyBox 的部分魅力在于它的极简主义。它的所有命令都被编译到一个二进制文件里（`busybox`），它的手册只有 81 页（根据我对 `man` 送到 `pr` 管道的计算），但它涵盖了近 400 条命令。

作为一个例子的比较，这是 “原版” 的 `useradd —help` 的输出：

```
 -b, --base-dir BASE_DIR       base directory for home
 -c, --comment COMMENT         GECOS field of the new account
 -d, --home-dir HOME_DIR       home directory of the new account
 -D, --defaults                print or change the default config
 -e, --expiredate EXPIRE_DATE  expiration date of the new account
 -f, --inactive INACTIVE       password inactivity
 -g, --gid GROUP               name or ID of the primary group
 -G, --groups GROUPS           list of supplementary groups
 -h, --help                    display this help message and exit
 -k, --skel SKEL_DIR           alternative skeleton dir
 -K, --key KEY=VALUE           override /etc/login.defs
 -l, --no-log-init             do not add the user to the lastlog
 -m, --create-home             create the user's home directory
 -M, --no-create-home          do not create the user's home directory
 -N, --no-user-group           do not create a group with the user's name
 -o, --non-unique              allow users with non-unique UIDs
 -p, --password PASSWORD       encrypted password of the new account
 -r, --system                  create a system account
 -R, --root CHROOT_DIR         directory to chroot into
 -s, --shell SHELL             login shell of the new account
 -u, --uid UID                 user ID of the new account
 -U, --user-group              create a group with the same name as a user

```

而这是是同一命令的 BusyBox 版本：

```
 -h DIR    Home directory
 -g GECOS  GECOS field
 -s SHELL  Login shell
 -G GRP    Group
 -S            Create a system user
 -D            Don't assign a password
 -H            Don't create home directory
 -u UID    User id
 -k SKEL   Skeleton directory (/etc/skel)

```

这种差异是一种特性还是一种限制，取决于你是喜欢你的命令拥有 20 个选项还是 10 个选项。对于一些用户和某些用例来说，BusyBox 的极简主义刚刚满足所需。对于其他人来说，它是一个很好的最小化环境，可以作为一个后备工具，或者作为安装更强大的工具的基础，比如 [Bash](https://opensource.com/article/20/4/bash-sysadmins-ebook)、[Zsh](https://opensource.com/article/19/9/getting-started-zsh)、GNU [Awk](https://opensource.com/article/20/9/awk-ebook) 等等。

### 安装 BusyBox

在 Linux 上，你可以使用你的软件包管理器安装 BusyBox。例如，在 Fedora 及类似发行版：

```
$ sudo dnf install busybox

```

在 Debian 及其衍生版：

```
$ sudo apt install busybox

```

在 MacOS 上，可以使用 [MacPorts](https://opensource.com/article/20/11/macports) 或 [Homebrew](https://opensource.com/article/20/6/homebrew-mac)。在 Windows 上，可以使用 [Chocolatey](https://opensource.com/article/20/3/chocolatey)。

你可以将 BusyBox 设置为你的 shell，使用 `chsh —shell` 命令，然后再加上 BusyBox `sh` 应用程序的路径。我把 BusyBox 放在 `/lib64` 中，但它的位置取决于你的发行版的安装位置。

```
$ which busybox
/lib64/busybox/busybox
$ chsh --shell /lib64/busybox/sh

```

用 BusyBox 全盘替换所有常见的命令要复杂一些，因为大多数发行版都是“硬接线”，会在特定的软件包寻找特定的命令。换句话说，虽然技术上可以用 BusyBox 的 `init` 替换系统的 `init`，但你的软件包管理器可能会拒绝让你删除包含 `init` 的软件包，以免你担心删除会导致系统无法启动。有一些发行版是建立在 BusyBox 之上的，所以从新环境开始可能是体验 BusyBox 系统的最简单方法。

### 试试 BusyBox

你不必为了尝试 BusyBox 而将你的 shell 永久改为 BusyBox。你可以从你当前的 shell 中启动一个 BusyBox shell。

```
$ busybox sh
~ $

```

不过你的系统仍然有安装的非 BusyBox 版本的命令，所以要体验 BusyBox 的工具，你必须把命令作为参数发给 `busybox` 可执行文件：

```
~ $ busybox echo $0
sh
~ $ busybox ls --help
BusyBox vX.YY.Z (2021-08-25 07:31:48 NZST) multi-call binary.

Usage: ls [-1AaCxdLHRFplinshrSXvctu] [-w WIDTH] [FILE]...

List directory contents

 -1  One column output
 -a  Include entries that start with .
 -A  Like -a, but exclude . and ..
 -x  List by lines
[...]

```

为了获得“完整”的 BusyBox 体验，你可以为每个命令创建一个 `busybox` 的符号链接。这很容易，只要你使用 [for 循环](https://opensource.com/article/19/10/programming-bash-loops) 就行：

```
$ mkdir bbx
$ for i in $(bbx --list); do \
    ln -s /path/to/busybox bbx/$i \
done

```

在你的 [路径](https://opensource.com/article/17/6/set-path-linux) 的 *开头* 添加这个符号链接目录，并启动 BusyBox：

```
$ PATH=$(pwd)/bbx:$PATH bbx/sh

```

### 用起来

BusyBox 是一个有趣的项目，也是一个可以实现 *极简* 计算的例子。无论你是把 BusyBox 作为 [你唤醒的](https://opensource.com/article/19/7/how-make-old-computer-useful-again) [古老的计算机](https://opensource.com/article/20/2/restore-old-computer-linux) 的轻量级环境，还是作为 [嵌入式设备](https://opensource.com/article/20/6/open-source-rtos) 的用户界面，抑或试用一个新的初始化系统，就算是为了好奇，让自己重新认识那些熟悉而又陌生的命令，都会很有意思。

---

via: <https://opensource.com/article/21/8/what-busybox>

作者：[Seth Kenlon](https://opensource.com/users/seth) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


硬核观察 #411 一半的开发人员因为烂代码考虑辞职，但是如果钱给够了……

Summary: 更多：• 一半的企业 PC 无法升级到 Windows 11 • AI 可以预测两小时后是否会下雨

![](/data/attachment/album/202110/01/200733kiw5xper5uu5zxr5.jpg)

### 一半的开发人员因为烂代码考虑辞职，但是如果钱给够了……

![](/data/attachment/album/202110/01/200743ck6hhw05ddm5mwb9.jpg)

一个对 200 名软件工程师的[调查显示](https://www.stepsize.com/report)，51% 的工程师因为技术债而考虑辞职，其中 20% 的人说这是他们离开公司的主要原因。不过在面试新职位时，技术债只是第四大因素。薪水问题最重要，82% 的人认为它是面试新职位的最重要因素之一。而技术挑战和成长机会是第二重要的因素，75% 的人选择了它。68% 的人选择了远程工作因素，只有 62% 的人将技术债当成换工作的首要考虑因素。

>
> 老王点评：一句话，钱给够怎么都行。
>
>
>

### 一半的企业 PC 无法升级到 Windows 11

![](/data/attachment/album/202110/01/200803lfdufsnwfuutmmsc.jpg)

Windows 11 对特定处理器和 TPM 2.0 的要求意味着有很大一部分用户将无法升级到该操作系统。 换句话说，如果你的个人电脑是在 2017 年之前生产的，它在运行 Windows 11 时可能面临阻碍。一项针对企业的[调查表明](https://www.lansweeper.com/itam/is-your-business-ready-for-windows-11/)，只有 54.53% 的企业工作站确定可以升级，而绝大部分服务器都无法升级。这些设备中满足要求的 CPU 只有 44.4%，满足要求的 TPM 只有 52.55%。

![](/data/attachment/album/202110/01/200832ugxdvxb9ib6j1ljq.jpg)

![](/data/attachment/album/202110/01/200840yju8a6u8ng8ap76a.jpg)

>
> 老王点评：可惜 Linux 桌面不给力，多好的机会啊。
>
>
>

### AI 可以预测两小时后是否会下雨

![](/data/attachment/album/202110/01/200823chv3vvzepv07eq0p.jpg)

谷歌旗下的 DeepMind 和埃克塞特大学的科学家们与英国气象局合作，可以对天气做出更准确的[短期预测](https://www.bbc.com/news/technology-58748934)，包括关键的风暴和洪水。该系统学会了如何识别常见的降雨模式，它使用英国 2016 年至 2018 年的雷达地图，在 2019 年的地图上进行了测试，气象学家们发现，在 89% 的情况下预测是准确的。

>
> 老王点评：又一个 AI 的好领域。
>
>
>


让 YAML 变得像它看起来一样简单

>
> YAML 看起来很简单，为什么它这么难写呢？了解成功使用 YAML 的两个秘诀。
>
>
>

![](/data/attachment/album/202110/02/101824shamurmpvldpu29a.jpg "Person using a laptop")

如果你曾经尝试过写 YAML，你可能一开始会对它看起来很容易感到高兴。乍一看，经常用于配置文件、Ansible 剧本和普通文件数据库的 YAML 看起来就像购物清单一样直观。然而，YAML 的结构中有很多细微的差别，它隐藏着一个危险的秘密：YAML 实际上是一种高度精确、结构化和令人惊讶的严格语言。好消息是，你只需要了解两件事就可以知道 YAML 的工作原理。

关于 YAML 的真相是，YAML 中只有两种数据结构：<ruby> 序列 <rt>  sequence </rt></ruby>和<ruby> 映射 <rt>  mapping </rt></ruby>。这是两个花哨的名字，你会发现它代表了你非常熟悉的概念。这篇文章解释了这两种结构，更重要的是，介绍了它们是如何协同工作，使 YAML 成为表示你所关心的数据的强大方式。

### YAML 序列

YAML 序列是一个列表。在其最简单的形式中，每行有一个项目，每行以破折号和空格开始。

下面是一个例子：

```
---
- Linux
- BSD
- Illumos

```

不同的语言有不同的方式来表示这种数据。例如，在 Python 中，同一个列表可以写成 `['Linux', 'BSD', 'Illumos']`。当你在现实生活中写一个列表时，例如在你去买菜之前，你写的可能近似于 YAML 序列。

### YAML 映射

YAML 映射是一个关键术语与该术语的定义相结合。在其他语言中，映射被称为“键值对”或“词典”。

这里有一个例子：

```
---
Kernel: Linux
CPU: AMD
RAM: '16 GB'

```

不同的语言有不同的方式来表示这种数据。[在 Python 中](https://opensource.com/article/21/3/dictionary-values-python)，例如，同样的数据可以写成 `{"Kernel": "Linux", "CPU": "AMD", "RAM": "16 GB"}`。在现实生活中，你可能会使用这种结构来计划，例如，与朋友的游戏之夜。一个朋友报名带零食，另一个报名带一副牌，另一个报名带一个棋盘游戏，等等。

### 组合序列和映射

你现在知道 YAML 的语法了。序列和映射是 YAML 中仅有的两种构件，你想在 YAML 中表示的任何东西都可以放在序列或映射中。

或者同时使用这二者！

是的，序列和映射可以被组合和嵌套，这就是 YAML 看起来很直观，但同时又感觉很复杂的原因之一。不过，只有四种可能的组合，一旦你学会如何看它们，YAML 就会觉得像它看起来一样简单。

### 序列的映射

当你想让一个键项有许多值时，你可以使用一个序列的映射。也就是说，你从一个映射（键）开始，但是给值一个列表：

```
---
Linux:
  - Fedora
  - Slackware
BSD:
  - FreeBSD
  - NetBSD

```

在这个示例代码中，`Linux` 是第一个键，它的值是一个序列，其中包含 `Fedora` 和 `Slackware`。第二个键是 `BSD`，它的值是一个序列，包含 `FreeBSD` 和 `NetBSD`。

### 映射的映射

当你想让一个键项的值中既有键又有值时，你可以使用映射的映射。也就是说，你从一个映射（键）开始，但是给值另一个映射。

这可能有点欺骗性，但它揭示了 YAML 中使用特定术语的原因：因为你只不过是创建了一个映射的列表，并不意味着你创建了一个序列。这里是一个映射的映射：

```
---
Desktop:
  CPU: RISC-V
  RAM: '32 GB'
Laptop:
  CPU: AMD
  RAM: '16 GB'

```

对大多数人来说，这看起来像一个列表。而且从技术上讲，它是一个列表。但重要的是要认识到，它不是 YAML 序列。它是一个映射，其中包含映射。作为半个 YAML 专家，你可以从明显缺少破折号的地方看出区别。

在 Ansible 剧本的所有结构中，我发现这个结构最容易欺骗人。作为人类，我们喜欢列表，当我们看到一个数据结构 *在字面上* 是列表时，大多数人会把它当成 YAML 序列。但是在 YAML 中，虽然序列是列表，但是列表并不总是序列。

### 序列的序列

就像你可以嵌套映射一样，你可以将一个序列嵌套到一个序列中：

```
---
- [Linux, FreeBSD, Illumos]
- [YAML, XML, JSON]

```

这可能是我在 YAML 的实际使用中遇到的最不常见的数据结构，但有时你需要一个列表的列表。

### 映射的序列

你也可以创建一个包含映射的序列。对于人类排序数据的方式来说，这并不太常见，但对于计算机来说，这可能是一个重要的结构。

这里有一个例子：

```
---
-
  CPU: AMD
  RAM: '16 GB'
-
  CPU: Intel
  RAM: '16 GB'

```

对于 YAML，这可能是最不直观的语法。我发现它在 Python 中呈现时更清晰：

```
[{"CPU": "AMD", "RAM": "16 GB"}, {"CPU": "Intel", "RAM": "16 GB"}]

```

方括号代表一个列表结构，这个列表包含两个字典。每个字典都包含键值对。

### 构建更好的 YAML

现在你知道了 YAML 的两个组成部分，以及它们如何被组合起来以表示复杂的数据结构。问题是：你要用 YAML 构建什么？

和很多人一样，我也使用 YAML 来编写 Ansible 剧本。我还用它作为一种简单的配置格式、作为 D&D 的角色表、表示项目组织所需的目录结构，等等。只要你能适应序列和映射的概念，你会发现 YAML 是一种很容易编写、阅读和（如果有合适的库）解析的格式。

如果你发现自己经常使用 YAML，请下载我们的 [YAML 速查表](https://opensource.com/downloads/yaml-cheat-sheet)，以帮助你直观地了解基本数据结构及其组合，并帮助你记住一些额外的语法约定。通过一点点的练习，你会发现 YAML 真的和它看起来一样简单！

---

via: <https://opensource.com/article/21/9/yaml-cheat-sheet>

作者：[Seth Kenlon](https://opensource.com/users/seth) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


Arch Linux 软件包制作入门

Summary: 如何把自己的软件制作成软件包呢？

如何把自己的软件制作成软件包呢？

![](/data/attachment/album/202110/02/130702ybexb5tsvkx1xbs1.jpg)

`PKGBUILD` 文件是为 Arch Linux 及其衍生版（如 Manjaro）构建和创建软件包的方式。

如果你曾经使用过 [AUR](https://itsfoss.com/aur-arch-linux/)（即 Arch Linux 的用户维护的 `PKGBUILD` 存储库），你甚至可能也遇到过它们。

但是，到底是如何从 `PKGBUILD` 到可安装软件包的呢？这两者之间到底发生了什么，如何把自己的软件制作成软件包呢？你将在这篇文章中了解这些。

### PKGBUILD 基础知识

对于那些熟悉 Bash 或其他 shell 的人来说，你可能知道，`PKGBUILD` 就是一个带有一些变量的 shell 脚本。

`PKGBUILD` 文件由变量和函数组成，所有这些都是用来定义软件包本身，以及如何构建它。

为了从 `PKGBUILD` 中创建一个软件包，需要使用 `makepkg` 命令行工具。在获得 `PKGBUILD` 文件后，你只需在包含 `PKGBUILD` 的目录中运行 `makepkg'，就可以得到一个可安装的软件包了。

![](/data/attachment/album/202110/02/130710nhaxurp2c6zrrfsr.png)

在本教程中，你将会看到我刚刚制作的软件包，它在运行时打印出 “Hello World!”。

![](/data/attachment/album/202110/02/130711ge1fjv3fh72cfl29.png)

### 准备

为了继续学习本教程，你需要创建几个文件。

首先，你需要创建一个名为 `PKGBUILD` 的文件，它将作为构建你的软件包的“配方”。

你需要做的另一个文件是一个叫 `hello-world.sh` 的文件。我稍后会解释它的用途。

你也可以用一个命令来创建这两个文件：

```
touch PKGBUILD hello-world.sh

```

你可以用 `ls` 命令检查这些文件是否被创建。

![](/data/attachment/album/202110/02/130712dxqn3hb8tkkrftyc.png)

然后你就可以开始了！

### 设置你的 PKGBUILD 文件

我不会让你复制粘贴整个文件，而是和你一起键入每一行，这样你就能更好地理解每一行的目的。如果你不喜欢这种学习方式，我强烈推荐 [Arch 维基](https://wiki.archlinux.org/title/Creating_packages) 中为 Arch Linux 创建软件包的文章。

这篇文章也没有介绍 `PKGBUILD` 中可以设置的每一个选项，只是介绍了一些常用的选项，以便你能尽快上手。

说完了这些，打开你的文本编辑器，让我们直接进入正题吧。

#### pkgname

首先是 `pkgname` 变量。这是安装时定义软件包名称的东西，也是 [Arch Linux 的软件包管理器 pacman](https://itsfoss.com/pacman-command/) 跟踪软件包的方式。

这个变量（以及其他一些变量）的格式是 `variable=value`，变量名在左边，变量的值在右边，用等号隔开。

要设置包的名称，请在 `PKGBUILD` 中输入以下内容：

```
pkgname="hello-world"

```

* 要设置一个不同的软件包名称，用你的软件包的名称替换 `hello-world`。
* 这并不设置用于运行程序的命令，这将在下面的 `package()` 部分中处理。

#### pkgver

正如变量名称本身所述，它设置了你的软件包的版本（即 `1.0.0`）。这在用户更新他们的系统时很有用，因为设置更高的版本会提示用户升级。

要设置版本号，请在 `PKGBUILD` 中输入以下内容（在前一行之后）：

```
pkgver="1.0.0"

```

#### pkgrel

这与 `pkgver` 变量有关，通常不需要知道。不过和 `pkgver` 变量一样，如果它被换到一个更高的数字，就将通知用户进行升级。

它适用于任何需要保持 `pkgver` 不变的情况下，例如 `PKGBUILD` 本身发生了变化。如果你为一个你使用的程序创建了一个 `PKGBUILD`（并希望保持软件包的版本相同），而你需要修复 `PKGBUILD` 本身的一个错误，这将是非常有用的。

要设置这个变量，请在 `PKGBUILD` 中输入以下内容：

```
pkgver="1"

```

这个变量应该 **总是** 从 `1` 开始，然后一次一次地向上移动。当 `pkgver` 本身向上移动时，这个变量可以（也应该）重置为 `1`，因为 `pkgver` 本身会通知用户升级。

#### pkgdesc

这将设置软件包的描述，用于帮助更好地识别该软件包。

要设置它，只需将描述放在引号内：

```
pkgdesc="Hello world in your terminal!"

```

#### arch

这个变量设置软件包所兼容的 [硬件架构](https://www.quora.com/What-is-CPU-architecture)。如果你不明白什么是架构，那也没关系，因为在大多数情况下，这个变量几乎是无用的。

无论如何，`makepkg` 仍然需要设置它，这样它就知道这个软件包与我们的系统是兼容的。

这个变量支持设置多个值，所以 `makepkg` 需要一个不同的语法，如下所示。

要设置它，请在 `PKGBUILD` 中输入以下内容：

```
arch=("x86_64")

```

如果你要设置多个值，需要用空格和引号分隔每个值，像这样。`arch=(“x86_x64" "arm")`。

#### depends

这列出了提供了我们的软件包所需功能的所有软件包。与 `arch` 一样，它也可以包含多个值，因此必须使用括号语法。

由于我们的软件包没有任何依赖关系，所以我们不需要在 `PKGBUILD` 中输入这个字段。然而，如果我们的软件包有依赖关系，我们就会使用与 `arch` 相同的语法。

#### optdepends

这里列出了那些并不是提供所需功能而是额外功能的软件包。

这与 `depends` 的语法相同。

#### conflicts

这告诉 `pacman` 哪些软件包会导致我们的软件包出现问题，或者以我们不希望的方式行事。

这里列出的任何软件包都会在我们的软件包被安装之前被卸载。

这与 `depends` 的语法相同。

#### license

这定义了你的程序所采用的 [软件许可证](https://en.wikipedia.org/wiki/Software_license)。如果你需要帮助你选择一个许可证，[Arch 维基](https://wiki.archlinux.org/title/PKGBUILD#license) 提供了一些信息。如果你不知道该怎么设置，将其设置为 `custom` 也可以。

这与 `arch` 和 `depends` 的语法相同：

```
license=("custom")

```

#### source

这就是 `makepkg` 如何知道要用什么文件来构建我们的软件包。它可以包含各种不同类型的源，包括本地文件和 URL。

在添加本地文件时，要输入相对于 `PKGBUILD` 文件的文件路径，比如以下目录布局：

```
PKGBUILD
file.txt
src/file.sh

```

如果你想在我们的 `PKGBUILD` 中包括 `file.sh`，你需要输入 `src/file.sh` 作为其名称。

当输入 URL 时，你只需输入完整的 URL，即 `https://mirrors.creativecommons.org/presskit/logos/cc.logo.large.png`。

你的这个软件包只需要 `hello-world.sh` 文件，由于它和 `PKGBUILD` 在同一个目录中，你只需输入它的名字作为 `source` 的值。

这个变量也使用与 `arch` 和 `depends` 相同的语法：

```
source=("hello-world.sh")

```

#### sha512sums

这是用来验证 `source` 中的文件没有被修改或下载错误。如何获得这个值的信息可以在 [Arch 维基关于 PKGBUILD 的文章](https://wiki.archlinux.org/title/PKGBUILD#Integrity) 中找到。

如果你宁愿不设置这个（或者你只是不需要，例如对于本地文件），你可以为 `source` 变量中的每个文件输入 `SKIP`：

```
sha512sums=("SKIP")

```

#### package()

这是最后一个，也是实际制作我们的包的最重要的部分。在处理这个问题时，知道两个变量很重要。

* `${srcdir}`：这是 `makepkg` 放置 `source` 变量中文件的地方。在这个目录中，你可以与这些文件进行交互，并对文件进行任何其他需要的修改。
* `${pkgdir}`：这是我们放置将被安装在系统中的文件的地方。 `${pkgdir}` 的文件夹结构是按照实际系统中的情况设置的（例如，使用 `pacman` 安装时，`${pkgdir}/usr/bin/hello-world` 会创建文件 `/usr/bin/hello-world`）。

`package()` 包含一个用于创建软件包的命令列表。

因此，如果（假设）你需要有个在 `/usr/share/motto.txt` 写着 “Linux is superior to Windows ”的文件，你会运行这样的东西：

```
package() {
  mkdir -p "${pkgdir}/usr/share"
  echo "Linux is superior to Windows" | tee "${pkgdir}/usr/share/motto.txt"
}

```

关于上述命令的一些说明：

* `${pkgdir}` 里面最初是 **不包含** 目录的。如果你跳过了 [mkdir 命令](https://linuxhandbook.com/mkdir-command/)，`tee` 会输出一个错误，说这个目录不存在。
* 在指定目录时，**总是** 在它们前面加上 `${pkgdir}` 或 `${srcdir}` 变量。如果输入 `/usr/share/motto.txt`，就会按照字面意义指向你当前运行的系统中的 `/usr/share/motto.txt`。

对于你的 `PKGBUILD`，你将把 `hello-world.sh` 文件放在目标系统的 `/usr/bin/hello-world` 中。你还将使该文件在运行时说 “Hello to you!”。

要做到这一点，请在 `PKGBUILD` 中输入以下内容：

```
package() {
  echo 'Hello to you!' > "${srcdir}/hello-world.sh"
  mkdir -p "${pkgdir}/usr/bin"
  cp "${srcdir}/hello-world.sh" "${pkgdir}/usr/bin/hello-world"
  chmod +x "${pkgdir}/usr/bin/hello-world"
}

```

然后就完成了！用 `makepkg -si` 构建和安装软件包，然后在终端运行 `hello-world`，查看其输出。

![](/data/attachment/album/202110/02/130714d08gygeexx2euoxz.png)

### 总结

就这样，你已经制作了你的第一个 `PKGBUILD`！你走在了为自己甚至是为 AUR 制作实际的软件包的路上。

有什么问题，或者有什么地方不对吗？请随时在下面的评论区发表。

---

via: <https://itsfoss.com/create-pkgbuild/>

作者：[Hunter Wittenborn](https://itsfoss.com/author/hunter/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


硬核观察 #412 二十年后，ATI Radeon R300 的 Linux 驱动仍有零星改进

Summary: 更多：• 谷歌赞助 Linux 基金会的安全开源计划 100 万美元 • Telegram 机器人正试图窃取你的一次性密码

![](/data/attachment/album/202110/02/150202pbo6awcosqplb8bp.jpg)

### 二十年后，ATI Radeon R300 的 Linux 驱动仍有零星改进

![](/data/attachment/album/202110/02/150212mfd8pm8v5r00br6p.jpg)

ATI R300 图形处理器推出于 2002 年.虽然 AMD 的开源 Linux 开发者在很长一段时间内不再关注这个 R300g 驱动，但由于开源的性质，社区能够对这个传统的硬件支持进行[偶尔的改进](https://www.phoronix.com/scan.php?page=news_item&px=R300-Friday-Night-2021)。仅今年就已经提交了 14 个补丁。当然，大多数与这个近 20 年历史的 GPU 驱动有关的提交都是围绕着错别字和普通修复。顺便说一句，R300g 并不是 ATI/AMD 官方的硬件 Linux 驱动，但正是通过这种逆向工程和业余工作，导致这些开源开发者中的几个人陆续加入了 AMD，并受雇于该公司为其开发更多现代驱动。

>
> 老王点评：这就是开源的好处，各种意义上的。
>
>
>

### 谷歌赞助 Linux 基金会的安全开源计划 100 万美元

![](/data/attachment/album/202110/02/150232gb79ba0aa7q899av.jpg)

[安全开源（SOS）试点计划](https://sos.dev/)会奖励广泛的改进措施，主动加强关键开源项目和支持基础设施，以应对针对应用程序和供应链的攻击。为了补充现有的奖励漏洞管理的计划，还会直接支持项目开发者。奖励范围从 10000 美元以上的复杂、高影响和持久的改进，几乎可以肯定地防止受影响的代码或支持的基础设施中的重大漏洞，到 500 美元的小改进，但至少从安全的角度来看需要有好处。谷歌初期向该计划投资 100 万美元，并计划根据社区反馈扩大该计划的范围。

>
> 老王点评：主动去加强开源软件的安全，我觉得谷歌这钱花的值。
>
>
>

### Telegram 机器人正试图窃取你的一次性密码

![](/data/attachment/album/202110/02/150246d3o4ou8uifzityiz.jpg)

虽然双因素认证（2FA）这样的一次性密码可以改进单独使用密码的安全性，但威胁者很快就开发出拦截这些 2FA 的方法，如通过恶意软件或社会工程。根据英特尔 471 团队的[消息](https://www.zdnet.com/article/telegram-bots-are-trying-to-steal-your-one-time-passwords/)，自 6 月以来，一些用于规避 2FA 的服务正在滥用 Telegram 的消息服务。由 Telegram 驱动的机器人正被用来窃取 2FA 安全中所要求的一次性密码。

>
> 老王点评：虽然 2FA 是一个好的安全实践，但是安全最薄弱的环节往往是人。
>
>
>


实例讲解代码之内存安全与效率

Summary: 了解有关内存安全和效率的更多信息。

了解有关内存安全和效率的更多信息。

>
> 了解有关内存安全和效率的更多信息。
>
>
>

![](/data/attachment/album/202110/03/101238cd0hgdrhamzab7hj.jpg "Code going into a computer.")

C 是一种高级语言，同时具有“<ruby> 接近金属 <rt>  close-to-the-metal </rt></ruby>”（LCTT 译注：即“接近人类思维方式”的反义词）的特性，这使得它有时看起来更像是一种可移植的汇编语言，而不像 Java 或 Python 这样的兄弟语言。内存管理作为上述特性之一，涵盖了正在执行的程序对内存的安全和高效使用。本文通过 C 语言代码示例，以及现代 C 语言编译器生成的汇编语言代码段，详细介绍了内存安全性和效率。

尽管代码示例是用 C 语言编写的，但安全高效的内存管理指南对于 C++ 是同样适用的。这两种语言在很多细节上有所不同（例如，C++ 具有 C 所缺乏的面向对象特性和泛型），但在内存管理方面面临的挑战是一样的。

### 执行中程序的内存概述

对于正在执行的程序（又名 *<ruby> 进程 <rt>  process </rt></ruby>*），内存被划分为三个区域：**<ruby> 栈 <rt>  stack </rt></ruby>**、**<ruby> 堆 <rt>  heap </rt></ruby>** 和 **<ruby> 静态区 <rt>  static area </rt></ruby>**。下文会给出每个区域的概述，以及完整的代码示例。

作为通用 CPU 寄存器的替补，*栈* 为代码块（例如函数或循环体）中的局部变量提供暂存器存储。传递给函数的参数在此上下文中也视作局部变量。看一下下面这个简短的示例：

```
void some_func(int a, int b) {
   int n;
   ...
}

```

通过 `a` 和 `b` 传递的参数以及局部变量 `n` 的存储会在栈中，除非编译器可以找到通用寄存器。编译器倾向于优先将通用寄存器用作暂存器，因为 CPU 对这些寄存器的访问速度很快（一个时钟周期）。然而，这些寄存器在台式机、笔记本电脑和手持机器的标准架构上很少（大约 16 个）。

在只有汇编语言程序员才能看到的实施层面，栈被组织为具有 `push`（插入）和 `pop`（删除）操作的 LIFO（后进先出）列表。 `top` 指针可以作为偏移的基地址；这样，除了 `top` 之外的栈位置也变得可访问了。例如，表达式 `top+16` 指向堆栈的 `top` 指针上方 16 个字节的位置，表达式 `top-16` 指向 `top` 指针下方 16 个字节的位置。因此，可以通过 `top` 指针访问实现了暂存器存储的栈的位置。在标准的 ARM 或 Intel 架构中，栈从高内存地址增长到低内存地址；因此，减小某进程的 `top` 就是增大其栈规模。

使用栈结构就意味着轻松高效地使用内存。编译器（而非程序员）会编写管理栈的代码，管理过程通过分配和释放所需的暂存器存储来实现；程序员声明函数参数和局部变量，将实现过程交给编译器。此外，完全相同的栈存储可以在连续的函数调用和代码块（如循环）中重复使用。精心设计的模块化代码会将栈存储作为暂存器的首选内存选项，同时优化编译器要尽可能使用通用寄存器而不是栈。

**堆** 提供的存储是通过程序员代码显式分配的，堆分配的语法因语言而异。在 C 中，成功调用库函数 `malloc`（或其变体 `calloc` 等）会分配指定数量的字节（在 C++ 和 Java 等语言中，`new` 运算符具有相同的用途）。编程语言在如何释放堆分配的存储方面有着巨大的差异：

* 在 Java、Go、Lisp 和 Python 等语言中，程序员不会显式释放动态分配的堆存储。

例如，下面这个 Java 语句为一个字符串分配了堆存储，并将这个堆存储的地址存储在变量 `greeting` 中：

```
String greeting = new String("Hello, world!");

```

Java 有一个垃圾回收器，它是一个运行时实用程序，如果进程无法再访问自己分配的堆存储，回收器可以使其自动释放。因此，Java 堆释放是通过垃圾收集器自动进行的。在上面的示例中，垃圾收集器将在变量 `greeting` 超出作用域后，释放字符串的堆存储。
* Rust 编译器会编写堆释放代码。这是 Rust 在不依赖垃圾回收器的情况下，使堆释放实现自动化的开创性努力，但这也会带来运行时复杂性和开销。向 Rust 的努力致敬！
* 在 C（和 C++）中，堆释放是程序员的任务。程序员调用 `malloc` 分配堆存储，然后负责相应地调用库函数 `free` 来释放该存储空间（在 C++ 中，`new` 运算符分配堆存储，而 `delete` 和 `delete[]` 运算符释放此类存储）。下面是一个 C 语言代码示例：

```
char* greeting = malloc(14);       /* 14 heap bytes */
strcpy(greeting, "Hello, world!"); /* copy greeting into bytes */
puts(greeting);                    /* print greeting */
free(greeting);                    /* free malloced bytes */

```

C 语言避免了垃圾回收器的成本和复杂性，但也不过是让程序员承担了堆释放的任务。

内存的 **静态区** 为可执行代码（例如 C 语言函数）、字符串文字（例如“Hello, world!”）和全局变量提供存储空间：

```
int n;                       /* global variable */
int main() {                 /* function */
   char* msg = "No comment"; /* string literal */
   ...
}

```

该区域是静态的，因为它的大小从进程执行开始到结束都固定不变。由于静态区相当于进程固定大小的内存占用，因此经验法则是通过避免使用全局数组等方法来使该区域尽可能小。

下文会结合代码示例对本节概述展开进一步讲解。

### 栈存储

想象一个有各种连续执行的任务的程序，任务包括了处理每隔几分钟通过网络下载并存储在本地文件中的数字数据。下面的 `stack` 程序简化了处理流程（仅是将奇数整数值转换为偶数），而将重点放在栈存储的好处上。

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

#define Infile   "incoming.dat"
#define Outfile  "outgoing.dat"
#define IntCount 128000  /* 128,000 */

void other_task1() { /*...*/ }
void other_task2() { /*...*/ }

void process_data(const char* infile,
          const char* outfile,
          const unsigned n) {
  int nums[n];
  FILE* input = fopen(infile, "r");
  if (NULL == infile) return;
  FILE* output = fopen(outfile, "w");
  if (NULL == output) {
    fclose(input);
    return;
  }

  fread(nums, n, sizeof(int), input); /* read input data */
  unsigned i;
  for (i = 0; i < n; i++) {
    if (1 == (nums[i] & 0x1))  /* odd parity? */
      nums[i]--;               /* make even */
  }
  fclose(input);               /* close input file */

  fwrite(nums, n, sizeof(int), output);
  fclose(output);
}

int main() {
  process_data(Infile, Outfile, IntCount);

  /** now perform other tasks **/
  other_task1(); /* automatically released stack storage available */
  other_task2(); /* ditto */

  return 0;
}

```

底部的 `main` 函数首先调用 `process_data` 函数，该函数会创建一个基于栈的数组，其大小由参数 `n` 给定（当前示例中为 128,000）。因此，该数组占用 `128000 * sizeof(int)` 个字节，在标准设备上达到了 512,000 字节（`int` 在这些设备上是四个字节）。然后数据会被读入数组（使用库函数 `fread`），循环处理，并保存到本地文件 `outgoing.dat`（使用库函数 `fwrite`）。

当 `process_data` 函数返回到其调用者 `main` 函数时，`process_data` 函数的大约 500MB 栈暂存器可供 `stack` 程序中的其他函数用作暂存器。在此示例中，`main` 函数接下来调用存根函数 `other_task1` 和 `other_task2`。这三个函数在 `main` 中依次调用，这意味着所有三个函数都可以使用相同的堆栈存储作为暂存器。因为编写栈管理代码的是编译器而不是程序员，所以这种方法对程序员来说既高效又容易。

在 C 语言中，在块（例如函数或循环体）内定义的任何变量默认都有一个 `auto` 存储类，这意味着该变量是基于栈的。存储类 `register` 现在已经过时了，因为 C 编译器会主动尝试尽可能使用 CPU 寄存器。只有在块内定义的变量可能是 `register`，如果没有可用的 CPU 寄存器，编译器会将其更改为 `auto`。基于栈的编程可能是不错的首选方式，但这种风格确实有一些挑战性。下面的 `badStack` 程序说明了这点。

```
#include <stdio.h>;

const int* get_array(const unsigned n) {
  int arr[n]; /* stack-based array */
  unsigned i;
  for (i = 0; i < n; i++) arr[i] = 1 + 1;

  return arr;  /** ERROR **/
}

int main() {
  const unsigned n = 16;
  const int* ptr = get_array(n);

  unsigned i;
  for (i = 0; i < n; i++) printf("%i ", ptr[i]);
  puts("\n");

  return 0;
}

```

`badStack` 程序中的控制流程很简单。`main` 函数使用 16（LCTT 译注：原文为 128，应为作者笔误）作为参数调用函数 `get_array`，然后被调用函数会使用传入参数来创建对应大小的本地数组。`get_array` 函数会初始化数组并返回给 `main` 中的数组标识符 `arr`。 `arr` 是一个指针常量，保存数组的第一个 `int` 元素的地址。

当然，本地数组 `arr` 可以在 `get_array` 函数中访问，但是一旦 `get_array` 返回，就不能合法访问该数组。尽管如此，`main` 函数会尝试使用函数 `get_array` 返回的堆栈地址 `arr` 来打印基于栈的数组。现代编译器会警告错误。例如，下面是来自 GNU 编译器的警告：

```
badStack.c: In function 'get_array':
badStack.c:9:10: warning: function returns address of local variable [-Wreturn-local-addr]
return arr;  /** ERROR **/

```

一般规则是，如果使用栈存储实现局部变量，应该仅在该变量所在的代码块内，访问这块基于栈的存储（在本例中，数组指针 `arr` 和循环计数器 `i` 均为这样的局部变量）。因此，函数永远不应该返回指向基于栈存储的指针。

### 堆存储

接下来使用若干代码示例凸显在 C 语言中使用堆存储的优点。在第一个示例中，使用了最优方案分配、使用和释放堆存储。第二个示例（在下一节中）将堆存储嵌套在了其他堆存储中，这会使其释放操作变得复杂。

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

int* get_heap_array(unsigned n) {
  int* heap_nums = malloc(sizeof(int) * n);

  unsigned i;
  for (i = 0; i < n; i++)
    heap_nums[i] = i + 1;  /* initialize the array */

  /* stack storage for variables heap_nums and i released
     automatically when get_num_array returns */
  return heap_nums; /* return (copy of) the pointer */
}

int main() {
  unsigned n = 100, i;
  int* heap_nums = get_heap_array(n); /* save returned address */

  if (NULL == heap_nums) /* malloc failed */
    fprintf(stderr, "%s\n", "malloc(...) failed...");
  else {
    for (i = 0; i < n; i++) printf("%i\n", heap_nums[i]);
    free(heap_nums); /* free the heap storage */
  }
  return 0;
}

```

上面的 `heap` 程序有两个函数： `main` 函数使用参数（示例中为 100）调用 `get_heap_array` 函数，参数用来指定数组应该有多少个 `int` 元素。因为堆分配可能会失败，`main` 函数会检查 `get_heap_array` 是否返回了 `NULL`；如果是，则表示失败。如果分配成功，`main` 将打印数组中的 `int` 值，然后立即调用库函数 `free` 来对堆存储解除分配。这就是最优的方案。

`get_heap_array` 函数以下列语句开头，该语句值得仔细研究一下：

```
int* heap_nums = malloc(sizeof(int) * n); /* heap allocation */

```

`malloc` 库函数及其变体函数针对字节进行操作；因此，`malloc` 的参数是 `n` 个 `int` 类型元素所需的字节数（`sizeof(int)` 在标准现代设备上是四个字节）。`malloc` 函数返回所分配字节段的首地址，如果失败则返回 `NULL` .

如果成功调用 `malloc`，在现代台式机上其返回的地址大小为 64 位。在手持设备和早些时候的台式机上，该地址的大小可能是 32 位，或者甚至更小，具体取决于其年代。堆分配数组中的元素是 `int` 类型，这是一个四字节的有符号整数。这些堆分配的 `int` 的地址存储在基于栈的局部变量 `heap_nums` 中。可以参考下图：

```
                 heap-based
 stack-based        /
     \        +----+----+   +----+
 heap-nums--->|int1|int2|...|intN|
              +----+----+   +----+

```

一旦 `get_heap_array` 函数返回，指针变量 `heap_nums` 的栈存储将自动回收——但动态 `int` 数组的堆存储仍然存在，这就是 `get_heap_array` 函数返回这个地址（的副本）给 `main` 函数的原因：它现在负责在打印数组的整数后，通过调用库函数 `free` 显式释放堆存储：

```
free(heap_nums); /* free the heap storage */

```

`malloc` 函数不会初始化堆分配的存储空间，因此里面是随机值。相比之下，其变体函数 `calloc` 会将分配的存储初始化为零。这两个函数都返回 `NULL` 来表示分配失败。

在 `heap` 示例中，`main` 函数在调用 `free` 后会立即返回，正在执行的程序会终止，这会让系统回收所有已分配的堆存储。尽管如此，程序员应该养成在不再需要时立即显式释放堆存储的习惯。

### 嵌套堆分配

下一个代码示例会更棘手一些。C 语言有很多返回指向堆存储的指针的库函数。下面是一个常见的使用情景：

1、C 程序调用一个库函数，该函数返回一个指向基于堆的存储的指针，而指向的存储通常是一个聚合体，如数组或结构体：

```
SomeStructure* ptr = lib_function(); /* returns pointer to heap storage */

```

2、 然后程序使用所分配的存储。

3、 对于清理而言，问题是对 `free` 的简单调用是否会清理库函数分配的所有堆分配存储。例如，`SomeStructure` 实例可能有指向堆分配存储的字段。一个特别麻烦的情况是动态分配的结构体数组，每个结构体有一个指向又一层动态分配的存储的字段。下面的代码示例说明了这个问题，并重点关注了如何设计一个可以安全地为客户端提供堆分配存储的库。

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

typedef struct {
  unsigned id;
  unsigned len;
  float*   heap_nums;
} HeapStruct;
unsigned structId = 1;

HeapStruct* get_heap_struct(unsigned n) {
  /* Try to allocate a HeapStruct. */
  HeapStruct* heap_struct = malloc(sizeof(HeapStruct));
  if (NULL == heap_struct) /* failure? */
    return NULL;           /* if so, return NULL */

  /* Try to allocate floating-point aggregate within HeapStruct. */
  heap_struct->heap_nums = malloc(sizeof(float) * n);
  if (NULL == heap_struct->heap_nums) {  /* failure? */
    free(heap_struct);                   /* if so, first free the HeapStruct */
    return NULL;                         /* then return NULL */
  }

  /* Success: set fields */
  heap_struct->id = structId++;
  heap_struct->len = n;

  return heap_struct; /* return pointer to allocated HeapStruct */
}

void free_all(HeapStruct* heap_struct) {
  if (NULL == heap_struct) /* NULL pointer? */
    return;                /* if so, do nothing */

  free(heap_struct->heap_nums); /* first free encapsulated aggregate */
  free(heap_struct);            /* then free containing structure */
}

int main() {
  const unsigned n = 100;
  HeapStruct* hs = get_heap_struct(n); /* get structure with N floats */

  /* Do some (meaningless) work for demo. */
  unsigned i;
  for (i = 0; i < n; i++) hs->heap_nums[i] = 3.14 + (float) i;
  for (i = 0; i < n; i += 10) printf("%12f\n", hs->heap_nums[i]);

  free_all(hs); /* free dynamically allocated storage */

  return 0;
}

```

上面的 `nestedHeap` 程序示例以结构体 `HeapStruct` 为中心，结构体中又有名为 `heap_nums` 的指针字段：

```
typedef struct {
  unsigned id;
  unsigned len;
  float*   heap_nums; /** pointer **/
} HeapStruct;

```

函数 `get_heap_struct` 尝试为 `HeapStruct` 实例分配堆存储，这需要为字段 `heap_nums` 指向的若干个 `float` 变量分配堆存储。如果成功调用 `get_heap_struct` 函数，并将指向堆分配结构体的指针以 `hs` 命名，其结果可以描述如下：

```
hs-->HeapStruct instance
        id
        len
        heap_nums-->N contiguous float elements

```

在 `get_heap_struct` 函数中，第一个堆分配过程很简单：

```
HeapStruct* heap_struct = malloc(sizeof(HeapStruct));
if (NULL == heap_struct) /* failure? */
  return NULL;           /* if so, return NULL */

```

`sizeof(HeapStruct)` 包括了 `heap_nums` 字段的字节数（32 位机器上为 4，64 位机器上为 8），`heap_nums` 字段则是指向动态分配数组中的 `float` 元素的指针。那么，问题关键在于 `malloc` 为这个结构体传送了字节空间还是表示失败的 `NULL`；如果是 `NULL`，`get_heap_struct` 函数就也返回 `NULL` 以通知调用者堆分配失败。

第二步尝试堆分配的过程更复杂，因为在这一步，`HeapStruct` 的堆存储已经分配好了：

```
heap_struct->heap_nums = malloc(sizeof(float) * n);
if (NULL == heap_struct->heap_nums) {  /* failure? */
  free(heap_struct);                   /* if so, first free the HeapStruct */
  return NULL;                         /* and then return NULL */
}

```

传递给 `get_heap_struct` 函数的参数 `n` 指明动态分配的 `heap_nums` 数组中应该有多少个 `float` 元素。如果可以分配所需的若干个 `float` 元素，则该函数在返回 `HeapStruct` 的堆地址之前会设置结构的 `id` 和 `len` 字段。 但是，如果尝试分配失败，则需要两个步骤来实现最优方案：

1、 必须释放 `HeapStruct` 的存储以避免内存泄漏。对于调用 `get_heap_struct` 的客户端函数而言，没有动态 `heap_nums` 数组的 `HeapStruct` 可能就是没用的；因此，`HeapStruct` 实例的字节空间应该显式释放，以便系统可以回收这些空间用于未来的堆分配。

2、 返回 `NULL` 以标识失败。

如果成功调用 `get_heap_struct` 函数，那么释放堆存储也很棘手，因为它涉及要以正确顺序进行的两次 `free` 操作。因此，该程序设计了一个 `free_all` 函数，而不是要求程序员再去手动实现两步释放操作。回顾一下，`free_all` 函数是这样的：

```
void free_all(HeapStruct* heap_struct) {
  if (NULL == heap_struct) /* NULL pointer? */
    return;                /* if so, do nothing */

  free(heap_struct->heap_nums); /* first free encapsulated aggregate */
  free(heap_struct);            /* then free containing structure */
}

```

检查完参数 `heap_struct` 不是 `NULL` 值后，函数首先释放 `heap_nums` 数组，这步要求 `heap_struct` 指针此时仍然是有效的。先释放 `heap_struct` 的做法是错误的。一旦 `heap_nums` 被释放，`heap_struct` 就可以释放了。如果 `heap_struct` 被释放，但 `heap_nums` 没有被释放，那么数组中的 `float` 元素就会泄漏：仍然分配了字节空间，但无法被访问到——因此一定要记得释放 `heap_nums`。存储泄漏将一直持续，直到 `nestedHeap` 程序退出，系统回收泄漏的字节时为止。

关于 `free` 库函数的注意事项就是要有顺序。回想一下上面的调用示例：

```
free(heap_struct->heap_nums); /* first free encapsulated aggregate */
free(heap_struct);            /* then free containing structure */

```

这些调用释放了分配的存储空间——但它们并 *不是* 将它们的操作参数设置为 `NULL`（`free` 函数会获取地址的副本作为参数；因此，将副本更改为 `NULL` 并不会改变原地址上的参数值）。例如，在成功调用 `free` 之后，指针 `heap_struct` 仍然持有一些堆分配字节的堆地址，但是现在使用这个地址将会产生错误，因为对 `free` 的调用使得系统有权回收然后重用这些分配过的字节。

使用 `NULL` 参数调用 `free` 没有意义，但也没有什么坏处。而在非 `NULL` 的地址上重复调用 `free` 会导致不确定结果的错误：

```
free(heap_struct);  /* 1st call: ok */
free(heap_struct);  /* 2nd call: ERROR */

```

### 内存泄漏和堆碎片化

“内存泄漏”是指动态分配的堆存储变得不再可访问。看一下相关的代码段：

```
float* nums = malloc(sizeof(float) * 10); /* 10 floats */
nums[0] = 3.14f;                          /* and so on */
nums = malloc(sizeof(float) * 25);        /* 25 new floats */

```

假如第一个 `malloc` 成功，第二个 `malloc` 会再将 `nums` 指针重置为 `NULL`（分配失败情况下）或是新分配的 25 个 `float` 中第一个的地址。最初分配的 10 个 `float` 元素的堆存储仍然处于被分配状态，但此时已无法再对其访问，因为 `nums` 指针要么指向别处，要么是 `NULL`。结果就是造成了 40 个字节（`sizeof(float) * 10`）的泄漏。

在第二次调用 `malloc` 之前，应该释放最初分配的存储空间：

```
float* nums = malloc(sizeof(float) * 10); /* 10 floats */
nums[0] = 3.14f;                          /* and so on */
free(nums);                               /** good **/
nums = malloc(sizeof(float) * 25);        /* no leakage */

```

即使没有泄漏，堆也会随着时间的推移而碎片化，需要对系统进行碎片整理。例如，假设两个最大的堆块当前的大小分别为 200MB 和 100MB。然而，这两个堆块并不连续，进程 `P` 此时又需要分配 250MB 的连续堆存储。在进行分配之前，系统可能要对堆进行 *碎片整理* 以给 `P` 提供 250MB 连续存储空间。碎片整理很复杂，因此也很耗时。

内存泄漏会创建处于已分配状态但不可访问的堆块，从而会加速碎片化。因此，释放不再需要的堆存储是程序员帮助减少碎片整理需求的一种方式。

### 诊断内存泄漏的工具

有很多工具可用于分析内存效率和安全性，其中我最喜欢的是 [valgrind](https://www.valgrind.org/)。为了说明该工具如何处理内存泄漏，这里给出 `leaky` 示例程序：

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

int* get_ints(unsigned n) {
  int* ptr = malloc(n * sizeof(int));
  if (ptr != NULL) {
    unsigned i;
    for (i = 0; i < n; i++) ptr[i] = i + 1;
  }
  return ptr;
}

void print_ints(int* ptr, unsigned n) {
  unsigned i;
  for (i = 0; i < n; i++) printf("%3i\n", ptr[i]);
}

int main() {
  const unsigned n = 32;
  int* arr = get_ints(n);
  if (arr != NULL) print_ints(arr, n);

  /** heap storage not yet freed... **/
  return 0;
}

```

`main` 函数调用了 `get_ints` 函数，后者会试着从堆中 `malloc` 32 个 4 字节的 `int`，然后初始化动态数组（如果 `malloc` 成功）。初始化成功后，`main` 函数会调用 `print_ints`函数。程序中并没有调用 `free` 来对应 `malloc` 操作；因此，内存泄漏了。

如果安装了 `valgrind` 工具箱，下面的命令会检查 `leaky` 程序是否存在内存泄漏（`%` 是命令行提示符）：

```
% valgrind --leak-check=full ./leaky

```

绝大部分输出都在下面给出了。左边的数字 207683 是正在执行的 `leaky` 程序的进程标识符。这份报告给出了泄漏发生位置的详细信息，本例中位置是在 `main` 函数所调用的 `get_ints` 函数中对 `malloc` 的调用处。

```
==207683== HEAP SUMMARY:
==207683==   in use at exit: 128 bytes in 1 blocks
==207683==   total heap usage: 2 allocs, 1 frees, 1,152 bytes allocated
==207683==
==207683== 128 bytes in 1 blocks are definitely lost in loss record 1 of 1
==207683==   at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==207683==   by 0x109186: get_ints (in /home/marty/gc/leaky)
==207683==   by 0x109236: main (in /home/marty/gc/leaky)
==207683==
==207683== LEAK SUMMARY:
==207683==   definitely lost: 128 bytes in 1 blocks
==207683==   indirectly lost: 0 bytes in 0 blocks
==207683==   possibly lost: 0 bytes in 0 blocks
==207683==   still reachable: 0 bytes in 0 blocks
==207683==   suppressed: 0 bytes in 0 blocks

```

如果把 `main` 函数改成在对 `print_ints` 的调用之后，再加上一个对 `free` 的调用，`valgrind` 就会对 `leaky` 程序给出一个干净的内存健康清单：

```
==218462== All heap blocks were freed -- no leaks are possible

```

### 静态区存储

在正统的 C 语言中，函数必须在所有块之外定义。这是一些 C 编译器支持的特性，杜绝了在另一个函数体内定义一个函数的可能。我举的例子都是在所有块之外定义的函数。这样的函数要么是 `static` ，即静态的，要么是 `extern`，即外部的，其中 `extern` 是默认值。

C 语言中，以 `static` 或 `extern` 修饰的函数和变量驻留在内存中所谓的 **静态区** 中，因为在程序执行期间该区域大小是固定不变的。这两个存储类型的语法非常复杂，我们应该回顾一下。在回顾之后，会有一个完整的代码示例来生动展示语法细节。在所有块之外定义的函数或变量默认为 `extern`；因此，函数和变量要想存储类型为 `static` ，必须显式指定：

```
/** file1.c: outside all blocks, five definitions  **/
int foo(int n) { return n * 2; }     /* extern by default */
static int bar(int n) { return n; }  /* static */
extern int baz(int n) { return -n; } /* explicitly extern */

int num1;        /* extern */
static int num2; /* static */

```

`extern` 和 `static` 的区别在于作用域：`extern` 修饰的函数或变量可以实现跨文件可见（需要声明）。相比之下，`static` 修饰的函数仅在 *定义* 该函数的文件中可见，而 `static` 修饰的变量仅在 *定义* 该变量的文件（或文件中的块）中可见：

```
static int n1;    /* scope is the file */
void func() {
   static int n2; /* scope is func's body */
   ...
}

```

如果在所有块之外定义了 `static` 变量，例如上面的 `n1`，该变量的作用域就是定义变量的文件。无论在何处定义 `static` 变量，变量的存储都在内存的静态区中。

`extern` 函数或变量在给定文件中的所有块之外定义，但这样定义的函数或变量也可以在其他文件中声明。典型的做法是在头文件中 *声明* 这样的函数或变量，只要需要就可以包含进来。下面这些简短的例子阐述了这些棘手的问题。

假设 `extern` 函数 `foo` 在 `file1.c` 中 *定义*，有无关键字 `extern` 效果都一样：

```
/** file1.c **/
int foo(int n) { return n * 2; } /* definition has a body {...} */

```

必须在其他文件（或其中的块）中使用显式的 `extern` *声明* 此函数才能使其可见。以下是使 `extern` 函数 `foo` 在文件 `file2.c` 中可见的声明语句：

```
/** file2.c: make function foo visible here **/
extern int foo(int); /* declaration (no body) */

```

回想一下，函数声明没有用大括号括起来的主体，而函数定义会有这样的主体。

为了便于查看，函数和变量声明通常会放在头文件中。准备好需要声明的源代码文件，然后就可以 `#include` 相关的头文件。下一节中的 `staticProg` 程序演示了这种方法。

至于 `extern` 的变量，规则就变得更棘手了（很抱歉增加了难度！）。任何 `extern` 的对象——无论函数或变量——必须 *定义* 在所有块之外。此外，在所有块之外定义的变量默认为 `extern`：

```
/** outside all blocks **/
int n; /* defaults to extern */

```

但是，只有在变量的 *定义* 中显式初始化变量时，`extern` 才能在变量的 *定义* 中显式修饰（LCTT 译注：换言之，如果下列代码中的 `int n1;` 行前加上 `extern`，该行就由 *定义* 变成了 *声明*）：

```
/** file1.c: outside all blocks **/
int n1;             /* defaults to extern, initialized by compiler to zero */
extern int n2 = -1; /* ok, initialized explicitly */
int n3 = 9876;      /* ok, extern by default and initialized explicitly */

```

要使在 `file1.c` 中定义为 `extern` 的变量在另一个文件（例如 `file2.c`）中可见，该变量必须在 `file2.c` 中显式 *声明* 为 `extern` 并且不能初始化（初始化会将声明转换为定义）：

```
/** file2.c **/
extern int n1; /* declaration of n1 defined in file1.c */

```

为了避免与 `extern` 变量混淆，经验是在 *声明* 中显式使用 `extern`（必须），但不要在 *定义* 中使用（非必须且棘手）。对于函数，`extern` 在定义中是可选使用的，但在声明中是必须使用的。下一节中的 `staticProg` 示例会把这些点整合到一个完整的程序中。

### staticProg 示例

`staticProg` 程序由三个文件组成：两个 C 语言源文件（`static1.c` 和 `static2.c`）以及一个头文件（`static.h`），头文件中包含两个声明：

```
/** header file static.h **/
#define NumCount 100               /* macro */
extern int global_nums[NumCount];  /* array declaration */
extern void fill_array();          /* function declaration */

```

两个声明中的 `extern`，一个用于数组，另一个用于函数，强调对象在别处（“外部”）*定义*：数组 `global_nums` 在文件 `static1.c` 中定义（没有显式的 `extern`），函数 `fill_array` 在文件 `static2.c` 中定义（也没有显式的 `extern`）。每个源文件都包含了头文件 `static.h`。`static1.c` 文件定义了两个驻留在内存静态区域中的数组（`global_nums` 和 `more_nums`）。第二个数组有 `static` 修饰，这将其作用域限制为定义数组的文件 (`static1.c`)。如前所述， `extern` 修饰的 `global_nums` 则可以实现在多个文件中可见。

```
/** static1.c **/
#include <stdio.h>
#include <stdlib.h>

#include "static.h"             /* declarations */

int global_nums[NumCount];      /* definition: extern (global) aggregate */
static int more_nums[NumCount]; /* definition: scope limited to this file */

int main() {
  fill_array(); /** defined in file static2.c **/

  unsigned i;
  for (i = 0; i < NumCount; i++)
    more_nums[i] = i * -1;

  /* confirm initialization worked */
  for (i = 0; i < NumCount; i += 10)
    printf("%4i\t%4i\n", global_nums[i], more_nums[i]);

  return 0;
}

```

下面的 `static2.c` 文件中定义了 `fill_array` 函数，该函数由 `main`（在 `static1.c` 文件中）调用；`fill_array` 函数会给名为 `global_nums` 的 `extern` 数组中的元素赋值，该数组在文件 `static1.c` 中定义。使用两个文件的唯一目的是凸显 `extern` 变量或函数能够跨文件可见。

```
/** static2.c **/
#include "static.h" /** declarations **/

void fill_array() { /** definition **/
  unsigned i;
  for (i = 0; i < NumCount; i++) global_nums[i] = i + 2;
}

```

`staticProg` 程序可以用如下编译：

```
% gcc -o staticProg static1.c static2.c

```

### 从汇编语言看更多细节

现代 C 编译器能够处理 C 和汇编语言的任意组合。编译 C 源文件时，编译器首先将 C 代码翻译成汇编语言。这是对从上文 `static1.c` 文件生成的汇编语言进行保存的命令：

```
% gcc -S static1.c

```

生成的文件就是 `static1.s`。这是文件顶部的一段代码，额外添加了行号以提高可读性：

```
    .file    "static1.c"          ## line  1
    .text                         ## line  2
    .comm    global_nums,400,32   ## line  3
    .local    more_nums           ## line  4
    .comm    more_nums,400,32     ## line  5
    .section    .rodata           ## line  6
.LC0:                             ## line  7
    .string    "%4i\t%4i\n"       ## line  8
    .text                         ## line  9
    .globl    main                ## line 10
    .type    main, @function      ## line 11
main:                             ## line 12
...

```

诸如 `.file`（第 1 行）之类的汇编语言指令以句点开头。顾名思义，指令会指导汇编程序将汇编语言翻译成机器代码。`.rodata` 指令（第 6 行）表示后面是只读对象，包括字符串常量 `"%4i\t%4i\n"`（第 8 行），`main` 函数（第 12 行）会使用此字符串常量来实现格式化输出。作为标签引入（通过末尾的冒号实现）的 `main` 函数（第 12 行），同样也是只读的。

在汇编语言中，标签就是地址。标签 `main:`（第 12 行）标记了 `main` 函数代码开始的地址，标签 `.LC0:`（第 7 行）标记了格式化字符串开头所在的地址。

`global_nums`（第 3 行）和 `more_nums`（第 4 行）数组的定义包含了两个数字：400 是每个数组中的总字节数，32 是每个数组（含 100 个 `int` 元素）中每个元素的比特数。（第 5 行中的 `.comm` 指令表示 `common name`，可以忽略。）

两个数组定义的不同之处在于 `more_nums` 被标记为 `.local`（第 4 行），这意味着其作用域仅限于其所在文件 `static1.s`。相比之下，`global_nums` 数组就能在多个文件中实现可见，包括由 `static1.c` 和 `static2.c` 文件翻译成的汇编文件。

最后，`.text` 指令在汇编代码段中出现了两次（第 2 行和第 9 行）。术语“text”表示“只读”，但也会涵盖一些读/写变量，例如两个数组中的元素。尽管本文展示的汇编语言是针对 Intel 架构的，但 Arm6 汇编也非常相似。对于这两种架构，`.text` 区域中的变量（本例中为两个数组中的元素）会自动初始化为零。

### 总结

C 语言中的内存高效和内存安全编程准则很容易说明，但可能会很难遵循，尤其是在调用设计不佳的库的时候。准则如下：

* 尽可能使用栈存储，进而鼓励编译器将通用寄存器用作暂存器，实现优化。栈存储代表了高效的内存使用并促进了代码的整洁和模块化。永远不要返回指向基于栈的存储的指针。
* 小心使用堆存储。C（和 C++）中的重难点是确保动态分配的存储尽快解除分配。良好的编程习惯和工具（如 `valgrind`）有助于攻关这些重难点。优先选用自身提供释放函数的库，例如 `nestedHeap` 代码示例中的 `free_all` 释放函数。
* 谨慎使用静态存储，因为这种存储会自始至终地影响进程的内存占用。特别是尽量避免使用 `extern` 和 `static` 数组。

本文 C 语言代码示例可在我的网站（<https://condor.depaul.edu/mkalin>）上找到。

---

via: <https://opensource.com/article/21/8/memory-programming-c>

作者：[Marty Kalin](https://opensource.com/users/mkalindepauledu) 选题：[lujun9972](https://github.com/lujun9972) 译者：[unigeorge](https://github.com/unigeorge) 校对：[wxy](https://github.com/wxy)

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


在 Fedora CoreOS 上运行 GitHub Actions

Summary: GitHub Actions 是一项为快速建立持续集成和交付（CI/CD）工作流程而提供的服务。

![](/data/attachment/album/202110/03/110314hhrbobyxzd55dhho.jpg)

[GitHub Actions](https://docs.github.com/en/actions) 是一项为快速建立持续集成和交付（CI/CD）工作流程而提供的服务。这些工作流程在被称为“<ruby> 运行器 <rt>  runner </rt></ruby>”的主机上运行。GitHub 提供的 [托管运行器](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners) 的操作系统的选择是有限的（Windows Server、Ubuntu、MacOS）。

另一个选择是使用 [自托管](https://docs.github.com/en/actions/hosting-your-own-runners) 的运行器，这让仓库管理员对运行器有更多控制。自托管的运行程序是专门为某个存储库或组织服务的。下面的文章介绍了使用 Fedora CoreOS 配置自托管运行程序的步骤。

### 入门

Fedora CoreOS 是一个精简的操作系统，旨在便于大规模的部署和维护。该操作系统会自动更新，并默认提供运行容器所需的工具。由于这些原因，Fedora CoreOS 是运行 CI/CD 工作流程的一个极佳选择。

配置和配备 Fedora CoreOS 机器的第一步是生成一个 [Ignition](https://github.com/coreos/ignition) 文件。[Butane](https://github.com/coreos/butane) 允许你使用更友好的格式（YAML）生成 Ignition 文件。

#### 配置一个 Fedora CoreOS 运行器

要在 Fedora CoreOS 上执行 GitHub Actions，托管主机需要用于注册和运行该运行器的二进制文件和脚本。从 [Actions 运行器项目](https://github.com/actions/runner) 下载二进制文件和脚本，并部署在 `/usr/local/sbin/actions-runner` 下。

```
version: "1.3.0"
variant: fcos
storage:
  directories:
    - path: /usr/local/sbin/actions-runner
      mode: 0755
      user:
        name: core
      group:
        name: core
  files:
    - path: /usr/local/sbin/actions-runner/actions-runner-linux.tar.gz
      overwrite: true
      contents:
        source: https://github.com/actions/runner/releases/download/v2.278.0/actions-runner-linux-x64-2.278.0.tar.gz
      mode: 0755
      user:
        name: core
      group:
        name: core

```

#### 注册和删除令牌

为一个项目配置运行器需要一个“<ruby> 令牌 <rt>  token </rt></ruby>”。这可以防止在没有正确权限的情况下从项目中注册或删除自托管的运行器。GitHub 提供的令牌有一个小时的过期时间。如果运行器在这个时间之后重新启动，它将需要一个新的注册令牌。

该令牌可能出问题，特别是在 Fedora CoreOS 自动更新时。更新过程希望托管主机在收到新数据后至少每隔几周重启一次。

幸运的是，可以使用 GitHub REST API 来获取这些令牌，并在托管主机每次重启时自动配置运行器。下面的 `manage-runner.sh` 脚本使用 API 来获取令牌，删除任何已经配置好的运行器，并用新的令牌注册运行器。

```
#!/bin/bash
# Handles the Github Action runner configuration.
# Remove and Registration token expires after 1 hour, if we want our runner
# to work after a reboot (auto update) we need to refresh the tokens.

# First remove the runner with a fresh remove token
REMOVE_TOKEN=$(curl -u ${GITHUB_USER}:${GITHUB_TOKEN} -X POST -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/${GITHUB_USER}/${GITHUB_REPO}/actions/runners/remove-token | jq -r '.token')
/usr/local/sbin/actions-runner/config.sh remove --token ${REMOVE_TOKEN}

# Then register the runner with a fresh registration token
REGISTRATION_TOKEN=$(curl -u ${GITHUB_USER}:${GITHUB_TOKEN} -X POST -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/${GITHUB_USER}/${GITHUB_REPO}/actions/runners/registration-token | jq -r '.token')
/usr/local/sbin/actions-runner/config.sh --url https://github.com/cverna/fcos-actions-runner --token ${REGISTRATION_TOKEN} --labels fcos --unattended

```

上面的脚本使用了一些环境变量，包含 GitHub 用户名和用于验证 REST API 请求的 <ruby> <a href="https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token">  个人访问令牌 </a> <rt>  Personal Access Token </rt></ruby>。个人访问令牌需要存储库权限，以便成功检索运行器的注册和移除令牌。该令牌是安全敏感信息，所以最好将其存储在一个具有更严格权限的不同文件中。在这个例子中，这个文件是 `actions-runner`。

```
GITHUB_USER=<user>
GITHUB_REPO=<repo>
GITHUB_TOKEN=<personal_access_token>

```

以下是创建这两个文件 `manage-runner.sh` 和 `actions-runner` 的 Butane 片段。

```
- path: /usr/local/sbin/actions-runner/manage-runner.sh
      contents:
        local: manage-runner.sh
      mode: 0755
      user:
        name: core
      group:
        name: core
    - path: /etc/actions-runner
      contents:
        local: actions-runner
      mode: 0700
      user:
        name: core
      group:
        name: core

```

### 在 Fedora CoreOS 上运行 Actions

最后，创建用于配置和启动运行器的 systemd 服务。在 Butane 配置文件中定义这些服务。

```
systemd:
  units:
    - name: github-runner-configure.service
      enabled: true
      contents: |
        [Unit]
        Description=Configure the github action runner for a repository
        After=network-online.target boot-complete.target
        Requires=boot-complete.target
        [Service]
        EnvironmentFile=/etc/actions-runner
        Type=oneshot
        RemainAfterExit=yes
        User=core
        WorkingDirectory=/usr/local/sbin/actions-runner
        ExecStartPre=tar xvf actions-runner-linux.tar.gz --no-same-owner
        ExecStart=/usr/local/sbin/actions-runner/manage-runner.sh
        [Install]
        WantedBy=multi-user.target
    - name: github-runner.service
      enabled: true
      contents: |
        [Unit]
        Description=Run the github action runner
        After=github-runner-configure.service
        [Service]
        WorkingDirectory=/usr/local/sbin/actions-runner
        User=core
        ExecStart=/usr/local/sbin/actions-runner/run.sh
        [Install]
        WantedBy=multi-user.target

```

这将创建两个服务：`github-runner-configure.service`（在主机启动完成后运行一次）和 `github-runner.service`（运行 Actions 运行器二进制文件并等待新的 CI/CD 作业）。

现在 Butane 配置已经完成，从中生成一个 Ignition 文件并配备一个 Fedora CoreOS Actions 运行器。

```
$ podman run -i --rm -v $PWD:/code:z --workdir /code quay.io/coreos/butane:release --pretty --strict --files-dir /code config.yaml -o config.ignition

```

一旦 Ignition 文件生成，它就可以用来在 [支持](https://docs.fedoraproject.org/en-US/fedora-coreos/bare-metal/) Fedora CoreOS 的平台上配备一个运行器。

### 配置一个 Action 来使用一个自托管的运行器

下面的测试 Action 工作流程将测试 FCOS 的自托管的运行器。在你的 git 存储库中创建以下文件 `.github/workflows/main.yml`。

```
# This is a basic workflow to help you get started with Actions

name: CI

# Controls when the action will run.
on:
  # Triggers the workflow on push or pull request events but only for the main branch
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: fcos

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Runs a single command using the runners shell
      - name: Run a one-line script
        run: podman run --rm fedora-minimal:34 echo Hello World !

```

请注意，`runs-on` 的配置被设置为使用标签为 `fcos` 的运行器。

本文介绍的代码可以在 [这里](https://github.com/cverna/fcos-actions-runner) 中找到。

---

via: <https://fedoramagazine.org/run-github-actions-on-fedora-coreos/>

作者：[Clément Verna](https://fedoramagazine.org/author/cverna/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


硬核观察 #413 Linus Torvalds 在采访中再次提及一些观点

Summary: 更多：• 上百万人遇到 Let's Encrypt 根证书到期问题 • 亚马逊、谷歌、微软和 IBM 等签署“可信云原则”

![](/data/attachment/album/202110/03/201151ftyy6v22ssxltpnn.jpg)

### Linus Torvalds 在采访中再次提及一些观点

![](/data/attachment/album/202110/03/201202woaovaaldlrf5vxw.jpg)

Linux 创始人参加了今年的北美开源峰会，并按惯例接受了[采访](https://thenewstack.io/linus-torvalds-on-community-rust-and-linuxs-longevity/)，就一些经常被问到的问题发表了一些并不算新的观点：

* 在关于使用 Rust 编写 Linux 内核模块的讨论中，他说，从技术角度有没有意义不重要，重要的是要有趣。
* 他表示真的很喜欢 C 语言，它是一种伟大的语言，当然，他也承认 C 语言存在容易被所有人忽视的陷阱，并认为 Rust 可能真的是一种解决方案。
* 关于社区，他说，社区使 Linux 保持了活力，要不是社区，Linux 可能早被他扔在脑后了。

>
> 老王点评：这些观点我们都知道了，但是我们还是一次次不厌其烦的愿意听到 Linus 讲它们。这就是精神领袖。
>
>
>

### 上百万人遇到 Let's Encrypt 根证书到期问题

![](/data/attachment/album/202110/03/201221m6pqz29cyqdr6zhh.jpg)

Let's Encrypt 之前使用的根证书于 9 月 30 日[到期](https://techcrunch.com/2021/09/21/lets-encrypt-root-expiry/)，可能受到证书过期影响的设备是那些不定期更新的设备。它早在 5 月就通知大家这一消息，并提供替代方案和变通办法，以确保设备在转换期间不受影响。周四晚上，至少有 200 万人在他们的手机、电脑或智能小工具上遇到了由于证书问题导致的一些互联网连接问题。数十种主要科技产品和服务受到证书过期的[严重影响](https://www.zdnet.com/article/fortinet-shopify-others-report-issues-after-root-ca-certificate-from-lets-encrypt-expires/)，如亚马逊、谷歌和微软的云计算服务；思科的 IT 和云安全服务；Shopify 的卖家无法登录；以及一些游戏和工作流平台等等。

>
> 老王点评：Let's Encrypt 几乎是以一己之力推动了 HTTPS 的普及，但是由于历史原因，其根证书的有效期并不很长，所以 Let's Encrypt 越成功，这种遗留问题就越大。
>
>
>

### 亚马逊、谷歌、微软和 IBM 等签署“可信云原则”

![](/data/attachment/album/202110/03/201236umnv79mlzm9f37as.jpg)

这些技术巨头联合起来[建立](https://www.zdnet.com/article/amazon-google-microsoft-and-other-tech-giants-establish-trusted-cloud-principles/)了“[可信云原则](https://trustedcloudprinciples.com/principles/)”，声称这是对保护客户权利的承诺。具体主要包括：除“特殊情况”外，政府应首先直接向企业客户而非云供应商索取数据；当政府试图直接从云服务供应商处获取客户数据时，客户应有权获得通知；云供应商应有明确的程序来质疑政府对客户数据的访问请求，包括通知相关数据保护机构，以保护客户的利益。此外，该原则还提出，政府应建立机制，提出并解决彼此之间的冲突，使云服务提供商在一国的合法合规性不等于在另一国的违法行为；政府应支持跨境数据流动。他们承诺支持允许政府通过遵守人权标准的透明程序索取数据的法律，支持在国家和国际层面改进规则和条例，以保护云客户的安全、隐私和安保以及他们对数据的所有权。

>
> 老王点评：这种业界共识需要更多方的参与，目前看起来想落地没那么多容易。
>
>
>


Linux Mint 和 Fedora：应该使用哪一个？

Fedora + 1

用 Ubuntu

![](/data/attachment/album/202110/04/123523kzzm3x4yr2ppmfj9.jpg)

Linux Mint 是一个 [为初学者定制的流行的 Linux 发行版](https://itsfoss.com/best-linux-beginners/)，同时为用户提供了与 Windows 类似的体验。事实上，它有 [一些地方比 Ubuntu 做的更好](https://itsfoss.com/linux-mint-vs-ubuntu/)，这使它成为每一类用户的合适选择。

它是基于 Ubuntu 的，完全由社区所支持。

另一方面，Fedora 是一个尝鲜发行版，它专注于纳入令人兴奋的变化，最终使其成为红帽企业 Linux（RHEL）的一部分。

与 Linux Mint 不同，Fedora 并不完全专注于个人（或非开发者）使用。即使他们提供了一个工作站版本，其目标也是开发者和有经验的 Linux 用户。

### Fedora 或 Mint，应该根据什么选择？

虽然我们知道 Fedora 并不是完全面向 Linux 新手的，但许多用户喜欢使用 Fedora 作为他们的日常系统。因此，在这篇文章中，我们将阐明两者之间的一些区别，以帮助你选择一个在你的台式机上使用的操作系统。

#### 系统要求 & 硬件兼容性

![](/data/attachment/album/202110/04/123531gtupupwufdiudkuw.png)

在选择任何 Linux 发行版之前，你都应该看一下它的系统要求，并检查硬件兼容性。

在这方面，Linux Mint 和 Fedora 都需要至少 2GB 的内存、20GB 的磁盘空间，以及 1024 x 768 分辨率的显示器来获得入门级的体验。

是的，官方文件可能提到 1GB 内存就可以起步，但要看实际使用情况。除非你有一台复古的电脑，想为一个特定的目的恢复它，否则它就不在考虑范围之内。

![Linux Mint 资源使用情况](/data/attachment/album/202110/04/123533lcabeozhauc3obtf.png)

在技术上，两者都支持现代的和陈旧的硬件，你只有在安装时才会知道软件/驱动是否支持它。除非你有一个特殊的外围设备或具有特殊功能的硬件组件，否则硬件支持可能不是什么大问题。

Linux Mint 19 系列仍然提供了对 32 位系统的支持，你可以使用它到 2023 年 4 月。而 Fedora 已经不支持 32 位系统了。

#### 软件更新周期

![Linux Mint 更新管理器](/data/attachment/album/202110/04/123534axkx30gxxxqx0kxx.png)

Linux Mint 专注于长期发布版（LTS），支持时间为五年。它的维护方式与 Ubuntu 相同。但没有像 Ubuntu 那样提供付费的扩展支持。

Fedora 不提供 LTS 版本，而是每 6 个月推送一次新的更新，每个版本都会得到 13 个月的软件支持。如果你愿意，你可以跳过一个版本。

如果你只是想安装一个可以使用多年的 Linux 发行版，而不在乎是不是最新的技术/功能，那么 Linux Mint 是个不错的选择。

但是，如果你想要最新的和最伟大的 Linux 技术（在一些罕见的情况下，这也可能破坏你的计算体验），并接受适应 Fedora 推动的重大变化，Fedora 可以是一个选择。

#### 桌面环境的选择

![Linux Mint Cinnamon 版](/data/attachment/album/202110/04/123537lf3868zefjx8jyhi.png)

Linux Mint 提供三种不同的 [桌面环境](https://itsfoss.com/best-linux-desktop-environments/)：MATE、Cinnamon 和 Xfce。它们有相同的更新周期，并从发布之日起支持 5 年。

尽管 Fedora 不提供 LTS 版本，但你可以通过 Fedora spins 的形式选择各种桌面。你可以得到 KDE、LXQt、MATE、Cinnamon、LXDE，以及一个内置 i3 平铺窗口管理器的版本。

![Fedora 34 with GNOME 40](/data/attachment/album/202110/04/123541kzcl6r7bpz6bpwc2.png)

所以，如果你想有更多的选择，Fedora 可以是一个相当令人激动的选择。

#### 软件可用性

![Linux Mint 的软件中心和软件包管理器](/data/attachment/album/202110/04/123542ck22ct8t47d2n2n8.png)

Linux Mint（或 Ubuntu）的默认软件库提供了大量可以安装的软件，而 Fedora 的默认软件库只坚持提供开源软件。

不仅仅限于此，Linux Mint 还配备了 [Synaptic 软件包管理器](https://itsfoss.com/synaptic-package-manager/)，这是一个令人印象深刻的安装软件的轻量级工具。

尽管你可以 [在 Fedora 中启用第三方软件库](https://itsfoss.com/fedora-third-party-repos/)，但这又是一个额外的步骤。而且，RPM Fusion 存储库可能没有 Ubuntu 的 universe 存储库那么庞大。

![Fedora 34 软件中心](/data/attachment/album/202110/04/123544i4dldy0mzgdxtltg.png)

所以，对于 Linux Mint 来说，总的来说，你可以得到更多可安装的软件包和各种安装软件的方法，开箱即用。

#### 使用和安装的便利性

对于一个 Linux 的新嫩用户来说，Ubuntu 或任何基于 Ubuntu 的发行版通常都是一个很好的开端。

从 [Ubuntu 的安装体验](https://itsfoss.com/install-ubuntu/)，到 [安装软件](https://itsfoss.com/remove-install-software-ubuntu/) 的简便性，同时还可以选择 LTS 版本，这让初学者觉得很方便。

而且，借助 Ubiquity 安装程序，Linux Mint 自然也有与 Ubuntu 相同的好处，因此，它的学习曲线最小，易于安装，易于使用。

虽然从表面上看 Fedora 并不复杂，但安装选项、软件包管理器以及默认存储库中缺乏的软件可能是一个耗时的因素。

如果你没有尝试过，我建议你试试我们的 [VirtualBox 版的 Fedora 安装指南](https://itsfoss.com/install-fedora-in-virtualbox/)。这是一个测试安装体验的好方法，然后再在你的任何生产系统上进行尝试。

#### 开箱即用的体验

最省事的体验通常是令人愉快的选择。嗯，对大多数人来说。

现在，你需要明白，根据硬件配置的不同，每个用户最终可能会有不同的“开箱即用”体验。

但是，作为参考，让我给你举个 Fedora 和 Linux Mint 的例子。

考虑到我的电脑上使用的是 NVIDIA GPU，我需要安装专有的驱动程序以获得最佳性能。

![](/data/attachment/album/202110/04/123545moy197lcoc47lx14.jpg)

而且，当我启动 Linux Mint 时，使用“驱动程序管理器”应用程序，安装驱动程序相当容易。

但是，对于 Fedora，即使我按照我们的 [在 Fedora 中安装 Nvidia 驱动程序](https://itsfoss.com/install-nvidia-drivers-fedora/) 的指南，我在重启时还是出现了一个错误。

![在 Fedora 中安装 NVIDIA 驱动程序](/data/attachment/album/202110/04/123546lsmuwjosdormcmzv.png)

不仅如此，由于某些原因，我的有线网络似乎没有被激活，因此，我没有互联网连接。

是的，当你遇到问题时，你总是可以尝试着去排除故障，但是对于 Linux Mint，我不需要这么做。所以，根据我的经验，我会推荐 Linux Mint，它有更好的开箱体验。

#### 文档

如果你依赖于文档资源并想在这个过程中挑战自己，获得不错的学习经验，我推荐你去看看 [Fedora 的文档](https://docs.fedoraproject.org/en-US/docs/)。

你会发现最近和最新的 Fedora 版本的最新信息，这是件好事。

另一方面，[Linux Mint 的文档](https://linuxmint.com/documentation.php) 没有定期更新，但在你想深入挖掘时很有用。

#### 社区支持

你会得到一个良好的社区支持。[Linux Mint 的论坛](https://forums.linuxmint.com) 是一个很基础的平台，容易使用并能解决问题。

[Fedora 的论坛](https://ask.fedoraproject.org) 是由 Discourse 驱动的，它是最 [流行的现代开源论坛软件](https://itsfoss.com/open-source-forum-software/) 之一。

#### 企业与社区的角度

Fedora 得到了最大的开源公司 [红帽](https://www.redhat.com/en) 的支持 —— 因此你可以得到良好的持续创新和长期的支持。

然而，正因为 Fedora 并不是为日常电脑用户而建立的，每一个版本的选择都可能完全影响你的用户体验。

另一方面，Linux Mint 完全由一个充满激情的 Linux 社区所支持，专注于使 Linux 在日常使用中更加容易和可靠。当然，它依赖于 Ubuntu 作为基础，但如果社区不喜欢上游的东西，Linux Mint 也会做出大胆的改变。

例如，Linux Mint 与 Ubuntu 官方发行版不同，默认情况下禁用了 snap。所以，如果你想使用它们，你就必须 [在 Linux Mint 中启用 snap](https://itsfoss.com/enable-snap-support-linux-mint/)。

### 总结

如果你想为你的家用电脑选择一个没有问题的、易于使用的操作系统，我的建议是 Linux Mint。但是，如果你想体验最新的、最伟大的 Linux 操作系统，同时在你的 Linux 学习经历中进行一次小小的冒险，Fedora 可以是一个不错的选择。

虽然每个操作系统都需要某种形式的故障排除，没有什么能保证你的硬件完全不出问题，但我认为 Linux Mint 对大多数用户来说可能没有问题。

在任何情况下，你可以重新审视上面提到的比较点，看看什么对你的电脑最重要。

你怎么看？你会选择 Fedora 而不是 Mint 吗？还有，为什么？请在下面的评论中告诉我。

---

via: <https://itsfoss.com/linux-mint-vs-fedora/>

作者：[Ankush Das](https://itsfoss.com/author/ankush/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


通过写“猜数字”游戏学习 Fortran

>
> Fortran 是在打孔卡时代编写的语言，因此它的语法非常有限。但你仍然可以用它编写有用和有趣的程序。
>
>
>

![](/data/attachment/album/202110/04/125311k6uwzpybabayyoag.jpg "Person typing on a 1980's computer")

Fortran 77 是我学习的第一门编译型编程语言。一开始时，我自学了如何在 Apple II 上用 BASIC 编写程序，后来又学会在 DOS 上用 QBasic 编写程序。但是当我去大学攻读物理学时，我又学习了 [Fortran](https://en.wikipedia.org/wiki/Fortran)。

Fortran 曾经在科学计算中很常见。曾几何时，所有计算机系统都有一个 Fortran 编译器。Fortran 曾经像今天的 Python 一样无处不在。因此，如果你是像我这样的物理学专业学生，在 1990 年代工作，那你肯定学习了 Fortran。

我一直认为 Fortran 与 BASIC 有点相似，所以每当我需要编写一个简短程序，来分析实验室数据或执行其他一些数值分析时，我都会很快想到 Fortran。我在空闲时用 Fortran 编写了一个“猜数字”游戏，其中计算机会在 1 到 100 之间选择一个数字，并让我猜这个数字。程序会一直循环，直到我猜对了为止。

“猜数字”程序练习了编程语言中的几个概念：如何为变量赋值、如何编写语句以及如何执行条件判断和循环。这是学习新编程语言时一个很好的的实践案例。

### Fortran 编程基础

虽然 Fortran 这些年来一直在更新，但我最熟悉的还是 Fortran 77，这是我多年前学习的实现版本。Fortran 是程序员还在打孔卡上编程的年代创建的，因此“经典” Fortran 仅限于处理可以放在打孔卡上的数据。这意味着你只能编写符合以下限制条件的经典 Fortran 程序（LCTT 译注：后来的 Fortran 95 等版本已经对这些限制做了很大的改进，如有兴趣**建议直接学习新版**）：

* 每张卡只允许一行源代码。
* 仅识别第 1-72 列（最后八列，73-80，保留给卡片分类器）。
* 行号（“标签”）位于第 1-5 列。
* 程序语句在第 7-72 列。
* 要表示跨行，请在第 6 列中输入一个连续字符（通常是 `+`）。
* 要创建注释行，请在第 1 列中输入 `C` 或 `*`。
* 只有字符 `A` 到`Z`（大写字母）、`0` 到`9`（数字）和特殊字符 `= + - * / ( ) , . $ ' :` 和空格能够使用。

虽然有这些限制，你仍然可以编写非常有用和有趣的程序。

### 在 Fortran 中猜数字

通过编写“猜数字”游戏来探索 Fortran。这是我的实现代码：

```
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
C     PROGRAM TO GUESS A NUMBER 1-100
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
      PROGRAM GUESSNUM
      INTEGER SEED, NUMBER, GUESS

      PRINT *, 'ENTER A RANDOM NUMBER SEED'
      READ *, SEED
      CALL SRAND(SEED)

      NUMBER = INT( RAND(0) * 100 + 1 )

      PRINT *, 'GUESS A NUMBER BETWEEN 1 AND 100'
 10   READ *, GUESS

      IF (GUESS.LT.NUMBER) THEN
         PRINT *, 'TOO LOW'
      ELSE IF (GUESS.GT.NUMBER) THEN
         PRINT *, 'TOO HIGH'
      ENDIF

      IF (GUESS.NE.NUMBER) GOTO 10

      PRINT *, 'THATS RIGHT!'
      END

```

如果你熟悉其他编程语言，你大概可以通过阅读源代码来弄清楚这个程序在做什么。前三行是注释块，表示程序的功能。第四行 `PROGRAM GUESSNUM` 将其标识为一个 <ruby> 程序 <rt>  program </rt></ruby>，并由最后一行的 `END` 语句关闭。

定义变量后，程序会提示用户输入随机数种子。Fortran 程序无法从操作系统初始化随机数生成器，因此你必须始终使用“种子”值和 `SRAND` <ruby> 子程序 <rt>  subroutine </rt></ruby> 启动随机数生成器。

Fortran 使用 `RAND(0)` 函数生成 0 到 0.999…… 之间的随机数。参数 `0` 告诉 `RAND` 函数生成一个随机数。将此随机数乘以 100 以生成 0 到 99.999…… 之间的数字，然后加 1 得到 1 到 100.999…… 之间的值。`INT` 函数将结果截断为整数；因此，变量 `NUMBER` 就是一个介于 1 到 100 之间的随机数。

程序会给出提示，然后进入一个循环。Fortran 不支持更现代的编程语言中可用的 `while` 或 `do-while` 循环（LCTT 译注：Fortran 95 等新版支持，也因此在一定程度上减少了 `GOTO` 的使用）。相反，你必须使用标签（行号）和 `GOTO` 语句来构建自己的循环。这就是 `READ` 语句有一个行号的原因：你可以在循环末尾使用 `GOTO` 跳转到此标签。

穿孔卡片没有 `<`（小于）和 `>`（大于）符号，因此 Fortran 采用了另一种语法来进行值比较。要测试一个值是否小于另一个值，请使用 `.LT.`（小于）。要测试一个值是否大于另一个值，请使用 `.GT.`（大于）。等于和不等于分别是 `.EQ.` 和 `.NE.`。

在每次循环中，程序都会验证用户的猜测值。如果用户的猜测值小于随机数，程序打印 `TOO LOW`，如果猜测大于随机数，程序打印 `TOO HIGH`。循环会一直持续，直到用户的猜测值等于目标随机数为止。

当循环退出时，程序打印 `THATS RIGHT!` 并立即结束运行。

```
$ gfortran -Wall -o guess guess.f

$ ./guess
 ENTER A RANDOM NUMBER SEED
93759
 GUESS A NUMBER BETWEEN 1 AND 100
50
 TOO LOW
80
 TOO HIGH
60
 TOO LOW
70
 TOO LOW
75
 TOO HIGH
73
 TOO LOW
74
 THATS RIGHT!

```

每次运行程序时，用户都需要输入不同的随机数种子。如果你总是输入相同的种子，程序给出的随机数也会一直不变。

### 在其他语言中尝试

在学习一门新的编程语言时，这个“猜数字”游戏是一个很好的入门程序，因为它以非常简单的方式练习了几个常见的编程概念。通过用不同的编程语言实现这个简单的游戏，你可以弄清一些核心概念以及比较每种语言的细节。

你有最喜欢的编程语言吗？如何用你最喜欢的语言来编写“猜数字”游戏？跟随本系列文章来查看你可能感兴趣的其他编程语言示例吧。

---

via: <https://opensource.com/article/21/1/fortran>

作者：[Jim Hall](https://opensource.com/users/jim-hall) 选题：[lujun9972](https://github.com/lujun9972) 译者：[unigeorge](https://github.com/unigeorge) 校对：[wxy](https://github.com/wxy)

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


硬核观察 #414 超人、蝙蝠侠、蜘蛛侠频频出现在密码中

Summary: 更多：• REvil 被发现其勒索软件含有秘密后门 • PostgreSQL 14 正式发布

![](/data/attachment/album/202110/04/150343pf0h6q0t0jbmfm9b.jpg)

### 超人、蝙蝠侠、蜘蛛侠频频出现在密码中

![](/data/attachment/album/202110/04/150355ageo4272fwn11e5o.jpg)

Mozilla 对密码泄露数据库的数据进行研究[发现](https://blog.mozilla.org/en/mozilla/news/superhero-passwords-may-be-your-kryptonite-wherever-you-go-online/)，数十万人使用他们最喜欢的超级英雄作为密码。在这些密码泄露事件中，“超人（Superman）”出现了 36 万次，“蝙蝠侠（Batman）”出现了 22 万次，“蜘蛛侠（Spider-Man）”出现 了 16 万次。金刚狼和铁人也出现了几千次。甚至连这些角色的扮演者的名字也经常被用于密码，尤以金刚狼的扮演者詹姆斯·豪利特受欢迎。

>
> 老王点评：在密码方面，超级英雄也不能给你力量，还是微软现在的无密码策略比较好。
>
>
>

### REvil 被发现其勒索软件含有秘密后门

![](/data/attachment/album/202110/04/150430sxciyny4f8ztmeui.jpg)

REvil 是近年来最臭名昭著的勒索软件攻击团伙之一，该组织还提供了“勒索软件即服务”，以通过租赁的方式从下游恶意攻击者那里抽成。安全研究人员在对地下论坛进行分析后发现，REvil 勒索软件还植入了[秘密后门](https://www.zdnet.com/article/these-ransomware-crooks-are-complaining-they-are-getting-ripped-off-by-other-ransomware-crooks/)，能让上线的黑客接管与受害者的协商，不给下线提成。在俄语的地下论坛，有人表示早就怀疑 REvil 的策略，他们与一名受害者讨论 700 万美元赎金时，协商突然结束了，他们认为是 REvil 使用后门接管了赎金谈判。地下论坛上的另一位也抱怨称，他们已经厌倦了这个“不被信任”的勒索软件团伙提供的“糟糕的合伙应用程序”。

>
> 老王点评：怎么说呢，这就是黑吃黑啊。
>
>
>

### PostgreSQL 14 正式发布

![](/data/attachment/album/202110/04/150447dca6w0l9asezd9tr.jpg)

广泛使用的 PostgreSQL [发布](https://www.postgresql.org/docs/14/release-14.html)了 14.0，带来了许多性能改进，特别是围绕并行查询、重度并发工作负载、分区表、逻辑复制和吸尘等方面进行了新的优化。

>
> 老王点评：这是我用过的第一个开源数据库，虽然一度其风头被 MySQL 盖住，但是现在越来越呈现出一种繁荣景象，值得关注学习。
>
>
>


官方的树莓派 4 外壳很烂！怎么样减少过热？

Summary: 你知道树莓派在没有适当冷却的情况下会限制性能吗？

你可以换个台式机了

你可以换个台式机了

你可以换个台式机了

你知道树莓派在没有适当冷却的情况下会限制性能吗？

![](/data/attachment/album/202110/04/201826ghpecyb43cxehi4y.jpg)

[树莓派 4](https://itsfoss.com/raspberry-pi-4/) 绝对是数百万人的最爱，特别是在极客社区里，我也不例外。但是你知道树莓派在没有适当冷却的情况下会限制性能吗？

在这里，我将介绍 [树莓派 4 官方外壳](https://www.raspberrypi.org/products/raspberry-pi-4-case/) 的一些严重缺点，同时也分享一些缓解这些缺点的方法。

![树莓派 4 官方外壳](/data/attachment/album/202110/04/201838f5q4qu4bjo9pu55o.jpg)

在第一次启动后，我的安装在 [树莓派 4 官方外壳](https://www.raspberrypi.org/products/raspberry-pi-4-case/) 内的树莓派 4（8GB 内存版），在无人值守的升级启动时，会高达 80℃。我在 Ubuntu 上进行了所有的 [固件更新](https://www.einfochips.com/blog/understanding-firmware-updates-the-whats-whys-and-hows/)，显然是为了 [解决发热问题](https://www.seeedstudio.com/blog/2019/11/29/raspberry-pi-4-firmware-update-pi-4-now-runs-cooler-than-ever/)。

就算在空闲时，这个烫手的香草和草莓蛋糕也绝不会低于 75℃。

我几乎无法使用它，直到我取下外壳顶部的白色盖子。它闲置时的温度降到只有 67℃ 左右 —— 你相信吗？即使是在我重新启动一段时间后再次检查也是这样。很明显，这仍然是不太可接受。如果我买了这个外壳并打算长期使用，我为什么要一直把盖子打开？

为什么会发生这样的事情？这都是因为官方的树莓派外壳的设计非常糟糕。

### 官方的树莓派 4 外壳是一个发热怪物！

简单地说，[热节流](https://www.pcmag.com/encyclopedia/term/thermal-throttling) 就是降低你的树莓派处理器（CPU）的性能，以使温度不超过极限高温（如 80℃）而 [导致损坏](https://www.pcgamer.com/cpu-temperature-overheat/)。

这个外壳是由塑料制成的，它是热的不良导体（简单的 [传统物理学](https://thermtest.com/stay-colder-for-longer-in-a-container-made-of-plastic-or-metal) 知识），因此无法将热量有效地散布到整个外壳和板子之外。因此，板上的处理器会发热，一旦温度达到惊人的程度，它的性能就会被降到一个极低的水平。我注意到，在第一次开机后，在**无人值守**的情况下进行升级时，CPU 的温度为 80℃，CPU 的使用率为 100%。

虽然这个官方的外壳看起来很美，但它对树莓派的性能造成了很大的影响。

如果你真的想让你的树莓派发挥最大的性能，你也必须负责它的冷却。这些发热问题不能被简单地忽视。

#### 热量被困在内部

一旦你把树莓派安装在这个外壳里，它甚至没有一个通风口可以让多余的热量排出。所以热量就一直在里面积累，直到达到那些疯狂的温度并触发了节流阀。

#### 没有风扇通风口（非常需要）

顶部的白色盖子上可以有一个圆形的通风口，至少可以把 [树莓派 4 的官方风扇](https://www.raspberrypi.org/products/raspberry-pi-4-case-fan/) 放在上面使用。

#### 没有被动冷却

如果外壳是金属的，它就可以作为散热器，有效地将树莓派板上的处理器的热量散发出去。

#### 除了发热问题之外，还有其他的缺点

树莓派 4 官方外壳还有一些缺点：

1. 不便于 SD 卡管理：将树莓派板子装入外壳内，并将 SD 卡端口放在正确的方向上，以便以后能够换卡，这不是很方便。
2. 没有螺丝钉系统：没有提供螺丝，也许是因为它可能会破坏机箱底座上的假支架，这些假支架看起来就像你可以用四颗螺丝把板子牢牢地固定在底座上。

### 你可以做什么来控制树莓派 4 的过热？

在做了一些紧张的研究之后，我找到了市场上一些最好的冷却解决方案 —— 这一切都要归功于我们了不起的改装社区。

#### 使用冰塔式冷却器

我首先发现了 [Jeff Geerling's](https://www.jeffgeerling.com/blog/2019/best-way-keep-your-cool-running-raspberry-pi-4) 对各种树莓派散热器的深入性能评估，他在网上被称为 [geerlingguy](https://www.jeffgeerling.com/about)。在看完温度统计后，我直接选择了冰塔式散热器，并组装了它：

![树莓派 4 冰塔冷却器](/data/attachment/album/202110/04/201839y6xjqj7l1vc5qlre.jpg)

它空闲和低载时的温度下降到 30℃，现在保持在 45℃ 左右。我还没有为它改装一个合适的外壳。我准备找个给冷却器提供了足够的空间的现成外壳。也许可以在亚马逊或其他网上商店找到这种外壳。

但我没有找到这种产品。

#### 使用铝制散热器进行被动散热

网上也有一个关于 [被动冷却](https://buildabroad.org/2016/11/05/passive-cooling/) 的出色视频，测评了一个用铝制散热片做的外壳。

它提供了一个热垫，它相当于台式机处理器上使用的散热膏。按照视频中显示的方式放置它，热量就会从树莓派板上的处理器散发到整个外壳内。这就是科学的神奇之处！

#### 改装官方的树莓派外壳

如果你仍然想买官方的外壳，建议你至少要做一个风扇的改装。

### 潜在的制造解决方案

这里有一些解决方案，通过应用 [DevOps](https://linuxhandbook.com/what-is-devops/) 启发的改进，可以使整个制造过程更容易。

* 想一想，从外壳顶部切下的那块圆形塑料可以回收，用来制造更多的树莓派 4 外壳，不是吗？这显然会是一个双赢的局面，同时也降低了成本!
* 铝是地球上最丰富的金属，但 [全球供应中断](https://www.reuters.com/article/global-metals-idUSL1N2Q90GA) 可能是一个挑战。即使如此，还有其他的 [导电性解决方案](https://news.mit.edu/2018/engineers-turn-plastic-insulator-heat-conductor-0330) 来探索用于设计案例的材料！

### 总结

希望这篇文章能帮助你从树莓派 4 中获得最大的收益。我很想知道你的想法、建议和经验，请在下面的评论中留言。请不要犹豫地分享。

---

via: <https://itsfoss.com/raspberry-pi-case-overheating/>

作者：[Avimanyu Bandyopadhyay](https://itsfoss.com/author/avimanyu/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

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


使用 trace-cmd 追踪内核

Summary: trace-cmd 是一个易于使用，且特性众多、可用来追踪内核函数的命令。

trace-cmd 是一个易于使用，且特性众多、可用来追踪内核函数的命令。

>
> trace-cmd 是一个易于使用，且特性众多、可用来追踪内核函数的命令。
>
>
>

![](/data/attachment/album/202110/05/145818d2i9tgjetzj8itqg.jpg "Puzzle pieces coming together to form a computer screen")

在 [之前的文章](/article-13752-1.html) 里，我介绍了如何利用 `ftrace` 来追踪内核函数。通过写入和读出文件来使用 `ftrace` 会变得很枯燥，所以我对它做了一个封装来运行带有选项的命令，以启用和禁用追踪、设置过滤器、查看输出、清除输出等等。

[trace-cmd](https://lwn.net/Articles/410200/) 命令是一个可以帮助你做到这一点的工具。在这篇文章中，我使用 `trace-cmd` 来执行我在 `ftrace` 文章中所做的相同任务。由于会经常参考那篇文章，建议在阅读这篇文章之前先阅读它。

### 安装 trace-cmd

本文中所有的命令都运行在 root 用户下。

因为 `ftrace` 机制被内置于内核中，因此你可以使用下面的命令进行验证它是否启用：

```
# mount | grep tracefs
none on /sys/kernel/tracing type tracefs (rw,relatime,seclabel)

```

不过，你需要手动尝试安装 `trace-cmd` 命令：

```
# dnf install trace-cmd -y

```

### 列出可用的追踪器

当使用 `ftrace` 时，你必须查看文件的内容以了解有哪些追踪器可用。但使用 `trace-cmd`，你可以通过以下方式获得这些信息:

```
# trace-cmd list -t
hwlat blk mmiotrace function_graph wakeup_dl wakeup_rt wakeup function nop

```

### 启用函数追踪器

在我 [之前的文章](/article-13752-1.html) 中，我使用了两个追踪器，在这里我也会这么做。用 `function` 启用你的第一个追踪器:

```
$ trace-cmd start -p function
  plugin 'function'

```

### 查看追踪输出

一旦追踪器被启用，你可以通过使用 `show` 参数来查看输出。这只显示了前 20 行以保持例子的简短（见我之前的文章对输出的解释）：

```
# trace-cmd show | head -20
## tracer: function
#
# entries-in-buffer/entries-written: 410142/3380032   #P:8
#
#                                _-----=> irqs-off
#                               / _----=> need-resched
#                              | / _---=> hardirq/softirq
#                              || / _--=> preempt-depth
#                              ||| /     delay
#           TASK-PID     CPU#  ||||   TIMESTAMP  FUNCTION
#              | |         |   ||||      |         |
           gdbus-2606    [004] ..s. 10520.538759: __msecs_to_jiffies <-rebalance_domains
           gdbus-2606    [004] ..s. 10520.538760: load_balance <-rebalance_domains
           gdbus-2606    [004] ..s. 10520.538761: idle_cpu <-load_balance
           gdbus-2606    [004] ..s. 10520.538762: group_balance_cpu <-load_balance
           gdbus-2606    [004] ..s. 10520.538762: find_busiest_group <-load_balance
           gdbus-2606    [004] ..s. 10520.538763: update_group_capacity <-update_sd_lb_stats.constprop.0
           gdbus-2606    [004] ..s. 10520.538763: __msecs_to_jiffies <-update_group_capacity
           gdbus-2606    [004] ..s. 10520.538765: idle_cpu <-update_sd_lb_stats.constprop.0
           gdbus-2606    [004] ..s. 10520.538766: __msecs_to_jiffies <-rebalance_domains

```

### 停止追踪并清除缓冲区

追踪将会在后台继续运行，你可以继续用 `show` 查看输出。

要停止追踪，请运行带有 `stop` 参数的 `trace-cmd` 命令：

```
# trace-cmd stop

```

要清除缓冲区，用 `clear` 参数运行它：

```
# trace-cmd clear

```

### 启用函数调用图追踪器

运行第二个追踪器，通过 `function_graph` 参数来启用它。

```
# trace-cmd start -p function_graph
  Plugin 'function_graph'

```

再次使用 `show` 参数查看输出。正如预期的那样，输出与第一次追踪输出略有不同。这一次，它包括一个**函数调用**链：

```
# trace-cmd show | head -20
## tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |
 4)   0.079 us    |        } /* rcu_all_qs */
 4)   0.327 us    |      } /* __cond_resched */
 4)   0.081 us    |      rcu_read_unlock_strict();
 4)               |      __cond_resched() {
 4)   0.078 us    |        rcu_all_qs();
 4)   0.243 us    |      }
 4)   0.080 us    |      rcu_read_unlock_strict();
 4)               |      __cond_resched() {
 4)   0.078 us    |        rcu_all_qs();
 4)   0.241 us    |      }
 4)   0.080 us    |      rcu_read_unlock_strict();
 4)               |      __cond_resched() {
 4)   0.079 us    |        rcu_all_qs();
 4)   0.235 us    |      }
 4)   0.095 us    |      rcu_read_unlock_strict();
 4)               |      __cond_resched() {

```

使用 `stop` 和 `clear` 命令来停止追踪和清除缓存区：

```
# trace-cmd stop
# trace-cmd clear

```

### 调整追踪以增加深度

如果你想在函数调用中看到更多的深度，你可以对追踪器进行调整：

```
# trace-cmd start -p function_graph --max-graph-depth 5
  plugin 'function_graph'

```

现在，当你将这个输出与你之前看到的进行比较时，你应该看到更多的嵌套函数调用：

```
# trace-cmd show | head -20
## tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |
 6)               |        __fget_light() {
 6)   0.804 us    |          __fget_files();
 6)   2.708 us    |        }
 6)   3.650 us    |      } /* __fdget */
 6)   0.547 us    |      eventfd_poll();
 6)   0.535 us    |      fput();
 6)               |      __fdget() {
 6)               |        __fget_light() {
 6)   0.946 us    |          __fget_files();
 6)   1.895 us    |        }
 6)   2.849 us    |      }
 6)               |      sock_poll() {
 6)   0.651 us    |        unix_poll();
 6)   1.905 us    |      }
 6)   0.475 us    |      fput();
 6)               |      __fdget() {

```

### 了解可被追踪的函数

如果你想只追踪某些函数而忽略其他的，你需要知道确切的函数名称。你可以用 `list -f` 参数来得到它们。例如搜索常见的内核函数 `kmalloc`，它被用来在内核中分配内存：

```
# trace-cmd list -f | grep kmalloc
bpf_map_kmalloc_node
mempool_kmalloc
__traceiter_kmalloc
__traceiter_kmalloc_node
kmalloc_slab
kmalloc_order
kmalloc_order_trace
kmalloc_large_node
__kmalloc
__kmalloc_track_caller
__kmalloc_node
__kmalloc_node_track_caller
[...]

```

下面是我的测试系统中可被追踪的函数总数：

```
# trace-cmd list -f | wc -l
63165

```

### 追踪内核模块相关的函数

你也可以追踪与特定内核模块相关的函数。假设你想追踪 `kvm` 内核模块相关的功能，你可以通过以下方式来实现。请确保该模块已经加载：

```
# lsmod | grep kvm_intel
kvm_intel 335872 0
kvm 987136 1 kvm_intel

```

再次运行 `trace-cmd`，使用 `list` 参数，并从输出结果中，`grep` 查找以 `]` 结尾的行。这将过滤掉内核模块。然后 `grep` 内核模块 `kvm_intel` ，你应该看到所有与该内核模块有关的函数。

```
# trace-cmd list -f | grep ]$  | grep kvm_intel
vmx_can_emulate_instruction [kvm_intel]
vmx_update_emulated_instruction [kvm_intel]
vmx_setup_uret_msr [kvm_intel]
vmx_set_identity_map_addr [kvm_intel]
handle_machine_check [kvm_intel]
handle_triple_fault [kvm_intel]
vmx_patch_hypercall [kvm_intel]

[...]

vmx_dump_dtsel [kvm_intel]
vmx_dump_sel [kvm_intel]

```

### 追踪特定函数

现在你知道了如何找到感兴趣的函数，请用一个例子把这些内容用于时间。就像前面的文章一样，试着追踪与文件系统相关的函数。我的测试系统上的文件系统是 `ext4`。

这个过程略有不同；你在运行命令时，不使用 `start` 参数，而是在 `record` 参数后面加上你想追踪的函数的“模式”。你还需要指定你想要的追踪器；在这种情况下，就是 `function_graph`。该命令会继续记录追踪，直到你用 `Ctrl+C` 停止它。所以几秒钟后，按 `Ctrl+C` 停止追踪：

```
# trace-cmd list -f | grep ^ext4_

# trace-cmd record -l ext4_* -p function_graph
  plugin 'function_graph'
Hit Ctrl^C to stop recording
^C
CPU0 data recorded at offset=0x856000
    8192 bytes in size
[...]

```

### 查看追踪记录

要查看你之前的追踪记录，运行带有 `report` 参数的命令。从输出结果来看，很明显过滤器起作用了，你只看到 `ext4` 相关的函数追踪：

```
# trace-cmd report | head -20
[...]
cpus=8
       trace-cmd-12697 [000] 11303.928103: funcgraph_entry:                   |  ext4_show_options() {
       trace-cmd-12697 [000] 11303.928104: funcgraph_entry:        0.187 us   |    ext4_get_dummy_policy();
       trace-cmd-12697 [000] 11303.928105: funcgraph_exit:         1.583 us   |  }
       trace-cmd-12697 [000] 11303.928122: funcgraph_entry:                   |  ext4_create() {
       trace-cmd-12697 [000] 11303.928122: funcgraph_entry:                   |    ext4_alloc_inode() {
       trace-cmd-12697 [000] 11303.928123: funcgraph_entry:        0.101 us   |      ext4_es_init_tree();
       trace-cmd-12697 [000] 11303.928123: funcgraph_entry:        0.083 us   |      ext4_init_pending_tree();
       trace-cmd-12697 [000] 11303.928123: funcgraph_entry:        0.141 us   |      ext4_fc_init_inode();
       trace-cmd-12697 [000] 11303.928123: funcgraph_exit:         0.931 us   |    }
       trace-cmd-12697 [000] 11303.928124: funcgraph_entry:        0.081 us   |    ext4_get_dummy_policy();
       trace-cmd-12697 [000] 11303.928124: funcgraph_entry:        0.133 us   |    ext4_get_group_desc();
       trace-cmd-12697 [000] 11303.928124: funcgraph_entry:        0.115 us   |    ext4_free_inodes_count();
       trace-cmd-12697 [000] 11303.928124: funcgraph_entry:        0.114 us   |    ext4_get_group_desc();

```

### 追踪一个特定的 PID

假设你想追踪与一个进程（PID）有关的函数。打开另一个终端，注意运行中的 shell 的PID：

```
# echo $$
10885

```

再次运行 `record` 命令，用 `-P` 选项传递PID。这一次，让终端运行（也就是说，先不要按 `Ctrl+C` ）：

```
# trace-cmd record -P 10885 -p function_graph
  Plugin 'function_graph'
Hit Ctrl^C to stop recording

```

### 在 shell 上运行一些命令

移动到另一个终端，在那里你有一个以特定 PID 运行的 shell，并运行任何命令，例如，`ls` 命令用来列出文件：

```
# ls
Temp-9b61f280-fdc1-4512-9211-5c60f764d702
tracker-extract-3-files.1000
v8-compile-cache-1000
[...]

```

移动到你启用追踪的终端，按 `Ctrl+C` 停止追踪：

```
# trace-cmd record -P 10885 -p function_graph
  plugin 'function_graph'
Hit Ctrl^C to stop recording
^C
CPU1 data recorded at offset=0x856000
    618496 bytes in size
[...]

```

在追踪的输出中，你可以看到左边是 PID 和 Bash shell，右边是与之相关的函数调用。这对于缩小你的追踪范围是非常方便的：

```
# trace-cmd report  | head -20

cpus=8
          <idle>-0     [001] 11555.380581: funcgraph_entry:                   |  switch_mm_irqs_off() {
          <idle>-0     [001] 11555.380583: funcgraph_entry:        1.703 us   |    load_new_mm_cr3();
          <idle>-0     [001] 11555.380586: funcgraph_entry:        0.493 us   |    switch_ldt();
          <idle>-0     [001] 11555.380587: funcgraph_exit:         7.235 us   |  }
            bash-10885 [001] 11555.380589: funcgraph_entry:        1.046 us   |  finish_task_switch.isra.0();
            bash-10885 [001] 11555.380591: funcgraph_entry:                   |  __fdget() {
            bash-10885 [001] 11555.380592: funcgraph_entry:        2.036 us   |    __fget_light();
            bash-10885 [001] 11555.380594: funcgraph_exit:         3.256 us   |  }
            bash-10885 [001] 11555.380595: funcgraph_entry:                   |  tty_poll() {
            bash-10885 [001] 11555.380597: funcgraph_entry:                   |    tty_ldisc_ref_wait() {
            bash-10885 [001] 11555.380598: funcgraph_entry:                   |      ldsem_down_read() {
            bash-10885 [001] 11555.380598: funcgraph_entry:                   |        __cond_resched() {

```

### 试一试

这些简短的例子显示了使用 `trace-cmd` 命令而不是底层的 `ftrace` 机制，是如何实现既容易使用又拥有丰富的功能，许多内容本文并没有涉及。要想了解更多信息并更好地使用它，请查阅它的手册，并尝试使用其他有用的命令。

---

via: <https://opensource.com/article/21/7/linux-kernel-trace-cmd>

作者：[Gaurav Kamathe](https://opensource.com/users/gkamathe) 选题：[lujun9972](https://github.com/lujun9972) 译者：[萌新阿岩](https://github.com/mengxinayan) 校对：[wxy](https://github.com/wxy)

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


硬核观察 #415 Facebook 宕机 6 小时，工程师一度无法远程和现场排除故障

Summary: 更多：• Windows 11 正式发布，微软解释为何限制硬件 • Android 12 正式发布

古语有云，解释就是掩饰

![](/data/attachment/album/202110/05/160145gdo81n83zg8q8uqw.jpg)

### Facebook 宕机 6 小时，工程师一度无法远程和现场排除故障

![](/data/attachment/album/202110/05/160159imrrekran599enq9.jpg)

美国东部时间周一上午 11:30 左右，Facebook 旗下的主要应用，包括 Facebook、Instagram、WhatsApp、Messenger 等从互联网上全部消失了 6 个小时左右。据 [外界分析](https://www.zdnet.com/article/what-took-facebook-down-major-global-outage-drags-on/)，是 Facebook 错误的 BGP 更新导致了问题，并因此阻止了对工程师们远程访问，无法及时进行恢复工作。不仅如此，其内部通信平台 Workplace 也因而下线，使他们之间难以及时联络。甚至工程师们无法接触到受影响的服务器，因为他们的数字身份认证系统同时也停止了工作。

根据 Facebook 二季度的财报，其每小时大约收入 1330 万美元，这意味着该事故导致 Facebook 至少损失了 8000 万美元的收入。并因此导致该公司股票被抛售，股票价格下跌了近 5%。据估计，该事故对全球经济总影响成本约为 9.68 亿美元。

>
> 老王点评：网络出问题时，一般都把锅丢给 DNS，但是其实更大的锅往往是 BGP 的，这个协议屡屡造成超大规模的网络问题。
>
>
>

### Windows 11 正式发布，微软解释为何限制硬件

![](/data/attachment/album/202110/05/160229tmk6m3mh1n5dd9h6.jpg)

微软表示，符合 [Windows 11](https://www.microsoft.com/zh-cn/windows/windows-11) 升级条件的现有 Windows 10 设备将从今天开始能够升级。

关于 Windows 11 最大的争议来自于其对硬件的硬性要求：需要较新的 CPU 和 TPM 2.0。微软对此[解释](https://www.neowin.net/news/microsoft-explains-why-tpm-20-and-vbs-on-windows-11-are-so-key-for-next-gen-security/)称，“保证所有用户的计算机包含 TPM 也意味着可以确保每个应用程序开发人员现在都可以在硬件中存储证书和密钥。更多的应用程序可以默认支持无密码；更多的应用可以进行数据加密；更多的应用程序可以有零信任保护，因为我们已经有了基于虚拟化的能力来报告他们的完整性。”

此外，关于在 Windows 11 中默认开启的“基于虚拟化的安全”（VBS）功能，微软[解释](https://www.neowin.net/news/microsoft-explains-why-tpm-20-and-vbs-on-windows-11-are-so-key-for-next-gen-security/)说，“我们从 Windows 10 中学到的是，如果你让安全设定变得可有可无，人们就不会把它们打开。这是一个很大的教训。有鉴于此，我们在 Windows 11 中将默认保护用户的安全。”他们在 Windows 11 中采用了和云计算相同的做法，即使有人获得了最高级别的权限，他们仍然无法读取独立的虚拟机中的内容。

>
> 老王点评：虽然我部分认同微软的安全观点，但是我觉得有一层微软没说的意思是，他们想推动人们买新的硬件。
>
>
>

### Android 12 正式发布

![](/data/attachment/album/202110/05/160248k4zz6f9kbi394oej.jpg)

[谷歌宣布](https://android-developers.googleblog.com/2021/10/android-12-is-live-in-aosp.html)，已经将 Android 12 源代码推送到 Android 开源项目（AOSP），这也意味着 Android 12 正式发布。接下来的几周内到今年晚些之后，从 Pixel 开始，三星、一加、OPPO、realme、传音、vivo、小米等品牌设备将陆续升级 Android 12。Android 12 提供了更快、更高效的系统性能，改进了应用程序启动时间并优化了 I/O，以加快应用程序加载速度。此外，还提供了重新设计的小部件，更新了通知设计等界面变化。

>
> 老王点评：看来今天都是大消息，不过 Android 12 的影响要几个月甚至更长才能推送到终端用户。
>
>
>