mirror of
https://github.com/LCTT/TranslateProject.git
synced 2025-01-25 23:11:02 +08:00
commit
77741c9995
@ -0,0 +1,484 @@
|
||||
从零写一个时间序列数据库
|
||||
==================
|
||||
|
||||
编者按:Prometheus 是 CNCF 旗下的开源监控告警解决方案,它已经成为 Kubernetes 生态圈中的核心监控系统。本文作者 Fabian Reinartz 是 Prometheus 的核心开发者,这篇文章是其于 2017 年写的一篇关于 Prometheus 中的时间序列数据库的设计思考,虽然写作时间有点久了,但是其中的考虑和思路非常值得参考。长文预警,请坐下来慢慢品味。
|
||||
|
||||
---
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/11/180646l7cqbhazqs7nsqsn.jpg)
|
||||
|
||||
我从事监控工作。特别是在 [Prometheus][2] 上,监控系统包含一个自定义的时间序列数据库,并且集成在 [Kubernetes][3] 上。
|
||||
|
||||
在许多方面上 Kubernetes 展现出了 Prometheus 所有的设计用途。它使得<ruby>持续部署<rt>continuous deployments</rt></ruby>,<ruby>弹性伸缩<rt>auto scaling</rt></ruby>和其他<ruby>高动态环境<rt>highly dynamic environments</rt></ruby>下的功能可以轻易地访问。查询语句和操作模型以及其它概念决策使得 Prometheus 特别适合这种环境。但是,如果监控的工作负载动态程度显著地增加,这就会给监控系统本身带来新的压力。考虑到这一点,我们就可以特别致力于在高动态或<ruby>瞬态服务<rt>transient services</rt></ruby>环境下提升它的表现,而不是回过头来解决 Prometheus 已经解决的很好的问题。
|
||||
|
||||
Prometheus 的存储层在历史以来都展现出卓越的性能,单一服务器就能够以每秒数百万个时间序列的速度摄入多达一百万个样本,同时只占用了很少的磁盘空间。尽管当前的存储做的很好,但我依旧提出一个新设计的存储子系统,它可以修正现存解决方案的缺点,并具备处理更大规模数据的能力。
|
||||
|
||||
> 备注:我没有数据库方面的背景。我说的东西可能是错的并让你误入歧途。你可以在 Freenode 的 #prometheus 频道上对我(fabxc)提出你的批评。
|
||||
|
||||
## 问题,难题,问题域
|
||||
|
||||
首先,快速地概览一下我们要完成的东西和它的关键难题。我们可以先看一下 Prometheus 当前的做法 ,它为什么做的这么好,以及我们打算用新设计解决哪些问题。
|
||||
|
||||
### 时间序列数据
|
||||
|
||||
我们有一个收集一段时间数据的系统。
|
||||
|
||||
```
|
||||
identifier -> (t0, v0), (t1, v1), (t2, v2), (t3, v3), ....
|
||||
```
|
||||
|
||||
每个数据点是一个时间戳和值的元组。在监控中,时间戳是一个整数,值可以是任意数字。64 位浮点数对于计数器和测量值来说是一个好的表示方法,因此我们将会使用它。一系列严格单调递增的时间戳数据点是一个序列,它由标识符所引用。我们的标识符是一个带有<ruby>标签维度<rt>label dimensions</rt></ruby>字典的度量名称。标签维度划分了单一指标的测量空间。每一个指标名称加上一个唯一标签集就成了它自己的时间序列,它有一个与之关联的<ruby>数据流<rt>value stream</rt></ruby>。
|
||||
|
||||
这是一个典型的<ruby>序列标识符<rt>series identifier</rt></ruby>集,它是统计请求指标的一部分:
|
||||
|
||||
```
|
||||
requests_total{path="/status", method="GET", instance=”10.0.0.1:80”}
|
||||
requests_total{path="/status", method="POST", instance=”10.0.0.3:80”}
|
||||
requests_total{path="/", method="GET", instance=”10.0.0.2:80”}
|
||||
```
|
||||
|
||||
让我们简化一下表示方法:度量名称可以当作另一个维度标签,在我们的例子中是 `__name__`。对于查询语句,可以对它进行特殊处理,但与我们存储的方式无关,我们后面也会见到。
|
||||
|
||||
```
|
||||
{__name__="requests_total", path="/status", method="GET", instance=”10.0.0.1:80”}
|
||||
{__name__="requests_total", path="/status", method="POST", instance=”10.0.0.3:80”}
|
||||
{__name__="requests_total", path="/", method="GET", instance=”10.0.0.2:80”}
|
||||
```
|
||||
|
||||
我们想通过标签来查询时间序列数据。在最简单的情况下,使用 `{__name__="requests_total"}` 选择所有属于 `requests_total` 指标的数据。对于所有选择的序列,我们在给定的时间窗口内获取数据点。
|
||||
|
||||
在更复杂的语句中,我们或许想一次性选择满足多个标签的序列,并且表示比相等条件更复杂的情况。例如,非语句(`method!="GET"`)或正则表达式匹配(`method=~"PUT|POST"`)。
|
||||
|
||||
这些在很大程度上定义了存储的数据和它的获取方式。
|
||||
|
||||
### 纵与横
|
||||
|
||||
在简化的视图中,所有的数据点可以分布在二维平面上。水平维度代表着时间,序列标识符域经纵轴展开。
|
||||
|
||||
```
|
||||
series
|
||||
^
|
||||
| . . . . . . . . . . . . . . . . . . . . . . {__name__="request_total", method="GET"}
|
||||
| . . . . . . . . . . . . . . . . . . . . . . {__name__="request_total", method="POST"}
|
||||
| . . . . . . .
|
||||
| . . . . . . . . . . . . . . . . . . . ...
|
||||
| . . . . . . . . . . . . . . . . . . . . .
|
||||
| . . . . . . . . . . . . . . . . . . . . . {__name__="errors_total", method="POST"}
|
||||
| . . . . . . . . . . . . . . . . . {__name__="errors_total", method="GET"}
|
||||
| . . . . . . . . . . . . . .
|
||||
| . . . . . . . . . . . . . . . . . . . ...
|
||||
| . . . . . . . . . . . . . . . . . . . .
|
||||
v
|
||||
<-------------------- time --------------------->
|
||||
```
|
||||
|
||||
Prometheus 通过定期地抓取一组时间序列的当前值来获取数据点。我们从中获取到的实体称为目标。因此,写入模式完全地垂直且高度并发,因为来自每个目标的样本是独立摄入的。
|
||||
|
||||
这里提供一些测量的规模:单一 Prometheus 实例从数万个目标中收集数据点,每个数据点都暴露在数百到数千个不同的时间序列中。
|
||||
|
||||
在每秒采集数百万数据点这种规模下,批量写入是一个不能妥协的性能要求。在磁盘上分散地写入单个数据点会相当地缓慢。因此,我们想要按顺序写入更大的数据块。
|
||||
|
||||
对于旋转式磁盘,它的磁头始终得在物理上向不同的扇区上移动,这是一个不足为奇的事实。而虽然我们都知道 SSD 具有快速随机写入的特点,但事实上它不能修改单个字节,只能写入一页或更多页的 4KiB 数据量。这就意味着写入 16 字节的样本相当于写入满满一个 4Kib 的页。这一行为就是所谓的[写入放大][4],这种特性会损耗你的 SSD。因此它不仅影响速度,而且还毫不夸张地在几天或几个周内破坏掉你的硬件。
|
||||
|
||||
关于此问题更深层次的资料,[“Coding for SSDs”系列][5]博客是极好的资源。让我们想想主要的用处:顺序写入和批量写入分别对于旋转式磁盘和 SSD 来说都是理想的写入模式。大道至简。
|
||||
|
||||
查询模式比起写入模式明显更不同。我们可以查询单一序列的一个数据点,也可以对 10000 个序列查询一个数据点,还可以查询一个序列几个周的数据点,甚至是 10000 个序列几个周的数据点。因此在我们的二维平面上,查询范围不是完全水平或垂直的,而是二者形成矩形似的组合。
|
||||
|
||||
[记录规则][6]可以减轻已知查询的问题,但对于<ruby>点对点<rt>ad-hoc</rt></ruby>查询来说并不是一个通用的解决方法。
|
||||
|
||||
我们知道我们想要批量地写入,但我们得到的仅仅是一系列垂直数据点的集合。当查询一段时间窗口内的数据点时,我们不仅很难弄清楚在哪才能找到这些单独的点,而且不得不从磁盘上大量随机的地方读取。也许一条查询语句会有数百万的样本,即使在最快的 SSD 上也会很慢。读入也会从磁盘上获取更多的数据而不仅仅是 16 字节的样本。SSD 会加载一整页,HDD 至少会读取整个扇区。不论哪一种,我们都在浪费宝贵的读取吞吐量。
|
||||
|
||||
因此在理想情况下,同一序列的样本将按顺序存储,这样我们就能通过尽可能少的读取来扫描它们。最重要的是,我们仅需要知道序列的起始位置就能访问所有的数据点。
|
||||
|
||||
显然,将收集到的数据写入磁盘的理想模式与能够显著提高查询效率的布局之间存在着明显的抵触。这是我们 TSDB 需要解决的一个基本问题。
|
||||
|
||||
#### 当前的解决方法
|
||||
|
||||
是时候看一下当前 Prometheus 是如何存储数据来解决这一问题的,让我们称它为“V2”。
|
||||
|
||||
我们创建一个时间序列的文件,它包含所有样本并按顺序存储。因为每几秒附加一个样本数据到所有文件中非常昂贵,我们在内存中打包 1Kib 样本序列的数据块,一旦打包完成就附加这些数据块到单独的文件中。这一方法解决了大部分问题。写入目前是批量的,样本也是按顺序存储的。基于给定的同一序列的样本相对之前的数据仅发生非常小的改变这一特性,它还支持非常高效的压缩格式。Facebook 在他们 Gorilla TSDB 上的论文中描述了一个相似的基于数据块的方法,并且[引入了一种压缩格式][7],它能够减少 16 字节的样本到平均 1.37 字节。V2 存储使用了包含 Gorilla 变体等在内的各种压缩格式。
|
||||
|
||||
```
|
||||
+----------+---------+---------+---------+---------+ series A
|
||||
+----------+---------+---------+---------+---------+
|
||||
+----------+---------+---------+---------+---------+ series B
|
||||
+----------+---------+---------+---------+---------+
|
||||
. . .
|
||||
+----------+---------+---------+---------+---------+---------+ series XYZ
|
||||
+----------+---------+---------+---------+---------+---------+
|
||||
chunk 1 chunk 2 chunk 3 ...
|
||||
```
|
||||
|
||||
尽管基于块存储的方法非常棒,但为每个序列保存一个独立的文件会给 V2 存储带来麻烦,因为:
|
||||
|
||||
* 实际上,我们需要的文件比当前收集数据的时间序列数量要多得多。多出的部分在<ruby>序列分流<rt>Series Churn</rt></ruby>上。有几百万个文件,迟早会使用光文件系统中的 [inode][1]。这种情况我们只能通过重新格式化来恢复磁盘,这种方式是最具有破坏性的。我们通常不想为了适应一个应用程序而格式化磁盘。
|
||||
* 即使是分块写入,每秒也会产生数千块的数据块并且准备持久化。这依然需要每秒数千次的磁盘写入。尽管通过为每个序列打包好多个块来缓解,但这反过来还是增加了等待持久化数据的总内存占用。
|
||||
* 要保持所有文件打开来进行读写是不可行的。特别是因为 99% 的数据在 24 小时之后不再会被查询到。如果查询它,我们就得打开数千个文件,找到并读取相关的数据点到内存中,然后再关掉。这样做就会引起很高的查询延迟,数据块缓存加剧会导致新的问题,这一点在“资源消耗”一节另作讲述。
|
||||
* 最终,旧的数据需要被删除,并且数据需要从数百万文件的头部删除。这就意味着删除实际上是写密集型操作。此外,循环遍历数百万文件并且进行分析通常会导致这一过程花费数小时。当它完成时,可能又得重新来过。喔天,继续删除旧文件又会进一步导致 SSD 产生写入放大。
|
||||
* 目前所积累的数据块仅维持在内存中。如果应用崩溃,数据就会丢失。为了避免这种情况,内存状态会定期的保存在磁盘上,这比我们能接受数据丢失窗口要长的多。恢复检查点也会花费数分钟,导致很长的重启周期。
|
||||
|
||||
我们能够从现有的设计中学到的关键部分是数据块的概念,我们当然希望保留这个概念。最新的数据块会保持在内存中一般也是好的主意。毕竟,最新的数据会大量的查询到。
|
||||
|
||||
一个时间序列对应一个文件,这个概念是我们想要替换掉的。
|
||||
|
||||
### 序列分流
|
||||
|
||||
在 Prometheus 的<ruby>上下文<rt>context</rt></ruby>中,我们使用术语<ruby>序列分流<rt>series churn</rt></ruby>来描述一个时间序列集合变得不活跃,即不再接收数据点,取而代之的是出现一组新的活跃序列。
|
||||
|
||||
例如,由给定微服务实例产生的所有序列都有一个相应的“instance”标签来标识其来源。如果我们为微服务执行了<ruby>滚动更新<rt>rolling update</rt></ruby>,并且为每个实例替换一个新的版本,序列分流便会发生。在更加动态的环境中,这些事情基本上每小时都会发生。像 Kubernetes 这样的<ruby>集群编排<rt>Cluster orchestration</rt></ruby>系统允许应用连续性的自动伸缩和频繁的滚动更新,这样也许会创建成千上万个新的应用程序实例,并且伴随着全新的时间序列集合,每天都是如此。
|
||||
|
||||
```
|
||||
series
|
||||
^
|
||||
| . . . . . .
|
||||
| . . . . . .
|
||||
| . . . . . .
|
||||
| . . . . . . .
|
||||
| . . . . . . .
|
||||
| . . . . . . .
|
||||
| . . . . . .
|
||||
| . . . . . .
|
||||
| . . . . .
|
||||
| . . . . .
|
||||
| . . . . .
|
||||
v
|
||||
<-------------------- time --------------------->
|
||||
```
|
||||
|
||||
所以即便整个基础设施的规模基本保持不变,过一段时间后数据库内的时间序列还是会成线性增长。尽管 Prometheus 很愿意采集 1000 万个时间序列数据,但要想在 10 亿个序列中找到数据,查询效果还是会受到严重的影响。
|
||||
|
||||
#### 当前解决方案
|
||||
|
||||
当前 Prometheus 的 V2 存储系统对所有当前保存的序列拥有基于 LevelDB 的索引。它允许查询语句含有给定的<ruby>标签对<rt>label pair</rt></ruby>,但是缺乏可伸缩的方法来从不同的标签选集中组合查询结果。
|
||||
|
||||
例如,从所有的序列中选择标签 `__name__="requests_total"` 非常高效,但是选择 `instance="A" AND __name__="requests_total"` 就有了可伸缩性的问题。我们稍后会重新考虑导致这一点的原因和能够提升查找延迟的调整方法。
|
||||
|
||||
事实上正是这个问题才催生出了对更好的存储系统的最初探索。Prometheus 需要为查找亿万个时间序列改进索引方法。
|
||||
|
||||
### 资源消耗
|
||||
|
||||
当试图扩展 Prometheus(或其他任何事情,真的)时,资源消耗是永恒不变的话题之一。但真正困扰用户的并不是对资源的绝对渴求。事实上,由于给定的需求,Prometheus 管理着令人难以置信的吞吐量。问题更在于面对变化时的相对未知性与不稳定性。通过其架构设计,V2 存储系统缓慢地构建了样本数据块,这一点导致内存占用随时间递增。当数据块完成之后,它们可以写到磁盘上并从内存中清除。最终,Prometheus 的内存使用到达稳定状态。直到监测环境发生了改变——每次我们扩展应用或者进行滚动更新,序列分流都会增加内存、CPU、磁盘 I/O 的使用。
|
||||
|
||||
如果变更正在进行,那么它最终还是会到达一个稳定的状态,但比起更加静态的环境,它的资源消耗会显著地提高。过渡时间通常为数个小时,而且难以确定最大资源使用量。
|
||||
|
||||
为每个时间序列保存一个文件这种方法也使得一个单个查询就很容易崩溃 Prometheus 进程。当查询的数据没有缓存在内存中,查询的序列文件就会被打开,然后将含有相关数据点的数据块读入内存。如果数据量超出内存可用量,Prometheus 就会因 OOM 被杀死而退出。
|
||||
|
||||
在查询语句完成之后,加载的数据便可以被再次释放掉,但通常会缓存更长的时间,以便更快地查询相同的数据。后者看起来是件不错的事情。
|
||||
|
||||
最后,我们看看之前提到的 SSD 的写入放大,以及 Prometheus 是如何通过批量写入来解决这个问题的。尽管如此,在许多地方还是存在因为批量太小以及数据未精确对齐页边界而导致的写入放大。对于更大规模的 Prometheus 服务器,现实当中会发现缩减硬件寿命的问题。这一点对于高写入吞吐量的数据库应用来说仍然相当普遍,但我们应该放眼看看是否可以解决它。
|
||||
|
||||
### 重新开始
|
||||
|
||||
到目前为止我们对于问题域、V2 存储系统是如何解决它的,以及设计上存在的问题有了一个清晰的认识。我们也看到了许多很棒的想法,这些或多或少都可以拿来直接使用。V2 存储系统相当数量的问题都可以通过改进和部分的重新设计来解决,但为了好玩(当然,在我仔细的验证想法之后),我决定试着写一个完整的时间序列数据库——从头开始,即向文件系统写入字节。
|
||||
|
||||
性能与资源使用这种最关键的部分直接影响了存储格式的选取。我们需要为数据找到正确的算法和磁盘布局来实现一个高性能的存储层。
|
||||
|
||||
这就是我解决问题的捷径——跳过令人头疼、失败的想法,数不尽的草图,泪水与绝望。
|
||||
|
||||
### V3—宏观设计
|
||||
|
||||
我们存储系统的宏观布局是什么?简而言之,是当我们在数据文件夹里运行 `tree` 命令时显示的一切。看看它能给我们带来怎样一副惊喜的画面。
|
||||
|
||||
```
|
||||
$ tree ./data
|
||||
./data
|
||||
+-- b-000001
|
||||
| +-- chunks
|
||||
| | +-- 000001
|
||||
| | +-- 000002
|
||||
| | +-- 000003
|
||||
| +-- index
|
||||
| +-- meta.json
|
||||
+-- b-000004
|
||||
| +-- chunks
|
||||
| | +-- 000001
|
||||
| +-- index
|
||||
| +-- meta.json
|
||||
+-- b-000005
|
||||
| +-- chunks
|
||||
| | +-- 000001
|
||||
| +-- index
|
||||
| +-- meta.json
|
||||
+-- b-000006
|
||||
+-- meta.json
|
||||
+-- wal
|
||||
+-- 000001
|
||||
+-- 000002
|
||||
+-- 000003
|
||||
```
|
||||
|
||||
在最顶层,我们有一系列以 `b-` 为前缀编号的<ruby>块<rt>block</rt></ruby>。每个块中显然保存了索引文件和含有更多编号文件的 `chunk` 文件夹。`chunks` 目录只包含不同序列<ruby>数据点的原始块<rt>raw chunks of data points</rt><ruby>。与 V2 存储系统一样,这使得通过时间窗口读取序列数据非常高效并且允许我们使用相同的有效压缩算法。这一点被证实行之有效,我们也打算沿用。显然,这里并不存在含有单个序列的文件,而是一堆保存着许多序列的数据块。
|
||||
|
||||
`index` 文件的存在应该不足为奇。让我们假设它拥有黑魔法,可以让我们找到标签、可能的值、整个时间序列和存放数据点的数据块。
|
||||
|
||||
但为什么这里有好几个文件夹都是索引和块文件的布局?并且为什么存在最后一个包含 `wal` 文件夹?理解这两个疑问便能解决九成的问题。
|
||||
|
||||
#### 许多小型数据库
|
||||
|
||||
我们分割横轴,即将时间域分割为不重叠的块。每一块扮演着完全独立的数据库,它包含该时间窗口所有的时间序列数据。因此,它拥有自己的索引和一系列块文件。
|
||||
|
||||
```
|
||||
|
||||
t0 t1 t2 t3 now
|
||||
+-----------+ +-----------+ +-----------+ +-----------+
|
||||
| | | | | | | | +------------+
|
||||
| | | | | | | mutable | <--- write ---- ┤ Prometheus |
|
||||
| | | | | | | | +------------+
|
||||
+-----------+ +-----------+ +-----------+ +-----------+ ^
|
||||
+--------------+-------+------+--------------+ |
|
||||
| query
|
||||
| |
|
||||
merge -------------------------------------------------+
|
||||
```
|
||||
|
||||
每一块的数据都是<ruby>不可变的<rt>immutable</rt></ruby>。当然,当我们采集新数据时,我们必须能向最近的块中添加新的序列和样本。对于该数据块,所有新的数据都将写入内存中的数据库中,它与我们的持久化的数据块一样提供了查找属性。内存中的数据结构可以高效地更新。为了防止数据丢失,所有传入的数据同样被写入临时的<ruby>预写日志<rt>write ahead log</rt></ruby>中,这就是 `wal` 文件夹中的一些列文件,我们可以在重新启动时通过它们重新填充内存数据库。
|
||||
|
||||
所有这些文件都带有序列化格式,有我们所期望的所有东西:许多标志、偏移量、变体和 CRC32 校验和。纸上得来终觉浅,绝知此事要躬行。
|
||||
|
||||
这种布局允许我们扩展查询范围到所有相关的块上。每个块上的部分结果最终合并成完整的结果。
|
||||
|
||||
这种横向分割增加了一些很棒的功能:
|
||||
|
||||
* 当查询一个时间范围,我们可以简单地忽略所有范围之外的数据块。通过减少需要检查的数据集,它可以初步解决序列分流的问题。
|
||||
* 当完成一个块,我们可以通过顺序的写入大文件从内存数据库中保存数据。这样可以避免任何的写入放大,并且 SSD 与 HDD 均适用。
|
||||
* 我们延续了 V2 存储系统的一个好的特性,最近使用而被多次查询的数据块,总是保留在内存中。
|
||||
* 很好,我们也不再受限于 1KiB 的数据块尺寸,以使数据在磁盘上更好地对齐。我们可以挑选对单个数据点和压缩格式最合理的尺寸。
|
||||
* 删除旧数据变得极为简单快捷。我们仅仅只需删除一个文件夹。记住,在旧的存储系统中我们不得不花数个小时分析并重写数亿个文件。
|
||||
|
||||
每个块还包含了 `meta.json` 文件。它简单地保存了关于块的存储状态和包含的数据,以便轻松了解存储状态及其包含的数据。
|
||||
|
||||
##### mmap
|
||||
|
||||
将数百万个小文件合并为少数几个大文件使得我们用很小的开销就能保持所有的文件都打开。这就解除了对 [mmap(2)][8] 的使用的阻碍,这是一个允许我们通过文件透明地回传虚拟内存的系统调用。简单起见,你可以将其视为<ruby>交换空间<rt>swap space</rt></ruby>,只是我们所有的数据已经保存在了磁盘上,并且当数据换出内存后不再会发生写入。
|
||||
|
||||
这意味着我们可以当作所有数据库的内容都视为在内存中却不占用任何物理内存。仅当我们访问数据库文件某些字节范围时,操作系统才会从磁盘上<ruby>惰性加载<rt>lazy load</rt></ruby>页数据。这使得我们将所有数据持久化相关的内存管理都交给了操作系统。通常,操作系统更有资格作出这样的决定,因为它可以全面了解整个机器和进程。查询的数据可以相当积极的缓存进内存,但内存压力会使得页被换出。如果机器拥有未使用的内存,Prometheus 目前将会高兴地缓存整个数据库,但是一旦其他进程需要,它就会立刻返回那些内存。
|
||||
|
||||
因此,查询不再轻易地使我们的进程 OOM,因为查询的是更多的持久化的数据而不是装入内存中的数据。内存缓存大小变得完全自适应,并且仅当查询真正需要时数据才会被加载。
|
||||
|
||||
就个人理解,这就是当今大多数数据库的工作方式,如果磁盘格式允许,这是一种理想的方式,——除非有人自信能在这个过程中超越操作系统。我们做了很少的工作但确实从外面获得了很多功能。
|
||||
|
||||
#### 压缩
|
||||
|
||||
存储系统需要定期“切”出新块并将之前完成的块写入到磁盘中。仅在块成功的持久化之后,才会被删除之前用来恢复内存块的日志文件(wal)。
|
||||
|
||||
我们希望将每个块的保存时间设置的相对短一些(通常配置为 2 小时),以避免内存中积累太多的数据。当查询多个块,我们必须将它们的结果合并为一个整体的结果。合并过程显然会消耗资源,一个星期的查询不应该由超过 80 个的部分结果所组成。
|
||||
|
||||
为了实现两者,我们引入<ruby>压缩<rt>compaction</rt></ruby>。压缩描述了一个过程:取一个或更多个数据块并将其写入一个可能更大的块中。它也可以在此过程中修改现有的数据。例如,清除已经删除的数据,或重建样本块以提升查询性能。
|
||||
|
||||
```
|
||||
|
||||
t0 t1 t2 t3 t4 now
|
||||
+------------+ +----------+ +-----------+ +-----------+ +-----------+
|
||||
| 1 | | 2 | | 3 | | 4 | | 5 mutable | before
|
||||
+------------+ +----------+ +-----------+ +-----------+ +-----------+
|
||||
+-----------------------------------------+ +-----------+ +-----------+
|
||||
| 1 compacted | | 4 | | 5 mutable | after (option A)
|
||||
+-----------------------------------------+ +-----------+ +-----------+
|
||||
+--------------------------+ +--------------------------+ +-----------+
|
||||
| 1 compacted | | 3 compacted | | 5 mutable | after (option B)
|
||||
+--------------------------+ +--------------------------+ +-----------+
|
||||
```
|
||||
|
||||
在这个例子中我们有顺序块 `[1,2,3,4]`。块 1、2、3 可以压缩在一起,新的布局将会是 `[1,4]`。或者,将它们成对压缩为 `[1,3]`。所有的时间序列数据仍然存在,但现在整体上保存在更少的块中。这极大程度地缩减了查询时间的消耗,因为需要合并的部分查询结果变得更少了。
|
||||
|
||||
#### 保留
|
||||
|
||||
我们看到了删除旧的数据在 V2 存储系统中是一个缓慢的过程,并且消耗 CPU、内存和磁盘。如何才能在我们基于块的设计上清除旧的数据?相当简单,只要删除我们配置的保留时间窗口里没有数据的块文件夹即可。在下面的例子中,块 1 可以被安全地删除,而块 2 则必须一直保留,直到它落在保留窗口边界之外。
|
||||
|
||||
```
|
||||
|
|
||||
+------------+ +----+-----+ +-----------+ +-----------+ +-----------+
|
||||
| 1 | | 2 | | | 3 | | 4 | | 5 | . . .
|
||||
+------------+ +----+-----+ +-----------+ +-----------+ +-----------+
|
||||
|
|
||||
|
|
||||
retention boundary
|
||||
```
|
||||
|
||||
随着我们不断压缩先前压缩的块,旧数据越大,块可能变得越大。因此必须为其设置一个上限,以防数据块扩展到整个数据库而损失我们设计的最初优势。
|
||||
|
||||
方便的是,这一点也限制了部分存在于保留窗口内部分存在于保留窗口外的块的磁盘消耗总量。例如上面例子中的块 2。当设置了最大块尺寸为总保留窗口的 10% 后,我们保留块 2 的总开销也有了 10% 的上限。
|
||||
|
||||
总结一下,保留与删除从非常昂贵到了几乎没有成本。
|
||||
|
||||
> 如果你读到这里并有一些数据库的背景知识,现在你也许会问:这些都是最新的技术吗?——并不是;而且可能还会做的更好。
|
||||
>
|
||||
> 在内存中批量处理数据,在预写日志中跟踪,并定期写入到磁盘的模式在现在相当普遍。
|
||||
>
|
||||
> 我们看到的好处无论在什么领域的数据里都是适用的。遵循这一方法最著名的开源案例是 LevelDB、Cassandra、InfluxDB 和 HBase。关键是避免重复发明劣质的轮子,采用经过验证的方法,并正确地运用它们。
|
||||
>
|
||||
> 脱离场景添加你自己的黑魔法是一种不太可能的情况。
|
||||
|
||||
### 索引
|
||||
|
||||
研究存储改进的最初想法是解决序列分流的问题。基于块的布局减少了查询所要考虑的序列总数。因此假设我们索引查找的复杂度是 `O(n^2)`,我们就要设法减少 n 个相当数量的复杂度,之后就相当于改进 `O(n^2)` 复杂度。——恩,等等……糟糕。
|
||||
|
||||
快速回顾一下“算法 101”课上提醒我们的,在理论上它并未带来任何好处。如果之前就很糟糕,那么现在也一样。理论是如此的残酷。
|
||||
|
||||
实际上,我们大多数的查询已经可以相当快响应。但是,跨越整个时间范围的查询仍然很慢,尽管只需要找到少部分数据。追溯到所有这些工作之前,最初我用来解决这个问题的想法是:我们需要一个更大容量的[倒排索引][9]。
|
||||
|
||||
倒排索引基于数据项内容的子集提供了一种快速的查找方式。简单地说,我可以通过标签 `app="nginx"` 查找所有的序列而无需遍历每个文件来看它是否包含该标签。
|
||||
|
||||
为此,每个序列被赋上一个唯一的 ID ,通过该 ID 可以恒定时间内检索它(`O(1)`)。在这个例子中 ID 就是我们的正向索引。
|
||||
|
||||
> 示例:如果 ID 为 10、29、9 的序列包含标签 `app="nginx"`,那么 “nginx”的倒排索引就是简单的列表 `[10, 29, 9]`,它就能用来快速地获取所有包含标签的序列。即使有 200 多亿个数据序列也不会影响查找速度。
|
||||
|
||||
简而言之,如果 `n` 是我们序列总数,`m` 是给定查询结果的大小,使用索引的查询复杂度现在就是 `O(m)`。查询语句依据它获取数据的数量 `m` 而不是被搜索的数据体 `n` 进行缩放是一个很好的特性,因为 `m` 一般相当小。
|
||||
|
||||
为了简单起见,我们假设可以在恒定时间内查找到倒排索引对应的列表。
|
||||
|
||||
实际上,这几乎就是 V2 存储系统具有的倒排索引,也是提供在数百万序列中查询性能的最低需求。敏锐的人会注意到,在最坏情况下,所有的序列都含有标签,因此 `m` 又成了 `O(n)`。这一点在预料之中,也相当合理。如果你查询所有的数据,它自然就会花费更多时间。一旦我们牵扯上了更复杂的查询语句就会有问题出现。
|
||||
|
||||
#### 标签组合
|
||||
|
||||
与数百万个序列相关的标签很常见。假设横向扩展着数百个实例的“foo”微服务,并且每个实例拥有数千个序列。每个序列都会带有标签 `app="foo"`。当然,用户通常不会查询所有的序列而是会通过进一步的标签来限制查询。例如,我想知道服务实例接收到了多少请求,那么查询语句便是 `__name__="requests_total" AND app="foo"`。
|
||||
|
||||
为了找到满足两个标签选择子的所有序列,我们得到每一个标签的倒排索引列表并取其交集。结果集通常会比任何一个输入列表小一个数量级。因为每个输入列表最坏情况下的大小为 `O(n)`,所以在嵌套地为每个列表进行<ruby>暴力求解<rt>brute force solution</rt><ruby>下,运行时间为 `O(n^2)`。相同的成本也适用于其他的集合操作,例如取并集(`app="foo" OR app="bar"`)。当在查询语句上添加更多标签选择子,耗费就会指数增长到 `O(n^3)`、`O(n^4)`、`O(n^5)`……`O(n^k)`。通过改变执行顺序,可以使用很多技巧以优化运行效率。越复杂,越是需要关于数据特征和标签之间相关性的知识。这引入了大量的复杂度,但是并没有减少算法的最坏运行时间。
|
||||
|
||||
这便是 V2 存储系统使用的基本方法,幸运的是,看似微小的改动就能获得显著的提升。如果我们假设倒排索引中的 ID 都是排序好的会怎么样?
|
||||
|
||||
假设这个例子的列表用于我们最初的查询:
|
||||
|
||||
```
|
||||
__name__="requests_total" -> [ 9999, 1000, 1001, 2000000, 2000001, 2000002, 2000003 ]
|
||||
app="foo" -> [ 1, 3, 10, 11, 12, 100, 311, 320, 1000, 1001, 10002 ]
|
||||
|
||||
intersection => [ 1000, 1001 ]
|
||||
```
|
||||
|
||||
它的交集相当小。我们可以为每个列表的起始位置设置游标,每次从最小的游标处移动来找到交集。当二者的数字相等,我们就添加它到结果中并移动二者的游标。总体上,我们以锯齿形扫描两个列表,因此总耗费是 `O(2n)=O(n)`,因为我们总是在一个列表上移动。
|
||||
|
||||
两个以上列表的不同集合操作也类似。因此 `k` 个集合操作仅仅改变了因子 `O(k*n)` 而不是最坏情况下查找运行时间的指数 `O(n^k)`。
|
||||
|
||||
我在这里所描述的是几乎所有[全文搜索引擎][10]使用的标准搜索索引的简化版本。每个序列描述符都视作一个简短的“文档”,每个标签(名称 + 固定值)作为其中的“单词”。我们可以忽略搜索引擎索引中通常遇到的很多附加数据,例如单词位置和和频率。
|
||||
|
||||
关于改进实际运行时间的方法似乎存在无穷无尽的研究,它们通常都是对输入数据做一些假设。不出意料的是,还有大量技术来压缩倒排索引,其中各有利弊。因为我们的“文档”比较小,而且“单词”在所有的序列里大量重复,压缩变得几乎无关紧要。例如,一个真实的数据集约有 440 万个序列与大约 12 个标签,每个标签拥有少于 5000 个单独的标签。对于最初的存储版本,我们坚持使用基本的方法而不压缩,仅做微小的调整来跳过大范围非交叉的 ID。
|
||||
|
||||
尽管维持排序好的 ID 听起来很简单,但实践过程中不是总能完成的。例如,V2 存储系统为新的序列赋上一个哈希值来当作 ID,我们就不能轻易地排序倒排索引。
|
||||
|
||||
另一个艰巨的任务是当磁盘上的数据被更新或删除掉后修改其索引。通常,最简单的方法是重新计算并写入,但是要保证数据库在此期间可查询且具有一致性。V3 存储系统通过每块上具有的独立不可变索引来解决这一问题,该索引仅通过压缩时的重写来进行修改。只有可变块上的索引需要被更新,它完全保存在内存中。
|
||||
|
||||
## 基准测试
|
||||
|
||||
我从存储的基准测试开始了初步的开发,它基于现实世界数据集中提取的大约 440 万个序列描述符,并生成合成数据点以输入到这些序列中。这个阶段的开发仅仅测试了单独的存储系统,对于快速找到性能瓶颈和高并发负载场景下的触发死锁至关重要。
|
||||
|
||||
在完成概念性的开发实施之后,该基准测试能够在我的 Macbook Pro 上维持每秒 2000 万的吞吐量 —— 并且这都是在打开着十几个 Chrome 的页面和 Slack 的时候。因此,尽管这听起来都很棒,它这也表明推动这项测试没有的进一步价值(或者是没有在高随机环境下运行)。毕竟,它是合成的数据,因此在除了良好的第一印象外没有多大价值。比起最初的设计目标高出 20 倍,是时候将它部署到真正的 Prometheus 服务器上了,为它添加更多现实环境中的开销和场景。
|
||||
|
||||
我们实际上没有可重现的 Prometheus 基准测试配置,特别是没有对于不同版本的 A/B 测试。亡羊补牢为时不晚,[不过现在就有一个了][11]!
|
||||
|
||||
我们的工具可以让我们声明性地定义基准测试场景,然后部署到 AWS 的 Kubernetes 集群上。尽管对于全面的基准测试来说不是最好环境,但它肯定比 64 核 128GB 内存的专用<ruby>裸机服务器<rt>bare metal servers</rt></ruby>更能反映出我们的用户群体。
|
||||
|
||||
我们部署了两个 Prometheus 1.5.2 服务器(V2 存储系统)和两个来自 2.0 开发分支的 Prometheus (V3 存储系统)。每个 Prometheus 运行在配备 SSD 的专用服务器上。我们将横向扩展的应用部署在了工作节点上,并且让其暴露典型的微服务度量。此外,Kubernetes 集群本身和节点也被监控着。整套系统由另一个 Meta-Prometheus 所监督,它监控每个 Prometheus 的健康状况和性能。
|
||||
|
||||
为了模拟序列分流,微服务定期的扩展和收缩来移除旧的 pod 并衍生新的 pod,生成新的序列。通过选择“典型”的查询来模拟查询负载,对每个 Prometheus 版本都执行一次。
|
||||
|
||||
总体上,伸缩与查询的负载以及采样频率极大的超出了 Prometheus 的生产部署。例如,我们每隔 15 分钟换出 60% 的微服务实例去产生序列分流。在现代的基础设施上,一天仅大约会发生 1-5 次。这就保证了我们的 V3 设计足以处理未来几年的工作负载。就结果而言,Prometheus 1.5.2 和 2.0 之间的性能差异在极端的环境下会变得更大。
|
||||
|
||||
总而言之,我们每秒从 850 个目标里收集大约 11 万份样本,每次暴露 50 万个序列。
|
||||
|
||||
在此系统运行一段时间之后,我们可以看一下数字。我们评估了两个版本在 12 个小时之后到达稳定时的几个指标。
|
||||
|
||||
> 请注意从 Prometheus 图形界面的截图中轻微截断的 Y 轴
|
||||
|
||||
![Heap usage GB](https://fabxc.org/tsdb/assets/heap_usage.png)
|
||||
|
||||
*堆内存使用(GB)*
|
||||
|
||||
内存资源的使用对用户来说是最为困扰的问题,因为它相对的不可预测且可能导致进程崩溃。
|
||||
|
||||
显然,查询的服务器正在消耗内存,这很大程度上归咎于查询引擎的开销,这一点可以当作以后优化的主题。总的来说,Prometheus 2.0 的内存消耗减少了 3-4 倍。大约 6 小时之后,在 Prometheus 1.5 上有一个明显的峰值,与我们设置的 6 小时的保留边界相对应。因为删除操作成本非常高,所以资源消耗急剧提升。这一点在下面几张图中均有体现。
|
||||
|
||||
![CPU usage cores](https://fabxc.org/tsdb/assets/cpu_usage.png)
|
||||
|
||||
*CPU 使用(核心/秒)*
|
||||
|
||||
类似的模式也体现在 CPU 使用上,但是查询的服务器与非查询的服务器之间的差异尤为明显。每秒获取大约 11 万个数据需要 0.5 核心/秒的 CPU 资源,比起评估查询所花费的 CPU 时间,我们的新存储系统 CPU 消耗可忽略不计。总的来说,新存储需要的 CPU 资源减少了 3 到 10 倍。
|
||||
|
||||
![Disk writes](https://fabxc.org/tsdb/assets/disk_writes.png)
|
||||
|
||||
*磁盘写入(MB/秒)*
|
||||
|
||||
迄今为止最引人注目和意想不到的改进表现在我们的磁盘写入利用率上。这就清楚的说明了为什么 Prometheus 1.5 很容易造成 SSD 损耗。我们看到最初的上升发生在第一个块被持久化到序列文件中的时期,然后一旦删除操作引发了重写就会带来第二个上升。令人惊讶的是,查询的服务器与非查询的服务器显示出了非常不同的利用率。
|
||||
|
||||
在另一方面,Prometheus 2.0 每秒仅向其预写日志写入大约一兆字节。当块被压缩到磁盘时,写入定期地出现峰值。这在总体上节省了:惊人的 97-99%。
|
||||
|
||||
![Disk usage](https://fabxc.org/tsdb/assets/disk_usage.png)
|
||||
|
||||
*磁盘大小(GB)*
|
||||
|
||||
与磁盘写入密切相关的是总磁盘空间占用量。由于我们对样本(这是我们的大部分数据)几乎使用了相同的压缩算法,因此磁盘占用量应当相同。在更为稳定的系统中,这样做很大程度上是正确地,但是因为我们需要处理高的序列分流,所以还要考虑每个序列的开销。
|
||||
|
||||
如我们所见,Prometheus 1.5 在这两个版本达到稳定状态之前,使用的存储空间因其保留操作而急速上升。Prometheus 2.0 似乎在每个序列上的开销显著降低。我们可以清楚的看到预写日志线性地充满整个存储空间,然后当压缩完成后瞬间下降。事实上对于两个 Prometheus 2.0 服务器,它们的曲线并不是完全匹配的,这一点需要进一步的调查。
|
||||
|
||||
前景大好。剩下最重要的部分是查询延迟。新的索引应当优化了查找的复杂度。没有实质上发生改变的是处理数据的过程,例如 `rate()` 函数或聚合。这些就是查询引擎要做的东西了。
|
||||
|
||||
![Query latency](https://fabxc.org/tsdb/assets/query_latency.png)
|
||||
|
||||
*第 99 个百分位查询延迟(秒)*
|
||||
|
||||
数据完全符合预期。在 Prometheus 1.5 上,查询延迟随着存储的序列而增加。只有在保留操作开始且旧的序列被删除后才会趋于稳定。作为对比,Prometheus 2.0 从一开始就保持在合适的位置。
|
||||
|
||||
我们需要花一些心思在数据是如何被采集上,对服务器发出的查询请求通过对以下方面的估计来选择:范围查询和即时查询的组合,进行更轻或更重的计算,访问更多或更少的文件。它并不需要代表真实世界里查询的分布。也不能代表冷数据的查询性能,我们可以假设所有的样本数据都是保存在内存中的热数据。
|
||||
|
||||
尽管如此,我们可以相当自信地说,整体查询效果对序列分流变得非常有弹性,并且在高压基准测试场景下提升了 4 倍的性能。在更为静态的环境下,我们可以假设查询时间大多数花费在了查询引擎上,改善程度明显较低。
|
||||
|
||||
![Ingestion rate](https://fabxc.org/tsdb/assets/ingestion_rate.png)
|
||||
|
||||
*摄入的样本/秒*
|
||||
|
||||
最后,快速地看一下不同 Prometheus 服务器的摄入率。我们可以看到搭载 V3 存储系统的两个服务器具有相同的摄入速率。在几个小时之后变得不稳定,这是因为不同的基准测试集群节点由于高负载变得无响应,与 Prometheus 实例无关。(两个 2.0 的曲线完全匹配这一事实希望足够具有说服力)
|
||||
|
||||
尽管还有更多 CPU 和内存资源,两个 Prometheus 1.5.2 服务器的摄入率大大降低。序列分流的高压导致了无法采集更多的数据。
|
||||
|
||||
那么现在每秒可以摄入的<ruby>绝对最大<rt>absolute maximum</rt></ruby>样本数是多少?
|
||||
|
||||
但是现在你可以摄取的每秒绝对最大样本数是多少?
|
||||
|
||||
我不知道 —— 虽然这是一个相当容易的优化指标,但除了稳固的基线性能之外,它并不是特别有意义。
|
||||
|
||||
有很多因素都会影响 Prometheus 数据流量,而且没有一个单独的数字能够描述捕获质量。最大摄入率在历史上是一个导致基准出现偏差的度量,并且忽视了更多重要的层面,例如查询性能和对序列分流的弹性。关于资源使用线性增长的大致猜想通过一些基本的测试被证实。很容易推断出其中的原因。
|
||||
|
||||
我们的基准测试模拟了高动态环境下 Prometheus 的压力,它比起真实世界中的更大。结果表明,虽然运行在没有优化的云服务器上,但是已经超出了预期的效果。最终,成功将取决于用户反馈而不是基准数字。
|
||||
|
||||
> 注意:在撰写本文的同时,Prometheus 1.6 正在开发当中,它允许更可靠地配置最大内存使用量,并且可能会显著地减少整体的消耗,有利于稍微提高 CPU 使用率。我没有重复对此进行测试,因为整体结果变化不大,尤其是面对高序列分流的情况。
|
||||
|
||||
## 总结
|
||||
|
||||
Prometheus 开始应对高基数序列与单独样本的吞吐量。这仍然是一项富有挑战性的任务,但是新的存储系统似乎向我们展示了未来的一些好东西。
|
||||
|
||||
第一个配备 V3 存储系统的 [alpha 版本 Prometheus 2.0][12] 已经可以用来测试了。在早期阶段预计还会出现崩溃,死锁和其他 bug。
|
||||
|
||||
存储系统的代码可以在[这个单独的项目中找到][13]。Prometheus 对于寻找高效本地存储时间序列数据库的应用来说可能非常有用,这一点令人非常惊讶。
|
||||
|
||||
> 这里需要感谢很多人作出的贡献,以下排名不分先后:
|
||||
|
||||
> Bjoern Rabenstein 和 Julius Volz 在 V2 存储引擎上的打磨工作以及 V3 存储系统的反馈,这为新一代的设计奠定了基础。
|
||||
|
||||
> Wilhelm Bierbaum 对新设计不断的建议与见解作出了很大的贡献。Brian Brazil 不断的反馈确保了我们最终得到的是语义上合理的方法。与 Peter Bourgon 深刻的讨论验证了设计并形成了这篇文章。
|
||||
|
||||
> 别忘了我们整个 CoreOS 团队与公司对于这项工作的赞助与支持。感谢所有那些听我一遍遍唠叨 SSD、浮点数、序列化格式的同学。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fabxc.org/blog/2017-04-10-writing-a-tsdb/
|
||||
|
||||
作者:[Fabian Reinartz][a]
|
||||
译者:[LuuMing](https://github.com/LuuMing)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://twitter.com/fabxc
|
||||
[1]:https://en.wikipedia.org/wiki/Inode
|
||||
[2]:https://prometheus.io/
|
||||
[3]:https://kubernetes.io/
|
||||
[4]:https://en.wikipedia.org/wiki/Write_amplification
|
||||
[5]:http://codecapsule.com/2014/02/12/coding-for-ssds-part-1-introduction-and-table-of-contents/
|
||||
[6]:https://prometheus.io/docs/practices/rules/
|
||||
[7]:http://www.vldb.org/pvldb/vol8/p1816-teller.pdf
|
||||
[8]:https://en.wikipedia.org/wiki/Mmap
|
||||
[9]:https://en.wikipedia.org/wiki/Inverted_index
|
||||
[10]:https://en.wikipedia.org/wiki/Search_engine_indexing#Inverted_indices
|
||||
[11]:https://github.com/prometheus/prombench
|
||||
[12]:https://prometheus.io/blog/2017/04/10/promehteus-20-sneak-peak/
|
||||
[13]:https://github.com/prometheus/tsdb
|
149
published/20170414 5 projects for Raspberry Pi at home.md
Normal file
149
published/20170414 5 projects for Raspberry Pi at home.md
Normal file
@ -0,0 +1,149 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (warmfrog)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10936-1.html)
|
||||
[#]: subject: (5 projects for Raspberry Pi at home)
|
||||
[#]: via: (https://opensource.com/article/17/4/5-projects-raspberry-pi-home)
|
||||
[#]: author: (Ben Nuttall https://opensource.com/users/bennuttall)
|
||||
|
||||
5 个可在家中使用的树莓派项目
|
||||
======================================
|
||||
|
||||
![5 projects for Raspberry Pi at home][1]
|
||||
|
||||
[树莓派][2] 电脑可被用来进行多种设置用于不同的目的。显然它在教育市场帮助学生在教室和创客空间中学习编程与创客技巧方面占有一席之地,它在工作场所和工厂中有大量行业应用。我打算介绍五个你可能想要在你的家中构建的项目。
|
||||
|
||||
### 媒体中心
|
||||
|
||||
在家中人们常用树莓派作为媒体中心来服务多媒体文件。它很容易搭建,树莓派提供了大量的 GPU(图形处理单元)运算能力来在大屏电视上渲染你的高清电视节目和电影。将 [Kodi][3](从前的 XBMC)运行在树莓派上是一个很棒的方式,它可以播放你的硬盘或网络存储上的任何媒体。你同样可以安装一个插件来播放 YouTube 视频。
|
||||
|
||||
还有几个略微不同的选择,最常见的是 [OSMC][4](开源媒体中心)和 [LibreELEC][5],都是基于 Kodi 的。它们在放映媒体内容方面表现的都非常好,但是 OSMC 有一个更酷炫的用户界面,而 LibreElec 更轻量级。你要做的只是选择一个发行版,下载镜像并安装到一个 SD 卡中(或者仅仅使用 [NOOBS][6]),启动,然后就准备好了。
|
||||
|
||||
![LibreElec ][7]
|
||||
|
||||
*LibreElec;树莓派基金会, CC BY-SA*
|
||||
|
||||
![OSMC][8]
|
||||
|
||||
*OSMC.tv, 版权所有, 授权使用*
|
||||
|
||||
在往下走之前,你需要决定[使用哪种树莓派][9]。这些发行版在任何树莓派(1、2、3 或 Zero)上都能运行,视频播放在这些树莓派中的任何一个上都能胜任。除了 Pi 3(和 Zero W)有内置 Wi-Fi,唯一可察觉的不同是用户界面的反应速度,在 Pi 3 上更快。Pi 2 也不会慢太多,所以如果你不需要 Wi-Fi 它也是可以的,但是当切换菜单时,你会注意到 Pi 3 比 Pi 1 和 Zero 表现的更好。
|
||||
|
||||
### SSH 网关
|
||||
|
||||
如果你想从外部网络访问你的家庭局域网的电脑和设备,你必须打开这些设备的端口来允许外部访问。在互联网中开放这些端口有安全风险,意味着你总是你总是处于被攻击、滥用或者其他各种未授权访问的风险中。然而,如果你在你的网络中安装一个树莓派,并且设置端口映射来仅允许通过 SSH 访问树莓派,你可以这么用来作为一个安全的网关来跳到网络中的其他树莓派和 PC。
|
||||
|
||||
大多数路由允许你配置端口映射规则。你需要给你的树莓派一个固定的内网 IP 地址来设置你的路由器端口 22 映射到你的树莓派端口 22。如果你的网络服务提供商给你提供了一个静态 IP 地址,你能够通过 SSH 和主机的 IP 地址访问(例如,`ssh pi@123.45.56.78`)。如果你有一个域名,你可以配置一个子域名指向这个 IP 地址,所以你没必要记住它(例如,`ssh pi@home.mydomain.com`)。
|
||||
|
||||
![][11]
|
||||
|
||||
然而,如果你不想将树莓派暴露在互联网上,你应该非常小心,不要让你的网络处于危险之中。如果你遵循一些简单的步骤来使它更安全:
|
||||
|
||||
1. 大多数人建议你更换你的登录密码(有道理,默认密码 “raspberry” 是众所周知的),但是这不能阻挡暴力攻击。你可以改变你的密码并添加一个双重验证(所以你需要你的密码*和*一个手机生成的与时间相关的密码),这么做更安全。但是,我相信最好的方法阻止入侵者访问你的树莓派是在你的 SSH 配置中[禁止密码认证][12],这样只能通过 SSH 密匙进入。这意味着任何试图猜测你的密码尝试登录的人都不会成功。只有你的私有密匙可以访问。简单来说,很多人建议将 SSH 端口从默认的 22 换成其他的,但是通过简单的 [Nmap][13] 扫描你的 IP 地址,你信任的 SSH 端口就会暴露。
|
||||
2. 最好,不要在这个树莓派上运行其他的软件,这样你不会意外暴露其他东西。如果你想要运行其他软件,你最好在网络中的其他树莓派上运行,它们没有暴露在互联网上。确保你经常升级来保证你的包是最新的,尤其是 `openssh-server` 包,这样你的安全缺陷就被打补丁了。
|
||||
3. 安装 [sshblack][14] 或 [fail2ban][15] 来将任何表露出恶意的用户加入黑名单,例如试图暴力破解你的 SSH 密码。
|
||||
|
||||
使树莓派安全后,让它在线,你将可以在世界的任何地方登录你的网络。一旦你登录到你的树莓派,你可以用 SSH 访问本地网络上的局域网地址(例如,192.168.1.31)访问其他设备。如果你在这些设备上有密码,用密码就好了。如果它们同样只允许 SSH 密匙,你需要确保你的密匙通过 SSH 转发,使用 `-A` 参数:`ssh -A pi@123.45.67.89`。
|
||||
|
||||
### CCTV / 宠物相机
|
||||
|
||||
另一个很棒的家庭项目是安装一个相机模块来拍照和录视频,录制并保存文件,在内网或者外网中进行流式传输。你想这么做有很多原因,但两个常见的情况是一个家庭安防相机或监控你的宠物。
|
||||
|
||||
[树莓派相机模块][16] 是一个优秀的配件。它提供全高清的相片和视频,包括很多高级配置,很[容易编程][17]。[红外线相机][18]用于这种目的是非常理想的,通过一个红外线 LED(树莓派可以控制的),你就能够在黑暗中看见东西。
|
||||
|
||||
如果你想通过一定频率拍摄静态图片来留意某件事,你可以仅仅写一个简短的 [Python][19] 脚本或者使用命令行工具 [raspistill][20], 在 [Cron][21] 中规划它多次运行。你可能想将它们保存到 [Dropbox][22] 或另一个网络服务,上传到一个网络服务器,你甚至可以创建一个[web 应用][23]来显示他们。
|
||||
|
||||
如果你想要在内网或外网中流式传输视频,那也相当简单。在 [picamera 文档][24]中(在 “web streaming” 章节)有一个简单的 MJPEG(Motion JPEG)例子。简单下载或者拷贝代码到文件中,运行并访问树莓派的 IP 地址的 8000 端口,你会看见你的相机的直播输出。
|
||||
|
||||
有一个更高级的流式传输项目 [pistreaming][25] 也可以,它通过在网络服务器中用 [JSMpeg][26] (一个 JavaScript 视频播放器)和一个用于相机流的单独运行的 websocket。这种方法性能更好,并且和之前的例子一样简单,但是如果要在互联网中流式传输,则需要包含更多代码,并且需要你开放两个端口。
|
||||
|
||||
一旦你的网络流建立起来,你可以将你的相机放在你想要的地方。我用一个来观察我的宠物龟:
|
||||
|
||||
![Tortoise ][27]
|
||||
|
||||
*Ben Nuttall, CC BY-SA*
|
||||
|
||||
如果你想控制相机位置,你可以用一个舵机。一个优雅的方案是用 Pimoroni 的 [Pan-Tilt HAT][28],它可以让你简单的在二维方向上移动相机。为了与 pistreaming 集成,可以看看该项目的 [pantilthat 分支][29].
|
||||
|
||||
![Pan-tilt][30]
|
||||
|
||||
*Pimoroni.com, Copyright, 授权使用*
|
||||
|
||||
如果你想将你的树莓派放到户外,你将需要一个防水的外围附件,并且需要一种给树莓派供电的方式。POE(通过以太网提供电力)电缆是一个不错的实现方式。
|
||||
|
||||
### 家庭自动化或物联网
|
||||
|
||||
现在是 2017 年(LCTT 译注:此文发表时间),到处都有很多物联网设备,尤其是家中。我们的电灯有 Wi-Fi,我们的面包烤箱比过去更智能,我们的茶壶处于俄国攻击的风险中,除非你确保你的设备安全,不然别将没有必要的设备连接到互联网,之后你可以在家中充分的利用物联网设备来完成自动化任务。
|
||||
|
||||
市场上有大量你可以购买或订阅的服务,像 Nest Thermostat 或 Philips Hue 电灯泡,允许你通过你的手机控制你的温度或者你的亮度,无论你是否在家。你可以用一个树莓派来催动这些设备的电源,通过一系列规则包括时间甚至是传感器来完成自动交互。用 Philips Hue,你做不到的当你进房间时打开灯光,但是有一个树莓派和一个运动传感器,你可以用 Python API 来打开灯光。类似地,当你在家的时候你可以通过配置你的 Nest 打开加热系统,但是如果你想在房间里至少有两个人时才打开呢?写一些 Python 代码来检查网络中有哪些手机,如果至少有两个,告诉 Nest 来打开加热器。
|
||||
|
||||
不用选择集成已存在的物联网设备,你可以用简单的组件来做的更多。一个自制的窃贼警报器,一个自动化的鸡笼门开关,一个夜灯,一个音乐盒,一个定时的加热灯,一个自动化的备份服务器,一个打印服务器,或者任何你能想到的。
|
||||
|
||||
### Tor 协议和屏蔽广告
|
||||
|
||||
Adafruit 的 [Onion Pi][31] 是一个 [Tor][32] 协议来使你的网络通讯匿名,允许你使用互联网而不用担心窥探者和各种形式的监视。跟随 Adafruit 的指南来设置 Onion Pi,你会找到一个舒服的匿名的浏览体验。
|
||||
|
||||
![Onion-Pi][33]
|
||||
|
||||
*Onion-pi from Adafruit, Copyright, 授权使用*
|
||||
|
||||
![Pi-hole][34]
|
||||
|
||||
可以在你的网络中安装一个树莓派来拦截所有的网络交通并过滤所有广告。简单下载 [Pi-hole][35] 软件到 Pi 中,你的网络中的所有设备都将没有广告(甚至屏蔽你的移动设备应用内的广告)。
|
||||
|
||||
树莓派在家中有很多用法。你在家里用树莓派来干什么?你想用它干什么?
|
||||
|
||||
在下方评论让我们知道。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/17/4/5-projects-raspberry-pi-home
|
||||
|
||||
作者:[Ben Nuttall][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[warmfrog](https://github.com/warmfrog)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/bennuttall
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/raspberry_pi_home_automation.png?itok=2TnmJpD8 (5 projects for Raspberry Pi at home)
|
||||
[2]: https://www.raspberrypi.org/
|
||||
[3]: https://kodi.tv/
|
||||
[4]: https://osmc.tv/
|
||||
[5]: https://libreelec.tv/
|
||||
[6]: https://www.raspberrypi.org/downloads/noobs/
|
||||
[7]: https://opensource.com/sites/default/files/libreelec_0.png (LibreElec )
|
||||
[8]: https://opensource.com/sites/default/files/osmc.png (OSMC)
|
||||
[9]: https://opensource.com/life/16/10/which-raspberry-pi-should-you-choose-your-project
|
||||
[10]: mailto:pi@home.mydomain.com
|
||||
[11]: https://opensource.com/sites/default/files/resize/screenshot_from_2017-04-07_15-13-01-700x380.png
|
||||
[12]: http://stackoverflow.com/questions/20898384/ssh-disable-password-authentication
|
||||
[13]: https://nmap.org/
|
||||
[14]: http://www.pettingers.org/code/sshblack.html
|
||||
[15]: https://www.fail2ban.org/wiki/index.php/Main_Page
|
||||
[16]: https://www.raspberrypi.org/products/camera-module-v2/
|
||||
[17]: https://opensource.com/life/15/6/raspberry-pi-camera-projects
|
||||
[18]: https://www.raspberrypi.org/products/pi-noir-camera-v2/
|
||||
[19]: http://picamera.readthedocs.io/
|
||||
[20]: https://www.raspberrypi.org/documentation/usage/camera/raspicam/raspistill.md
|
||||
[21]: https://www.raspberrypi.org/documentation/linux/usage/cron.md
|
||||
[22]: https://github.com/RZRZR/plant-cam
|
||||
[23]: https://github.com/bennuttall/bett-bot
|
||||
[24]: http://picamera.readthedocs.io/en/release-1.13/recipes2.html#web-streaming
|
||||
[25]: https://github.com/waveform80/pistreaming
|
||||
[26]: http://jsmpeg.com/
|
||||
[27]: https://opensource.com/sites/default/files/tortoise.jpg (Tortoise)
|
||||
[28]: https://shop.pimoroni.com/products/pan-tilt-hat
|
||||
[29]: https://github.com/waveform80/pistreaming/tree/pantilthat
|
||||
[30]: https://opensource.com/sites/default/files/pan-tilt.gif (Pan-tilt)
|
||||
[31]: https://learn.adafruit.com/onion-pi/overview
|
||||
[32]: https://www.torproject.org/
|
||||
[33]: https://opensource.com/sites/default/files/onion-pi.jpg (Onion-Pi)
|
||||
[34]: https://opensource.com/sites/default/files/resize/pi-hole-250x250.png (Pi-hole)
|
||||
[35]: https://pi-hole.net/
|
||||
|
||||
|
||||
|
103
published/20180324 Memories of writing a parser for man pages.md
Normal file
103
published/20180324 Memories of writing a parser for man pages.md
Normal file
@ -0,0 +1,103 @@
|
||||
为 man 手册页编写解析器的备忘录
|
||||
======
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/11/235607fiqfqapvpzqhh8n1.jpg)
|
||||
|
||||
我一般都很喜欢无所事事,但有时候太无聊了也不行 —— 2015 年的一个星期天下午就是这样,我决定开始写一个开源项目来让我不那么无聊。
|
||||
|
||||
在我寻求创意时,我偶然发现了一个请求,要求构建一个由 [Mathias Bynens][2] 提出的“[按 Web 标准构建的 Man 手册页查看器][1]”。没有考虑太多,我开始使用 JavaScript 编写一个手册页解析器,经过大量的反复思考,最终做出了一个 [Jroff][3]。
|
||||
|
||||
那时候,我非常熟悉手册页这个概念,而且使用过很多次,但我知道的仅止于此,我不知道它们是如何生成的,或者是否有一个标准。在经过两年后,我有了一些关于此事的想法。
|
||||
|
||||
### man 手册页是如何写的
|
||||
|
||||
当时令我感到惊讶的第一件事是,手册页的核心只是存储在系统某处的纯文本文件(你可以使用 `manpath` 命令检查这些目录)。
|
||||
|
||||
此文件中不仅包含文档,还包含使用了 20 世纪 70 年代名为 `troff` 的排版系统的格式化信息。
|
||||
|
||||
> troff 及其 GNU 实现 groff 是处理文档的文本描述以生成适合打印的排版版本的程序。**它更像是“你所描述的即你得到的”,而不是你所见即所得的。**
|
||||
>
|
||||
> - 摘自 [troff.org][4]
|
||||
|
||||
如果你对排版格式毫不熟悉,可以将它们视为 steroids 期刊用的 Markdown,但其灵活性带来的就是更复杂的语法:
|
||||
|
||||
![groff-compressor][5]
|
||||
|
||||
`groff` 文件可以手工编写,也可以使用许多不同的工具从其他格式生成,如 Markdown、Latex、HTML 等。
|
||||
|
||||
为什么 `groff` 和 man 手册页绑在一起是有历史原因的,其格式[随时间有变化][6],它的血统由一系列类似命名的程序组成:RUNOFF > roff > nroff > troff > groff。
|
||||
|
||||
但这并不一定意味着 `groff` 与手册页有多紧密的关系,它是一种通用格式,已被用于[书籍][7],甚至用于[照相排版][8]。
|
||||
|
||||
此外,值得注意的是 `groff` 也可以调用后处理器将其中间输出结果转换为最终格式,这对于终端显示来说不一定是 ascii !一些支持的格式是:TeX DVI、HTML、Canon、HP LaserJet4 兼容格式、PostScript、utf8 等等。
|
||||
|
||||
### 宏
|
||||
|
||||
该格式的其他很酷的功能是它的可扩展性,你可以编写宏来增强其基本功能。
|
||||
|
||||
鉴于 *nix 系统的悠久历史,有几个可以根据你想要生成的输出而将特定功能组合在一起的宏包,例如 `man`、`mdoc`、`mom`、`ms`、`mm` 等等。
|
||||
|
||||
手册页通常使用 `man` 和 `mdoc` 宏包编写。
|
||||
|
||||
区分原生的 `groff` 命令和宏的方式是通过标准 `groff` 包大写其宏名称。对于 `man` 宏包,每个宏的名称都是大写的,如 `.PP`、`.TH`、`.SH` 等。对于 `mdoc` 宏包,只有第一个字母是大写的: `.Pp`、`.Dt`、`.Sh`。
|
||||
|
||||
![groff-example][9]
|
||||
|
||||
### 挑战
|
||||
|
||||
无论你是考虑编写自己的 `groff` 解析器,还是只是好奇,这些都是我发现的一些更具挑战性的问题。
|
||||
|
||||
#### 上下文敏感的语法
|
||||
|
||||
表面上,`groff` 的语法是上下文无关的,遗憾的是,因为宏描述的是主体不透明的令牌,所以包中的宏集合本身可能不会实现上下文无关的语法。
|
||||
|
||||
这导致我在那时做不出来一个解析器生成器(不管好坏)。
|
||||
|
||||
#### 嵌套的宏
|
||||
|
||||
`mdoc` 宏包中的大多数宏都是可调用的,这差不多意味着宏可以用作其他宏的参数,例如,你看看这个:
|
||||
|
||||
* 宏 `Fl`(Flag)会在其参数中添加破折号,因此 `Fl s` 会生成 `-s`
|
||||
* 宏 `Ar`(Argument)提供了定义参数的工具
|
||||
* 宏 `Op`(Optional)会将其参数括在括号中,因为这是将某些东西定义为可选的标准习惯用法
|
||||
* 以下组合 `.Op Fl s Ar file ` 将生成 `[-s file]`,因为 `Op` 宏可以嵌套。
|
||||
|
||||
#### 缺乏适合初学者的资源
|
||||
|
||||
让我感到困惑的是缺乏一个规范的、定义明确的、清晰的来源,网上有很多信息,这些信息对读者来说很重要,需要时间来掌握。
|
||||
|
||||
### 有趣的宏
|
||||
|
||||
总结一下,我会向你提供一个非常简短的宏列表,我在开发 jroff 时发现它很有趣:
|
||||
|
||||
`man` 宏包:
|
||||
|
||||
* `.TH`:用 `man` 宏包编写手册页时,你的第一个不是注释的行必须是这个宏,它接受五个参数:`title`、`section`、`date`、`source`、`manual`。
|
||||
* `.BI`:粗体加斜体(特别适用于函数格式)
|
||||
* `.BR`:粗体加正体(特别适用于参考其他手册页)
|
||||
|
||||
`mdoc` 宏包:
|
||||
|
||||
* `.Dd`、`.Dt`、`.Os`:类似于 `man` 宏包需要 `.TH`,`mdoc` 宏也需要这三个宏,需要按特定顺序使用。它们的缩写分别代表:文档日期、文档标题和操作系统。
|
||||
* `.Bl`、`.It`、`.El`:这三个宏用于创建列表,它们的名称不言自明:开始列表、项目和结束列表。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://monades.roperzh.com/memories-writing-parser-man-pages/
|
||||
|
||||
作者:[Roberto Dip][a]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://monades.roperzh.com
|
||||
[1]:https://github.com/h5bp/lazyweb-requests/issues/114
|
||||
[2]:https://mathiasbynens.be/
|
||||
[3]:jroff
|
||||
[4]:https://www.troff.org/
|
||||
[5]:https://user-images.githubusercontent.com/4419992/37868021-2e74027c-2f7f-11e8-894b-80829ce39435.gif
|
||||
[6]:https://manpages.bsd.lv/history.html
|
||||
[7]:https://rkrishnan.org/posts/2016-03-07-how-is-gopl-typeset.html
|
||||
[8]:https://en.wikipedia.org/wiki/Phototypesetting
|
||||
[9]:https://user-images.githubusercontent.com/4419992/37866838-e602ad78-2f6e-11e8-97a9-2a4494c766ae.jpg
|
@ -0,0 +1,191 @@
|
||||
如何使用 GParted 实用工具缩放根分区
|
||||
======
|
||||
|
||||
今天,我们将讨论磁盘分区。这是 Linux 中的一个好话题。这允许用户来重新调整在 Linux 中的活动 root 分区。
|
||||
|
||||
在这篇文章中,我们将教你如何使用 GParted 缩放在 Linux 上的活动根分区。
|
||||
|
||||
比如说,当我们安装 Ubuntu 操作系统时,并没有恰当地配置,我们的系统仅有 30 GB 磁盘。我们需要安装另一个操作系统,因此我们想在其中制作第二个分区。
|
||||
|
||||
虽然不建议重新调整活动分区。然而,我们要执行这个操作,因为没有其它方法来释放系统分区。
|
||||
|
||||
> 注意:在执行这个动作前,确保你备份了重要的数据,因为如果一些东西出错(例如,电源故障或你的系统重启),你可以得以保留你的数据。
|
||||
|
||||
### Gparted 是什么
|
||||
|
||||
[GParted][1] 是一个自由的分区管理器,它使你能够缩放、复制和移动分区,而不丢失数据。通过使用 GParted 的 Live 可启动镜像,我们可以使用 GParted 应用程序的所有功能。GParted Live 可以使你能够在 GNU/Linux 以及其它的操作系统上使用 GParted,例如,Windows 或 Mac OS X 。
|
||||
|
||||
#### 1) 使用 df 命令检查磁盘空间利用率
|
||||
|
||||
我只是想使用 `df` 命令向你显示我的分区。`df` 命令输出清楚地表明我仅有一个分区。
|
||||
|
||||
```
|
||||
$ df -h
|
||||
Filesystem Size Used Avail Use% Mounted on
|
||||
/dev/sda1 30G 3.4G 26.2G 16% /
|
||||
none 4.0K 0 4.0K 0% /sys/fs/cgroup
|
||||
udev 487M 4.0K 487M 1% /dev
|
||||
tmpfs 100M 844K 99M 1% /run
|
||||
none 5.0M 0 5.0M 0% /run/lock
|
||||
none 498M 152K 497M 1% /run/shm
|
||||
none 100M 52K 100M 1% /run/user
|
||||
```
|
||||
|
||||
#### 2) 使用 fdisk 命令检查磁盘分区
|
||||
|
||||
我将使用 `fdisk` 命令验证这一点。
|
||||
|
||||
```
|
||||
$ sudo fdisk -l
|
||||
[sudo] password for daygeek:
|
||||
|
||||
Disk /dev/sda: 33.1 GB, 33129218048 bytes
|
||||
255 heads, 63 sectors/track, 4027 cylinders, total 64705504 sectors
|
||||
Units = sectors of 1 * 512 = 512 bytes
|
||||
Sector size (logical/physical): 512 bytes / 512 bytes
|
||||
I/O size (minimum/optimal): 512 bytes / 512 bytes
|
||||
Disk identifier: 0x000473a3
|
||||
|
||||
Device Boot Start End Blocks Id System
|
||||
/dev/sda1 * 2048 62609407 31303680 83 Linux
|
||||
/dev/sda2 62611454 64704511 1046529 5 Extended
|
||||
/dev/sda5 62611456 64704511 1046528 82 Linux swap / Solaris
|
||||
```
|
||||
|
||||
#### 3) 下载 GParted live ISO 镜像
|
||||
|
||||
使用下面的命令来执行下载 GParted live ISO。
|
||||
|
||||
```
|
||||
$ wget https://downloads.sourceforge.net/gparted/gparted-live-0.31.0-1-amd64.iso
|
||||
```
|
||||
|
||||
#### 4) 使用 GParted Live 安装介质启动你的系统
|
||||
|
||||
使用 GParted Live 安装介质(如烧录的 CD/DVD 或 USB 或 ISO 镜像)启动你的系统。你将获得类似于下面屏幕的输出。在这里选择 “GParted Live (Default settings)” ,并敲击回车按键。
|
||||
|
||||
![][3]
|
||||
|
||||
#### 5) 键盘选择
|
||||
|
||||
默认情况下,它选择第二个选项,按下回车即可。
|
||||
|
||||
![][4]
|
||||
|
||||
#### 6) 语言选择
|
||||
|
||||
默认情况下,它选择 “33” 美国英语,按下回车即可。
|
||||
|
||||
![][5]
|
||||
|
||||
#### 7) 模式选择(图形用户界面或命令行)
|
||||
|
||||
默认情况下,它选择 “0” 图形用户界面模式,按下回车即可。
|
||||
|
||||
![][6]
|
||||
|
||||
#### 8) 加载 GParted Live 屏幕
|
||||
|
||||
现在,GParted Live 屏幕已经加载,它显示我以前创建的分区列表。
|
||||
|
||||
![][7]
|
||||
|
||||
#### 9) 如何重新调整根分区大小
|
||||
|
||||
选择你想重新调整大小的根分区,在这里仅有一个分区,所以我将编辑这个分区以便于安装另一个操作系统。
|
||||
|
||||
![][8]
|
||||
|
||||
为做到这一点,按下 “Resize/Move” 按钮来重新调整分区大小。
|
||||
|
||||
![][9]
|
||||
|
||||
现在,在第一个框中输入你想从这个分区中取出的大小。我将索要 “10GB”,所以,我添加 “10240MB”,并让该对话框的其余部分为默认值,然后点击 “Resize/Move” 按钮。
|
||||
|
||||
![][10]
|
||||
|
||||
它将再次要求你确认重新调整分区的大小,因为你正在编辑活动的系统分区,然后点击 “Ok”。
|
||||
|
||||
![][11]
|
||||
|
||||
分区从 30GB 缩小到 20GB 已经成功。也显示 10GB 未分配的磁盘空间。
|
||||
|
||||
![][12]
|
||||
|
||||
最后点击 “Apply” 按钮来执行下面剩余的操作。
|
||||
|
||||
![][13]
|
||||
|
||||
`e2fsck` 是一个文件系统检查实用程序,自动修复文件系统中与 HDD 相关的坏扇道、I/O 错误。
|
||||
|
||||
![][14]
|
||||
|
||||
`resize2fs` 程序将重新调整 ext2、ext3 或 ext4 文件系统的大小。它可以被用于扩大或缩小一个位于设备上的未挂载的文件系统。
|
||||
|
||||
![][15]
|
||||
|
||||
`e2image` 程序将保存位于设备上的关键的 ext2、ext3 或 ext4 文件系统的元数据到一个指定文件中。
|
||||
|
||||
![][16]
|
||||
|
||||
所有的操作完成,关闭对话框。
|
||||
|
||||
![][17]
|
||||
|
||||
现在,我们可以看到未分配的 “10GB” 磁盘分区。
|
||||
|
||||
![][18]
|
||||
|
||||
重启系统来检查这一结果。
|
||||
|
||||
![][19]
|
||||
|
||||
#### 10) 检查剩余空间
|
||||
|
||||
重新登录系统,并使用 `fdisk` 命令来查看在分区中可用的空间。是的,我可以看到这个分区上未分配的 “10GB” 磁盘空间。
|
||||
|
||||
```
|
||||
$ sudo parted /dev/sda print free
|
||||
[sudo] password for daygeek:
|
||||
Model: ATA VBOX HARDDISK (scsi)
|
||||
Disk /dev/sda: 32.2GB
|
||||
Sector size (logical/physical): 512B/512B
|
||||
Partition Table: msdos
|
||||
Disk Flags:
|
||||
|
||||
Number Start End Size Type File system Flags
|
||||
32.3kB 10.7GB 10.7GB Free Space
|
||||
1 10.7GB 32.2GB 21.5GB primary ext4 boot
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.2daygeek.com/author/magesh/
|
||||
[1]:https://gparted.org/
|
||||
[2]:data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
[3]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-1.png
|
||||
[4]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-2.png
|
||||
[5]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-3.png
|
||||
[6]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-4.png
|
||||
[7]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-5.png
|
||||
[8]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-6.png
|
||||
[9]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-7.png
|
||||
[10]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-8.png
|
||||
[11]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-9.png
|
||||
[12]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-10.png
|
||||
[13]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-11.png
|
||||
[14]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-12.png
|
||||
[15]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-13.png
|
||||
[16]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-14.png
|
||||
[17]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-15.png
|
||||
[18]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-16.png
|
||||
[19]:https://www.2daygeek.com/wp-content/uploads/2014/08/how-to-resize-active-primary-root-partition-in-linux-using-gparted-utility-17.png
|
@ -1,21 +1,22 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (lujun9972)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10977-1.html)
|
||||
[#]: subject: (Get desktop notifications from Emacs shell commands ·)
|
||||
[#]: via: (https://blog.hoetzel.info/post/eshell-notifications/)
|
||||
[#]: author: (Jürgen Hötzel https://blog.hoetzel.info)
|
||||
|
||||
Get desktop notifications from Emacs shell commands ·
|
||||
让 Emacs shell 命令发送桌面通知
|
||||
======
|
||||
When interacting with the operating systems I always use [Eshell][1] because it integrates seamlessly with Emacs, supports (remote) [TRAMP][2] file names and also works nice on Windows.
|
||||
|
||||
After starting shell commands (like long running build jobs) I often lose track the task when switching buffers.
|
||||
我总是使用 [Eshell][1] 来与操作系统进行交互,因为它与 Emacs 无缝整合、支持处理 (远程) [TRAMP][2] 文件,而且在 Windows 上也能工作得很好。
|
||||
|
||||
Thanks to Emacs [hooks][3] mechanism you can customize Emacs to call a elisp function when an external command finishes.
|
||||
启动 shell 命令后 (比如耗时严重的构建任务) 我经常会由于切换缓冲区而忘了追踪任务的运行状态。
|
||||
|
||||
I use [John Wiegleys][4] excellent [alert][5] package to send desktop notifications:
|
||||
多亏了 Emacs 的 [钩子][3] 机制,你可以配置 Emacs 在某个外部命令完成后调用一个 elisp 函数。
|
||||
|
||||
我使用 [John Wiegleys][4] 所编写的超棒的 [alert][5] 包来发送桌面通知:
|
||||
|
||||
```
|
||||
(require 'alert)
|
||||
@ -32,7 +33,7 @@ I use [John Wiegleys][4] excellent [alert][5] package to send desktop notificati
|
||||
(add-hook 'eshell-kill-hook #'eshell-command-alert)
|
||||
```
|
||||
|
||||
[alert][5] rules can be setup programmatically. In my case I only want to get notified if the corresponding buffer is not visible:
|
||||
[alert][5] 的规则可以用程序来设置。就我这个情况来看,我只需要当对应的缓冲区不可见时得到通知:
|
||||
|
||||
```
|
||||
(alert-add-rule :status '(buried) ;only send alert when buffer not visible
|
||||
@ -40,7 +41,8 @@ I use [John Wiegleys][4] excellent [alert][5] package to send desktop notificati
|
||||
:style 'notifications)
|
||||
```
|
||||
|
||||
This even works on [TRAMP][2] buffers. Below is a screenshot showing a Gnome desktop notification of a failed `make` command.
|
||||
|
||||
这甚至对于 [TRAMP][2] 也一样生效。下面这个截屏展示了失败的 `make` 命令产生的 Gnome 桌面通知。
|
||||
|
||||
![../../img/eshell.png][6]
|
||||
|
||||
@ -51,7 +53,7 @@ via: https://blog.hoetzel.info/post/eshell-notifications/
|
||||
作者:[Jürgen Hötzel][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[lujun9972](https://github.com/lujun9972)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
@ -62,4 +64,4 @@ via: https://blog.hoetzel.info/post/eshell-notifications/
|
||||
[3]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Hooks.html (hooks)
|
||||
[4]: https://github.com/jwiegley (John Wiegleys)
|
||||
[5]: https://github.com/jwiegley/alert (alert)
|
||||
[6]: https://blog.hoetzel.info/img/eshell.png (../../img/eshell.png)
|
||||
[6]: https://blog.hoetzel.info/img/eshell.png
|
@ -0,0 +1,178 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10969-1.html)
|
||||
[#]: subject: (GoAccess – A Real-Time Web Server Log Analyzer And Interactive Viewer)
|
||||
[#]: via: (https://www.2daygeek.com/goaccess-a-real-time-web-server-log-analyzer-and-interactive-viewer/)
|
||||
[#]: author: (Vinoth Kumar https://www.2daygeek.com/author/vinoth/)
|
||||
|
||||
GoAccess:一个实时的 Web 日志分析器及交互式查看器
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/12/222616h80pl0k0tt811071.jpg)
|
||||
|
||||
分析日志文件对于 Linux 管理员来说是一件非常令人头疼的事情,因为它记录了很多东西。大多数新手和初级管理员都不知道如何分析。如果你在分析日志方面拥有很多知识,那么你就成了 *NIX 系统高手。
|
||||
|
||||
Linux 中有许多工具可以轻松分析日志。GoAccess 是允许用户轻松分析 Web 服务器日志的工具之一。我们将在本文中详细讨论 GoAccess 工具。
|
||||
|
||||
### GoAccess
|
||||
|
||||
GoAccess 是一个实时 Web 日志分析器和交互式查看器,可以在 *nix 系统中的终端运行或通过浏览器访问。
|
||||
|
||||
GoAccess 需要的依赖极少,它是用 C 语言编写的,只需要 ncurses。
|
||||
|
||||
它支持 Apache、Nginx 和 Lighttpd 日志。它为需要动态可视化服务器报告的系统管理员即时提供了快速且有价值的 HTTP 统计信息。
|
||||
|
||||
GoAccess 可以解析指定的 Web 日志文件并将数据输出到 X 终端和浏览器。
|
||||
|
||||
GoAccess 被设计成一个基于终端的快速日志分析器。其核心思想是实时快速分析和查看 Web 服务器统计信息,而无需使用浏览器。
|
||||
|
||||
默认输出是在终端输出,它也能够生成完整的、自包含的实时 HTML 报告,以及 JSON 和 CSV 报告。
|
||||
|
||||
GoAccess 支持任何自定义日志格式,并包含以下预定义日志格式选项:Apache/Nginx 中的组合日志格式 XLF/ELF,Apache 中的通用日志格式 CLF,但不限于此。
|
||||
|
||||
### GoAccess 功能
|
||||
|
||||
* 完全实时:所有指标在终端上每 200 毫秒更新一次,在 HTML 输出上每秒更新一次。
|
||||
* 跟踪应用程序响应时间:跟踪服务请求所需的时间。如果你想跟踪减慢了网站速度的网页,则非常有用。
|
||||
* 访问者:按小时或日期确定最慢运行的请求的点击量、访问者数、带宽数和指标。
|
||||
* 按虚拟主机的度量标准:如果有多个虚拟主机(`Server`),它提供了一个面板,可显示哪些虚拟主机正在消耗大部分 Web 服务器资源。
|
||||
|
||||
### 如何安装 GoAccess?
|
||||
|
||||
我建议用户在包管理器的帮助下从发行版官方的存储库安装 GoAccess。它在大多数发行版官方存储库中都可用。
|
||||
|
||||
我们知道,我们在标准发行方式的发行版中得到的是过时的软件包,而滚动发行方式的发行版总是包含最新的软件包。
|
||||
|
||||
如果你使用标准发行方式的发行版运行操作系统,我建议你检查替代选项,如 PPA 或 GoAccess 官方维护者存储库等,以获取最新的软件包。
|
||||
|
||||
对于 Debian / Ubuntu 系统,使用 [APT-GET 命令][1]或 [APT 命令][2]在你的系统上安装 GoAccess。
|
||||
|
||||
```
|
||||
# apt install goaccess
|
||||
```
|
||||
|
||||
要获取最新的 GoAccess 包,请使用以下 GoAccess 官方存储库。
|
||||
|
||||
```
|
||||
$ echo "deb https://deb.goaccess.io/ $(lsb_release -cs) main" | sudo tee -a /etc/apt/sources.list.d/goaccess.list
|
||||
$ wget -O - https://deb.goaccess.io/gnugpg.key | sudo apt-key add -
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install goaccess
|
||||
```
|
||||
|
||||
对于 RHEL / CentOS 系统,使用 [YUM 包管理器][3]在你的系统上安装 GoAccess。
|
||||
|
||||
```
|
||||
# yum install goaccess
|
||||
```
|
||||
|
||||
对于 Fedora 系统,使用 [DNF 包管理器][4]在你的系统上安装 GoAccess。
|
||||
|
||||
```
|
||||
# dnf install goaccess
|
||||
```
|
||||
|
||||
对于基于 ArchLinux / Manjaro 的系统,使用 [Pacman 包管理器][5]在你的系统上安装 GoAccess。
|
||||
|
||||
```
|
||||
# pacman -S goaccess
|
||||
```
|
||||
|
||||
对于 openSUSE Leap 系统,使用[Zypper 包管理器][6]在你的系统上安装 GoAccess。
|
||||
|
||||
```
|
||||
# zypper install goaccess
|
||||
# zypper ar -f obs://server:http
|
||||
# zypper ref && zypper in goaccess
|
||||
```
|
||||
|
||||
### 如何使用 GoAccess?
|
||||
|
||||
成功安装 GoAccess 后。只需输入 `goaccess` 命令,然后输入 Web 服务器日志位置即可查看。
|
||||
|
||||
```
|
||||
# goaccess [options] /path/to/Web Server/access.log
|
||||
# goaccess /var/log/apache/2daygeek_access.log
|
||||
```
|
||||
|
||||
执行上述命令时,它会要求您选择日志格式配置。
|
||||
|
||||
![][8]
|
||||
|
||||
我用 Apache 访问日志对此进行了测试。Apache 日志被分为十五个部分。详情如下。主要部分显示了这十五个部分的摘要。
|
||||
|
||||
以下屏幕截图包括四个部分,例如唯一身份访问者、请求的文件、静态请求、未找到的网址。
|
||||
|
||||
![][10]
|
||||
|
||||
以下屏幕截图包括四个部分,例如访客主机名和 IP、操作系统、浏览器、时间分布。
|
||||
|
||||
![][10]
|
||||
|
||||
以下屏幕截图包括四个部分,例如来源网址、来源网站,Google 的搜索引擎结果、HTTP状态代码。
|
||||
|
||||
![][11]
|
||||
|
||||
如果要生成 html 报告,请使用以下命令。最初我在尝试生成 html 报告时遇到错误。
|
||||
|
||||
```
|
||||
# goaccess 2daygeek_access.log -a > report.html
|
||||
|
||||
GoAccess - version 1.3 - Nov 23 2018 11:28:19
|
||||
Config file: No config file used
|
||||
|
||||
Fatal error has occurred
|
||||
Error occurred at: src/parser.c - parse_log - 2764
|
||||
No time format was found on your conf file.Parsing... [0] [0/s]
|
||||
```
|
||||
|
||||
它说“你的 conf 文件没有找到时间格式”。要解决此问题,请为其添加 “COMBINED” 日志格式选项。
|
||||
|
||||
```
|
||||
# goaccess -f 2daygeek_access.log --log-format=COMBINED -o 2daygeek.html
|
||||
Parsing...[0,165] [50,165/s]
|
||||
```
|
||||
|
||||
![][12]
|
||||
|
||||
GoAccess 也允许你访问和分析实时日志并进行过滤和解析。
|
||||
|
||||
```
|
||||
# tail -f /var/log/apache/2daygeek_access.log | goaccess -
|
||||
```
|
||||
|
||||
更多细节请参考其 man 手册页或帮助。
|
||||
|
||||
```
|
||||
# man goaccess
|
||||
或
|
||||
# goaccess --help
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/goaccess-a-real-time-web-server-log-analyzer-and-interactive-viewer/
|
||||
|
||||
作者:[Vinoth Kumar][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/vinoth/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[2]: https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[3]: https://www.2daygeek.com/yum-command-examples-manage-packages-rhel-centos-systems/
|
||||
[4]: https://www.2daygeek.com/dnf-command-examples-manage-packages-fedora-system/
|
||||
[5]: https://www.2daygeek.com/pacman-command-examples-manage-packages-arch-linux-system/
|
||||
[6]: https://www.2daygeek.com/zypper-command-examples-manage-packages-opensuse-system/
|
||||
[7]: data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
[8]: https://www.2daygeek.com/wp-content/uploads/2019/01/goaccess-a-real-time-web-server-log-analyzer-and-interactive-viewer-1.png
|
||||
[9]: https://www.2daygeek.com/wp-content/uploads/2019/01/goaccess-a-real-time-web-server-log-analyzer-and-interactive-viewer-2.png
|
||||
[10]: https://www.2daygeek.com/wp-content/uploads/2019/01/goaccess-a-real-time-web-server-log-analyzer-and-interactive-viewer-3.png
|
||||
[11]: https://www.2daygeek.com/wp-content/uploads/2019/01/goaccess-a-real-time-web-server-log-analyzer-and-interactive-viewer-4.png
|
||||
[12]: https://www.2daygeek.com/wp-content/uploads/2019/01/goaccess-a-real-time-web-server-log-analyzer-and-interactive-viewer-5.png
|
@ -0,0 +1,163 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10956-1.html)
|
||||
[#]: subject: (Blockchain 2.0 – Explaining Smart Contracts And Its Types [Part 5])
|
||||
[#]: via: (https://www.ostechnix.com/blockchain-2-0-explaining-smart-contracts-and-its-types/)
|
||||
[#]: author: (editor https://www.ostechnix.com/author/editor/)
|
||||
|
||||
区块链 2.0:智能合约及其类型(五)
|
||||
======
|
||||
|
||||
![Explaining Smart Contracts And Its Types][1]
|
||||
|
||||
这是 区块链 2.0 系列的第 5 篇文章。本系列的前一篇文章探讨了我们如何[在房地产行业实现区块链][2]。本文简要探讨了区块链及相关技术领域内的<ruby>智能合约<rt>Smart Contract</rt></ruby>主题。智能合约是在区块链上验证和创建新“数据块”的基本协议,它被吹捧为该系统未来发展和应用的焦点。 然而,像所有“万灵药”一样,它不是一切的答案。我们将从基础知识中探索这个概念,以了解“智能合约”是什么以及它们不是什么。
|
||||
|
||||
### 不断发展的合同
|
||||
|
||||
这个世界建立在合同(合约)之上。在当前社会,没有合约的使用和再利用,地球上任何个人或公司都无法运作。订立、维护和执行合同的任务变得如此复杂,以至于整个司法和法律系统都必须以“合同法”的名义建立起来以支持它。事实上,大多数合同都是由一个“可信的”第三方监督,以确保最终的利益攸关者按照达成的条件得到妥善处理。有些合同甚至涉及到了第三方受益人。此类合同旨在对不是合同的活跃(或参与)方的第三方产生影响。解决和争论合同义务占据了民事诉讼所涉及的大部分法律纠纷。当然,更好的处理合同的方式来对于个人和企业来说都是天赐之物。更不用说它将以核查和证明的名义节省政府的巨大的[文书工作][7] [^1]。
|
||||
|
||||
本系列中的大多数文章都研究了如何利用现有的区块链技术。相比之下,这篇文章将更多地讲述对未来几年的预期。关于“智能合约”的讨论源于前一篇文章中提出的财产讨论。当前这篇文章旨在概述区块链自动执行“智能”可执行程序的能力。务实地处理这个问题意味着我们首先必须定义和探索这些“智能合约”是什么,以及它们如何适应现有的合同系统。我们将在下一篇题为“区块链 2.0:正在进行的项目”的文章中查看当前该领域正在进行的主要应用和项目。
|
||||
|
||||
### 定义智能合约
|
||||
|
||||
[本系列的第一篇文章][3]从基本的角度来看待区块链,将其看作由数据块组成的“分布式分类账本”,这些数据块是:
|
||||
|
||||
* 防篡改
|
||||
* 不可否认(意味着每个数据块都是由某人显式创建的,并且该人不能否认相同的责任)
|
||||
* 安全,且能抵御传统的网络攻击方法
|
||||
* 几乎是永久性的(当然这取决于区块链协议层)
|
||||
* 高度冗余,通过存在于多个网络节点或参与者系统上,其中一个节点的故障不会以任何方式影响系统的功能,并且,
|
||||
* 根据应用的不同可以提供更快的处理速度。
|
||||
|
||||
由于每个数据实例都是安全存储和通过适当的凭证访问的,因此区块链网络可以为精确验证事实和信息提供简便的基础,而无需第三方监督。区块链 2.0 开发也允许“分布式应用程序(DApp)”(我们将在接下来的文章中详细介绍这个术语)。这些分布式应用程序要求存在网络上并在其上运行。当用户需要它们时就会调用它们,并通过使用已经过审核并存储在区块链上的信息来执行它们。
|
||||
|
||||
上面的最后一段为智能合约的定义提供了基础。<ruby>数字商会<rt>The Chamber for Digital Commerce</rt></ruby>提供了一个许多专家都同意的智能合约定义。
|
||||
|
||||
> “(智能合约是一种)计算机代码,在发生指定条件时,能够根据预先指定的功能自动运行。该代码可以在分布式分类帐本上存储和处理,并将产生的任何更改写入分布式分类帐本” [^2]。
|
||||
|
||||
智能合约如上所述是一种简单的计算机程序,就像 “if-then” 或 “if-else if” 语句一样工作。关于其“智能”的方面来自这样一个事实,即该程序的预定义输入来自区块链分类账本,如上所述,它是一个记录信息的安全可靠的来源。如有必要,程序可以调用外部服务或来源以获取信息,以验证操作条款,并且仅在满足所有预定义条件后才执行。
|
||||
|
||||
必须记住,与其名称所暗示的不同,智能合约通常不是自治实体,严格来说,也不是合同。1996 年,Nick Szabo 很早就提到了智能合约,他将其与接受付款并交付用户选择的产品的自动售货机进行了比较。可以在[这里][4]查看全文。此外,人们正在制定允许智能合约进入主流合同使用的法律框架,因此目前该技术的使用仅限于法律监督不那么明确和严格的领域 [^4]。
|
||||
|
||||
### 智能合约的主要类型
|
||||
|
||||
假设读者对合同和计算机编程有基本的了解,并且基于我们对智能合约的定义,我们可以将智能合约和协议粗略地分类为以下主要类别。
|
||||
|
||||
#### 1、智能法律合约
|
||||
|
||||
这大概是最明显的一种。大多数(如果不是全部)合同都具有法律效力。在不涉及太多技术问题的情况下,智能法律合约是涉及到严格的法律追索权的合同,以防参与合同的当事人不履行其交易的目的。如前所述,不同国家和地区的现行法律框架对区块链上的智能和自动化合约缺乏足够的支持,其法律地位也不明确。但是,一旦制定了法律,就可以订立智能合约,以简化目前涉及严格监管的流程,如金融和房地产市场交易、政府补贴、国际贸易等。
|
||||
|
||||
#### 2、DAO
|
||||
|
||||
<ruby>去中心化自治组织<rt>Decentralized Autonomous Organization</rt></ruby>,即DAO,可以粗略地定义为区块链上存在的社区。该社区可以通过一组规则来定义,这些规则通过智能合约来体现并放入代码中。然后,每个参与者的每一个行动都将受到这些规则的约束,其任务是在程序中断的情况下执行并获得追索权。许多智能合约构成了这些规则,它们协同监管和监督参与者。
|
||||
|
||||
名为“创世纪 DAO” 的 DAO 是由以太坊参与者于 2016 年 5 月创建。该社区旨在成为众筹和风险投资平台。在极短的时间内,他们设法筹集了惊人的 1.5 亿美元。然而,由于黑客在系统中发现了漏洞,并设法从众筹投资者手中窃取价值约 5000 万美元的以太币。这次黑客破坏的后果导致以太坊区块链[分裂为两个][8],以太坊和以太坊经典。
|
||||
|
||||
#### 3、应用逻辑合约(ALC)
|
||||
|
||||
如果你已经听说过与区块链相结合的物联网,那么很可能它涉及到了<ruby>应用逻辑合约<rt>Application logic contract</rt></ruby>,即 ALC。此类智能合约包含特定于应用的代码,这些代码可以与区块链上的其他智能合约和程序一起工作。它们有助于与设备进行通信并验证设备之间的通信(在物联网领域)。ALC 是每个多功能智能合约的关键部分,并且大多数都是在一个管理程序下工作。在这里引用的大多数例子中,它们到处都能找到[应用][9] [^6]。
|
||||
|
||||
*由于该领域还在开发中,因此目前所说的任何定义或标准最多只能说是变化而模糊的。*
|
||||
|
||||
### 智能合约是如何工作的?
|
||||
|
||||
为简化起见,让我们用个例子来说明。
|
||||
|
||||
约翰和彼得是两个争论足球比赛得分的人。他们对比赛结果持有相互矛盾的看法,他们都支持不同的球队(这是背景情况)。由于他们两个都需要去其他地方并且无法看完比赛,所以约翰认为如果 A 队在比赛中击败 B 队,他就*支付*给彼得 100 美元。彼得*考虑*之后*接受*了该赌注,同时明确表示他们必须接受这些条款。但是,他们没有兑现该赌注的相互信任,也没有时间和钱来指定第三方监督赌注。
|
||||
|
||||
假设约翰和彼得都使用像 [Etherparty][5] 这样的智能合约平台,它可以在合约谈判时自动结算赌注,他们都会将基于区块链的身份链接到该合约,并设置条款,明确表示一旦比赛结束,该程序将找出获胜方是谁,并自动将该金额从输家中归入获胜者银行账户。一旦比赛结束并且媒体报道同样的结果,该程序将在互联网上搜索规定的来源,确定哪支球队获胜,将其与合约条款联系起来,在这种情况下,如果 A 队赢了彼得将从约翰哪里得到钱,也就是说将约翰的 100 美元转移到彼得的账户。执行完毕后,除非另有说明,否则智能合约将终止并在未来所有的时间内处于非活动状态。
|
||||
|
||||
抛开例子的简单不说,这种情况涉及到一个经典的合同,而参与者选择使用智能合约实现了相同目的。所有的智能合约基本上都遵循类似的原则,对程序进行编码,以便在预定义的参数上执行,并且只抛出预期的输出。智能合同咨询的外部来源可以是有时被称为 IT 世界中的<ruby>神谕<rt>Oracle</rt></ruby>。神谕是当今全球许多智能合约系统的常见部分。
|
||||
|
||||
在这种情况下使用智能合约使参与者可以获得以下好处:
|
||||
|
||||
* 它比在一起并手动结算更快。
|
||||
* 从其中删除了信任问题。
|
||||
* 消除了受信任的第三方代表有关各方处理和解的必要性。
|
||||
* 执行时无需任何费用。
|
||||
* 在如何处理参数和敏感数据方面是安全的。
|
||||
* 相关数据将永久保留在他们运行的区块链平台中,未来可以通过调用相同的函数并为其提供更多输入来设置投注。
|
||||
* 随着时间的推移,假设约翰和彼得变得赌博成瘾,该程序可以帮助他们开发可靠的统计数据来衡量他们的连胜纪录。
|
||||
|
||||
现在我们知道**什么是智能合约**和**它们如何工作**,我们还没有解决**为什么我们需要它们**。
|
||||
|
||||
### 智能合约的需要
|
||||
|
||||
正如之前的例子我们重点提到过的,出于各种原因,我们需要智能合约。
|
||||
|
||||
#### 透明度
|
||||
|
||||
交易对手非常清楚所涉及的条款和条件。此外,由于程序或智能合约的执行涉及某些明确的输入,因此用户可以非常直接地核实会影响他们和合约受益人的因素。
|
||||
|
||||
#### 时间效率
|
||||
|
||||
如上所述,智能合约一旦被控制变量或用户调用所触发,就立即开始工作。由于数据是通过区块链和网络中的其它来源即时提供给系统,因此执行不需要任何时间来验证和处理信息并解决交易。例如,转移土地所有权契约,这是一个涉及手工核实大量文书工作并且需要数周时间的过程,可以在几分钟甚至几秒钟内通过智能合约程序来处理文件和相关各方。
|
||||
|
||||
#### 精度
|
||||
|
||||
由于平台基本上只是计算机代码和预定义的内容,因此不存在主观错误,所有结果都是精确的,完全没有人为错误。
|
||||
|
||||
#### 安全
|
||||
|
||||
区块链的一个固有特征是每个数据块都是安全加密的。这意味着为了实现冗余,即使数据存储在网络上的多个节点上,**也只有数据所有者才能访问以查看和使用数据**。类似地,利用区块链在过程中存储重要变量和结果,所有过程都将是完全安全和防篡改的。同样也通过按时间顺序为审计人员提供原始的、未经更改的和不可否认的数据版本,简化了审计和法规事务。
|
||||
|
||||
#### 信任
|
||||
|
||||
这个文章系列开篇说到区块链为互联网及其上运行的服务增加了急需的信任层。智能合约在任何情况下都不会在执行协议时表现出偏见或主观性,这意味着所涉及的各方对结果完全有约束力,并且可以不附带任何条件地信任该系统。这也意味着,在具有重要价值的传统合同中所需的“可信第三方”,在此处不需要。当事人之间的犯规和监督将成为过去的问题。
|
||||
|
||||
#### 成本效益
|
||||
|
||||
如示例中所强调的,使用智能合约需要最低的成本。企业通常有专门从事使其交易合法并遵守法规的行政人员。如果交易涉及多方,则重复工作是不可避免的。智能合约基本上使前者无关紧要,并且消除了重复,因为双方可以同时完成尽职调查。
|
||||
|
||||
### 智能合约的应用
|
||||
|
||||
基本上,如果两个或多个参与方使用共同的区块链平台,并就一组原则或业务逻辑达成一致,他们可以一起在区块链上创建一个智能合约,并且在没有人为干预的情况下执行。没有人可以篡改所设置的条件,如果原始代码允许,任何更改都会加上时间戳并带有编辑者的指纹,从而增加了问责制。想象一下,在更大的企业级规模上出现类似的情况,你就会明白智能合约的能力是什么,实际上从 2016 年开始的 **Capgemini 研究** 发现智能合约实际上可能是**“未来几年的”** [^8] 商业主流。商业的应用涉及保险、金融市场、物联网、贷款、身份管理系统、托管账户、雇佣合同以及专利和版税合同等用途。像以太坊这样的区块链平台,是一个设计时就考虑了智能合约的系统,它允许个人私人用户免费使用智能合约。
|
||||
|
||||
通过对处理智能合约的公司的探讨,本系列的下一篇文章中将更全面地概述智能合约在当前技术问题上的应用。
|
||||
|
||||
### 那么,它有什么缺点呢?
|
||||
|
||||
这并不是说对智能合约的使用没有任何顾虑。这种担忧实际上也减缓了这方面的发展。所有区块链的防篡改性质实质上使得,如果所涉及的各方需要在没有重大改革或法律追索的情况下,几乎不可能修改或添加现有条款的新条款。
|
||||
|
||||
其次,即使公有链上的活动是开放的,所有人都可以看到和观察。交易中涉及的各方的个人身份并不总是已知的。这种匿名性造成在任何一方违约的情况下法律有罪不罚的问题,特别是因为现行法律和立法者并不完全适应现代技术。
|
||||
|
||||
第三,区块链和智能合约在很多方面仍然存在安全缺陷,因为对其所以涉及的技术仍处于发展的初期阶段。 对代码和平台的这种缺乏经验最终导致了 2016 年的 DAO 事件。
|
||||
|
||||
所有这些都可能导致企业或公司在需要调整区块链以供其使用时需要大量的初始投资。然而,这些是最初的一次性投资,并且随之而来的是潜在的节约,这才是人们感兴趣的。
|
||||
|
||||
### 结论
|
||||
|
||||
目前的法律框架并没有真正支持一个全面的智能合约的社会,并且由于显然的原因,在不久的将来也不会支持。一个解决方案是选择**“混合”合约**,它将传统的法律文本和文件与在为此目的设计的区块链上运行的智能合约代码相结合。然而,即使是混合合约仍然很大程度上尚未得到探索,因为需要创新的立法机构才能实现这些合约。这里简要提到的应用以及更多内容将在[本系列的下一篇文章][6]中详细探讨。
|
||||
|
||||
[^1]: S. C. A. Chamber of Digital Commerce, “Smart contracts – Is the law ready,” no. September, 2018.
|
||||
[^2]: S. C. A. Chamber of Digital Commerce, “Smart contracts – Is the law ready,” no. September, 2018.
|
||||
[^4]: Cardozo Blockchain Project, “‘Smart Contracts’ & Legal Enforceability,” vol. 2, p. 28, 2018.
|
||||
[^6]: F. Idelberger, G. Governatori, R. Riveret, and G. Sartor, “Evaluation of Logic-Based Smart Contracts for Blockchain Systems,” 2016, pp. 167–183.
|
||||
[^8]: B. Cant et al., “Smart Contracts in Financial Services : Getting from Hype to Reality,” Capgemini Consult., pp. 1–24, 2016.
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/blockchain-2-0-explaining-smart-contracts-and-its-types/
|
||||
|
||||
作者:[ostechnix][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/editor/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ostechnix.com/wp-content/uploads/2019/03/smart-contracts-720x340.png
|
||||
[2]: https://linux.cn/article-10914-1.html
|
||||
[3]: https://linux.cn/article-10650-1.html
|
||||
[4]: http://www.fon.hum.uva.nl/rob/Courses/InformationInSpeech/CDROM/Literature/LOTwinterschool2006/szabo.best.vwh.net/smart_contracts_2.html
|
||||
[5]: https://etherparty.com/
|
||||
[6]: https://www.ostechnix.com/blockchain-2-0-ongoing-projects-the-state-of-smart-contracts-now/
|
||||
[7]: http://www.legal-glossary.org/
|
||||
[8]: https://futurism.com/the-dao-heist-undone-97-of-eth-holders-vote-for-the-hard-fork/
|
||||
[9]: https://www.everestgrp.com/2016-10-types-smart-contracts-based-applications-market-insights-36573.html/
|
@ -0,0 +1,127 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (tomjlw)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10939-1.html)
|
||||
[#]: subject: (How to build a mobile particulate matter sensor with a Raspberry Pi)
|
||||
[#]: via: (https://opensource.com/article/19/3/mobile-particulate-matter-sensor)
|
||||
[#]: author: (Stephan Tetzel https://opensource.com/users/stephan)
|
||||
|
||||
如何用树莓派搭建一个颗粒物传感器
|
||||
======
|
||||
|
||||
> 用树莓派、一个廉价的传感器和一个便宜的屏幕监测空气质量。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/05/005121bbveeavwgyc1i1gk.jpg)
|
||||
|
||||
大约一年前,我写了一篇关于如何使用树莓派和廉价传感器测量[空气质量][2]的文章。我们这几年已在学校里和私下使用了这个项目。然而它有一个缺点:由于它基于无线/有线网,因此它不是便携的。如果你的树莓派、你的智能手机和电脑不在同一个网络的话,你甚至都不能访问传感器测量的数据。
|
||||
|
||||
为了弥补这一缺陷,我们给树莓派添加了一块小屏幕,这样我们就可以直接从该设备上读取数据。以下是我们如何为我们的移动细颗粒物传感器搭建并配置好屏幕。
|
||||
|
||||
### 为树莓派搭建好屏幕
|
||||
|
||||
在[亚马逊][3]、阿里巴巴以及其它来源有许多可以买到的树莓派屏幕,从 ePaper 屏幕到可触控 LCD。我们选择了一个便宜的带触控功能且分辨率为 320*480 像素的[3.5英寸 LCD][3],可以直接插进树莓派的 GPIO 引脚。3.5 英寸屏幕和树莓派几乎一样大,这一点不错。
|
||||
|
||||
当你第一次启动屏幕打开树莓派的时候,会因为缺少驱动屏幕会保持白屏。你得首先为屏幕安装[合适的驱动][5]。通过 SSH 登入并执行以下命令:
|
||||
|
||||
```
|
||||
$ rm -rf LCD-show
|
||||
$ git clone <https://github.com/goodtft/LCD-show.git>
|
||||
$ chmod -R 755 LCD-show
|
||||
$ cd LCD-show/
|
||||
```
|
||||
|
||||
为你的屏幕执行合适的命令以安装驱动。例如这是给我们 MPI3501 型屏幕的命令:
|
||||
|
||||
```
|
||||
$ sudo ./LCD35-show
|
||||
```
|
||||
|
||||
这行命令会安装合适的驱动并重启树莓派。
|
||||
|
||||
### 安装 PIXEL 桌面并设置自动启动
|
||||
|
||||
以下是我们想要我们项目能够做到的事情:如果树莓派启动,我们想要展现一个有我们空气质量测量数据的网站。
|
||||
|
||||
首先,安装树莓派的[PIXEL 桌面环境][6]:
|
||||
|
||||
```
|
||||
$ sudo apt install raspberrypi-ui-mods
|
||||
```
|
||||
|
||||
然后安装 Chromium 浏览器以显示网站:
|
||||
|
||||
```
|
||||
$ sudo apt install chromium-browser
|
||||
```
|
||||
|
||||
需要自动登录以使测量数据在启动后直接显示;否则你将只会看到登录界面。然而树莓派用户并没有默认设置好自动登录。你可以用 `raspi-config` 工具设置自动登录:
|
||||
|
||||
```
|
||||
$ sudo raspi-config
|
||||
```
|
||||
|
||||
在菜单中,选择:“3 Boot Options → B1 Desktop / CLI → B4 Desktop Autologin”。
|
||||
|
||||
在启动后用 Chromium 打开我们的网站这块少了一步。创建文件夹 `/home/pi/.config/lxsession/LXDE-pi/`:
|
||||
|
||||
```
|
||||
$ mkdir -p /home/pi/config/lxsession/LXDE-pi/
|
||||
```
|
||||
|
||||
然后在该文件夹里创建 `autostart` 文件:
|
||||
|
||||
```
|
||||
$ nano /home/pi/.config/lxsession/LXDE-pi/autostart
|
||||
```
|
||||
|
||||
并粘贴以下代码:
|
||||
|
||||
```
|
||||
#@unclutter
|
||||
@xset s off
|
||||
@xset -dpms
|
||||
@xset s noblank
|
||||
|
||||
# Open Chromium in Full Screen Mode
|
||||
@chromium-browser --incognito --kiosk <http://localhost>
|
||||
```
|
||||
|
||||
如果你想要隐藏鼠标指针,你得安装 `unclutter` 包并移除 `autostart` 文件开头的注释。
|
||||
|
||||
```
|
||||
$ sudo apt install unclutter
|
||||
```
|
||||
|
||||
![移动颗粒物传感器][7]
|
||||
|
||||
我对去年的代码做了些小修改。因此如果你之前搭建过空气质量项目,确保用[原文章][2]中的指导为 AQI 网站重新下载脚本和文件。
|
||||
|
||||
通过添加触摸屏,你现在拥有了一个便携的颗粒物传感器!我们在学校用它来检查教室里的空气质量或者进行比较测量。使用这种配置,你无需再依赖网络连接或 WLAN。你可以在任何地方使用这个小型测量站——你甚至可以使用移动电源以摆脱电网。
|
||||
|
||||
* * *
|
||||
|
||||
这篇文章原来在<ruby>[开源学校解决方案][8]<rt>Open Scool Solutions</rt></ruby>上发表,获得许可重新发布。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/3/mobile-particulate-matter-sensor
|
||||
|
||||
作者:[Stephan Tetzel][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[tomjlw](https://github.com/tomjlw)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/stephan
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/talk_chat_team_mobile_desktop.png?itok=d7sRtKfQ (Team communication, chat)
|
||||
[2]: https://linux.cn/article-9620-1.html
|
||||
[3]: https://www.amazon.com/gp/search/ref=as_li_qf_sp_sr_tl?ie=UTF8&tag=openschoolsol-20&keywords=lcd%20raspberry&index=aps&camp=1789&creative=9325&linkCode=ur2&linkId=51d6d7676e10d6c7db203c4a8b3b529a
|
||||
[4]: https://amzn.to/2CcvgpC
|
||||
[5]: https://github.com/goodtft/LCD-show
|
||||
[6]: https://linux.cn/article-8459-1.html
|
||||
[7]: https://opensource.com/sites/default/files/uploads/mobile-aqi-sensor.jpg (Mobile particulate matter sensor)
|
||||
[8]: https://openschoolsolutions.org/mobile-particulate-matter-sensor/
|
||||
|
56
published/20190409 5 Linux rookie mistakes.md
Normal file
56
published/20190409 5 Linux rookie mistakes.md
Normal file
@ -0,0 +1,56 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10952-1.html)
|
||||
[#]: subject: (5 Linux rookie mistakes)
|
||||
[#]: via: (https://opensource.com/article/19/4/linux-rookie-mistakes)
|
||||
[#]: author: (Jen Wike Huger https://opensource.com/users/jen-wike/users/bcotton/users/petercheer/users/greg-p/users/greg-p)
|
||||
|
||||
5 个 Linux 新手会犯的失误
|
||||
======
|
||||
|
||||
> Linux 爱好者们分享了他们犯下的一些最大错误。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/09/103635akfkghwh5mp58g68.jpg)
|
||||
|
||||
终身学习是明智的 —— 它可以让你的思维敏捷,让你在就业市场上更具竞争力。但是有些技能比其他技能更难学,尤其是那些小菜鸟错误,当你尝试修复它们时可能会花费你很多时间,给你带来很大困扰。
|
||||
|
||||
以学习 [Linux][2] 为例。如果你习惯于在 Windows 或 MacOS 图形界面中工作,那么转移到 Linux,要将不熟悉的命令输入到终端中,可能会有很大的学习曲线。但是,其回报是值得的,因为已经有数以百万计的人们已经证明了这一点。
|
||||
|
||||
也就是说,这趟学习之旅并不是一帆风顺的。我们让一些 Linux 爱好者回想了一下他们刚开始使用 Linux 的时候,并告诉我们他们犯下的最大错误。
|
||||
|
||||
“不要进入[任何类型的命令行界面(CLI)工作]时就期望命令会以合理或一致的方式工作,因为这可能会导致你感到挫折。这不是因为设计选择不当 —— 虽然当你在键盘上敲击时就像在敲在你的脑袋上一样 —— 而是反映了这些系统是历经了几代的软件和操作系统的发展而陆续添加完成的事实。顺其自然,写下或记住你需要的命令,并且(尽量不要)在[事情不是你所期望的][3]时感到沮丧。” —— [Gina Likins] [4]
|
||||
|
||||
“尽可能简单地复制和粘贴命令以使事情顺利进行,首先阅读命令,至少对将要执行的操作有一个大致的了解,特别是如果有管道命令时,如果有多个管道更要特别注意。有很多破坏性的命令看起来无害 —— 直到你意识到它们能做什么(例如 `rm`、`dd`),而你不会想要意外破坏什么东西(别问我怎么知道)。” —— [Katie McLaughlin] [5]
|
||||
|
||||
“在我的 Linux 之旅的早期,我并不知道我所处在文件系统中的位置的重要性。我正在删除一些我认为是我的主目录的文件,我输入了 `sudo rm -rf *`,然后就删除了我系统上的所有启动文件。现在,我经常使用 `pwd` 来确保我在发出这样的命令之前确认我在哪里。幸运的是,我能够使用 USB 驱动器启动被搞坏的笔记本电脑并恢复我的文件。” —— [Don Watkins] [6]
|
||||
|
||||
“不要因为你认为‘权限很难理解’而你希望应用程序可以访问某些内容时就将整个文件系统的权限重置为 [777][7]。”—— [Matthew Helmke] [8]
|
||||
|
||||
“我从我的系统中删除一个软件包,而我没有检查它依赖的其他软件包。我只是让它删除它想删除要的东西,最终导致我的一些重要程序崩溃并变得不可用。” —— [Kedar Vijay Kulkarni] [9]
|
||||
|
||||
你在学习使用 Linux 时犯过什么错误?请在评论中分享。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/linux-rookie-mistakes
|
||||
|
||||
作者:[Jen Wike Huger][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/jen-wike/users/bcotton/users/petercheer/users/greg-p/users/greg-p
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/mistake_bug_fix_find_error.png?itok=PZaz3dga (magnifying glass on computer screen, finding a bug in the code)
|
||||
[2]: https://opensource.com/resources/linux
|
||||
[3]: https://lintqueen.com/2017/07/02/learning-while-frustrated/
|
||||
[4]: https://opensource.com/users/lintqueen
|
||||
[5]: https://opensource.com/users/glasnt
|
||||
[6]: https://opensource.com/users/don-watkins
|
||||
[7]: https://www.maketecheasier.com/file-permissions-what-does-chmod-777-means/
|
||||
[8]: https://twitter.com/matthewhelmke
|
||||
[9]: https://opensource.com/users/kkulkarn
|
108
published/20190409 5 open source mobile apps.md
Normal file
108
published/20190409 5 open source mobile apps.md
Normal file
@ -0,0 +1,108 @@
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: "fuzheng1998"
|
||||
[#]: reviewer: "wxy"
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-10931-1.html"
|
||||
[#]: subject: "5 open source mobile apps"
|
||||
[#]: via: "https://opensource.com/article/19/4/mobile-apps"
|
||||
[#]: author: "Chris Hermansen https://opensource.com/users/clhermansen/users/bcotton/users/clhermansen/users/bcotton/users/clhermansen"
|
||||
|
||||
5 个可以满足你的生产力、沟通和娱乐需求的开源手机应用
|
||||
======
|
||||
|
||||
> 你可以依靠这些应用来满足你的生产力、沟通和娱乐需求。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/03/001949brnq19j5qeqn3onv.jpg)
|
||||
|
||||
像世界上大多数人一样,我的手似乎就没有离开过手机。多亏了我从 Google Play 和 F-Droid 安装的开源移动应用程序,让我的 Android 设备好像提供了无限的沟通、生产力和娱乐服务一样。
|
||||
|
||||
在我的手机上的许多开源应用程序中,当想听音乐、与朋友/家人和同事联系、或者在旅途中完成工作时,以下五个是我一直使用的。
|
||||
|
||||
### MPDroid
|
||||
|
||||
一个音乐播放器进程 (MPD)的 Android 控制器。
|
||||
|
||||
![MPDroid][2]
|
||||
|
||||
MPD 是将音乐从小型音乐服务器电脑传输到大型的黑色立体声音箱的好方法。它直连 ALSA,因此可以通过 ALSA 硬件接口与数模转换器(DAC)对话,它可以通过我的网络进行控制——但是用什么东西控制呢?好吧,事实证明 MPDroid 是一个很棒的 MPD 控制器。它可以管理我的音乐数据库,显示专辑封面,处理播放列表,并支持互联网广播。而且它是开源的,所以如果某些东西不好用的话……
|
||||
|
||||
MPDroid 可在 [Google Play][4] 和 [F-Droid][5] 上找到。
|
||||
|
||||
### RadioDroid
|
||||
|
||||
一台能单独使用及与 Chromecast 搭配使用的 Android 网络收音机。
|
||||
|
||||
![RadioDroid][6]
|
||||
|
||||
RadioDroid 是一个网络收音机,而 MPDroid 则管理我音乐的数据库;从本质上讲,RadioDroid 是 [Internet-Radio.com][7] 的一个前端。此外,通过将耳机插入 Android 设备,通过耳机插孔或 USB 将 Android 设备直接连接到立体声系统,或通过兼容设备使用其 Chromecast 功能,可以享受 RadioDroid。这是一个查看芬兰天气情况,听取排名前 40 的西班牙语音乐,或收到到最新新闻消息的好方法。
|
||||
|
||||
RadioDroid 可在 [Google Play][8] 和 [F-Droid][9] 上找到。
|
||||
|
||||
### Signal
|
||||
|
||||
一个支持 Android、iOS,还有桌面系统的安全即时消息客户端。
|
||||
|
||||
![Signal][10]
|
||||
|
||||
如果你喜欢 WhatsApp,但是因为它与 Facebook [日益密切][11]的关系而感到困扰,那么 Signal 应该是你的下一个产品。Signal 的唯一问题是说服你的朋友们最好用 Signal 取代 WhatsApp。但除此之外,它有一个与 WhatsApp 类似的界面;很棒的语音和视频通话;很好的加密;恰到好处的匿名;并且它受到了一个不打算通过使用软件来获利的基金会的支持。为什么不喜欢它呢?
|
||||
|
||||
Signal 可用于 [Android][12]、[iOS][13] 和 [桌面][14]。
|
||||
|
||||
### ConnectBot
|
||||
|
||||
Android SSH 客户端。
|
||||
|
||||
![ConnectBot][15]
|
||||
|
||||
有时我离电脑很远,但我需要登录服务器才能办事。[ConnectBot][16] 是将 SSH 会话搬到手机上的绝佳解决方案。
|
||||
|
||||
ConnectBot 可在 [Google Play][17] 上找到。
|
||||
|
||||
### Termux
|
||||
|
||||
有多种熟悉的功能的安卓终端模拟器。
|
||||
|
||||
![Termux][18]
|
||||
|
||||
你是否需要在手机上运行 `awk` 脚本?[Termux][19] 是个解决方案。如果你需要做终端类的工作,而且你不想一直保持与远程计算机的 SSH 连接,请使用 ConnectBot 将文件放到手机上,然后退出会话,在 Termux 中执行你的操作,用 ConnectBot 发回结果。
|
||||
|
||||
Termux 可在 [Google Play][20] 和 [F-Droid][21] 上找到。
|
||||
|
||||
* * *
|
||||
|
||||
你最喜欢用于工作或娱乐的开源移动应用是什么呢?请在评论中分享它们。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/mobile-apps
|
||||
|
||||
作者:[Chris Hermansen][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[fuzheng1998](https://github.com/fuzheng1998)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/clhermansen/users/bcotton/users/clhermansen/users/bcotton/users/clhermansen
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/rh_003588_01_rd3os.combacktoschoolserieshe_rh_041x_0.png?itok=tfg6_I78
|
||||
[2]: https://opensource.com/sites/default/files/uploads/mpdroid.jpg "MPDroid"
|
||||
[3]: https://opensource.com/article/17/4/fun-new-gadget
|
||||
[4]: https://play.google.com/store/apps/details?id=com.namelessdev.mpdroid&hl=en_US
|
||||
[5]: https://f-droid.org/en/packages/com.namelessdev.mpdroid/
|
||||
[6]: https://opensource.com/sites/default/files/uploads/radiodroid.png "RadioDroid"
|
||||
[7]: https://www.internet-radio.com/
|
||||
[8]: https://play.google.com/store/apps/details?id=net.programmierecke.radiodroid2
|
||||
[9]: https://f-droid.org/en/packages/net.programmierecke.radiodroid2/
|
||||
[10]: https://opensource.com/sites/default/files/uploads/signal.png "Signal"
|
||||
[11]: https://opensource.com/article/19/3/open-messenger-client
|
||||
[12]: https://play.google.com/store/apps/details?id=org.thoughtcrime.securesms
|
||||
[13]: https://itunes.apple.com/us/app/signal-private-messenger/id874139669?mt=8
|
||||
[14]: https://signal.org/download/
|
||||
[15]: https://opensource.com/sites/default/files/uploads/connectbot.png "ConnectBot"
|
||||
[16]: https://connectbot.org/
|
||||
[17]: https://play.google.com/store/apps/details?id=org.connectbot
|
||||
[18]: https://opensource.com/sites/default/files/uploads/termux.jpg "Termux"
|
||||
[19]: https://termux.com/
|
||||
[20]: https://play.google.com/store/apps/details?id=com.termux
|
||||
[21]: https://f-droid.org/packages/com.termux/
|
@ -0,0 +1,98 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10957-1.html)
|
||||
[#]: subject: (How we built a Linux desktop app with Electron)
|
||||
[#]: via: (https://opensource.com/article/19/4/linux-desktop-electron)
|
||||
[#]: author: (Nils Ganther https://opensource.com/users/nils-ganther)
|
||||
|
||||
我们是如何使用 Electron 构建 Linux 桌面应用程序的
|
||||
======
|
||||
|
||||
> 这是借助 Electron 框架,构建一个在 Linux 桌面上原生运行的开源电子邮件服务的故事。
|
||||
|
||||
![document sending](https://img.linux.net.cn/data/attachment/album/201906/10/123114abz0lvbllktkulx7.jpg)
|
||||
|
||||
[Tutanota][2] 是一种安全的开源电子邮件服务,它可通过浏览器使用,也有 iOS 和 Android 应用。其客户端代码在 GPLv3 下发布,Android 应用程序可在 [F-Droid][3] 上找到,以便每个人都可以使用完全与 Google 无关的版本。
|
||||
|
||||
由于 Tutanota 关注开源和 Linux 客户端开发,因此我们希望为 Linux 和其他平台发布一个桌面应用程序。作为一个小团队,我们很快就排除了为 Linux、Windows 和 MacOS 构建原生应用程序的可能性,并决定使用 [Electron][4] 来构建我们的应用程序。
|
||||
|
||||
对于任何想要快速交付视觉一致的跨平台应用程序的人来说,Electron 是最适合的选择,尤其是如果你已经有一个 Web 应用程序,想要从浏览器 API 的束缚中摆脱出来时。Tutanota 就是这样一个案例。
|
||||
|
||||
Tutanota 基于 [SystemJS][5] 和 [Mithril][6],旨在为每个人提供简单、安全的电子邮件通信。 因此,它必须提供很多用户期望从电子邮件客户端获得的标准功能。
|
||||
|
||||
由于采用了现代 API 和标准,其中一些功能(如基本的推送通知、搜索文本和联系人以及支持双因素身份验证)很容易在浏览器中提供。其它功能(例如自动备份或无需我们的服务器中转的 IMAP 支持)需要对系统资源的限制性访问,而这正是 Electron 框架提供的功能。
|
||||
|
||||
虽然有人批评 Electron “只是一个基本的包装”,但它有明显的好处:
|
||||
|
||||
* Electron 可以使你能够快速地为 Linux、Windows 和 MacOS 桌面构造 Web 应用。事实上,大多数 Linux 桌面应用都是使用 Electron 构建的。
|
||||
* Electron 可以轻松地将桌面客户端与 Web 应用程序达到同样的功能水准。
|
||||
* 发布桌面应用程序后,你可以自由使用开发功能添加桌面端特定的功能,从而增强可用性和安全性。
|
||||
* 最后但同样重要的是,这是让应用程序具备原生的感觉、融入用户系统,而同时保持其识别度的好方法。
|
||||
|
||||
### 满足用户的需求
|
||||
|
||||
Tutanota 不依靠于大笔的投资资金,而是依靠社区驱动的项目。基于越来越多的用户升级到我们的免费服务的付费计划,我们有机地发展我们的团队。倾听用户的需求不仅对我们很重要,而且对我们的成功至关重要。
|
||||
|
||||
提供桌面客户端是 Tutanota 用户[最想要的功能][7],我们感到自豪的是,我们现在可以为所有用户提供免费的桌面客户端测试版。(我们还实现了另一个高度要求的功能 —— [搜索加密数据][8] —— 但这是另一个主题了。)
|
||||
|
||||
我们喜欢为用户提供签名版本的 Tutanota 并支持浏览器中无法实现的功能,例如通过后台进程推送通知。 现在,我们计划添加更多特定于桌面的功能,例如 IMAP 支持(而不依赖于我们的服务器充当代理),自动备份和离线可用性。
|
||||
|
||||
我们选择 Electron 是因为它的 Chromium 和 Node.js 的组合最适合我们的小型开发团队,因为它只需要对我们的 Web 应用程序进行最小的更改。在我们开始使用时,可以将浏览器 API 用于所有功能特别有用,随着我们的进展,慢慢地用更多原生版本替换这些组件。这种方法对附件下载和通知特别方便。
|
||||
|
||||
### 调整安全性
|
||||
|
||||
我们知道有些人关注 Electron 的安全问题,但我们发现 Electron 在 Web 应用程序中微调访问的选项非常令人满意。你可以使用 Electron 的[安全文档][9]和 Luca Carettoni 的[Electron 安全清单][10]等资源,来帮助防止 Web 应用程序中不受信任的内容发生灾难性事故。
|
||||
|
||||
### 实现特定功能
|
||||
|
||||
Tutanota Web 客户端从一开始就构建了一个用于进程间通信的可靠协议。我们利用 Web 线程在加密和请求数据时保持用户界面(UI)响应性。当我们开始实现我们的移动应用时,这就派上用场,这些应用程序使用相同的协议在原生部分和 Web 视图之间进行通信。
|
||||
|
||||
这就是为什么当我们开始构建桌面客户端时,很多用于本机推送通知、打开邮箱和使用文件系统的部分等已经存在,因此只需要实现原生端(Node.js)。
|
||||
|
||||
另一个便利是我们的构建过程使用 [Babel 转译器][11],它允许我们以现代 ES6 JavaScript 编写整个代码库,并在不同环境之间混合和匹配功能模块。这使我们能够快速调整基于 Electron 的桌面应用程序的代码。但是,我们也遇到了一些挑战。
|
||||
|
||||
### 克服挑战
|
||||
|
||||
虽然 Electron 允许我们很容易地与不同平台的桌面环境集成,但你不能低估投入的时间!最后,正是这些小事情占用了比我们预期更多的时间,但对完成桌面客户端项目也至关重要。
|
||||
|
||||
特定于平台的代码导致了大部分阻碍:
|
||||
|
||||
* 例如,窗口管理和托盘仍然在三个平台上以略有不同的方式处理。
|
||||
* 注册 Tutanota 作为默认邮件程序并设置自动启动需要深入 Windows 注册表,同时确保以 [UAC] [12] 兼容的方式提示用户进行管理员访问。
|
||||
* 我们需要使用 Electron 的 API 作为快捷方式和菜单,以提供复制、粘贴、撤消和重做等标准功能。
|
||||
|
||||
由于用户对不同平台上的应用程序的某些(有时不直接兼容)行为的期望,此过程有点复杂。使三个版本感觉像原生的需要一些迭代,甚至需要对 Web 应用程序进行一些适度的补充,以提供类似于浏览器中的文本搜索的功能。
|
||||
|
||||
### 总结
|
||||
|
||||
我们在 Electron 方面的经验基本上是积极的,我们在不到四个月的时间内完成了该项目。尽管有一些相当耗时的功能,但我们感到惊讶的是,我们可以轻松地为 Linux 提供一个测试版的 [Tutanota 桌面客户端][13]。如果你有兴趣,可以深入了解 [GitHub][14] 上的源代码。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/linux-desktop-electron
|
||||
|
||||
作者:[Nils Ganther][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/nils-ganther
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/email_paper_envelope_document.png?itok=uPj_kouJ (document sending)
|
||||
[2]: https://tutanota.com/
|
||||
[3]: https://f-droid.org/en/packages/de.tutao.tutanota/
|
||||
[4]: https://electronjs.org/
|
||||
[5]: https://github.com/systemjs/systemjs
|
||||
[6]: https://mithril.js.org/
|
||||
[7]: https://tutanota.uservoice.com/forums/237921-general/filters/top?status_id=1177482
|
||||
[8]: https://tutanota.com/blog/posts/first-search-encrypted-data/
|
||||
[9]: https://electronjs.org/docs/tutorial/security
|
||||
[10]: https://www.blackhat.com/docs/us-17/thursday/us-17-Carettoni-Electronegativity-A-Study-Of-Electron-Security-wp.pdf
|
||||
[11]: https://babeljs.io/
|
||||
[12]: https://en.wikipedia.org/wiki/User_Account_Control
|
||||
[13]: https://tutanota.com/blog/posts/desktop-clients/
|
||||
[14]: https://www.github.com/tutao/tutanota
|
135
published/20190411 Be your own certificate authority.md
Normal file
135
published/20190411 Be your own certificate authority.md
Normal file
@ -0,0 +1,135 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10921-1.html)
|
||||
[#]: subject: (Be your own certificate authority)
|
||||
[#]: via: (https://opensource.com/article/19/4/certificate-authority)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshez/users/elenajon123)
|
||||
|
||||
|
||||
自己成为一个证书颁发机构(CA)
|
||||
======
|
||||
|
||||
> 为你的微服务架构或者集成测试创建一个简单的内部 CA。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201905/31/091023sg9s0ss11rsoseqg.jpg)
|
||||
|
||||
传输层安全([TLS][2])模型(有时也称它的旧名称 SSL)基于<ruby>[证书颁发机构][3]<rt>certificate authoritie</rt></ruby>(CA)的概念。这些机构受到浏览器和操作系统的信任,从而*签名*服务器的的证书以用于验证其所有权。
|
||||
|
||||
但是,对于内部网络,微服务架构或集成测试,有时候*本地 CA*更有用:一个只在内部受信任的 CA,然后签名本地服务器的证书。
|
||||
|
||||
这对集成测试特别有意义。获取证书可能会带来负担,因为这会占用服务器几分钟。但是在代码中使用“忽略证书”可能会被引入到生产环境,从而导致安全灾难。
|
||||
|
||||
CA 证书与常规服务器证书没有太大区别。重要的是它被本地代码信任。例如,在 Python `requests` 库中,可以通过将 `REQUESTS_CA_BUNDLE` 变量设置为包含此证书的目录来完成。
|
||||
|
||||
在为集成测试创建证书的例子中,不需要*长期的*证书:如果你的集成测试需要超过一天,那么你应该已经测试失败了。
|
||||
|
||||
因此,计算**昨天**和**明天**作为有效期间隔:
|
||||
|
||||
```
|
||||
>>> import datetime
|
||||
>>> one_day = datetime.timedelta(days=1)
|
||||
>>> today = datetime.date.today()
|
||||
>>> yesterday = today - one_day
|
||||
>>> tomorrow = today - one_day
|
||||
```
|
||||
|
||||
现在你已准备好创建一个简单的 CA 证书。你需要生成私钥,创建公钥,设置 CA 的“参数”,然后自签名证书:CA 证书*总是*自签名的。最后,导出证书文件以及私钥文件。
|
||||
|
||||
```
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography import x509
|
||||
from cryptography.x509.oid import NameOID
|
||||
|
||||
|
||||
private_key = rsa.generate_private_key(
|
||||
public_exponent=65537,
|
||||
key_size=2048,
|
||||
backend=default_backend()
|
||||
)
|
||||
public_key = private_key.public_key()
|
||||
builder = x509.CertificateBuilder()
|
||||
builder = builder.subject_name(x509.Name([
|
||||
x509.NameAttribute(NameOID.COMMON_NAME, 'Simple Test CA'),
|
||||
]))
|
||||
builder = builder.issuer_name(x509.Name([
|
||||
x509.NameAttribute(NameOID.COMMON_NAME, 'Simple Test CA'),
|
||||
]))
|
||||
builder = builder.not_valid_before(yesterday)
|
||||
builder = builder.not_valid_after(tomorrow)
|
||||
builder = builder.serial_number(x509.random_serial_number())
|
||||
builder = builder.public_key(public_key)
|
||||
builder = builder.add_extension(
|
||||
x509.BasicConstraints(ca=True, path_length=None),
|
||||
critical=True)
|
||||
certificate = builder.sign(
|
||||
private_key=private_key, algorithm=hashes.SHA256(),
|
||||
backend=default_backend()
|
||||
)
|
||||
private_bytes = private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||
encryption_algorithm=serialization.NoEncrption())
|
||||
public_bytes = certificate.public_bytes(
|
||||
encoding=serialization.Encoding.PEM)
|
||||
with open("ca.pem", "wb") as fout:
|
||||
fout.write(private_bytes + public_bytes)
|
||||
with open("ca.crt", "wb") as fout:
|
||||
fout.write(public_bytes)
|
||||
```
|
||||
|
||||
通常,真正的 CA 会需要[证书签名请求][4](CSR)来签名证书。但是,当你是自己的 CA 时,你可以制定自己的规则!可以径直签名你想要的内容。
|
||||
|
||||
继续集成测试的例子,你可以创建私钥并立即签名相应的公钥。注意 `COMMON_NAME` 需要是 `https` URL 中的“服务器名称”。如果你已配置名称查询,你需要服务器能响应对 `service.test.local` 的请求。
|
||||
|
||||
```
|
||||
service_private_key = rsa.generate_private_key(
|
||||
public_exponent=65537,
|
||||
key_size=2048,
|
||||
backend=default_backend()
|
||||
)
|
||||
service_public_key = service_private_key.public_key()
|
||||
builder = x509.CertificateBuilder()
|
||||
builder = builder.subject_name(x509.Name([
|
||||
x509.NameAttribute(NameOID.COMMON_NAME, 'service.test.local')
|
||||
]))
|
||||
builder = builder.not_valid_before(yesterday)
|
||||
builder = builder.not_valid_after(tomorrow)
|
||||
builder = builder.public_key(public_key)
|
||||
certificate = builder.sign(
|
||||
private_key=private_key, algorithm=hashes.SHA256(),
|
||||
backend=default_backend()
|
||||
)
|
||||
private_bytes = service_private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||
encryption_algorithm=serialization.NoEncrption())
|
||||
public_bytes = certificate.public_bytes(
|
||||
encoding=serialization.Encoding.PEM)
|
||||
with open("service.pem", "wb") as fout:
|
||||
fout.write(private_bytes + public_bytes)
|
||||
```
|
||||
|
||||
现在 `service.pem` 文件有一个私钥和一个“有效”的证书:它已由本地的 CA 签名。该文件的格式可以给 Nginx、HAProxy 或大多数其他 HTTPS 服务器使用。
|
||||
|
||||
通过将此逻辑用在测试脚本中,只要客户端配置信任该 CA,那么就可以轻松创建看起来真实的 HTTPS 服务器。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/certificate-authority
|
||||
|
||||
作者:[Moshe Zadka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/moshez/users/elenajon123
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/rh_commun_4604_02_mech_connections_rhcz0.5x.png?itok=YPPU4dMj
|
||||
[2]: https://en.wikipedia.org/wiki/Transport_Layer_Security
|
||||
[3]: https://en.wikipedia.org/wiki/Certificate_authority
|
||||
[4]: https://en.wikipedia.org/wiki/Certificate_signing_request
|
@ -0,0 +1,374 @@
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: "FSSlc"
|
||||
[#]: reviewer: "wxy"
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-10930-1.html"
|
||||
[#]: subject: "Inter-process communication in Linux: Sockets and signals"
|
||||
[#]: via: "https://opensource.com/article/19/4/interprocess-communication-linux-networking"
|
||||
[#]: author: "Marty Kalin https://opensource.com/users/mkalindepauledu"
|
||||
|
||||
Linux 下的进程间通信:套接字和信号
|
||||
======
|
||||
|
||||
> 学习在 Linux 中进程是如何与其他进程进行同步的。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/02/234437y6gig4tg4yy94356.jpg)
|
||||
|
||||
本篇是 Linux 下[进程间通信][1](IPC)系列的第三篇同时也是最后一篇文章。[第一篇文章][2]聚焦在通过共享存储(文件和共享内存段)来进行 IPC,[第二篇文章][3]则通过管道(无名的或者命名的)及消息队列来达到相同的目的。这篇文章将目光从高处(套接字)然后到低处(信号)来关注 IPC。代码示例将用力地充实下面的解释细节。
|
||||
|
||||
### 套接字
|
||||
|
||||
正如管道有两种类型(命名和无名)一样,套接字也有两种类型。IPC 套接字(即 Unix 套接字)给予进程在相同设备(主机)上基于通道的通信能力;而网络套接字给予进程运行在不同主机的能力,因此也带来了网络通信的能力。网络套接字需要底层协议的支持,例如 TCP(传输控制协议)或 UDP(用户数据报协议)。
|
||||
|
||||
与之相反,IPC 套接字依赖于本地系统内核的支持来进行通信;特别的,IPC 通信使用一个本地的文件作为套接字地址。尽管这两种套接字的实现有所不同,但在本质上,IPC 套接字和网络套接字的 API 是一致的。接下来的例子将包含网络套接字的内容,但示例服务器和客户端程序可以在相同的机器上运行,因为服务器使用了 `localhost`(127.0.0.1)这个网络地址,该地址表示的是本地机器上的本地机器地址。
|
||||
|
||||
套接字以流的形式(下面将会讨论到)被配置为双向的,并且其控制遵循 C/S(客户端/服务器端)模式:客户端通过尝试连接一个服务器来初始化对话,而服务器端将尝试接受该连接。假如万事顺利,来自客户端的请求和来自服务器端的响应将通过管道进行传输,直到其中任意一方关闭该通道,从而断开这个连接。
|
||||
|
||||
一个迭代服务器(只适用于开发)将一直和连接它的客户端打交道:从最开始服务第一个客户端,然后到这个连接关闭,然后服务第二个客户端,循环往复。这种方式的一个缺点是处理一个特定的客户端可能会挂起,使得其他的客户端一直在后面等待。生产级别的服务器将是并发的,通常使用了多进程或者多线程的混合。例如,我台式机上的 Nginx 网络服务器有一个 4 个<ruby>工人<rt>worker</rt></ruby>的进程池,它们可以并发地处理客户端的请求。在下面的代码示例中,我们将使用迭代服务器,使得我们将要处理的问题保持在一个很小的规模,只关注基本的 API,而不去关心并发的问题。
|
||||
|
||||
最后,随着各种 POSIX 改进的出现,套接字 API 随着时间的推移而发生了显著的变化。当前针对服务器端和客户端的示例代码特意写的比较简单,但是它着重强调了基于流的套接字中连接的双方。下面是关于流控制的一个总结,其中服务器端在一个终端中开启,而客户端在另一个不同的终端中开启:
|
||||
|
||||
* 服务器端等待客户端的连接,对于给定的一个成功连接,它就读取来自客户端的数据。
|
||||
* 为了强调是双方的会话,服务器端会对接收自客户端的数据做回应。这些数据都是 ASCII 字符代码,它们组成了一些书的标题。
|
||||
* 客户端将书的标题写给服务器端的进程,并从服务器端的回应中读取到相同的标题。然后客户端和服务器端都在屏幕上打印出标题。下面是服务器端的输出,客户端的输出也和它完全一样:
|
||||
|
||||
```
|
||||
Listening on port 9876 for clients...
|
||||
War and Peace
|
||||
Pride and Prejudice
|
||||
The Sound and the Fury
|
||||
```
|
||||
|
||||
#### 示例 1. 使用套接字的客户端程序
|
||||
|
||||
```c
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "sock.h"
|
||||
|
||||
void report(const char* msg, int terminate) {
|
||||
perror(msg);
|
||||
if (terminate) exit(-1); /* failure */
|
||||
}
|
||||
|
||||
int main() {
|
||||
int fd = socket(AF_INET, /* network versus AF_LOCAL */
|
||||
SOCK_STREAM, /* reliable, bidirectional: TCP */
|
||||
0); /* system picks underlying protocol */
|
||||
if (fd < 0) report("socket", 1); /* terminate */
|
||||
|
||||
/* bind the server's local address in memory */
|
||||
struct sockaddr_in saddr;
|
||||
memset(&saddr, 0, sizeof(saddr)); /* clear the bytes */
|
||||
saddr.sin_family = AF_INET; /* versus AF_LOCAL */
|
||||
saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* host-to-network endian */
|
||||
saddr.sin_port = htons(PortNumber); /* for listening */
|
||||
|
||||
if (bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
|
||||
report("bind", 1); /* terminate */
|
||||
|
||||
/* listen to the socket */
|
||||
if (listen(fd, MaxConnects) < 0) /* listen for clients, up to MaxConnects */
|
||||
report("listen", 1); /* terminate */
|
||||
|
||||
fprintf(stderr, "Listening on port %i for clients...\n", PortNumber);
|
||||
/* a server traditionally listens indefinitely */
|
||||
while (1) {
|
||||
struct sockaddr_in caddr; /* client address */
|
||||
int len = sizeof(caddr); /* address length could change */
|
||||
|
||||
int client_fd = accept(fd, (struct sockaddr*) &caddr, &len); /* accept blocks */
|
||||
if (client_fd < 0) {
|
||||
report("accept", 0); /* don't terminated, though there's a problem */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read from client */
|
||||
int i;
|
||||
for (i = 0; i < ConversationLen; i++) {
|
||||
char buffer[BuffSize + 1];
|
||||
memset(buffer, '\0', sizeof(buffer));
|
||||
int count = read(client_fd, buffer, sizeof(buffer));
|
||||
if (count > 0) {
|
||||
puts(buffer);
|
||||
write(client_fd, buffer, sizeof(buffer)); /* echo as confirmation */
|
||||
}
|
||||
}
|
||||
close(client_fd); /* break connection */
|
||||
} /* while(1) */
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
上面的服务器端程序执行典型的 4 个步骤来准备回应客户端的请求,然后接受其他的独立请求。这里每一个步骤都以服务器端程序调用的系统函数来命名。
|
||||
|
||||
1. `socket(…)`:为套接字连接获取一个文件描述符
|
||||
2. `bind(…)`:将套接字和服务器主机上的一个地址进行绑定
|
||||
3. `listen(…)`:监听客户端请求
|
||||
4. `accept(…)`:接受一个特定的客户端请求
|
||||
|
||||
上面的 `socket` 调用的完整形式为:
|
||||
|
||||
```
|
||||
int sockfd = socket(AF_INET, /* versus AF_LOCAL */
|
||||
SOCK_STREAM, /* reliable, bidirectional */
|
||||
0); /* system picks protocol (TCP) */
|
||||
```
|
||||
|
||||
第一个参数特别指定了使用的是一个网络套接字,而不是 IPC 套接字。对于第二个参数有多种选项,但 `SOCK_STREAM` 和 `SOCK_DGRAM`(数据报)是最为常用的。基于流的套接字支持可信通道,在这种通道中如果发生了信息的丢失或者更改,都将会被报告。这种通道是双向的,并且从一端到另外一端的有效载荷在大小上可以是任意的。相反的,基于数据报的套接字大多是不可信的,没有方向性,并且需要固定大小的载荷。`socket` 的第三个参数特别指定了协议。对于这里展示的基于流的套接字,只有一种协议选择:TCP,在这里表示的 `0`。因为对 `socket` 的一次成功调用将返回相似的文件描述符,套接字可以被读写,对应的语法和读写一个本地文件是类似的。
|
||||
|
||||
对 `bind` 的调用是最为复杂的,因为它反映出了在套接字 API 方面上的各种改进。我们感兴趣的点是这个调用将一个套接字和服务器端所在机器中的一个内存地址进行绑定。但对 `listen` 的调用就非常直接了:
|
||||
|
||||
```
|
||||
if (listen(fd, MaxConnects) < 0)
|
||||
```
|
||||
|
||||
第一个参数是套接字的文件描述符,第二个参数则指定了在服务器端处理一个拒绝连接错误之前,有多少个客户端连接被允许连接。(在头文件 `sock.h` 中 `MaxConnects` 的值被设置为 `8`。)
|
||||
|
||||
`accept` 调用默认将是一个阻塞等待:服务器端将不做任何事情直到一个客户端尝试连接它,然后进行处理。`accept` 函数返回的值如果是 `-1` 则暗示有错误发生。假如这个调用是成功的,则它将返回另一个文件描述符,这个文件描述符被用来指代另一个可读可写的套接字,它与 `accept` 调用中的第一个参数对应的接收套接字有所不同。服务器端使用这个可读可写的套接字来从客户端读取请求然后写回它的回应。接收套接字只被用于接受客户端的连接。
|
||||
|
||||
在设计上,服务器端可以一直运行下去。当然服务器端可以通过在命令行中使用 `Ctrl+C` 来终止它。
|
||||
|
||||
#### 示例 2. 使用套接字的客户端
|
||||
|
||||
```c
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netdb.h>
|
||||
#include "sock.h"
|
||||
|
||||
const char* books[] = {"War and Peace",
|
||||
"Pride and Prejudice",
|
||||
"The Sound and the Fury"};
|
||||
|
||||
void report(const char* msg, int terminate) {
|
||||
perror(msg);
|
||||
if (terminate) exit(-1); /* failure */
|
||||
}
|
||||
|
||||
int main() {
|
||||
/* fd for the socket */
|
||||
int sockfd = socket(AF_INET, /* versus AF_LOCAL */
|
||||
SOCK_STREAM, /* reliable, bidirectional */
|
||||
0); /* system picks protocol (TCP) */
|
||||
if (sockfd < 0) report("socket", 1); /* terminate */
|
||||
|
||||
/* get the address of the host */
|
||||
struct hostent* hptr = gethostbyname(Host); /* localhost: 127.0.0.1 */
|
||||
if (!hptr) report("gethostbyname", 1); /* is hptr NULL? */
|
||||
if (hptr->h_addrtype != AF_INET) /* versus AF_LOCAL */
|
||||
report("bad address family", 1);
|
||||
|
||||
/* connect to the server: configure server's address 1st */
|
||||
struct sockaddr_in saddr;
|
||||
memset(&saddr, 0, sizeof(saddr));
|
||||
saddr.sin_family = AF_INET;
|
||||
saddr.sin_addr.s_addr =
|
||||
((struct in_addr*) hptr->h_addr_list[0])->s_addr;
|
||||
saddr.sin_port = htons(PortNumber); /* port number in big-endian */
|
||||
|
||||
if (connect(sockfd, (struct sockaddr*) &saddr, sizeof(saddr)) < 0)
|
||||
report("connect", 1);
|
||||
|
||||
/* Write some stuff and read the echoes. */
|
||||
puts("Connect to server, about to write some stuff...");
|
||||
int i;
|
||||
for (i = 0; i < ConversationLen; i++) {
|
||||
if (write(sockfd, books[i], strlen(books[i])) > 0) {
|
||||
/* get confirmation echoed from server and print */
|
||||
char buffer[BuffSize + 1];
|
||||
memset(buffer, '\0', sizeof(buffer));
|
||||
if (read(sockfd, buffer, sizeof(buffer)) > 0)
|
||||
puts(buffer);
|
||||
}
|
||||
}
|
||||
puts("Client done, about to exit...");
|
||||
close(sockfd); /* close the connection */
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
客户端程序的设置代码和服务器端类似。两者主要的区别既不是在于监听也不在于接收,而是连接:
|
||||
|
||||
```
|
||||
if (connect(sockfd, (struct sockaddr*) &saddr, sizeof(saddr)) < 0)
|
||||
```
|
||||
|
||||
对 `connect` 的调用可能因为多种原因而导致失败,例如客户端拥有错误的服务器端地址或者已经有太多的客户端连接上了服务器端。假如 `connect` 操作成功,客户端将在一个 `for` 循环中,写入它的请求然后读取返回的响应。在会话后,服务器端和客户端都将调用 `close` 去关闭这个可读可写套接字,尽管任何一边的关闭操作就足以关闭它们之间的连接。此后客户端可以退出了,但正如前面提到的那样,服务器端可以一直保持开放以处理其他事务。
|
||||
|
||||
从上面的套接字示例中,我们看到了请求信息被回显给客户端,这使得客户端和服务器端之间拥有进行丰富对话的可能性。也许这就是套接字的主要魅力。在现代系统中,客户端应用(例如一个数据库客户端)和服务器端通过套接字进行通信非常常见。正如先前提及的那样,本地 IPC 套接字和网络套接字只在某些实现细节上面有所不同,一般来说,IPC 套接字有着更低的消耗和更好的性能。它们的通信 API 基本是一样的。
|
||||
|
||||
### 信号
|
||||
|
||||
信号会中断一个正在执行的程序,在这种意义下,就是用信号与这个程序进行通信。大多数的信号要么可以被忽略(阻塞)或者被处理(通过特别设计的代码)。`SIGSTOP` (暂停)和 `SIGKILL`(立即停止)是最应该提及的两种信号。这种符号常量有整数类型的值,例如 `SIGKILL` 对应的值为 `9`。
|
||||
|
||||
信号可以在与用户交互的情况下发生。例如,一个用户从命令行中敲了 `Ctrl+C` 来终止一个从命令行中启动的程序;`Ctrl+C` 将产生一个 `SIGTERM` 信号。`SIGTERM` 意即终止,它可以被阻塞或者被处理,而不像 `SIGKILL` 信号那样。一个进程也可以通过信号和另一个进程通信,这样使得信号也可以作为一种 IPC 机制。
|
||||
|
||||
考虑一下一个多进程应用,例如 Nginx 网络服务器是如何被另一个进程优雅地关闭的。`kill` 函数:
|
||||
|
||||
```
|
||||
int kill(pid_t pid, int signum); /* declaration */
|
||||
```
|
||||
|
||||
可以被一个进程用来终止另一个进程或者一组进程。假如 `kill` 函数的第一个参数是大于 `0` 的,那么这个参数将会被认为是目标进程的 `pid`(进程 ID),假如这个参数是 `0`,则这个参数将会被视作信号发送者所属的那组进程。
|
||||
|
||||
`kill` 的第二个参数要么是一个标准的信号数字(例如 `SIGTERM` 或 `SIGKILL`),要么是 `0` ,这将会对信号做一次询问,确认第一个参数中的 `pid` 是否是有效的。这样优雅地关闭一个多进程应用就可以通过向组成该应用的一组进程发送一个终止信号来完成,具体来说就是调用一个 `kill` 函数,使得这个调用的第二个参数是 `SIGTERM` 。(Nginx 主进程可以通过调用 `kill` 函数来终止其他工人进程,然后再停止自己。)就像许多库函数一样,`kill` 函数通过一个简单的可变语法拥有更多的能力和灵活性。
|
||||
|
||||
#### 示例 3. 一个多进程系统的优雅停止
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
void graceful(int signum) {
|
||||
printf("\tChild confirming received signal: %i\n", signum);
|
||||
puts("\tChild about to terminate gracefully...");
|
||||
sleep(1);
|
||||
puts("\tChild terminating now...");
|
||||
_exit(0); /* fast-track notification of parent */
|
||||
}
|
||||
|
||||
void set_handler() {
|
||||
struct sigaction current;
|
||||
sigemptyset(¤t.sa_mask); /* clear the signal set */
|
||||
current.sa_flags = 0; /* enables setting sa_handler, not sa_action */
|
||||
current.sa_handler = graceful; /* specify a handler */
|
||||
sigaction(SIGTERM, ¤t, NULL); /* register the handler */
|
||||
}
|
||||
|
||||
void child_code() {
|
||||
set_handler();
|
||||
|
||||
while (1) { /` loop until interrupted `/
|
||||
sleep(1);
|
||||
puts("\tChild just woke up, but going back to sleep.");
|
||||
}
|
||||
}
|
||||
|
||||
void parent_code(pid_t cpid) {
|
||||
puts("Parent sleeping for a time...");
|
||||
sleep(5);
|
||||
|
||||
/* Try to terminate child. */
|
||||
if (-1 == kill(cpid, SIGTERM)) {
|
||||
perror("kill");
|
||||
exit(-1);
|
||||
}
|
||||
wait(NULL); /` wait for child to terminate `/
|
||||
puts("My child terminated, about to exit myself...");
|
||||
}
|
||||
|
||||
int main() {
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
perror("fork");
|
||||
return -1; /* error */
|
||||
}
|
||||
if (0 == pid)
|
||||
child_code();
|
||||
else
|
||||
parent_code(pid);
|
||||
return 0; /* normal */
|
||||
}
|
||||
```
|
||||
|
||||
上面的停止程序模拟了一个多进程系统的优雅退出,在这个例子中,这个系统由一个父进程和一个子进程组成。这次模拟的工作流程如下:
|
||||
|
||||
* 父进程尝试去 `fork` 一个子进程。假如这个 `fork` 操作成功了,每个进程就执行它自己的代码:子进程就执行函数 `child_code`,而父进程就执行函数 `parent_code`。
|
||||
* 子进程将会进入一个潜在的无限循环,在这个循环中子进程将睡眠一秒,然后打印一个信息,接着再次进入睡眠状态,以此循环往复。来自父进程的一个 `SIGTERM` 信号将引起子进程去执行一个信号处理回调函数 `graceful`。这样这个信号就使得子进程可以跳出循环,然后进行子进程和父进程之间的优雅终止。在终止之前,进程将打印一个信息。
|
||||
* 在 `fork` 一个子进程后,父进程将睡眠 5 秒,使得子进程可以执行一会儿;当然在这个模拟中,子进程大多数时间都在睡眠。然后父进程调用 `SIGTERM` 作为第二个参数的 `kill` 函数,等待子进程的终止,然后自己再终止。
|
||||
|
||||
下面是一次运行的输出:
|
||||
|
||||
```
|
||||
% ./shutdown
|
||||
Parent sleeping for a time...
|
||||
Child just woke up, but going back to sleep.
|
||||
Child just woke up, but going back to sleep.
|
||||
Child just woke up, but going back to sleep.
|
||||
Child just woke up, but going back to sleep.
|
||||
Child confirming received signal: 15 ## SIGTERM is 15
|
||||
Child about to terminate gracefully...
|
||||
Child terminating now...
|
||||
My child terminated, about to exit myself...
|
||||
```
|
||||
|
||||
对于信号的处理,上面的示例使用了 `sigaction` 库函数(POSIX 推荐的用法)而不是传统的 `signal` 函数,`signal` 函数有移植性问题。下面是我们主要关心的代码片段:
|
||||
|
||||
* 假如对 `fork` 的调用成功了,父进程将执行 `parent_code` 函数,而子进程将执行 `child_code` 函数。在给子进程发送信号之前,父进程将会等待 5 秒:
|
||||
|
||||
```
|
||||
puts("Parent sleeping for a time...");
|
||||
sleep(5);
|
||||
if (-1 == kill(cpid, SIGTERM)) {
|
||||
...sleepkillcpidSIGTERM...
|
||||
```
|
||||
|
||||
假如 `kill` 调用成功了,父进程将在子进程终止时做等待,使得子进程不会变成一个僵尸进程。在等待完成后,父进程再退出。
|
||||
|
||||
* `child_code` 函数首先调用 `set_handler` 然后进入它的可能永久睡眠的循环。下面是我们将要查看的 `set_handler` 函数:
|
||||
|
||||
```
|
||||
void set_handler() {
|
||||
struct sigaction current; /* current setup */
|
||||
sigemptyset(¤t.sa_mask); /* clear the signal set */
|
||||
current.sa_flags = 0; /* for setting sa_handler, not sa_action */
|
||||
current.sa_handler = graceful; /* specify a handler */
|
||||
sigaction(SIGTERM, ¤t, NULL); /* register the handler */
|
||||
}
|
||||
```
|
||||
|
||||
上面代码的前三行在做相关的准备。第四个语句将为 `graceful` 设定为句柄,它将在调用 `_exit` 来停止之前打印一些信息。第 5 行和最后一行的语句将通过调用 `sigaction` 来向系统注册上面的句柄。`sigaction` 的第一个参数是 `SIGTERM` ,用作终止;第二个参数是当前的 `sigaction` 设定,而最后的参数(在这个例子中是 `NULL` )可被用来保存前面的 `sigaction` 设定,以备后面的可能使用。
|
||||
|
||||
使用信号来作为 IPC 的确是一个很轻量的方法,但确实值得尝试。通过信号来做 IPC 显然可以被归入 IPC 工具箱中。
|
||||
|
||||
### 这个系列的总结
|
||||
|
||||
在这个系列中,我们通过三篇有关 IPC 的文章,用示例代码介绍了如下机制:
|
||||
|
||||
* 共享文件
|
||||
* 共享内存(通过信号量)
|
||||
* 管道(命名和无名)
|
||||
* 消息队列
|
||||
* 套接字
|
||||
* 信号
|
||||
|
||||
甚至在今天,在以线程为中心的语言,例如 Java、C# 和 Go 等变得越来越流行的情况下,IPC 仍然很受欢迎,因为相比于使用多线程,通过多进程来实现并发有着一个明显的优势:默认情况下,每个进程都有它自己的地址空间,除非使用了基于共享内存的 IPC 机制(为了达到安全的并发,竞争条件在多线程和多进程的时候必须被加上锁),在多进程中可以排除掉基于内存的竞争条件。对于任何一个写过即使是基本的通过共享变量来通信的多线程程序的人来说,他都会知道想要写一个清晰、高效、线程安全的代码是多么具有挑战性。使用单线程的多进程的确是很有吸引力的,这是一个切实可行的方式,使用它可以利用好今天多处理器的机器,而不需要面临基于内存的竞争条件的风险。
|
||||
|
||||
当然,没有一个简单的答案能够回答上述 IPC 机制中的哪一个更好。在编程中每一种 IPC 机制都会涉及到一个取舍问题:是追求简洁,还是追求功能强大。以信号来举例,它是一个相对简单的 IPC 机制,但并不支持多个进程之间的丰富对话。假如确实需要这样的对话,另外的选择可能会更合适一些。带有锁的共享文件则相对直接,但是当要处理大量共享的数据流时,共享文件并不能很高效地工作。管道,甚至是套接字,有着更复杂的 API,可能是更好的选择。让具体的问题去指导我们的选择吧。
|
||||
|
||||
尽管所有的示例代码(可以在[我的网站][4]上获取到)都是使用 C 写的,其他的编程语言也经常提供这些 IPC 机制的轻量包装。这些代码示例都足够短小简单,希望这样能够鼓励你去进行实验。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/interprocess-communication-linux-networking
|
||||
|
||||
作者:[Marty Kalin][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[FSSlc](https://github.com/FSSlc)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/mkalindepauledu
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://en.wikipedia.org/wiki/Inter-process_communication
|
||||
[2]: https://linux.cn/article-10826-1.html
|
||||
[3]: https://linux.cn/article-10845-1.html
|
||||
[4]: http://condor.depaul.edu/mkalin
|
@ -0,0 +1,69 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10926-1.html)
|
||||
[#]: subject: (4 open source apps for plant-based diets)
|
||||
[#]: via: (https://opensource.com/article/19/4/apps-plant-based-diets)
|
||||
[#]: author: (Joshua Allen Holm https://opensource.com/users/holmja)
|
||||
|
||||
4 款“吃草”的开源应用
|
||||
======
|
||||
|
||||
> 这些应用使素食者、纯素食主义者和那些想吃得更健康的杂食者找到可以吃的食物。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/01/193302nompumppxnmnxirz.jpg)
|
||||
|
||||
减少对肉类、乳制品和加工食品的消费对地球来说更好,也对你的健康更有益。改变你的饮食习惯可能很困难,但是一些开源的 Android 应用可以让你吃的更清淡。无论你是参加[无肉星期一][2]、践行 Mark Bittman 的 [6:00 前的素食][3]指南,还是完全切换到<ruby>[植物全食饮食][4]<rt>whole-food, plant-based diet</rt></ruby>(WFPB),这些应用能帮助你找出要吃什么、发现素食和素食友好的餐馆,并轻松地将你的饮食偏好传达给他人,来助你更好地走这条路。所有这些应用都是开源的,可从 [F-Droid 仓库][5]下载。
|
||||
|
||||
### Daily Dozen
|
||||
|
||||
![Daily Dozen app][6]
|
||||
|
||||
[Daily Dozen][7] 提供了医学博士、美国法医学会院士(FACLM) Michael Greger 推荐的项目清单作为健康饮食和生活方式的一部分。Greger 博士建议食用由多种食物组成的基于植物的全食饮食,并坚持日常锻炼。该应用可以让你跟踪你吃的每种食物的份数,你喝了多少份水(或其他获准的饮料,如茶),以及你是否每天锻炼。每类食物都提供食物分量和属于该类别的食物清单。例如,十字花科蔬菜类包括白菜、花椰菜、抱子甘蓝等许多其他建议。
|
||||
|
||||
### Food Restrictions
|
||||
|
||||
![Food Restrictions app][8]
|
||||
|
||||
[Food Restrictions][9] 是一个简单的应用,它可以帮助你将你的饮食限制传达给他人,即使这些人不会说你的语言。用户可以输入七种不同类别的食物限制:鸡肉、牛肉、猪肉、鱼、奶酪、牛奶和辣椒。每种类别都有“我不吃”和“我过敏”选项。“不吃”选项会显示带有红色 X 的图标。“过敏” 选项显示 “X” 和小骷髅图标。可以使用文本而不是图标显示相同的信息,但文本仅提供英语和葡萄牙语。还有一个选项可以显示一条文字信息,说明用户是素食主义者或纯素食主义者,它比选择选项更简洁、更准确地总结了这些饮食限制。纯素食主义者的文本清楚地提到不吃鸡蛋和蜂蜜,这在选择选项中是没有的。但是,就像选择选项方式的文字版本一样,这些句子仅提供英语和葡萄牙语。
|
||||
|
||||
### OpenFoodFacts
|
||||
|
||||
![Open Food Facts app][10]
|
||||
|
||||
购买杂货时避免买入不必要的成分可能令人沮丧,但 [OpenFoodFacts][11] 可以帮助简化流程。该应用可让你扫描产品上的条形码,以获得有关产品成分和是否健康的报告。即使产品符合纯素产品的标准,产品仍然可能非常不健康。拥有成分列表和营养成分可让你在购物时做出明智的选择。此应用的唯一缺点是数据是用户贡献的,因此并非每个产品都可有数据,但如果你想回馈项目,你可以贡献新数据。
|
||||
|
||||
### OpenVegeMap
|
||||
|
||||
![OpenVegeMap app][12]
|
||||
|
||||
使用 [OpenVegeMap][13] 查找你附近的纯素食或素食主义餐厅。此应用可以通过手机的当前位置或者输入地址来搜索。餐厅分类为仅限纯素食者、适合纯素食者,仅限素食主义者,适合素食者,非素食和未知。该应用使用来自 [OpenStreetMap][14] 的数据和用户提供的有关餐馆的信息,因此请务必仔细检查以确保所提供的信息是最新且准确的。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/apps-plant-based-diets
|
||||
|
||||
作者:[Joshua Allen Holm][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/holmja
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/rh_003588_01_rd3os.combacktoschoolserieshe_rh_041x_0.png?itok=tfg6_I78
|
||||
[2]: https://www.meatlessmonday.com/
|
||||
[3]: https://www.amazon.com/dp/0385344740/
|
||||
[4]: https://nutritionstudies.org/whole-food-plant-based-diet-guide/
|
||||
[5]: https://f-droid.org/
|
||||
[6]: https://opensource.com/sites/default/files/uploads/daily_dozen.png (Daily Dozen app)
|
||||
[7]: https://f-droid.org/en/packages/org.nutritionfacts.dailydozen/
|
||||
[8]: https://opensource.com/sites/default/files/uploads/food_restrictions.png (Food Restrictions app)
|
||||
[9]: https://f-droid.org/en/packages/br.com.frs.foodrestrictions/
|
||||
[10]: https://opensource.com/sites/default/files/uploads/openfoodfacts.png (Open Food Facts app)
|
||||
[11]: https://f-droid.org/en/packages/openfoodfacts.github.scrachx.openfood/
|
||||
[12]: https://opensource.com/sites/default/files/uploads/openvegmap.png (OpenVegeMap app)
|
||||
[13]: https://f-droid.org/en/packages/pro.rudloff.openvegemap/
|
||||
[14]: https://www.openstreetmap.org/
|
@ -0,0 +1,137 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (Modrisco)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10968-1.html)
|
||||
[#]: subject: (Epic Games Store is Now Available on Linux Thanks to Lutris)
|
||||
[#]: via: (https://itsfoss.com/epic-games-lutris-linux/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
有了 Lutris,Linux 现在也可以启动 Epic 游戏商城
|
||||
======
|
||||
|
||||
> 开源游戏平台 Lutris 现在使你能够在 Linux 上使用 Epic 游戏商城。我们使用 Ubuntu 19.04 版本进行了测试,以下是我们的使用体验。
|
||||
|
||||
[在 Linux 上玩游戏][1] 正变得越来越容易。Steam [正在开发中的][3] 特性可以帮助你实现 [在 Linux 上玩 Windows 游戏][2]。
|
||||
|
||||
如果说 Steam 在 Linux 运行 Windows 游戏领域还是新玩家,而 Lutris 却已从事多年。
|
||||
|
||||
[Lutris][4] 是一款为 Linux 开发的开源游戏平台,提供诸如 Origin、Steam、战网等平台的游戏安装器。它使用 Wine 来运行 Linux 不能支持的程序。
|
||||
|
||||
Lutris 近期宣布你可以通过它来运行 Epic 游戏商店。
|
||||
|
||||
### Lutris 为 Linux 带来了 Epic 游戏
|
||||
|
||||
![Epic Games Store Lutris Linux][5]
|
||||
|
||||
[Epic 游戏商城][6] 是一个类似 Steam 的电子游戏分销平台。它目前只支持 Windows 和 macOS。
|
||||
|
||||
Lutris 团队付出了大量努力使 Linux 用户可以通过 Lutris 使用 Epic 游戏商城。虽然我不用 Epic 商城,但可以通过 Lutris 在 Linux 上运行 Epic 商城终归是个好消息。
|
||||
|
||||
> 好消息! 你现在可以通过 Lutris 安装获得 [@EpicGames][7] 商城在 Linux 下的全功能支持!没有发现任何问题。 <https://t.co/cYmd7PcYdG>[@TimSweeneyEpic][8] 可能会很喜欢 😊
|
||||
>
|
||||
> ![pic.twitter.com/7mt9fXt7TH][9]
|
||||
>
|
||||
> — Lutris Gaming (@LutrisGaming) [April 17, 2019][10]
|
||||
|
||||
作为一名狂热的游戏玩家和 Linux 用户,我立即得到了这个消息,并安装了 Lutris 来运行 Epic 游戏。
|
||||
|
||||
**备注:** 我使用 [Ubuntu 19.04][11] 来测试 Linux 环境下的游戏运行情况。
|
||||
|
||||
### 通过 Lutris 在 Linux 下使用 Epic 游戏商城
|
||||
|
||||
为了在你的 Linux 系统中安装 Epic 游戏商城,请确保你已经安装了 Wine 和 Python 3。接下来,[在 Ubuntu 中安装 Wine][12] ,或任何你正在使用的 Linux 发行版本也都可以。然后, [从官方网站下载 Lutris][13].
|
||||
|
||||
#### 安装 Epic 游戏商城
|
||||
|
||||
Lutris 安装成功后,直接启动它。
|
||||
|
||||
当我尝试时,我遇到了一个问题(当我用 GUI 启动时却没有遇到)。当我尝试在命令行输入 `lutris` 来启动时,我发现了下图所示的错误:
|
||||
|
||||
![][15]
|
||||
|
||||
感谢 Abhishek,我了解到了这是一个常见问题 (你可以在 [GitHub][16] 上查看这个问题)。
|
||||
|
||||
总之,为了解决这个问题,我需要在命令行中输入以下命令:
|
||||
|
||||
```
|
||||
export LC_ALL=C
|
||||
```
|
||||
|
||||
当你遇到同样的问题时,只要你输入这个命令,就能正常启动 Lutris 了。
|
||||
|
||||
**注意**:每次启动 Lutris 时都必须输入这个命令。因此,最好将其添加到 `.bashrc` 文件或环境变量列表中。
|
||||
|
||||
上述操作完成后,只要启动并搜索 “Epic Games Store” 会显示以下图片中的内容:
|
||||
|
||||
![Epic Games Store in Lutris][17]
|
||||
|
||||
在这里,我已经安装过了,所以你将会看到“安装”选项,它会自动询问你是否需要安装需要的包。只需要继续操作就可以成功安装。就是这样,不需要任何黑科技。
|
||||
|
||||
#### 玩一款 Epic 游戏商城中的游戏
|
||||
|
||||
![Epic Games Store][18]
|
||||
|
||||
现在我们已经通过 Lutris 在 Linux 上安装了 Epic 游戏商城,启动它并登录你的账号就可以开始了。
|
||||
|
||||
但这真会奏效吗?
|
||||
|
||||
*是的,Epic 游戏商城可以运行。* **但是所有游戏都不能玩。**(LCTT 译注:莫生气,请看文末的进一步解释!)
|
||||
|
||||
好吧,我并没有尝试过所有内容,但是我拿了一个免费的游戏(Transistor —— 一款回合制 ARPG 游戏)来检查它是否工作。
|
||||
|
||||
![Transistor – Epic Games Store][19]
|
||||
|
||||
很不幸,游戏没有启动。当我运行时界面显示了 “Running” 不过什么都没有发生。
|
||||
|
||||
到目前为止,我还不知道有什么解决方案 —— 所以如果我找到解决方案,我会尽力让你们知道最新情况。
|
||||
|
||||
### 总结
|
||||
|
||||
通过 Lutris 这样的工具使 Linux 的游戏场景得到了改善,这终归是个好消息 。不过,仍有许多工作要做。
|
||||
|
||||
对于在 Linux 上运行的游戏来说,无障碍运行仍然是一个挑战。其中可能就会有我遇到的这种问题,或者其它类似的。但它正朝着正确的方向发展 —— 即使还存在着一些问题。
|
||||
|
||||
你有什么看法吗?你是否也尝试用 Lutris 在 Linux 上启动 Epic 游戏商城?在下方评论让我们看看你的意见。
|
||||
|
||||
### 补充
|
||||
|
||||
Transistor 实际上有一个原生的 Linux 移植版。到目前为止,我从 Epic 获得的所有游戏都是如此。所以我会试着压下我的郁闷,而因为 Epic 只让你玩你通过他们的商店/启动器购买的游戏,所以在 Linux 机器上用 Lutris 玩这个原生的 Linux 游戏是不可能的。这简直愚蠢极了。Steam 有一个原生的 Linux 启动器,虽然不是很理想,但它可以工作。GOG 允许你从网站下载购买的内容,可以在你喜欢的任何平台上玩这些游戏。他们的启动器完全是可选的。
|
||||
|
||||
我对此非常恼火,因为我在我的 Epic 库中的游戏都是可以在我的笔记本电脑上运行得很好的游戏,当我坐在桌面前时,玩起来很有趣。但是因为那台桌面机是我唯一拥有的 Windows 机器……
|
||||
|
||||
我选择使用 Linux 时已经知道会存在兼容性问题,并且我有一个专门用于游戏的 Windows 机器,而我通过 Epic 获得的游戏都是免费的,所以我在这里只是表示一下不满。但是,他们两个作为最著名的竞争对手,Epic 应该有在我的 Linux 机器上玩原生 Linux 移植版的机制。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/epic-games-lutris-linux/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[Modrisco](https://github.com/Modrisco)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://linux.cn/article-7316-1.html
|
||||
[2]: https://linux.cn/article-10061-1.html
|
||||
[3]: https://linux.cn/article-10054-1.html
|
||||
[4]: https://lutris.net/
|
||||
[5]: https://itsfoss.com/wp-content/uploads/2019/04/epic-games-store-lutris-linux-800x450.png
|
||||
[6]: https://www.epicgames.com/store/en-US/
|
||||
[7]: https://twitter.com/EpicGames?ref_src=twsrc%5Etfw
|
||||
[8]: https://twitter.com/TimSweeneyEpic?ref_src=twsrc%5Etfw
|
||||
[9]: https://pbs.twimg.com/media/D4XkXafX4AARDkW?format=jpg&name=medium
|
||||
[10]: https://twitter.com/LutrisGaming/status/1118552969816018948?ref_src=twsrc%5Etfw
|
||||
[11]: https://itsfoss.com/ubuntu-19-04-release-features/
|
||||
[12]: https://itsfoss.com/install-latest-wine/
|
||||
[13]: https://lutris.net/downloads/
|
||||
[14]: https://itsfoss.com/ubuntu-mate-entroware/
|
||||
[15]: https://itsfoss.com/wp-content/uploads/2019/04/lutris-error.jpg
|
||||
[16]: https://github.com/lutris/lutris/issues/660
|
||||
[17]: https://itsfoss.com/wp-content/uploads/2019/04/lutris-epic-games-store-800x520.jpg
|
||||
[18]: https://itsfoss.com/wp-content/uploads/2019/04/epic-games-store-800x450.jpg
|
||||
[19]: https://itsfoss.com/wp-content/uploads/2019/04/transistor-game-epic-games-store-800x410.jpg
|
||||
[20]: https://itsfoss.com/skpe-alpha-linux/
|
@ -0,0 +1,256 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (tomjlw)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10955-1.html)
|
||||
[#]: subject: (How to identify same-content files on Linux)
|
||||
[#]: via: (https://www.networkworld.com/article/3390204/how-to-identify-same-content-files-on-linux.html#tk.rss_all)
|
||||
[#]: author: (Sandra Henry-Stocker https://www.networkworld.com/author/Sandra-Henry_Stocker/)
|
||||
|
||||
如何在 Linux 上识别同样内容的文件
|
||||
======
|
||||
> 有时文件副本相当于对硬盘空间的巨大浪费,并会在你想要更新文件时造成困扰。以下是用来识别这些文件的六个命令。
|
||||
|
||||
![Vinoth Chandar \(CC BY 2.0\)][1]
|
||||
|
||||
在最近的帖子中,我们看了[如何识别并定位硬链接的文件][2](即,指向同一硬盘内容并共享 inode)。在本文中,我们将查看能找到具有相同*内容*,却不相链接的文件的命令。
|
||||
|
||||
硬链接很有用是因为它们能够使文件存放在文件系统内的多个地方却不会占用额外的硬盘空间。另一方面,有时文件副本相当于对硬盘空间的巨大浪费,在你想要更新文件时也会有造成困扰之虞。在本文中,我们将看一下多种识别这些文件的方式。
|
||||
|
||||
### 用 diff 命令比较文件
|
||||
|
||||
可能比较两个文件最简单的方法是使用 `diff` 命令。输出会显示你文件的不同之处。`<` 和 `>` 符号代表在当参数传过来的第一个(`<`)或第二个(`>`)文件中是否有额外的文字行。在这个例子中,在 `backup.html` 中有额外的文字行。
|
||||
|
||||
```
|
||||
$ diff index.html backup.html
|
||||
2438a2439,2441
|
||||
> <pre>
|
||||
> That's all there is to report.
|
||||
> </pre>
|
||||
```
|
||||
|
||||
如果 `diff` 没有输出那代表两个文件相同。
|
||||
|
||||
```
|
||||
$ diff home.html index.html
|
||||
$
|
||||
```
|
||||
|
||||
`diff` 的唯一缺点是它一次只能比较两个文件并且你必须指定用来比较的文件,这篇帖子中的一些命令可以为你找到多个重复文件。
|
||||
|
||||
### 使用校验和
|
||||
|
||||
`cksum`(checksum) 命令计算文件的校验和。校验和是一种将文字内容转化成一个长数字(例如2819078353 228029)的数学简化。虽然校验和并不是完全独有的,但是文件内容不同校验和却相同的概率微乎其微。
|
||||
|
||||
```
|
||||
$ cksum *.html
|
||||
2819078353 228029 backup.html
|
||||
4073570409 227985 home.html
|
||||
4073570409 227985 index.html
|
||||
```
|
||||
|
||||
在上述示例中,你可以看到产生同样校验和的第二个和第三个文件是如何可以被默认为相同的。
|
||||
|
||||
### 使用 find 命令
|
||||
|
||||
虽然 `find` 命令并没有寻找重复文件的选项,它依然可以被用来通过名字或类型寻找文件并运行 `cksum` 命令。例如:
|
||||
|
||||
```
|
||||
$ find . -name "*.html" -exec cksum {} \;
|
||||
4073570409 227985 ./home.html
|
||||
2819078353 228029 ./backup.html
|
||||
4073570409 227985 ./index.html
|
||||
```
|
||||
|
||||
### 使用 fslint 命令
|
||||
|
||||
`fslint` 命令可以被特地用来寻找重复文件。注意我们给了它一个起始位置。如果它需要遍历相当多的文件,这就需要花点时间来完成。注意它是如何列出重复文件并寻找其它问题的,比如空目录和坏 ID。
|
||||
|
||||
```
|
||||
$ fslint .
|
||||
-----------------------------------file name lint
|
||||
-------------------------------Invalid utf8 names
|
||||
-----------------------------------file case lint
|
||||
----------------------------------DUPlicate files <==
|
||||
home.html
|
||||
index.html
|
||||
-----------------------------------Dangling links
|
||||
--------------------redundant characters in links
|
||||
------------------------------------suspect links
|
||||
--------------------------------Empty Directories
|
||||
./.gnupg
|
||||
----------------------------------Temporary Files
|
||||
----------------------duplicate/conflicting Names
|
||||
------------------------------------------Bad ids
|
||||
-------------------------Non Stripped executables
|
||||
```
|
||||
|
||||
你可能需要在你的系统上安装 `fslint`。你可能也需要将它加入你的命令搜索路径:
|
||||
|
||||
```
|
||||
$ export PATH=$PATH:/usr/share/fslint/fslint
|
||||
```
|
||||
|
||||
### 使用 rdfind 命令
|
||||
|
||||
`rdfind` 命令也会寻找重复(相同内容的)文件。它的名字意即“重复数据搜寻”,并且它能够基于文件日期判断哪个文件是原件——这在你选择删除副本时很有用因为它会移除较新的文件。
|
||||
|
||||
```
|
||||
$ rdfind ~
|
||||
Now scanning "/home/shark", found 12 files.
|
||||
Now have 12 files in total.
|
||||
Removed 1 files due to nonunique device and inode.
|
||||
Total size is 699498 bytes or 683 KiB
|
||||
Removed 9 files due to unique sizes from list.2 files left.
|
||||
Now eliminating candidates based on first bytes:removed 0 files from list.2 files left.
|
||||
Now eliminating candidates based on last bytes:removed 0 files from list.2 files left.
|
||||
Now eliminating candidates based on sha1 checksum:removed 0 files from list.2 files left.
|
||||
It seems like you have 2 files that are not unique
|
||||
Totally, 223 KiB can be reduced.
|
||||
Now making results file results.txt
|
||||
```
|
||||
|
||||
你可以在 `dryrun` 模式中运行这个命令 (换句话说,仅仅汇报可能会另外被做出的改动)。
|
||||
|
||||
```
|
||||
$ rdfind -dryrun true ~
|
||||
(DRYRUN MODE) Now scanning "/home/shark", found 12 files.
|
||||
(DRYRUN MODE) Now have 12 files in total.
|
||||
(DRYRUN MODE) Removed 1 files due to nonunique device and inode.
|
||||
(DRYRUN MODE) Total size is 699352 bytes or 683 KiB
|
||||
Removed 9 files due to unique sizes from list.2 files left.
|
||||
(DRYRUN MODE) Now eliminating candidates based on first bytes:removed 0 files from list.2 files left.
|
||||
(DRYRUN MODE) Now eliminating candidates based on last bytes:removed 0 files from list.2 files left.
|
||||
(DRYRUN MODE) Now eliminating candidates based on sha1 checksum:removed 0 files from list.2 files left.
|
||||
(DRYRUN MODE) It seems like you have 2 files that are not unique
|
||||
(DRYRUN MODE) Totally, 223 KiB can be reduced.
|
||||
(DRYRUN MODE) Now making results file results.txt
|
||||
```
|
||||
|
||||
`rdfind` 命令同样提供了类似忽略空文档(`-ignoreempty`)和跟踪符号链接(`-followsymlinks`)的功能。查看 man 页面获取解释。
|
||||
|
||||
```
|
||||
-ignoreempty ignore empty files
|
||||
-minsize ignore files smaller than speficied size
|
||||
-followsymlinks follow symbolic links
|
||||
-removeidentinode remove files referring to identical inode
|
||||
-checksum identify checksum type to be used
|
||||
-deterministic determiness how to sort files
|
||||
-makesymlinks turn duplicate files into symbolic links
|
||||
-makehardlinks replace duplicate files with hard links
|
||||
-makeresultsfile create a results file in the current directory
|
||||
-outputname provide name for results file
|
||||
-deleteduplicates delete/unlink duplicate files
|
||||
-sleep set sleep time between reading files (milliseconds)
|
||||
-n, -dryrun display what would have been done, but don't do it
|
||||
```
|
||||
|
||||
注意 `rdfind` 命令提供了 `-deleteduplicates true` 的设置选项以删除副本。希望这个命令语法上的小问题不会惹恼你。;-)
|
||||
|
||||
```
|
||||
$ rdfind -deleteduplicates true .
|
||||
...
|
||||
Deleted 1 files. <==
|
||||
```
|
||||
|
||||
你将可能需要在你的系统上安装 `rdfind` 命令。试验它以熟悉如何使用它可能是一个好主意。
|
||||
|
||||
### 使用 fdupes 命令
|
||||
|
||||
`fdupes` 命令同样使得识别重复文件变得简单。它同时提供了大量有用的选项——例如用来迭代的 `-r`。在这个例子中,它像这样将重复文件分组到一起:
|
||||
|
||||
```
|
||||
$ fdupes ~
|
||||
/home/shs/UPGRADE
|
||||
/home/shs/mytwin
|
||||
|
||||
/home/shs/lp.txt
|
||||
/home/shs/lp.man
|
||||
|
||||
/home/shs/penguin.png
|
||||
/home/shs/penguin0.png
|
||||
/home/shs/hideme.png
|
||||
```
|
||||
|
||||
这是使用迭代的一个例子,注意许多重复文件是重要的(用户的 `.bashrc` 和 `.profile` 文件)并且不应被删除。
|
||||
|
||||
```
|
||||
# fdupes -r /home
|
||||
/home/shark/home.html
|
||||
/home/shark/index.html
|
||||
|
||||
/home/dory/.bashrc
|
||||
/home/eel/.bashrc
|
||||
|
||||
/home/nemo/.profile
|
||||
/home/dory/.profile
|
||||
/home/shark/.profile
|
||||
|
||||
/home/nemo/tryme
|
||||
/home/shs/tryme
|
||||
|
||||
/home/shs/arrow.png
|
||||
/home/shs/PNGs/arrow.png
|
||||
|
||||
/home/shs/11/files_11.zip
|
||||
/home/shs/ERIC/file_11.zip
|
||||
|
||||
/home/shs/penguin0.jpg
|
||||
/home/shs/PNGs/penguin.jpg
|
||||
/home/shs/PNGs/penguin0.jpg
|
||||
|
||||
/home/shs/Sandra_rotated.png
|
||||
/home/shs/PNGs/Sandra_rotated.png
|
||||
```
|
||||
|
||||
`fdupe` 命令的许多选项列如下。使用 `fdupes -h` 命令或者阅读 man 页面获取详情。
|
||||
|
||||
```
|
||||
-r --recurse recurse
|
||||
-R --recurse: recurse through specified directories
|
||||
-s --symlinks follow symlinked directories
|
||||
-H --hardlinks treat hard links as duplicates
|
||||
-n --noempty ignore empty files
|
||||
-f --omitfirst omit the first file in each set of matches
|
||||
-A --nohidden ignore hidden files
|
||||
-1 --sameline list matches on a single line
|
||||
-S --size show size of duplicate files
|
||||
-m --summarize summarize duplicate files information
|
||||
-q --quiet hide progress indicator
|
||||
-d --delete prompt user for files to preserve
|
||||
-N --noprompt when used with --delete, preserve the first file in set
|
||||
-I --immediate delete duplicates as they are encountered
|
||||
-p --permissions don't soncider files with different owner/group or
|
||||
permission bits as duplicates
|
||||
-o --order=WORD order files according to specification
|
||||
-i --reverse reverse order while sorting
|
||||
-v --version display fdupes version
|
||||
-h --help displays help
|
||||
```
|
||||
|
||||
`fdupes` 命令是另一个你可能需要安装并使用一段时间才能熟悉其众多选项的命令。
|
||||
|
||||
### 总结
|
||||
|
||||
Linux 系统提供能够定位并(潜在地)能移除重复文件的一系列的好工具,以及能让你指定搜索区域及当对你所发现的重复文件时的处理方式的选项。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3390204/how-to-identify-same-content-files-on-linux.html#tk.rss_all
|
||||
|
||||
作者:[Sandra Henry-Stocker][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[tomjlw](https://github.com/tomjlw)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Sandra-Henry_Stocker/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://images.idgesg.net/images/article/2019/04/chairs-100794266-large.jpg
|
||||
[2]: https://www.networkworld.com/article/3387961/how-to-identify-duplicate-files-on-linux.html
|
||||
[3]: https://www.youtube.com/playlist?list=PL7D2RMSmRO9J8OTpjFECi8DJiTQdd4hua
|
||||
[4]: https://www.networkworld.com/article/3242170/linux/invaluable-tips-and-tricks-for-troubleshooting-linux.html
|
||||
[5]: https://www.facebook.com/NetworkWorld/
|
||||
[6]: https://www.linkedin.com/company/network-world
|
||||
|
@ -0,0 +1,146 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (cycoe)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10929-1.html)
|
||||
[#]: subject: (Monitoring CPU and GPU Temperatures on Linux)
|
||||
[#]: via: (https://itsfoss.com/monitor-cpu-gpu-temp-linux/)
|
||||
[#]: author: (It's FOSS Community https://itsfoss.com/author/itsfoss/)
|
||||
|
||||
在 Linux 上监控 CPU 和 GPU 温度
|
||||
======
|
||||
|
||||
> 本篇文章讨论了在 Linux 命令行中监控 CPU 和 GPU 温度的两种简单方式。
|
||||
|
||||
由于 [Steam][1](包括 [Steam Play][2],即 Proton)和一些其他的发展,GNU/Linux 正在成为越来越多计算机用户的日常游戏平台的选择。也有相当一部分用户在遇到像[视频编辑][3]或图形设计等(Kdenlive 和 [Blender][4] 是这类应用程序中很好的例子)资源消耗型计算任务时,也会使用 GNU/Linux。
|
||||
|
||||
不管你是否是这些用户中的一员或其他用户,你也一定想知道你的电脑 CPU 和 GPU 能有多热(如果你想要超频的话更会如此)。如果是这样,那么继续读下去。我们会介绍两个非常简单的命令来监控 CPU 和 GPU 温度。
|
||||
|
||||
我的装置包括一台 [Slimbook Kymera][5] 和两台显示器(一台 TV 和一台 PC 监视器),使得我可以用一台来玩游戏,另一台来留意监控温度。另外,因为我使用 [Zorin OS][6],我会将关注点放在 Ubuntu 和 Ubuntu 的衍生发行版上。
|
||||
|
||||
为了监控 CPU 和 GPU 的行为,我们将利用实用的 `watch` 命令在每几秒钟之后动态地得到读数。
|
||||
|
||||
![][7]
|
||||
|
||||
### 在 Linux 中监控 CPU 温度
|
||||
|
||||
对于 CPU 温度,我们将结合使用 `watch` 与 `sensors` 命令。一篇关于[此工具的图形用户界面版本][8]的有趣文章已经在 It's FOSS 中介绍过了。然而,我们将在此处使用命令行版本:
|
||||
|
||||
```
|
||||
watch -n 2 sensors
|
||||
```
|
||||
|
||||
`watch` 保证了读数会在每 2 秒钟更新一次(当然,这个周期值能够根据你的需要去更改):
|
||||
|
||||
```
|
||||
Every 2,0s: sensors
|
||||
|
||||
iwlwifi-virtual-0
|
||||
Adapter: Virtual device
|
||||
temp1: +39.0°C
|
||||
|
||||
acpitz-virtual-0
|
||||
Adapter: Virtual device
|
||||
temp1: +27.8°C (crit = +119.0°C)
|
||||
temp2: +29.8°C (crit = +119.0°C)
|
||||
|
||||
coretemp-isa-0000
|
||||
Adapter: ISA adapter
|
||||
Package id 0: +37.0°C (high = +82.0°C, crit = +100.0°C)
|
||||
Core 0: +35.0°C (high = +82.0°C, crit = +100.0°C)
|
||||
Core 1: +35.0°C (high = +82.0°C, crit = +100.0°C)
|
||||
Core 2: +33.0°C (high = +82.0°C, crit = +100.0°C)
|
||||
Core 3: +36.0°C (high = +82.0°C, crit = +100.0°C)
|
||||
Core 4: +37.0°C (high = +82.0°C, crit = +100.0°C)
|
||||
Core 5: +35.0°C (high = +82.0°C, crit = +100.0°C)
|
||||
```
|
||||
|
||||
除此之外,我们还能得到如下信息:
|
||||
|
||||
* 我们有 5 个核心正在被使用(并且当前的最高温度为 37.0℃)。
|
||||
* 温度超过 82.0℃ 会被认为是过热。
|
||||
* 超过 100.0℃ 的温度会被认为是超过临界值。
|
||||
|
||||
根据以上的温度值我们可以得出结论,我的电脑目前的工作负载非常小。
|
||||
|
||||
### 在 Linux 中监控 GPU 温度
|
||||
|
||||
现在让我们来看看显卡。我从来没使用过 AMD 的显卡,因此我会将重点放在 Nvidia 的显卡上。我们需要做的第一件事是从 [Ubuntu 的附加驱动][10] 中下载合适的最新驱动。
|
||||
|
||||
在 Ubuntu(Zorin 或 Linux Mint 也是相同的)中,进入“软件和更新 > 附加驱动”选项,选择最新的可用驱动。另外,你可以添加或启用显示卡的官方 ppa(通过命令行或通过“软件和更新 > 其他软件”来实现)。安装驱动程序后,你将可以使用 “Nvidia X Server” 的 GUI 程序以及命令行工具 `nvidia-smi`(Nvidia 系统管理界面)。因此我们将使用 `watch` 和 `nvidia-smi`:
|
||||
|
||||
```
|
||||
watch -n 2 nvidia-smi
|
||||
```
|
||||
|
||||
与 CPU 的情况一样,我们会在每两秒得到一次更新的读数:
|
||||
|
||||
```
|
||||
Every 2,0s: nvidia-smi
|
||||
|
||||
Fri Apr 19 20:45:30 2019
|
||||
+-----------------------------------------------------------------------------+
|
||||
| Nvidia-SMI 418.56 Driver Version: 418.56 CUDA Version: 10.1 |
|
||||
|-------------------------------+----------------------+----------------------+
|
||||
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
|
||||
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|
||||
|===============================+======================+======================|
|
||||
| 0 GeForce GTX 106... Off | 00000000:01:00.0 On | N/A |
|
||||
| 0% 54C P8 10W / 120W | 433MiB / 6077MiB | 4% Default |
|
||||
+-------------------------------+----------------------+----------------------+
|
||||
|
||||
+-----------------------------------------------------------------------------+
|
||||
| Processes: GPU Memory |
|
||||
| GPU PID Type Process name Usage |
|
||||
|=============================================================================|
|
||||
| 0 1557 G /usr/lib/xorg/Xorg 190MiB |
|
||||
| 0 1820 G /usr/bin/gnome-shell 174MiB |
|
||||
| 0 7820 G ...equest-channel-token=303407235874180773 65MiB |
|
||||
+-----------------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
从这个表格中我们得到了关于显示卡的如下信息:
|
||||
|
||||
* 它正在使用版本号为 418.56 的开源驱动。
|
||||
* 显示卡的当前温度为 54.0℃,并且风扇的使用量为 0%。
|
||||
* 电量的消耗非常低:仅仅 10W。
|
||||
* 总量为 6GB 的 vram(视频随机存取存储器),只使用了 433MB。
|
||||
* vram 正在被 3 个进程使用,他们的 ID 分别为 1557、1820 和 7820。
|
||||
|
||||
大部分这些事实或数值都清晰地表明,我们没有在玩任何消耗系统资源的游戏或处理大负载的任务。当我们开始玩游戏、处理视频或其他类似任务时,这些值就会开始上升。
|
||||
|
||||
#### 结论
|
||||
|
||||
即便我们有 GUI 工具,但我还是发现这两个命令对于实时监控硬件非常的顺手。
|
||||
|
||||
你将如何去使用它们呢?你可以通过阅读他们的 man 手册来学习更多关于这些工具的使用技巧。
|
||||
|
||||
你有其他偏爱的工具吗?在评论里分享给我们吧 ;)。
|
||||
|
||||
玩得开心!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/monitor-cpu-gpu-temp-linux/
|
||||
|
||||
作者:[Alejandro Egea-Abellán][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[cycoe](https://github.com/cycoe)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/itsfoss/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://itsfoss.com/install-steam-ubuntu-linux/
|
||||
[2]: https://itsfoss.com/steam-play-proton/
|
||||
[3]: https://itsfoss.com/best-video-editing-software-linux/
|
||||
[4]: https://www.blender.org/
|
||||
[5]: https://slimbook.es/
|
||||
[6]: https://zorinos.com/
|
||||
[7]: https://itsfoss.com/wp-content/uploads/2019/04/monitor-cpu-gpu-temperature-linux-800x450.png
|
||||
[8]: https://itsfoss.com/check-laptop-cpu-temperature-ubuntu/
|
||||
[9]: https://itsfoss.com/best-command-line-games-linux/
|
||||
[10]: https://itsfoss.com/install-additional-drivers-ubuntu/
|
||||
[11]: https://itsfoss.com/review-googler-linux/
|
||||
[12]: https://itsfoss.com/wp-content/uploads/2019/04/EGEA-ABELLAN-Alejandro.jpg
|
@ -0,0 +1,112 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10919-1.html)
|
||||
[#]: subject: (Installing Budgie Desktop on Ubuntu [Quick Guide])
|
||||
[#]: via: (https://itsfoss.com/install-budgie-ubuntu/)
|
||||
[#]: author: (Atharva Lele https://itsfoss.com/author/atharva/)
|
||||
|
||||
在 Ubuntu 上安装 Budgie 桌面
|
||||
======
|
||||
|
||||
> 在这个逐步的教程中学习如何在 Ubuntu 上安装 Budgie 桌面。
|
||||
|
||||
在所有[各种 Ubuntu 版本][1]中,[Ubuntu Budgie][2] 是最被低估的版本。它外观优雅,而且需要的资源也不多。
|
||||
|
||||
阅读这篇 《[Ubuntu Budgie 点评][3]》或观看下面的视频,了解 Ubuntu Budgie 18.04 的外观如何。
|
||||
|
||||
- [Ubuntu 18.04 Budgie Desktop Tour [It's Elegant]](https://youtu.be/KXgreWOK33k)
|
||||
|
||||
如果你喜欢 [Budgie 桌面][5]但你正在使用其他版本的 Ubuntu,例如默认 Ubuntu 带有 GNOME 桌面,我有个好消息。你可以在当前的 Ubuntu 系统上安装 Budgie 并切换桌面环境。
|
||||
|
||||
在这篇文章中,我将告诉你到底该怎么做。但首先,对那些不了解 Budgie 的人进行一点介绍。
|
||||
|
||||
Budgie 桌面环境主要由 [Solus Linux 团队开发][6]。它的设计注重优雅和现代使用。Budgie 适用于所有主流 Linux 发行版,可以让用户在其上尝试体验这种新的桌面环境。Budgie 现在非常成熟,并提供了出色的桌面体验。
|
||||
|
||||
> 警告
|
||||
>
|
||||
> 在同一系统上安装多个桌面可能会导致冲突,你可能会遇到一些问题,如面板中缺少图标或同一程序的多个图标。
|
||||
>
|
||||
> 你也许不会遇到任何问题。是否要尝试不同桌面由你决定。
|
||||
|
||||
### 在 Ubuntu 上安装 Budgie
|
||||
|
||||
此方法未在 Linux Mint 上进行测试,因此我建议你 Mint 上不要按照此指南进行操作。
|
||||
|
||||
对于正在使用 Ubuntu 的人,Budgie 现在默认是 Ubuntu 仓库的一部分。因此,我们不需要添加任何 PPA 来下载 Budgie。
|
||||
|
||||
要安装 Budgie,只需在终端中运行此命令即可。我们首先要确保系统已完全更新。
|
||||
|
||||
```
|
||||
sudo apt update && sudo apt upgrade
|
||||
sudo apt install ubuntu-budgie-desktop
|
||||
```
|
||||
|
||||
下载完成后,你将看到选择显示管理器的提示。选择 “lightdm” 以获得完整的 Budgie 体验。
|
||||
|
||||
![Select lightdm][7]
|
||||
|
||||
安装完成后,重启计算机。然后,你会看到 Budgie 的登录页面。输入你的密码进入主屏幕。
|
||||
|
||||
![Budgie Desktop Home][8]
|
||||
|
||||
### 切换到其他桌面环境
|
||||
|
||||
![Budgie login screen][9]
|
||||
|
||||
你可以单击登录名旁边的 Budgie 图标获取登录选项。在那里,你可以在已安装的桌面环境(DE)之间进行选择。就我而言,我看到了 Budgie 和默认的 Ubuntu(GNOME)桌面。
|
||||
|
||||
![Select your DE][10]
|
||||
|
||||
因此,无论何时你想登录 GNOME,都可以使用此菜单执行此操作。
|
||||
|
||||
### 如何删除 Budgie
|
||||
|
||||
如果你不喜欢 Budgie 或只是想回到常规的以前的 Ubuntu,你可以如上节所述切换回常规桌面。
|
||||
|
||||
但是,如果你真的想要删除 Budgie 及其组件,你可以按照以下命令回到之前的状态。
|
||||
|
||||
**在使用这些命令之前先切换到其他桌面环境:**
|
||||
|
||||
```
|
||||
sudo apt remove ubuntu-budgie-desktop ubuntu-budgie* lightdm
|
||||
sudo apt autoremove
|
||||
sudo apt install --reinstall gdm3
|
||||
```
|
||||
|
||||
成功运行所有命令后,重启计算机。
|
||||
|
||||
现在,你将回到 GNOME 或其他你有的桌面。
|
||||
|
||||
### 你对 Budgie 有什么看法?
|
||||
|
||||
Budgie 是[最佳 Linux 桌面环境][12]之一。希望这个简短的指南帮助你在 Ubuntu 上安装了很棒的 Budgie 桌面。
|
||||
|
||||
如果你安装了 Budgie,你最喜欢它的什么?请在下面的评论中告诉我们。像往常一样,欢迎任何问题或建议。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/install-budgie-ubuntu/
|
||||
|
||||
作者:[Atharva Lele][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/atharva/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://itsfoss.com/which-ubuntu-install/
|
||||
[2]: https://ubuntubudgie.org/
|
||||
[3]: https://itsfoss.com/ubuntu-budgie-18-review/
|
||||
[4]: https://www.youtube.com/c/itsfoss?sub_confirmation=1
|
||||
[5]: https://github.com/solus-project/budgie-desktop
|
||||
[6]: https://getsol.us/home/
|
||||
[7]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/04/budgie_install_select_dm.png?fit=800%2C559&ssl=1
|
||||
[8]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/04/budgie_homescreen.jpg?fit=800%2C500&ssl=1
|
||||
[9]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/04/budgie_install_lockscreen.png?fit=800%2C403&ssl=1
|
||||
[10]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/04/budgie_install_lockscreen_select_de.png?fit=800%2C403&ssl=1
|
||||
[11]: https://itsfoss.com/snapd-error-ubuntu/
|
||||
[12]: https://itsfoss.com/best-linux-desktop-environments/
|
@ -7,37 +7,37 @@
|
||||
|
||||
**`/dev/urandom` 不安全。加密用途必须使用 `/dev/random`**
|
||||
|
||||
事实:`/dev/urandom` 才是类 Unix 操作系统下推荐的加密种子。
|
||||
*事实*:`/dev/urandom` 才是类 Unix 操作系统下推荐的加密种子。
|
||||
|
||||
**`/dev/urandom` 是<ruby>伪随机数生成器<rt>pseudo random number generator</rt></ruby>(PRND),而 `/dev/random` 是“真”随机数生成器。**
|
||||
|
||||
事实:它们两者本质上用的是同一种 CSPRNG (一种密码学伪随机数生成器)。它们之间细微的差别和“真”不“真”随机完全无关
|
||||
*事实*:它们两者本质上用的是同一种 CSPRNG (一种密码学伪随机数生成器)。它们之间细微的差别和“真”“不真”随机完全无关。(参见:“Linux 随机数生成器的构架”一节)
|
||||
|
||||
**`/dev/random` 在任何情况下都是密码学应用更好地选择。即便 `/dev/urandom` 也同样安全,我们还是不应该用它。**
|
||||
|
||||
事实:`/dev/random` 有个很恶心人的问题:它是阻塞的。(LCTT 译注:意味着请求都得逐个执行,等待前一个请求完成)
|
||||
*事实*:`/dev/random` 有个很恶心人的问题:它是阻塞的。(参见:“阻塞有什么问题?”一节)(LCTT 译注:意味着请求都得逐个执行,等待前一个请求完成)
|
||||
|
||||
**但阻塞不是好事吗!`/dev/random` 只会给出电脑收集的信息熵足以支持的随机量。`/dev/urandom` 在用完了所有熵的情况下还会不断吐不安全的随机数给你。**
|
||||
**但阻塞不是好事吗!`/dev/random` 只会给出电脑收集的信息熵足以支持的随机量。`/dev/urandom` 在用完了所有熵的情况下还会不断吐出不安全的随机数给你。**
|
||||
|
||||
事实:这是误解。就算我们不去考虑应用层面后续对随机种子的用法,“用完信息熵池”这个概念本身就不存在。仅仅 256 位的熵就足以生成计算上安全的随机数很长、很长的一段时间了。
|
||||
*事实*:这是误解。就算我们不去考虑应用层面后续对随机种子的用法,“用完信息熵池”这个概念本身就不存在。仅仅 256 位的熵就足以生成计算上安全的随机数很长、很长的一段时间了。(参见:“那熵池快空了的情况呢?”一节)
|
||||
|
||||
问题的关键还在后头:`/dev/random` 怎么知道有系统会*多少*可用的信息熵?接着看!
|
||||
|
||||
**但密码学家老是讨论重新选种子(re-seeding)。这难道不和上一条冲突吗?**
|
||||
|
||||
事实:你说的也没错!某种程度上吧。确实,随机数生成器一直在使用系统信息熵的状态重新选种。但这么做(一部分)是因为别的原因。
|
||||
*事实*:你说的也没错!某种程度上吧。确实,随机数生成器一直在使用系统信息熵的状态重新选种。但这么做(一部分)是因为别的原因。(参见:“重新选种”一节)
|
||||
|
||||
这样说吧,我没有说引入新的信息熵是坏的。更多的熵肯定更好。我只是说在熵池低的时候阻塞是没必要的。
|
||||
|
||||
**好,就算你说的都对,但是 `/dev/(u)random` 的 man 页面和你说的也不一样啊!到底有没有专家同意你说的这堆啊?**
|
||||
|
||||
事实:其实 man 页面和我说的不冲突。它看似好像在说 `/dev/urandom` 对密码学用途来说不安全,但如果你真的理解这堆密码学术语你就知道它说的并不是这个意思。
|
||||
*事实*:其实 man 页面和我说的不冲突。它看似好像在说 `/dev/urandom` 对密码学用途来说不安全,但如果你真的理解这堆密码学术语你就知道它说的并不是这个意思。(参见:“random 和 urandom 的 man 页面”一节)
|
||||
|
||||
man 页面确实说在一些情况下推荐使用 `/dev/random` (我觉得也没问题,但绝对不是说必要的),但它也推荐在大多数“一般”的密码学应用下使用 `/dev/urandom` 。
|
||||
|
||||
虽然诉诸权威一般来说不是好事,但在密码学这么严肃的事情上,和专家统一意见是很有必要的。
|
||||
|
||||
所以说呢,还确实有一些*专家*和我的一件事一致的:`/dev/urandom` 就应该是类 UNIX 操作系统下密码学应用的首选。显然的,是他们的观点说服了我而不是反过来的。
|
||||
所以说呢,还确实有一些*专家*和我的一件事一致的:`/dev/urandom` 就应该是类 UNIX 操作系统下密码学应用的首选。显然的,是他们的观点说服了我而不是反过来的。(参见:“正道”一节)
|
||||
|
||||
------
|
||||
|
||||
@ -45,9 +45,9 @@ man 页面确实说在一些情况下推荐使用 `/dev/random` (我觉得也
|
||||
|
||||
我尝试不讲太高深的东西,但是有两点内容必须先提一下才能让我们接着论证观点。
|
||||
|
||||
首当其冲的,*什么是随机性*,或者更准确地:我们在探讨什么样的随机性?
|
||||
首当其冲的,*什么是随机性*,或者更准确地:我们在探讨什么样的随机性?(参见:“真随机”一节)
|
||||
|
||||
另外一点很重要的是,我*没有尝试以说教的态度*对你们写这段话。我写这篇文章是为了日后可以在讨论起的时候指给别人看。比 140 字长(LCTT 译注:推特长度)。这样我就不用一遍遍重复我的观点了。能把论点磨炼成一篇文章本身就很有助于将来的讨论。
|
||||
另外一点很重要的是,我*没有尝试以说教的态度*对你们写这段话。我写这篇文章是为了日后可以在讨论起的时候指给别人看。比 140 字长(LCTT 译注:推特长度)。这样我就不用一遍遍重复我的观点了。能把论点磨炼成一篇文章本身就很有助于将来的讨论。(参见:“你是在说我笨?!”一节)
|
||||
|
||||
并且我非常乐意听到不一样的观点。但我只是认为单单地说 `/dev/urandom` 坏是不够的。你得能指出到底有什么问题,并且剖析它们。
|
||||
|
||||
@ -55,43 +55,43 @@ man 页面确实说在一些情况下推荐使用 `/dev/random` (我觉得也
|
||||
|
||||
绝对没有!
|
||||
|
||||
事实上我自己也相信了 “`/dev/urandom` 是不安全的” 好些年。这几乎不是我们的错,因为那么德高望重的人在 Usenet、论坛、推特上跟我们重复这个观点。甚至*连 man 手册*都似是而非地说着。我们当年怎么可能鄙视诸如“信息熵太低了”这种看上去就很让人信服的观点呢?
|
||||
事实上我自己也相信了 “`/dev/urandom` 是不安全的” 好些年。这几乎不是我们的错,因为那么德高望重的人在 Usenet、论坛、推特上跟我们重复这个观点。甚至*连 man 手册*都似是而非地说着。我们当年怎么可能鄙视诸如“信息熵太低了”这种看上去就很让人信服的观点呢?(参见:“random 和 urandom 的 man 页面”一节)
|
||||
|
||||
整个流言之所以如此广为流传不是因为人们太蠢,而是因为但凡有点关于信息熵和密码学概念的人都会觉得这个说法很有道理。直觉似乎都在告诉我们这流言讲的很有道理。很不幸直觉在密码学里通常不管用,这次也一样。
|
||||
|
||||
### 真随机
|
||||
|
||||
什么叫一个随机变量是“真随机的”?
|
||||
随机数是“真正随机”是什么意思?
|
||||
|
||||
我不想搞的太复杂以至于变成哲学范畴的东西。这种讨论很容易走偏因为随机模型大家见仁见智,讨论很快变得毫无意义。
|
||||
我不想搞的太复杂以至于变成哲学范畴的东西。这种讨论很容易走偏因为对于随机模型大家见仁见智,讨论很快变得毫无意义。
|
||||
|
||||
在我看来“真随机”的“试金石”是量子效应。一个光子穿过或不穿过一个半透镜。或者观察一个放射性粒子衰变。这类东西是现实世界最接近真随机的东西。当然,有些人也不相信这类过程是真随机的,或者这个世界根本不存在任何随机性。这个就百家争鸣了,我也不好多说什么了。
|
||||
|
||||
密码学家一般都会通过不去讨论什么是“真随机”来避免这种争论。它们更关心的是<ruby>不可预测性<rt> unpredictability</rt></ruby>。只要没有*任何*方法能猜出下一个随机数就可以了。所以当你以密码学应用为前提讨论一个随机数好不好的时候,在我看来这才是最重要的。
|
||||
密码学家一般都会通过不去讨论什么是“真随机”来避免这种哲学辩论。他们更关心的是<ruby>不可预测性<rt>unpredictability</rt></ruby>。只要没有*任何*方法能猜出下一个随机数就可以了。所以当你以密码学应用为前提讨论一个随机数好不好的时候,在我看来这才是最重要的。
|
||||
|
||||
无论如何,我不怎么关心“哲学上安全”的随机数,这也包括别人嘴里的“真”随机数。
|
||||
|
||||
## 两种安全,一种有用
|
||||
### 两种安全,一种有用
|
||||
|
||||
但就让我们退一步说,你有了一个“真”随机变量。你下一步做什么呢?
|
||||
|
||||
你把它们打印出来然后挂在墙上来展示量子宇宙的美与和谐?牛逼!我很理解你。
|
||||
你把它们打印出来然后挂在墙上来展示量子宇宙的美与和谐?牛逼!我支持你。
|
||||
|
||||
但是等等,你说你要*用*它们?做密码学用途?额,那这就废了,因为这事情就有点复杂了。
|
||||
|
||||
事情是这样的,你的真随机,量子力学加护的随机数即将被用进不理想的现实世界程序里。
|
||||
事情是这样的,你的真随机、量子力学加护的随机数即将被用进不理想的现实世界算法里去。
|
||||
|
||||
因为我们使用的大多数算法并不是<ruby>理论信息学<rt>information-theoretic</rt></ruby>上安全的。它们“只能”提供 **计算意义上的安全**。我能想到为数不多的例外就只有 Shamir 密钥分享 和 One-time pad 算法。并且就算前者是名副其实的(如果你实际打算用的话),后者则毫无可行性可言。
|
||||
因为我们使用的几乎所有的算法都并不是<ruby>信息论安全性<rt>information-theoretic security </rt></ruby>的。它们“只能”提供**计算意义上的安全**。我能想到为数不多的例外就只有 Shamir 密钥分享和<ruby>一次性密码本<rt>One-time pad</rt></ruby>(OTP)算法。并且就算前者是名副其实的(如果你实际打算用的话),后者则毫无可行性可言。
|
||||
|
||||
但所有那些大名鼎鼎的密码学算法,AES、RSA、Diffie-Hellman、椭圆曲线,还有所有那些加密软件包,OpenSSL、GnuTLS、Keyczar、你的操作系统的加密 API,都仅仅是计算意义上的安全的。
|
||||
但所有那些大名鼎鼎的密码学算法,AES、RSA、Diffie-Hellman、椭圆曲线,还有所有那些加密软件包,OpenSSL、GnuTLS、Keyczar、你的操作系统的加密 API,都仅仅是计算意义上安全的。
|
||||
|
||||
那区别是什么呢?理论信息学上的安全肯定是安全的,绝对是,其它那些的算法都可能在理论上被拥有无限计算力的穷举破解。我们依然愉快地使用它们因为全世界的计算机加起来都不可能在宇宙年龄的时间里破解,至少现在是这样。而这就是我们文章里说的“不安全”。
|
||||
那区别是什么呢?信息论安全的算法肯定是安全的,绝对是,其它那些的算法都可能在理论上被拥有无限计算力的穷举破解。我们依然愉快地使用它们是因为全世界的计算机加起来都不可能在宇宙年龄的时间里破解,至少现在是这样。而这就是我们文章里说的“不安全”。
|
||||
|
||||
除非哪个聪明的家伙破解了算法本身——在只需要极少量计算力的情况下。这也是每个密码学家梦寐以求的圣杯:破解 AES 本身、破解 RSA 本身等等。
|
||||
除非哪个聪明的家伙破解了算法本身 —— 在只需要更少量计算力、在今天可实现的计算力的情况下。这也是每个密码学家梦寐以求的圣杯:破解 AES 本身、破解 RSA 本身等等。
|
||||
|
||||
所以现在我们来到了更底层的东西:随机数生成器,你坚持要“真随机”而不是“伪随机”。但是没过一会儿你的真随机数就被喂进了你极为鄙视的伪随机算法里了!
|
||||
|
||||
真相是,如果我们最先进的 hash 算法被破解了,或者最先进的块加密被破解了,你得到这些那些“哲学上不安全的”甚至无所谓了,因为反正你也没有安全的应用方法了。
|
||||
真相是,如果我们最先进的哈希算法被破解了,或者最先进的分组加密算法被破解了,你得到的这些“哲学上不安全”的随机数甚至无所谓了,因为反正你也没有安全的应用方法了。
|
||||
|
||||
所以把计算性上安全的随机数喂给你的仅仅是计算性上安全的算法就可以了,换而言之,用 `/dev/urandom`。
|
||||
|
||||
@ -103,7 +103,7 @@ man 页面确实说在一些情况下推荐使用 `/dev/random` (我觉得也
|
||||
|
||||
![image: mythical structure of the kernel's random number generator][1]
|
||||
|
||||
“真随机数”,尽管可能有点瑕疵,进入操作系统然后它的熵立刻被加入内部熵计数器。然后经过“矫偏”和“漂白”之后它进入内核的熵池,然后 `/dev/random` 和 `/dev/urandom` 从里面生成随机数。
|
||||
“真正的随机性”,尽管可能有点瑕疵,进入操作系统然后它的熵立刻被加入内部熵计数器。然后经过“矫偏”和“漂白”之后它进入内核的熵池,然后 `/dev/random` 和 `/dev/urandom` 从里面生成随机数。
|
||||
|
||||
“真”随机数生成器,`/dev/random`,直接从池里选出随机数,如果熵计数器表示能满足需要的数字大小,那就吐出数字并且减少熵计数。如果不够的话,它会阻塞程序直至有足够的熵进入系统。
|
||||
|
||||
@ -123,25 +123,25 @@ man 页面确实说在一些情况下推荐使用 `/dev/random` (我觉得也
|
||||
|
||||
![image: actual structure of the kernel's random number generator before Linux 4.8][2]
|
||||
|
||||
> 这是个很粗糙的简化。实际上不仅有一个,而是三个熵池。一个主池,另一个给 `/dev/random`,还有一个给 `/dev/urandom`,后两者依靠从主池里获取熵。这三个池都有各自的熵计数器,但二级池(后两个)的计数器基本都在 0 附近,而“新鲜”的熵总在需要的时候从主池流过来。同时还有好多混合和回流进系统在同时进行。整个过程对于这篇文档来说都过于复杂了我们跳过。
|
||||
|
||||
你看到最大的区别了吗?CSPRNG 并不是和随机数生成器一起跑的,以 `/dev/urandom` 需要输出但熵不够的时候进行填充。CSPRNG 是整个随机数生成过程的内部组件之一。从来就没有什么 `/dev/random` 直接从池里输出纯纯的随机性。每个随机源的输入都在 CSPRNG 里充分混合和散列过了,这一切都发生在实际变成一个随机数,被 `/dev/urandom` 或者 `/dev/random` 吐出去之前。
|
||||
你看到最大的区别了吗?CSPRNG 并不是和随机数生成器一起跑的,它在 `/dev/urandom` 需要输出但熵不够的时候进行填充。CSPRNG 是整个随机数生成过程的内部组件之一。从来就没有什么 `/dev/random` 直接从池里输出纯纯的随机性。每个随机源的输入都在 CSPRNG 里充分混合和散列过了,这一切都发生在实际变成一个随机数,被 `/dev/urandom` 或者 `/dev/random` 吐出去之前。
|
||||
|
||||
另外一个重要的区别是这里没有熵计数器的任何事情,只有预估。一个源给你的熵的量并不是什么很明确能直接得到的数字。你得预估它。注意,如果你太乐观地预估了它,那 `/dev/random` 最重要的特性——只给出熵允许的随机量——就荡然无存了。很不幸的,预估熵的量是很困难的。
|
||||
|
||||
Linux 内核只使用事件的到达时间来预估熵的量。它通过多项式插值,某种模型,来预估实际的到达时间有多“出乎意料”。这种多项式插值的方法到底是不是好的预估熵量的方法本身就是个问题。同时硬件情况会不会以某种特定的方式影响到达时间也是个问题。而所有硬件的取样率也是个问题,因为这基本上就直接决定了随机数到达时间的颗粒度。
|
||||
> 这是个很粗糙的简化。实际上不仅有一个,而是三个熵池。一个主池,另一个给 `/dev/random`,还有一个给 `/dev/urandom`,后两者依靠从主池里获取熵。这三个池都有各自的熵计数器,但二级池(后两个)的计数器基本都在 0 附近,而“新鲜”的熵总在需要的时候从主池流过来。同时还有好多混合和回流进系统在同时进行。整个过程对于这篇文档来说都过于复杂了,我们跳过。
|
||||
|
||||
Linux 内核只使用事件的到达时间来预估熵的量。根据模型,它通过多项式插值来预估实际的到达时间有多“出乎意料”。这种多项式插值的方法到底是不是好的预估熵量的方法本身就是个问题。同时硬件情况会不会以某种特定的方式影响到达时间也是个问题。而所有硬件的取样率也是个问题,因为这基本上就直接决定了随机数到达时间的颗粒度。
|
||||
|
||||
说到最后,至少现在看来,内核的熵预估还是不错的。这也意味着它比较保守。有些人会具体地讨论它有多好,这都超出我的脑容量了。就算这样,如果你坚持不想在没有足够多的熵的情况下吐出随机数,那你看到这里可能还会有一丝紧张。我睡的就很香了,因为我不关心熵预估什么的。
|
||||
|
||||
最后强调一下终点:`/dev/random` 和 `/dev/urandom` 都是被同一个 CSPRNG 喂的输入。只有它们在用完各自熵池(根据某种预估标准)的时候,它们的行为会不同:`/dev/random` 阻塞,`/dev/urandom` 不阻塞。
|
||||
最后要明确一下:`/dev/random` 和 `/dev/urandom` 都是被同一个 CSPRNG 饲喂的。只有它们在用完各自熵池(根据某种预估标准)的时候,它们的行为会不同:`/dev/random` 阻塞,`/dev/urandom` 不阻塞。
|
||||
|
||||
##### Linux 4.8 以后
|
||||
|
||||
在 Linux 4.8 里,`/dev/random` 和 `/dev/urandom` 的等价性被放弃了。现在 `/dev/urandom` 的输出不来自于熵池,而是直接从 CSPRNG 来。
|
||||
|
||||
![image: actual structure of the kernel's random number generator from Linux 4.8 onward][3]
|
||||
|
||||
*我们很快会理解*为什么这不是一个安全问题。
|
||||
在 Linux 4.8 里,`/dev/random` 和 `/dev/urandom` 的等价性被放弃了。现在 `/dev/urandom` 的输出不来自于熵池,而是直接从 CSPRNG 来。
|
||||
|
||||
*我们很快会理解*为什么这不是一个安全问题。(参见:“CSPRNG 没问题”一节)
|
||||
|
||||
### 阻塞有什么问题?
|
||||
|
||||
@ -149,7 +149,7 @@ Linux 内核只使用事件的到达时间来预估熵的量。它通过多项
|
||||
|
||||
这些都是问题。阻塞本质上会降低可用性。换而言之你的系统不干你让它干的事情。不用我说,这是不好的。要是它不干活你干嘛搭建它呢?
|
||||
|
||||
> 我在工厂自动化里做过和安全相关的系统。猜猜看安全系统失效的主要原因是什么?被错误操作。就这么简单。很多安全措施的流程让工人恼火了。比如时间太长,或者太不方便。你要知道人很会找捷径来“解决”问题。
|
||||
> 我在工厂自动化里做过和安全相关的系统。猜猜看安全系统失效的主要原因是什么?操作问题。就这么简单。很多安全措施的流程让工人恼火了。比如时间太长,或者太不方便。你要知道人很会找捷径来“解决”问题。
|
||||
|
||||
但其实有个更深刻的问题:人们不喜欢被打断。它们会找一些绕过的方法,把一些诡异的东西接在一起仅仅因为这样能用。一般人根本不知道什么密码学什么乱七八糟的,至少正常的人是这样吧。
|
||||
|
||||
@ -157,23 +157,23 @@ Linux 内核只使用事件的到达时间来预估熵的量。它通过多项
|
||||
|
||||
到头来如果东西太难用的话,你的用户就会被迫开始做一些降低系统安全性的事情——你甚至不知道它们会做些什么。
|
||||
|
||||
我们很容易会忽视可用性之类的重要性。毕竟安全第一对吧?所以比起牺牲安全,不可用,难用,不方便都是次要的?
|
||||
我们很容易会忽视可用性之类的重要性。毕竟安全第一对吧?所以比起牺牲安全,不可用、难用、不方便都是次要的?
|
||||
|
||||
这种二元对立的想法是错的。阻塞不一定就安全了。正如我们看到的,`/dev/urandom` 直接从 CSPRNG 里给你一样好的随机数。用它不好吗!
|
||||
|
||||
### CSPRNG 没问题
|
||||
|
||||
现在情况听上去很沧桑。如果连高质量的 `/dev/random` 都是从一个 CSPRNG 里来的,我们怎么敢在高安全性的需求上使用它呢?
|
||||
现在情况听上去很惨淡。如果连高质量的 `/dev/random` 都是从一个 CSPRNG 里来的,我们怎么敢在高安全性的需求上使用它呢?
|
||||
|
||||
实际上,“看上去随机”是现存大多数密码学基础组件的基本要求。如果你观察一个密码学哈希的输出,它一定得和随机的字符串不可区分,密码学家才会认可这个算法。如果你生成一个块加密,它的输出(在你不知道密钥的情况下)也必须和随机数据不可区分才行。
|
||||
实际上,“看上去随机”是现存大多数密码学基础组件的基本要求。如果你观察一个密码学哈希的输出,它一定得和随机的字符串不可区分,密码学家才会认可这个算法。如果你生成一个分组加密,它的输出(在你不知道密钥的情况下)也必须和随机数据不可区分才行。
|
||||
|
||||
如果任何人能比暴力穷举要更有效地破解一个加密,比如它利用了某些 CSPRNG 伪随机的弱点,那这就又是老一套了:一切都废了,也别谈后面的了。块加密、哈希,一切都是基于某个数学算法,比如 CSPRNG。所以别害怕,到头来都一样。
|
||||
如果任何人能比暴力穷举要更有效地破解一个加密,比如它利用了某些 CSPRNG 伪随机的弱点,那这就又是老一套了:一切都废了,也别谈后面的了。分组加密、哈希,一切都是基于某个数学算法,比如 CSPRNG。所以别害怕,到头来都一样。
|
||||
|
||||
### 那熵池快空了的情况呢?
|
||||
|
||||
毫无影响。
|
||||
|
||||
加密算法的根基建立在攻击者不能预测输出上,只要最一开始有足够的随机性(熵)就行了。一般的下限是 256 位,不需要更多了。
|
||||
加密算法的根基建立在攻击者不能预测输出上,只要最一开始有足够的随机性(熵)就行了。“足够”的下限可以是 256 位,不需要更多了。
|
||||
|
||||
介于我们一直在很随意的使用“熵”这个概念,我用“位”来量化随机性希望读者不要太在意细节。像我们之前讨论的那样,内核的随机数生成器甚至没法精确地知道进入系统的熵的量。只有一个预估。而且这个预估的准确性到底怎么样也没人知道。
|
||||
|
||||
@ -211,7 +211,7 @@ Linux 内核只使用事件的到达时间来预估熵的量。它通过多项
|
||||
|
||||
我们在回到 man 页面说:“使用 `/dev/random`”。我们已经知道了,虽然 `/dev/urandom` 不阻塞,但是它的随机数和 `/dev/random` 都是从同一个 CSPRNG 里来的。
|
||||
|
||||
如果你真的需要信息论理论上安全的随机数(你不需要的,相信我),那才有可能成为唯一一个你需要等足够熵进入 CSPRNG 的理由。而且你也不能用 `/dev/random`。
|
||||
如果你真的需要信息论安全性的随机数(你不需要的,相信我),那才有可能成为唯一一个你需要等足够熵进入 CSPRNG 的理由。而且你也不能用 `/dev/random`。
|
||||
|
||||
man 页面有毒,就这样。但至少它还稍稍挽回了一下自己:
|
||||
|
||||
@ -227,7 +227,7 @@ man 页面有毒,就这样。但至少它还稍稍挽回了一下自己:
|
||||
|
||||
### 正道
|
||||
|
||||
本篇文章里的观点显然在互联网上是“小众”的。但如果问问一个真正的密码学家,你很难找到一个认同阻塞 `/dev/random` 的人。
|
||||
本篇文章里的观点显然在互联网上是“小众”的。但如果问一个真正的密码学家,你很难找到一个认同阻塞 `/dev/random` 的人。
|
||||
|
||||
比如我们看看 [Daniel Bernstein][5](即著名的 djb)的看法:
|
||||
|
||||
@ -238,8 +238,6 @@ man 页面有毒,就这样。但至少它还稍稍挽回了一下自己:
|
||||
>
|
||||
> 对密码学家来说这甚至都不好笑了
|
||||
|
||||
|
||||
|
||||
或者 [Thomas Pornin][6] 的看法,他也是我在 stackexchange 上见过最乐于助人的一位:
|
||||
|
||||
> 简单来说,是的。展开说,答案还是一样。`/dev/urandom` 生成的数据可以说和真随机完全无法区分,至少在现有科技水平下。使用比 `/dev/urandom` “更好的“随机性毫无意义,除非你在使用极为罕见的“信息论安全”的加密算法。这肯定不是你的情况,不然你早就说了。
|
||||
@ -260,13 +258,13 @@ Linux 的 `/dev/urandom` 会很乐意给你吐点不怎么随机的随机数,
|
||||
|
||||
FreeBSD 的行为更正确点:`/dev/random` 和 `/dev/urandom` 是一样的,在系统启动的时候 `/dev/random` 会阻塞到有足够的熵为止,然后它们都再也不阻塞了。
|
||||
|
||||
> 与此同时 Linux 实行了一个新的<ruby>系统调用<rt>syscall</rt></ruby>,最早由 OpenBSD 引入叫 `getentrypy(2)`,在 Linux 下这个叫 `getrandom(2)`。这个系统调用有着上述正确的行为:阻塞到有足够的熵为止,然后再也不阻塞了。当然,这是个系统调用,而不是一个字节设备(LCTT 译注:指不在 `/dev/` 下),所以它在 shell 或者别的脚本语言里没那么容易获取。这个系统调用 自 Linux 3.17 起存在。
|
||||
> 与此同时 Linux 实行了一个新的<ruby>系统调用<rt>syscall</rt></ruby>,最早由 OpenBSD 引入叫 `getentrypy(2)`,在 Linux 下这个叫 `getrandom(2)`。这个系统调用有着上述正确的行为:阻塞到有足够的熵为止,然后再也不阻塞了。当然,这是个系统调用,而不是一个字节设备(LCTT 译注:不在 `/dev/` 下),所以它在 shell 或者别的脚本语言里没那么容易获取。这个系统调用 自 Linux 3.17 起存在。
|
||||
|
||||
在 Linux 上其实这个问题不太大,因为 Linux 发行版会在启动的过程中储蓄一点随机数(这发生在已经有一些熵之后,因为启动程序不会在按下电源的一瞬间就开始运行)到一个种子文件中,以便系统下次启动的时候读取。所以每次启动的时候系统都会从上一次会话里带一点随机性过来。
|
||||
在 Linux 上其实这个问题不太大,因为 Linux 发行版会在启动的过程中保存一点随机数(这发生在已经有一些熵之后,因为启动程序不会在按下电源的一瞬间就开始运行)到一个种子文件中,以便系统下次启动的时候读取。所以每次启动的时候系统都会从上一次会话里带一点随机性过来。
|
||||
|
||||
显然这比不上在关机脚本里写入一些随机种子,因为这样的显然就有更多熵可以操作了。但这样做显而易见的好处就是它不用关心系统是不是正确关机了,比如可能你系统崩溃了。
|
||||
|
||||
而且这种做法在你真正第一次启动系统的时候也没法帮你随机,不过好在系统安装器一般会写一个种子文件,所以基本上问题不大。
|
||||
而且这种做法在你真正第一次启动系统的时候也没法帮你随机,不过好在 Linux 系统安装程序一般会保存一个种子文件,所以基本上问题不大。
|
||||
|
||||
虚拟机是另外一层问题。因为用户喜欢克隆它们,或者恢复到某个之前的状态。这种情况下那个种子文件就帮不到你了。
|
||||
|
@ -1,37 +1,39 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10850-1.html)
|
||||
[#]: subject: (Build a game framework with Python using the module Pygame)
|
||||
[#]: via: (https://opensource.com/article/17/12/game-framework-python)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
使用 Python 和 Pygame 模块构建一个游戏框架
|
||||
======
|
||||
这系列的第一篇通过创建一个简单的骰子游戏来探究 Python。现在是来从零制作你自己的游戏的时间。
|
||||
|
||||
> 这系列的第一篇通过创建一个简单的骰子游戏来探究 Python。现在是来从零制作你自己的游戏的时间。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/python2-header.png?itok=tEvOVo4A)
|
||||
|
||||
在我的 [这系列的第一篇文章][1] 中, 我已经讲解如何使用 Python 创建一个简单的,基于文本的骰子游戏。这次,我将展示如何使用 Python 和 Pygame 模块来创建一个图形化游戏。它将占用一些文章来得到一个确实完成一些东西的游戏,但是在这系列的结尾,你将有一个更好的理解,如何查找和学习新的 Python 模块和如何从其基础上构建一个应用程序。
|
||||
在我的[这系列的第一篇文章][1] 中, 我已经讲解如何使用 Python 创建一个简单的、基于文本的骰子游戏。这次,我将展示如何使用 Python 模块 Pygame 来创建一个图形化游戏。它将需要几篇文章才能来得到一个确实做成一些东西的游戏,但是到这系列的结尾,你将更好地理解如何查找和学习新的 Python 模块和如何从其基础上构建一个应用程序。
|
||||
|
||||
在开始前,你必须安装 [Pygame][2]。
|
||||
|
||||
### 安装新的 Python 模块
|
||||
|
||||
这里有一些方法来安装 Python 模块,但是最通用的两个是:
|
||||
有几种方法来安装 Python 模块,但是最通用的两个是:
|
||||
|
||||
* 从你的发行版的软件存储库
|
||||
* 使用 Python 的软件包管理器,pip
|
||||
* 使用 Python 的软件包管理器 `pip`
|
||||
|
||||
两个方法都工作很好,并且每一个都有它自己的一套优势。如果你是在 Linux 或 BSD 上开发,促使你的发行版的软件存储库确保自动及时更新。
|
||||
两个方法都工作的很好,并且每一个都有它自己的一套优势。如果你是在 Linux 或 BSD 上开发,可以利用你的发行版的软件存储库来自动和及时地更新。
|
||||
|
||||
然而,使用 Python 的内置软件包管理器给予你控制更新模块时间的能力。而且,它不是明确指定操作系统的,意味着,即使当你不是在你常用的开发机器上时,你也可以使用它。pip 的其它的优势是允许模块局部安装,如果你没有一台正在使用的计算机的权限,它是有用的。
|
||||
然而,使用 Python 的内置软件包管理器可以给予你控制更新模块时间的能力。而且,它不是特定于操作系统的,这意味着,即使当你不是在你常用的开发机器上时,你也可以使用它。`pip` 的其它的优势是允许本地安装模块,如果你没有正在使用的计算机的管理权限,这是有用的。
|
||||
|
||||
### 使用 pip
|
||||
|
||||
如果 Python 和 Python3 都安装在你的系统上,你想使用的命令很可能是 `pip3`,它区分来自Python 2.x 的 `pip` 的命令。如果你不确定,先尝试 `pip3`。
|
||||
如果 Python 和 Python3 都安装在你的系统上,你想使用的命令很可能是 `pip3`,它用来区分 Python 2.x 的 `pip` 的命令。如果你不确定,先尝试 `pip3`。
|
||||
|
||||
`pip` 命令有些像大多数 Linux 软件包管理器的工作。你可以使用 `search` 搜索 Pythin 模块,然后使用 `install` 安装它们。如果你没有你正在使用的计算机的权限来安装软件,你可以使用 `--user` 选项来仅仅安装模块到你的 home 目录。
|
||||
`pip` 命令有些像大多数 Linux 软件包管理器一样工作。你可以使用 `search` 搜索 Python 模块,然后使用 `install` 安装它们。如果你没有你正在使用的计算机的管理权限来安装软件,你可以使用 `--user` 选项来仅仅安装模块到你的家目录。
|
||||
|
||||
```
|
||||
$ pip3 search pygame
|
||||
@ -44,11 +46,11 @@ pygame_cffi (0.2.1) - A cffi-based SDL wrapper that copies the
|
||||
$ pip3 install Pygame --user
|
||||
```
|
||||
|
||||
Pygame 是一个 Python 模块,这意味着它仅仅是一套可以被使用在你的 Python 程序中库。换句话说,它不是一个你启动的程序,像 [IDLE][3] 或 [Ninja-IDE][4] 一样。
|
||||
Pygame 是一个 Python 模块,这意味着它仅仅是一套可以使用在你的 Python 程序中的库。换句话说,它不是一个像 [IDLE][3] 或 [Ninja-IDE][4] 一样可以让你启动的程序。
|
||||
|
||||
### Pygame 新手入门
|
||||
|
||||
一个电子游戏需要一个故事背景;一个发生的地点。在 Python 中,有两种不同的方法来创建你的故事背景:
|
||||
一个电子游戏需要一个背景设定:故事发生的地点。在 Python 中,有两种不同的方法来创建你的故事背景:
|
||||
|
||||
* 设置一种背景颜色
|
||||
* 设置一张背景图片
|
||||
@ -57,15 +59,15 @@ Pygame 是一个 Python 模块,这意味着它仅仅是一套可以被使用
|
||||
|
||||
### 设置你的 Pygame 脚本
|
||||
|
||||
为了开始一个新的 Pygame 脚本,在计算机上创建一个文件夹。游戏的全部文件被放在这个目录中。在工程文件夹内部保持所需要的所有的文件来运行游戏是极其重要的。
|
||||
要开始一个新的 Pygame 工程,先在计算机上创建一个文件夹。游戏的全部文件被放在这个目录中。在你的工程文件夹内部保持所需要的所有的文件来运行游戏是极其重要的。
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/project.jpg)
|
||||
|
||||
一个 Python 脚本以文件类型,你的姓名,和你想使用的协议开始。使用一个开放源码协议,以便你的朋友可以改善你的游戏并与你一起分享他们的更改:
|
||||
一个 Python 脚本以文件类型、你的姓名,和你想使用的许可证开始。使用一个开放源码许可证,以便你的朋友可以改善你的游戏并与你一起分享他们的更改:
|
||||
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
# Seth Kenlon 编写
|
||||
# by Seth Kenlon
|
||||
|
||||
## GPLv3
|
||||
# This program is free software: you can redistribute it and/or
|
||||
@ -75,14 +77,14 @@ Pygame 是一个 Python 模块,这意味着它仅仅是一套可以被使用
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
```
|
||||
|
||||
然后,你告诉 Python 你想使用的模块。一些模块是常见的 Python 库,当然,你想包括一个你刚刚安装的,Pygame 。
|
||||
然后,你告诉 Python 你想使用的模块。一些模块是常见的 Python 库,当然,你想包括一个你刚刚安装的 Pygame 模块。
|
||||
|
||||
```
|
||||
import pygame # 加载 pygame 关键字
|
||||
@ -90,7 +92,7 @@ import sys # 让 python 使用你的文件系统
|
||||
import os # 帮助 python 识别你的操作系统
|
||||
```
|
||||
|
||||
由于你将用这个脚本文件工作很多,在文件中制作成段落是有帮助的,以便你知道在哪里放原料。使用语句块注释来做这些,这些注释仅在看你的源文件代码时是可见的。在你的代码中创建三个语句块。
|
||||
由于你将用这个脚本文件做很多工作,在文件中分成段落是有帮助的,以便你知道在哪里放代码。你可以使用块注释来做这些,这些注释仅在看你的源文件代码时是可见的。在你的代码中创建三个块。
|
||||
|
||||
```
|
||||
'''
|
||||
@ -114,7 +116,7 @@ Main Loop
|
||||
|
||||
接下来,为你的游戏设置窗口大小。注意,不是每一个人都有大计算机屏幕,所以,最好使用一个适合大多数人的计算机的屏幕大小。
|
||||
|
||||
这里有一个方法来切换全屏模式,很多现代电子游戏做的方法,但是,由于你刚刚开始,保存它简单和仅设置一个大小。
|
||||
这里有一个方法来切换全屏模式,很多现代电子游戏都会这样做,但是,由于你刚刚开始,简单起见仅设置一个大小即可。
|
||||
|
||||
```
|
||||
'''
|
||||
@ -124,7 +126,7 @@ worldx = 960
|
||||
worldy = 720
|
||||
```
|
||||
|
||||
在一个脚本中使用 Pygame 引擎前,你需要一些基本的设置。你必需设置帧频,启动它的内部时钟,然后开始 (`init`) Pygame 。
|
||||
在脚本中使用 Pygame 引擎前,你需要一些基本的设置。你必须设置帧频,启动它的内部时钟,然后开始 (`init`)Pygame 。
|
||||
|
||||
```
|
||||
fps = 40 # 帧频
|
||||
@ -137,17 +139,15 @@ pygame.init()
|
||||
|
||||
### 设置背景
|
||||
|
||||
在你继续前,打开一个图形应用程序,并为你的游戏世界创建一个背景。在你的工程目录中的 `images` 文件夹内部保存它为 `stage.png` 。
|
||||
在你继续前,打开一个图形应用程序,为你的游戏世界创建一个背景。在你的工程目录中的 `images` 文件夹内部保存它为 `stage.png` 。
|
||||
|
||||
这里有一些你可以使用的自由图形应用程序。
|
||||
|
||||
* [Krita][5] 是一个专业级绘图原料模拟器,它可以被用于创建漂亮的图片。如果你对电子游戏创建艺术作品非常感兴趣,你甚至可以购买一系列的[游戏艺术作品教程][6].
|
||||
* [Pinta][7] 是一个基本的,易于学习的绘图应用程序。
|
||||
* [Inkscape][8] 是一个矢量图形应用程序。使用它来绘制形状,线,样条曲线,和 Bézier 曲线。
|
||||
* [Krita][5] 是一个专业级绘图素材模拟器,它可以被用于创建漂亮的图片。如果你对创建电子游戏艺术作品非常感兴趣,你甚至可以购买一系列的[游戏艺术作品教程][6]。
|
||||
* [Pinta][7] 是一个基本的,易于学习的绘图应用程序。
|
||||
* [Inkscape][8] 是一个矢量图形应用程序。使用它来绘制形状、线、样条曲线和贝塞尔曲线。
|
||||
|
||||
|
||||
|
||||
你的图像不必很复杂,你可以以后回去更改它。一旦你有它,在你文件的 setup 部分添加这些代码:
|
||||
你的图像不必很复杂,你可以以后回去更改它。一旦有了它,在你文件的 Setup 部分添加这些代码:
|
||||
|
||||
```
|
||||
world = pygame.display.set_mode([worldx,worldy])
|
||||
@ -155,13 +155,13 @@ backdrop = pygame.image.load(os.path.join('images','stage.png').convert())
|
||||
backdropbox = world.get_rect()
|
||||
```
|
||||
|
||||
如果你仅仅用一种颜色来填充你的游戏的背景,你需要做的全部是:
|
||||
如果你仅仅用一种颜色来填充你的游戏的背景,你需要做的就是:
|
||||
|
||||
```
|
||||
world = pygame.display.set_mode([worldx,worldy])
|
||||
```
|
||||
|
||||
你也必需定义一个来使用的颜色。在你的 setup 部分,使用红,绿,蓝 (RGB) 的值来创建一些颜色的定义。
|
||||
你也必须定义颜色以使用。在你的 Setup 部分,使用红、绿、蓝 (RGB) 的值来创建一些颜色的定义。
|
||||
|
||||
```
|
||||
'''
|
||||
@ -173,13 +173,13 @@ BLACK = (23,23,23 )
|
||||
WHITE = (254,254,254)
|
||||
```
|
||||
|
||||
在这点上,你能理论上启动你的游戏。问题是,它可能仅持续一毫秒。
|
||||
至此,你理论上可以启动你的游戏了。问题是,它可能仅持续了一毫秒。
|
||||
|
||||
为证明这一点,保存你的文件为 `your-name_game.py` (用你真实的名称替换 `your-name` )。然后启动你的游戏。
|
||||
为证明这一点,保存你的文件为 `your-name_game.py`(用你真实的名称替换 `your-name`)。然后启动你的游戏。
|
||||
|
||||
如果你正在使用 IDLE ,通过选择来自 Run 菜单的 `Run Module` 来运行你的游戏。
|
||||
如果你正在使用 IDLE,通过选择来自 “Run” 菜单的 “Run Module” 来运行你的游戏。
|
||||
|
||||
如果你正在使用 Ninja ,在左侧按钮条中单击 `Run file` 按钮。
|
||||
如果你正在使用 Ninja,在左侧按钮条中单击 “Run file” 按钮。
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/ninja_run_0.png)
|
||||
|
||||
@ -189,27 +189,27 @@ WHITE = (254,254,254)
|
||||
$ python3 ./your-name_game.py
|
||||
```
|
||||
|
||||
如果你正在使用 Windows ,使用这命令:
|
||||
如果你正在使用 Windows,使用这命令:
|
||||
|
||||
```
|
||||
py.exe your-name_game.py
|
||||
```
|
||||
|
||||
你启动它,不过不要期望很多,因为你的游戏现在仅仅持续几毫秒。你可以在下一部分中修复它。
|
||||
启动它,不过不要期望很多,因为你的游戏现在仅仅持续几毫秒。你可以在下一部分中修复它。
|
||||
|
||||
### 循环
|
||||
|
||||
除非另有说明,一个 Python 脚本运行一次并仅一次。近来计算机的运行速度是非常快的,所以你的 Python 脚本运行时间少于1秒钟。
|
||||
除非另有说明,一个 Python 脚本运行一次并仅一次。近来计算机的运行速度是非常快的,所以你的 Python 脚本运行时间会少于 1 秒钟。
|
||||
|
||||
为强制你的游戏来处于足够长的打开和活跃状态来让人看到它(更不要说玩它),使用一个 `while` 循环。为使你的游戏保存打开,你可以设置一个变量为一些值,然后告诉一个 `while` 循环只要变量保持未更改则一直保存循环。
|
||||
|
||||
这经常被称为一个"主循环",你可以使用术语 `main` 作为你的变量。在你的 setup 部分的任意位置添加这些代码:
|
||||
这经常被称为一个“主循环”,你可以使用术语 `main` 作为你的变量。在你的 Setup 部分的任意位置添加代码:
|
||||
|
||||
```
|
||||
main = True
|
||||
```
|
||||
|
||||
在主循环期间,使用 Pygame 关键字来检查是否在键盘上的按键已经被按下或释放。添加这些代码到你的主循环部分:
|
||||
在主循环期间,使用 Pygame 关键字来检查键盘上的按键是否已经被按下或释放。添加这些代码到你的主循环部分:
|
||||
|
||||
```
|
||||
'''
|
||||
@ -228,7 +228,7 @@ while main == True:
|
||||
main = False
|
||||
```
|
||||
|
||||
也在你的循环中,刷新你世界的背景。
|
||||
也是在你的循环中,刷新你世界的背景。
|
||||
|
||||
如果你使用一个图片作为背景:
|
||||
|
||||
@ -242,33 +242,33 @@ world.blit(backdrop, backdropbox)
|
||||
world.fill(BLUE)
|
||||
```
|
||||
|
||||
最后,告诉 Pygame 来刷新在屏幕上的所有内容并推进游戏的内部时钟。
|
||||
最后,告诉 Pygame 来重新刷新屏幕上的所有内容,并推进游戏的内部时钟。
|
||||
|
||||
```
|
||||
pygame.display.flip()
|
||||
clock.tick(fps)
|
||||
```
|
||||
|
||||
保存你的文件,再次运行它来查看曾经创建的最无趣的游戏。
|
||||
保存你的文件,再次运行它来查看你曾经创建的最无趣的游戏。
|
||||
|
||||
退出游戏,在你的键盘上按 `q` 键。
|
||||
|
||||
在这系列的 [下一篇文章][9] 中,我将向你演示,如何加强你当前空的游戏世界,所以,继续学习并创建一些将要使用的图形!
|
||||
在这系列的 [下一篇文章][9] 中,我将向你演示,如何加强你当前空空如也的游戏世界,所以,继续学习并创建一些将要使用的图形!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
通过: https://opensource.com/article/17/12/game-framework-python
|
||||
via: https://opensource.com/article/17/12/game-framework-python
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/article/17/10/python-101
|
||||
[1]: https://linux.cn/article-9071-1.html
|
||||
[2]: http://www.pygame.org/wiki/about
|
||||
[3]: https://en.wikipedia.org/wiki/IDLE
|
||||
[4]: http://ninja-ide.org/
|
@ -0,0 +1,163 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (cycoe)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10858-1.html)
|
||||
[#]: subject: (How to add a player to your Python game)
|
||||
[#]: via: (https://opensource.com/article/17/12/game-python-add-a-player)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
如何在你的 Python 游戏中添加一个玩家
|
||||
======
|
||||
> 这是用 Python 从头开始构建游戏的系列文章的第三部分。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/python3-game.png?itok=jG9UdwC3)
|
||||
|
||||
在 [这个系列的第一篇文章][1] 中,我解释了如何使用 Python 创建一个简单的基于文本的骰子游戏。在第二部分中,我向你们展示了如何从头开始构建游戏,即从 [创建游戏的环境][2] 开始。但是每个游戏都需要一名玩家,并且每个玩家都需要一个可操控的角色,这也就是我们接下来要在这个系列的第三部分中需要做的。
|
||||
|
||||
在 Pygame 中,玩家操控的图标或者化身被称作<ruby>妖精<rt>sprite</rt></ruby>。如果你现在还没有任何可用于玩家妖精的图像,你可以使用 [Krita][3] 或 [Inkscape][4] 来自己创建一些图像。如果你对自己的艺术细胞缺乏自信,你也可以在 [OpenClipArt.org][5] 或 [OpenGameArt.org][6] 搜索一些现成的图像。如果你还未按照上一篇文章所说的单独创建一个 `images` 文件夹,那么你需要在你的 Python 项目目录中创建它。将你想要在游戏中使用的图片都放 `images` 文件夹中。
|
||||
|
||||
为了使你的游戏真正的刺激,你应该为你的英雄使用一张动态的妖精图片。这意味着你需要绘制更多的素材,并且它们要大不相同。最常见的动画就是走路循环,通过一系列的图像让你的妖精看起来像是在走路。走路循环最快捷粗糙的版本需要四张图像。
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/walk-cycle-poses.jpg)
|
||||
|
||||
注意:这篇文章中的代码示例同时兼容静止的和动态的玩家妖精。
|
||||
|
||||
将你的玩家妖精命名为 `hero.png`。如果你正在创建一个动态的妖精,则需要在名字后面加上一个数字,从 `hero1.png` 开始。
|
||||
|
||||
### 创建一个 Python 类
|
||||
|
||||
在 Python 中,当你在创建一个你想要显示在屏幕上的对象时,你需要创建一个类。
|
||||
|
||||
在你的 Python 脚本靠近顶端的位置,加入如下代码来创建一个玩家。在以下的代码示例中,前三行已经在你正在处理的 Python 脚本中:
|
||||
|
||||
```
|
||||
import pygame
|
||||
import sys
|
||||
import os # 以下是新代码
|
||||
|
||||
class Player(pygame.sprite.Sprite):
|
||||
'''
|
||||
生成一个玩家
|
||||
'''
|
||||
def __init__(self):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.images = []
|
||||
img = pygame.image.load(os.path.join('images','hero.png')).convert()
|
||||
self.images.append(img)
|
||||
self.image = self.images[0]
|
||||
self.rect = self.image.get_rect()
|
||||
```
|
||||
|
||||
如果你的可操控角色拥有一个走路循环,在 `images` 文件夹中将对应图片保存为 `hero1.png` 到 `hero4.png` 的独立文件。
|
||||
|
||||
使用一个循环来告诉 Python 遍历每个文件。
|
||||
|
||||
```
|
||||
'''
|
||||
对象
|
||||
'''
|
||||
|
||||
class Player(pygame.sprite.Sprite):
|
||||
'''
|
||||
生成一个玩家
|
||||
'''
|
||||
def __init__(self):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.images = []
|
||||
for i in range(1,5):
|
||||
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
|
||||
self.images.append(img)
|
||||
self.image = self.images[0]
|
||||
self.rect = self.image.get_rect()
|
||||
```
|
||||
|
||||
### 将玩家带入游戏世界
|
||||
|
||||
现在已经创建好了一个 Player 类,你需要使用它在你的游戏世界中生成一个玩家妖精。如果你不调用 Player 类,那它永远不会起作用,(游戏世界中)也就不会有玩家。你可以通过立马运行你的游戏来验证一下。游戏会像上一篇文章末尾看到的那样运行,并得到明确的结果:一个空荡荡的游戏世界。
|
||||
|
||||
为了将一个玩家妖精带到你的游戏世界,你必须通过调用 Player 类来生成一个妖精,并将它加入到 Pygame 的妖精组中。在如下的代码示例中,前三行是已经存在的代码,你需要在其后添加代码:
|
||||
|
||||
```
|
||||
world = pygame.display.set_mode([worldx,worldy])
|
||||
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert()
|
||||
backdropbox = screen.get_rect()
|
||||
|
||||
# 以下是新代码
|
||||
|
||||
player = Player() # 生成玩家
|
||||
player.rect.x = 0 # 移动 x 坐标
|
||||
player.rect.y = 0 # 移动 y 坐标
|
||||
player_list = pygame.sprite.Group()
|
||||
player_list.add(player)
|
||||
```
|
||||
|
||||
尝试启动你的游戏来看看发生了什么。高能预警:它不会像你预期的那样工作,当你启动你的项目,玩家妖精没有出现。事实上它生成了,只不过只出现了一毫秒。你要如何修复一个只出现了一毫秒的东西呢?你可能回想起上一篇文章中,你需要在主循环中添加一些东西。为了使玩家的存在时间超过一毫秒,你需要告诉 Python 在每次循环中都绘制一次。
|
||||
|
||||
将你的循环底部的语句更改如下:
|
||||
|
||||
```
|
||||
world.blit(backdrop, backdropbox)
|
||||
player_list.draw(screen) # 绘制玩家
|
||||
pygame.display.flip()
|
||||
clock.tick(fps)
|
||||
```
|
||||
|
||||
现在启动你的游戏,你的玩家出现了!
|
||||
|
||||
### 设置 alpha 通道
|
||||
|
||||
根据你如何创建你的玩家妖精,在它周围可能会有一个色块。你所看到的是 alpha 通道应该占据的空间。它本来是不可见的“颜色”,但 Python 现在还不知道要使它不可见。那么你所看到的,是围绕在妖精周围的边界区(或现代游戏术语中的“<ruby>命中区<rt>hit box</rt></ruby>”)内的空间。
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/greenscreen.jpg)
|
||||
|
||||
你可以通过设置一个 alpha 通道和 RGB 值来告诉 Python 使哪种颜色不可见。如果你不知道你使用 alpha 通道的图像的 RGB 值,你可以使用 Krita 或 Inkscape 打开它,并使用一种独特的颜色,比如 `#00ff00`(差不多是“绿屏绿”)来填充图像周围的空白区域。记下颜色对应的十六进制值(此处为 `#00ff00`,绿屏绿)并将其作为 alpha 通道用于你的 Python 脚本。
|
||||
|
||||
使用 alpha 通道需要在你的妖精生成相关代码中添加如下两行。类似第一行的代码已经存在于你的脚本中,你只需要添加另外两行:
|
||||
|
||||
```
|
||||
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
|
||||
img.convert_alpha() # 优化 alpha
|
||||
img.set_colorkey(ALPHA) # 设置 alpha
|
||||
```
|
||||
|
||||
除非你告诉它,否则 Python 不知道将哪种颜色作为 alpha 通道。在你代码的设置相关区域,添加一些颜色定义。将如下的变量定义添加于你的设置相关区域的任意位置:
|
||||
|
||||
```
|
||||
ALPHA = (0, 255, 0)
|
||||
```
|
||||
|
||||
在以上示例代码中,`0,255,0` 被我们使用,它在 RGB 中所代表的值与 `#00ff00` 在十六进制中所代表的值相同。你可以通过一个优秀的图像应用程序,如 [GIMP][7]、Krita 或 Inkscape,来获取所有这些颜色值。或者,你可以使用一个优秀的系统级颜色选择器,如 [KColorChooser][8],来检测颜色。
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/kcolor.png)
|
||||
|
||||
如果你的图像应用程序将你的妖精背景渲染成了其他的值,你可以按需调整 `ALPHA` 变量的值。不论你将 alpha 设为多少,最后它都将“不可见”。RGB 颜色值是非常严格的,因此如果你需要将 alpha 设为 000,但你又想将 000 用于你图像中的黑线,你只需要将图像中线的颜色设为 111。这样一来,(图像中的黑线)就足够接近黑色,但除了电脑以外没有人能看出区别。
|
||||
|
||||
运行你的游戏查看结果。
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/alpha.jpg)
|
||||
|
||||
在 [这个系列的第四篇文章][9] 中,我会向你们展示如何使你的妖精动起来。多么的激动人心啊!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/17/12/game-python-add-a-player
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[cycoe](https://github.com/cycoe)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://linux.cn/article-9071-1.html
|
||||
[2]: https://linux.cn/article-10850-1.html
|
||||
[3]: http://krita.org
|
||||
[4]: http://inkscape.org
|
||||
[5]: http://openclipart.org
|
||||
[6]: https://opengameart.org/
|
||||
[7]: http://gimp.org
|
||||
[8]: https://github.com/KDE/kcolorchooser
|
||||
[9]: https://opensource.com/article/17/12/program-game-python-part-4-moving-your-sprite
|
@ -0,0 +1,130 @@
|
||||
DomTerm:一款为 Linux 打造的终端模拟器
|
||||
======
|
||||
> 了解一下 DomTerm,这是一款终端模拟器和复用器,带有 HTML 图形和其它不多见的功能。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/osdc_terminals.png?itok=CfBqYBah)
|
||||
|
||||
[DomTerm][1] 是一款现代化的终端模拟器,它使用浏览器引擎作为 “GUI 工具包”。这就支持了一些相关的特性,例如可嵌入图像和链接、HTML 富文本以及可折叠(显示/隐藏)命令。除此以外,它看起来感觉就像一个功能完整、独立的终端模拟器,有着出色 xterm 兼容性(包括鼠标处理和 24 位色)和恰当的 “装饰” (菜单)。另外它内置支持了会话管理和副窗口(如同 `tmux` 和 `GNU Screen` 中一样)、基本输入编辑(如在 `readline` 中)以及分页(如在 `less` 中)。
|
||||
|
||||
![](https://opensource.com/sites/default/files/u128651/domterm1.png)
|
||||
|
||||
*图 1: DomTerminal 终端模拟器。*
|
||||
|
||||
在以下部分我们将看一看这些特性。我们将假设你已经安装好了 `domterm` (如果你需要获取并构建 Dormterm 请跳到本文最后)。开始之前先让我们概览一下这项技术。
|
||||
|
||||
### 前端 vs. 后端
|
||||
|
||||
DomTerm 大部分是用 JavaScript 写的,它运行在一个浏览器引擎中。它可以是像例如 Chrome 或者 Firefox 一样的桌面浏览器(见图 3),也可以是一个内嵌的浏览器。使用一个通用的网页浏览器没有问题,但是用户体验却不够好(因为菜单是为通用的网页浏览而不是为了终端模拟器所打造),并且其安全模型也会妨碍使用。因此使用内嵌的浏览器更好一些。
|
||||
|
||||
目前以下这些是支持的:
|
||||
|
||||
* qdomterm,使用了 Qt 工具包 和 QtWebEngine
|
||||
* 一个内嵌的 [Electron][2](见图 1)
|
||||
* atom-domterm 以 [Atom 文本编辑器][3](同样基于 Electron)包的形式运行 DomTerm,并和 Atom 面板系统集成在一起(见图 2)
|
||||
* 一个为 JavaFX 的 WebEngine 包装器,这对 Java 编程十分有用(见图 4)
|
||||
* 之前前端使用 [Firefox-XUL][4] 作为首选,但是 Mozilla 已经终止了 XUL
|
||||
|
||||
![在 Atom 编辑器中的 DomTerm 终端面板][6]
|
||||
|
||||
*图 2:在 Atom 编辑器中的 DomTerm 终端面板。*
|
||||
|
||||
目前,Electron 前端可能是最佳选择,紧随其后的是 Qt 前端。如果你使用 Atom,atom-domterm 也工作得相当不错。
|
||||
|
||||
后端服务器是用 C 写的。它管理着伪终端(PTY)和会话。它同样也是一个为前端提供 Javascript 和其它文件的 HTTP 服务器。`domterm` 命令启动终端任务和执行其它请求。如果没有服务器在运行,domterm 就会自己来服务。后端与服务器之间的通讯通常是用 WebSockets(在服务器端是[libwebsockets][8])完成的。然而,JavaFX 的嵌入既不用 Websockets 也不用 DomTerm 服务器。相反 Java 应用直接通过 Java-Javascript 桥接进行通讯。
|
||||
|
||||
### 一个稳健的可兼容 xterm 的终端模拟器
|
||||
|
||||
DomTerm 看上去感觉像一个现代的终端模拟器。它处理鼠标事件、24 位色、Unicode、倍宽字符(CJK)以及输入方式。DomTerm 在 [vttest 测试套件][9] 上工作地十分出色。
|
||||
|
||||
其不同寻常的特性包括:
|
||||
|
||||
**展示/隐藏按钮(“折叠”):** 小三角(如上图 2)是隐藏/展示相应输出的按钮。仅需在[提示符][11]中添加特定的[转义字符][10]就可以创建按钮。
|
||||
|
||||
**对于 readline 和类似输入编辑器的鼠标点击支持:** 如果你点击输入区域(黄色),DomTerm 会向应用发送正确的方向键按键序列。(可以通过提示符中的转义字符启用这一特性,你也可以通过 `Alt+点击` 强制使用。)
|
||||
|
||||
**用 CSS 样式化终端:** 这通常是在 `~/.domterm/settings.ini` 里完成的,保存时会自动重载。例如在图 2 中,设置了终端专用的背景色。
|
||||
|
||||
### 一个更好的 REPL 控制台
|
||||
|
||||
一个经典的终端模拟器基于长方形的字符单元格工作的。这在 REPL(命令行)上没问题,但是并不理想。这里有些通常在终端模拟器中不常见的 REPL 很有用的 DomTerm 特性:
|
||||
|
||||
**一个能“打印”图片、图形、数学公式或者一组可点击的链接的命令:** 应用可以发送包含几乎任何 HTML 的转义字符。(HTML 会被剔除部分,以移除 JavaScript 和其它危险特性。)
|
||||
|
||||
图 3 显示了来自 [gnuplot][12] 会话的一个片段。Gnuplot(2.1 或者跟高版本)支持 DormTerm 作为终端类型。图形输出被转换成 [SVG 图片][13],然后被打印到终端。我的博客帖子[在 DormTerm 上的 Gnuplot 展示][14]在这方面提供了更多信息。
|
||||
|
||||
![](https://opensource.com/sites/default/files/dt-gnuplot.png)
|
||||
|
||||
*图 3:Gnuplot 截图。*
|
||||
|
||||
[Kawa][15] 语言有一个创建并转换[几何图像值][16]的库。如果你将这样的图片值打印到 DomTerm 终端,图片就会被转换成 SVG 形式并嵌入进输出中。
|
||||
|
||||
![](https://opensource.com/sites/default/files/dt-kawa1.png)
|
||||
|
||||
*图 4:Kawa 中可计算的几何形状。*
|
||||
|
||||
**富文本输出:** 有着 HTML 样式的帮助信息更加便于阅读,看上去也更漂亮。图片 1 的下面面板展示 `dormterm help` 的输出。(如果没在 DomTerm 下运行的话输出的是普通文本。)注意自带的分页器中的 `PAUSED` 消息。
|
||||
|
||||
**包括可点击链接的错误消息:** DomTerm 可以识别语法 `filename:line:column` 并将其转化成一个能在可定制文本编辑器中打开文件并定位到行的链接。(这适用于相对路径的文件名,如果你用 `PROMPT_COMMAND` 或类似的跟踪目录。)
|
||||
|
||||
编译器可以侦测到它在 DomTerm 下运行,并直接用转义字符发出文件链接。这比依赖 DomTerm 的样式匹配要稳健得多,因为它可以处理空格和其他字符并且无需依赖目录追踪。在图 4 中,你可以看到来自 [Kawa Compiler][15] 的错误消息。悬停在文件位置上会使其出现下划线,`file:` URL 出现在 `atom-domterm` 消息栏(窗口底部)中。(当不用 atom-domterm 时,这样的消息会在一个浮层的框中显示,如图 1 中所看到的 `PAUSED` 消息所示。)
|
||||
|
||||
点击链接时的动作是可以配置的。默认对于带有 `#position` 后缀的 `file:` 链接的动作是在文本编辑器中打开那个文件。
|
||||
|
||||
**结构化内部表示:**以下内容均以内部节点结构表示:命令、提示符、输入行、正常和错误输出、标签,如果“另存为 HTML”,则保留结构。HTML 文件与 XML 兼容,因此你可以使用 XML 工具搜索或转换输出。命令 `domterm view-saved` 会以一种启用命令折叠(显示/隐藏按钮处于活动状态)和重新调整窗口大小的方式打开保存的 HTML 文件。
|
||||
|
||||
**内建的 Lisp 样式优美打印:** 你可以在输出中包括优美打印指令(比如,grouping),这样断行会根据窗口大小调整而重新计算。查看我的文章 [DomTerm 中的动态优美打印][17]以更深入探讨。
|
||||
|
||||
**基本的内建行编辑**,带着历史记录(像 GNU readline 一样): 这使用浏览器自带的编辑器,因此它有着优秀的鼠标和选择处理机制。你可以在正常字符模式(大多数输入的字符被指接送向进程);或者行模式(通常的字符是直接插入的,而控制字符导致编辑操作,回车键会向进程发送被编辑行)之间转换。默认的是自动模式,根据 PTY 是在原始模式还是终端模式中,DomTerm 在字符模式与行模式间转换。
|
||||
|
||||
**自带的分页器**(类似简化版的 `less`):键盘快捷键控制滚动。在“页模式”中,输出在每个新的屏幕(或者单独的行,如果你想一行行地向前移)后暂停;页模式对于用户输入简单智能,因此(如果你想的话)你无需阻碍交互式程序就可以运行它。
|
||||
|
||||
### 多路复用和会话
|
||||
|
||||
**标签和平铺:** 你不仅可以创建多个终端标签,也可以平铺它们。你可以要么使用鼠标或键盘快捷键来创建或者切换面板和标签。它们可以用鼠标重新排列并调整大小。这是通过 [GoldenLayout][18] JavaScript 库实现的。图 1 展示了一个有着两个面板的窗口。上面的有两个标签,一个运行 [Midnight Commander][20];底下的面板以 HTML 形式展示了 `dormterm help` 输出。然而相反在 Atom 中我们使用其自带的可拖拽的面板和标签。你可以在图 2 中看到这个。
|
||||
|
||||
**分离或重接会话:** 与 `tmux` 和 GNU `screen` 类似,DomTerm 支持会话安排。你甚至可以给同样的会话接上多个窗口或面板。这支持多用户会话分享和远程链接。(为了安全,同一个服务器的所有会话都需要能够读取 Unix 域接口和一个包含随机密钥的本地文件。当我们有了良好、安全的远程链接,这个限制将会有所放松。)
|
||||
|
||||
**domterm 命令** 类似与 `tmux` 和 GNU `screen`,它有多个选项可以用于控制或者打开单个或多个会话的服务器。主要的差别在于,如果它没在 DomTerm 下运行,`dormterm` 命令会创建一个新的顶层窗口,而不是在现有的终端中运行。
|
||||
|
||||
与 `tmux` 和 `git` 类似,`dormterm` 命令有许多子命令。一些子命令创建窗口或者会话。另一些(例如“打印”一张图片)仅在现有的 DormTerm 会话下起作用。
|
||||
|
||||
命令 `domterm browse` 打开一个窗口或者面板以浏览一个指定的 URL,例如浏览文档的时候。
|
||||
|
||||
### 获取并安装 DomTerm
|
||||
|
||||
DomTerm 可以从其 [Github 仓库][21]获取。目前没有提前构建好的包,但是有[详细指导][22]。所有的前提条件在 Fedora 27 上都有,这使得其特别容易被搭建。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/18/1/introduction-domterm-terminal-emulator
|
||||
|
||||
作者:[Per Bothner][a]
|
||||
译者:[tomjlw](https://github.com/tomjlw)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://opensource.com/users/perbothner
|
||||
[1]:http://domterm.org/
|
||||
[2]:https://electronjs.org/
|
||||
[3]:https://atom.io/
|
||||
[4]:https://en.wikipedia.org/wiki/XUL
|
||||
[5]:/file/385346
|
||||
[6]:https://opensource.com/sites/default/files/images/dt-atom1.png (DomTerm terminal panes in Atom editor)
|
||||
[7]:https://opensource.com/sites/default/files/images/dt-atom1.png
|
||||
[8]:https://libwebsockets.org/
|
||||
[9]:http://invisible-island.net/vttest/
|
||||
[10]:http://domterm.org/Wire-byte-protocol.html
|
||||
[11]:http://domterm.org/Shell-prompts.html
|
||||
[12]:http://www.gnuplot.info/
|
||||
[13]:https://developer.mozilla.org/en-US/docs/Web/SVG
|
||||
[14]:http://per.bothner.com/blog/2016/gnuplot-in-domterm/
|
||||
[15]:https://www.gnu.org/software/kawa/
|
||||
[16]:https://www.gnu.org/software/kawa/Composable-pictures.html
|
||||
[17]:http://per.bothner.com/blog/2017/dynamic-prettyprinting/
|
||||
[18]:https://golden-layout.com/
|
||||
[19]:https://opensource.com/sites/default/files/u128651/domterm1.png
|
||||
[20]:https://midnight-commander.org/
|
||||
[21]:https://github.com/PerBothner/DomTerm
|
||||
[22]:http://domterm.org/Downloading-and-building.html
|
||||
|
@ -0,0 +1,229 @@
|
||||
ddgr:一个从终端搜索 DuckDuckGo 的命令行工具
|
||||
======
|
||||
|
||||
在 Linux 中,Bash 技巧非常棒,它使 Linux 中的一切成为可能。
|
||||
|
||||
对于开发人员或系统管理员来说,它真的很管用,因为他们大部分时间都在使用终端。你知道他们为什么喜欢这种技巧吗?
|
||||
|
||||
因为这些技巧可以提高他们的工作效率,也能使他们工作更快。
|
||||
|
||||
### 什么是 ddgr
|
||||
|
||||
[ddgr][1] 是一个命令行实用程序,用于从终端搜索 DuckDuckGo。如果设置了 `BROWSER` 环境变量,ddgr 可以在几个基于文本的浏览器中开箱即用。
|
||||
|
||||
确保你的系统安装了任何一个基于文本的浏览器。你可能知道 [googler][2],它允许用户从 Linux 命令行进行 Google 搜索。
|
||||
|
||||
它在命令行用户中非常受欢迎,他们期望对隐私敏感的 DuckDuckGo 也有类似的实用程序,这就是 `ddgr` 出现的原因。
|
||||
|
||||
与 Web 界面不同,你可以指定每页要查看的搜索结果数。
|
||||
|
||||
**建议阅读:**
|
||||
|
||||
- [Googler – 从 Linux 命令行搜索 Google][2]
|
||||
- [Buku – Linux 中一个强大的命令行书签管理器][3]
|
||||
- [SoCLI – 从终端搜索和浏览 StackOverflow 的简单方法][4]
|
||||
- [RTV(Reddit 终端查看器)- 一个简单的 Reddit 终端查看器][5]
|
||||
|
||||
### 什么是 DuckDuckGo
|
||||
|
||||
DDG 即 DuckDuckGo。DuckDuckGo(DDG)是一个真正保护用户搜索和隐私的互联网搜索引擎。它没有过滤用户的个性化搜索结果,对于给定的搜索词,它会向所有用户显示相同的搜索结果。
|
||||
|
||||
大多数用户更喜欢谷歌搜索引擎,但是如果你真的担心隐私,那么你可以放心地使用 DuckDuckGo。
|
||||
|
||||
### ddgr 特性
|
||||
|
||||
* 快速且干净(没有广告、多余的 URL 或杂物参数),自定义颜色
|
||||
* 旨在以最小的空间提供最高的可读性
|
||||
* 指定每页显示的搜索结果数
|
||||
* 可以在 omniprompt 中导航结果,在浏览器中打开 URL
|
||||
* 用于 Bash、Zsh 和 Fish 的搜索和选项补完脚本
|
||||
* 支持 DuckDuckGo Bang(带有自动补完)
|
||||
* 直接在浏览器中打开第一个结果(如同 “I’m Feeling Ducky”)
|
||||
* 不间断搜索:无需退出即可在 omniprompt 中触发新搜索
|
||||
* 关键字支持(例如:filetype:mime、site:somesite.com)
|
||||
* 按时间、指定区域搜索,禁用安全搜索
|
||||
* 支持 HTTPS 代理,支持 Do Not Track,可选择禁用用户代理字符串
|
||||
* 支持自定义 URL 处理程序脚本或命令行实用程序
|
||||
* 全面的文档,man 页面有方便的使用示例
|
||||
* 最小的依赖关系
|
||||
|
||||
### 需要条件
|
||||
|
||||
`ddgr` 需要 Python 3.4 或更高版本。因此,确保你的系统应具有 Python 3.4 或更高版本。
|
||||
|
||||
```
|
||||
$ python3 --version
|
||||
Python 3.6.3
|
||||
```
|
||||
|
||||
### 如何在 Linux 中安装 ddgr
|
||||
|
||||
我们可以根据发行版使用以下命令轻松安装 `ddgr`。
|
||||
|
||||
对于 Fedora ,使用 [DNF 命令][6]来安装 `ddgr`。
|
||||
|
||||
```
|
||||
# dnf install ddgr
|
||||
```
|
||||
|
||||
或者我们可以使用 [SNAP 命令][7]来安装 `ddgr`。
|
||||
|
||||
```
|
||||
# snap install ddgr
|
||||
```
|
||||
|
||||
对于 LinuxMint/Ubuntu,使用 [APT-GET 命令][8] 或 [APT 命令][9]来安装 `ddgr`。
|
||||
|
||||
```
|
||||
$ sudo add-apt-repository ppa:twodopeshaggy/jarun
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install ddgr
|
||||
```
|
||||
|
||||
对于基于 Arch Linux 的系统,使用 [Yaourt 命令][10]或 [Packer 命令][11]从 AUR 仓库安装 `ddgr`。
|
||||
|
||||
```
|
||||
$ yaourt -S ddgr
|
||||
或
|
||||
$ packer -S ddgr
|
||||
```
|
||||
|
||||
对于 Debian,使用 [DPKG 命令][12] 安装 `ddgr`。
|
||||
|
||||
```
|
||||
# wget https://github.com/jarun/ddgr/releases/download/v1.2/ddgr_1.2-1_debian9.amd64.deb
|
||||
# dpkg -i ddgr_1.2-1_debian9.amd64.deb
|
||||
```
|
||||
|
||||
对于 CentOS 7,使用 [YUM 命令][13]来安装 `ddgr`。
|
||||
|
||||
```
|
||||
# yum install https://github.com/jarun/ddgr/releases/download/v1.2/ddgr-1.2-1.el7.3.centos.x86_64.rpm
|
||||
```
|
||||
|
||||
对于 opensuse,使用 [zypper 命令][14]来安装 `ddgr`。
|
||||
|
||||
```
|
||||
# zypper install https://github.com/jarun/ddgr/releases/download/v1.2/ddgr-1.2-1.opensuse42.3.x86_64.rpm
|
||||
```
|
||||
|
||||
### 如何启动 ddgr
|
||||
|
||||
在终端上输入 `ddgr` 命令,不带任何选项来进行 DuckDuckGo 搜索。你将获得类似于下面的输出。
|
||||
|
||||
```
|
||||
$ ddgr
|
||||
```
|
||||
|
||||
![][16]
|
||||
|
||||
### 如何使用 ddgr 进行搜索
|
||||
|
||||
我们可以通过两种方式启动搜索。从 omniprompt 或者直接从终端开始。你可以搜索任何你想要的短语。
|
||||
|
||||
直接从终端:
|
||||
|
||||
```
|
||||
$ ddgr 2daygeek
|
||||
```
|
||||
|
||||
![][17]
|
||||
|
||||
从 omniprompt:
|
||||
|
||||
![][18]
|
||||
|
||||
### Omniprompt 快捷方式
|
||||
|
||||
输入 `?` 以获得 omniprompt,它将显示关键字列表和进一步使用 `ddgr` 的快捷方式。
|
||||
|
||||
![][19]
|
||||
|
||||
### 如何移动下一页、上一页和第一页
|
||||
|
||||
它允许用户移动下一页、上一页或第一页。
|
||||
|
||||
* `n`: 移动到下一组搜索结果
|
||||
* `p`: 移动到上一组搜索结果
|
||||
* `f`: 跳转到第一页
|
||||
|
||||
![][20]
|
||||
|
||||
### 如何启动新搜索
|
||||
|
||||
`d` 选项允许用户从 omniprompt 发起新的搜索。例如,我搜索了 “2daygeek website”,现在我将搜索 “Magesh Maruthamuthu” 这个新短语。
|
||||
|
||||
从 omniprompt:
|
||||
|
||||
```
|
||||
ddgr (? for help) d magesh maruthmuthu
|
||||
```
|
||||
|
||||
![][21]
|
||||
|
||||
### 在搜索结果中显示完整的 URL
|
||||
|
||||
默认情况下,它仅显示文章标题,在搜索中添加 `x` 选项以在搜索结果中显示完整的文章网址。
|
||||
|
||||
```
|
||||
$ ddgr -n 5 -x 2daygeek
|
||||
```
|
||||
|
||||
![][22]
|
||||
|
||||
### 限制搜索结果
|
||||
|
||||
默认情况下,搜索结果每页显示 10 个结果。如果你想为方便起见限制页面结果,可以使用 `ddgr` 带有 `--num` 或 ` -n` 参数。
|
||||
|
||||
```
|
||||
$ ddgr -n 5 2daygeek
|
||||
```
|
||||
|
||||
![][23]
|
||||
|
||||
### 网站特定搜索
|
||||
|
||||
要搜索特定网站的特定页面,使用以下格式。这将从网站获取给定关键字的结果。例如,我们在 2daygeek 网站下搜索 “Package Manager”,查看结果。
|
||||
|
||||
```
|
||||
$ ddgr -n 5 --site 2daygeek "package manager"
|
||||
```
|
||||
|
||||
![][24]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/ddgr-duckduckgo-search-from-the-command-line-in-linux/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.2daygeek.com/author/magesh/
|
||||
[1]:https://github.com/jarun/ddgr
|
||||
[2]:https://www.2daygeek.com/googler-google-search-from-the-command-line-on-linux/
|
||||
[3]:https://www.2daygeek.com/buku-command-line-bookmark-manager-linux/
|
||||
[4]:https://www.2daygeek.com/socli-search-and-browse-stack-overflow-from-linux-terminal/
|
||||
[5]:https://www.2daygeek.com/rtv-reddit-terminal-viewer-a-simple-terminal-viewer-for-reddit/
|
||||
[6]:https://www.2daygeek.com/dnf-command-examples-manage-packages-fedora-system/
|
||||
[7]:https://www.2daygeek.com/snap-command-examples/
|
||||
[8]:https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[9]:https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[10]:https://www.2daygeek.com/install-yaourt-aur-helper-on-arch-linux/
|
||||
[11]:https://www.2daygeek.com/install-packer-aur-helper-on-arch-linux/
|
||||
[12]:https://www.2daygeek.com/dpkg-command-to-manage-packages-on-debian-ubuntu-linux-mint-systems/
|
||||
[13]:https://www.2daygeek.com/yum-command-examples-manage-packages-rhel-centos-systems/
|
||||
[14]:https://www.2daygeek.com/zypper-command-examples-manage-packages-opensuse-system/
|
||||
[15]:data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
[16]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux1.png
|
||||
[17]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-3.png
|
||||
[18]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-2.png
|
||||
[19]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-4.png
|
||||
[20]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-5a.png
|
||||
[21]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-6a.png
|
||||
[22]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-7a.png
|
||||
[23]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-8.png
|
||||
[24]:https://www.2daygeek.com/wp-content/uploads/2018/03/ddgr-duckduckgo-command-line-search-for-linux-9a.png
|
155
published/201905/20180429 The Easiest PDO Tutorial (Basics).md
Normal file
155
published/201905/20180429 The Easiest PDO Tutorial (Basics).md
Normal file
@ -0,0 +1,155 @@
|
||||
PHP PDO 简单教程
|
||||
======
|
||||
|
||||
![](http://www.theitstuff.com/wp-content/uploads/2018/04/php-language.jpg)
|
||||
|
||||
大约 80% 的 Web 应用程序由 PHP 提供支持。类似地,SQL 也是如此。PHP 5.5 版本之前,我们有用于访问 MySQL 数据库的 mysql_ 命令,但由于安全性不足,它们最终被弃用。
|
||||
|
||||
弃用这件事是发生在 2013 年的 PHP 5.5 上,我写这篇文章的时间是 2018 年,PHP 版本为 7.2。mysql_ 的弃用带来了访问数据库的两种主要方法:mysqli 和 PDO 库。
|
||||
|
||||
虽然 mysqli 库是官方指定的,但由于 mysqli 只能支持 mysql 数据库,而 PDO 可以支持 12 种不同类型的数据库驱动程序,因此 PDO 获得了更多的赞誉。此外,PDO 还有其它一些特性,使其成为大多数开发人员的更好选择。你可以在下表中看到一些特性比较:
|
||||
|
||||
| | PDO | MySQLi
|
||||
---|---|---
|
||||
| 数据库支持 | 12 种驱动 | 只有 MySQL
|
||||
| 范例 | OOP | 过程 + OOP
|
||||
| 预处理语句(客户端侧) | Yes | No
|
||||
| 1命名参数 | Yes | No
|
||||
|
||||
现在我想对于大多数开发人员来说,PDO 是首选的原因已经很清楚了。所以让我们深入研究它,并希望在本文中尽量涵盖关于 PDO 你需要的了解的。
|
||||
|
||||
### 连接
|
||||
|
||||
第一步是连接到数据库,由于 PDO 是完全面向对象的,所以我们将使用 PDO 类的实例。
|
||||
|
||||
我们要做的第一件事是定义主机、数据库名称、用户名、密码和数据库字符集。
|
||||
|
||||
```
|
||||
$host = 'localhost';
|
||||
$db = 'theitstuff';
|
||||
$user = 'root';
|
||||
$pass = 'root';
|
||||
$charset = 'utf8mb4';
|
||||
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
|
||||
$conn = new PDO($dsn, $user, $pass);
|
||||
```
|
||||
|
||||
之后,正如你在上面的代码中看到的,我们创建了 DSN 变量,DSN 变量只是一个保存数据库信息的变量。对于一些在外部服务器上运行 MySQL 的人,你还可以通过提供一个 `port=$port_number` 来调整端口号。
|
||||
|
||||
最后,你可以创建一个 PDO 类的实例,我使用了 `$conn` 变量,并提供了 `$dsn`、`$user`、`$pass` 参数。如果你遵循这些步骤,你现在应该有一个名为 `$conn` 的对象,它是 PDO 连接类的一个实例。现在是时候进入数据库并运行一些查询。
|
||||
|
||||
### 一个简单的 SQL 查询
|
||||
|
||||
现在让我们运行一个简单的 SQL 查询。
|
||||
|
||||
```
|
||||
$tis = $conn->query('SELECT name, age FROM students');
|
||||
while ($row = $tis->fetch())
|
||||
{
|
||||
echo $row['name']."\t";
|
||||
echo $row['age'];
|
||||
echo "<br>";
|
||||
}
|
||||
```
|
||||
|
||||
这是使用 PDO 运行查询的最简单形式。我们首先创建了一个名为 `tis`(TheITStuff 的缩写 )的变量,然后你可以看到我们使用了创建的 `$conn` 对象中的查询函数。
|
||||
|
||||
然后我们运行一个 `while` 循环并创建了一个 `$row` 变量来从 `$tis` 对象中获取内容,最后通过调用列名来显示每一行。
|
||||
|
||||
很简单,不是吗?现在让我们来看看预处理语句。
|
||||
|
||||
### 预处理语句
|
||||
|
||||
预处理语句是人们开始使用 PDO 的主要原因之一,因为它提供了可以阻止 SQL 注入的语句。
|
||||
|
||||
有两种基本方法可供使用,你可以使用位置参数或命名参数。
|
||||
|
||||
#### 位置参数
|
||||
|
||||
让我们看一个使用位置参数的查询示例。
|
||||
|
||||
```
|
||||
$tis = $conn->prepare("INSERT INTO STUDENTS(name, age) values(?, ?)");
|
||||
$tis->bindValue(1,'mike');
|
||||
$tis->bindValue(2,22);
|
||||
$tis->execute();
|
||||
```
|
||||
|
||||
在上面的例子中,我们放置了两个问号,然后使用 `bindValue()` 函数将值映射到查询中。这些值绑定到语句问号中的位置。
|
||||
|
||||
我还可以使用变量而不是直接提供值,通过使用 `bindParam()` 函数相同例子如下:
|
||||
|
||||
```
|
||||
$name='Rishabh'; $age=20;
|
||||
$tis = $conn->prepare("INSERT INTO STUDENTS(name, age) values(?, ?)");
|
||||
$tis->bindParam(1,$name);
|
||||
$tis->bindParam(2,$age);
|
||||
$tis->execute();
|
||||
```
|
||||
|
||||
### 命名参数
|
||||
|
||||
命名参数也是预处理语句,它将值/变量映射到查询中的命名位置。由于没有位置绑定,因此在多次使用相同变量的查询中非常有效。
|
||||
|
||||
```
|
||||
$name='Rishabh'; $age=20;
|
||||
$tis = $conn->prepare("INSERT INTO STUDENTS(name, age) values(:name, :age)");
|
||||
$tis->bindParam(':name', $name);
|
||||
$tis->bindParam(':age', $age);
|
||||
$tis->execute();
|
||||
```
|
||||
|
||||
你可以注意到,唯一的变化是我使用 `:name` 和 `:age` 作为占位符,然后将变量映射到它们。冒号在参数之前使用,让 PDO 知道该位置是一个变量,这非常重要。
|
||||
|
||||
你也可以类似地使用 `bindValue()` 来使用命名参数直接映射值。
|
||||
|
||||
### 获取数据
|
||||
|
||||
PDO 在获取数据时非常丰富,它实际上提供了许多格式来从数据库中获取数据。
|
||||
|
||||
你可以使用 `PDO::FETCH_ASSOC` 来获取关联数组,`PDO::FETCH_NUM` 来获取数字数组,使用 `PDO::FETCH_OBJ` 来获取对象数组。
|
||||
|
||||
```
|
||||
$tis = $conn->prepare("SELECT * FROM STUDENTS");
|
||||
$tis->execute();
|
||||
$result = $tis->fetchAll(PDO::FETCH_ASSOC);
|
||||
```
|
||||
|
||||
你可以看到我使用了 `fetchAll`,因为我想要所有匹配的记录。如果只需要一行,你可以简单地使用 `fetch`。
|
||||
|
||||
现在我们已经获取了数据,现在是时候循环它了,这非常简单。
|
||||
|
||||
```
|
||||
foreach ($result as $lnu){
|
||||
echo $lnu['name'];
|
||||
echo $lnu['age']."<br>";
|
||||
}
|
||||
```
|
||||
|
||||
你可以看到,因为我请求了关联数组,所以我正在按名称访问各个成员。
|
||||
|
||||
虽然在定义希望如何传输递数据方面没有要求,但在定义 `$conn` 变量本身时,实际上可以将其设置为默认值。
|
||||
|
||||
你需要做的就是创建一个 `$options` 数组,你可以在其中放入所有默认配置,只需在 `$conn` 变量中传递数组即可。
|
||||
|
||||
```
|
||||
$options = [
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
];
|
||||
$conn = new PDO($dsn, $user, $pass, $options);
|
||||
```
|
||||
|
||||
这是一个非常简短和快速的 PDO 介绍,我们很快就会制作一个高级教程。如果你在理解本教程的任何部分时遇到任何困难,请在评论部分告诉我,我会在那你为你解答。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: http://www.theitstuff.com/easiest-pdo-tutorial-basics
|
||||
|
||||
作者:[Rishabh Kandari][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:http://www.theitstuff.com/author/reevkandari
|
@ -1,21 +1,22 @@
|
||||
没有恶棍,英雄又将如何?如何向你的 Python 游戏中添加一个敌人
|
||||
如何向你的 Python 游戏中添加一个敌人
|
||||
======
|
||||
|
||||
> 在本系列的第五部分,学习如何增加一个坏蛋与你的好人战斗。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/game-dogs-chess-play-lead.png?itok=NAuhav4Z)
|
||||
|
||||
在本系列的前几篇文章中(参见 [第一部分][1]、[第二部分][2]、[第三部分][3] 以及 [第四部分][4]),你已经学习了如何使用 Pygame 和 Python 在一个空白的视频游戏世界中生成一个可玩的角色。但没有恶棍,英雄又将如何?
|
||||
|
||||
如果你没有敌人,那将会是一个非常无聊的游戏。所以在此篇文章中,你将为你的游戏添加一个敌人并构建一个用于创建关卡的框架。
|
||||
|
||||
在对玩家妖精实现全部功能仍有许多事情可做之前,跳向敌人似乎就很奇怪。但你已经学到了很多东西,创造恶棍与与创造玩家妖精非常相似。所以放轻松,使用你已经掌握的知识,看看能挑起怎样一些麻烦。
|
||||
在对玩家妖精实现全部功能之前,就来实现一个敌人似乎就很奇怪。但你已经学到了很多东西,创造恶棍与与创造玩家妖精非常相似。所以放轻松,使用你已经掌握的知识,看看能挑起怎样一些麻烦。
|
||||
|
||||
针对本次训练,你能够从 [Open Game Art][5] 下载一些预创建的素材。此处是我使用的一些素材:
|
||||
|
||||
|
||||
+ 印加花砖(译注:游戏中使用的花砖贴图)
|
||||
+ 印加花砖(LCTT 译注:游戏中使用的花砖贴图)
|
||||
+ 一些侵略者
|
||||
+ 妖精、角色、物体以及特效
|
||||
|
||||
|
||||
### 创造敌方妖精
|
||||
|
||||
是的,不管你意识到与否,你其实已经知道如何去实现敌人。这个过程与创造一个玩家妖精非常相似:
|
||||
@ -24,40 +25,27 @@
|
||||
2. 创建 `update` 方法使得敌人能够检测碰撞
|
||||
3. 创建 `move` 方法使得敌人能够四处游荡
|
||||
|
||||
|
||||
|
||||
从类入手。从概念上看,它与你的 Player 类大体相同。你设置一张或者一组图片,然后设置妖精的初始位置。
|
||||
从类入手。从概念上看,它与你的 `Player` 类大体相同。你设置一张或者一组图片,然后设置妖精的初始位置。
|
||||
|
||||
在继续下一步之前,确保你有一张你的敌人的图像,即使只是一张临时图像。将图像放在你的游戏项目的 `images` 目录(你放置你的玩家图像的相同目录)。
|
||||
|
||||
如果所有的活物都拥有动画,那么游戏看起来会好得多。为敌方妖精设置动画与为玩家妖精设置动画具有相同的方式。但现在,为了保持简单,我们使用一个没有动画的妖精。
|
||||
|
||||
在你代码 `objects` 节的顶部,使用以下代码创建一个叫做 `Enemy` 的类:
|
||||
|
||||
```
|
||||
class Enemy(pygame.sprite.Sprite):
|
||||
|
||||
'''
|
||||
|
||||
生成一个敌人
|
||||
|
||||
'''
|
||||
|
||||
def __init__(self,x,y,img):
|
||||
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
|
||||
self.image = pygame.image.load(os.path.join('images',img))
|
||||
|
||||
self.image.convert_alpha()
|
||||
|
||||
self.image.set_colorkey(ALPHA)
|
||||
|
||||
self.rect = self.image.get_rect()
|
||||
|
||||
self.rect.x = x
|
||||
|
||||
self.rect.y = y
|
||||
|
||||
```
|
||||
|
||||
如果你想让你的敌人动起来,使用让你的玩家拥有动画的 [相同方式][4]。
|
||||
@ -67,25 +55,21 @@ class Enemy(pygame.sprite.Sprite):
|
||||
你能够通过告诉类,妖精应使用哪张图像,应出现在世界上的什么地方,来生成不只一个敌人。这意味着,你能够使用相同的敌人类,在游戏世界的任意地方生成任意数量的敌方妖精。你需要做的仅仅是调用这个类,并告诉它应使用哪张图像,以及你期望生成点的 X 和 Y 坐标。
|
||||
|
||||
再次,这从原则上与生成一个玩家精灵相似。在你脚本的 `setup` 节添加如下代码:
|
||||
|
||||
```
|
||||
enemy = Enemy(20,200,'yeti.png') # 生成敌人
|
||||
|
||||
enemy_list = pygame.sprite.Group() # 创建敌人组
|
||||
|
||||
enemy_list.add(enemy) # 将敌人加入敌人组
|
||||
|
||||
```
|
||||
|
||||
在示例代码中,X 坐标为 20,Y 坐标为 200。你可能需要根据你的敌方妖精的大小,来调整这些数字,但尽量生成在一个地方,使得你的玩家妖精能够到它。`Yeti.png` 是用于敌人的图像。
|
||||
在示例代码中,X 坐标为 20,Y 坐标为 200。你可能需要根据你的敌方妖精的大小,来调整这些数字,但尽量生成在一个范围内,使得你的玩家妖精能够碰到它。`Yeti.png` 是用于敌人的图像。
|
||||
|
||||
接下来,将敌人组的所有敌人绘制在屏幕上。现在,你只有一个敌人,如果你想要更多你可以稍后添加。一但你将一个敌人加入敌人组,它就会在主循环中被绘制在屏幕上。中间这一行是你需要添加的新行:
|
||||
|
||||
```
|
||||
player_list.draw(world)
|
||||
|
||||
enemy_list.draw(world) # 刷新敌人
|
||||
|
||||
pygame.display.flip()
|
||||
|
||||
```
|
||||
|
||||
启动你的游戏,你的敌人会出现在游戏世界中你选择的 X 和 Y 坐标处。
|
||||
@ -96,42 +80,31 @@ enemy_list.add(enemy) # 将敌人加入敌人组
|
||||
|
||||
思考一下“关卡”是什么。你如何知道你是在游戏中的一个特定关卡中呢?
|
||||
|
||||
你可以把关卡想成一系列项目的集合。就像你刚刚创建的这个平台中,一个关卡,包含了平台、敌人放置、赃物等的一个特定排列。你可以创建一个类,用来在你的玩家附近创建关卡。最终,当你创建了超过一个关卡,你就可以在你的玩家达到特定目标时,使用这个类生成下一个关卡。
|
||||
你可以把关卡想成一系列项目的集合。就像你刚刚创建的这个平台中,一个关卡,包含了平台、敌人放置、战利品等的一个特定排列。你可以创建一个类,用来在你的玩家附近创建关卡。最终,当你创建了一个以上的关卡,你就可以在你的玩家达到特定目标时,使用这个类生成下一个关卡。
|
||||
|
||||
将你写的用于生成敌人及其群组的代码,移动到一个每次生成新关卡时都会被调用的新函数中。你需要做一些修改,使得每次你创建新关卡时,你都能够创建一些敌人。
|
||||
|
||||
```
|
||||
class Level():
|
||||
|
||||
def bad(lvl,eloc):
|
||||
|
||||
if lvl == 1:
|
||||
|
||||
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # 生成敌人
|
||||
|
||||
enemy_list = pygame.sprite.Group() # 生成敌人组
|
||||
|
||||
enemy_list.add(enemy) # 将敌人加入敌人组
|
||||
|
||||
if lvl == 2:
|
||||
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
|
||||
|
||||
return enemy_list
|
||||
|
||||
```
|
||||
|
||||
`return` 语句确保了当你调用 `Level.bad` 方法时,你将会得到一个 `enemy_list` 变量包含了所有你定义的敌人。
|
||||
|
||||
因为你现在将创造敌人作为每个关卡的一部分,你的 `setup` 部分也需要做些更改。不同于创造一个敌人,取而代之的是你必须去定义敌人在那里生成,以及敌人属于哪个关卡。
|
||||
|
||||
```
|
||||
eloc = []
|
||||
|
||||
eloc = [200,20]
|
||||
|
||||
enemy_list = Level.bad( 1, eloc )
|
||||
|
||||
```
|
||||
|
||||
再次运行游戏来确认你的关卡生成正确。与往常一样,你应该会看到你的玩家,并且能看到你在本章节中添加的敌人。
|
||||
@ -140,31 +113,27 @@ enemy_list = Level.bad( 1, eloc )
|
||||
|
||||
一个敌人如果对玩家没有效果,那么它不太算得上是一个敌人。当玩家与敌人发生碰撞时,他们通常会对玩家造成伤害。
|
||||
|
||||
因为你可能想要去跟踪玩家的生命值,因此碰撞检测发生在 Player 类,而不是 Enemy 类中。当然如果你想,你也可以跟踪敌人的生命值。它们之间的逻辑与代码大体相似,现在,我们只需要跟踪玩家的生命值。
|
||||
因为你可能想要去跟踪玩家的生命值,因此碰撞检测发生在 `Player` 类,而不是 `Enemy` 类中。当然如果你想,你也可以跟踪敌人的生命值。它们之间的逻辑与代码大体相似,现在,我们只需要跟踪玩家的生命值。
|
||||
|
||||
为了跟踪玩家的生命值,你必须为它确定一个变量。代码示例中的第一行是上下文提示,那么将第二行代码添加到你的 Player 类中:
|
||||
|
||||
```
|
||||
self.frame = 0
|
||||
|
||||
self.health = 10
|
||||
|
||||
```
|
||||
|
||||
在你 Player 类的 `update` 方法中,添加如下代码块:
|
||||
在你 `Player` 类的 `update` 方法中,添加如下代码块:
|
||||
|
||||
```
|
||||
hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
|
||||
|
||||
for enemy in hit_list:
|
||||
|
||||
self.health -= 1
|
||||
|
||||
print(self.health)
|
||||
|
||||
```
|
||||
|
||||
这段代码使用 Pygame 的 `sprite.spritecollide` 方法,建立了一个碰撞检测器,称作 `enemy_hit`。每当它的父类妖精(生成检测器的玩家妖精)的碰撞区触碰到 `enemy_list` 中的任一妖精的碰撞区时,碰撞检测器都会发出一个信号。当这个信号被接收,`for` 循环就会被触发,同时扣除一点玩家生命值。
|
||||
|
||||
一旦这段代码出现在你 Player 类的 `update` 方法,并且 `update` 方法在你的主循环中被调用,Pygame 会在每个时钟 tick 检测一次碰撞。
|
||||
一旦这段代码出现在你 `Player` 类的 `update` 方法,并且 `update` 方法在你的主循环中被调用,Pygame 会在每个时钟滴答中检测一次碰撞。
|
||||
|
||||
### 移动敌人
|
||||
|
||||
@ -176,60 +145,41 @@ enemy_list = Level.bad( 1, eloc )
|
||||
|
||||
举个例子,你告诉你的敌方妖精向右移动 10 步,向左移动 10 步。但敌方妖精不会计数,因此你需要创建一个变量来跟踪你的敌人已经移动了多少步,并根据计数变量的值来向左或向右移动你的敌人。
|
||||
|
||||
首先,在你的 Enemy 类中创建计数变量。添加以下代码示例中的最后一行代码:
|
||||
首先,在你的 `Enemy` 类中创建计数变量。添加以下代码示例中的最后一行代码:
|
||||
|
||||
```
|
||||
self.rect = self.image.get_rect()
|
||||
|
||||
self.rect.x = x
|
||||
|
||||
self.rect.y = y
|
||||
|
||||
self.counter = 0 # 计数变量
|
||||
|
||||
```
|
||||
|
||||
然后,在你的 Enemy 类中创建一个 `move` 方法。使用 if-else 循环来创建一个所谓的死循环:
|
||||
然后,在你的 `Enemy` 类中创建一个 `move` 方法。使用 if-else 循环来创建一个所谓的死循环:
|
||||
|
||||
* 如果计数在 0 到 100 之间,向右移动;
|
||||
* 如果计数在 100 到 200 之间,向左移动;
|
||||
* 如果计数大于 200,则将计数重置为 0。
|
||||
|
||||
|
||||
|
||||
死循环没有终点,因为循环判断条件永远为真,所以它将永远循环下去。在此情况下,计数器总是介于 0 到 100 或 100 到 200 之间,因此敌人会永远地从左向右再从右向左移动。
|
||||
|
||||
你用于敌人在每个方向上移动距离的具体值,取决于你的屏幕尺寸,更确切地说,取决于你的敌人移动的平台大小。从较小的值开始,依据习惯逐步提高数值。首先进行如下尝试:
|
||||
|
||||
```
|
||||
def move(self):
|
||||
|
||||
'''
|
||||
|
||||
敌人移动
|
||||
|
||||
'''
|
||||
|
||||
distance = 80
|
||||
|
||||
speed = 8
|
||||
|
||||
|
||||
|
||||
if self.counter >= 0 and self.counter <= distance:
|
||||
|
||||
self.rect.x += speed
|
||||
|
||||
elif self.counter >= distance and self.counter <= distance*2:
|
||||
|
||||
self.rect.x -= speed
|
||||
|
||||
else:
|
||||
|
||||
self.counter = 0
|
||||
|
||||
|
||||
|
||||
self.counter += 1
|
||||
|
||||
```
|
||||
|
||||
你可以根据需要调整距离和速度。
|
||||
@ -237,13 +187,11 @@ enemy_list = Level.bad( 1, eloc )
|
||||
当你现在启动游戏,这段代码有效果吗?
|
||||
|
||||
当然不,你应该也知道原因。你必须在主循环中调用 `move` 方法。如下示例代码中的第一行是上下文提示,那么添加最后两行代码:
|
||||
|
||||
```
|
||||
enemy_list.draw(world) #refresh enemy
|
||||
|
||||
for e in enemy_list:
|
||||
|
||||
e.move()
|
||||
|
||||
```
|
||||
|
||||
启动你的游戏看看当你打击敌人时发生了什么。你可能需要调整妖精的生成地点,使得你的玩家和敌人能够碰撞。当他们发生碰撞时,查看 [IDLE][6] 或 [Ninja-IDE][7] 的控制台,你可以看到生命值正在被扣除。
|
||||
@ -261,15 +209,15 @@ via: https://opensource.com/article/18/5/pygame-enemy
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[cycoe](https://github.com/cycoe)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[1]:https://opensource.com/article/17/10/python-101
|
||||
[2]:https://opensource.com/article/17/12/game-framework-python
|
||||
[3]:https://opensource.com/article/17/12/game-python-add-a-player
|
||||
[4]:https://opensource.com/article/17/12/game-python-moving-player
|
||||
[1]:https://linux.cn/article-9071-1.html
|
||||
[2]:https://linux.cn/article-10850-1.html
|
||||
[3]:https://linux.cn/article-10858-1.html
|
||||
[4]:https://linux.cn/article-10874-1.html
|
||||
[5]:https://opengameart.org
|
||||
[6]:https://docs.python.org/3/library/idle.html
|
||||
[7]:http://ninja-ide.org/
|
@ -0,0 +1,130 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10830-1.html)
|
||||
[#]: subject: (How to use autofs to mount NFS shares)
|
||||
[#]: via: (https://opensource.com/article/18/6/using-autofs-mount-nfs-shares)
|
||||
[#]: author: (Alan Formy-Duval https://opensource.com/users/alanfdoss)
|
||||
|
||||
如何使用 autofs 挂载 NFS 共享
|
||||
======
|
||||
|
||||
> 给你的网络文件系统(NFS)配置一个基本的自动挂载功能。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201905/08/115328rva7kqw9wqh2qees.jpg)
|
||||
|
||||
大多数 Linux 文件系统在引导时挂载,并在系统运行时保持挂载状态。对于已在 `fstab` 中配置的任何远程文件系统也是如此。但是,有时你可能希望仅按需挂载远程文件系统。例如,通过减少网络带宽使用来提高性能,或出于安全原因隐藏或混淆某些目录。[autofs][1] 软件包提供此功能。在本文中,我将介绍如何配置基本的自动挂载。
|
||||
|
||||
首先做点假设:假设有台 NFS 服务器 `tree.mydatacenter.net` 已经启动并运行。另外假设一个名为 `ourfiles` 的数据目录还有供 Carl 和 Sarah 使用的用户目录,它们都由服务器共享。
|
||||
|
||||
一些最佳实践可以使工作更好:服务器上的用户和任何客户端工作站上的帐号有相同的用户 ID。此外,你的工作站和服务器应有相同的域名。检查相关配置文件应该确认。
|
||||
|
||||
```
|
||||
alan@workstation1:~$ sudo getent passwd carl sarah
|
||||
[sudo] password for alan:
|
||||
carl:x:1020:1020:Carl,,,:/home/carl:/bin/bash
|
||||
sarah:x:1021:1021:Sarah,,,:/home/sarah:/bin/bash
|
||||
|
||||
alan@workstation1:~$ sudo getent hosts
|
||||
127.0.0.1 localhost
|
||||
127.0.1.1 workstation1.mydatacenter.net workstation1
|
||||
10.10.1.5 tree.mydatacenter.net tree
|
||||
```
|
||||
|
||||
如你所见,客户端工作站和 NFS 服务器都在 `hosts` 文件中配置。我假设这是一个基本的家庭甚至小型办公室网络,可能缺乏适合的内部域名服务(即 DNS)。
|
||||
|
||||
### 安装软件包
|
||||
|
||||
你只需要安装两个软件包:用于 NFS 客户端的 `nfs-common` 和提供自动挂载的 `autofs`。
|
||||
|
||||
```
|
||||
alan@workstation1:~$ sudo apt-get install nfs-common autofs
|
||||
```
|
||||
|
||||
你可以验证 autofs 相关的文件是否已放在 `/etc` 目录中:
|
||||
|
||||
```
|
||||
alan@workstation1:~$ cd /etc; ll auto*
|
||||
-rw-r--r-- 1 root root 12596 Nov 19 2015 autofs.conf
|
||||
-rw-r--r-- 1 root root 857 Mar 10 2017 auto.master
|
||||
-rw-r--r-- 1 root root 708 Jul 6 2017 auto.misc
|
||||
-rwxr-xr-x 1 root root 1039 Nov 19 2015 auto.net*
|
||||
-rwxr-xr-x 1 root root 2191 Nov 19 2015 auto.smb*
|
||||
alan@workstation1:/etc$
|
||||
```
|
||||
|
||||
### 配置 autofs
|
||||
|
||||
现在你需要编辑其中几个文件并添加 `auto.home` 文件。首先,将以下两行添加到文件 `auto.master` 中:
|
||||
|
||||
```
|
||||
/mnt/tree /etc/auto.misc
|
||||
/home/tree /etc/auto.home
|
||||
```
|
||||
|
||||
每行以挂载 NFS 共享的目录开头。继续创建这些目录:
|
||||
|
||||
```
|
||||
alan@workstation1:/etc$ sudo mkdir /mnt/tree /home/tree
|
||||
```
|
||||
|
||||
接下来,将以下行添加到文件 `auto.misc`:
|
||||
|
||||
```
|
||||
ourfiles -fstype=nfs tree:/share/ourfiles
|
||||
```
|
||||
|
||||
该行表示 autofs 将挂载 `auto.master` 文件中匹配 `auto.misc` 的 `ourfiles` 共享。如上所示,这些文件将在 `/mnt/tree/ourfiles` 目录中。
|
||||
|
||||
第三步,使用以下行创建文件 `auto.home`:
|
||||
|
||||
```
|
||||
* -fstype=nfs tree:/home/&
|
||||
```
|
||||
|
||||
该行表示 autofs 将挂载 `auto.master` 文件中匹配 `auto.home` 的用户共享。在这种情况下,Carl 和 Sarah 的文件将分别在目录 `/home/tree/carl` 或 `/home/tree/sarah`中。星号 `*`(称为通配符)使每个用户的共享可以在登录时自动挂载。`&` 符号也可以作为表示服务器端用户目录的通配符。它们的主目录会相应地根据 `passwd` 文件映射。如果你更喜欢本地主目录,则无需执行此操作。相反,用户可以将其用作特定文件的简单远程存储。
|
||||
|
||||
最后,重启 `autofs` 守护进程,以便识别并加载这些配置的更改。
|
||||
|
||||
```
|
||||
alan@workstation1:/etc$ sudo service autofs restart
|
||||
```
|
||||
|
||||
### 测试 autofs
|
||||
|
||||
如果更改文件 `auto.master` 中的列出目录,并运行 `ls` 命令,那么不会立即看到任何内容。例如,切换到目录 `/mnt/tree`。首先,`ls` 的输出不会显示任何内容,但在运行 `cd ourfiles` 之后,将自动挂载 `ourfiles` 共享目录。 `cd` 命令也将被执行,你将进入新挂载的目录中。
|
||||
|
||||
```
|
||||
carl@workstation1:~$ cd /mnt/tree
|
||||
carl@workstation1:/mnt/tree$ ls
|
||||
carl@workstation1:/mnt/tree$ cd ourfiles
|
||||
carl@workstation1:/mnt/tree/ourfiles$
|
||||
```
|
||||
|
||||
为了进一步确认正常工作,`mount` 命令会显示已挂载共享的细节。
|
||||
|
||||
```
|
||||
carl@workstation1:~$ mount
|
||||
|
||||
tree:/mnt/share/ourfiles on /mnt/tree/ourfiles type nfs4 (rw,relatime,vers=4.0,rsize=131072,wsize=131072,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,clientaddr=10.10.1.22,local_lock=none,addr=10.10.1.5)
|
||||
|
||||
```
|
||||
|
||||
对于 Carl 和 Sarah,`/home/tree` 目录工作方式相同。
|
||||
|
||||
我发现在我的文件管理器中添加这些目录的书签很有用,可以用来快速访问。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/18/6/using-autofs-mount-nfs-shares
|
||||
|
||||
作者:[Alan Formy-Duval][a]
|
||||
选题:[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/) 荣誉推出
|
||||
|
||||
[a]:https://opensource.com/users/alanfdoss
|
||||
[1]:https://wiki.archlinux.org/index.php/autofs
|
@ -0,0 +1,86 @@
|
||||
Adobe Lightroom 的三个开源替代品
|
||||
=======
|
||||
|
||||
> 摄影师们:在没有 Lightroom 套件的情况下,可以看看这些 RAW 图像处理器。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/camera-photography-film.jpg?itok=oe2ixyu6)
|
||||
|
||||
如今智能手机的摄像功能已经完备到多数人认为可以代替传统摄影了。虽然这在傻瓜相机的市场中是个事实,但是对于许多摄影爱好者和专业摄影师看来,一个高端单反相机所能带来的照片景深、清晰度以及真实质感是口袋中的智能手机无法与之相比的。
|
||||
|
||||
所有的这些功能在便利性上要付出一些很小的代价;就像传统的胶片相机中的反色负片,单反照相得到的 RAW 格式文件必须预先处理才能印刷或编辑;因此对于单反相机,照片的后期处理是无可替代的,并且 首选应用就是 Adobe Lightroom。但是由于 Adobe Lightroom 的昂贵价格、基于订阅的定价模式以及专有许可证都使更多人开始关注其开源替代品。
|
||||
|
||||
Lightroom 有两大主要功能:处理 RAW 格式的图片文件,以及数字资产管理系统(DAM) —— 通过标签、评星以及其他元数据信息来简单清晰地整理照片。
|
||||
|
||||
在这篇文章中,我们将介绍三个开源的图片处理软件:Darktable、LightZone 以及 RawTherapee。所有的软件都有 DAM 系统,但没有任何一个具有 Lightroom 基于机器学习的图像分类和标签功能。如果你想要知道更多关于开源的 DAM 系统的软件,可以看 Terry Hacock 的文章:“[开源项目的 DAM 管理][2]”,他分享了他在自己的 [Lunatics!][3] 电影项目研究过的开源多媒体软件。
|
||||
|
||||
### Darktable
|
||||
|
||||
![Darktable][4]
|
||||
|
||||
类似其他两个软件,Darktable 可以处理 RAW 格式的图像并将它们转换成可用的文件格式 —— JPEG、PNG、TIFF、PPM、PFM 和 EXR,它同时支持 Google 和 Facebook 的在线相册,上传至 Flikr,通过邮件附件发送以及创建在线相册。
|
||||
|
||||
它有 61 个图像处理模块,可以调整图像的对比度、色调、明暗、色彩、噪点;添加水印;切割以及旋转;等等。如同另外两个软件一样,不论你做出多少次修改,这些修改都是“无损的” —— 你的初始 RAW 图像文件始终会被保存。
|
||||
|
||||
Darktable 可以从 400 多种相机型号中直接导入照片,以及有 JPEG、CR2、DNG、OpenEXR 和 PFM 等格式的支持。图像在一个数据库中显示,因此你可以轻易地过滤并查询这些元数据,包括了文字标签、评星以及颜色标签。软件同时支持 21 种语言,支持 Linux、MacOS、BSD、Solaris 11/GNOME 以及 Windows(Windows 版本是最新发布的,Darktable 声明它比起其他版本可能还有一些不完备之处,有一些未实现的功能)。
|
||||
|
||||
Darktable 在开源许可证 [GPLv3][7] 下发布,你可以了解更多它的 [特性][8],查阅它的 [用户手册][9],或者直接去 Github 上看[源代码][10] 。
|
||||
|
||||
### LightZone
|
||||
|
||||
![LightZone's tool stack][11]
|
||||
|
||||
[LightZone][12] 和其他两个软件类似同样是无损的 RAW 格式图像处理工具:它是跨平台的,有 Windows、MacOS 和 Linux 版本,除 RAW 格式之外,它还支持 JPG 和 TIFF 格式的图像处理。接下来说说 LightZone 其他独特特性。
|
||||
|
||||
这个软件最初在 2005 年时,是以专有许可证发布的图像处理软件,后来在 BSD 证书下开源。此外,在你下载这个软件之前,你必须注册一个免费账号,以便 LightZone的 开发团队可以跟踪软件的下载数量以及建立相关社区。(许可很快,而且是自动的,因此这不是一个很大的使用障碍。)
|
||||
|
||||
除此之外的一个特性是这个软件的图像处理通常是通过很多可组合的工具实现的,而不是叠加滤镜(就像大多数图像处理软件),这些工具组可以被重新编排以及移除,以及被保存并且复制用到另一些图像上。如果想要编辑图片的部分区域,你还可以通过矢量工具或者根据色彩和亮度来选择像素。
|
||||
|
||||
想要了解更多,见 LightZone 的[论坛][13] 或者查看 Github上的 [源代码][14]。
|
||||
|
||||
### RawTherapee
|
||||
|
||||
![RawTherapee][15]
|
||||
|
||||
[RawTherapee][16] 是另一个值得关注的开源([GPL][17])的 RAW 图像处理器。就像 Darktable 和 LightZone,它是跨平台的(支持 Windows、MacOS 和 Linux),一切修改都在无损条件下进行,因此不论你叠加多少滤镜做出多少改变,你都可以回到你最初的 RAW 文件。
|
||||
|
||||
RawTherapee 采用的是一个面板式的界面,包括一个历史记录面板来跟踪你做出的修改,以方便随时回到先前的图像;一个快照面板可以让你同时处理一张照片的不同版本;一个可滚动的工具面板可以方便准确地选择工具。这些工具包括了一系列的调整曝光、色彩、细节、图像变换以及去马赛克功能。
|
||||
|
||||
这个软件可以从多数相机直接导入 RAW 文件,并且支持超过 25 种语言,得到了广泛使用。批量处理以及 [SSE][18] 优化这类功能也进一步提高了图像处理的速度以及对 CPU 性能的利用。
|
||||
|
||||
RawTherapee 还提供了很多其他 [功能][19];可以查看它的 [官方文档][20] 以及 [源代码][21] 了解更多细节。
|
||||
|
||||
你是否在摄影中使用另外的开源 RAW 图像处理工具?有任何建议和推荐都可以在评论中分享。
|
||||
|
||||
------
|
||||
|
||||
via: https://opensource.com/alternatives/adobe-lightroom
|
||||
|
||||
作者:[Opensource.com][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[scoutydren](https://github.com/scoutydren)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com
|
||||
[1]: https://en.wikipedia.org/wiki/Raw_image_format
|
||||
[2]: https://opensource.com/article/18/3/movie-open-source-software
|
||||
[3]: http://lunatics.tv/
|
||||
[4]: https://opensource.com/sites/default/files/styles/panopoly_image_original/public/uploads/raw-image-processors_darkroom1.jpg?itok=0fjk37tC "Darktable"
|
||||
[5]: http://www.darktable.org/
|
||||
[6]: https://www.darktable.org/about/faq/#faq-windows
|
||||
[7]: https://github.com/darktable-org/darktable/blob/master/LICENSE
|
||||
[8]: https://www.darktable.org/about/features/
|
||||
[9]: https://www.darktable.org/resources/
|
||||
[10]: https://github.com/darktable-org/darktable
|
||||
[11]: https://opensource.com/sites/default/files/styles/panopoly_image_original/public/uploads/raw-image-processors_lightzone1tookstack.jpg?itok=1e3s85CZ
|
||||
[12]: http://www.lightzoneproject.org/
|
||||
[13]: http://www.lightzoneproject.org/Forum
|
||||
[14]: https://github.com/ktgw0316/LightZone
|
||||
[15]: https://opensource.com/sites/default/files/styles/panopoly_image_original/public/uploads/raw-image-processors_rawtherapee.jpg?itok=meiuLxPw "RawTherapee"
|
||||
[16]: http://rawtherapee.com/
|
||||
[17]: https://github.com/Beep6581/RawTherapee/blob/dev/LICENSE.txt
|
||||
[18]: https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions
|
||||
[19]: http://rawpedia.rawtherapee.com/Features
|
||||
[20]: http://rawpedia.rawtherapee.com/Main_Page
|
||||
[21]: https://github.com/Beep6581/RawTherapee
|
@ -0,0 +1,593 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10902-1.html)
|
||||
[#]: subject: (Put platforms in a Python game with Pygame)
|
||||
[#]: via: (https://opensource.com/article/18/7/put-platforms-python-game)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
在 Pygame 游戏中放置平台
|
||||
======
|
||||
|
||||
> 在这个从零构建一个 Python 游戏系列的第六部分中,为你的角色创建一些平台来旅行。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/header.png?itok=iq8HFoEJ)
|
||||
|
||||
这是仍在进行中的关于使用 Pygame 模块来在 Python 3 中创建电脑游戏的系列文章的第六部分。先前的文章是:
|
||||
|
||||
+ [通过构建一个简单的掷骰子游戏去学习怎么用 Python 编程][24]
|
||||
+ [使用 Python 和 Pygame 模块构建一个游戏框架][25]
|
||||
+ [如何在你的 Python 游戏中添加一个玩家][26]
|
||||
+ [用 Pygame 使你的游戏角色移动起来][27]
|
||||
+ [如何向你的 Python 游戏中添加一个敌人][28]
|
||||
|
||||
一个平台类游戏需要平台。
|
||||
|
||||
在 [Pygame][1] 中,平台本身也是个妖精,正像你那个可玩的妖精。这一点是重要的,因为有个是对象的平台,可以使你的玩家妖精更容易与之互动。
|
||||
|
||||
创建平台有两个主要步骤。首先,你必须给该对象编写代码,然后,你必须映射出你希望该对象出现的位置。
|
||||
|
||||
### 编码平台对象
|
||||
|
||||
要构建一个平台对象,你要创建一个名为 `Platform` 的类。它是一个妖精,正像你的 `Player` [妖精][2] 一样,带有很多相同的属性。
|
||||
|
||||
你的 `Platform` 类需要知道很多平台类型的信息,它应该出现在游戏世界的哪里、它应该包含的什么图片等等。这其中很多信息可能还尚不存在,这要看你为你的游戏计划了多少,但是没有关系。正如直到[移动你的游戏角色][3]那篇文章结束时,你都没有告诉你的玩家妖精移动速度有多快,你不必事先告诉 `Platform` 每一件事。
|
||||
|
||||
在这系列中你所写的脚本的开头附近,创建一个新的类。在这个代码示例中前三行是用于说明上下文,因此在注释的下面添加代码:
|
||||
|
||||
```
|
||||
import pygame
|
||||
import sys
|
||||
import os
|
||||
## 新代码如下:
|
||||
|
||||
class Platform(pygame.sprite.Sprite):
|
||||
# x location, y location, img width, img height, img file
|
||||
def __init__(self,xloc,yloc,imgw,imgh,img):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(os.path.join('images',img)).convert()
|
||||
self.image.convert_alpha()
|
||||
self.image.set_colorkey(ALPHA)
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.y = yloc
|
||||
self.rect.x = xloc
|
||||
```
|
||||
|
||||
当被调用时,这个类在某个 X 和 Y 位置上创建一个屏上对象,具有某种宽度和高度,并使用某种图像作为纹理。这与如何在屏上绘制出玩家或敌人非常类似。
|
||||
|
||||
### 平台的类型
|
||||
|
||||
下一步是绘制出你的平台需要出现的地方。
|
||||
|
||||
#### 瓷砖方式
|
||||
|
||||
实现平台类游戏世界有几种不同的方法。在最初的横向滚轴游戏中,例如,马里奥超级兄弟和刺猬索尼克,这个技巧是使用“瓷砖”方式,也就是说有几个代表地面和各种平台的块,并且这些块被重复使用来制作一个关卡。你只能有 8 或 12 种不同的块,你可以将它们排列在屏幕上来创建地面、浮动的平台,以及你游戏中需要的一切其它的事物。有人发现这是制作游戏最容易的方法了,因为你只需要制作(或下载)一小组关卡素材就能创建很多不同的关卡。然而,这里的代码需要一点数学知识。
|
||||
|
||||
![Supertux, a tile-based video game][5]
|
||||
|
||||
*[SuperTux][6] ,一个基于瓷砖的电脑游戏。*
|
||||
|
||||
#### 手工绘制方式
|
||||
|
||||
另一种方法是将每个素材作为一个整体图像。如果你喜欢为游戏世界创建素材,那你会在用图形应用程序构建游戏世界的每个部分上花费很多时间。这种方法不需要太多的数学知识,因为所有的平台都是整体的、完整的对象,你只需要告诉 [Python][7] 将它们放在屏幕上的什么位置。
|
||||
|
||||
每种方法都有优势和劣势,并且根据于你选择使用的方式,代码稍有不同。我将覆盖这两方面,所以你可以在你的工程中使用一种或另一种,甚至两者的混合。
|
||||
|
||||
### 关卡绘制
|
||||
|
||||
总的来说,绘制你的游戏世界是关卡设计和游戏编程中的一个重要的部分。这需要数学知识,但是没有什么太难的,而且 Python 擅长数学,它会有所帮助。
|
||||
|
||||
你也许发现先在纸张上设计是有用的。拿一张表格纸,并绘制一个方框来代表你的游戏窗体。在方框中绘制平台,并标记其每一个平台的 X 和 Y 坐标,以及它的宽度和高度。在方框中的实际位置没有必要是精确的,你只要保持数字合理即可。譬如,假设你的屏幕是 720 像素宽,那么你不能在一个屏幕上放 8 块 100 像素的平台。
|
||||
|
||||
当然,不是你游戏中的所有平台都必须容纳在一个屏幕大小的方框里,因为你的游戏将随着你的玩家行走而滚动。所以,可以继续绘制你的游戏世界到第一屏幕的右侧,直到关卡结束。
|
||||
|
||||
如果你更喜欢精确一点,你可以使用方格纸。当设计一个瓷砖类的游戏时,这是特别有用的,因为每个方格可以代表一个瓷砖。
|
||||
|
||||
![Example of a level map][9]
|
||||
|
||||
*一个关卡地图示例。*
|
||||
|
||||
#### 坐标系
|
||||
|
||||
你可能已经在学校中学习过[笛卡尔坐标系][10]。你学习的东西也适用于 Pygame,除了在 Pygame 中你的游戏世界的坐标系的原点 `0,0` 是放置在你的屏幕的左上角而不是在中间,是你在地理课上用过的坐标是在中间的。
|
||||
|
||||
![Example of coordinates in Pygame][12]
|
||||
|
||||
*在 Pygame 中的坐标示例。*
|
||||
|
||||
X 轴起始于最左边的 0,向右无限增加。Y 轴起始于屏幕顶部的 0,向下延伸。
|
||||
|
||||
#### 图片大小
|
||||
|
||||
如果你不知道你的玩家、敌人、平台是多大的,绘制出一个游戏世界是毫无意义的。你可以在图形程序中找到你的平台或瓷砖的尺寸。例如在 [Krita][13] 中,单击“图像”菜单,并选择“属性”。你可以在“属性”窗口的最顶部处找到它的尺寸。
|
||||
|
||||
另外,你也可以创建一个简单的 Python 脚本来告诉你的一个图像的尺寸。打开一个新的文本文件,并输入这些代码到其中:
|
||||
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from PIL import Image
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
print(sys.argv[1])
|
||||
else:
|
||||
sys.exit('Syntax: identify.py [filename]')
|
||||
|
||||
pic = sys.argv[1]
|
||||
dim = Image.open(pic)
|
||||
X = dim.size[0]
|
||||
Y = dim.size[1]
|
||||
|
||||
print(X,Y)
|
||||
```
|
||||
|
||||
保存该文本文件为 `identify.py`。
|
||||
|
||||
要使用这个脚本,你必须安装一些额外的 Python 模块,它们包含了这个脚本中新使用的关键字:
|
||||
|
||||
```
|
||||
$ pip3 install Pillow --user
|
||||
```
|
||||
|
||||
一旦安装好,在你游戏工程目录中运行这个脚本:
|
||||
|
||||
```
|
||||
$ python3 ./identify.py images/ground.png
|
||||
(1080, 97)
|
||||
```
|
||||
|
||||
在这个示例中,地面平台的图形的大小是 1080 像素宽和 97 像素高。
|
||||
|
||||
### 平台块
|
||||
|
||||
如果你选择单独地绘制每个素材,你必须创建想要插入到你的游戏世界中的几个平台和其它元素,每个素材都放在它自己的文件中。换句话说,你应该让每个素材都有一个文件,像这样:
|
||||
|
||||
![One image file per object][15]
|
||||
|
||||
*每个对象一个图形文件。*
|
||||
|
||||
你可以按照你希望的次数重复使用每个平台,只要确保每个文件仅包含一个平台。你不能使用一个文件包含全部素材,像这样:
|
||||
|
||||
![Your level cannot be one image file][17]
|
||||
|
||||
*你的关卡不能是一个图形文件。*
|
||||
|
||||
当你完成时,你可能希望你的游戏看起来像这样,但是如果你在一个大文件中创建你的关卡,你就没有方法从背景中区分出一个平台,因此,要么把对象绘制在它们自己的文件中,要么从一个更大的文件中裁剪出它们,并保存为单独的副本。
|
||||
|
||||
**注意:** 如同你的其它素材,你可以使用 [GIMP][18]、Krita、[MyPaint][19],或 [Inkscape][20] 来创建你的游戏素材。
|
||||
|
||||
平台出现在每个关卡开始的屏幕上,因此你必须在你的 `Level` 类中添加一个 `platform` 函数。在这里特例是地面平台,它重要到应该拥有它自己的一个组。通过把地面看作一组特殊类型的平台,你可以选择它是否滚动,或它上面是否可以站立,而其它平台可以漂浮在它上面。这取决于你。
|
||||
|
||||
添加这两个函数到你的 `Level` 类:
|
||||
|
||||
```
|
||||
def ground(lvl,x,y,w,h):
|
||||
ground_list = pygame.sprite.Group()
|
||||
if lvl == 1:
|
||||
ground = Platform(x,y,w,h,'block-ground.png')
|
||||
ground_list.add(ground)
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return ground_list
|
||||
|
||||
def platform( lvl ):
|
||||
plat_list = pygame.sprite.Group()
|
||||
if lvl == 1:
|
||||
plat = Platform(200, worldy-97-128, 285,67,'block-big.png')
|
||||
plat_list.add(plat)
|
||||
plat = Platform(500, worldy-97-320, 197,54,'block-small.png')
|
||||
plat_list.add(plat)
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return plat_list
|
||||
```
|
||||
|
||||
`ground` 函数需要一个 X 和 Y 位置,以便 Pygame 知道在哪里放置地面平台。它也需要知道平台的宽度和高度,这样 Pygame 知道地面延伸到每个方向有多远。该函数使用你的 `Platform` 类来生成一个屏上对象,然后将这个对象添加到 `ground_list` 组。
|
||||
|
||||
`platform` 函数本质上是相同的,除了其有更多的平台。在这个示例中,仅有两个平台,但是你可以想有多少就有多少。在进入一个平台后,在列出另一个前你必须添加它到 `plat_list` 中。如果你不添加平台到组中,那么它将不出现在你的游戏中。
|
||||
|
||||
> **提示:** 很难想象你的游戏世界的 0 是在顶部,因为在真实世界中发生的情况是相反的;当估计你有多高时,你不会从上往下测量你自己,而是从脚到头顶来测量。
|
||||
>
|
||||
> 如果对你来说从“地面”上来构建你的游戏世界更容易,将 Y 轴值表示为负数可能有帮助。例如,你知道你的游戏世界的底部是 `worldy` 的值。因此 `worldy` 减去地面的高度(在这个示例中是 97)是你的玩家正常站立的位置。如果你的角色是 64 像素高,那么地面减去 128 正好是你的玩家的两倍高。事实上,一个放置在 128 像素处平台大约是相对于你的玩家的两层楼高度。一个平台在 -320 处比三层楼更高。等等。
|
||||
|
||||
正像你现在可能所知的,如果你不使用它们,你的类和函数是没有价值的。添加这些代码到你的设置部分(第一行只是上下文,所以添加最后两行):
|
||||
|
||||
```
|
||||
enemy_list = Level.bad( 1, eloc )
|
||||
ground_list = Level.ground( 1,0,worldy-97,1080,97 )
|
||||
plat_list = Level.platform( 1 )
|
||||
```
|
||||
|
||||
并把这些行加到你的主循环(再一次,第一行仅用于上下文):
|
||||
|
||||
```
|
||||
enemy_list.draw(world) # 刷新敌人
|
||||
ground_list.draw(world) # 刷新地面
|
||||
plat_list.draw(world) # 刷新平台
|
||||
```
|
||||
|
||||
### 瓷砖平台
|
||||
|
||||
瓷砖类游戏世界更容易制作,因为你只需要在前面绘制一些块,就能在游戏中一再使用它们创建每个平台。在像 [OpenGameArt.org][21] 这样的网站上甚至有一套瓷砖供你来使用。
|
||||
|
||||
`Platform` 类与在前面部分中的类是相同的。
|
||||
|
||||
`ground` 和 `platform` 在 `Level` 类中,然而,必须使用循环来计算使用多少块来创建每个平台。
|
||||
|
||||
如果你打算在你的游戏世界中有一个坚固的地面,这种地面是很简单的。你只需要从整个窗口的一边到另一边“克隆”你的地面瓷砖。例如,你可以创建一个 X 和 Y 值的列表来规定每个瓷砖应该放置的位置,然后使用一个循环来获取每个值并绘制每一个瓷砖。这仅是一个示例,所以不要添加这到你的代码:
|
||||
|
||||
```
|
||||
# Do not add this to your code
|
||||
gloc = [0,656,64,656,128,656,192,656,256,656,320,656,384,656]
|
||||
```
|
||||
|
||||
不过,如果你仔细看,你可以看到所有的 Y 值是相同的,X 值以 64 的增量不断地增加 —— 这就是瓷砖的大小。这种重复是精确地,是计算机擅长的,因此你可以使用一点数学逻辑来让计算机为你做所有的计算:
|
||||
|
||||
添加这些到你的脚本的设置部分:
|
||||
|
||||
```
|
||||
gloc = []
|
||||
tx = 64
|
||||
ty = 64
|
||||
|
||||
i=0
|
||||
while i <= (worldx/tx)+tx:
|
||||
gloc.append(i*tx)
|
||||
i=i+1
|
||||
|
||||
ground_list = Level.ground( 1,gloc,tx,ty )
|
||||
```
|
||||
|
||||
现在,不管你的窗口的大小,Python 会通过瓷砖的宽度分割游戏世界的宽度,并创建一个数组列表列出每个 X 值。这里不计算 Y 值,因为在平的地面上这个从不会变化。
|
||||
|
||||
为了在一个函数中使用数组,使用一个 `while` 循环,查看每个条目并在适当的位置添加一个地面瓷砖:
|
||||
|
||||
```
|
||||
def ground(lvl,gloc,tx,ty):
|
||||
ground_list = pygame.sprite.Group()
|
||||
i=0
|
||||
if lvl == 1:
|
||||
while i < len(gloc):
|
||||
ground = Platform(gloc[i],worldy-ty,tx,ty,'tile-ground.png')
|
||||
ground_list.add(ground)
|
||||
i=i+1
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return ground_list
|
||||
```
|
||||
|
||||
除了 `while` 循环,这几乎与在上面一部分中提供的瓷砖类平台的 `ground` 函数的代码相同。
|
||||
|
||||
对于移动的平台,原理是相似的,但是这里有一些技巧可以使它简单。
|
||||
|
||||
你可以通过它的起始像素(它的 X 值)、距地面的高度(它的 Y 值)、绘制多少瓷砖来定义一个平台,而不是通过像素绘制每个平台。这样,你不必操心每个平台的宽度和高度。
|
||||
|
||||
这个技巧的逻辑有一点复杂,因此请仔细复制这些代码。有一个 `while` 循环嵌套在另一个 `while` 循环的内部,因为这个函数必须考虑每个数组项的三个值来成功地建造一个完整的平台。在这个示例中,这里仅有三个平台以 `ploc.append` 语句定义,但是你的游戏可能需要更多,因此你需要多少就定义多少。当然,有一些不会出现,因为它们远在屏幕外,但是一旦当你进行滚动时,它们将呈现在眼前。
|
||||
|
||||
```
|
||||
def platform(lvl,tx,ty):
|
||||
plat_list = pygame.sprite.Group()
|
||||
ploc = []
|
||||
i=0
|
||||
if lvl == 1:
|
||||
ploc.append((200,worldy-ty-128,3))
|
||||
ploc.append((300,worldy-ty-256,3))
|
||||
ploc.append((500,worldy-ty-128,4))
|
||||
while i < len(ploc):
|
||||
j=0
|
||||
while j <= ploc[i][2]:
|
||||
plat = Platform((ploc[i][0]+(j*tx)),ploc[i][1],tx,ty,'tile.png')
|
||||
plat_list.add(plat)
|
||||
j=j+1
|
||||
print('run' + str(i) + str(ploc[i]))
|
||||
i=i+1
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return plat_list
|
||||
```
|
||||
|
||||
要让这些平台出现在你的游戏世界,它们必须出现在你的主循环中。如果你还没有这样做,添加这些行到你的主循环(再一次,第一行仅被用于上下文)中:
|
||||
|
||||
```
|
||||
enemy_list.draw(world) # 刷新敌人
|
||||
ground_list.draw(world) # 刷新地面
|
||||
plat_list.draw(world) # 刷新平台
|
||||
```
|
||||
|
||||
启动你的游戏,根据需要调整你的平台的放置位置。如果你看不见屏幕外产生的平台,不要担心;你不久后就可以修复它。
|
||||
|
||||
到目前为止,这是游戏的图片和代码:
|
||||
|
||||
![Pygame game][23]
|
||||
|
||||
*到目前为止,我们的 Pygame 平台。*
|
||||
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
# draw a world
|
||||
# add a player and player control
|
||||
# add player movement
|
||||
# add enemy and basic collision
|
||||
# add platform
|
||||
|
||||
# GNU All-Permissive License
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved. This file is offered as-is,
|
||||
# without any warranty.
|
||||
|
||||
import pygame
|
||||
import sys
|
||||
import os
|
||||
|
||||
'''
|
||||
Objects
|
||||
'''
|
||||
|
||||
class Platform(pygame.sprite.Sprite):
|
||||
# x location, y location, img width, img height, img file
|
||||
def __init__(self,xloc,yloc,imgw,imgh,img):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(os.path.join('images',img)).convert()
|
||||
self.image.convert_alpha()
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.y = yloc
|
||||
self.rect.x = xloc
|
||||
|
||||
class Player(pygame.sprite.Sprite):
|
||||
'''
|
||||
Spawn a player
|
||||
'''
|
||||
def __init__(self):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.movex = 0
|
||||
self.movey = 0
|
||||
self.frame = 0
|
||||
self.health = 10
|
||||
self.score = 1
|
||||
self.images = []
|
||||
for i in range(1,9):
|
||||
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
|
||||
img.convert_alpha()
|
||||
img.set_colorkey(ALPHA)
|
||||
self.images.append(img)
|
||||
self.image = self.images[0]
|
||||
self.rect = self.image.get_rect()
|
||||
|
||||
def control(self,x,y):
|
||||
'''
|
||||
control player movement
|
||||
'''
|
||||
self.movex += x
|
||||
self.movey += y
|
||||
|
||||
def update(self):
|
||||
'''
|
||||
Update sprite position
|
||||
'''
|
||||
|
||||
self.rect.x = self.rect.x + self.movex
|
||||
self.rect.y = self.rect.y + self.movey
|
||||
|
||||
# moving left
|
||||
if self.movex < 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[self.frame//ani]
|
||||
|
||||
# moving right
|
||||
if self.movex > 0:
|
||||
self.frame += 1
|
||||
if self.frame > ani*3:
|
||||
self.frame = 0
|
||||
self.image = self.images[(self.frame//ani)+4]
|
||||
|
||||
# collisions
|
||||
enemy_hit_list = pygame.sprite.spritecollide(self, enemy_list, False)
|
||||
for enemy in enemy_hit_list:
|
||||
self.health -= 1
|
||||
print(self.health)
|
||||
|
||||
ground_hit_list = pygame.sprite.spritecollide(self, ground_list, False)
|
||||
for g in ground_hit_list:
|
||||
self.health -= 1
|
||||
print(self.health)
|
||||
|
||||
|
||||
class Enemy(pygame.sprite.Sprite):
|
||||
'''
|
||||
Spawn an enemy
|
||||
'''
|
||||
def __init__(self,x,y,img):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.image = pygame.image.load(os.path.join('images',img))
|
||||
#self.image.convert_alpha()
|
||||
#self.image.set_colorkey(ALPHA)
|
||||
self.rect = self.image.get_rect()
|
||||
self.rect.x = x
|
||||
self.rect.y = y
|
||||
self.counter = 0
|
||||
|
||||
def move(self):
|
||||
'''
|
||||
enemy movement
|
||||
'''
|
||||
distance = 80
|
||||
speed = 8
|
||||
|
||||
if self.counter >= 0 and self.counter <= distance:
|
||||
self.rect.x += speed
|
||||
elif self.counter >= distance and self.counter <= distance*2:
|
||||
self.rect.x -= speed
|
||||
else:
|
||||
self.counter = 0
|
||||
|
||||
self.counter += 1
|
||||
|
||||
class Level():
|
||||
def bad(lvl,eloc):
|
||||
if lvl == 1:
|
||||
enemy = Enemy(eloc[0],eloc[1],'yeti.png') # spawn enemy
|
||||
enemy_list = pygame.sprite.Group() # create enemy group
|
||||
enemy_list.add(enemy) # add enemy to group
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return enemy_list
|
||||
|
||||
def loot(lvl,lloc):
|
||||
print(lvl)
|
||||
|
||||
def ground(lvl,gloc,tx,ty):
|
||||
ground_list = pygame.sprite.Group()
|
||||
i=0
|
||||
if lvl == 1:
|
||||
while i < len(gloc):
|
||||
print("blockgen:" + str(i))
|
||||
ground = Platform(gloc[i],worldy-ty,tx,ty,'ground.png')
|
||||
ground_list.add(ground)
|
||||
i=i+1
|
||||
|
||||
if lvl == 2:
|
||||
print("Level " + str(lvl) )
|
||||
|
||||
return ground_list
|
||||
|
||||
'''
|
||||
Setup
|
||||
'''
|
||||
worldx = 960
|
||||
worldy = 720
|
||||
|
||||
fps = 40 # frame rate
|
||||
ani = 4 # animation cycles
|
||||
clock = pygame.time.Clock()
|
||||
pygame.init()
|
||||
main = True
|
||||
|
||||
BLUE = (25,25,200)
|
||||
BLACK = (23,23,23 )
|
||||
WHITE = (254,254,254)
|
||||
ALPHA = (0,255,0)
|
||||
|
||||
world = pygame.display.set_mode([worldx,worldy])
|
||||
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert()
|
||||
backdropbox = world.get_rect()
|
||||
player = Player() # spawn player
|
||||
player.rect.x = 0
|
||||
player.rect.y = 0
|
||||
player_list = pygame.sprite.Group()
|
||||
player_list.add(player)
|
||||
steps = 10 # how fast to move
|
||||
|
||||
eloc = []
|
||||
eloc = [200,20]
|
||||
gloc = []
|
||||
#gloc = [0,630,64,630,128,630,192,630,256,630,320,630,384,630]
|
||||
tx = 64 #tile size
|
||||
ty = 64 #tile size
|
||||
|
||||
i=0
|
||||
while i <= (worldx/tx)+tx:
|
||||
gloc.append(i*tx)
|
||||
i=i+1
|
||||
print("block: " + str(i))
|
||||
|
||||
enemy_list = Level.bad( 1, eloc )
|
||||
ground_list = Level.ground( 1,gloc,tx,ty )
|
||||
|
||||
'''
|
||||
Main loop
|
||||
'''
|
||||
while main == True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit(); sys.exit()
|
||||
main = False
|
||||
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
player.control(-steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_UP or event.key == ord('w'):
|
||||
print('jump')
|
||||
|
||||
if event.type == pygame.KEYUP:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
player.control(-steps,0)
|
||||
if event.key == ord('q'):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
main = False
|
||||
|
||||
# world.fill(BLACK)
|
||||
world.blit(backdrop, backdropbox)
|
||||
player.update()
|
||||
player_list.draw(world) #refresh player position
|
||||
enemy_list.draw(world) # refresh enemies
|
||||
ground_list.draw(world) # refresh enemies
|
||||
for e in enemy_list:
|
||||
e.move()
|
||||
pygame.display.flip()
|
||||
clock.tick(fps)
|
||||
```
|
||||
|
||||
(LCTT 译注:到本文翻译完为止,该系列已经近一年没有继续更新了~)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/18/7/put-platforms-python-game
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.pygame.org/news
|
||||
[2]: https://opensource.com/article/17/12/game-python-add-a-player
|
||||
[3]: https://opensource.com/article/17/12/game-python-moving-player
|
||||
[4]: /file/403841
|
||||
[5]: https://opensource.com/sites/default/files/uploads/supertux.png (Supertux, a tile-based video game)
|
||||
[6]: https://www.supertux.org/
|
||||
[7]: https://www.python.org/
|
||||
[8]: /file/403861
|
||||
[9]: https://opensource.com/sites/default/files/uploads/layout.png (Example of a level map)
|
||||
[10]: https://en.wikipedia.org/wiki/Cartesian_coordinate_system
|
||||
[11]: /file/403871
|
||||
[12]: https://opensource.com/sites/default/files/uploads/pygame_coordinates.png (Example of coordinates in Pygame)
|
||||
[13]: https://krita.org/en/
|
||||
[14]: /file/403876
|
||||
[15]: https://opensource.com/sites/default/files/uploads/pygame_floating.png (One image file per object)
|
||||
[16]: /file/403881
|
||||
[17]: https://opensource.com/sites/default/files/uploads/pygame_flattened.png (Your level cannot be one image file)
|
||||
[18]: https://www.gimp.org/
|
||||
[19]: http://mypaint.org/about/
|
||||
[20]: https://inkscape.org/en/
|
||||
[21]: https://opengameart.org/content/simplified-platformer-pack
|
||||
[22]: /file/403886
|
||||
[23]: https://opensource.com/sites/default/files/uploads/pygame_platforms.jpg (Pygame game)
|
||||
[24]: https://linux.cn/article-9071-1.html
|
||||
[25]: https://linux.cn/article-10850-1.html
|
||||
[26]: https://linux.cn/article-10858-1.html
|
||||
[27]: https://linux.cn/article-10874-1.html
|
||||
[28]: https://linux.cn/article-10883-1.html
|
||||
|
@ -0,0 +1,596 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10848-1.html)
|
||||
[#]: subject: (TLP – An Advanced Power Management Tool That Improve Battery Life On Linux Laptop)
|
||||
[#]: via: (https://www.2daygeek.com/tlp-increase-optimize-linux-laptop-battery-life/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
TLP:一个可以延长 Linux 笔记本电池寿命的高级电源管理工具
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201905/13/094413iu77i8w75t80tq7h.jpg)
|
||||
|
||||
笔记本电池是针对 Windows 操作系统进行了高度优化的,当我在笔记本电脑中使用 Windows 操作系统时,我已经意识到这一点,但对于 Linux 来说却不一样。
|
||||
|
||||
多年来,Linux 在电池优化方面取得了很大进步,但我们仍然需要做一些必要的事情来改善 Linux 中笔记本电脑的电池寿命。
|
||||
|
||||
当我考虑延长电池寿命时,我没有多少选择,但我觉得 TLP 对我来说是一个更好的解决方案,所以我会继续使用它。
|
||||
|
||||
在本教程中,我们将详细讨论 TLP 以延长电池寿命。
|
||||
|
||||
我们之前在我们的网站上写过三篇关于 Linux [笔记本电池节电工具][1] 的文章:[PowerTOP][2] 和 [电池充电状态][3]。
|
||||
|
||||
### TLP
|
||||
|
||||
[TLP][4] 是一款自由开源的高级电源管理工具,可在不进行任何配置更改的情况下延长电池寿命。
|
||||
|
||||
由于它的默认配置已针对电池寿命进行了优化,因此你可能只需要安装,然后就忘记它吧。
|
||||
|
||||
此外,它可以高度定制化,以满足你的特定要求。TLP 是一个具有自动后台任务的纯命令行工具。它不包含GUI。
|
||||
|
||||
TLP 适用于各种品牌的笔记本电脑。设置电池充电阈值仅适用于 IBM/Lenovo ThinkPad。
|
||||
|
||||
所有 TLP 设置都存储在 `/etc/default/tlp` 中。其默认配置提供了开箱即用的优化的节能设置。
|
||||
|
||||
以下 TLP 设置可用于自定义,如果需要,你可以相应地进行必要的更改。
|
||||
|
||||
### TLP 功能
|
||||
|
||||
* 内核笔记本电脑模式和脏缓冲区超时
|
||||
* 处理器频率调整,包括 “turbo boost”/“turbo core”
|
||||
* 限制最大/最小的 P 状态以控制 CPU 的功耗
|
||||
* HWP 能源性能提示
|
||||
* 用于多核/超线程的功率感知进程调度程序
|
||||
* 处理器性能与节能策略(`x86_energy_perf_policy`)
|
||||
* 硬盘高级电源管理级别(APM)和降速超时(按磁盘)
|
||||
* AHCI 链路电源管理(ALPM)与设备黑名单
|
||||
* PCIe 活动状态电源管理(PCIe ASPM)
|
||||
* PCI(e) 总线设备的运行时电源管理
|
||||
* Radeon 图形电源管理(KMS 和 DPM)
|
||||
* Wifi 省电模式
|
||||
* 关闭驱动器托架中的光盘驱动器
|
||||
* 音频省电模式
|
||||
* I/O 调度程序(按磁盘)
|
||||
* USB 自动暂停,支持设备黑名单/白名单(输入设备自动排除)
|
||||
* 在系统启动和关闭时启用或禁用集成的 wifi、蓝牙或 wwan 设备
|
||||
* 在系统启动时恢复无线电设备状态(从之前的关机时的状态)
|
||||
* 无线电设备向导:在网络连接/断开和停靠/取消停靠时切换无线电
|
||||
* 禁用 LAN 唤醒
|
||||
* 挂起/休眠后恢复集成的 WWAN 和蓝牙状态
|
||||
* 英特尔处理器的动态电源降低 —— 需要内核和 PHC-Patch 支持
|
||||
* 电池充电阈值 —— 仅限 ThinkPad
|
||||
* 重新校准电池 —— 仅限 ThinkPad
|
||||
|
||||
### 如何在 Linux 上安装 TLP
|
||||
|
||||
TLP 包在大多数发行版官方存储库中都可用,因此,使用发行版的 [包管理器][5] 来安装它。
|
||||
|
||||
对于 Fedora 系统,使用 [DNF 命令][6] 安装 TLP。
|
||||
|
||||
```
|
||||
$ sudo dnf install tlp tlp-rdw
|
||||
```
|
||||
|
||||
ThinkPad 需要一些附加软件包。
|
||||
|
||||
```
|
||||
$ sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm
|
||||
$ sudo dnf install http://repo.linrunner.de/fedora/tlp/repos/releases/tlp-release.fc$(rpm -E %fedora).noarch.rpm
|
||||
$ sudo dnf install akmod-tp_smapi akmod-acpi_call kernel-devel
|
||||
```
|
||||
|
||||
安装 smartmontool 以显示 tlp-stat 中 S.M.A.R.T. 数据。
|
||||
|
||||
```
|
||||
$ sudo dnf install smartmontools
|
||||
```
|
||||
|
||||
对于 Debian/Ubuntu 系统,使用 [APT-GET 命令][7] 或 [APT 命令][8] 安装 TLP。
|
||||
|
||||
```
|
||||
$ sudo apt install tlp tlp-rdw
|
||||
```
|
||||
|
||||
ThinkPad 需要一些附加软件包。
|
||||
|
||||
```
|
||||
$ sudo apt-get install tp-smapi-dkms acpi-call-dkms
|
||||
```
|
||||
|
||||
安装 smartmontool 以显示 tlp-stat 中 S.M.A.R.T. 数据。
|
||||
|
||||
```
|
||||
$ sudo apt-get install smartmontools
|
||||
```
|
||||
|
||||
当基于 Ubuntu 的系统的官方软件包过时时,请使用以下 PPA 存储库,该存储库提供最新版本。运行以下命令以使用 PPA 安装 TLP。
|
||||
|
||||
```
|
||||
$ sudo add-apt-repository ppa:linrunner/tlp
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install tlp
|
||||
```
|
||||
|
||||
对于基于 Arch Linux 的系统,使用 [Pacman 命令][9] 安装 TLP。
|
||||
|
||||
```
|
||||
$ sudo pacman -S tlp tlp-rdw
|
||||
```
|
||||
|
||||
ThinkPad 需要一些附加软件包。
|
||||
|
||||
```
|
||||
$ pacman -S tp_smapi acpi_call
|
||||
```
|
||||
|
||||
安装 smartmontool 以显示 tlp-stat 中 S.M.A.R.T. 数据。
|
||||
|
||||
```
|
||||
$ sudo pacman -S smartmontools
|
||||
```
|
||||
|
||||
对于基于 Arch Linux 的系统,在启动时启用 TLP 和 TLP-Sleep 服务。
|
||||
|
||||
```
|
||||
$ sudo systemctl enable tlp.service
|
||||
$ sudo systemctl enable tlp-sleep.service
|
||||
```
|
||||
|
||||
对于基于 Arch Linux 的系统,你还应该屏蔽以下服务以避免冲突,并确保 TLP 的无线电设备切换选项的正确操作。
|
||||
|
||||
```
|
||||
$ sudo systemctl mask systemd-rfkill.service
|
||||
$ sudo systemctl mask systemd-rfkill.socket
|
||||
```
|
||||
|
||||
对于 RHEL/CentOS 系统,使用 [YUM 命令][10] 安装 TLP。
|
||||
|
||||
```
|
||||
$ sudo yum install tlp tlp-rdw
|
||||
```
|
||||
|
||||
安装 smartmontool 以显示 tlp-stat 中 S.M.A.R.T. 数据。
|
||||
|
||||
```
|
||||
$ sudo yum install smartmontools
|
||||
```
|
||||
|
||||
对于 openSUSE Leap 系统,使用 [Zypper 命令][11] 安装 TLP。
|
||||
|
||||
```
|
||||
$ sudo zypper install TLP
|
||||
```
|
||||
|
||||
安装 smartmontool 以显示 tlp-stat 中 S.M.A.R.T. 数据。
|
||||
|
||||
```
|
||||
$ sudo zypper install smartmontools
|
||||
```
|
||||
|
||||
成功安装 TLP 后,使用以下命令启动服务。
|
||||
|
||||
```
|
||||
$ systemctl start tlp.service
|
||||
```
|
||||
|
||||
### 使用方法
|
||||
|
||||
#### 显示电池信息
|
||||
|
||||
```
|
||||
$ sudo tlp-stat -b
|
||||
或
|
||||
$ sudo tlp-stat --battery
|
||||
```
|
||||
|
||||
```
|
||||
--- TLP 1.1 --------------------------------------------
|
||||
|
||||
+++ Battery Status
|
||||
/sys/class/power_supply/BAT0/manufacturer = SMP
|
||||
/sys/class/power_supply/BAT0/model_name = L14M4P23
|
||||
/sys/class/power_supply/BAT0/cycle_count = (not supported)
|
||||
/sys/class/power_supply/BAT0/energy_full_design = 60000 [mWh]
|
||||
/sys/class/power_supply/BAT0/energy_full = 48850 [mWh]
|
||||
/sys/class/power_supply/BAT0/energy_now = 48850 [mWh]
|
||||
/sys/class/power_supply/BAT0/power_now = 0 [mW]
|
||||
/sys/class/power_supply/BAT0/status = Full
|
||||
|
||||
Charge = 100.0 [%]
|
||||
Capacity = 81.4 [%]
|
||||
```
|
||||
|
||||
#### 显示磁盘信息
|
||||
|
||||
```
|
||||
$ sudo tlp-stat -d
|
||||
或
|
||||
$ sudo tlp-stat --disk
|
||||
```
|
||||
|
||||
```
|
||||
--- TLP 1.1 --------------------------------------------
|
||||
|
||||
+++ Storage Devices
|
||||
/dev/sda:
|
||||
Model = WDC WD10SPCX-24HWST1
|
||||
Firmware = 02.01A02
|
||||
APM Level = 128
|
||||
Status = active/idle
|
||||
Scheduler = mq-deadline
|
||||
|
||||
Runtime PM: control = on, autosuspend_delay = (not available)
|
||||
|
||||
SMART info:
|
||||
4 Start_Stop_Count = 18787
|
||||
5 Reallocated_Sector_Ct = 0
|
||||
9 Power_On_Hours = 606 [h]
|
||||
12 Power_Cycle_Count = 1792
|
||||
193 Load_Cycle_Count = 25775
|
||||
194 Temperature_Celsius = 31 [°C]
|
||||
|
||||
|
||||
+++ AHCI Link Power Management (ALPM)
|
||||
/sys/class/scsi_host/host0/link_power_management_policy = med_power_with_dipm
|
||||
/sys/class/scsi_host/host1/link_power_management_policy = med_power_with_dipm
|
||||
/sys/class/scsi_host/host2/link_power_management_policy = med_power_with_dipm
|
||||
/sys/class/scsi_host/host3/link_power_management_policy = med_power_with_dipm
|
||||
|
||||
+++ AHCI Host Controller Runtime Power Management
|
||||
/sys/bus/pci/devices/0000:00:17.0/ata1/power/control = on
|
||||
/sys/bus/pci/devices/0000:00:17.0/ata2/power/control = on
|
||||
/sys/bus/pci/devices/0000:00:17.0/ata3/power/control = on
|
||||
/sys/bus/pci/devices/0000:00:17.0/ata4/power/control = on
|
||||
```
|
||||
|
||||
#### 显示 PCI 设备信息
|
||||
|
||||
```
|
||||
$ sudo tlp-stat -e
|
||||
或
|
||||
$ sudo tlp-stat --pcie
|
||||
```
|
||||
|
||||
```
|
||||
$ sudo tlp-stat -e
|
||||
or
|
||||
$ sudo tlp-stat --pcie
|
||||
|
||||
--- TLP 1.1 --------------------------------------------
|
||||
|
||||
+++ Runtime Power Management
|
||||
Device blacklist = (not configured)
|
||||
Driver blacklist = amdgpu nouveau nvidia radeon pcieport
|
||||
|
||||
/sys/bus/pci/devices/0000:00:00.0/power/control = auto (0x060000, Host bridge, skl_uncore)
|
||||
/sys/bus/pci/devices/0000:00:01.0/power/control = auto (0x060400, PCI bridge, pcieport)
|
||||
/sys/bus/pci/devices/0000:00:02.0/power/control = auto (0x030000, VGA compatible controller, i915)
|
||||
/sys/bus/pci/devices/0000:00:14.0/power/control = auto (0x0c0330, USB controller, xhci_hcd)
|
||||
|
||||
......
|
||||
```
|
||||
|
||||
#### 显示图形卡信息
|
||||
|
||||
```
|
||||
$ sudo tlp-stat -g
|
||||
或
|
||||
$ sudo tlp-stat --graphics
|
||||
```
|
||||
|
||||
```
|
||||
--- TLP 1.1 --------------------------------------------
|
||||
|
||||
+++ Intel Graphics
|
||||
/sys/module/i915/parameters/enable_dc = -1 (use per-chip default)
|
||||
/sys/module/i915/parameters/enable_fbc = 1 (enabled)
|
||||
/sys/module/i915/parameters/enable_psr = 0 (disabled)
|
||||
/sys/module/i915/parameters/modeset = -1 (use per-chip default)
|
||||
```
|
||||
|
||||
#### 显示处理器信息
|
||||
|
||||
```
|
||||
$ sudo tlp-stat -p
|
||||
或
|
||||
$ sudo tlp-stat --processor
|
||||
```
|
||||
|
||||
```
|
||||
--- TLP 1.1 --------------------------------------------
|
||||
|
||||
+++ Processor
|
||||
CPU model = Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
|
||||
|
||||
/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver = intel_pstate
|
||||
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor = powersave
|
||||
/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors = performance powersave
|
||||
/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq = 800000 [kHz]
|
||||
/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq = 3500000 [kHz]
|
||||
/sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference = balance_power
|
||||
/sys/devices/system/cpu/cpu0/cpufreq/energy_performance_available_preferences = default performance balance_performance balance_power power
|
||||
|
||||
......
|
||||
|
||||
/sys/devices/system/cpu/intel_pstate/min_perf_pct = 22 [%]
|
||||
/sys/devices/system/cpu/intel_pstate/max_perf_pct = 100 [%]
|
||||
/sys/devices/system/cpu/intel_pstate/no_turbo = 0
|
||||
/sys/devices/system/cpu/intel_pstate/turbo_pct = 33 [%]
|
||||
/sys/devices/system/cpu/intel_pstate/num_pstates = 28
|
||||
|
||||
x86_energy_perf_policy: program not installed.
|
||||
|
||||
/sys/module/workqueue/parameters/power_efficient = Y
|
||||
/proc/sys/kernel/nmi_watchdog = 0
|
||||
|
||||
+++ Undervolting
|
||||
PHC kernel not available.
|
||||
```
|
||||
|
||||
#### 显示系统数据信息
|
||||
|
||||
```
|
||||
$ sudo tlp-stat -s
|
||||
或
|
||||
$ sudo tlp-stat --system
|
||||
```
|
||||
|
||||
```
|
||||
--- TLP 1.1 --------------------------------------------
|
||||
|
||||
+++ System Info
|
||||
System = LENOVO Lenovo ideapad Y700-15ISK 80NV
|
||||
BIOS = CDCN35WW
|
||||
Release = "Manjaro Linux"
|
||||
Kernel = 4.19.6-1-MANJARO #1 SMP PREEMPT Sat Dec 1 12:21:26 UTC 2018 x86_64
|
||||
/proc/cmdline = BOOT_IMAGE=/boot/vmlinuz-4.19-x86_64 root=UUID=69d9dd18-36be-4631-9ebb-78f05fe3217f rw quiet resume=UUID=a2092b92-af29-4760-8e68-7a201922573b
|
||||
Init system = systemd
|
||||
Boot mode = BIOS (CSM, Legacy)
|
||||
|
||||
+++ TLP Status
|
||||
State = enabled
|
||||
Last run = 11:04:00 IST, 596 sec(s) ago
|
||||
Mode = battery
|
||||
Power source = battery
|
||||
```
|
||||
|
||||
#### 显示温度和风扇速度信息
|
||||
|
||||
```
|
||||
$ sudo tlp-stat -t
|
||||
或
|
||||
$ sudo tlp-stat --temp
|
||||
```
|
||||
|
||||
```
|
||||
--- TLP 1.1 --------------------------------------------
|
||||
|
||||
+++ Temperatures
|
||||
CPU temp = 36 [°C]
|
||||
Fan speed = (not available)
|
||||
```
|
||||
|
||||
#### 显示 USB 设备数据信息
|
||||
|
||||
```
|
||||
$ sudo tlp-stat -u
|
||||
或
|
||||
$ sudo tlp-stat --usb
|
||||
```
|
||||
|
||||
```
|
||||
--- TLP 1.1 --------------------------------------------
|
||||
|
||||
+++ USB
|
||||
Autosuspend = disabled
|
||||
Device whitelist = (not configured)
|
||||
Device blacklist = (not configured)
|
||||
Bluetooth blacklist = disabled
|
||||
Phone blacklist = disabled
|
||||
WWAN blacklist = enabled
|
||||
|
||||
Bus 002 Device 001 ID 1d6b:0003 control = auto, autosuspend_delay_ms = 0 -- Linux Foundation 3.0 root hub (hub)
|
||||
Bus 001 Device 003 ID 174f:14e8 control = auto, autosuspend_delay_ms = 2000 -- Syntek (uvcvideo)
|
||||
|
||||
......
|
||||
```
|
||||
|
||||
#### 显示警告信息
|
||||
|
||||
```
|
||||
$ sudo tlp-stat -w
|
||||
或
|
||||
$ sudo tlp-stat --warn
|
||||
```
|
||||
|
||||
```
|
||||
--- TLP 1.1 --------------------------------------------
|
||||
|
||||
No warnings detected.
|
||||
```
|
||||
|
||||
#### 状态报告及配置和所有活动的设置
|
||||
|
||||
```
|
||||
$ sudo tlp-stat
|
||||
```
|
||||
|
||||
```
|
||||
--- TLP 1.1 --------------------------------------------
|
||||
|
||||
+++ Configured Settings: /etc/default/tlp
|
||||
TLP_ENABLE=1
|
||||
TLP_DEFAULT_MODE=AC
|
||||
TLP_PERSISTENT_DEFAULT=0
|
||||
DISK_IDLE_SECS_ON_AC=0
|
||||
DISK_IDLE_SECS_ON_BAT=2
|
||||
MAX_LOST_WORK_SECS_ON_AC=15
|
||||
MAX_LOST_WORK_SECS_ON_BAT=60
|
||||
|
||||
......
|
||||
|
||||
+++ System Info
|
||||
System = LENOVO Lenovo ideapad Y700-15ISK 80NV
|
||||
BIOS = CDCN35WW
|
||||
Release = "Manjaro Linux"
|
||||
Kernel = 4.19.6-1-MANJARO #1 SMP PREEMPT Sat Dec 1 12:21:26 UTC 2018 x86_64
|
||||
/proc/cmdline = BOOT_IMAGE=/boot/vmlinuz-4.19-x86_64 root=UUID=69d9dd18-36be-4631-9ebb-78f05fe3217f rw quiet resume=UUID=a2092b92-af29-4760-8e68-7a201922573b
|
||||
Init system = systemd
|
||||
Boot mode = BIOS (CSM, Legacy)
|
||||
|
||||
+++ TLP Status
|
||||
State = enabled
|
||||
Last run = 11:04:00 IST, 684 sec(s) ago
|
||||
Mode = battery
|
||||
Power source = battery
|
||||
|
||||
+++ Processor
|
||||
CPU model = Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
|
||||
|
||||
/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver = intel_pstate
|
||||
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor = powersave
|
||||
/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors = performance powersave
|
||||
|
||||
......
|
||||
|
||||
/sys/devices/system/cpu/intel_pstate/min_perf_pct = 22 [%]
|
||||
/sys/devices/system/cpu/intel_pstate/max_perf_pct = 100 [%]
|
||||
/sys/devices/system/cpu/intel_pstate/no_turbo = 0
|
||||
/sys/devices/system/cpu/intel_pstate/turbo_pct = 33 [%]
|
||||
/sys/devices/system/cpu/intel_pstate/num_pstates = 28
|
||||
|
||||
x86_energy_perf_policy: program not installed.
|
||||
|
||||
/sys/module/workqueue/parameters/power_efficient = Y
|
||||
/proc/sys/kernel/nmi_watchdog = 0
|
||||
|
||||
+++ Undervolting
|
||||
PHC kernel not available.
|
||||
|
||||
+++ Temperatures
|
||||
CPU temp = 42 [°C]
|
||||
Fan speed = (not available)
|
||||
|
||||
+++ File System
|
||||
/proc/sys/vm/laptop_mode = 2
|
||||
/proc/sys/vm/dirty_writeback_centisecs = 6000
|
||||
/proc/sys/vm/dirty_expire_centisecs = 6000
|
||||
/proc/sys/vm/dirty_ratio = 20
|
||||
/proc/sys/vm/dirty_background_ratio = 10
|
||||
|
||||
+++ Storage Devices
|
||||
/dev/sda:
|
||||
Model = WDC WD10SPCX-24HWST1
|
||||
Firmware = 02.01A02
|
||||
APM Level = 128
|
||||
Status = active/idle
|
||||
Scheduler = mq-deadline
|
||||
|
||||
Runtime PM: control = on, autosuspend_delay = (not available)
|
||||
|
||||
SMART info:
|
||||
4 Start_Stop_Count = 18787
|
||||
5 Reallocated_Sector_Ct = 0
|
||||
9 Power_On_Hours = 606 [h]
|
||||
12 Power_Cycle_Count = 1792
|
||||
193 Load_Cycle_Count = 25777
|
||||
194 Temperature_Celsius = 31 [°C]
|
||||
|
||||
|
||||
+++ AHCI Link Power Management (ALPM)
|
||||
/sys/class/scsi_host/host0/link_power_management_policy = med_power_with_dipm
|
||||
/sys/class/scsi_host/host1/link_power_management_policy = med_power_with_dipm
|
||||
/sys/class/scsi_host/host2/link_power_management_policy = med_power_with_dipm
|
||||
/sys/class/scsi_host/host3/link_power_management_policy = med_power_with_dipm
|
||||
|
||||
+++ AHCI Host Controller Runtime Power Management
|
||||
/sys/bus/pci/devices/0000:00:17.0/ata1/power/control = on
|
||||
/sys/bus/pci/devices/0000:00:17.0/ata2/power/control = on
|
||||
/sys/bus/pci/devices/0000:00:17.0/ata3/power/control = on
|
||||
/sys/bus/pci/devices/0000:00:17.0/ata4/power/control = on
|
||||
|
||||
+++ PCIe Active State Power Management
|
||||
/sys/module/pcie_aspm/parameters/policy = powersave
|
||||
|
||||
+++ Intel Graphics
|
||||
/sys/module/i915/parameters/enable_dc = -1 (use per-chip default)
|
||||
/sys/module/i915/parameters/enable_fbc = 1 (enabled)
|
||||
/sys/module/i915/parameters/enable_psr = 0 (disabled)
|
||||
/sys/module/i915/parameters/modeset = -1 (use per-chip default)
|
||||
|
||||
+++ Wireless
|
||||
bluetooth = on
|
||||
wifi = on
|
||||
wwan = none (no device)
|
||||
|
||||
hci0(btusb) : bluetooth, not connected
|
||||
wlp8s0(iwlwifi) : wifi, connected, power management = on
|
||||
|
||||
+++ Audio
|
||||
/sys/module/snd_hda_intel/parameters/power_save = 1
|
||||
/sys/module/snd_hda_intel/parameters/power_save_controller = Y
|
||||
|
||||
+++ Runtime Power Management
|
||||
Device blacklist = (not configured)
|
||||
Driver blacklist = amdgpu nouveau nvidia radeon pcieport
|
||||
|
||||
/sys/bus/pci/devices/0000:00:00.0/power/control = auto (0x060000, Host bridge, skl_uncore)
|
||||
/sys/bus/pci/devices/0000:00:01.0/power/control = auto (0x060400, PCI bridge, pcieport)
|
||||
/sys/bus/pci/devices/0000:00:02.0/power/control = auto (0x030000, VGA compatible controller, i915)
|
||||
|
||||
......
|
||||
|
||||
+++ USB
|
||||
Autosuspend = disabled
|
||||
Device whitelist = (not configured)
|
||||
Device blacklist = (not configured)
|
||||
Bluetooth blacklist = disabled
|
||||
Phone blacklist = disabled
|
||||
WWAN blacklist = enabled
|
||||
|
||||
Bus 002 Device 001 ID 1d6b:0003 control = auto, autosuspend_delay_ms = 0 -- Linux Foundation 3.0 root hub (hub)
|
||||
Bus 001 Device 003 ID 174f:14e8 control = auto, autosuspend_delay_ms = 2000 -- Syntek (uvcvideo)
|
||||
Bus 001 Device 002 ID 17ef:6053 control = on, autosuspend_delay_ms = 2000 -- Lenovo (usbhid)
|
||||
Bus 001 Device 004 ID 8087:0a2b control = auto, autosuspend_delay_ms = 2000 -- Intel Corp. (btusb)
|
||||
Bus 001 Device 001 ID 1d6b:0002 control = auto, autosuspend_delay_ms = 0 -- Linux Foundation 2.0 root hub (hub)
|
||||
|
||||
+++ Battery Status
|
||||
/sys/class/power_supply/BAT0/manufacturer = SMP
|
||||
/sys/class/power_supply/BAT0/model_name = L14M4P23
|
||||
/sys/class/power_supply/BAT0/cycle_count = (not supported)
|
||||
/sys/class/power_supply/BAT0/energy_full_design = 60000 [mWh]
|
||||
/sys/class/power_supply/BAT0/energy_full = 51690 [mWh]
|
||||
/sys/class/power_supply/BAT0/energy_now = 50140 [mWh]
|
||||
/sys/class/power_supply/BAT0/power_now = 12185 [mW]
|
||||
/sys/class/power_supply/BAT0/status = Discharging
|
||||
|
||||
Charge = 97.0 [%]
|
||||
Capacity = 86.2 [%]
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/tlp-increase-optimize-linux-laptop-battery-life/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.2daygeek.com/check-laptop-battery-status-and-charging-state-in-linux-terminal/
|
||||
[2]: https://www.2daygeek.com/powertop-monitors-laptop-battery-usage-linux/
|
||||
[3]: https://www.2daygeek.com/monitor-laptop-battery-charging-state-linux/
|
||||
[4]: https://linrunner.de/en/tlp/docs/tlp-linux-advanced-power-management.html
|
||||
[5]: https://www.2daygeek.com/category/package-management/
|
||||
[6]: https://www.2daygeek.com/dnf-command-examples-manage-packages-fedora-system/
|
||||
[7]: https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[8]: https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[9]: https://www.2daygeek.com/pacman-command-examples-manage-packages-arch-linux-system/
|
||||
[10]: https://www.2daygeek.com/yum-command-examples-manage-packages-rhel-centos-systems/
|
||||
[11]: https://www.2daygeek.com/zypper-command-examples-manage-packages-opensuse-system/
|
@ -0,0 +1,354 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (cycoe)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10874-1.html)
|
||||
[#]: subject: (Using Pygame to move your game character around)
|
||||
[#]: via: (https://opensource.com/article/17/12/game-python-moving-player)
|
||||
[#]: author: (Seth Kenlon https://opensource.com/users/seth)
|
||||
|
||||
用 Pygame 使你的游戏角色移动起来
|
||||
======
|
||||
> 在本系列的第四部分,学习如何编写移动游戏角色的控制代码。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/python4-game.png?itok=tXFHaLdt)
|
||||
|
||||
在这个系列的第一篇文章中,我解释了如何使用 Python 创建一个简单的[基于文本的骰子游戏][1]。在第二部分中,我向你们展示了如何从头开始构建游戏,即从 [创建游戏的环境][2] 开始。然后在第三部分,我们[创建了一个玩家妖精][3],并且使它在你的(而不是空的)游戏世界内生成。你可能已经注意到,如果你不能移动你的角色,那么游戏不是那么有趣。在本篇文章中,我们将使用 Pygame 来添加键盘控制,如此一来你就可以控制你的角色的移动。
|
||||
|
||||
在 Pygame 中有许多函数可以用来添加(除键盘外的)其他控制,但如果你正在敲击 Python 代码,那么你一定是有一个键盘的,这将成为我们接下来会使用的控制方式。一旦你理解了键盘控制,你可以自己去探索其他选项。
|
||||
|
||||
在本系列的第二篇文章中,你已经为退出游戏创建了一个按键,移动角色的(按键)原则也是相同的。但是,使你的角色移动起来要稍微复杂一点。
|
||||
|
||||
让我们从简单的部分入手:设置控制器按键。
|
||||
|
||||
### 为控制你的玩家妖精设置按键
|
||||
|
||||
在 IDLE、Ninja-IDE 或文本编辑器中打开你的 Python 游戏脚本。
|
||||
|
||||
因为游戏需要时刻“监听”键盘事件,所以你写的代码需要连续运行。你知道应该把需要在游戏周期中持续运行的代码放在哪里吗?
|
||||
|
||||
如果你回答“放在主循环中”,那么你是正确的!记住除非代码在循环中,否则(大多数情况下)它只会运行仅一次。如果它被写在一个从未被使用的类或函数中,它可能根本不会运行。
|
||||
|
||||
要使 Python 监听传入的按键,将如下代码添加到主循环。目前的代码还不能产生任何的效果,所以使用 `print` 语句来表示成功的信号。这是一种常见的调试技术。
|
||||
|
||||
```
|
||||
while main == True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit(); sys.exit()
|
||||
main = False
|
||||
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
print('left')
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
print('right')
|
||||
if event.key == pygame.K_UP or event.key == ord('w'):
|
||||
print('jump')
|
||||
|
||||
if event.type == pygame.KEYUP:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
print('left stop')
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
print('right stop')
|
||||
if event.key == ord('q'):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
main = False
|
||||
```
|
||||
|
||||
一些人偏好使用键盘字母 `W`、`A`、`S` 和 `D` 来控制玩家角色,而另一些偏好使用方向键。因此确保你包含了两种选项。
|
||||
|
||||
注意:当你在编程时,同时考虑所有用户是非常重要的。如果你写代码只是为了自己运行,那么很可能你会成为你写的程序的唯一用户。更重要的是,如果你想找一个通过写代码赚钱的工作,你写的代码就应该让所有人都能运行。给你的用户选择权,比如提供使用方向键或 WASD 的选项,是一个优秀程序员的标志。
|
||||
|
||||
使用 Python 启动你的游戏,并在你按下“上下左右”方向键或 `A`、`D` 和 `W` 键的时候查看控制台窗口的输出。
|
||||
|
||||
```
|
||||
$ python ./your-name_game.py
|
||||
left
|
||||
left stop
|
||||
right
|
||||
right stop
|
||||
jump
|
||||
```
|
||||
|
||||
这验证了 Pygame 可以正确地检测按键。现在是时候来完成使妖精移动的艰巨任务了。
|
||||
|
||||
### 编写玩家移动函数
|
||||
|
||||
为了使你的妖精移动起来,你必须为你的妖精创建一个属性代表移动。当你的妖精没有在移动时,这个变量被设为 `0`。
|
||||
|
||||
如果你正在为你的妖精设置动画,或者你决定在将来为它设置动画,你还必须跟踪帧来使走路循环保持在轨迹上。
|
||||
|
||||
在 `Player` 类中创建如下变量。开头两行作为上下文对照(如果你一直跟着做,你的代码中就已经有这两行),因此只需要添加最后三行:
|
||||
|
||||
```
|
||||
def __init__(self):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.movex = 0 # 沿 X 方向移动
|
||||
self.movey = 0 # 沿 Y 方向移动
|
||||
self.frame = 0 # 帧计数
|
||||
```
|
||||
|
||||
设置好了这些变量,是时候去为妖精移动编写代码了。
|
||||
|
||||
玩家妖精不需要时刻响应控制,有时它并没有在移动。控制妖精的代码,仅仅只是玩家妖精所有能做的事情中的一小部分。在 Python 中,当你想要使一个对象做某件事并独立于剩余其他代码时,你可以将你的新代码放入一个函数。Python 的函数以关键词 `def` 开头,(该关键词)代表了定义函数。
|
||||
|
||||
在你的 `Player` 类中创建如下函数,来为你的妖精在屏幕上的位置增加几个像素。现在先不要担心你增加几个像素,这将在后续的代码中确定。
|
||||
|
||||
```
|
||||
def control(self,x,y):
|
||||
'''
|
||||
控制玩家移动
|
||||
'''
|
||||
self.movex += x
|
||||
self.movey += y
|
||||
```
|
||||
|
||||
为了在 Pygame 中移动妖精,你需要告诉 Python 在新的位置重绘妖精,以及这个新位置在哪里。
|
||||
|
||||
因为玩家妖精并不总是在移动,所以更新只需要是 Player 类中的一个函数。将此函数添加前面创建的 `control` 函数之后。
|
||||
|
||||
要使妖精看起来像是在行走(或者飞行,或是你的妖精应该做的任何事),你需要在按下适当的键时改变它在屏幕上的位置。要让它在屏幕上移动,你需要将它的位置(由 `self.rect.x` 和 `self.rect.y` 属性指定)重新定义为当前位置加上已应用的任意 `movex` 或 `movey`。(移动的像素数量将在后续进行设置。)
|
||||
|
||||
```
|
||||
def update(self):
|
||||
'''
|
||||
更新妖精位置
|
||||
'''
|
||||
self.rect.x = self.rect.x + self.movex
|
||||
```
|
||||
|
||||
对 Y 方向做同样的处理:
|
||||
|
||||
```
|
||||
self.rect.y = self.rect.y + self.movey
|
||||
```
|
||||
|
||||
对于动画,在妖精移动时推进动画帧,并使用相应的动画帧作为玩家的图像:
|
||||
|
||||
```
|
||||
# 向左移动
|
||||
if self.movex < 0:
|
||||
self.frame += 1
|
||||
if self.frame > 3*ani:
|
||||
self.frame = 0
|
||||
self.image = self.images[self.frame//ani]
|
||||
|
||||
# 向右移动
|
||||
if self.movex > 0:
|
||||
self.frame += 1
|
||||
if self.frame > 3*ani:
|
||||
self.frame = 0
|
||||
self.image = self.images[(self.frame//ani)+4]
|
||||
```
|
||||
|
||||
通过设置一个变量来告诉代码为你的妖精位置增加多少像素,然后在触发你的玩家妖精的函数时使用这个变量。
|
||||
|
||||
首先,在你的设置部分创建这个变量。在如下代码中,开头两行是上下文对照,因此只需要在你的脚本中增加第三行代码:
|
||||
|
||||
```
|
||||
player_list = pygame.sprite.Group()
|
||||
player_list.add(player)
|
||||
steps = 10 # 移动多少个像素
|
||||
```
|
||||
|
||||
现在你已经有了适当的函数和变量,使用你的按键来触发函数并将变量传递给你的妖精。
|
||||
|
||||
为此,将主循环中的 `print` 语句替换为玩家妖精的名字(`player`)、函数(`.control`)以及你希望玩家妖精在每个循环中沿 X 轴和 Y 轴移动的步数。
|
||||
|
||||
```
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
player.control(-steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_UP or event.key == ord('w'):
|
||||
print('jump')
|
||||
|
||||
if event.type == pygame.KEYUP:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
player.control(-steps,0)
|
||||
if event.key == ord('q'):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
main = False
|
||||
```
|
||||
|
||||
记住,`steps` 变量代表了当一个按键被按下时,你的妖精会移动多少个像素。如果当你按下 `D` 或右方向键时,你的妖精的位置增加了 10 个像素。那么当你停止按下这个键时,你必须(将 `step`)减 10(`-steps`)来使你的妖精的动量回到 0。
|
||||
|
||||
现在尝试你的游戏。注意:它不会像你预想的那样运行。
|
||||
|
||||
为什么你的妖精仍无法移动?因为主循环还没有调用 `update` 函数。
|
||||
|
||||
将如下代码加入到你的主循环中来告诉 Python 更新你的玩家妖精的位置。增加带注释的那行:
|
||||
|
||||
```
|
||||
player.update() # 更新玩家位置
|
||||
player_list.draw(world)
|
||||
pygame.display.flip()
|
||||
clock.tick(fps)
|
||||
```
|
||||
|
||||
再次启动你的游戏来见证你的玩家妖精在你的命令下在屏幕上来回移动。现在还没有垂直方向的移动,因为这部分函数会被重力控制,不过这是另一篇文章中的课程了。
|
||||
|
||||
与此同时,如果你拥有一个摇杆,你可以尝试阅读 Pygame 中 [joystick][4] 模块相关的文档,看看你是否能通过这种方式让你的妖精移动起来。或者,看看你是否能通过[鼠标][5]与你的妖精互动。
|
||||
|
||||
最重要的是,玩的开心!
|
||||
|
||||
### 本教程中用到的所有代码
|
||||
|
||||
为了方便查阅,以下是目前本系列文章用到的所有代码。
|
||||
|
||||
```
|
||||
#!/usr/bin/env python3
|
||||
# 绘制世界
|
||||
# 添加玩家和玩家控制
|
||||
# 添加玩家移动控制
|
||||
|
||||
# GNU All-Permissive License
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved. This file is offered as-is,
|
||||
# without any warranty.
|
||||
|
||||
import pygame
|
||||
import sys
|
||||
import os
|
||||
|
||||
'''
|
||||
Objects
|
||||
'''
|
||||
|
||||
class Player(pygame.sprite.Sprite):
|
||||
'''
|
||||
生成玩家
|
||||
'''
|
||||
def __init__(self):
|
||||
pygame.sprite.Sprite.__init__(self)
|
||||
self.movex = 0
|
||||
self.movey = 0
|
||||
self.frame = 0
|
||||
self.images = []
|
||||
for i in range(1,5):
|
||||
img = pygame.image.load(os.path.join('images','hero' + str(i) + '.png')).convert()
|
||||
img.convert_alpha()
|
||||
img.set_colorkey(ALPHA)
|
||||
self.images.append(img)
|
||||
self.image = self.images[0]
|
||||
self.rect = self.image.get_rect()
|
||||
|
||||
def control(self,x,y):
|
||||
'''
|
||||
控制玩家移动
|
||||
'''
|
||||
self.movex += x
|
||||
self.movey += y
|
||||
|
||||
def update(self):
|
||||
'''
|
||||
更新妖精位置
|
||||
'''
|
||||
|
||||
self.rect.x = self.rect.x + self.movex
|
||||
self.rect.y = self.rect.y + self.movey
|
||||
|
||||
# 向左移动
|
||||
if self.movex < 0:
|
||||
self.frame += 1
|
||||
if self.frame > 3*ani:
|
||||
self.frame = 0
|
||||
self.image = self.images[self.frame//ani]
|
||||
|
||||
# 向右移动
|
||||
if self.movex > 0:
|
||||
self.frame += 1
|
||||
if self.frame > 3*ani:
|
||||
self.frame = 0
|
||||
self.image = self.images[(self.frame//ani)+4]
|
||||
|
||||
|
||||
'''
|
||||
设置
|
||||
'''
|
||||
worldx = 960
|
||||
worldy = 720
|
||||
|
||||
fps = 40 # 帧刷新率
|
||||
ani = 4 # 动画循环
|
||||
clock = pygame.time.Clock()
|
||||
pygame.init()
|
||||
main = True
|
||||
|
||||
BLUE = (25,25,200)
|
||||
BLACK = (23,23,23 )
|
||||
WHITE = (254,254,254)
|
||||
ALPHA = (0,255,0)
|
||||
|
||||
world = pygame.display.set_mode([worldx,worldy])
|
||||
backdrop = pygame.image.load(os.path.join('images','stage.png')).convert()
|
||||
backdropbox = world.get_rect()
|
||||
player = Player() # 生成玩家
|
||||
player.rect.x = 0
|
||||
player.rect.y = 0
|
||||
player_list = pygame.sprite.Group()
|
||||
player_list.add(player)
|
||||
steps = 10 # 移动速度
|
||||
|
||||
'''
|
||||
主循环
|
||||
'''
|
||||
while main == True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit(); sys.exit()
|
||||
main = False
|
||||
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
player.control(-steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_UP or event.key == ord('w'):
|
||||
print('jump')
|
||||
|
||||
if event.type == pygame.KEYUP:
|
||||
if event.key == pygame.K_LEFT or event.key == ord('a'):
|
||||
player.control(steps,0)
|
||||
if event.key == pygame.K_RIGHT or event.key == ord('d'):
|
||||
player.control(-steps,0)
|
||||
if event.key == ord('q'):
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
main = False
|
||||
|
||||
# world.fill(BLACK)
|
||||
world.blit(backdrop, backdropbox)
|
||||
player.update()
|
||||
player_list.draw(world) # 更新玩家位置
|
||||
pygame.display.flip()
|
||||
clock.tick(fps)
|
||||
```
|
||||
|
||||
你已经学了很多,但还仍有许多可以做。在接下来的几篇文章中,你将实现添加敌方妖精、模拟重力等等。与此同时,练习 Python 吧!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/17/12/game-python-moving-player
|
||||
|
||||
作者:[Seth Kenlon][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[cycoe](https://github.com/cycoe)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/seth
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://linux.cn/article-9071-1.html
|
||||
[2]: https://linux.cn/article-10850-1.html
|
||||
[3]: https://linux.cn/article-10858-1.html
|
||||
[4]: http://pygame.org/docs/ref/joystick.html
|
||||
[5]: http://pygame.org/docs/ref/mouse.html#module-pygame.mouse
|
172
published/201905/20190107 Aliases- To Protect and Serve.md
Normal file
172
published/201905/20190107 Aliases- To Protect and Serve.md
Normal file
@ -0,0 +1,172 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10918-1.html)
|
||||
[#]: subject: (Aliases: To Protect and Serve)
|
||||
[#]: via: (https://www.linux.com/blog/learn/2019/1/aliases-protect-and-serve)
|
||||
[#]: author: (Paul Brown https://www.linux.com/users/bro66)
|
||||
|
||||
命令别名:保护和服务
|
||||
======
|
||||
|
||||
> Linux shell 允许你将命令彼此链接在一起,一次触发执行复杂的操作,并且可以对此创建别名作为快捷方式。
|
||||
|
||||
![](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/prairie-path_1920.jpg?itok=wRARsM7p)
|
||||
|
||||
让我们将继续我们的别名系列。到目前为止,你可能已经阅读了我们的[关于别名的第一篇文章][1],并且应该非常清楚它们是如何为你省去很多麻烦的最简单方法。例如,你已经看到它们帮助我们减少了输入,让我们看看别名派上用场的其他几个案例。
|
||||
|
||||
### 别名即快捷方式
|
||||
|
||||
Linux shell 最美妙的事情之一是可以使用数以万计的选项和把命令连接在一起执行真正复杂的操作。好吧,也许这种美丽是在旁观者的眼中的,但是我们觉得这个功能很实用。
|
||||
|
||||
不利的一面是,你经常需要记得难以记忆或难以打字出来的命令组合。比如说硬盘上的空间非常宝贵,而你想要做一些清洁工作。你的第一步可能是寻找隐藏在你的家目录里的东西。你可以用来判断的一个标准是查找不再使用的内容。`ls` 可以帮助你:
|
||||
|
||||
```
|
||||
ls -lct
|
||||
```
|
||||
|
||||
上面的命令显示了每个文件和目录的详细信息(`-l`),并显示了每一项上次访问的时间(`-c`),然后它按从最近访问到最少访问的顺序排序这个列表(`-t`)。
|
||||
|
||||
这难以记住吗?你可能不会每天都使用 `-c` 和 `-t` 选项,所以也许是吧。无论如何,定义一个别名,如:
|
||||
|
||||
```
|
||||
alias lt='ls -lct'
|
||||
```
|
||||
|
||||
会更容易一些。
|
||||
|
||||
然后,你也可能希望列表首先显示最旧的文件:
|
||||
|
||||
```
|
||||
alias lo='lt -F | tac'
|
||||
```
|
||||
|
||||
![aliases][3]
|
||||
|
||||
*图 1:使用 lt 和 lo 别名。*
|
||||
|
||||
这里有一些有趣的事情。首先,我们使用别名(`lt`)来创建另一个别名 —— 这是完全可以的。其次,我们将一个新参数传递给 `lt`(后者又通过 `lt` 别名的定义传递给了 `ls`)。
|
||||
|
||||
`-F` 选项会将特殊符号附加到项目的名称后,以便更好地区分常规文件(没有符号)和可执行文件(附加了 `*`)、目录文件(以 `/` 结尾),以及所有链接文件、符号链接文件(以 `@` 符号结尾)等等。`-F` 选项是当你回归到单色终端的日子里,没有其他方法可以轻松看到列表项之间的差异时用的。在这里使用它是因为当你将输出从 `lt` 传递到 `tac` 时,你会丢失 `ls` 的颜色。
|
||||
|
||||
第三件我们需要注意的事情是我们使用了管道。管道用于你将一个命令的输出传递给另外一个命令时。第二个命令可以使用这些输出作为它的输入。在包括 Bash 在内的许多 shell 里,你可以使用管道符(`|`) 来做传递。
|
||||
|
||||
在这里,你将来自 `lt -F` 的输出导给 `tac`。`tac` 这个命令有点玩笑的意思,你或许听说过 `cat` 命令,它名义上用于将文件彼此连接(con`cat`),而在实践中,它被用于将一个文件的内容打印到终端。`tac` 做的事情一样,但是它是以逆序将接收到的内容输出出来。明白了吗?`cat` 和 `tac`,技术人有时候也挺有趣的。
|
||||
|
||||
`cat` 和 `tac` 都能输出通过管道传递过来的内容,在这里,也就是一个按时间顺序排序的文件列表。
|
||||
|
||||
那么,在有些离题之后,最终我们得到的就是这个列表将当前目录中的文件和目录以新鲜度的逆序列出(即老的在前)。
|
||||
|
||||
最后你需要注意的是,当在当前目录或任何目录运行 `lt` 时:
|
||||
|
||||
```
|
||||
# 这可以工作:
|
||||
lt
|
||||
# 这也可以:
|
||||
lt /some/other/directory
|
||||
```
|
||||
|
||||
……而 `lo` 只能在当前目录奏效:
|
||||
|
||||
```
|
||||
# 这可工作:
|
||||
lo
|
||||
# 而这不行:
|
||||
lo /some/other/directory
|
||||
```
|
||||
|
||||
这是因为 Bash 会展开别名的组分。当你键入:
|
||||
|
||||
```
|
||||
lt /some/other/directory
|
||||
```
|
||||
|
||||
Bash 实际上运行的是:
|
||||
|
||||
```
|
||||
ls -lct /some/other/directory
|
||||
```
|
||||
|
||||
这是一个有效的 Bash 命令。
|
||||
|
||||
而当你键入:
|
||||
|
||||
```
|
||||
lo /some/other/directory
|
||||
```
|
||||
|
||||
Bash 试图运行:
|
||||
|
||||
```
|
||||
ls -lct -F | tac /some/other/directory
|
||||
```
|
||||
|
||||
这不是一个有效的命令,主要是因为 `/some/other/directory` 是个目录,而 `cat` 和 `tac` 不能用于目录。
|
||||
|
||||
### 更多的别名快捷方式
|
||||
|
||||
* `alias lll='ls -R'` 会打印出目录的内容,并深入到子目录里面打印子目录的内容,以及子目录的子目录,等等。这是一个查看一个目录下所有内容的方式。
|
||||
* `mkdir='mkdir -pv'` 可以让你一次性创建目录下的目录。按照 `mkdir` 的基本形式,要创建一个包含子目录的目录,你必须这样:
|
||||
|
||||
```
|
||||
mkdir newdir
|
||||
mkdir newdir/subdir
|
||||
```
|
||||
|
||||
或这样:
|
||||
|
||||
```
|
||||
mkdir -p newdir/subdir
|
||||
```
|
||||
|
||||
而用这个别名你将只需要这样就行:
|
||||
|
||||
```
|
||||
mkdir newdir/subdir
|
||||
```
|
||||
|
||||
你的新 `mkdir` 也会告诉你创建子目录时都做了什么。
|
||||
|
||||
### 别名也是一种保护
|
||||
|
||||
别名的另一个好处是它可以作为防止你意外地删除或覆写已有的文件的保护措施。你可能听说过这个 Linux 新用户的传言,当他们以 root 身份运行:
|
||||
|
||||
```
|
||||
rm -rf /
|
||||
```
|
||||
|
||||
整个系统就爆了。而决定输入如下命令的用户:
|
||||
|
||||
```
|
||||
rm -rf /some/directory/ *
|
||||
```
|
||||
|
||||
就很好地干掉了他们的家目录的全部内容。这里不小心键入的目录和 `*` 之间的那个空格有时候很容易就会被忽视掉。
|
||||
|
||||
这两种情况我们都可以通过 `alias rm='rm -i'` 别名来避免。`-i` 选项会使 `rm` 询问用户是否真的要做这个操作,在你对你的文件系统做出不可弥补的损失之前给你第二次机会。
|
||||
|
||||
对于 `cp` 也是一样,它能够覆盖一个文件而不会给你任何提示。创建一个类似 `alias cp='cp -i'` 来保持安全吧。
|
||||
|
||||
### 下一次
|
||||
|
||||
我们越来越深入到了脚本领域,下一次,我们将沿着这个方向,看看如何在命令行组合命令以给你真正的乐趣,并可靠地解决系统管理员每天面临的问题。
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.linux.com/blog/learn/2019/1/aliases-protect-and-serve
|
||||
|
||||
作者:[Paul Brown][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.linux.com/users/bro66
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://linux.cn/article-10377-1.html
|
||||
[2]: https://www.linux.com/files/images/fig01png-0
|
||||
[3]: https://www.linux.com/sites/lcom/files/styles/rendered_file/public/fig01_0.png?itok=crqTm_va (aliases)
|
||||
[4]: https://www.linux.com/licenses/category/used-permission
|
@ -0,0 +1,210 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10884-1.html)
|
||||
[#]: subject: (Virtual filesystems in Linux: Why we need them and how they work)
|
||||
[#]: via: (https://opensource.com/article/19/3/virtual-filesystems-linux)
|
||||
[#]: author: (Alison Chariken )
|
||||
|
||||
详解 Linux 中的虚拟文件系统
|
||||
======
|
||||
|
||||
> 虚拟文件系统是一种神奇的抽象,它使得 “一切皆文件” 哲学在 Linux 中成为了可能。
|
||||
|
||||
![](https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/documents_papers_file_storage_work.png?itok=YlXpAqAJ)
|
||||
|
||||
什么是文件系统?根据早期的 Linux 贡献者和作家 [Robert Love][1] 所说,“文件系统是一个遵循特定结构的数据的分层存储。” 不过,这种描述也同样适用于 VFAT(<ruby>虚拟文件分配表<rt>Virtual File Allocation Table</rt></ruby>)、Git 和[Cassandra][2](一种 [NoSQL 数据库][3])。那么如何区别文件系统呢?
|
||||
|
||||
### 文件系统基础概念
|
||||
|
||||
Linux 内核要求文件系统必须是实体,它还必须在持久对象上实现 `open()`、`read()` 和 `write()` 方法,并且这些实体需要有与之关联的名字。从 [面向对象编程][4] 的角度来看,内核将通用文件系统视为一个抽象接口,这三大函数是“虚拟”的,没有默认定义。因此,内核的默认文件系统实现被称为虚拟文件系统(VFS)。
|
||||
|
||||
![][5]
|
||||
|
||||
*如果我们能够 `open()`、`read()` 和 `write()`,它就是一个文件,如这个主控台会话所示。*
|
||||
|
||||
VFS 是著名的类 Unix 系统中 “一切皆文件” 概念的基础。让我们看一下它有多奇怪,上面的小小演示体现了字符设备 `/dev/console` 实际的工作。该图显示了一个在虚拟电传打字控制台(tty)上的交互式 Bash 会话。将一个字符串发送到虚拟控制台设备会使其显示在虚拟屏幕上。而 VFS 甚至还有其它更奇怪的属性。例如,它[可以在其中寻址][6]。
|
||||
|
||||
我们熟悉的文件系统如 ext4、NFS 和 /proc 都在名为 [file_operations] [7] 的 C 语言数据结构中提供了三大函数的定义。此外,个别的文件系统会以熟悉的面向对象的方式扩展和覆盖了 VFS 功能。正如 Robert Love 指出的那样,VFS 的抽象使 Linux 用户可以轻松地将文件复制到(或复制自)外部操作系统或抽象实体(如管道),而无需担心其内部数据格式。在用户空间这一侧,通过系统调用,进程可以使用文件系统方法之一 `read()` 从文件复制到内核的数据结构中,然后使用另一种文件系统的方法 `write()` 输出数据。
|
||||
|
||||
属于 VFS 基本类型的函数定义本身可以在内核源代码的 [fs/*.c 文件][8] 中找到,而 `fs/` 的子目录中包含了特定的文件系统。内核还包含了类似文件系统的实体,例如 cgroup、`/dev` 和 tmpfs,在引导过程的早期需要它们,因此定义在内核的 `init/` 子目录中。请注意,cgroup、`/dev` 和 tmpfs 不会调用 `file_operations` 的三大函数,而是直接读取和写入内存。
|
||||
|
||||
下图大致说明了用户空间如何访问通常挂载在 Linux 系统上的各种类型文件系统。像管道、dmesg 和 POSIX 时钟这样的结构在此图中未显示,它们也实现了 `struct file_operations`,而且其访问也要通过 VFS 层。
|
||||
|
||||
![How userspace accesses various types of filesystems][9]
|
||||
|
||||
VFS 是个“垫片层”,位于系统调用和特定 `file_operations` 的实现(如 ext4 和 procfs)之间。然后,`file_operations` 函数可以与特定于设备的驱动程序或内存访问器进行通信。tmpfs、devtmpfs 和 cgroup 不使用 `file_operations` 而是直接访问内存。
|
||||
|
||||
VFS 的存在促进了代码重用,因为与文件系统相关的基本方法不需要由每种文件系统类型重新实现。代码重用是一种被广泛接受的软件工程最佳实践!唉,但是如果重用的代码[引入了严重的错误][10],那么继承常用方法的所有实现都会受到影响。
|
||||
|
||||
### /tmp:一个小提示
|
||||
|
||||
找出系统中存在的 VFS 的简单方法是键入 `mount | grep -v sd | grep -v :/`,在大多数计算机上,它将列出所有未驻留在磁盘上,同时也不是 NFS 的已挂载文件系统。其中一个列出的 VFS 挂载肯定是 `/tmp`,对吧?
|
||||
|
||||
![Man with shocked expression][11]
|
||||
|
||||
*谁都知道把 /tmp 放在物理存储设备上简直是疯了!图片:<https://tinyurl.com/ybomxyfo>*
|
||||
|
||||
为什么把 `/tmp` 留在存储设备上是不可取的?因为 `/tmp` 中的文件是临时的(!),并且存储设备比内存慢,所以创建了 tmpfs 这种文件系统。此外,比起内存,物理设备频繁写入更容易磨损。最后,`/tmp` 中的文件可能包含敏感信息,因此在每次重新启动时让它们消失是一项功能。
|
||||
|
||||
不幸的是,默认情况下,某些 Linux 发行版的安装脚本仍会在存储设备上创建 /tmp。如果你的系统出现这种情况,请不要绝望。按照一直优秀的 [Arch Wiki][12] 上的简单说明来解决问题就行,记住分配给 tmpfs 的内存就不能用于其他目的了。换句话说,包含了大文件的庞大的 tmpfs 可能会让系统耗尽内存并崩溃。
|
||||
|
||||
另一个提示:编辑 `/etc/fstab` 文件时,请务必以换行符结束,否则系统将无法启动。(猜猜我怎么知道。)
|
||||
|
||||
### /proc 和 /sys
|
||||
|
||||
除了 `/tmp` 之外,大多数 Linux 用户最熟悉的 VFS 是 `/proc` 和 `/sys`。(`/dev` 依赖于共享内存,而没有 `file_operations` 结构)。为什么有两种呢?让我们来看看更多细节。
|
||||
|
||||
procfs 为用户空间提供了内核及其控制的进程的瞬时状态的快照。在 `/proc` 中,内核发布有关其提供的设施的信息,如中断、虚拟内存和调度程序。此外,`/proc/sys` 是存放可以通过 [sysctl 命令][13]配置的设置的地方,可供用户空间访问。单个进程的状态和统计信息在 `/proc/<PID>` 目录中报告。
|
||||
|
||||
![Console][14]
|
||||
|
||||
*/proc/meminfo 是一个空文件,但仍包含有价值的信息。*
|
||||
|
||||
`/proc` 文件的行为说明了 VFS 可以与磁盘上的文件系统不同。一方面,`/proc/meminfo` 包含了可由命令 `free` 展现出来的信息。另一方面,它还是空的!怎么会这样?这种情况让人联想起康奈尔大学物理学家 N. David Mermin 在 1985 年写的一篇名为《[没有人看见月亮的情况吗?][15]现实和量子理论》。事实是当进程从 `/proc` 请求数据时内核再收集有关内存的统计信息,而且当没有人查看它时,`/proc` 中的文件实际上没有任何内容。正如 [Mermin 所说][16],“这是一个基本的量子学说,一般来说,测量不会揭示被测属性的预先存在的价值。”(关于月球的问题的答案留作练习。)
|
||||
|
||||
![Full moon][17]
|
||||
|
||||
*当没有进程访问它们时,/proc 中的文件为空。([来源][18])*
|
||||
|
||||
procfs 的空文件是有道理的,因为那里可用的信息是动态的。sysfs 的情况则不同。让我们比较一下 `/proc` 与 `/sys` 中不为空的文件数量。
|
||||
|
||||
![](https://opensource.com/sites/default/files/uploads/virtualfilesystems_6-filesize.png)
|
||||
|
||||
procfs 只有一个不为空的文件,即导出的内核配置,这是一个例外,因为每次启动只需要生成一次。另一方面,`/sys` 有许多更大一些的文件,其中大多数由一页内存组成。通常,sysfs 文件只包含一个数字或字符串,与通过读取 `/proc/meminfo` 等文件生成的信息表格形成鲜明对比。
|
||||
|
||||
sysfs 的目的是将内核称为 “kobject” 的可读写属性公开给用户空间。kobject 的唯一目的是引用计数:当删除对 kobject 的最后一个引用时,系统将回收与之关联的资源。然而,`/sys` 构成了内核著名的“[到用户空间的稳定 ABI][19]”,它的大部分内容[在任何情况下都没有人能“破坏”][20]。但这并不意味着 sysfs 中的文件是静态,这与易失性对象的引用计数相反。
|
||||
|
||||
内核的稳定 ABI 限制了 `/sys` 中可能出现的内容,而不是任何给定时刻实际存在的内容。列出 sysfs 中文件的权限可以了解如何设置或读取设备、模块、文件系统等的可配置、可调参数。逻辑上强调 procfs 也是内核稳定 ABI 的一部分的结论,尽管内核的[文档][19]没有明确说明。
|
||||
|
||||
![Console][21]
|
||||
|
||||
*sysfs 中的文件确切地描述了实体的每个属性,并且可以是可读的、可写的,或两者兼而有之。文件中的“0”表示 SSD 不可移动的存储设备。*
|
||||
|
||||
### 用 eBPF 和 bcc 工具一窥 VFS 内部
|
||||
|
||||
了解内核如何管理 sysfs 文件的最简单方法是观察它的运行情况,在 ARM64 或 x86_64 上观看的最简单方法是使用 eBPF。eBPF(<ruby>扩展的伯克利数据包过滤器<rt>extended Berkeley Packet Filter</rt></ruby>)由[在内核中运行的虚拟机][22]组成,特权用户可以从命令行进行查询。内核源代码告诉读者内核可以做什么;而在一个启动的系统上运行 eBPF 工具会显示内核实际上做了什么。
|
||||
|
||||
令人高兴的是,通过 [bcc][23] 工具入门使用 eBPF 非常容易,这些工具在[主要 Linux 发行版的软件包][24] 中都有,并且已经由 Brendan Gregg [给出了充分的文档说明][25]。bcc 工具是带有小段嵌入式 C 语言片段的 Python 脚本,这意味着任何对这两种语言熟悉的人都可以轻松修改它们。据当前统计,[bcc/tools 中有 80 个 Python 脚本][26],使得系统管理员或开发人员很有可能能够找到与她/他的需求相关的已有脚本。
|
||||
|
||||
要了解 VFS 在正在运行中的系统上的工作情况,请尝试使用简单的 [vfscount][27] 或 [vfsstat][28] 脚本,这可以看到每秒都会发生数十次对 `vfs_open()` 及其相关的调用。
|
||||
|
||||
![Console - vfsstat.py][29]
|
||||
|
||||
*vfsstat.py 是一个带有嵌入式 C 片段的 Python 脚本,它只是计数 VFS 函数调用。*
|
||||
|
||||
作为一个不太重要的例子,让我们看一下在运行的系统上插入 USB 记忆棒时 sysfs 中会发生什么。
|
||||
|
||||
![Console when USB is inserted][30]
|
||||
|
||||
*用 eBPF 观察插入 USB 记忆棒时 /sys 中会发生什么,简单的和复杂的例子。*
|
||||
|
||||
在上面的第一个简单示例中,只要 `sysfs_create_files()` 命令运行,[trace.py][31] bcc 工具脚本就会打印出一条消息。我们看到 `sysfs_create_files()` 由一个 kworker 线程启动,以响应 USB 棒的插入事件,但是它创建了什么文件?第二个例子说明了 eBPF 的强大能力。这里,`trace.py` 正在打印内核回溯(`-K` 选项)以及 `sysfs_create_files()` 创建的文件的名称。单引号内的代码段是一些 C 源代码,包括一个易于识别的格式字符串,所提供的 Python 脚本[引入 LLVM 即时编译器(JIT)][32] 来在内核虚拟机内编译和执行它。必须在第二个命令中重现完整的 `sysfs_create_files()` 函数签名,以便格式字符串可以引用其中一个参数。在此 C 片段中出错会导致可识别的 C 编译器错误。例如,如果省略 `-I` 参数,则结果为“无法编译 BPF 文本”。熟悉 C 或 Python 的开发人员会发现 bcc 工具易于扩展和修改。
|
||||
|
||||
插入 USB 记忆棒后,内核回溯显示 PID 7711 是一个 kworker 线程,它在 sysfs 中创建了一个名为 `events` 的文件。使用 `sysfs_remove_files()` 进行相应的调用表明,删除 USB 记忆棒会导致删除该 `events` 文件,这与引用计数的想法保持一致。在 USB 棒插入期间(未显示)在 eBPF 中观察 `sysfs_create_link()` 表明创建了不少于 48 个符号链接。
|
||||
|
||||
无论如何,`events` 文件的目的是什么?使用 [cscope][33] 查找函数 [`__device_add_disk()`][34] 显示它调用 `disk_add_events()`,并且可以将 “media_change” 或 “eject_request” 写入到该文件。这里,内核的块层通知用户空间该 “磁盘” 的出现和消失。考虑一下这种检查 USB 棒的插入的工作原理的方法与试图仅从源头中找出该过程的速度有多快。
|
||||
|
||||
### 只读根文件系统使得嵌入式设备成为可能
|
||||
|
||||
确实,没有人通过拔出电源插头来关闭服务器或桌面系统。为什么?因为物理存储设备上挂载的文件系统可能有挂起的(未完成的)写入,并且记录其状态的数据结构可能与写入存储器的内容不同步。当发生这种情况时,系统所有者将不得不在下次启动时等待 [fsck 文件系统恢复工具][35] 运行完成,在最坏的情况下,实际上会丢失数据。
|
||||
|
||||
然而,狂热爱好者会听说许多物联网和嵌入式设备,如路由器、恒温器和汽车现在都运行着 Linux。许多这些设备几乎完全没有用户界面,并且没有办法干净地让它们“解除启动”。想一想启动电池耗尽的汽车,其中[运行 Linux 的主机设备][36] 的电源会不断加电断电。当引擎最终开始运行时,系统如何在没有长时间 fsck 的情况下启动呢?答案是嵌入式设备依赖于[只读根文件系统][37](简称 ro-rootfs)。
|
||||
|
||||
![Photograph of a console][38]
|
||||
|
||||
*ro-rootfs 是嵌入式系统不经常需要 fsck 的原因。 来源:<https://tinyurl.com/yxoauoub>*
|
||||
|
||||
ro-rootfs 提供了许多优点,虽然这些优点不如耐用性那么显然。一个是,如果 Linux 进程不可以写入,那么恶意软件也无法写入 `/usr` 或 `/lib`。另一个是,基本上不可变的文件系统对于远程设备的现场支持至关重要,因为支持人员拥有理论上与现场相同的本地系统。也许最重要(但也是最微妙)的优势是 ro-rootfs 迫使开发人员在项目的设计阶段就决定好哪些系统对象是不可变的。处理 ro-rootfs 可能经常是不方便甚至是痛苦的,[编程语言中的常量变量][39]经常就是这样,但带来的好处很容易偿还这种额外的开销。
|
||||
|
||||
对于嵌入式开发人员,创建只读根文件系统确实需要做一些额外的工作,而这正是 VFS 的用武之地。Linux 需要 `/var` 中的文件可写,此外,嵌入式系统运行的许多流行应用程序会尝试在 `$HOME` 中创建配置的点文件。放在家目录中的配置文件的一种解决方案通常是预生成它们并将它们构建到 rootfs 中。对于 `/var`,一种方法是将其挂载在单独的可写分区上,而 `/` 本身以只读方式挂载。使用绑定或叠加挂载是另一种流行的替代方案。
|
||||
|
||||
### 绑定和叠加挂载以及在容器中的使用
|
||||
|
||||
运行 [man mount][40] 是了解<ruby>绑定挂载<rt>bind mount</rt></ruby>和<ruby>叠加挂载<rt>overlay mount</rt></ruby>的最好办法,这种方法使得嵌入式开发人员和系统管理员能够在一个路径位置创建文件系统,然后以另外一个路径将其提供给应用程序。对于嵌入式系统,这代表着可以将文件存储在 `/var` 中的不可写闪存设备上,但是在启动时将 tmpfs 中的路径叠加挂载或绑定挂载到 `/var` 路径上,这样应用程序就可以在那里随意写它们的内容了。下次加电时,`/var` 中的变化将会消失。叠加挂载为 tmpfs 和底层文件系统提供了联合,允许对 ro-rootfs 中的现有文件进行直接修改,而绑定挂载可以使新的空 tmpfs 目录在 ro-rootfs 路径中显示为可写。虽然叠加文件系统是一种适当的文件系统类型,而绑定挂载由 [VFS 命名空间工具][41] 实现的。
|
||||
|
||||
根据叠加挂载和绑定挂载的描述,没有人会对 [Linux 容器][42] 中大量使用它们感到惊讶。让我们通过运行 bcc 的 `mountsnoop` 工具监视当使用 [systemd-nspawn][43] 启动容器时会发生什么:
|
||||
|
||||
![Console - system-nspawn invocation][44]
|
||||
|
||||
*在 mountsnoop.py 运行的同时,system-nspawn 调用启动容器。*
|
||||
|
||||
让我们看看发生了什么:
|
||||
|
||||
![Console - Running mountsnoop][45]
|
||||
|
||||
*在容器 “启动” 期间运行 `mountsnoop` 可以看到容器运行时很大程度上依赖于绑定挂载。(仅显示冗长输出的开头)*
|
||||
|
||||
这里,`systemd-nspawn` 将主机的 procfs 和 sysfs 中的选定文件按其 rootfs 中的路径提供给容器。除了设置绑定挂载时的 `MS_BIND` 标志之外,`mount` 系统调用的一些其它标志用于确定主机命名空间和容器中的更改之间的关系。例如,绑定挂载可以将 `/proc` 和 `/sys` 中的更改传播到容器,也可以隐藏它们,具体取决于调用。
|
||||
|
||||
### 总结
|
||||
|
||||
理解 Linux 内部结构看似是一项不可能完成的任务,因为除了 Linux 用户空间应用程序和 glibc 这样的 C 库中的系统调用接口,内核本身也包含大量代码。取得进展的一种方法是阅读一个内核子系统的源代码,重点是理解面向用户空间的系统调用和头文件以及主要的内核内部接口,这里以 `file_operations` 表为例。`file_operations` 使得“一切都是文件”得以可以实际工作,因此掌握它们收获特别大。顶级 `fs/` 目录中的内核 C 源文件构成了虚拟文件系统的实现,虚拟文件系统是支持流行的文件系统和存储设备的广泛且相对简单的互操作性的垫片层。通过 Linux 命名空间进行绑定挂载和覆盖挂载是 VFS 魔术,它使容器和只读根文件系统成为可能。结合对源代码的研究,eBPF 内核工具及其 bcc 接口使得探测内核比以往任何时候都更简单。
|
||||
|
||||
非常感谢 [Akkana Peck][46] 和 [Michael Eager][47] 的评论和指正。
|
||||
|
||||
Alison Chaiken 也于 3 月 7 日至 10 日在加利福尼亚州帕萨迪纳举行的第 17 届南加州 Linux 博览会([SCaLE 17x][49])上演讲了[本主题][48]。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/3/virtual-filesystems-linux
|
||||
|
||||
作者:[Alison Chariken][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/chaiken
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.pearson.com/us/higher-education/program/Love-Linux-Kernel-Development-3rd-Edition/PGM202532.html
|
||||
[2]: http://cassandra.apache.org/
|
||||
[3]: https://en.wikipedia.org/wiki/NoSQL
|
||||
[4]: http://lwn.net/Articles/444910/
|
||||
[5]: https://opensource.com/sites/default/files/uploads/virtualfilesystems_1-console.png (Console)
|
||||
[6]: https://lwn.net/Articles/22355/
|
||||
[7]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/fs.h
|
||||
[8]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs
|
||||
[9]: https://opensource.com/sites/default/files/uploads/virtualfilesystems_2-shim-layer.png (How userspace accesses various types of filesystems)
|
||||
[10]: https://lwn.net/Articles/774114/
|
||||
[11]: https://opensource.com/sites/default/files/uploads/virtualfilesystems_3-crazy.jpg (Man with shocked expression)
|
||||
[12]: https://wiki.archlinux.org/index.php/Tmpfs
|
||||
[13]: http://man7.org/linux/man-pages/man8/sysctl.8.html
|
||||
[14]: https://opensource.com/sites/default/files/uploads/virtualfilesystems_4-proc-meminfo.png (Console)
|
||||
[15]: http://www-f1.ijs.si/~ramsak/km1/mermin.moon.pdf
|
||||
[16]: https://en.wikiquote.org/wiki/David_Mermin
|
||||
[17]: https://opensource.com/sites/default/files/uploads/virtualfilesystems_5-moon.jpg (Full moon)
|
||||
[18]: https://commons.wikimedia.org/wiki/Moon#/media/File:Full_Moon_Luc_Viatour.jpg
|
||||
[19]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/ABI/stable
|
||||
[20]: https://lkml.org/lkml/2012/12/23/75
|
||||
[21]: https://opensource.com/sites/default/files/uploads/virtualfilesystems_7-sysfs.png (Console)
|
||||
[22]: https://events.linuxfoundation.org/sites/events/files/slides/bpf_collabsummit_2015feb20.pdf
|
||||
[23]: https://github.com/iovisor/bcc
|
||||
[24]: https://github.com/iovisor/bcc/blob/master/INSTALL.md
|
||||
[25]: http://brendangregg.com/ebpf.html
|
||||
[26]: https://github.com/iovisor/bcc/tree/master/tools
|
||||
[27]: https://github.com/iovisor/bcc/blob/master/tools/vfscount_example.txt
|
||||
[28]: https://github.com/iovisor/bcc/blob/master/tools/vfsstat.py
|
||||
[29]: https://opensource.com/sites/default/files/uploads/virtualfilesystems_8-vfsstat.png (Console - vfsstat.py)
|
||||
[30]: https://opensource.com/sites/default/files/uploads/virtualfilesystems_9-ebpf.png (Console when USB is inserted)
|
||||
[31]: https://github.com/iovisor/bcc/blob/master/tools/trace_example.txt
|
||||
[32]: https://events.static.linuxfound.org/sites/events/files/slides/bpf_collabsummit_2015feb20.pdf
|
||||
[33]: http://northstar-www.dartmouth.edu/doc/solaris-forte/manuals/c/user_guide/cscope.html
|
||||
[34]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/block/genhd.c#n665
|
||||
[35]: http://www.man7.org/linux/man-pages/man8/fsck.8.html
|
||||
[36]: https://wiki.automotivelinux.org/_media/eg-rhsa/agl_referencehardwarespec_v0.1.0_20171018.pdf
|
||||
[37]: https://elinux.org/images/1/1f/Read-only_rootfs.pdf
|
||||
[38]: https://opensource.com/sites/default/files/uploads/virtualfilesystems_10-code.jpg (Photograph of a console)
|
||||
[39]: https://www.meetup.com/ACCU-Bay-Area/events/drpmvfytlbqb/
|
||||
[40]: http://man7.org/linux/man-pages/man8/mount.8.html
|
||||
[41]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/filesystems/sharedsubtree.txt
|
||||
[42]: https://coreos.com/os/docs/latest/kernel-modules.html
|
||||
[43]: https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html
|
||||
[44]: https://opensource.com/sites/default/files/uploads/virtualfilesystems_11-system-nspawn.png (Console - system-nspawn invocation)
|
||||
[45]: https://opensource.com/sites/default/files/uploads/virtualfilesystems_12-mountsnoop.png (Console - Running mountsnoop)
|
||||
[46]: http://shallowsky.com/
|
||||
[47]: http://eagercon.com/
|
||||
[48]: https://www.socallinuxexpo.org/scale/17x/presentations/virtual-filesystems-why-we-need-them-and-how-they-work
|
||||
[49]: https://www.socallinuxexpo.org/
|
@ -0,0 +1,56 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10914-1.html)
|
||||
[#]: subject: (Blockchain 2.0: Blockchain In Real Estate [Part 4])
|
||||
[#]: via: (https://www.ostechnix.com/blockchain-2-0-blockchain-in-real-estate/)
|
||||
[#]: author: (ostechnix https://www.ostechnix.com/author/editor/)
|
||||
|
||||
区块链 2.0:房地产区块链(四)
|
||||
======
|
||||
|
||||
![](https://www.ostechnix.com/wp-content/uploads/2019/03/Blockchain-In-Real-Estate-720x340.png)
|
||||
|
||||
### 区块链 2.0:“更”智能的房地产
|
||||
|
||||
在本系列的[上一篇文章][1]中我们探讨了区块链的特征,这些区块链将使机构能够将**传统银行**和**融资系统**转换和交织在一起。这部分将探讨**房地产区块链**。房地产业正在走向革命。它是人类已知的交易最活跃、最重要的资产类别之一。然而,由于充满了监管障碍和欺诈、欺骗的无数可能性,它也是最难参与交易的之一。利用适当的共识算法的区块链的分布式分类账本功能被吹捧为这个行业的前进方向,而这个行业传统上被认为其面对变革是保守的。
|
||||
|
||||
就其无数的业务而言,房地产一直是一个非常保守的行业。这似乎也是理所当然的。2008 年金融危机或 20 世纪上半叶的大萧条等重大经济危机成功摧毁了该行业及其参与者。然而,与大多数具有经济价值的产品一样,房地产行业具有弹性,而这种弹性则源于其保守性。
|
||||
|
||||
全球房地产市场由价值 228 万亿 [^1] 美元的资产类别组成,出入不大。其他投资资产,如股票、债券和股票合计价值仅为 170 万亿美元。显然,在这样一个行业中实施的交易在很大程度上都是精心策划和执行的。很多时候,房地产也因许多欺诈事件而臭名昭著,并且随之而来的是毁灭性的损失。由于其运营非常保守,该行业也难以驾驭。它受到了法律的严格监管,创造了一个交织在一起的细微差别网络,这对于普通人来说太难以完全理解,使得大多数人无法进入和参与。如果你曾参与过这样的交易,那么你就会知道纸质文件的重要性和长期性。
|
||||
|
||||
从一个微不足道的开始,虽然是一个重要的例子,以显示当前的记录管理实践在房地产行业有多糟糕,考虑一下[产权保险业务][2] [^3]。产权保险用于对冲土地所有权和所有权记录不可接受且从而无法执行的可能性。诸如此类的保险产品也称为赔偿保险。在许多情况下,法律要求财产拥有产权保险,特别是在处理多年来多次易手的财产时。抵押贷款公司在支持房地产交易时也可能坚持同样的要求。事实上,这种产品自 19 世纪 50 年代就已存在,并且仅在美国每年至少有 1.5 万亿美元的商业价值这一事实证明了一开始的说法。在这种情况下,这些记录的维护方式必须进行改革,区块链提供了一个可持续解决方案。根据[美国土地产权协会][4],平均每个案例的欺诈平均约为 10 万美元,并且涉及交易的所有产权中有 25% 的文件存在问题。区块链允许设置一个不可变的永久数据库,该数据库将跟踪资产本身,记录已经进入的每个交易或投资。这样的分类帐本系统将使包括一次性购房者在内的房地产行业的每个人的生活更加轻松,并使诸如产权保险等金融产品基本上无关紧要。将诸如房地产之类的实物资产转换为这样的数字资产是非常规的,并且目前仅在理论上存在。然而,这种变化迫在眉睫,而不是迟到 [^5]。
|
||||
|
||||
区块链在房地产中影响最大的领域如上所述,在维护透明和安全的产权管理系统方面。基于区块链的财产记录可以包含有关财产、其所在地、所有权历史以及相关的公共记录的[信息][6]。这将允许房地产交易快速完成,并且无需第三方监控和监督。房地产评估和税收计算等任务成为有形的、客观的参数问题,而不是主观测量和猜测,因为可靠的历史数据是可公开验证的。[UBITQUITY][7] 就是这样一个平台,为企业客户提供定制的基于区块链的解决方案。该平台允许客户跟踪所有房产细节、付款记录、抵押记录,甚至允许运行智能合约,自动处理税收和租赁。
|
||||
|
||||
这为我们带来了房地产区块链的第二大机遇和用例。由于该行业受到众多第三方的高度监管,除了参与交易的交易对手外,尽职调查和财务评估可能非常耗时。这些流程主要通过离线渠道进行,文书工作需要在最终评估报告出来之前进行数天。对于公司房地产交易尤其如此,这构成了顾问所收取的总计费时间的大部分。如果交易由抵押背书,则这些过程的重复是不可避免的。一旦与所涉及的人员和机构的数字身份相结合,就可以完全避免当前的低效率,并且可以在几秒钟内完成交易。租户、投资者、相关机构、顾问等可以单独验证数据并达成一致的共识,从而验证永久性的财产记录 [^8]。这提高了验证流程的准确性。房地产巨头 RE/MAX 最近宣布与服务提供商 XYO Network Partners 合作,[建立墨西哥房上市地产国家数据库][9]。他们希望有朝一日能够创建世界上最大的(截至目前)去中心化房地产登记中心之一。
|
||||
|
||||
然而,区块链可以带来的另一个重要且可以说是非常民主的变化是投资房地产。与其他投资资产类别不同,即使是小型家庭投资者也可能参与其中,房地产通常需要大量的手工付款才能参与。诸如 ATLANT 和 BitOfProperty 之类的公司将房产的账面价值代币化,并将其转换为加密货币的等价物。这些代币随后在交易所出售,类似于股票和股票的交易方式。[房地产后续产生的任何现金流都会根据其在财产中的“份额”记入贷方或借记给代币所有者][4]。
|
||||
|
||||
然而,尽管如此,区块链技术仍处于房地产领域的早期采用阶段,目前的法规还没有明确定义它。诸如分布式应用程序、分布式匿名组织(DAO)、智能合约等概念在许多国家的法律领域是闻所未闻的。一旦所有利益相关者充分接受了区块链复杂性的良好教育,就会彻底改革现有的法规和指导方针,这是最务实的前进方式。 同样,这将是一个缓慢而渐进的变化,但是它是一个急需的变化。本系列的下一篇文章将介绍 “智能合约”,例如由 UBITQUITY 和 XYO 等公司实施的那些是如何在区块链中创建和执行的。
|
||||
|
||||
[^1]: HSBC, “Global Real Estate,” no. April, 2008
|
||||
[^3]: D. B. Burke, Law of title insurance. Aspen Law & Business, 2000.
|
||||
[^5]: M. Swan, O’Reilly – Blockchain. Blueprint for a New Economy – 2015.
|
||||
[^8]: Deloite, “Blockchain in commercial real estate The future is here ! Table of contents.”
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/blockchain-2-0-blockchain-in-real-estate/
|
||||
|
||||
作者:[ostechnix][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/editor/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://linux.cn/article-10689-1.html
|
||||
[2]: https://www.forbes.com/sites/jordanlulich/2018/06/21/what-is-title-insurance-and-why-its-important/#1472022b12bb
|
||||
[4]: https://www.cbinsights.com/research/blockchain-real-estate-disruption/#financing
|
||||
[6]: https://www2.deloitte.com/us/en/pages/financial-services/articles/blockchain-in-commercial-real-estate.html
|
||||
[7]: https://www.ubitquity.io/
|
||||
[9]: https://www.businesswire.com/news/home/20181012005068/en/XYO-Network-Partners-REMAX-M%C3%A9xico-Bring-Blockchain
|
@ -1,48 +1,47 @@
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: "zgj1024 "
|
||||
[#]: reviewer: " "
|
||||
[#]: publisher: " "
|
||||
[#]: url: " "
|
||||
[#]: translator: "zgj1024"
|
||||
[#]: reviewer: "wxy"
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-10834-1.html"
|
||||
[#]: subject: "Why DevOps is the most important tech strategy today"
|
||||
[#]: via: "https://opensource.com/article/19/3/devops-most-important-tech-strategy"
|
||||
[#]: author: "Kelly AlbrechtWilly-Peter Schaub https://opensource.com/users/ksalbrecht/users/brentaaronreed/users/wpschaub/users/wpschaub/users/ksalbrecht"
|
||||
[#]: author: "Kelly Albrecht https://opensource.com/users/ksalbrecht"
|
||||
|
||||
为何 DevOps 是如今最重要的技术策略
|
||||
======
|
||||
消除一些关于 DevOps 的疑惑
|
||||
|
||||
> 消除一些关于 DevOps 的疑惑。
|
||||
|
||||
![CICD with gears][1]
|
||||
|
||||
很多人初学 [DevOps][2] 时,看到它其中一个结果就问这个是如何得来的。其实理解这部分 Devops 的怎样实现并不重要,重要的是——理解(使用) DevOps 策略的原因——这是做一个行业的领导者还是追随者的差别。
|
||||
|
||||
你可能会听过些 Devops 的难以置信的成果,例如生产环境非常有弹性,“混世猴子”([Chaos Monkey][3])程序运行时,将周围的连接随机切断,每天仍可以处理数千个版本。这是令人印象深刻的,但就其本身而言,这是一个 DevOps 的无力案例,本质上会被一个[反例][4]困扰:DevOps 环境有弹性是因为没有观察到严重的故障。。。还没有。
|
||||
你可能会听过些 Devops 的难以置信的成果,例如生产环境非常有弹性,就算是有个“<ruby>[癫狂的猴子][3]<rt>Chaos Monkey</rt></ruby>)跳来跳去将不知道哪个插头随便拔下,每天仍可以处理数千个发布。这是令人印象深刻的,但就其本身而言,这是一个 DevOps 的证据不足的案例,其本质上会被一个[反例][4]困扰:DevOps 环境有弹性是因为严重的故障还没有被观测到。
|
||||
|
||||
有很多关于 DevOps 的疑惑,并且许多人还在尝试弄清楚它的意义。下面是来自我 LinkedIn Feed 中的某个人的一个案例:
|
||||
|
||||
> 最近我参加一些 #DevOps 的交流会,那里一些演讲人好像在倡导 #敏捷开发是 DevOps 的子集。不知为何,我的理解洽洽相反。
|
||||
> 最近我参加一些 #DevOps 的交流会,那里一些演讲人好像在倡导 #敏捷开发是 DevOps 的子集。不知为何,我的理解恰恰相反。
|
||||
>
|
||||
> 能听一下你们的想法吗?你认为敏捷开发和 DevOps 之间是什么关系呢?
|
||||
>
|
||||
> 1. DevOps 是敏捷开发的子集
|
||||
> 2. 敏捷开发 是 DevOps 的子集
|
||||
> 2. 敏捷开发是 DevOps 的子集
|
||||
> 3. DevOps 是敏捷开发的扩展,从敏捷开发结束的地方开始
|
||||
> 4. DevOps 是敏捷开发的新版本
|
||||
>
|
||||
|
||||
科技行业的专业人士在那篇 LinkedIn 的帖子上达标各样的答案,你会怎样回复呢?
|
||||
科技行业的专业人士在那篇 LinkedIn 的帖子上表达了各种各样的答案,你会怎样回复呢?
|
||||
|
||||
### DevOps源于精益和敏捷
|
||||
### DevOps 源于精益和敏捷
|
||||
|
||||
如果我们从亨利福特的战略和丰田生产系统对福特车型的改进(的历史)开始, DevOps 就更有意义了。精益制造就诞生在那段历史中,人们对精益制作进行了良好的研究。James P. Womack 和 Daniel T. Jones 将精益思维([Lean Thinking][5])提炼为五个原则:
|
||||
|
||||
如果我们从亨利福特的战略和丰田生产系统对福特车型的改进(的历史)开始, DevOps 就更有意义了。精益制造就诞生在那段历史中,人们对精益制作进行了良好的研究。James P. Womack 和 Daniel T. Jones 将精益思维( [Lean Thinking][5])提炼为五个原则:
|
||||
1. 指明客户所需的价值
|
||||
2. 确定提供该价值的每个产品的价值流,并对当前提供该价值所需的所有浪费步骤提起挑战
|
||||
3. 使产品通过剩余的增值步骤持续流动
|
||||
4. 在可以连续流动的所有步骤之间引入拉力
|
||||
5. 管理要尽善尽美,以便为客户服务所需的步骤数量和时间以及信息量持续下降
|
||||
|
||||
|
||||
Lean seeks to continuously remove waste and increase the flow of value to the customer. This is easily recognizable and understood through a core tenet of lean: single piece flow. We can do a number of activities to learn why moving single pieces at a time is magnitudes faster than batches of many pieces; the [Penny Game][6] and the [Airplane Game][7] are two of them. In the Penny Game, if a batch of 20 pennies takes two minutes to get to the customer, they get the whole batch after waiting two minutes. If you move one penny at a time, the customer gets the first penny in about five seconds and continues getting pennies until the 20th penny arrives approximately 25 seconds later.
|
||||
|
||||
精益致力于持续消除浪费并增加客户的价值流动。这很容易识别并明白精益的核心原则:单一流。我们可以做一些游戏去了解为何同一时间移动单个比批量移动要快得多。其中的两个游戏是[硬币游戏][6]和[飞机游戏][7]。在硬币游戏中,如果一批 20 个硬币到顾客手中要用 2 分钟,顾客等 2 分钟后能拿到整批硬币。如果一次只移动一个硬币,顾客会在 5 秒内得到第一枚硬币,并会持续获得硬币,直到在大约 25 秒后第 20 个硬币到达。(译者注:有相关的视频的)
|
||||
精益致力于持续消除浪费并增加客户的价值流动。这很容易识别并明白精益的核心原则:单一流。我们可以做一些游戏去了解为何同一时间移动单个比批量移动要快得多。其中的两个游戏是[硬币游戏][6]和[飞机游戏][7]。在硬币游戏中,如果一批 20 个硬币到顾客手中要用 2 分钟,顾客等 2 分钟后能拿到整批硬币。如果一次只移动一个硬币,顾客会在 5 秒内得到第一枚硬币,并会持续获得硬币,直到在大约 25 秒后第 20 个硬币到达。(LCTT 译注:有相关的视频的)
|
||||
|
||||
这是巨大的不同,但是不是生活中的所有事都像硬币游戏那样简单并可预测的。这就是敏捷的出现的原因。我们当然看到了高效绩敏捷团队的精益原则,但这些团队需要的不仅仅是精益去做他们要做的事。
|
||||
|
||||
@ -52,13 +51,13 @@ Lean seeks to continuously remove waste and increase the flow of value to the cu
|
||||
|
||||
### 最佳批量大小
|
||||
|
||||
要了解 DevOps 在软件开发中的请强大功能,这会帮助我们理解批处理大小的经济学。请考虑以下来自Donald Reinertsen 的[产品开发流程原则][8]的U曲线优化示例:
|
||||
要了解 DevOps 在软件开发中的强大功能,这会帮助我们理解批处理大小的经济学。请考虑以下来自Donald Reinertsen 的[产品开发流程原则][8]的U曲线优化示例:
|
||||
|
||||
![U-curve optimization illustration of optimal batch size][9]
|
||||
|
||||
这可以类比杂货店购物来解释。假设你需要买一些鸡蛋,而你住的地方离商店只有 30 分的路程。买一个鸡蛋(图种最左边)意味着每次要花 30 分钟的路程,这就是你的_交易成本_。_持有成本_可能是鸡蛋变质和在你的冰箱中持续地占用空间。_总成本_是_交易成本_加上你的_持有成本_。这 U 型曲线解释了为什么对大部分来说,一次买一打鸡蛋是他们的_最佳批量大小_。如果你就住在商店的旁边,步行到那里不会花费你任何的时候,你可能每次只会买一小盒鸡蛋,以此来节省冰箱的空间并享受新鲜的鸡蛋。
|
||||
这可以类比杂货店购物来解释。假设你需要买一些鸡蛋,而你住的地方离商店只有 30 分钟的路程。买一个鸡蛋(图中最左边)意味着每次要花 30 分钟的路程,这就是你的*交易成本*。*持有成本*可能是鸡蛋变质和在你的冰箱中持续地占用空间。*总成本*是*交易成本*加上你的*持有成本*。这个 U 型曲线解释了为什么对大部分人来说,一次买一打鸡蛋是他们的*最佳批量大小*。如果你就住在商店的旁边,步行到那里不会花费你任何的时候,你可能每次只会买一小盒鸡蛋,以此来节省冰箱的空间并享受新鲜的鸡蛋。
|
||||
|
||||
这 U 型优化曲线可以说明为什么在成功敏捷转换中生产力会显著提高。考虑敏捷转换对组织决策的影响。在传统的分级组织中,决策权是集中的。这会导致较少的人做更大的决策。敏捷方法论会有效地降低组织决策中的交易成本,方法是将决策分散到最被人熟知的认识和信息的位置:跨越高度信任,自组织的敏捷团队。
|
||||
这 U 型优化曲线可以说明为什么在成功的敏捷转换中生产力会显著提高。考虑敏捷转换对组织决策的影响。在传统的分级组织中,决策权是集中的。这会导致较少的人做更大的决策。敏捷方法论会有效地降低组织决策中的交易成本,方法是将决策分散到最被人熟知的认识和信息的位置:跨越高度信任,自组织的敏捷团队。
|
||||
|
||||
下面的动画演示了降低事务成本后,最佳批量大小是如何向左移动。在更频繁地做出更快的决策方面,你不能低估组织的价值。
|
||||
|
||||
@ -66,22 +65,21 @@ Lean seeks to continuously remove waste and increase the flow of value to the cu
|
||||
|
||||
### DevOps 适合哪些地方
|
||||
|
||||
自动化是 DevOps 最知名的事情之一。前面的插图非常详细地展示了自动化的价值。通过自动化,我们将交易成本降低到接近零,实质上是免费进行测试和部署。这使我们可以利用越来越小的批量工作。较小批量的工作更容易理解、提交、测试、审查和知道何时能完成。这些较小的批量大小也包含较少的差异和风险,使其更易于部署,如果出现问题,可以进行故障排除和恢复。通过自动化与扎实的敏捷实践相结合,我们可以使我们的功能开发非常接近单件流程,从而快速,持续地为客户提供价值。
|
||||
自动化是 DevOps 最知名的事情之一。前面的插图非常详细地展示了自动化的价值。通过自动化,我们将交易成本降低到接近于零,实质上是可以免费进行测试和部署。这使我们可以利用越来越小的批量工作。较小批量的工作更容易理解、提交、测试、审查和知道何时能完成。这些较小的批量大小也包含较少的差异和风险,使其更易于部署,如果出现问题,可以进行故障排除和恢复。通过自动化与扎实的敏捷实践相结合,我们可以使我们的功能开发非常接近单件流程,从而快速、持续地为客户提供价值。
|
||||
|
||||
更传统地说,DevOps 被理解为一种打破开发团队和运营团队之间混乱局面的方法。在这个模型中,开发团队开发新的功能,而运营团队则保持系统的稳定和平稳运行。摩擦的发生是因为开发过程中的新功能将更改引入到系统中,从而增加了停机的风险,运营团队并不认为要对此负责,但无论如何都必须处理这一问题。DevOps 不仅仅尝试让人们一起工作,更重要的是尝试在复杂的环境中安全地进行更频繁的更改。
|
||||
|
||||
我们可以向 [Ron Westrum][11] 寻求有关在复杂组织中实现安全性的研究。在研究为什么有些组织比其他组织更安全时,他发现组织的文化可以预测其安全性。他确定了三种文化:病态,官僚主义的和生产式的。他发现病理的可以预测安全性较低,而生产式文化被预测为更安全(例如,在他的主要研究领域中,飞机坠毁或意外住院死亡的数量要少得多)。
|
||||
我们可以看看 [Ron Westrum][11] 在有关复杂组织中实现安全性的研究。在研究为什么有些组织比其他组织更安全时,他发现组织的文化可以预测其安全性。他确定了三种文化:病态的、官僚主义的和生产式的。他发现病态的可以预测其安全性较低,而生产式文化被预测为更安全(例如,在他的主要研究领域中,飞机坠毁或意外住院死亡的数量要少得多)。
|
||||
|
||||
![Three types of culture identified by Ron Westrum][12]
|
||||
|
||||
高效的 DevOps 团队通过精益和敏捷的实践实现了一种生成性文化,这表明速度和安全性是互补的,或者说是同一个问题的两个方面。通过将决策和功能的最佳批量大小减少到非常小,DevOps 实现了更快的信息流和价值,同时消除了浪费并降低了风险。
|
||||
|
||||
与 Westrum的研究一致,在提高安全性和可靠性的同时,变化也很容易发生。。当一个敏捷的 DevOps 团队被信任做出自己的决定时,我们将获得 DevOps 目前最为人所知的工具和技术:自动化和持续交付。通过这种自动化,交易成本比以往任何时候都进一步降低,并且实现了近乎单一的精益流程,每天创造数千个决策和发布的潜力,正如我们在高效绩的 DevOps 组织中看到的那样
|
||||
与 Westrum 的研究一致,在提高安全性和可靠性的同时,变化也很容易发生。当一个敏捷的 DevOps 团队被信任做出自己的决定时,我们将获得 DevOps 目前最为人所知的工具和技术:自动化和持续交付。通过这种自动化,交易成本比以往任何时候都进一步降低,并且实现了近乎单一的精益流程,每天创造数千个决策和发布的潜力,正如我们在高效绩的 DevOps 组织中看到的那样
|
||||
|
||||
### 流动、反馈、学习
|
||||
|
||||
DevOps 并不止于此。我们主要讨论了 DevOps 实现了革命性的流程,但通过类似的努力可以进一步放大精益和敏捷实践,从而实现更快的反馈循环和更快的学习。在[_DevOps手册_][13] 中,作者除了详细解释快速流程外, DevOps 如何在整个价值流中实现遥测,从而获得快速且持续的反馈。此外,利用[精益求精的突破][14]和scrum 的[回顾][15],高效的 DevOps 团队将不断推动学习和持续改进深入到他们的组织的基础,实现软件产品开发行业的精益制造革命。
|
||||
|
||||
DevOps 并不止于此。我们主要讨论了 DevOps 实现了革命性的流程,但通过类似的努力可以进一步放大精益和敏捷实践,从而实现更快的反馈循环和更快的学习。在[DevOps手册][13] 中,作者除了详细解释快速流程外, DevOps 如何在整个价值流中实现遥测,从而获得快速且持续的反馈。此外,利用[精益求精的突破][14]和 scrum 的[回顾][15],高效的 DevOps 团队将不断推动学习和持续改进深入到他们的组织的基础,实现软件产品开发行业的精益制造革命。
|
||||
|
||||
### 从 DevOps 评估开始
|
||||
|
||||
@ -91,18 +89,14 @@ DevOps 并不止于此。我们主要讨论了 DevOps 实现了革命性的流
|
||||
|
||||
在本文的[第二部分][16]中,我们将查看 Drupal 社区中 DevOps 调查的结果,并了解最有可能找到快速获胜的位置。
|
||||
|
||||
* * *
|
||||
|
||||
_Rob_ _Bayliss and Kelly Albrecht will present[DevOps: Why, How, and What][17] and host a follow-up [Birds of a][18]_ [_Feather_][18] _[discussion][18] at [DrupalCon 2019][19] in Seattle, April 8-12._
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/3/devops-most-important-tech-strategy
|
||||
|
||||
作者:[Kelly AlbrechtWilly-Peter Schaub][a]
|
||||
作者:[Kelly Albrecht][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/zgj1024)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
译者:[zgj1024](https://github.com/zgj1024)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,173 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10916-1.html)
|
||||
[#]: subject: (How to manage your Linux environment)
|
||||
[#]: via: (https://www.networkworld.com/article/3385516/how-to-manage-your-linux-environment.html)
|
||||
[#]: author: (Sandra Henry-Stocker https://www.networkworld.com/author/Sandra-Henry_Stocker/)
|
||||
|
||||
如何管理你的 Linux 环境变量
|
||||
======
|
||||
|
||||
> Linux 用户环境变量可以帮助你找到你需要的命令,无须了解系统如何配置的细节而完成大量工作。而这些设置来自哪里和如何被修改它们是另一个话题。
|
||||
|
||||
![IIP Photo Archive \(CC BY 2.0\)][1]
|
||||
|
||||
在 Linux 系统上的用户账户配置以多种方法简化了系统的使用。你可以运行命令,而不需要知道它们的位置。你可以重新使用先前运行的命令,而不用发愁系统是如何追踪到它们的。你可以查看你的电子邮件,查看手册页,并容易地回到你的家目录,而不用管你在文件系统中身在何方。并且,当需要的时候,你可以调整你的账户设置,以便其更符合你喜欢的方式。
|
||||
|
||||
Linux 环境设置来自一系列的文件:一些是系统范围(意味着它们影响所有用户账户),一些是处于你的家目录中的配置文件里。系统范围的设置在你登录时生效,而本地设置在其后生效,所以,你在你账户中作出的更改将覆盖系统范围设置。对于 bash 用户,这些文件包含这些系统文件:
|
||||
|
||||
```
|
||||
/etc/environment
|
||||
/etc/bash.bashrc
|
||||
/etc/profile
|
||||
```
|
||||
|
||||
以及一些本地文件:
|
||||
|
||||
```
|
||||
~/.bashrc
|
||||
~/.profile # 如果有 ~/.bash_profile 或 ~/.bash_login 就不会读此文件
|
||||
~/.bash_profile
|
||||
~/.bash_login
|
||||
```
|
||||
|
||||
你可以修改本地存在的四个文件的任何一个,因为它们处于你的家目录,并且它们是属于你的。
|
||||
|
||||
### 查看你的 Linux 环境设置
|
||||
|
||||
为查看你的环境设置,使用 `env` 命令。你的输出将可能与这相似:
|
||||
|
||||
```
|
||||
$ env
|
||||
LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;
|
||||
01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:
|
||||
*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:
|
||||
*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:
|
||||
*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;
|
||||
31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:
|
||||
*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:
|
||||
*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:
|
||||
*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:
|
||||
*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:
|
||||
*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:
|
||||
*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:
|
||||
*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:
|
||||
*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:
|
||||
*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:
|
||||
*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:
|
||||
*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:
|
||||
*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.spf=00;36:
|
||||
SSH_CONNECTION=192.168.0.21 34975 192.168.0.11 22
|
||||
LESSCLOSE=/usr/bin/lesspipe %s %s
|
||||
LANG=en_US.UTF-8
|
||||
OLDPWD=/home/shs
|
||||
XDG_SESSION_ID=2253
|
||||
USER=shs
|
||||
PWD=/home/shs
|
||||
HOME=/home/shs
|
||||
SSH_CLIENT=192.168.0.21 34975 22
|
||||
XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop
|
||||
SSH_TTY=/dev/pts/0
|
||||
MAIL=/var/mail/shs
|
||||
TERM=xterm
|
||||
SHELL=/bin/bash
|
||||
SHLVL=1
|
||||
LOGNAME=shs
|
||||
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
|
||||
XDG_RUNTIME_DIR=/run/user/1000
|
||||
PATH=/home/shs/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
|
||||
LESSOPEN=| /usr/bin/lesspipe %s
|
||||
_=/usr/bin/env
|
||||
```
|
||||
|
||||
虽然你可能会看到大量的输出,上面显示的第一大部分用于在命令行上使用颜色标识各种文件类型。当你看到类似 `*.tar=01;31:` 这样的东西,这告诉你 `tar` 文件将以红色显示在文件列表中,然而 `*.jpg=01;35:` 告诉你 jpg 文件将以紫色显现出来。这些颜色旨在使它易于从一个文件列表中分辨出某些文件。你可以在《[在 Linux 命令行中自定义你的颜色][3]》处学习更多关于这些颜色的定义,和如何自定义它们。
|
||||
|
||||
当你更喜欢一种不加装饰的显示时,一种关闭颜色显示的简单方法是使用如下命令:
|
||||
|
||||
```
|
||||
$ ls -l --color=never
|
||||
```
|
||||
|
||||
这个命令可以简单地转换到一个别名:
|
||||
|
||||
```
|
||||
$ alias ll2='ls -l --color=never'
|
||||
```
|
||||
|
||||
你也可以使用 `echo` 命令来单独地显现某个设置。在这个命令中,我们显示在历史缓存区中将被记忆命令的数量:
|
||||
|
||||
```
|
||||
$ echo $HISTSIZE
|
||||
1000
|
||||
```
|
||||
|
||||
如果你已经移动到某个位置,你在文件系统中的最后位置会被记在这里:
|
||||
|
||||
```
|
||||
PWD=/home/shs
|
||||
OLDPWD=/tmp
|
||||
```
|
||||
|
||||
### 作出更改
|
||||
|
||||
你可以使用一个像这样的命令更改环境设置,但是,如果你希望保持这个设置,在你的 `~/.bashrc` 文件中添加一行代码,例如 `HISTSIZE=1234`。
|
||||
|
||||
```
|
||||
$ export HISTSIZE=1234
|
||||
```
|
||||
|
||||
### “export” 一个变量的本意是什么
|
||||
|
||||
导出一个环境变量可使设置用于你的 shell 和可能的子 shell。默认情况下,用户定义的变量是本地的,并不被导出到新的进程,例如,子 shell 和脚本。`export` 命令使得环境变量可用在子进程中发挥功用。
|
||||
|
||||
### 添加和移除变量
|
||||
|
||||
你可以很容易地在命令行和子 shell 上创建新的变量,并使它们可用。然而,当你登出并再次回来时这些变量将消失,除非你也将它们添加到 `~/.bashrc` 或一个类似的文件中。
|
||||
|
||||
```
|
||||
$ export MSG="Hello, World!"
|
||||
```
|
||||
|
||||
如果你需要,你可以使用 `unset` 命令来消除一个变量:
|
||||
|
||||
```
|
||||
$ unset MSG
|
||||
```
|
||||
|
||||
如果变量是局部定义的,你可以通过加载你的启动文件来简单地将其设置回来。例如:
|
||||
|
||||
```
|
||||
$ echo $MSG
|
||||
Hello, World!
|
||||
$ unset $MSG
|
||||
$ echo $MSG
|
||||
|
||||
$ . ~/.bashrc
|
||||
$ echo $MSG
|
||||
Hello, World!
|
||||
```
|
||||
|
||||
### 小结
|
||||
|
||||
用户账户是用一组恰当的启动文件设立的,创建了一个有用的用户环境,而个人用户和系统管理员都可以通过编辑他们的个人设置文件(对于用户)或很多来自设置起源的文件(对于系统管理员)来更改默认设置。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3385516/how-to-manage-your-linux-environment.html
|
||||
|
||||
作者:[Sandra Henry-Stocker][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Sandra-Henry_Stocker/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://images.idgesg.net/images/article/2019/03/environment-rocks-leaves-100792229-large.jpg
|
||||
[2]: https://www.youtube.com/playlist?list=PL7D2RMSmRO9J8OTpjFECi8DJiTQdd4hua
|
||||
[3]: https://www.networkworld.com/article/3269587/customizing-your-text-colors-on-the-linux-command-line.html
|
||||
[4]: https://www.facebook.com/NetworkWorld/
|
||||
[5]: https://www.linkedin.com/company/network-world
|
@ -0,0 +1,108 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10833-1.html)
|
||||
[#]: subject: (Getting started with Python's cryptography library)
|
||||
[#]: via: (https://opensource.com/article/19/4/cryptography-python)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshez)
|
||||
|
||||
Python 的加密库入门
|
||||
======
|
||||
|
||||
> 加密你的数据并使其免受攻击者的攻击。
|
||||
|
||||
![lock on world map][1]
|
||||
|
||||
密码学俱乐部的第一条规则是:永远不要自己*发明*密码系统。密码学俱乐部的第二条规则是:永远不要自己*实现*密码系统:在现实世界中,在*实现*以及设计密码系统阶段都找到过许多漏洞。
|
||||
|
||||
Python 中的一个有用的基本加密库就叫做 [cryptography][2]。它既是一个“安全”方面的基础库,也是一个“危险”层。“危险”层需要更加小心和相关的知识,并且使用它很容易出现安全漏洞。在这篇介绍性文章中,我们不会涵盖“危险”层中的任何内容!
|
||||
|
||||
cryptography 库中最有用的高级安全功能是一种 Fernet 实现。Fernet 是一种遵循最佳实践的加密缓冲区的标准。它不适用于非常大的文件,如千兆字节以上的文件,因为它要求你一次加载要加密或解密的内容到内存缓冲区中。
|
||||
|
||||
Fernet 支持<ruby>对称<rt>symmetric</rt></ruby>(即<ruby>密钥<rt>secret key</rt></ruby>)加密方式*:加密和解密使用相同的密钥,因此必须保持安全。
|
||||
|
||||
生成密钥很简单:
|
||||
|
||||
```
|
||||
>>> k = fernet.Fernet.generate_key()
|
||||
>>> type(k)
|
||||
<class 'bytes'>
|
||||
```
|
||||
|
||||
这些字节可以写入有适当权限的文件,最好是在安全的机器上。
|
||||
|
||||
有了密钥后,加密也很容易:
|
||||
|
||||
```
|
||||
>>> frn = fernet.Fernet(k)
|
||||
>>> encrypted = frn.encrypt(b"x marks the spot")
|
||||
>>> encrypted[:10]
|
||||
b'gAAAAABb1'
|
||||
```
|
||||
|
||||
如果在你的机器上加密,你会看到略微不同的值。不仅因为(我希望)你生成了和我不同的密钥,而且因为 Fernet 将要加密的值与一些随机生成的缓冲区连接起来。这是我之前提到的“最佳实践”之一:它将阻止对手分辨哪些加密值是相同的,这有时是攻击的重要部分。
|
||||
|
||||
解密同样简单:
|
||||
|
||||
```
|
||||
>>> frn = fernet.Fernet(k)
|
||||
>>> frn.decrypt(encrypted)
|
||||
b'x marks the spot'
|
||||
```
|
||||
|
||||
请注意,这仅加密和解密*字节串*。为了加密和解密*文本串*,通常需要对它们使用 [UTF-8][3] 进行编码和解码。
|
||||
|
||||
20 世纪中期密码学最有趣的进展之一是<ruby>公钥<rt>public key</rt></ruby>加密。它可以在发布加密密钥的同时而让*解密密钥*保持保密。例如,它可用于保存服务器使用的 API 密钥:服务器是唯一可以访问解密密钥的一方,但是任何人都可以保存公共加密密钥。
|
||||
|
||||
虽然 cryptography 没有任何支持公钥加密的*安全*功能,但 [PyNaCl][4] 库有。PyNaCl 封装并提供了一些很好的方法来使用 Daniel J. Bernstein 发明的 [NaCl][5] 加密系统。
|
||||
|
||||
NaCl 始终同时<ruby>加密<rt>encrypt</rt></ruby>和<ruby>签名<rt>sign</rt></ruby>或者同时<ruby>解密<rt>decrypt</rt></ruby>和<ruby>验证签名<rt>verify signature</rt></ruby>。这是一种防止<ruby>基于可伸缩性<rt>malleability-based</rt></ruby>的攻击的方法,其中攻击者会修改加密值。
|
||||
|
||||
加密是使用公钥完成的,而签名是使用密钥完成的:
|
||||
|
||||
```
|
||||
>>> from nacl.public import PrivateKey, PublicKey, Box
|
||||
>>> source = PrivateKey.generate()
|
||||
>>> with open("target.pubkey", "rb") as fpin:
|
||||
... target_public_key = PublicKey(fpin.read())
|
||||
>>> enc_box = Box(source, target_public_key)
|
||||
>>> result = enc_box.encrypt(b"x marks the spot")
|
||||
>>> result[:4]
|
||||
b'\xe2\x1c0\xa4'
|
||||
```
|
||||
|
||||
解密颠倒了角色:它需要私钥进行解密,需要公钥验证签名:
|
||||
|
||||
```
|
||||
>>> from nacl.public import PrivateKey, PublicKey, Box
|
||||
>>> with open("source.pubkey", "rb") as fpin:
|
||||
... source_public_key = PublicKey(fpin.read())
|
||||
>>> with open("target.private_key", "rb") as fpin:
|
||||
... target = PrivateKey(fpin.read())
|
||||
>>> dec_box = Box(target, source_public_key)
|
||||
>>> dec_box.decrypt(result)
|
||||
b'x marks the spot'
|
||||
```
|
||||
|
||||
最后,[PocketProtector][6] 库构建在 PyNaCl 之上,包含完整的密钥管理方案。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/cryptography-python
|
||||
|
||||
作者:[Moshe Zadka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/moshez
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/security-lock-cloud-safe.png?itok=yj2TFPzq (lock on world map)
|
||||
[2]: https://cryptography.io/en/latest/
|
||||
[3]: https://en.wikipedia.org/wiki/UTF-8
|
||||
[4]: https://pynacl.readthedocs.io/en/stable/
|
||||
[5]: https://nacl.cr.yp.to/
|
||||
[6]: https://github.com/SimpleLegal/pocket_protector/blob/master/USER_GUIDE.md
|
@ -0,0 +1,74 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10822-1.html)
|
||||
[#]: subject: (How to quickly deploy, run Linux applications as unikernels)
|
||||
[#]: via: (https://www.networkworld.com/article/3387299/how-to-quickly-deploy-run-linux-applications-as-unikernels.html#tk.rss_all)
|
||||
[#]: author: (Sandra Henry-Stocker https://www.networkworld.com/author/Sandra-Henry_Stocker/)
|
||||
|
||||
如何快速部署并作为 unikernel 运行 Linux 应用
|
||||
======
|
||||
|
||||
unikernel 是一种用于在云基础架构上部署应用程序的更小、更快、更安全的方式。使用 NanoVMs OPS,任何人都可以将 Linux 应用程序作为 unikernel 运行而无需额外编码。
|
||||
|
||||
![Marcho Verch \(CC BY 2.0\)][1]
|
||||
|
||||
随着 unikernel 的出现,构建和部署轻量级应用变得更容易、更可靠。虽然功能有限,但 unikernal 在速度和安全性方面有许多优势。
|
||||
|
||||
### 什么是 unikernel?
|
||||
|
||||
unikernel 是一种非常特殊的<ruby>单一地址空间<rt>single-address-space</rt></ruby>的机器镜像,类似于已经主导大批互联网的云应用,但它们相当小并且是单一用途的。它们很轻,只提供所需的资源。它们加载速度非常快,而且安全性更高 —— 攻击面非常有限。单个可执行文件中包含所需的所有驱动、I/O 例程和支持库。其最终生成的虚拟镜像可以无需其它部分就可以引导和运行。它们通常比容器快 10 到 20 倍。
|
||||
|
||||
潜在的攻击者无法进入 shell 并获得控制权,因为它没有 shell。他们无法获取系统的 `/etc/passwd`或 `/etc/shadow` 文件,因为这些文件不存在。创建一个 unikernel 就像应用将自己变成操作系统。使用 unikernel,应用和操作系统将成为一个单一的实体。你忽略了不需要的东西,从而消除了漏洞并大幅提高性能。
|
||||
|
||||
简而言之,unikernel:
|
||||
|
||||
* 提供更高的安全性(例如,shell 破解代码无用武之地)
|
||||
* 比标准云应用占用更小空间
|
||||
* 经过高度优化
|
||||
* 启动非常快
|
||||
|
||||
### unikernel 有什么缺点吗?
|
||||
|
||||
unikernel 的唯一严重缺点是你必须构建它们。对于许多开发人员来说,这是一个巨大的进步。由于应用的底层特性,将应用简化为所需的内容然后生成紧凑、平稳运行的应用可能很复杂。在过去,你几乎必须是系统开发人员或底层程序员才能生成它们。
|
||||
|
||||
### 这是怎么改变的?
|
||||
|
||||
最近(2019 年 3 月 24 日)[NanoVMs][3] 宣布了一个将任何 Linux 应用加载为 unikernel 的工具。使用 NanoVMs OPS,任何人都可以将 Linux 应用作为 unikernel 运行而无需额外编码。该应用还可以更快、更安全地运行,并且成本和开销更低。
|
||||
|
||||
### 什么是 NanoVMs OPS?
|
||||
|
||||
NanoVMs 是给开发人员的 unikernel 工具。它能让你运行各种企业级软件,但仍然可以非常严格地控制它的运行。
|
||||
|
||||
使用 OPS 的其他好处包括:
|
||||
|
||||
* 无需经验或知识,开发人员就可以构建 unikernel。
|
||||
* 该工具可在笔记本电脑上本地构建和运行 unikernel。
|
||||
* 无需创建帐户,只需下载并一个命令即可执行 OPS。
|
||||
|
||||
NanoVMs 的介绍可以在 [Youtube 上的 NanoVMs 视频][5] 上找到。你还可以查看该公司的 [LinkedIn 页面][6]并在[此处][7]阅读有关 NanoVMs 安全性的信息。
|
||||
|
||||
还有有关如何[入门][8]的一些信息。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3387299/how-to-quickly-deploy-run-linux-applications-as-unikernels.html
|
||||
|
||||
作者:[Sandra Henry-Stocker][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Sandra-Henry_Stocker/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://images.idgesg.net/images/article/2019/04/corn-kernels-100792925-large.jpg
|
||||
[3]: https://nanovms.com/
|
||||
[5]: https://www.youtube.com/watch?v=VHWDGhuxHPM
|
||||
[6]: https://www.linkedin.com/company/nanovms/
|
||||
[7]: https://nanovms.com/security
|
||||
[8]: https://nanovms.gitbook.io/ops/getting_started
|
||||
[9]: https://www.facebook.com/NetworkWorld/
|
||||
[10]: https://www.linkedin.com/company/network-world
|
@ -0,0 +1,177 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10843-1.html)
|
||||
[#]: subject: (Anbox – Easy Way To Run Android Apps On Linux)
|
||||
[#]: via: (https://www.2daygeek.com/anbox-best-android-emulator-for-linux/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
Anbox:在 Linux 上运行 Android 应用程序的简单方式
|
||||
======
|
||||
|
||||
Android 模拟器允许我们直接从 Linux 系统上运行我们最喜欢的 Android 应用程序或游戏。对于 Linux 来说,有很多的这样的 Android 模拟器,在过去我们介绍过几个此类应用程序。
|
||||
|
||||
你可以通过导航到下面的网址回顾它们。
|
||||
|
||||
* [如何在 Linux 上安装官方 Android 模拟器 (SDK)][1]
|
||||
* [如何在 Linux 上安装 GenyMotion (Android 模拟器)][2]
|
||||
|
||||
今天我们将讨论 Anbox Android 模拟器。
|
||||
|
||||
### Anbox 是什么?
|
||||
|
||||
Anbox 是 “Android in a box” 的缩写。Anbox 是一个基于容器的方法,可以在普通的 GNU/Linux 系统上启动完整的 Android 系统。
|
||||
|
||||
它是现代化的新模拟器之一。
|
||||
|
||||
Anbox 可以让你在 Linux 系统上运行 Android,而没有虚拟化的迟钝,因为核心的 Android 操作系统已经使用 Linux 命名空间(LXE)放置到容器中了。
|
||||
|
||||
Android 容器不能直接访问到任何硬件,所有硬件的访问都是通过在主机上的守护进程进行的。
|
||||
|
||||
每个应用程序将在一个单独窗口打开,就像其它本地系统应用程序一样,并且它可以显示在启动器中。
|
||||
|
||||
### 如何在 Linux 中安装 Anbox ?
|
||||
|
||||
Anbox 也可作为 snap 软件包安装,请确保你已经在你的系统上启用了 snap 支持。
|
||||
|
||||
Anbox 软件包最近被添加到 Ubuntu 18.10 (Cosmic) 和 Debian 10 (Buster) 软件仓库。如果你正在运行这些版本,那么你可以轻松地在官方发行版的软件包管理器的帮助下安装。否则可以用 snap 软件包安装。
|
||||
|
||||
为使 Anbox 工作,确保需要的内核模块已经安装在你的系统中。对于基于 Ubuntu 的用户,使用下面的 PPA 来安装它。
|
||||
|
||||
```
|
||||
$ sudo add-apt-repository ppa:morphis/anbox-support
|
||||
$ sudo apt update
|
||||
$ sudo apt install linux-headers-generic anbox-modules-dkms
|
||||
```
|
||||
|
||||
在你安装 `anbox-modules-dkms` 软件包后,你必须手动重新加载内核模块,或需要系统重新启动。
|
||||
|
||||
```
|
||||
$ sudo modprobe ashmem_linux
|
||||
$ sudo modprobe binder_linux
|
||||
```
|
||||
|
||||
对于 Debian/Ubuntu 系统,使用 [APT-GET 命令][3] 或 [APT 命令][4] 来安装 anbox。
|
||||
|
||||
```
|
||||
$ sudo apt install anbox
|
||||
```
|
||||
|
||||
对于基于 Arch Linux 的系统,我们总是习惯从 AUR 储存库中获取软件包。所以,使用任一个的 [AUR 助手][5] 来安装它。我喜欢使用 [Yay 工具][6]。
|
||||
|
||||
```
|
||||
$ yuk -S anbox-git
|
||||
```
|
||||
|
||||
否则,你可以通过导航到下面的文章来 [在 Linux 中安装和配置 snap][7]。如果你已经在你的系统上安装 snap,其它的步骤可以忽略。
|
||||
|
||||
```
|
||||
$ sudo snap install --devmode --beta anbox
|
||||
```
|
||||
|
||||
### Anbox 的必要条件
|
||||
|
||||
默认情况下,Anbox 并没有带有 Google Play Store。因此,我们需要手动下载每个应用程序(APK),并使用 Android 调试桥(ADB)安装它。
|
||||
|
||||
ADB 工具在大多数的发行版的软件仓库是轻易可获得的,我们可以容易地安装它。
|
||||
|
||||
对于 Debian/Ubuntu 系统,使用 [APT-GET 命令][3] 或 [APT 命令][4] 来安装 ADB。
|
||||
|
||||
```
|
||||
$ sudo apt install android-tools-adb
|
||||
```
|
||||
|
||||
对于 Fedora 系统,使用 [DNF 命令][8] 来安装 ADB。
|
||||
|
||||
```
|
||||
$ sudo dnf install android-tools
|
||||
```
|
||||
|
||||
对于基于 Arch Linux 的系统,使用 [Pacman 命令][9] 来安装 ADB。
|
||||
|
||||
```
|
||||
$ sudo pacman -S android-tools
|
||||
```
|
||||
|
||||
对于 openSUSE Leap 系统,使用 [Zypper 命令][10] 来安装 ADB。
|
||||
|
||||
```
|
||||
$ sudo zypper install android-tools
|
||||
```
|
||||
|
||||
### 在哪里下载 Android 应用程序?
|
||||
|
||||
既然我们不能使用 Play Store ,你就得从信得过的网站来下载 APK 软件包,像 [APKMirror][11] ,然后手动安装它。
|
||||
|
||||
### 如何启动 Anbox?
|
||||
|
||||
Anbox 可以从 Dash 启动。这是默认的 Anbox 外貌。
|
||||
|
||||
![][13]
|
||||
|
||||
### 如何把应用程序推到 Anbox ?
|
||||
|
||||
像我先前所说,我们需要手动安装它。为测试目的,我们将安装 YouTube 和 Firefox 应用程序。
|
||||
|
||||
首先,你需要启动 ADB 服务。为做到这样,运行下面的命令。
|
||||
|
||||
```
|
||||
$ adb devices
|
||||
```
|
||||
|
||||
我们已经下载 YouTube 和 Firefox 应用程序,现在我们将安装。
|
||||
|
||||
语法格式:
|
||||
|
||||
```
|
||||
$ adb install Name-Of-Your-Application.apk
|
||||
```
|
||||
|
||||
安装 YouTube 和 Firefox 应用程序:
|
||||
|
||||
```
|
||||
$ adb install 'com.google.android.youtube_14.13.54-1413542800_minAPI19(x86_64)(nodpi)_apkmirror.com.apk'
|
||||
Success
|
||||
|
||||
$ adb install 'org.mozilla.focus_9.0-330191219_minAPI21(x86)(nodpi)_apkmirror.com.apk'
|
||||
Success
|
||||
```
|
||||
|
||||
我已经在我的 Anbox 中安装 YouTube 和 Firefox。查看下面的截图。
|
||||
|
||||
![][14]
|
||||
|
||||
像我们在文章的开始所说,它将以新的标签页打开任何的应用程序。在这里,我们将打开 Firefox ,并访问 [2daygeek.com][15] 网站。
|
||||
|
||||
![][16]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/anbox-best-android-emulator-for-linux/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.2daygeek.com/install-configure-sdk-android-emulator-on-linux/
|
||||
[2]: https://www.2daygeek.com/install-genymotion-android-emulator-on-ubuntu-debian-fedora-arch-linux/
|
||||
[3]: https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[4]: https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[5]: https://www.2daygeek.com/category/aur-helper/
|
||||
[6]: https://www.2daygeek.com/install-yay-yet-another-yogurt-aur-helper-on-arch-linux/
|
||||
[7]: https://www.2daygeek.com/linux-snap-package-manager-ubuntu/
|
||||
[8]: https://www.2daygeek.com/dnf-command-examples-manage-packages-fedora-system/
|
||||
[9]: https://www.2daygeek.com/pacman-command-examples-manage-packages-arch-linux-system/
|
||||
[10]: https://www.2daygeek.com/zypper-command-examples-manage-packages-opensuse-system/
|
||||
[11]: https://www.apkmirror.com/
|
||||
[12]: data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
[13]: https://www.2daygeek.com/wp-content/uploads/2019/04/anbox-best-android-emulator-for-linux-1.jpg
|
||||
[14]: https://www.2daygeek.com/wp-content/uploads/2019/04/anbox-best-android-emulator-for-linux-2.jpg
|
||||
[15]: https://www.2daygeek.com/
|
||||
[16]: https://www.2daygeek.com/wp-content/uploads/2019/04/anbox-best-android-emulator-for-linux-3.jpg
|
@ -1,69 +1,59 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (arrowfeng)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10820-1.html)
|
||||
[#]: subject: (How To Install And Configure Chrony As NTP Client?)
|
||||
[#]: via: (https://www.2daygeek.com/configure-ntp-client-using-chrony-in-linux/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
如何正确安装和配置Chrony作为NTP客户端?
|
||||
如何安装和配置 Chrony 作为 NTP 客户端?
|
||||
======
|
||||
|
||||
NTP服务器和NTP客户端运行我们通过网络来同步时钟。
|
||||
NTP 服务器和 NTP 客户端可以让我们通过网络来同步时钟。之前,我们已经撰写了一篇关于 [NTP 服务器和 NTP 客户端的安装与配置][1] 的文章。
|
||||
|
||||
在过去,我们已经撰写了一篇关于 **[NTP服务器和NTP客户端的安装与配置][1]** 的文章。
|
||||
如果你想看这些内容,点击上述的 URL 访问。
|
||||
|
||||
如果你想看这些内容,点击上述的URL访问。
|
||||
### Chrony 客户端
|
||||
|
||||
### 什么是Chrony客户端?
|
||||
Chrony 是 NTP 客户端的替代品。它能以更精确的时间和更快的速度同步时钟,并且它对于那些不是全天候在线的系统非常有用。
|
||||
|
||||
Chrony是NTP客户端的替代品。
|
||||
|
||||
它能以更精确的时间和更快的速度同步时钟,并且它对于那些不是全天候在线的系统非常有用。
|
||||
|
||||
chronyd更小、更省电,它占用更少的内存且仅当需要时它才唤醒CPU。
|
||||
|
||||
即使网络拥塞较长时间,它也能很好地运行。
|
||||
|
||||
它支持Linux上的硬件时间戳,允许在本地网络进行极其准确的同步。
|
||||
chronyd 更小、更节能,它占用更少的内存且仅当需要时它才唤醒 CPU。即使网络拥塞较长时间,它也能很好地运行。它支持 Linux 上的硬件时间戳,允许在本地网络进行极其准确的同步。
|
||||
|
||||
它提供下列两个服务。
|
||||
|
||||
* **`chronyc:`** Chrony的命令行接口。
|
||||
* **`chronyd:`** Chrony守护进程服务。
|
||||
* `chronyc`:Chrony 的命令行接口。
|
||||
* `chronyd`:Chrony 守护进程服务。
|
||||
|
||||
|
||||
|
||||
### 如何在Linux上安装和配置Chrony?
|
||||
### 如何在 Linux 上安装和配置 Chrony?
|
||||
|
||||
由于安装包在大多数发行版的官方仓库中可用,因此直接使用包管理器去安装它。
|
||||
|
||||
对于 **`Fedora`** 系统, 使用 **[DNF 命令][2]** 去安装chrony.
|
||||
对于 Fedora 系统,使用 [DNF 命令][2] 去安装 chrony。
|
||||
|
||||
```
|
||||
$ sudo dnf install chrony
|
||||
```
|
||||
|
||||
对于 **`Debian/Ubuntu`** 系统, 使用 **[APT-GET 命令][3]** 或者 **[APT 命令][4]** 去安装chrony.
|
||||
对于 Debian/Ubuntu 系统,使用 [APT-GET 命令][3] 或者 [APT 命令][4] 去安装 chrony。
|
||||
|
||||
```
|
||||
$ sudo apt install chrony
|
||||
```
|
||||
|
||||
对基于 **`Arch Linux`** 的系统, 使用 **[Pacman 命令][5]** 去安装chrony.
|
||||
对基于 Arch Linux 的系统,使用 [Pacman 命令][5] 去安装 chrony。
|
||||
|
||||
```
|
||||
$ sudo pacman -S chrony
|
||||
```
|
||||
|
||||
对于 **`RHEL/CentOS`** 系统, 使用 **[YUM 命令][6]** 去安装chrony.
|
||||
对于 RHEL/CentOS 系统,使用 [YUM 命令][6] 去安装 chrony。
|
||||
|
||||
```
|
||||
$ sudo yum install chrony
|
||||
```
|
||||
|
||||
对于**`openSUSE Leap`** 系统, 使用 **[Zypper 命令][7]** 去安装chrony.
|
||||
对于 openSUSE Leap 系统,使用 [Zypper 命令][7] 去安装 chrony。
|
||||
|
||||
```
|
||||
$ sudo zypper install chrony
|
||||
@ -71,20 +61,18 @@ $ sudo zypper install chrony
|
||||
|
||||
在这篇文章中,我们将使用下列设置去测试。
|
||||
|
||||
* **`NTP服务器:`** 主机名: CentOS7.2daygeek.com, IP:192.168.1.5, OS:CentOS 7
|
||||
* **`Chrony客户端:`** 主机名: Ubuntu18.2daygeek.com, IP:192.168.1.3, OS:Ubuntu 18.04
|
||||
* NTP 服务器:主机名:CentOS7.2daygeek.com,IP:192.168.1.5,OS:CentOS 7
|
||||
* Chrony 客户端:主机名:Ubuntu18.2daygeek.com,IP:192.168.1.3,OS:Ubuntu 18.04
|
||||
|
||||
服务器的安装请访问 [在 Linux 上安装和配置 NTP 服务器][1] 的 URL。
|
||||
|
||||
导航到 **[在Linux上安装和配置NTP服务器][1]** 的URL。
|
||||
我已经在 CentOS7.2daygeek.com 这台主机上安装和配置了 NTP 服务器,因此,将其附加到所有的客户端机器上。此外,还包括其他所需信息。
|
||||
|
||||
`chrony.conf` 文件的位置根据你的发行版不同而不同。
|
||||
|
||||
我已经在`CentOS7.2daygeek.com`这台主机上安装和配置了NTP服务器,因此,将其附加到所有的客户端机器上。此外,还包括其他所需信息。
|
||||
对基于 RHEL 的系统,它位于 `/etc/chrony.conf`。
|
||||
|
||||
`chrony.conf`文件的位置根据你的发行版不同而不同。
|
||||
|
||||
对基于RHEL的系统,它位于`/etc/chrony.conf`。
|
||||
|
||||
对基于Debian的系统,它位于`/etc/chrony/chrony.conf`。
|
||||
对基于 Debian 的系统,它位于 `/etc/chrony/chrony.conf`。
|
||||
|
||||
```
|
||||
# vi /etc/chrony/chrony.conf
|
||||
@ -98,28 +86,25 @@ makestep 1 3
|
||||
cmdallow 192.168.1.0/24
|
||||
```
|
||||
|
||||
更新配置后需要重启Chrony服务。
|
||||
更新配置后需要重启 Chrony 服务。
|
||||
|
||||
对于sysvinit系统。基于RHEL的系统需要去运行`chronyd`而不是chrony。
|
||||
对于 sysvinit 系统。基于 RHEL 的系统需要去运行 `chronyd` 而不是 `chrony`。
|
||||
|
||||
```
|
||||
# service chronyd restart
|
||||
|
||||
# chkconfig chronyd on
|
||||
```
|
||||
|
||||
对于systemctl系统。 基于RHEL的系统需要去运行`chronyd`而不是chrony。
|
||||
对于 systemctl 系统。 基于 RHEL 的系统需要去运行 `chronyd` 而不是 `chrony`。
|
||||
|
||||
```
|
||||
# systemctl restart chronyd
|
||||
|
||||
# systemctl enable chronyd
|
||||
```
|
||||
|
||||
使用像tacking,sources和sourcestats这样的命令去检查chrony的同步细节。
|
||||
|
||||
去检查chrony的跟踪状态。
|
||||
使用像 `tacking`、`sources` 和 `sourcestats` 这样的子命令去检查 chrony 的同步细节。
|
||||
|
||||
去检查 chrony 的追踪状态。
|
||||
|
||||
```
|
||||
# chronyc tracking
|
||||
@ -138,7 +123,7 @@ Update interval : 2.0 seconds
|
||||
Leap status : Normal
|
||||
```
|
||||
|
||||
运行sources命令去显示当前时间源的信息。
|
||||
运行 `sources` 命令去显示当前时间源的信息。
|
||||
|
||||
```
|
||||
# chronyc sources
|
||||
@ -148,7 +133,7 @@ MS Name/IP address Stratum Poll Reach LastRx Last sample
|
||||
^* CentOS7.2daygeek.com 2 6 17 62 +36us[+1230us] +/- 1111ms
|
||||
```
|
||||
|
||||
sourcestats命令显示有关chronyd当前正在检查的每个源的漂移率和偏移估计过程的信息。
|
||||
`sourcestats` 命令显示有关 chronyd 当前正在检查的每个源的漂移率和偏移估计过程的信息。
|
||||
|
||||
```
|
||||
# chronyc sourcestats
|
||||
@ -158,7 +143,7 @@ Name/IP Address NP NR Span Frequency Freq Skew Offset Std Dev
|
||||
CentOS7.2daygeek.com 5 3 71 -97.314 78.754 -469us 441us
|
||||
```
|
||||
|
||||
当chronyd配置为NTP客户端或对等端时,你就能通过chronyc ntpdata命令向每一个NTP源发送和接收时间戳模式和交错模式报告。
|
||||
当 chronyd 配置为 NTP 客户端或对等端时,你就能通过 `chronyc ntpdata` 命令向每一个 NTP 源发送/接收时间戳模式和交错模式的报告。
|
||||
|
||||
```
|
||||
# chronyc ntpdata
|
||||
@ -191,15 +176,14 @@ Total RX : 46
|
||||
Total valid RX : 46
|
||||
```
|
||||
|
||||
最后运行`date`命令。
|
||||
最后运行 `date` 命令。
|
||||
|
||||
```
|
||||
# date
|
||||
Thu Mar 28 03:08:11 CDT 2019
|
||||
```
|
||||
|
||||
为了立即切换系统时钟,通过转换绕过任何正在进行的调整,请以root身份发出以下命令(手动调整系统时钟)。
|
||||
To step the system clock immediately, bypassing any adjustments in progress by slewing, issue the following command as root (To adjust the system clock manually).
|
||||
为了立即跟进系统时钟,绕过任何正在进行的缓步调整,请以 root 身份运行以下命令(以手动调整系统时钟)。
|
||||
|
||||
```
|
||||
# chronyc makestep
|
||||
@ -212,13 +196,13 @@ via: https://www.2daygeek.com/configure-ntp-client-using-chrony-in-linux/
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[arrowfeng](https://github.com/arrowfeng)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.2daygeek.com/install-configure-ntp-server-ntp-client-in-linux/
|
||||
[1]: https://linux.cn/article-10811-1.html
|
||||
[2]: https://www.2daygeek.com/dnf-command-examples-manage-packages-fedora-system/
|
||||
[3]: https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[4]: https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/
|
@ -1,49 +1,46 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (warmfrog)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10817-1.html)
|
||||
[#]: subject: (Installing Ubuntu MATE on a Raspberry Pi)
|
||||
[#]: via: (https://itsfoss.com/ubuntu-mate-raspberry-pi/)
|
||||
[#]: author: (Chinmay https://itsfoss.com/author/chinmay/)
|
||||
|
||||
在 Raspberry Pi 上安装 Ubuntu MATE
|
||||
在树莓派上安装 Ubuntu MATE
|
||||
=================================
|
||||
|
||||
_**简介: 这篇快速指南告诉你如何在 Raspberry Pi 设备上安装 Ubuntu MATE。**_
|
||||
> 简介: 这篇快速指南告诉你如何在树莓派设备上安装 Ubuntu MATE。
|
||||
|
||||
[Raspberry Pi][1] 是目前最流行的单板机并且是制造商的首选。[Raspbian][2] 是基于 Debian 的 Pi 的官方操作系统。它是轻量级的,内置了教育工具和能在大部分场景下完成工作的工具。
|
||||
|
||||
[安装 Raspbian][3] 安装同样简单,但是与 [Debian][4] 一起的问题是慢的升级周期和旧的软件包。
|
||||
|
||||
在 Raspberry Pi 上运行 Ubuntu 给你带来一个更丰富的体验和最新的软件。当在你的 Pi 上运行 Ubuntu 时我们有几个选择。
|
||||
|
||||
1. [Ubuntu MATE][5] :Ubuntu MATE 是仅有的原生支持 Raspberry Pi 包含一个完整的桌面环境的分发版。
|
||||
2. [Ubuntu Server 18.04][6] \+ 手动安装一个桌面环境。
|
||||
3. 使用 [Ubuntu Pi Flavor Maker][7] 社区构建的镜像,_这些镜像只支持 Raspberry Pi 2B 和 3B 的变种_并且**不能**更新到最新的 LTS 发布版。
|
||||
[树莓派][1] 是目前最流行的单板机并且是创客首选的板子。[Raspbian][2] 是基于 Debian 的树莓派官方操作系统。它是轻量级的,内置了教育工具和能在大部分场景下完成工作的工具。
|
||||
|
||||
[安装 Raspbian][3] 安装同样简单,但是与 [Debian][4] 随同带来的问题是慢的升级周期和旧的软件包。
|
||||
|
||||
在树莓派上运行 Ubuntu 可以给你带来一个更丰富的体验和最新的软件。当在你的树莓派上运行 Ubuntu 时我们有几个选择。
|
||||
|
||||
1. [Ubuntu MATE][5] :Ubuntu MATE 是仅有的原生支持树莓派且包含一个完整的桌面环境的发行版。
|
||||
2. [Ubuntu Server 18.04][6] + 手动安装一个桌面环境。
|
||||
3. 使用 [Ubuntu Pi Flavor Maker][7] 社区构建的镜像,这些镜像只支持树莓派 2B 和 3B 的变种,并且**不能**更新到最新的 LTS 发布版。
|
||||
|
||||
第一个选择安装是最简单和快速的,而第二个选择给了你自由选择安装桌面环境的机会。我推荐选择前两个中的任一个。
|
||||
|
||||
这里是一些磁盘镜像下载链接。在这篇文章里我只会提及 Ubuntu MATE 的安装。
|
||||
|
||||
### 在 Raspberry Pi 上安装 Ubuntu MATE
|
||||
### 在树莓派上安装 Ubuntu MATE
|
||||
|
||||
去 Ubuntu MATE 的下载页面获取推荐的镜像。
|
||||
|
||||
![][8]
|
||||
|
||||
试验 ARM64 版只应在你需要在 Raspberry Pi 服务器上运行像 MongoDB 这样的 64-bit 应用时使用。
|
||||
试验性的 ARM64 版本只应在你需要在树莓派服务器上运行像 MongoDB 这样的 64 位应用时使用。
|
||||
|
||||
[ 下载为 Raspberry Pi 准备的 Ubuntu MATE][9]
|
||||
- [下载为树莓派准备的 Ubuntu MATE][9]
|
||||
|
||||
#### 第 1 步:设置 SD 卡
|
||||
|
||||
镜像文件一旦下载完成后需要解压。你应该简单的右击来提取它。
|
||||
镜像文件一旦下载完成后需要解压。你可以简单的右击来提取它。
|
||||
|
||||
可替换地,下面命令做同样的事。
|
||||
也可以使用下面命令做同样的事。
|
||||
|
||||
```
|
||||
xz -d ubuntu-mate***.img.xz
|
||||
@ -51,7 +48,7 @@ xz -d ubuntu-mate***.img.xz
|
||||
|
||||
如果你在 Windows 上你可以使用 [7-zip][10] 替代。
|
||||
|
||||
安装 **[Balena Etcher][11]**,我们将使用这个工具将镜像写入 SD 卡。确保你的 SD 卡有至少 8 GB 的容量。
|
||||
安装 [Balena Etcher][11],我们将使用这个工具将镜像写入 SD 卡。确保你的 SD 卡有至少 8 GB 的容量。
|
||||
|
||||
启动 Etcher,选择镜像文件和 SD 卡。
|
||||
|
||||
@ -59,21 +56,19 @@ xz -d ubuntu-mate***.img.xz
|
||||
|
||||
一旦进度完成 SD 卡就准备好了。
|
||||
|
||||
#### 第 2 步:设置 Raspberry Pi
|
||||
#### 第 2 步:设置树莓派
|
||||
|
||||
你可能已经知道你需要一些外设才能使用 Raspberry Pi 例如 鼠标,键盘, HDMI 线等等。你同样可以[不用键盘和鼠标安装 Raspberry Pi][13] 但是这篇指南不是那样。
|
||||
你可能已经知道你需要一些外设才能使用树莓派,例如鼠标、键盘、HDMI 线等等。你同样可以[不用键盘和鼠标安装树莓派][13],但是这篇指南不是那样。
|
||||
|
||||
* 插入一个鼠标和一个键盘。
|
||||
* 连接 HDMI 线缆。
|
||||
* 插入 SD 卡 到 SD 卡槽。
|
||||
|
||||
|
||||
|
||||
插入电源线给它供电。确保你有一个好的电源供应(5V,3A 至少)。一个不好的电源供应可能降低性能。
|
||||
插入电源线给它供电。确保你有一个好的电源供应(5V、3A 至少)。一个不好的电源供应可能降低性能。
|
||||
|
||||
#### Ubuntu MATE 安装
|
||||
|
||||
一旦你给 Raspberry Pi 供电,你将遇到非常熟悉的 Ubuntu 安装过程。在这里的安装过程相当直接。
|
||||
一旦你给树莓派供电,你将遇到非常熟悉的 Ubuntu 安装过程。在这里的安装过程相当直接。
|
||||
|
||||
![选择你的键盘布局][14]
|
||||
|
||||
@ -83,7 +78,7 @@ xz -d ubuntu-mate***.img.xz
|
||||
|
||||
![添加用户名和密码][16]
|
||||
|
||||
在设置了键盘布局,时区和用户凭证后,在几分钟后你将被带到登录界面。瞧!你快要完成了。
|
||||
在设置了键盘布局、时区和用户凭证后,在几分钟后你将被带到登录界面。瞧!你快要完成了。
|
||||
|
||||
![][17]
|
||||
|
||||
@ -98,9 +93,9 @@ sudo apt upgrade
|
||||
|
||||
![][19]
|
||||
|
||||
一旦更新完成安装你就可以开始了。你可以根据你的需要继续安装 Raspberry Pi 平台的为 GPIO 和其他 I/O 准备的特定软件包。
|
||||
一旦更新完成安装你就可以开始了。你可以根据你的需要继续安装树莓派平台为 GPIO 和其他 I/O 准备的特定软件包。
|
||||
|
||||
是什么让你考虑在 Raspberry 上安装 Ubuntu,你对 Raspbian 的体验如何呢?在下方评论来让我知道。
|
||||
是什么让你考虑在 Raspberry 上安装 Ubuntu,你对 Raspbian 的体验如何呢?请在下方评论来让我知道。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -109,7 +104,7 @@ via: https://itsfoss.com/ubuntu-mate-raspberry-pi/
|
||||
作者:[Chinmay][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[warmfrog](https://github.com/warmfrog)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,38 +1,38 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (warmfrog)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10823-1.html)
|
||||
[#]: subject: (12 Single Board Computers: Alternative to Raspberry Pi)
|
||||
[#]: via: (https://itsfoss.com/raspberry-pi-alternatives/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
12 个可替换 Raspberry Pi 的单片机
|
||||
12 个可替代树莓派的单板机
|
||||
================================
|
||||
|
||||
_**简介: 正在寻找 Raspberry Pi 的替代品? 这里有一些单片机可以满足你的 DIY 渴求**_
|
||||
> 正在寻找树莓派的替代品?这里有一些单板机可以满足你的 DIY 渴求。
|
||||
|
||||
Raspberry Pi 是当前最流行的单片机。你可以在你的 DIY 项目中使用它,或者用它作为一个成本效益高的系统来学习编代码,或者为了你的便利,利用一个[流媒体软件][1]运行在上面作为流媒体设备。
|
||||
树莓派是当前最流行的单板机。你可以在你的 DIY 项目中使用它,或者用它作为一个成本效益高的系统来学习编代码,或者为了你的便利,利用一个[流媒体软件][1]运行在上面作为流媒体设备。
|
||||
|
||||
你可以使用 Raspberry Pi 做很多事,但它不是各种极客的最终解决方案。一些人可能在寻找更便宜的开发板,一些可能在寻找更强大的。
|
||||
你可以使用树莓派做很多事,但它不是各种极客的最终解决方案。一些人可能在寻找更便宜的开发板,一些可能在寻找更强大的。
|
||||
|
||||
无论是哪种情况,我们都有很多原因需要 Raspberry Pi 的替代品。因此,在这片文章里,我们将讨论最好的十个我们认为能够替代 Raspberry Pi 的单片机。
|
||||
无论是哪种情况,我们都有很多原因需要树莓派的替代品。因此,在这片文章里,我们将讨论最好的 12 个我们认为能够替代树莓派的单板机。
|
||||
|
||||
![][2]
|
||||
|
||||
### 满足你 DIY 渴望的 Raspberry Pi 替代品
|
||||
### 满足你 DIY 渴望的树莓派替代品
|
||||
|
||||
这个列表没有特定的顺序排名。链接的一部分是附属链接。请阅读我们的[附属政策][3].
|
||||
这个列表没有特定的顺序排名。链接的一部分是赞助链接。请阅读我们的[赞助政策][3]。
|
||||
|
||||
#### 1\. Onion Omega2+
|
||||
#### 1、Onion Omega2+
|
||||
|
||||
![][4]
|
||||
|
||||
只要 **$13**,Omega2+ 是这里你可以找到的最便宜的 IoT 单片机设备。它运行 LEDE(Linux 嵌入式开发环境)Linux 系统 - 一个基于 [OpenWRT][5] 的分发版。
|
||||
只要 $13,Omega2+ 是这里你可以找到的最便宜的 IoT 单板机设备。它运行 LEDE(Linux 嵌入式开发环境)Linux 系统 —— 这是一个基于 [OpenWRT][5] 的发行版。
|
||||
|
||||
由于运行一个自定义 Linux 系统,它的组成因素,花费,和灵活性使它完美适合几乎所有类型的 IoT 应用。
|
||||
由于运行一个自定义 Linux 系统,它的组成元件、花费和灵活性使它完美适合几乎所有类型的 IoT 应用。
|
||||
|
||||
你可以在[亚马逊商城的 Onion Omega 装备][6]或者从他们的网站下单,可能会收取额外的邮费。
|
||||
你可以在[亚马逊商城的 Onion Omega 套件][6]或者从他们的网站下单,可能会收取额外的邮费。
|
||||
|
||||
**关键参数:**
|
||||
|
||||
@ -44,15 +44,15 @@ Raspberry Pi 是当前最流行的单片机。你可以在你的 DIY 项目中
|
||||
* USB 2.0
|
||||
* 12 GPIO Pins
|
||||
|
||||
[查看官网][7]
|
||||
[查看官网][7]
|
||||
|
||||
#### 2\. NVIDIA Jetson Nano Developer Kit
|
||||
#### 2、NVIDIA Jetson Nano Developer Kit
|
||||
|
||||
这是来自 NVIDIA 的只要 **$99** 的非常独特和有趣的 Raspberry Pi 替代品。是的,它不是每个人都能充分利用的设备 - 只为特定的一组极客或者开发者。
|
||||
这是来自 NVIDIA 的只要 **$99** 的非常独特和有趣的树莓派替代品。是的,它不是每个人都能充分利用的设备 —— 只为特定的一组极客或者开发者而生。
|
||||
|
||||
NVIDIA 使用下面的用例解释它:
|
||||
|
||||
> NVIDIA® Jetson Nano™ Developer Kit 是一个小的,强大的让你并行运行多个神经网络的应用像图像分类,对象侦察,分段,语音处理。全部在一个易于使用的运行功率只有 5 瓦特平台。
|
||||
> NVIDIA® Jetson Nano™ Developer Kit 是一个小的、强大的计算机,可以让你并行运行多个神经网络的应用像图像分类、对象侦察、图像分段、语音处理。全部在一个易于使用的、运行功率只有 5 瓦特的平台上。
|
||||
>
|
||||
> nvidia
|
||||
|
||||
@ -66,20 +66,17 @@ NVIDIA 使用下面的用例解释它:
|
||||
* Display: HDMI 2.0
|
||||
* 4 x USB 3.0 and eDP 1.4
|
||||
|
||||
[查看官网][9]
|
||||
|
||||
|
||||
[查看官网
|
||||
][9]
|
||||
|
||||
#### 3\. ASUS Tinker Board S
|
||||
#### 3、ASUS Tinker Board S
|
||||
|
||||
![][10]
|
||||
|
||||
ASUS Tinker Board S 不是大多数可负担得起的可替代 Raspberry Pi 的替换设备 (**$82**, [亚马逊商城][11]),但是它是一个强大的替代品。它的特点是有你通常可以发现与标准 Raspberry Pi 3 Model 一样的 40 针脚的连接器,但是提供了强大的处理器和 GPU。同样的,Tinker Board S 的大小恰巧和标准的 Raspberry Pi 3 一样大。
|
||||
ASUS Tinker Board S 不是大多数人可负担得起的树莓派的替换设备 (**$82**,[亚马逊商城][11]),但是它是一个强大的替代品。它的特点是它有你通常可以发现与标准树莓派 3 一样的 40 针脚的连接器,但是提供了强大的处理器和 GPU。同样的,Tinker Board S 的大小恰巧和标准的树莓派3 一样大。
|
||||
|
||||
这个板子的主要亮点是 16 GB [eMMC][12] (用外行术语说,它的板上有一个类似 SSD 的存储单元使它工作时运行的更快。) 的存在。
|
||||
|
||||
**关键参数**
|
||||
**关键参数:**
|
||||
|
||||
* Rockchip Quad-Core RK3288 processor
|
||||
* 2 GB DDR3 RAM
|
||||
@ -92,20 +89,17 @@ ASUS Tinker Board S 不是大多数可负担得起的可替代 Raspberry Pi 的
|
||||
* 28 GPIO pins
|
||||
* HDMI Interface
|
||||
|
||||
[查看网站][13]
|
||||
|
||||
|
||||
[查看网站
|
||||
][13]
|
||||
|
||||
#### 4\. ClockworkPi
|
||||
#### 4、ClockworkPi
|
||||
|
||||
![][14]
|
||||
|
||||
如果你在想方设法组装一个模块化的复古的游戏控制台,Clockwork Pi 通常是 [GameShell Kit][15] 的一部分。然而,你可以 使用 $49 单独购买板子。
|
||||
如果你正在想方设法组装一个模块化的复古的游戏控制台,Clockwork Pi 可能就是你需要的,它通常是 [GameShell Kit][15] 的一部分。然而,你可以 使用 $49 单独购买板子。
|
||||
|
||||
它紧凑的大小,WiFi 连接性,和 micro HDMI 端口的存在使它成为很多事物的选择。
|
||||
它紧凑的大小、WiFi 连接性和 micro HDMI 端口的存在使它成为许多方面的选择。
|
||||
|
||||
**关键参数**
|
||||
**关键参数:**
|
||||
|
||||
* Allwinner R16-J Quad-core Cortex-A7 CPU @1.2GHz
|
||||
* Mali-400 MP2 GPU
|
||||
@ -114,41 +108,33 @@ ASUS Tinker Board S 不是大多数可负担得起的可替代 Raspberry Pi 的
|
||||
* Micro HDMI output
|
||||
* MicroSD Card Slot
|
||||
|
||||
[查看官网][16]
|
||||
|
||||
|
||||
[查看官网
|
||||
][16]
|
||||
|
||||
#### 5\. Arduino Mega 2560
|
||||
#### 5、Arduino Mega 2560
|
||||
|
||||
![][17]
|
||||
|
||||
如果你正在研究机器人项目或者你想要一个 3D 打印机 - Arduino Mega 2560 将是 Raspberry Pi 的便利的替代品。不像 Raspberry Pi,它是基于微控制器而不是微处理器的。
|
||||
如果你正在研究机器人项目或者你想要一个 3D 打印机 —— Arduino Mega 2560 将是树莓派的便利的替代品。不像树莓派,它是基于微控制器而不是微处理器的。
|
||||
|
||||
在他们的[官网][18],它会花费你 $38.50 或者在[在亚马逊商城 $33][19]。
|
||||
在他们的[官网][18],你需要花费 $38.50,或者在[在亚马逊商城是 $33][19]。
|
||||
|
||||
**关键参数:**
|
||||
|
||||
**Key Specifications:**
|
||||
|
||||
* Microcontroller: ATmega2560
|
||||
* Clock Speed: 16 MHz
|
||||
* Digital I/O Pins: 54
|
||||
* Analog Input Pins: 16
|
||||
* Flash Memory: 256 KB of which 8 KB used by bootloader
|
||||
|
||||
[查看官网][18]
|
||||
|
||||
|
||||
[查看官网
|
||||
][18]
|
||||
|
||||
#### 6\. Rock64 Media Board
|
||||
#### 6、Rock64 Media Board
|
||||
|
||||
![][20]
|
||||
|
||||
对于与你可能想要 Raspberry Pi 3 B+ 相同的投资,你将在 Rock64 Media Board 上获得更快的处理器和双倍的内存。除此之外,如果你想要 1 GB RAM 版的,它提供了一个 Raspberry Pi 的 更便宜的替代,花费更少,只要 $10 。
|
||||
用与你可能想要的树莓派 3 B+ 相同的价格,你将在 Rock64 Media Board 上获得更快的处理器和双倍的内存。除此之外,如果你想要 1 GB RAM 版的,它提供了一个比树莓派更便宜的替代品,花费更少,只要 $10 。
|
||||
|
||||
不像 Raspberry Pi,这里没有无线连接支持,但是 USB 3.0 和 HDMI 2.0 的存在使它与众不同,如果它对你很重要的话。
|
||||
不像树莓派,它没有无线连接支持,但是 USB 3.0 和 HDMI 2.0 的存在使它与众不同,如果它对你很重要的话。
|
||||
|
||||
**关键参数:**
|
||||
|
||||
@ -159,20 +145,18 @@ ASUS Tinker Board S 不是大多数可负担得起的可替代 Raspberry Pi 的
|
||||
* USB 3.0
|
||||
* HDMI 2.0
|
||||
|
||||
[查看官网][21]
|
||||
|
||||
|
||||
[查看官网
|
||||
][21]
|
||||
|
||||
#### 7\. Odroid-XU4
|
||||
#### 7、Odroid-XU4
|
||||
|
||||
![][22]
|
||||
|
||||
Odroid-XU4 是一个完美的 Raspberry Pi 的替代,如果你有能够稍微提高预算的空间($80-$100 甚至更低,取决于存储的容量)。
|
||||
Odroid-XU4 是一个完美的树莓派的替代品,如果你有能够稍微提高预算的空间($80-$100 甚至更低,取决于存储的容量)。
|
||||
|
||||
它确实是一个强大的替代并且体积更小。 支持 eMMC 和 USB 3.0 使它工作起来更快。
|
||||
它确实是一个强大的替代品并且体积更小。支持 eMMC 和 USB 3.0 使它工作起来更快。
|
||||
|
||||
**关键参数:**
|
||||
|
||||
* Samsung Exynos 5422 Octa ARM Cortex™-A15 Quad 2Ghz and Cortex™-A7 Quad 1.3GHz CPUs
|
||||
* 2Gbyte LPDDR3 RAM
|
||||
* GPU: Mali-T628 MP6
|
||||
@ -181,16 +165,13 @@ Odroid-XU4 是一个完美的 Raspberry Pi 的替代,如果你有能够稍微提
|
||||
* eMMC 5.0 module socket
|
||||
* MicroSD Card Slot
|
||||
|
||||
[查看官网][23]
|
||||
|
||||
|
||||
[查看官网
|
||||
][23]
|
||||
|
||||
#### 8\. **PocketBeagle**
|
||||
#### 8、PocketBeagle
|
||||
|
||||
![][24]
|
||||
|
||||
它是一个难以置信的小的单片机 - 几乎和 Raspberry Pi Zero 相似。然而它会花费完全大小的 Raspberry Pi 3 相同的价格。主要的亮点是你可以用它作为一个 USB 便携式信息终端 并且进入 Linux 命令行工作。
|
||||
它是一个难以置信的小的单板机 —— 几乎和树莓派Zero 相似。然而它的价格相当于完整大小的树莓派 3。主要的亮点是你可以用它作为一个 USB 便携式信息终端,并且进入 Linux 命令行工作。
|
||||
|
||||
**关键参数:**
|
||||
|
||||
@ -200,22 +181,18 @@ Odroid-XU4 是一个完美的 Raspberry Pi 的替代,如果你有能够稍微提
|
||||
* microUSB
|
||||
* USB 2.0
|
||||
|
||||
[查看官网][25]
|
||||
|
||||
|
||||
[查看官网
|
||||
][25]
|
||||
|
||||
#### 9\. Le Potato
|
||||
#### 9、Le Potato
|
||||
|
||||
![][26]
|
||||
|
||||
由 [Libre Computer][27] 出品的 Le Potato,同样被它的型号 AML-S905X-CC 标识。它花费你 [$45][28]。
|
||||
由 [Libre Computer][27] 出品的 Le Potato,其型号是 AML-S905X-CC。它需要花费你 [$45][28]。
|
||||
|
||||
如果你花费的比 Raspberry Pi 更多的钱,你就能得到想要双倍内存和 HDMI 2.0 接口,这可能是一个完美的选择。尽管,你还是不能发现嵌入的无线连接。
|
||||
如果你花费的比树莓派更多的钱,你就能得到双倍内存和 HDMI 2.0 接口,这可能是一个完美的选择。尽管,你还是不能找到嵌入的无线连接。
|
||||
|
||||
**关键参数:**
|
||||
|
||||
|
||||
* Amlogic S905X SoC
|
||||
* 2GB DDR3 SDRAM
|
||||
* USB 2.0
|
||||
@ -224,18 +201,15 @@ Odroid-XU4 是一个完美的 Raspberry Pi 的替代,如果你有能够稍微提
|
||||
* MicroSD Card Slot
|
||||
* eMMC Interface
|
||||
|
||||
[查看官网][29]
|
||||
|
||||
|
||||
[查看官网
|
||||
][29]
|
||||
|
||||
#### 10\. Banana Pi M64
|
||||
#### 10、Banana Pi M64
|
||||
|
||||
![][30]
|
||||
|
||||
它自带了 8 Gigs 的 eMMC - 是替代 Raspberry Pi 的主要亮点。由于相同的原因,它花费 $60。
|
||||
它自带了 8G 的 eMMC —— 这是替代树莓派的主要亮点。因此,它需要花费 $60。
|
||||
|
||||
HDMI 接口的存在使它胜任 4K。除此之外,Banana Pi 提供了更多种类的开源单片机作为 Raspberry Pi 的替代。
|
||||
HDMI 接口的存在使它胜任 4K。除此之外,Banana Pi 提供了更多种类的开源单板机作为树莓派的替代。
|
||||
|
||||
**关键参数:**
|
||||
|
||||
@ -246,18 +220,15 @@ HDMI 接口的存在使它胜任 4K。除此之外,Banana Pi 提供了更多
|
||||
* USB 2.0
|
||||
* HDMI
|
||||
|
||||
[查看官网][31]
|
||||
|
||||
|
||||
[查看官网
|
||||
][31]
|
||||
|
||||
#### 11\. Orange Pi Zero
|
||||
#### 11、Orange Pi Zero
|
||||
|
||||
![][32]
|
||||
|
||||
Orange Pi Zero 相对于 Raspberry Pi 难以置信的便宜。你可以在 Aliexpress 或者亚马逊上以最多 $10 就能够获得。如果[稍微投资多点,你能够获得 512 MB RAM][33]。
|
||||
Orange Pi Zero 相对于树莓派来说难以置信的便宜。你可以在 Aliexpress 或者亚马逊上以最多 $10 就能够获得。如果[稍微多花点,你能够获得 512 MB RAM][33]。
|
||||
|
||||
如果这还不够充分,你可以花费大概 $25 获得更好的配置像 Orange Pi 3。
|
||||
如果这还不够,你可以花费大概 $25 获得更好的配置,比如 Orange Pi 3。
|
||||
|
||||
**关键参数:**
|
||||
|
||||
@ -268,18 +239,15 @@ Orange Pi Zero 相对于 Raspberry Pi 难以置信的便宜。你可以在 Aliex
|
||||
* WiFi
|
||||
* USB 2.0
|
||||
|
||||
[查看官网][34]
|
||||
|
||||
|
||||
[查看官网
|
||||
][34]
|
||||
|
||||
#### 12\. VIM 2 SBC by Khadas
|
||||
#### 12、VIM 2 SBC by Khadas
|
||||
|
||||
![][35]
|
||||
|
||||
由 Khadas 出品的 VIM 2 是最新的单片机,因此你能够在板上获取到蓝牙 5.0。[从 $99 的基础款到上限 $140][36].
|
||||
由 Khadas 出品的 VIM 2 是最新的单板机,因此你能够在板上得到蓝牙 5.0 支持。它的价格范围[从 $99 的基础款到上限 $140][36]。
|
||||
|
||||
基础款包含 2 GB RAM,16 GB eMMC 和蓝牙 4.1。然而,Pro/Max 版包含蓝牙 5.0,更多的内存,更多的 eMMC 存储。
|
||||
基础款包含 2 GB RAM、16 GB eMMC 和蓝牙 4.1。然而,Pro/Max 版包含蓝牙 5.0,更多的内存,更多的 eMMC 存储。
|
||||
|
||||
**关键参数:**
|
||||
|
||||
@ -292,13 +260,11 @@ Orange Pi Zero 相对于 Raspberry Pi 难以置信的便宜。你可以在 Aliex
|
||||
* HDMI 2.0a
|
||||
* WiFi
|
||||
|
||||
### 总结
|
||||
|
||||
我们知道有很多不同种类的单板机电脑。一些比树莓派更好 —— 它的一些小规格的版本有更便宜的价格。同样的,像 Jetson Nano 这样的单板机已经被裁剪用于特定用途。因此,取决于你需要什么 —— 你应该检查一下单板机的配置。
|
||||
|
||||
**总结**
|
||||
|
||||
我们知道有很多不同种类的单片机电脑。一些比 Raspberry Pi 更好 - 它的一些小规格的版本有更便宜的价格。同样的,单片机像 Jetson Nano 已经被裁剪用于特定用途。因此,取决于你需要什么 - 你应该验证单片机的配置。
|
||||
|
||||
如果你认为你知道比上述提到的更好的东西,请随意在下方评论来让我们知道。
|
||||
如果你知道比上述提到的更好的东西,请随意在下方评论来让我们知道。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -307,7 +273,7 @@ via: https://itsfoss.com/raspberry-pi-alternatives/
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[warmfrog](https://github.com/warmfrog)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -1,38 +1,30 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (bodhix)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10844-1.html)
|
||||
[#]: subject: (How To Enable (UP) And Disable (DOWN) A Network Interface Port (NIC) In Linux?)
|
||||
[#]: via: (https://www.2daygeek.com/enable-disable-up-down-nic-network-interface-port-linux-using-ifconfig-ifdown-ifup-ip-nmcli-nmtui/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
How To Enable (UP) And Disable (DOWN) A Network Interface Port (NIC) In Linux?
|
||||
Linux 中如何启用和禁用网卡?
|
||||
======
|
||||
|
||||
You may need to run these commands based on your requirements.
|
||||
你可能会根据你的需要执行以下命令。我会在这里列举一些你会用到这些命令的例子。
|
||||
|
||||
I can tell you few examples, where you would be needed this.
|
||||
当你添加一个网卡或者从一个物理网卡创建出一个虚拟网卡的时候,你可能需要使用这些命令将新网卡启用起来。另外,如果你对网卡做了某些修改或者网卡本身没有启用,那么你也需要使用以下的某个命令将网卡启用起来。
|
||||
|
||||
When you add a new network interface or when you create a new virtual network interface from the original physical interface.
|
||||
启用、禁用网卡有很多种方法。在这篇文章里,我们会介绍我们使用过的最好的 5 种方法。
|
||||
|
||||
you may need to bounce these commands to bring up the new interface.
|
||||
启用禁用网卡可以使用以下 5 个方法来完成:
|
||||
|
||||
Also, if you made any changes or if it’s down then you need to run one of the below commands to bring them up.
|
||||
* `ifconfig` 命令:用于配置网卡。它可以提供网卡的很多信息。
|
||||
* `ifdown/up` 命令:`ifdown` 命令用于禁用网卡,`ifup` 命令用于启用网卡。
|
||||
* `ip` 命令:用于管理网卡,用于替代老旧的、不推荐使用的 `ifconfig` 命令。它和 `ifconfig` 命令很相似,但是提供了很多 `ifconfig` 命令所不具有的强大的特性。
|
||||
* `nmcli` 命令:是一个控制 NetworkManager 并报告网络状态的命令行工具。
|
||||
* `nmtui` 命令:是一个与 NetworkManager 交互的、基于 curses 图形库的终端 UI 应用。
|
||||
|
||||
It can be done on many ways and we would like to add best five method which we used in the article.
|
||||
|
||||
It can be done using the below five methods.
|
||||
|
||||
* **`ifconfig Command:`** The ifconfig command is used configure a network interface. It provides so many information about NIC.
|
||||
* **`ifdown/up Command:`** The ifdown command take a network interface down and the ifup command bring a network interface up.
|
||||
* **`ip Command:`** ip command is used to manage NIC. It’s replacement of old and deprecated ifconfig command. It’s similar to ifconfig command but has many powerful features which isn’t available in ifconfig command.
|
||||
* **`nmcli Command:`** nmcli is a command-line tool for controlling NetworkManager and reporting network status.
|
||||
* **`nmtui Command:`** nmtui is a curses‐based TUI application for interacting with NetworkManager.
|
||||
|
||||
|
||||
|
||||
The below output shows the available network interface card (NIC) information in my Linux system.
|
||||
以下显示的是我的 Linux 系统中可用网卡的信息。
|
||||
|
||||
```
|
||||
# ip a
|
||||
@ -56,25 +48,25 @@ The below output shows the available network interface card (NIC) information in
|
||||
valid_lft forever preferred_lft forever
|
||||
```
|
||||
|
||||
### 1) How To Bring UP And Bring Down A Network Interface In Linux Using ifconfig Command?
|
||||
### 1、如何使用 ifconfig 命令启用禁用网卡?
|
||||
|
||||
The ifconfig command is used configure a network interface.
|
||||
`ifconfig` 命令用于配置网卡。
|
||||
|
||||
It is used at boot time to set up interfaces as necessary. It provides so many information about NIC. We can use ifconfig command when we need to make any changes on NIC.
|
||||
在系统启动过程中如果需要启用网卡,调用的命令就是 `ifconfig`。`ifconfig` 可以提供很多网卡的信息。不管我们想修改网卡的什么配置,都可以使用该命令。
|
||||
|
||||
Common Syntax for ifconfig:
|
||||
`ifconfig` 的常用语法:
|
||||
|
||||
```
|
||||
# ifconfig [NIC_NAME] Down/Up
|
||||
```
|
||||
|
||||
Run the following command to bring down the `enp0s3` interface in Linux. Make a note, you have to input your interface name instead of us.
|
||||
执行以下命令禁用 `enp0s3` 网卡。注意,这里你需要输入你自己的网卡名字。
|
||||
|
||||
```
|
||||
# ifconfig enp0s3 down
|
||||
```
|
||||
|
||||
Yes, the given interface is down now as per the following output.
|
||||
从以下输出结果可以看到网卡已经被禁用了。
|
||||
|
||||
```
|
||||
# ip a | grep -A 1 "enp0s3:"
|
||||
@ -82,13 +74,13 @@ Yes, the given interface is down now as per the following output.
|
||||
link/ether 08:00:27:c2:e4:e8 brd ff:ff:ff:ff:ff:ff
|
||||
```
|
||||
|
||||
Run the following command to bring down the `enp0s3` interface in Linux.
|
||||
执行以下命令启用 `enp0s3` 网卡。
|
||||
|
||||
```
|
||||
# ifconfig enp0s3 up
|
||||
```
|
||||
|
||||
Yes, the given interface is up now as per the following output.
|
||||
从以下输出结果可以看到网卡已经启用了。
|
||||
|
||||
```
|
||||
# ip a | grep -A 5 "enp0s3:"
|
||||
@ -100,27 +92,26 @@ Yes, the given interface is up now as per the following output.
|
||||
valid_lft forever preferred_lft forever
|
||||
```
|
||||
|
||||
### 2) How To Enable And Disable A Network Interface In Linux Using ifdown/up Command?
|
||||
### 2、如何使用 ifdown/up 命令启用禁用网卡?
|
||||
|
||||
The ifdown command take a network interface down and the ifup command bring a network interface up.
|
||||
`ifdown` 命令用于禁用网卡,`ifup` 命令用于启用网卡。
|
||||
|
||||
**Note:**It doesn’t work on new interface device name like `enpXXX`
|
||||
注意:这两个命令不支持以 `enpXXX` 命名的新的网络设备。
|
||||
|
||||
Common Syntax for ifdown/ifup:
|
||||
`ifdown`/`ifup` 的常用语法:
|
||||
|
||||
```
|
||||
# ifdown [NIC_NAME]
|
||||
|
||||
# ifup [NIC_NAME]
|
||||
```
|
||||
|
||||
Run the following command to bring down the `eth1` interface in Linux.
|
||||
执行以下命令禁用 `eth1` 网卡。
|
||||
|
||||
```
|
||||
# ifdown eth0
|
||||
# ifdown eth1
|
||||
```
|
||||
|
||||
Run the following command to bring down the `eth1` interface in Linux.
|
||||
从以下输出结果可以看到网卡已经被禁用了。
|
||||
|
||||
```
|
||||
# ip a | grep -A 3 "eth1:"
|
||||
@ -128,13 +119,13 @@ Run the following command to bring down the `eth1` interface in Linux.
|
||||
link/ether 08:00:27:d5:a0:18 brd ff:ff:ff:ff:ff:ff
|
||||
```
|
||||
|
||||
Run the following command to bring down the `eth1` interface in Linux.
|
||||
执行以下命令启用 `eth1` 网卡。
|
||||
|
||||
```
|
||||
# ifup eth0
|
||||
# ifup eth1
|
||||
```
|
||||
|
||||
Yes, the given interface is up now as per the following output.
|
||||
从以下输出结果可以看到网卡已经启用了。
|
||||
|
||||
```
|
||||
# ip a | grep -A 5 "eth1:"
|
||||
@ -145,32 +136,32 @@ Yes, the given interface is up now as per the following output.
|
||||
valid_lft forever preferred_lft forever
|
||||
```
|
||||
|
||||
ifup and ifdown doesn’t supporting the latest interface device `enpXXX` names. I got the below message when i ran the command.
|
||||
`ifup` 和 `ifdown` 不支持以 `enpXXX` 命名的网卡。当执行该命令时得到的结果如下:
|
||||
|
||||
```
|
||||
# ifdown enp0s8
|
||||
Unknown interface enp0s8
|
||||
```
|
||||
|
||||
### 3) How To Bring UP/Bring Down A Network Interface In Linux Using ip Command?
|
||||
### 3、如何使用 ip 命令启用禁用网卡?
|
||||
|
||||
ip command is used to manage Network Interface Card (NIC). It’s replacement of old and deprecated ifconfig command on modern Linux systems.
|
||||
`ip` 命令用于管理网卡,用于替代老旧的、不推荐使用的 `ifconfig` 命令。
|
||||
|
||||
It’s similar to ifconfig command but has many powerful features which isn’t available in ifconfig command.
|
||||
它和 `ifconfig` 命令很相似,但是提供了很多 `ifconfig` 命令不具有的强大的特性。
|
||||
|
||||
Common Syntax for ip:
|
||||
`ip` 的常用语法:
|
||||
|
||||
```
|
||||
# ip link set Down/Up
|
||||
```
|
||||
|
||||
Run the following command to bring down the `enp0s3` interface in Linux.
|
||||
执行以下命令禁用 `enp0s3` 网卡。
|
||||
|
||||
```
|
||||
# ip link set enp0s3 down
|
||||
```
|
||||
|
||||
Yes, the given interface is down now as per the following output.
|
||||
从以下输出结果可以看到网卡已经被禁用了。
|
||||
|
||||
```
|
||||
# ip a | grep -A 1 "enp0s3:"
|
||||
@ -178,13 +169,13 @@ Yes, the given interface is down now as per the following output.
|
||||
link/ether 08:00:27:c2:e4:e8 brd ff:ff:ff:ff:ff:ff
|
||||
```
|
||||
|
||||
Run the following command to bring down the `enp0s3` interface in Linux.
|
||||
执行以下命令启用 `enp0s3` 网卡。
|
||||
|
||||
```
|
||||
# ip link set enp0s3 up
|
||||
```
|
||||
|
||||
Yes, the given interface is up now as per the following output.
|
||||
从以下输出结果可以看到网卡已经启用了。
|
||||
|
||||
```
|
||||
# ip a | grep -A 5 "enp0s3:"
|
||||
@ -196,15 +187,13 @@ Yes, the given interface is up now as per the following output.
|
||||
valid_lft forever preferred_lft forever
|
||||
```
|
||||
|
||||
### 4) How To Enable And Disable A Network Interface In Linux Using nmcli Command?
|
||||
### 4、如何使用 nmcli 命令启用禁用网卡?
|
||||
|
||||
nmcli is a command-line tool for controlling NetworkManager and reporting network status.
|
||||
`nmcli` 是一个控制 NetworkManager 并报告网络状态的命令行工具。
|
||||
|
||||
It can be utilized as a replacement for nm-applet or other graphical clients. nmcli is used to create, display, edit, delete, activate, and deactivate network
|
||||
`nmcli` 可以用做 nm-applet 或者其他图形化客户端的替代品。它可以用于展示、创建、修改、删除、启用和停用网络连接。除此之后,它还可以用来管理和展示网络设备状态。
|
||||
|
||||
connections, as well as control and display network device status.
|
||||
|
||||
Run the following command to identify the interface name because nmcli command is perform most of the task using `profile name` instead of `device name`.
|
||||
`nmcli` 命令大部分情况下都是使用“配置名称”工作而不是“设备名称”。所以,执行以下命令,获取网卡对应的配置名称。(LCTT 译注:在使用 `nmtui` 或者 `nmcli` 管理网络连接的时候,可以为网络连接配置一个名称,就是这里提到的<ruby>配置名称<rt>Profile name</rt></ruby>`)
|
||||
|
||||
```
|
||||
# nmcli con show
|
||||
@ -213,20 +202,20 @@ Wired connection 1 3d5afa0a-419a-3d1a-93e6-889ce9c6a18c ethernet enp0s3
|
||||
Wired connection 2 a22154b7-4cc4-3756-9d8d-da5a4318e146 ethernet enp0s8
|
||||
```
|
||||
|
||||
Common Syntax for ip:
|
||||
`nmcli` 的常用语法:
|
||||
|
||||
```
|
||||
# nmcli con Down/Up
|
||||
```
|
||||
|
||||
Run the following command to bring down the `enp0s3` interface in Linux. You have to give `profile name` instead of `device name` to bring down it.
|
||||
执行以下命令禁用 `enp0s3` 网卡。在禁用网卡的时候,你需要使用配置名称而不是设备名称。
|
||||
|
||||
```
|
||||
# nmcli con down 'Wired connection 1'
|
||||
Connection 'Wired connection 1' successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/6)
|
||||
```
|
||||
|
||||
Yes, the given interface is down now as per the following output.
|
||||
从以下输出结果可以看到网卡已经禁用了。
|
||||
|
||||
```
|
||||
# nmcli dev status
|
||||
@ -236,14 +225,14 @@ enp0s3 ethernet disconnected --
|
||||
lo loopback unmanaged --
|
||||
```
|
||||
|
||||
Run the following command to bring down the `enp0s3` interface in Linux. You have to give `profile name` instead of `device name` to bring down it.
|
||||
执行以下命令启用 `enp0s3` 网卡。同样的,这里你需要使用配置名称而不是设备名称。
|
||||
|
||||
```
|
||||
# nmcli con up 'Wired connection 1'
|
||||
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/7)
|
||||
```
|
||||
|
||||
Yes, the given interface is up now as per the following output.
|
||||
从以下输出结果可以看到网卡已经启用了。
|
||||
|
||||
```
|
||||
# nmcli dev status
|
||||
@ -253,25 +242,27 @@ enp0s3 ethernet connected Wired connection 1
|
||||
lo loopback unmanaged --
|
||||
```
|
||||
|
||||
### 5) How To Bring UP/Bring Down A Network Interface In Linux Using nmtui Command?
|
||||
### 5、如何使用 nmtui 命令启用禁用网卡?
|
||||
|
||||
nmtui is a curses based TUI application for interacting with NetworkManager.
|
||||
`nmtui` 是一个与 NetworkManager 交互的、基于 curses 图形库的终端 UI 应用。
|
||||
|
||||
When starting nmtui, the user is prompted to choose the activity to perform unless it was specified as the first argument.
|
||||
在启用 `nmtui` 的时候,如果第一个参数没有特别指定,它会引导用户选择对应的操作去执行。
|
||||
|
||||
Run the following command launch the nmtui interface. Select “Active a connection” and hit “OK”
|
||||
执行以下命令打开 `mntui` 界面。选择 “Active a connection” 然后点击 “OK”。
|
||||
|
||||
```
|
||||
# nmtui
|
||||
```
|
||||
|
||||
[![][1]![][1]][2]
|
||||
![][2]
|
||||
|
||||
Select the interface which you want to bring down then hit “Deactivate” button.
|
||||
[![][1]![][1]][3]
|
||||
选择你要禁用的网卡,然后点击 “Deactivate” 按钮,就可以将网卡禁用。
|
||||
|
||||
For activation do the same above procedure.
|
||||
[![][1]![][1]][4]
|
||||
![][3]
|
||||
|
||||
如果要启用网卡,使用上述同样的步骤即可。
|
||||
|
||||
![][4]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -279,8 +270,8 @@ via: https://www.2daygeek.com/enable-disable-up-down-nic-network-interface-port-
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
译者:[bodhix](https://github.com/bodhix)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,439 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (FSSlc)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10826-1.html)
|
||||
[#]: subject: (Inter-process communication in Linux: Shared storage)
|
||||
[#]: via: (https://opensource.com/article/19/4/interprocess-communication-linux-storage)
|
||||
[#]: author: (Marty Kalin https://opensource.com/users/mkalindepauledu)
|
||||
|
||||
Linux 下的进程间通信:共享存储
|
||||
======
|
||||
|
||||
> 学习在 Linux 中进程是如何与其他进程进行同步的。
|
||||
|
||||
![Filing papers and documents][1]
|
||||
|
||||
本篇是 Linux 下[进程间通信][2](IPC)系列的第一篇文章。这个系列将使用 C 语言代码示例来阐明以下 IPC 机制:
|
||||
|
||||
* 共享文件
|
||||
* 共享内存(使用信号量)
|
||||
* 管道(命名的或非命名的管道)
|
||||
* 消息队列
|
||||
* 套接字
|
||||
* 信号
|
||||
|
||||
在聚焦上面提到的共享文件和共享内存这两个机制之前,这篇文章将带你回顾一些核心的概念。
|
||||
|
||||
### 核心概念
|
||||
|
||||
*进程*是运行着的程序,每个进程都有着它自己的地址空间,这些空间由进程被允许访问的内存地址组成。进程有一个或多个执行*线程*,而线程是一系列执行指令的集合:*单线程*进程就只有一个线程,而*多线程*的进程则有多个线程。一个进程中的线程共享各种资源,特别是地址空间。另外,一个进程中的线程可以直接通过共享内存来进行通信,尽管某些现代语言(例如 Go)鼓励一种更有序的方式,例如使用线程安全的通道。当然对于不同的进程,默认情况下,它们**不**能共享内存。
|
||||
|
||||
有多种方法启动之后要进行通信的进程,下面所举的例子中主要使用了下面的两种方法:
|
||||
|
||||
* 一个终端被用来启动一个进程,另外一个不同的终端被用来启动另一个。
|
||||
* 在一个进程(父进程)中调用系统函数 `fork`,以此生发另一个进程(子进程)。
|
||||
|
||||
第一个例子采用了上面使用终端的方法。这些[代码示例][3]的 ZIP 压缩包可以从我的网站下载到。
|
||||
|
||||
### 共享文件
|
||||
|
||||
程序员对文件访问应该都已经很熟识了,包括许多坑(不存在的文件、文件权限损坏等等),这些问题困扰着程序对文件的使用。尽管如此,共享文件可能是最为基础的 IPC 机制了。考虑一下下面这样一个相对简单的例子,其中一个进程(生产者 `producer`)创建和写入一个文件,然后另一个进程(消费者 `consumer `)从这个相同的文件中进行读取:
|
||||
|
||||
```
|
||||
writes +-----------+ reads
|
||||
producer-------->| disk file |<-------consumer
|
||||
+-----------+
|
||||
```
|
||||
|
||||
在使用这个 IPC 机制时最明显的挑战是*竞争条件*可能会发生:生产者和消费者可能恰好在同一时间访问该文件,从而使得输出结果不确定。为了避免竞争条件的发生,该文件在处于*读*或*写*状态时必须以某种方式处于被锁状态,从而阻止在*写*操作执行时和其他操作的冲突。在标准系统库中与锁相关的 API 可以被总结如下:
|
||||
|
||||
* 生产者应该在写入文件时获得一个文件的排斥锁。一个*排斥*锁最多被一个进程所拥有。这样就可以排除掉竞争条件的发生,因为在锁被释放之前没有其他的进程可以访问这个文件。
|
||||
* 消费者应该在从文件中读取内容时得到至少一个共享锁。多个*读取者*可以同时保有一个*共享*锁,但是没有*写入者*可以获取到文件内容,甚至在当只有一个*读取者*保有一个共享锁时。
|
||||
|
||||
共享锁可以提升效率。假如一个进程只是读入一个文件的内容,而不去改变它的内容,就没有什么原因阻止其他进程来做同样的事。但如果需要写入内容,则很显然需要文件有排斥锁。
|
||||
|
||||
标准的 I/O 库中包含一个名为 `fcntl` 的实用函数,它可以被用来检查或者操作一个文件上的排斥锁和共享锁。该函数通过一个*文件描述符*(一个在进程中的非负整数值)来标记一个文件(在不同的进程中不同的文件描述符可能标记同一个物理文件)。对于文件的锁定, Linux 提供了名为 `flock` 的库函数,它是 `fcntl` 的一个精简包装。第一个例子中使用 `fcntl` 函数来暴露这些 API 细节。
|
||||
|
||||
#### 示例 1. 生产者程序
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#define FileName "data.dat"
|
||||
#define DataString "Now is the winter of our discontent\nMade glorious summer by this sun of York\n"
|
||||
|
||||
void report_and_exit(const char* msg) {
|
||||
perror(msg);
|
||||
exit(-1); /* EXIT_FAILURE */
|
||||
}
|
||||
|
||||
int main() {
|
||||
struct flock lock;
|
||||
lock.l_type = F_WRLCK; /* read/write (exclusive versus shared) lock */
|
||||
lock.l_whence = SEEK_SET; /* base for seek offsets */
|
||||
lock.l_start = 0; /* 1st byte in file */
|
||||
lock.l_len = 0; /* 0 here means 'until EOF' */
|
||||
lock.l_pid = getpid(); /* process id */
|
||||
|
||||
int fd; /* file descriptor to identify a file within a process */
|
||||
if ((fd = open(FileName, O_RDWR | O_CREAT, 0666)) < 0) /* -1 signals an error */
|
||||
report_and_exit("open failed...");
|
||||
|
||||
if (fcntl(fd, F_SETLK, &lock) < 0) /** F_SETLK doesn't block, F_SETLKW does **/
|
||||
report_and_exit("fcntl failed to get lock...");
|
||||
else {
|
||||
write(fd, DataString, strlen(DataString)); /* populate data file */
|
||||
fprintf(stderr, "Process %d has written to data file...\n", lock.l_pid);
|
||||
}
|
||||
|
||||
/* Now release the lock explicitly. */
|
||||
lock.l_type = F_UNLCK;
|
||||
if (fcntl(fd, F_SETLK, &lock) < 0)
|
||||
report_and_exit("explicit unlocking failed...");
|
||||
|
||||
close(fd); /* close the file: would unlock if needed */
|
||||
return 0; /* terminating the process would unlock as well */
|
||||
}
|
||||
```
|
||||
|
||||
上面生产者程序的主要步骤可以总结如下:
|
||||
|
||||
* 这个程序首先声明了一个类型为 `struct flock` 的变量,它代表一个锁,并对它的 5 个域做了初始化。第一个初始化
|
||||
|
||||
```c
|
||||
lock.l_type = F_WRLCK; /* exclusive lock */
|
||||
```
|
||||
使得这个锁为排斥锁(read-write)而不是一个共享锁(read-only)。假如生产者获得了这个锁,则其他的进程将不能够对文件做读或者写操作,直到生产者释放了这个锁,或者显式地调用 `fcntl`,又或者隐式地关闭这个文件。(当进程终止时,所有被它打开的文件都会被自动关闭,从而释放了锁)
|
||||
* 上面的程序接着初始化其他的域。主要的效果是*整个*文件都将被锁上。但是,有关锁的 API 允许特别指定的字节被上锁。例如,假如文件包含多个文本记录,则单个记录(或者甚至一个记录的一部分)可以被锁,而其余部分不被锁。
|
||||
* 第一次调用 `fcntl`
|
||||
|
||||
```c
|
||||
if (fcntl(fd, F_SETLK, &lock) < 0)
|
||||
```
|
||||
尝试排斥性地将文件锁住,并检查调用是否成功。一般来说, `fcntl` 函数返回 `-1` (因此小于 0)意味着失败。第二个参数 `F_SETLK` 意味着 `fcntl` 的调用*不是*堵塞的;函数立即做返回,要么获得锁,要么显示失败了。假如替换地使用 `F_SETLKW`(末尾的 `W` 代指*等待*),那么对 `fcntl` 的调用将是阻塞的,直到有可能获得锁的时候。在调用 `fcntl` 函数时,它的第一个参数 `fd` 指的是文件描述符,第二个参数指定了将要采取的动作(在这个例子中,`F_SETLK` 指代设置锁),第三个参数为锁结构的地址(在本例中,指的是 `&lock`)。
|
||||
* 假如生产者获得了锁,这个程序将向文件写入两个文本记录。
|
||||
* 在向文件写入内容后,生产者改变锁结构中的 `l_type` 域为 `unlock` 值:
|
||||
|
||||
```c
|
||||
lock.l_type = F_UNLCK;
|
||||
```
|
||||
并调用 `fcntl` 来执行解锁操作。最后程序关闭了文件并退出。
|
||||
|
||||
#### 示例 2. 消费者程序
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define FileName "data.dat"
|
||||
|
||||
void report_and_exit(const char* msg) {
|
||||
perror(msg);
|
||||
exit(-1); /* EXIT_FAILURE */
|
||||
}
|
||||
|
||||
int main() {
|
||||
struct flock lock;
|
||||
lock.l_type = F_WRLCK; /* read/write (exclusive) lock */
|
||||
lock.l_whence = SEEK_SET; /* base for seek offsets */
|
||||
lock.l_start = 0; /* 1st byte in file */
|
||||
lock.l_len = 0; /* 0 here means 'until EOF' */
|
||||
lock.l_pid = getpid(); /* process id */
|
||||
|
||||
int fd; /* file descriptor to identify a file within a process */
|
||||
if ((fd = open(FileName, O_RDONLY)) < 0) /* -1 signals an error */
|
||||
report_and_exit("open to read failed...");
|
||||
|
||||
/* If the file is write-locked, we can't continue. */
|
||||
fcntl(fd, F_GETLK, &lock); /* sets lock.l_type to F_UNLCK if no write lock */
|
||||
if (lock.l_type != F_UNLCK)
|
||||
report_and_exit("file is still write locked...");
|
||||
|
||||
lock.l_type = F_RDLCK; /* prevents any writing during the reading */
|
||||
if (fcntl(fd, F_SETLK, &lock) < 0)
|
||||
report_and_exit("can't get a read-only lock...");
|
||||
|
||||
/* Read the bytes (they happen to be ASCII codes) one at a time. */
|
||||
int c; /* buffer for read bytes */
|
||||
while (read(fd, &c, 1) > 0) /* 0 signals EOF */
|
||||
write(STDOUT_FILENO, &c, 1); /* write one byte to the standard output */
|
||||
|
||||
/* Release the lock explicitly. */
|
||||
lock.l_type = F_UNLCK;
|
||||
if (fcntl(fd, F_SETLK, &lock) < 0)
|
||||
report_and_exit("explicit unlocking failed...");
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
相比于锁的 API,消费者程序会相对复杂一点儿。特别的,消费者程序首先检查文件是否被排斥性的被锁,然后才尝试去获得一个共享锁。相关的代码为:
|
||||
|
||||
```
|
||||
lock.l_type = F_WRLCK;
|
||||
...
|
||||
fcntl(fd, F_GETLK, &lock); /* sets lock.l_type to F_UNLCK if no write lock */
|
||||
if (lock.l_type != F_UNLCK)
|
||||
report_and_exit("file is still write locked...");
|
||||
```
|
||||
|
||||
在 `fcntl` 调用中的 `F_GETLK` 操作指定检查一个锁,在本例中,上面代码的声明中给了一个 `F_WRLCK` 的排斥锁。假如特指的锁不存在,那么 `fcntl` 调用将会自动地改变锁类型域为 `F_UNLCK` 以此来显示当前的状态。假如文件是排斥性地被锁,那么消费者将会终止。(一个更健壮的程序版本或许应该让消费者*睡*会儿,然后再尝试几次。)
|
||||
|
||||
假如当前文件没有被锁,那么消费者将尝试获取一个共享(read-only)锁(`F_RDLCK`)。为了缩短程序,`fcntl` 中的 `F_GETLK` 调用可以丢弃,因为假如其他进程已经保有一个读写锁,`F_RDLCK` 的调用就可能会失败。重新调用一个只读锁能够阻止其他进程向文件进行写的操作,但可以允许其他进程对文件进行读取。简而言之,共享锁可以被多个进程所保有。在获取了一个共享锁后,消费者程序将立即从文件中读取字节数据,然后在标准输出中打印这些字节的内容,接着释放锁,关闭文件并终止。
|
||||
|
||||
下面的 `%` 为命令行提示符,下面展示的是从相同终端开启这两个程序的输出:
|
||||
|
||||
```
|
||||
% ./producer
|
||||
Process 29255 has written to data file...
|
||||
|
||||
% ./consumer
|
||||
Now is the winter of our discontent
|
||||
Made glorious summer by this sun of York
|
||||
```
|
||||
|
||||
在本次的代码示例中,通过 IPC 传输的数据是文本:它们来自莎士比亚的戏剧《理查三世》中的两行台词。然而,共享文件的内容还可以是纷繁复杂的,任意的字节数据(例如一个电影)都可以,这使得文件共享变成了一个非常灵活的 IPC 机制。但它的缺点是文件获取速度较慢,因为文件的获取涉及到读或者写。同往常一样,编程总是伴随着折中。下面的例子将通过共享内存来做 IPC,而不是通过共享文件,在性能上相应的有极大的提升。
|
||||
|
||||
### 共享内存
|
||||
|
||||
对于共享内存,Linux 系统提供了两类不同的 API:传统的 System V API 和更新一点的 POSIX API。在单个应用中,这些 API 不能混用。但是,POSIX 方式的一个坏处是它的特性仍在发展中,并且依赖于安装的内核版本,这非常影响代码的可移植性。例如,默认情况下,POSIX API 用*内存映射文件*来实现共享内存:对于一个共享的内存段,系统为相应的内容维护一个*备份文件*。在 POSIX 规范下共享内存可以被配置为不需要备份文件,但这可能会影响可移植性。我的例子中使用的是带有备份文件的 POSIX API,这既结合了内存获取的速度优势,又获得了文件存储的持久性。
|
||||
|
||||
下面的共享内存例子中包含两个程序,分别名为 `memwriter` 和 `memreader`,并使用*信号量*来调整它们对共享内存的获取。在任何时候当共享内存进入一个*写入者*场景时,无论是多进程还是多线程,都有遇到基于内存的竞争条件的风险,所以,需要引入信号量来协调(同步)对共享内存的获取。
|
||||
|
||||
`memwriter` 程序应当在它自己所处的终端首先启动,然后 `memreader` 程序才可以在它自己所处的终端启动(在接着的十几秒内)。`memreader` 的输出如下:
|
||||
|
||||
```
|
||||
This is the way the world ends...
|
||||
```
|
||||
|
||||
在每个源程序的最上方注释部分都解释了在编译它们时需要添加的链接参数。
|
||||
|
||||
首先让我们复习一下信号量是如何作为一个同步机制工作的。一般的信号量也被叫做一个*计数信号量*,因为带有一个可以增加的值(通常初始化为 0)。考虑一家租用自行车的商店,在它的库存中有 100 辆自行车,还有一个供职员用于租赁的程序。每当一辆自行车被租出去,信号量就增加 1;当一辆自行车被还回来,信号量就减 1。在信号量的值为 100 之前都还可以进行租赁业务,但如果等于 100 时,就必须停止业务,直到至少有一辆自行车被还回来,从而信号量减为 99。
|
||||
|
||||
*二元信号量*是一个特例,它只有两个值:0 和 1。在这种情况下,信号量的表现为*互斥量*(一个互斥的构造)。下面的共享内存示例将把信号量用作互斥量。当信号量的值为 0 时,只有 `memwriter` 可以获取共享内存,在写操作完成后,这个进程将增加信号量的值,从而允许 `memreader` 来读取共享内存。
|
||||
|
||||
#### 示例 3. memwriter 进程的源程序
|
||||
|
||||
```c
|
||||
/** Compilation: gcc -o memwriter memwriter.c -lrt -lpthread **/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <semaphore.h>
|
||||
#include <string.h>
|
||||
#include "shmem.h"
|
||||
|
||||
void report_and_exit(const char* msg) {
|
||||
perror(msg);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
int fd = shm_open(BackingFile, /* name from smem.h */
|
||||
O_RDWR | O_CREAT, /* read/write, create if needed */
|
||||
AccessPerms); /* access permissions (0644) */
|
||||
if (fd < 0) report_and_exit("Can't open shared mem segment...");
|
||||
|
||||
ftruncate(fd, ByteSize); /* get the bytes */
|
||||
|
||||
caddr_t memptr = mmap(NULL, /* let system pick where to put segment */
|
||||
ByteSize, /* how many bytes */
|
||||
PROT_READ | PROT_WRITE, /* access protections */
|
||||
MAP_SHARED, /* mapping visible to other processes */
|
||||
fd, /* file descriptor */
|
||||
0); /* offset: start at 1st byte */
|
||||
if ((caddr_t) -1 == memptr) report_and_exit("Can't get segment...");
|
||||
|
||||
fprintf(stderr, "shared mem address: %p [0..%d]\n", memptr, ByteSize - 1);
|
||||
fprintf(stderr, "backing file: /dev/shm%s\n", BackingFile );
|
||||
|
||||
/* semaphore code to lock the shared mem */
|
||||
sem_t* semptr = sem_open(SemaphoreName, /* name */
|
||||
O_CREAT, /* create the semaphore */
|
||||
AccessPerms, /* protection perms */
|
||||
0); /* initial value */
|
||||
if (semptr == (void*) -1) report_and_exit("sem_open");
|
||||
|
||||
strcpy(memptr, MemContents); /* copy some ASCII bytes to the segment */
|
||||
|
||||
/* increment the semaphore so that memreader can read */
|
||||
if (sem_post(semptr) < 0) report_and_exit("sem_post");
|
||||
|
||||
sleep(12); /* give reader a chance */
|
||||
|
||||
/* clean up */
|
||||
munmap(memptr, ByteSize); /* unmap the storage */
|
||||
close(fd);
|
||||
sem_close(semptr);
|
||||
shm_unlink(BackingFile); /* unlink from the backing file */
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
下面是 `memwriter` 和 `memreader` 程序如何通过共享内存来通信的一个总结:
|
||||
|
||||
* 上面展示的 `memwriter` 程序调用 `shm_open` 函数来得到作为系统协调共享内存的备份文件的文件描述符。此时,并没有内存被分配。接下来调用的是令人误解的名为 `ftruncate` 的函数
|
||||
|
||||
```c
|
||||
ftruncate(fd, ByteSize); /* get the bytes */
|
||||
```
|
||||
它将分配 `ByteSize` 字节的内存,在该情况下,一般为大小适中的 512 字节。`memwriter` 和 `memreader` 程序都只从共享内存中获取数据,而不是从备份文件。系统将负责共享内存和备份文件之间数据的同步。
|
||||
* 接着 `memwriter` 调用 `mmap` 函数:
|
||||
|
||||
```c
|
||||
caddr_t memptr = mmap(NULL, /* let system pick where to put segment */
|
||||
ByteSize, /* how many bytes */
|
||||
PROT_READ | PROT_WRITE, /* access protections */
|
||||
MAP_SHARED, /* mapping visible to other processes */
|
||||
fd, /* file descriptor */
|
||||
0); /* offset: start at 1st byte */
|
||||
```
|
||||
来获得共享内存的指针。(`memreader` 也做一次类似的调用。) 指针类型 `caddr_t` 以 `c` 开头,它代表 `calloc`,而这是动态初始化分配的内存为 0 的一个系统函数。`memwriter` 通过库函数 `strcpy`(字符串复制)来获取后续*写*操作的 `memptr`。
|
||||
* 到现在为止,`memwriter` 已经准备好进行写操作了,但首先它要创建一个信号量来确保共享内存的排斥性。假如 `memwriter` 正在执行写操作而同时 `memreader` 在执行读操作,则有可能出现竞争条件。假如调用 `sem_open` 成功了:
|
||||
|
||||
```c
|
||||
sem_t* semptr = sem_open(SemaphoreName, /* name */
|
||||
O_CREAT, /* create the semaphore */
|
||||
AccessPerms, /* protection perms */
|
||||
0); /* initial value */
|
||||
```
|
||||
那么,接着写操作便可以执行。上面的 `SemaphoreName`(任意一个唯一的非空名称)用来在 `memwriter` 和 `memreader` 识别信号量。初始值 0 将会传递给信号量的创建者,在这个例子中指的是 `memwriter` 赋予它执行*写*操作的权利。
|
||||
* 在写操作完成后,`memwriter* 通过调用 `sem_post` 函数将信号量的值增加到 1:
|
||||
|
||||
```c
|
||||
if (sem_post(semptr) < 0) ..
|
||||
```
|
||||
增加信号了将释放互斥锁,使得 `memreader` 可以执行它的*读*操作。为了更好地测量,`memwriter` 也将从它自己的地址空间中取消映射,
|
||||
|
||||
```c
|
||||
munmap(memptr, ByteSize); /* unmap the storage *
|
||||
```
|
||||
这将使得 `memwriter` 不能进一步地访问共享内存。
|
||||
|
||||
#### 示例 4. memreader 进程的源代码
|
||||
|
||||
```c
|
||||
/** Compilation: gcc -o memreader memreader.c -lrt -lpthread **/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <semaphore.h>
|
||||
#include <string.h>
|
||||
#include "shmem.h"
|
||||
|
||||
void report_and_exit(const char* msg) {
|
||||
perror(msg);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
int fd = shm_open(BackingFile, O_RDWR, AccessPerms); /* empty to begin */
|
||||
if (fd < 0) report_and_exit("Can't get file descriptor...");
|
||||
|
||||
/* get a pointer to memory */
|
||||
caddr_t memptr = mmap(NULL, /* let system pick where to put segment */
|
||||
ByteSize, /* how many bytes */
|
||||
PROT_READ | PROT_WRITE, /* access protections */
|
||||
MAP_SHARED, /* mapping visible to other processes */
|
||||
fd, /* file descriptor */
|
||||
0); /* offset: start at 1st byte */
|
||||
if ((caddr_t) -1 == memptr) report_and_exit("Can't access segment...");
|
||||
|
||||
/* create a semaphore for mutual exclusion */
|
||||
sem_t* semptr = sem_open(SemaphoreName, /* name */
|
||||
O_CREAT, /* create the semaphore */
|
||||
AccessPerms, /* protection perms */
|
||||
0); /* initial value */
|
||||
if (semptr == (void*) -1) report_and_exit("sem_open");
|
||||
|
||||
/* use semaphore as a mutex (lock) by waiting for writer to increment it */
|
||||
if (!sem_wait(semptr)) { /* wait until semaphore != 0 */
|
||||
int i;
|
||||
for (i = 0; i < strlen(MemContents); i++)
|
||||
write(STDOUT_FILENO, memptr + i, 1); /* one byte at a time */
|
||||
sem_post(semptr);
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
munmap(memptr, ByteSize);
|
||||
close(fd);
|
||||
sem_close(semptr);
|
||||
unlink(BackingFile);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
`memwriter` 和 `memreader` 程序中,共享内存的主要着重点都在 `shm_open` 和 `mmap` 函数上:在成功时,第一个调用返回一个备份文件的文件描述符,而第二个调用则使用这个文件描述符从共享内存段中获取一个指针。它们对 `shm_open` 的调用都很相似,除了 `memwriter` 程序创建共享内存,而 `memreader 只获取这个已经创建的内存:
|
||||
|
||||
```c
|
||||
int fd = shm_open(BackingFile, O_RDWR | O_CREAT, AccessPerms); /* memwriter */
|
||||
int fd = shm_open(BackingFile, O_RDWR, AccessPerms); /* memreader */
|
||||
```
|
||||
|
||||
有了文件描述符,接着对 `mmap` 的调用就是类似的了:
|
||||
|
||||
```c
|
||||
caddr_t memptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
```
|
||||
|
||||
`mmap` 的第一个参数为 `NULL`,这意味着让系统自己决定在虚拟内存地址的哪个地方分配内存,当然也可以指定一个地址(但很有技巧性)。`MAP_SHARED` 标志着被分配的内存在进程中是共享的,最后一个参数(在这个例子中为 0 ) 意味着共享内存的偏移量应该为第一个字节。`size` 参数特别指定了将要分配的字节数目(在这个例子中是 512);另外的保护参数(`AccessPerms`)暗示着共享内存是可读可写的。
|
||||
|
||||
当 `memwriter` 程序执行成功后,系统将创建并维护备份文件,在我的系统中,该文件为 `/dev/shm/shMemEx`,其中的 `shMemEx` 是我为共享存储命名的(在头文件 `shmem.h` 中给定)。在当前版本的 `memwriter` 和 `memreader` 程序中,下面的语句
|
||||
|
||||
```c
|
||||
shm_unlink(BackingFile); /* removes backing file */
|
||||
```
|
||||
|
||||
将会移除备份文件。假如没有 `unlink` 这个语句,则备份文件在程序终止后仍然持久地保存着。
|
||||
|
||||
`memreader` 和 `memwriter` 一样,在调用 `sem_open` 函数时,通过信号量的名字来获取信号量。但 `memreader` 随后将进入等待状态,直到 `memwriter` 将初始值为 0 的信号量的值增加。
|
||||
|
||||
```c
|
||||
if (!sem_wait(semptr)) { /* wait until semaphore != 0 */
|
||||
```
|
||||
|
||||
一旦等待结束,`memreader` 将从共享内存中读取 ASCII 数据,然后做些清理工作并终止。
|
||||
|
||||
共享内存 API 包括显式地同步共享内存段和备份文件。在这次的示例中,这些操作都被省略了,以免文章显得杂乱,好让我们专注于内存共享和信号量的代码。
|
||||
|
||||
即便在信号量代码被移除的情况下,`memwriter` 和 `memreader` 程序很大几率也能够正常执行而不会引入竞争条件:`memwriter` 创建了共享内存段,然后立即向它写入;`memreader` 不能访问共享内存,直到共享内存段被创建好。然而,当一个*写操作*处于混合状态时,最佳实践需要共享内存被同步。信号量 API 足够重要,值得在代码示例中着重强调。
|
||||
|
||||
### 总结
|
||||
|
||||
上面共享文件和共享内存的例子展示了进程是怎样通过*共享存储*来进行通信的,前者通过文件而后者通过内存块。这两种方法的 API 相对来说都很直接。这两种方法有什么共同的缺点吗?现代的应用经常需要处理流数据,而且是非常大规模的数据流。共享文件或者共享内存的方法都不能很好地处理大规模的流数据。按照类型使用管道会更加合适一些。所以这个系列的第二部分将会介绍管道和消息队列,同样的,我们将使用 C 语言写的代码示例来辅助讲解。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/interprocess-communication-linux-storage
|
||||
|
||||
作者:[Marty Kalin][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[FSSlc](https://github.com/FSSlc)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/mkalindepauledu
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/documents_papers_file_storage_work.png?itok=YlXpAqAJ (Filing papers and documents)
|
||||
[2]: https://en.wikipedia.org/wiki/Inter-process_communication
|
||||
[3]: http://condor.depaul.edu/mkalin
|
||||
[4]: http://www.opengroup.org/onlinepubs/009695399/functions/perror.html
|
||||
[5]: http://www.opengroup.org/onlinepubs/009695399/functions/exit.html
|
||||
[6]: http://www.opengroup.org/onlinepubs/009695399/functions/strlen.html
|
||||
[7]: http://www.opengroup.org/onlinepubs/009695399/functions/fprintf.html
|
||||
[8]: http://www.opengroup.org/onlinepubs/009695399/functions/strcpy.html
|
209
published/201905/20190415 Kubernetes on Fedora IoT with k3s.md
Normal file
209
published/201905/20190415 Kubernetes on Fedora IoT with k3s.md
Normal file
@ -0,0 +1,209 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (StdioA)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10908-1.html)
|
||||
[#]: subject: (Kubernetes on Fedora IoT with k3s)
|
||||
[#]: via: (https://fedoramagazine.org/kubernetes-on-fedora-iot-with-k3s/)
|
||||
[#]: author: (Lennart Jern https://fedoramagazine.org/author/lennartj/)
|
||||
|
||||
使用 k3s 在 Fedora IoT 上运行 K8S
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201905/28/094048yrzlik9oek5rbs5s.jpg)
|
||||
|
||||
Fedora IoT 是一个即将发布的、面向物联网的 Fedora 版本。去年 Fedora Magazine 的《[如何使用 Fedora IoT 点亮 LED 灯][2]》一文第一次介绍了它。从那以后,它与 Fedora Silverblue 一起不断改进,以提供针对面向容器的工作流的不可变基础操作系统。
|
||||
|
||||
Kubernetes 是一个颇受欢迎的容器编排系统。它可能最常用在那些能够处理巨大负载的强劲硬件上。不过,它也能在像树莓派 3 这样轻量级的设备上运行。让我们继续阅读,来了解如何运行它。
|
||||
|
||||
### 为什么用 Kubernetes?
|
||||
|
||||
虽然 Kubernetes 在云计算领域风靡一时,但让它在小型单板机上运行可能并不是常见的。不过,我们有非常明确的理由来做这件事。首先,这是一个不需要昂贵硬件就可以学习并熟悉 Kubernetes 的好方法;其次,由于它的流行性,市面上有[大量应用][3]进行了预先打包,以用于在 Kubernetes 集群中运行。更不用说,当你遇到问题时,会有大规模的社区用户为你提供帮助。
|
||||
|
||||
最后但同样重要的是,即使是在家庭实验室这样的小规模环境中,容器编排也确实能够使事情变得更加简单。虽然在学习曲线方面,这一点并不明显,但这些技能在你将来与任何集群打交道的时候都会有帮助。不管你面对的是一个单节点树莓派集群,还是一个大规模的机器学习场,它们的操作方式都是类似的。
|
||||
|
||||
#### K3s - 轻量级的 Kubernetes
|
||||
|
||||
一个“正常”安装的 Kubernetes(如果有这么一说的话)对于物联网来说有点沉重。K8s 的推荐内存配置,是每台机器 2GB!不过,我们也有一些替代品,其中一个新人是 [k3s][4] —— 一个轻量级的 Kubernetes 发行版。
|
||||
|
||||
K3s 非常特殊,因为它将 etcd 替换成了 SQLite 以满足键值存储需求。还有一点,在于整个 k3s 将使用一个二进制文件分发,而不是每个组件一个。这减少了内存占用并简化了安装过程。基于上述原因,我们只需要 512MB 内存即可运行 k3s,极度适合小型单板电脑!
|
||||
|
||||
### 你需要的东西
|
||||
|
||||
1. Fedora IoT 运行在虚拟机或实体设备中运行的。在[这里][5]可以看到优秀的入门指南。一台机器就足够了,不过两台可以用来测试向集群添加更多节点。
|
||||
2. [配置防火墙][6],允许 6443 和 8372 端口的通信。或者,你也可以简单地运行 `systemctl stop firewalld` 来为这次实验关闭防火墙。
|
||||
|
||||
### 安装 k3s
|
||||
|
||||
安装 k3s 非常简单。直接运行安装脚本:
|
||||
|
||||
```
|
||||
curl -sfL https://get.k3s.io | sh -
|
||||
```
|
||||
|
||||
它会下载、安装并启动 k3s。安装完成后,运行以下命令来从服务器获取节点列表:
|
||||
|
||||
```
|
||||
kubectl get nodes
|
||||
```
|
||||
|
||||
需要注意的是,有几个选项可以通过环境变量传递给安装脚本。这些选项可以在[文档][7]中找到。当然,你也完全可以直接下载二进制文件来手动安装 k3s。
|
||||
|
||||
对于实验和学习来说,这样已经很棒了,不过单节点的集群也不能算一个集群。幸运的是,添加另一个节点并不比设置第一个节点要难。只需要向安装脚本传递两个环境变量,它就可以找到第一个节点,而不用运行 k3s 的服务器部分。
|
||||
|
||||
```
|
||||
curl -sfL https://get.k3s.io | K3S_URL=https://example-url:6443 \
|
||||
K3S_TOKEN=XXX sh -
|
||||
```
|
||||
|
||||
上面的 `example-url` 应被替换为第一个节点的 IP 地址,或一个完全限定域名。在该节点中,(用 XXX 表示的)令牌可以在 `/var/lib/rancher/k3s/server/node-token` 文件中找到。
|
||||
|
||||
### 部署一些容器
|
||||
|
||||
现在我们有了一个 Kubernetes 集群,我们可以真正做些什么呢?让我们从部署一个简单的 Web 服务器开始吧。
|
||||
|
||||
```
|
||||
kubectl create deployment my-server --image nginx
|
||||
```
|
||||
|
||||
这会从名为 `nginx` 的容器镜像中创建出一个名叫 `my-server` 的 [部署][8](默认使用 docker hub 注册中心,以及 `latest` 标签)。
|
||||
|
||||
```
|
||||
kubectl get pods
|
||||
```
|
||||
|
||||
为了访问到 pod 中运行的 nginx 服务器,首先通过一个 [服务][9] 来暴露该部署。以下命令将创建一个与该部署同名的服务。
|
||||
|
||||
```
|
||||
kubectl expose deployment my-server --port 80
|
||||
```
|
||||
|
||||
服务将作为一种负载均衡器和 Pod 的 DNS 记录来工作。比如,当运行第二个 Pod 时,我们只需指定 `my-server`(服务名称)就可以通过 `curl` 访问 nginx 服务器。有关如何操作,可以看下面的实例。
|
||||
|
||||
```
|
||||
# 启动一个 pod,在里面以交互方式运行 bash
|
||||
kubectl run debug --generator=run-pod/v1 --image=fedora -it -- bash
|
||||
# 等待 bash 提示符出现
|
||||
curl my-server
|
||||
# 你可以看到“Welcome to nginx!”的输出页面
|
||||
```
|
||||
|
||||
### Ingress 控制器及外部 IP
|
||||
|
||||
默认状态下,一个服务只能获得一个 ClusterIP(只能从集群内部访问),但你也可以通过把它的类型设置为 [LoadBalancer][10] 为该服务申请一个外部 IP。不过,并非所有应用都需要自己的 IP 地址。相反,通常可以通过基于 Host 请求头部或请求路径进行路由,从而使多个服务共享一个 IP 地址。你可以在 Kubernetes 使用 [Ingress][11] 完成此操作,而这也是我们要做的。Ingress 也提供了额外的功能,比如无需配置应用即可对流量进行 TLS 加密。
|
||||
|
||||
Kubernetes 需要 Ingress 控制器来使 Ingress 资源工作,k3s 包含 [Traefik][12] 正是出于此目的。它还包含了一个简单的服务负载均衡器,可以为集群中的服务提供外部 IP。这篇[文档][13]描述了这种服务:
|
||||
|
||||
> k3s 包含一个使用可用主机端口的基础服务负载均衡器。比如,如果你尝试创建一个监听 80 端口的负载均衡器,它会尝试在集群中寻找一个 80 端口空闲的节点。如果没有可用端口,那么负载均衡器将保持在 Pending 状态。
|
||||
>
|
||||
> k3s README
|
||||
|
||||
Ingress 控制器已经通过这个负载均衡器暴露在外。你可以使用以下命令找到它正在使用的 IP 地址。
|
||||
|
||||
```
|
||||
$ kubectl get svc --all-namespaces
|
||||
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
default kubernetes ClusterIP 10.43.0.1 443/TCP 33d
|
||||
default my-server ClusterIP 10.43.174.38 80/TCP 30m
|
||||
kube-system kube-dns ClusterIP 10.43.0.10 53/UDP,53/TCP,9153/TCP 33d
|
||||
kube-system traefik LoadBalancer 10.43.145.104 10.0.0.8 80:31596/TCP,443:31539/TCP 33d
|
||||
```
|
||||
|
||||
找到名为 `traefik` 的服务。在上面的例子中,我们感兴趣的 IP 是 10.0.0.8。
|
||||
|
||||
### 路由传入的请求
|
||||
|
||||
让我们创建一个 Ingress,使它通过基于 Host 头部的路由规则将请求路由至我们的服务器。这个例子中我们使用 [xip.io][14] 来避免必要的 DNS 记录配置工作。它的工作原理是将 IP 地址作为子域包含,以使用 `10.0.0.8.xip.io` 的任何子域来达到 IP `10.0.0.8`。换句话说,`my-server.10.0.0.8.xip.io` 被用于访问集群中的 Ingress 控制器。你现在就可以尝试(使用你自己的 IP,而不是 10.0.0.8)。如果没有 Ingress,你应该会访问到“默认后端”,只是一个写着“404 page not found”的页面。
|
||||
|
||||
我们可以使用以下 Ingress 让 Ingress 控制器将请求路由到我们的 Web 服务器的服务。
|
||||
|
||||
```
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: my-server
|
||||
spec:
|
||||
rules:
|
||||
- host: my-server.10.0.0.8.xip.io
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: my-server
|
||||
servicePort: 80
|
||||
```
|
||||
|
||||
将以上片段保存到 `my-ingress.yaml` 文件中,然后运行以下命令将其加入集群:
|
||||
|
||||
```
|
||||
kubectl apply -f my-ingress.yaml
|
||||
```
|
||||
|
||||
你现在应该能够在你选择的完全限定域名中访问到 nginx 的默认欢迎页面了。在我的例子中,它是 `my-server.10.0.0.8.xip.io`。Ingress 控制器会通过 Ingress 中包含的信息来路由请求。对 `my-server.10.0.0.8.xip.io` 的请求将被路由到 Ingress 中定义为 `backend` 的服务和端口(在本例中为 `my-server` 和 `80`)。
|
||||
|
||||
### 那么,物联网呢?
|
||||
|
||||
想象如下场景:你的家或农场周围有很多的设备。它是一个具有各种硬件功能、传感器和执行器的物联网设备的异构集合。也许某些设备拥有摄像头、天气或光线传感器。其它设备可能会被连接起来,用来控制通风、灯光、百叶窗或闪烁的 LED。
|
||||
|
||||
这种情况下,你想从所有传感器中收集数据,在最终使用它来制定决策和控制执行器之前,也可能会对其进行处理和分析。除此之外,你可能还想配置一个仪表盘来可视化那些正在发生的事情。那么 Kubernetes 如何帮助我们来管理这样的事情呢?我们怎么保证 Pod 在合适的设备上运行?
|
||||
|
||||
简单的答案就是“标签”。你可以根据功能来标记节点,如下所示:
|
||||
|
||||
```
|
||||
kubectl label nodes <node-name> <label-key>=<label-value>
|
||||
# 举例
|
||||
kubectl label nodes node2 camera=available
|
||||
```
|
||||
|
||||
一旦它们被打上标签,我们就可以轻松地使用 [nodeSelector][15] 为你的工作负载选择合适的节点。拼图的最后一块:如果你想在*所有*合适的节点上运行 Pod,那应该使用 [DaemonSet][16] 而不是部署。换句话说,应为每个使用唯一传感器的数据收集应用程序创建一个 DaemonSet,并使用 nodeSelector 确保它们仅在具有适当硬件的节点上运行。
|
||||
|
||||
服务发现功能允许 Pod 通过服务名称来寻找彼此,这项功能使得这类分布式系统的管理工作变得易如反掌。你不需要为应用配置 IP 地址或自定义端口,也不需要知道它们。相反,它们可以通过集群中的命名服务轻松找到彼此。
|
||||
|
||||
#### 充分利用空闲资源
|
||||
|
||||
随着集群的启动并运行,收集数据并控制灯光和气候,可能使你觉得你已经把它完成了。不过,集群中还有大量的计算资源可以用于其它项目。这才是 Kubernetes 真正出彩的地方。
|
||||
|
||||
你不必担心这些资源的确切位置,或者去计算是否有足够的内存来容纳额外的应用程序。这正是编排系统所解决的问题!你可以轻松地在集群中部署更多的应用,让 Kubernetes 来找出适合运行它们的位置(或是否适合运行它们)。
|
||||
|
||||
为什么不运行一个你自己的 [NextCloud][17] 实例呢?或者运行 [gitea][18]?你还可以为你所有的物联网容器设置一套 CI/CD 流水线。毕竟,如果你可以在集群中进行本地构建,为什么还要在主计算机上构建并交叉编译它们呢?
|
||||
|
||||
这里的要点是,Kubernetes 可以更容易地利用那些你可能浪费掉的“隐藏”资源。Kubernetes 根据可用资源和容错处理规则来调度 Pod,因此你也无需手动完成这些工作。但是,为了帮助 Kubernetes 做出合理的决定,你绝对应该为你的工作负载添加[资源请求][19]配置。
|
||||
|
||||
### 总结
|
||||
|
||||
尽管 Kuberenetes 或一般的容器编排平台通常不会与物联网相关联,但在管理分布式系统时,使用一个编排系统肯定是有意义的。你不仅可以使用统一的方式来处理多样化和异构的设备,还可以简化它们的通信方式。此外,Kubernetes 还可以更好地对闲置资源加以利用。
|
||||
|
||||
容器技术使构建“随处运行”应用的想法成为可能。现在,Kubernetes 可以更轻松地来负责“随处”的部分。作为构建一切的不可变基础,我们使用 Fedora IoT。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/kubernetes-on-fedora-iot-with-k3s/
|
||||
|
||||
作者:[Lennart Jern][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[StdioA](https://github.com/StdioA)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/lennartj/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2019/04/k3s-1-816x345.png
|
||||
[2]: https://linux.cn/article-10380-1.html
|
||||
[3]: https://hub.helm.sh/
|
||||
[4]: https://k3s.io
|
||||
[5]: https://docs.fedoraproject.org/en-US/iot/getting-started/
|
||||
[6]: https://github.com/rancher/k3s#open-ports--network-security
|
||||
[7]: https://github.com/rancher/k3s#systemd
|
||||
[8]: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
|
||||
[9]: https://kubernetes.io/docs/concepts/services-networking/service/
|
||||
[10]: https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer
|
||||
[11]: https://kubernetes.io/docs/concepts/services-networking/ingress/
|
||||
[12]: https://traefik.io/
|
||||
[13]: https://github.com/rancher/k3s/blob/master/README.md#service-load-balancer
|
||||
[14]: http://xip.io/
|
||||
[15]: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/
|
||||
[16]: https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/
|
||||
[17]: https://nextcloud.com/
|
||||
[18]: https://gitea.io/en-us/
|
||||
[19]: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/
|
@ -0,0 +1,347 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10840-1.html)
|
||||
[#]: subject: (Building a DNS-as-a-service with OpenStack Designate)
|
||||
[#]: via: (https://opensource.com/article/19/4/getting-started-openstack-designate)
|
||||
[#]: author: (Amjad Yaseen https://opensource.com/users/ayaseen)
|
||||
|
||||
用 OpenStack Designate 构建一个 DNS 即服务(DNSaaS)
|
||||
======
|
||||
|
||||
> 学习如何安装和配置 Designate,这是一个 OpenStack 的多租户 DNS 即服务(DNSaaS)。
|
||||
|
||||
![Command line prompt](https://img.linux.net.cn/data/attachment/album/201905/11/110822rjub9wtwtwtmccet.jpg)
|
||||
|
||||
[Designate][2] 是一个多租户的 DNS 即服务,它包括一个用于域名和记录管理的 REST API 和集成了 [Neutron][3] 的框架,并支持 Bind9。
|
||||
|
||||
DNSaaS 可以提供:
|
||||
|
||||
* 一个管理区域和记录的干净利落的 REST API
|
||||
* 自动生成记录(集成 OpenStack)
|
||||
* 支持多个授权名字服务器
|
||||
* 可以托管多个项目/组织
|
||||
|
||||
![Designate's architecture][4]
|
||||
|
||||
这篇文章解释了如何在 CentOS 和 RHEL 上手动安装和配置 Designate 的最新版本,但是同样的配置也可以用在其它发行版上。
|
||||
|
||||
### 在 OpenStack 上安装 Designate
|
||||
|
||||
在我的 [GitHub 仓库][5]里,我已经放了 Ansible 的 bind 和 Designate 角色的示范设置。
|
||||
|
||||
这个设置假定 bing 服务是安装 OpenStack 控制器节点之外(即使你可以在本地安装 bind)。
|
||||
|
||||
1、在 OpenStack 控制节点上安装 Designate 和 bind 软件包:
|
||||
|
||||
```
|
||||
# yum install openstack-designate-* bind bind-utils -y
|
||||
```
|
||||
|
||||
2、创建 Designate 数据库和用户:
|
||||
|
||||
```
|
||||
MariaDB [(none)]> CREATE DATABASE designate CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||
|
||||
MariaDB [(none)]> GRANT ALL PRIVILEGES ON designate.* TO \
|
||||
'designate'@'localhost' IDENTIFIED BY 'rhlab123';
|
||||
|
||||
MariaDB [(none)]> GRANT ALL PRIVILEGES ON designate.* TO 'designate'@'%' \
|
||||
IDENTIFIED BY 'rhlab123';
|
||||
```
|
||||
|
||||
注意:bind 包必须安装在控制节点之外才能使<ruby>远程名字服务控制<rt>Remote Name Daemon Control</rt></ruby>(RNDC)功能正常。
|
||||
|
||||
### 配置 bind(DNS 服务器)
|
||||
|
||||
1、生成 RNDC 文件:
|
||||
|
||||
```
|
||||
rndc-confgen -a -k designate -c /etc/rndc.key -r /dev/urandom
|
||||
|
||||
cat <<EOF> etcrndc.conf
|
||||
include "/etc/rndc.key";
|
||||
options {
|
||||
default-key "designate";
|
||||
default-server {{ DNS_SERVER_IP }};
|
||||
default-port 953;
|
||||
};
|
||||
EOF
|
||||
```
|
||||
|
||||
2、将下列配置添加到 `named.conf`:
|
||||
|
||||
```
|
||||
include "/etc/rndc.key";
|
||||
controls {
|
||||
inet {{ DNS_SERVER_IP }} allow { localhost;{{ CONTROLLER_SERVER_IP }}; } keys { "designate"; };
|
||||
};
|
||||
```
|
||||
|
||||
在 `option` 节中,添加:
|
||||
|
||||
```
|
||||
options {
|
||||
...
|
||||
allow-new-zones yes;
|
||||
request-ixfr no;
|
||||
listen-on port 53 { any; };
|
||||
recursion no;
|
||||
allow-query { 127.0.0.1; {{ CONTROLLER_SERVER_IP }}; };
|
||||
};
|
||||
```
|
||||
|
||||
添加正确的权限:
|
||||
|
||||
```
|
||||
chown named:named /etc/rndc.key
|
||||
chown named:named /etc/rndc.conf
|
||||
chmod 600 /etc/rndc.key
|
||||
chown -v root:named /etc/named.conf
|
||||
chmod g+w /var/named
|
||||
|
||||
# systemctl restart named
|
||||
# setsebool named_write_master_zones 1
|
||||
```
|
||||
|
||||
3、把 `rndc.key` 和 `rndc.conf` 推入 OpenStack 控制节点:
|
||||
|
||||
```
|
||||
# scp -r /etc/rndc* {{ CONTROLLER_SERVER_IP }}:/etc/
|
||||
```
|
||||
|
||||
### 创建 OpenStack Designate 服务和端点
|
||||
|
||||
输入:
|
||||
|
||||
```
|
||||
# openstack user create --domain default --password-prompt designate
|
||||
# openstack role add --project services --user designate admin
|
||||
# openstack service create --name designate --description "DNS" dns
|
||||
|
||||
# openstack endpoint create --region RegionOne dns public http://{{ CONTROLLER_SERVER_IP }}:9001/
|
||||
# openstack endpoint create --region RegionOne dns internal http://{{ CONTROLLER_SERVER_IP }}:9001/
|
||||
# openstack endpoint create --region RegionOne dns admin http://{{ CONTROLLER_SERVER_IP }}:9001/
|
||||
```
|
||||
|
||||
### 配置 Designate 服务
|
||||
|
||||
1、编辑 `/etc/designate/designate.conf`:
|
||||
|
||||
在 `[service:api]` 节配置 `auth_strategy`:
|
||||
|
||||
```
|
||||
[service:api]
|
||||
listen = 0.0.0.0:9001
|
||||
auth_strategy = keystone
|
||||
api_base_uri = http://{{ CONTROLLER_SERVER_IP }}:9001/
|
||||
enable_api_v2 = True
|
||||
enabled_extensions_v2 = quotas, reports
|
||||
```
|
||||
|
||||
在 `[keystone_authtoken]` 节配置下列选项:
|
||||
|
||||
```
|
||||
[keystone_authtoken]
|
||||
auth_type = password
|
||||
username = designate
|
||||
password = rhlab123
|
||||
project_name = service
|
||||
project_domain_name = Default
|
||||
user_domain_name = Default
|
||||
www_authenticate_uri = http://{{ CONTROLLER_SERVER_IP }}:5000/
|
||||
auth_url = http://{{ CONTROLLER_SERVER_IP }}:5000/
|
||||
```
|
||||
|
||||
在 `[service:worker]` 节,启用 worker 模型:
|
||||
|
||||
```
|
||||
enabled = True
|
||||
notify = True
|
||||
```
|
||||
|
||||
在 `[storage:sqlalchemy]` 节,配置数据库访问:
|
||||
|
||||
```
|
||||
[storage:sqlalchemy]
|
||||
connection = mysql+pymysql://designate:rhlab123@{{ CONTROLLER_SERVER_IP }}/designate
|
||||
```
|
||||
|
||||
填充 Designate 数据库:
|
||||
|
||||
```
|
||||
# su -s /bin/sh -c "designate-manage database sync" designate
|
||||
```
|
||||
|
||||
2、 创建 Designate 的 `pools.yaml` 文件(包含 target 和 bind 细节):
|
||||
|
||||
编辑 `/etc/designate/pools.yaml`:
|
||||
|
||||
```
|
||||
- name: default
|
||||
# The name is immutable. There will be no option to change the name after
|
||||
# creation and the only way will to change it will be to delete it
|
||||
# (and all zones associated with it) and recreate it.
|
||||
description: Default Pool
|
||||
|
||||
attributes: {}
|
||||
|
||||
# List out the NS records for zones hosted within this pool
|
||||
# This should be a record that is created outside of designate, that
|
||||
# points to the public IP of the controller node.
|
||||
ns_records:
|
||||
- hostname: {{Controller_FQDN}}. # Thisis mDNS
|
||||
priority: 1
|
||||
|
||||
# List out the nameservers for this pool. These are the actual BIND servers.
|
||||
# We use these to verify changes have propagated to all nameservers.
|
||||
nameservers:
|
||||
- host: {{ DNS_SERVER_IP }}
|
||||
port: 53
|
||||
|
||||
# List out the targets for this pool. For BIND there will be one
|
||||
# entry for each BIND server, as we have to run rndc command on each server
|
||||
targets:
|
||||
- type: bind9
|
||||
description: BIND9 Server 1
|
||||
|
||||
# List out the designate-mdns servers from which BIND servers should
|
||||
# request zone transfers (AXFRs) from.
|
||||
# This should be the IP of the controller node.
|
||||
# If you have multiple controllers you can add multiple masters
|
||||
# by running designate-mdns on them, and adding them here.
|
||||
masters:
|
||||
- host: {{ CONTROLLER_SERVER_IP }}
|
||||
port: 5354
|
||||
|
||||
# BIND Configuration options
|
||||
options:
|
||||
host: {{ DNS_SERVER_IP }}
|
||||
port: 53
|
||||
rndc_host: {{ DNS_SERVER_IP }}
|
||||
rndc_port: 953
|
||||
rndc_key_file: /etc/rndc.key
|
||||
rndc_config_file: /etc/rndc.conf
|
||||
```
|
||||
|
||||
填充 Designate 池:
|
||||
|
||||
```
|
||||
su -s /bin/sh -c "designate-manage pool update" designate
|
||||
```
|
||||
|
||||
3、启动 Designate 中心和 API 服务:
|
||||
|
||||
```
|
||||
systemctl enable --now designate-central designate-api
|
||||
```
|
||||
|
||||
4、验证 Designate 服务运行:
|
||||
|
||||
```
|
||||
# openstack dns service list
|
||||
|
||||
+--------------+--------+-------+--------------+
|
||||
| service_name | status | stats | capabilities |
|
||||
+--------------+--------+-------+--------------+
|
||||
| central | UP | - | - |
|
||||
| api | UP | - | - |
|
||||
| mdns | UP | - | - |
|
||||
| worker | UP | - | - |
|
||||
| producer | UP | - | - |
|
||||
+--------------+--------+-------+--------------+
|
||||
```
|
||||
|
||||
### 用外部 DNS 配置 OpenStack Neutron
|
||||
|
||||
1、为 Designate 服务配置 iptables:
|
||||
|
||||
```
|
||||
# iptables -I INPUT -p tcp -m multiport --dports 9001 -m comment --comment "designate incoming" -j ACCEPT
|
||||
|
||||
# iptables -I INPUT -p tcp -m multiport --dports 5354 -m comment --comment "Designate mdns incoming" -j ACCEPT
|
||||
|
||||
# iptables -I INPUT -p tcp -m multiport --dports 53 -m comment --comment "bind incoming" -j ACCEPT
|
||||
|
||||
# iptables -I INPUT -p udp -m multiport --dports 53 -m comment --comment "bind/powerdns incoming" -j ACCEPT
|
||||
|
||||
# iptables -I INPUT -p tcp -m multiport --dports 953 -m comment --comment "rndc incoming - bind only" -j ACCEPT
|
||||
|
||||
# service iptables save; service iptables restart
|
||||
# setsebool named_write_master_zones 1
|
||||
```
|
||||
|
||||
2、 编辑 `/etc/neutron/neutron.conf` 的 `[default]` 节:
|
||||
|
||||
```
|
||||
external_dns_driver = designate
|
||||
```
|
||||
|
||||
3、 在 `/etc/neutron/neutron.conf` 中添加 `[designate]` 节:
|
||||
|
||||
```
|
||||
[designate]
|
||||
url = http://{{ CONTROLLER_SERVER_IP }}:9001/v2 ## This end point of designate
|
||||
auth_type = password
|
||||
auth_url = http://{{ CONTROLLER_SERVER_IP }}:5000
|
||||
username = designate
|
||||
password = rhlab123
|
||||
project_name = services
|
||||
project_domain_name = Default
|
||||
user_domain_name = Default
|
||||
allow_reverse_dns_lookup = True
|
||||
ipv4_ptr_zone_prefix_size = 24
|
||||
ipv6_ptr_zone_prefix_size = 116
|
||||
```
|
||||
|
||||
4、编辑 `neutron.conf` 的 `dns_domain`:
|
||||
|
||||
```
|
||||
dns_domain = rhlab.dev.
|
||||
```
|
||||
|
||||
重启:
|
||||
|
||||
```
|
||||
# systemctl restart neutron-*
|
||||
```
|
||||
|
||||
5、在 `/etc/neutron/plugins/ml2/ml2_conf.ini` 中的组成层 2(ML2)中添加 `dns`:
|
||||
|
||||
```
|
||||
extension_drivers=port_security,qos,dns
|
||||
```
|
||||
|
||||
6、在 Designate 中添加区域:
|
||||
|
||||
```
|
||||
# openstack zone create –email=admin@rhlab.dev rhlab.dev.
|
||||
```
|
||||
|
||||
在 `rhlab.dev` 区域中添加记录:
|
||||
|
||||
```
|
||||
# openstack recordset create --record '192.168.1.230' --type A rhlab.dev. Test
|
||||
```
|
||||
|
||||
Designate 现在就安装和配置好了。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/getting-started-openstack-designate
|
||||
|
||||
作者:[Amjad Yaseen][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/ayaseen
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/command_line_prompt.png?itok=wbGiJ_yg (Command line prompt)
|
||||
[2]: https://docs.openstack.org/designate/latest/
|
||||
[3]: /article/19/3/openstack-neutron
|
||||
[4]: https://opensource.com/sites/default/files/uploads/openstack_designate_architecture.png (Designate's architecture)
|
||||
[5]: https://github.com/ayaseen/designate
|
@ -1,85 +1,86 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (warmfrog)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10891-1.html)
|
||||
[#]: subject: (Detecting malaria with deep learning)
|
||||
[#]: via: (https://opensource.com/article/19/4/detecting-malaria-deep-learning)
|
||||
[#]: author: (Dipanjan Sarkar https://opensource.com/users/djsarkar)
|
||||
[#]: author: (Dipanjan Sarkar https://opensource.com/users/djsarkar)
|
||||
|
||||
使用深度学习检测疟疾
|
||||
==================
|
||||
人工智能结合开源硬件工具能够提升严重传染病疟疾的诊断。
|
||||
|
||||
> 人工智能结合开源硬件工具能够提升严重传染病疟疾的诊断。
|
||||
|
||||
![][1]
|
||||
|
||||
人工智能(AI)和开源工具,技术,和框架是促进社会进步的强有力的结合。_“健康就是财富”_可能有点陈词滥调,但它却是非常准确的!在本篇文章,我们将测试 AI 是如何与低花费,有效,精确的开源深度学习方法一起被利用来检测致死的传染病疟疾。
|
||||
人工智能(AI)和开源工具、技术和框架是促进社会进步的强有力的结合。“健康就是财富”可能有点陈词滥调,但它却是非常准确的!在本篇文章,我们将测试 AI 是如何与低成本、有效、精确的开源深度学习方法结合起来一起用来检测致死的传染病疟疾。
|
||||
|
||||
我既不是一个医生,也不是一个医疗保健研究者,我也绝不像他们那样合格,我只是对将 AI 应用到医疗保健研究感兴趣。在这片文章中我的想法是展示 AI 和开源解决方案如何帮助疟疾检测和减少人工劳动的方法。
|
||||
|
||||
![Python and TensorFlow][2]
|
||||
|
||||
Python and TensorFlow: 一个构建开源深度学习方法的很棒的结合
|
||||
*Python 和 TensorFlow: 一个构建开源深度学习方法的很棒的结合*
|
||||
|
||||
感谢 Python 的强大 和像 TensorFlow 这样的深度学习框架,我们能够构建鲁棒的,大规模的,有效的深度学习方法。因为这些工具是自由和开源的,我们能够构建低成本的能够轻易被任何人采纳和使用的解决方案。让我们开始吧!
|
||||
感谢 Python 的强大和像 TensorFlow 这样的深度学习框架,我们能够构建健壮的、大规模的、有效的深度学习方法。因为这些工具是自由和开源的,我们能够构建非常经济且易于被任何人采纳和使用的解决方案。让我们开始吧!
|
||||
|
||||
### 项目动机
|
||||
|
||||
疟疾是由_疟原虫_造成的致死的,有传染性的,蚊子传播的疾病,主要通过受感染的雌性按蚊叮咬传播。共有五种寄生虫能够造成疟疾,但是样例中的大多数是这两种类型- _恶性疟原虫_ 和 _间日疟原虫_ 造成的。
|
||||
疟疾是由*疟原虫*造成的致死的、有传染性的、蚊子传播的疾病,主要通过受感染的雌性按蚊叮咬传播。共有五种寄生虫能够引起疟疾,但是大多数病例是这两种类型造成的:恶性疟原虫和间日疟原虫。
|
||||
|
||||
![疟疾热图][3]
|
||||
|
||||
这个地图显示了疟疾在全球传播分布形势,尤其在热带地区,但疾病的性质和致命性是该项目的主要动机。
|
||||
|
||||
如果一个雌性蚊子咬了你,蚊子携带的寄生虫进入你的血液并且开始破坏携带氧气的红细胞(RBC)。通常,疟疾的最初症状类似于流感病毒,在蚊子叮咬后,他们通常在几天或几周内发作。然而,这些致死的寄生虫可以在你的身体里生存长达一年并且不会造成任何症状,延迟治疗可能造成并发症甚至死亡。因此,早期的检查能够挽救生命。
|
||||
如果一只受感染雌性蚊子叮咬了你,蚊子携带的寄生虫进入你的血液,并且开始破坏携带氧气的红细胞(RBC)。通常,疟疾的最初症状类似于流感病毒,在蚊子叮咬后,他们通常在几天或几周内发作。然而,这些致死的寄生虫可以在你的身体里生存长达一年并且不会造成任何症状,延迟治疗可能造成并发症甚至死亡。因此,早期的检查能够挽救生命。
|
||||
|
||||
世界健康组织(WHO)的[疟疾事件][4]暗示世界近乎一半的人口面临疟疾的风险,有超过 2 亿 的疟疾病例,每年由于疟疾造成的死亡近乎 40 万。这是使疟疾检测和诊断快速,简单和有效的一个动机。
|
||||
世界健康组织(WHO)的[疟疾实情][4]表明,世界近乎一半的人口面临疟疾的风险,有超过 2 亿的疟疾病例,每年由于疟疾造成的死亡将近 40 万。这是使疟疾检测和诊断快速、简单和有效的一个动机。
|
||||
|
||||
### 检测疟疾的方法
|
||||
|
||||
有几种方法能够用来检测和诊断疟疾。该文中的项目就是基于 Rajaraman,et al. 的论文:“[预先训练的卷积神经网络作为特征提取器,用于改善薄血涂片图像中的疟疾寄生虫检测][5]”,介绍了一些方法,包含聚合酶链反应(PCR)和快速诊断测试(RDT)。这两种测试通常在高质量的显微镜下使用,但这样的设备不是轻易能够获得的。
|
||||
有几种方法能够用来检测和诊断疟疾。该文中的项目就是基于 Rajaraman, et al. 的论文:“[预先训练的卷积神经网络作为特征提取器,用于改善薄血涂片图像中的疟疾寄生虫检测][5]”介绍的一些方法,包含聚合酶链反应(PCR)和快速诊断测试(RDT)。这两种测试通常用于无法提供高质量显微镜服务的地方。
|
||||
|
||||
标准的疟疾诊断通常使基于血液涂片工作流的,根据 Carlos Ariza 的文章“[Malaria Hero: 一个更快诊断疟原虫的网络应用][6]”,我从中了解到 Adrian Rosebrock 的“[使用 Keras 的深度学习和医学图像分析][7]”。我感激这些优秀的资源的作者,让我在疟原虫预防,诊断和治疗方面有了更多的想法。
|
||||
标准的疟疾诊断通常是基于血液涂片工作流程的,根据 Carlos Ariza 的文章“[Malaria Hero:一个更快诊断疟原虫的网络应用][6]”,我从中了解到 Adrian Rosebrock 的“[使用 Keras 的深度学习和医学图像分析][7]”。我感激这些优秀的资源的作者,让我在疟原虫预防、诊断和治疗方面有了更多的想法。
|
||||
|
||||
![疟原虫检测的血涂片工作流程][8]
|
||||
|
||||
一个疟原虫检测的血涂片工作流程
|
||||
*一个疟原虫检测的血涂片工作流程*
|
||||
|
||||
根据 WHO 草案,诊断通常包括对放大 100 倍的血涂片的集中检测。训练人们人工计数在 5000 个细胞中有多少红细胞中包含疟原虫。正如上述解释中引用的 Rajaraman, et al. 的论文:
|
||||
根据 WHO 方案,诊断通常包括对放大 100 倍的血涂片的集中检测。受过训练的人们手工计算在 5000 个细胞中有多少红细胞中包含疟原虫。正如上述解释中引用的 Rajaraman, et al. 的论文:
|
||||
|
||||
> 薄血涂片帮助检测疟原虫的存在性并且帮助识别造成传染(疾病控制和抑制中心,2012)的物种。诊断准确性在很大程度上取决于人类的专业知识,并且可能受到观察者间差异和疾病流行/资源受限区域大规模诊断所造成的不利影响(Mitiku, Mengistu, and Gelaw, 2003)。可替代的技术是使用聚合酶链反应(PCR)和快速诊断测试(RDT);然而,PCR 分析受限于它的性能(Hommelsheim, et al., 2014),RDT 在疾病流行的地区成本效益低(Hawkes,Katsuva, and Masumbuko, 2009)。
|
||||
> 厚血涂片有助于检测寄生虫的存在,而薄血涂片有助于识别引起感染的寄生虫种类(疾病控制和预防中心, 2012)。诊断准确性在很大程度上取决于诊断人的专业知识,并且可能受到观察者间差异和疾病流行/资源受限区域大规模诊断所造成的不利影响(Mitiku, Mengistu 和 Gelaw, 2003)。可替代的技术是使用聚合酶链反应(PCR)和快速诊断测试(RDT);然而,PCR 分析受限于它的性能(Hommelsheim, et al., 2014),RDT 在疾病流行的地区成本效益低(Hawkes, Katsuva 和 Masumbuko, 2009)。
|
||||
|
||||
因此,疟疾检测可能受益于使用机器学习的自动化。
|
||||
|
||||
### 疟原虫检测的深度学习
|
||||
### 疟疾检测的深度学习
|
||||
|
||||
人工诊断血涂片是一个加强的人工过程,需要专业知识来分类和计数被寄生虫感染的和未感染的细胞。这个过程可能不能很好的规模化,尤其在那些专业人士不足的地区。在利用最先进的图像处理和分析技术提取人工选取特征和构建基于机器学习的分类模型方面取得了一些进展。然而,这些模型不能大规模推广,因为没有更多的数据用来训练,并且人工选取特征需要花费很长时间。
|
||||
人工诊断血涂片是一个繁重的手工过程,需要专业知识来分类和计数被寄生虫感染的和未感染的细胞。这个过程可能不能很好的规模化,尤其在那些专业人士不足的地区。在利用最先进的图像处理和分析技术提取人工选取特征和构建基于机器学习的分类模型方面取得了一些进展。然而,这些模型不能大规模推广,因为没有更多的数据用来训练,并且人工选取特征需要花费很长时间。
|
||||
|
||||
深度学习模型,或者更具体地讲,卷积神经网络(CNNs),已经被证明在各种计算机视觉任务中非常有效。(如果你想有额外的关于 CNNs 的背景知识,我推荐你阅读[视觉识别的 CS2331n 卷积神经网络][9]。)简单地讲,CNN 模型的关键层包含卷积和池化层,正如下面图像显示。
|
||||
深度学习模型,或者更具体地讲,卷积神经网络(CNN),已经被证明在各种计算机视觉任务中非常有效。(如果你想更多的了解关于 CNN 的背景知识,我推荐你阅读[视觉识别的 CS2331n 卷积神经网络][9]。)简单地讲,CNN 模型的关键层包含卷积和池化层,正如下图所示。
|
||||
|
||||
![A typical CNN architecture][10]
|
||||
|
||||
一个典型的 CNN 架构
|
||||
*一个典型的 CNN 架构*
|
||||
|
||||
卷积层从数据中学习空间层级模式,它是平移不变的,因此它们能够学习不同方面的图像。例如,第一个卷积层将学习小的和本地图案,例如边缘和角落,第二个卷积层学习基于第一层的特征的更大的图案,等等。这允许 CNNs 自动化提取特征并且学习对于新数据点通用的有效的特征。池化层帮助下采样和降维。
|
||||
卷积层从数据中学习空间层级模式,它是平移不变的,因此它们能够学习图像的不同方面。例如,第一个卷积层将学习小的和局部图案,例如边缘和角落,第二个卷积层将基于第一层的特征学习更大的图案,等等。这允许 CNN 自动化提取特征并且学习对于新数据点通用的有效的特征。池化层有助于下采样和减少尺寸。
|
||||
|
||||
因此,CNNs 帮助自动化和规模化的特征工程。同样,在模型末尾加上密集层允许我们执行像图像分类这样的任务。使用像 CNNs 者的深度学习模型自动的疟疾检测可能非常有效,便宜和具有规模性,尤其是迁移学习和预训练模型效果非常好,甚至在少量数据的约束下。
|
||||
因此,CNN 有助于自动化和规模化的特征工程。同样,在模型末尾加上密集层允许我们执行像图像分类这样的任务。使用像 CNN 这样的深度学习模型自动的疟疾检测可能非常有效、便宜和具有规模性,尤其是迁移学习和预训练模型效果非常好,甚至在少量数据的约束下。
|
||||
|
||||
Rajaraman, et al. 的论文在一个数据集上利用六个预训练模型在检测疟疾 vs 无感染样本获取到令人吃惊的 95.9% 的准确率。我们的关注点是从头开始尝试一些简单的 CNN 模型和用一个预训练的训练模型使用迁移学习来查看我们能够从相同的数据集中得到什么。我们将使用开源工具和框架,包括 Python 和 TensorFlow,来构建我们的模型。
|
||||
Rajaraman, et al. 的论文在一个数据集上利用六个预训练模型在检测疟疾对比无感染样本获取到令人吃惊的 95.9% 的准确率。我们的重点是从头开始尝试一些简单的 CNN 模型和用一个预训练的训练模型使用迁移学习来查看我们能够从相同的数据集中得到什么。我们将使用开源工具和框架,包括 Python 和 TensorFlow,来构建我们的模型。
|
||||
|
||||
### 数据集
|
||||
|
||||
我们分析的数据来自 Lister Hill 国家生物医学交流中心(LHNCBC),国家医学图书馆(NLM)的一部分,他们细心收集和标记了健康和受感染的血涂片图像的[公众可获得的数据集][11]。这些研究者已经开发了一个运行在 Android 智能手机的移动[疟疾检测应用][12],连接到一个传统的光学显微镜。它们使用 吉姆萨染液 将 150 个受恶性疟原虫感染的和 50 个健康病人的薄血涂片染色,这些薄血涂片是在孟加拉的吉大港医学院附属医院收集和照相的。使用智能手机的内置相机获取每个显微镜视窗内的图像。这些图片由在泰国曼谷的马希多-牛津热带医学研究所的一个专家使用幻灯片阅读器标记的。
|
||||
我们分析的数据来自 Lister Hill 国家生物医学交流中心(LHNCBC)的研究人员,该中心是国家医学图书馆(NLM)的一部分,他们细心收集和标记了公开可用的健康和受感染的血涂片图像的[数据集][11]。这些研究者已经开发了一个运行在 Android 智能手机的[疟疾检测手机应用][12],连接到一个传统的光学显微镜。它们使用吉姆萨染液将 150 个受恶性疟原虫感染的和 50 个健康病人的薄血涂片染色,这些薄血涂片是在孟加拉的吉大港医学院附属医院收集和照相的。使用智能手机的内置相机获取每个显微镜视窗内的图像。这些图片由在泰国曼谷的马希多-牛津热带医学研究所的一个专家使用幻灯片阅读器标记的。
|
||||
|
||||
让我们简洁的查看数据集的结构。首先,我将安装一些基础的依赖(基于使用的操作系统)。
|
||||
让我们简要地查看一下数据集的结构。首先,我将安装一些基础的依赖(基于使用的操作系统)。
|
||||
|
||||
![Installing dependencies][13]
|
||||
|
||||
我使用的是云上的带有一个 GPU 的基于 Debian 的操作系统,这样我能更快的运行我的模型。为了查看目录结构,我们必须安装 tree 依赖(如果我们没有安装的话)使用 **sudo apt install tree**。
|
||||
我使用的是云上的带有一个 GPU 的基于 Debian 的操作系统,这样我能更快的运行我的模型。为了查看目录结构,我们必须使用 `sudo apt install tree` 安装 `tree` 及其依赖(如果我们没有安装的话)。
|
||||
|
||||
![Installing the tree dependency][14]
|
||||
|
||||
我们有两个文件夹包含血细胞的图像,包括受感染的和健康的。我们可以获取关于图像总数更多的细节通过输入:
|
||||
|
||||
我们有两个文件夹包含血细胞的图像,包括受感染的和健康的。我们通过输入可以获取关于图像总数更多的细节:
|
||||
|
||||
```
|
||||
import os
|
||||
@ -97,7 +98,7 @@ len(infected_files), len(healthy_files)
|
||||
(13779, 13779)
|
||||
```
|
||||
|
||||
看起来我们有一个平衡的 13,779 张疟疾的 和 13,779 张非疟疾的(健康的)血细胞图像。让我们根据这些构建数据帧,我们将用这些数据帧来构建我们的数据集。
|
||||
看起来我们有一个平衡的数据集,包含 13,779 张疟疾的和 13,779 张非疟疾的(健康的)血细胞图像。让我们根据这些构建数据帧,我们将用这些数据帧来构建我们的数据集。
|
||||
|
||||
|
||||
```
|
||||
@ -107,8 +108,8 @@ import pandas as pd
|
||||
np.random.seed(42)
|
||||
|
||||
files_df = pd.DataFrame({
|
||||
'filename': infected_files + healthy_files,
|
||||
'label': ['malaria'] * len(infected_files) + ['healthy'] * len(healthy_files)
|
||||
'filename': infected_files + healthy_files,
|
||||
'label': ['malaria'] * len(infected_files) + ['healthy'] * len(healthy_files)
|
||||
}).sample(frac=1, random_state=42).reset_index(drop=True)
|
||||
|
||||
files_df.head()
|
||||
@ -116,9 +117,9 @@ files_df.head()
|
||||
|
||||
![Datasets][15]
|
||||
|
||||
### 构建和参所图像数据集
|
||||
### 构建和了解图像数据集
|
||||
|
||||
为了构建深度学习模型,我们需要训练数据,但是我们还需要使用不可见的数据测试模型的性能。相应的,我们将使用 60:10:30 的划分用于训练,验证和测试数据集。我们将在训练期间应用训练和验证数据集并用测试数据集来检查模型的性能。
|
||||
为了构建深度学习模型,我们需要训练数据,但是我们还需要使用不可见的数据测试模型的性能。相应的,我们将使用 60:10:30 的比例来划分用于训练、验证和测试的数据集。我们将在训练期间应用训练和验证数据集,并用测试数据集来检查模型的性能。
|
||||
|
||||
|
||||
```
|
||||
@ -126,24 +127,23 @@ from sklearn.model_selection import train_test_split
|
||||
from collections import Counter
|
||||
|
||||
train_files, test_files, train_labels, test_labels = train_test_split(files_df['filename'].values,
|
||||
files_df['label'].values,
|
||||
test_size=0.3, random_state=42)
|
||||
files_df['label'].values,
|
||||
test_size=0.3, random_state=42)
|
||||
train_files, val_files, train_labels, val_labels = train_test_split(train_files,
|
||||
train_labels,
|
||||
test_size=0.1, random_state=42)
|
||||
train_labels,
|
||||
test_size=0.1, random_state=42)
|
||||
|
||||
print(train_files.shape, val_files.shape, test_files.shape)
|
||||
print('Train:', Counter(train_labels), '\nVal:', Counter(val_labels), '\nTest:', Counter(test_labels))
|
||||
|
||||
# Output
|
||||
(17361,) (1929,) (8268,)
|
||||
Train: Counter({'healthy': 8734, 'malaria': 8627})
|
||||
Val: Counter({'healthy': 970, 'malaria': 959})
|
||||
Train: Counter({'healthy': 8734, 'malaria': 8627})
|
||||
Val: Counter({'healthy': 970, 'malaria': 959})
|
||||
Test: Counter({'malaria': 4193, 'healthy': 4075})
|
||||
```
|
||||
|
||||
这些图片维度并不相同,因此血涂片和细胞图像是基于人类,测试方法,图片的朝向。让我们总结我们的训练数据集的统计信息来决定最佳的图像维度(牢记,我们根本不会碰测试数据集)。
|
||||
|
||||
这些图片尺寸并不相同,因为血涂片和细胞图像是基于人、测试方法、图片方向不同而不同的。让我们总结我们的训练数据集的统计信息来决定最佳的图像尺寸(牢记,我们根本不会碰测试数据集)。
|
||||
|
||||
```
|
||||
import cv2
|
||||
@ -151,24 +151,25 @@ from concurrent import futures
|
||||
import threading
|
||||
|
||||
def get_img_shape_parallel(idx, img, total_imgs):
|
||||
if idx % 5000 == 0 or idx == (total_imgs - 1):
|
||||
print('{}: working on img num: {}'.format(threading.current_thread().name,
|
||||
idx))
|
||||
return cv2.imread(img).shape
|
||||
|
||||
if idx % 5000 == 0 or idx == (total_imgs - 1):
|
||||
print('{}: working on img num: {}'.format(threading.current_thread().name,
|
||||
idx))
|
||||
return cv2.imread(img).shape
|
||||
|
||||
ex = futures.ThreadPoolExecutor(max_workers=None)
|
||||
data_inp = [(idx, img, len(train_files)) for idx, img in enumerate(train_files)]
|
||||
print('Starting Img shape computation:')
|
||||
train_img_dims_map = ex.map(get_img_shape_parallel,
|
||||
[record[0] for record in data_inp],
|
||||
[record[1] for record in data_inp],
|
||||
[record[2] for record in data_inp])
|
||||
train_img_dims_map = ex.map(get_img_shape_parallel,
|
||||
[record[0] for record in data_inp],
|
||||
[record[1] for record in data_inp],
|
||||
[record[2] for record in data_inp])
|
||||
train_img_dims = list(train_img_dims_map)
|
||||
print('Min Dimensions:', np.min(train_img_dims, axis=0))
|
||||
print('Min Dimensions:', np.min(train_img_dims, axis=0))
|
||||
print('Avg Dimensions:', np.mean(train_img_dims, axis=0))
|
||||
print('Median Dimensions:', np.median(train_img_dims, axis=0))
|
||||
print('Max Dimensions:', np.max(train_img_dims, axis=0))
|
||||
|
||||
|
||||
# Output
|
||||
Starting Img shape computation:
|
||||
ThreadPoolExecutor-0_0: working on img num: 0
|
||||
@ -176,27 +177,26 @@ ThreadPoolExecutor-0_17: working on img num: 5000
|
||||
ThreadPoolExecutor-0_15: working on img num: 10000
|
||||
ThreadPoolExecutor-0_1: working on img num: 15000
|
||||
ThreadPoolExecutor-0_7: working on img num: 17360
|
||||
Min Dimensions: [46 46 3]
|
||||
Avg Dimensions: [132.77311215 132.45757733 3.]
|
||||
Median Dimensions: [130. 130. 3.]
|
||||
Max Dimensions: [385 394 3]
|
||||
Min Dimensions: [46 46 3]
|
||||
Avg Dimensions: [132.77311215 132.45757733 3.]
|
||||
Median Dimensions: [130. 130. 3.]
|
||||
Max Dimensions: [385 394 3]
|
||||
```
|
||||
|
||||
我们应用并行处理来加速图像读取,并且在总结统计时,我们将重新调整每幅图片到 125x125 像素。让我们载入我们所有的图像并重新调整它们为这些固定的大小。
|
||||
|
||||
我们应用并行处理来加速图像读取,并且基于汇总统计结果,我们将每幅图片的尺寸重新调整到 125x125 像素。让我们载入我们所有的图像并重新调整它们为这些固定尺寸。
|
||||
|
||||
```
|
||||
IMG_DIMS = (125, 125)
|
||||
|
||||
def get_img_data_parallel(idx, img, total_imgs):
|
||||
if idx % 5000 == 0 or idx == (total_imgs - 1):
|
||||
print('{}: working on img num: {}'.format(threading.current_thread().name,
|
||||
idx))
|
||||
img = cv2.imread(img)
|
||||
img = cv2.resize(img, dsize=IMG_DIMS,
|
||||
interpolation=cv2.INTER_CUBIC)
|
||||
img = np.array(img, dtype=np.float32)
|
||||
return img
|
||||
if idx % 5000 == 0 or idx == (total_imgs - 1):
|
||||
print('{}: working on img num: {}'.format(threading.current_thread().name,
|
||||
idx))
|
||||
img = cv2.imread(img)
|
||||
img = cv2.resize(img, dsize=IMG_DIMS,
|
||||
interpolation=cv2.INTER_CUBIC)
|
||||
img = np.array(img, dtype=np.float32)
|
||||
return img
|
||||
|
||||
ex = futures.ThreadPoolExecutor(max_workers=None)
|
||||
train_data_inp = [(idx, img, len(train_files)) for idx, img in enumerate(train_files)]
|
||||
@ -204,27 +204,28 @@ val_data_inp = [(idx, img, len(val_files)) for idx, img in enumerate(val_files)]
|
||||
test_data_inp = [(idx, img, len(test_files)) for idx, img in enumerate(test_files)]
|
||||
|
||||
print('Loading Train Images:')
|
||||
train_data_map = ex.map(get_img_data_parallel,
|
||||
[record[0] for record in train_data_inp],
|
||||
[record[1] for record in train_data_inp],
|
||||
[record[2] for record in train_data_inp])
|
||||
train_data_map = ex.map(get_img_data_parallel,
|
||||
[record[0] for record in train_data_inp],
|
||||
[record[1] for record in train_data_inp],
|
||||
[record[2] for record in train_data_inp])
|
||||
train_data = np.array(list(train_data_map))
|
||||
|
||||
print('\nLoading Validation Images:')
|
||||
val_data_map = ex.map(get_img_data_parallel,
|
||||
[record[0] for record in val_data_inp],
|
||||
[record[1] for record in val_data_inp],
|
||||
[record[2] for record in val_data_inp])
|
||||
val_data_map = ex.map(get_img_data_parallel,
|
||||
[record[0] for record in val_data_inp],
|
||||
[record[1] for record in val_data_inp],
|
||||
[record[2] for record in val_data_inp])
|
||||
val_data = np.array(list(val_data_map))
|
||||
|
||||
print('\nLoading Test Images:')
|
||||
test_data_map = ex.map(get_img_data_parallel,
|
||||
[record[0] for record in test_data_inp],
|
||||
[record[1] for record in test_data_inp],
|
||||
[record[2] for record in test_data_inp])
|
||||
test_data_map = ex.map(get_img_data_parallel,
|
||||
[record[0] for record in test_data_inp],
|
||||
[record[1] for record in test_data_inp],
|
||||
[record[2] for record in test_data_inp])
|
||||
test_data = np.array(list(test_data_map))
|
||||
|
||||
train_data.shape, val_data.shape, test_data.shape
|
||||
train_data.shape, val_data.shape, test_data.shape
|
||||
|
||||
|
||||
# Output
|
||||
Loading Train Images:
|
||||
@ -245,23 +246,22 @@ ThreadPoolExecutor-1_8: working on img num: 8267
|
||||
((17361, 125, 125, 3), (1929, 125, 125, 3), (8268, 125, 125, 3))
|
||||
```
|
||||
|
||||
我们再次应用并行处理来加速有关图像载入和重新调整大小。最终,我们获得了想要的维度的图片张量,正如之前描述的。我们现在查看一些血细胞图像样本来对我们的数据什么样有个印象。
|
||||
|
||||
我们再次应用并行处理来加速有关图像载入和重新调整大小的计算。最终,我们获得了所需尺寸的图片张量,正如前面的输出所示。我们现在查看一些血细胞图像样本,以对我们的数据有个印象。
|
||||
|
||||
```
|
||||
import matplotlib.pyplot as plt
|
||||
%matplotlib inline
|
||||
|
||||
plt.figure(1 , figsize = (8 , 8))
|
||||
n = 0
|
||||
n = 0
|
||||
for i in range(16):
|
||||
n += 1
|
||||
r = np.random.randint(0 , train_data.shape[0] , 1)
|
||||
plt.subplot(4 , 4 , n)
|
||||
plt.subplots_adjust(hspace = 0.5 , wspace = 0.5)
|
||||
plt.imshow(train_data[r[0]]/255.)
|
||||
plt.title('{}'.format(train_labels[r[0]]))
|
||||
plt.xticks([]) , plt.yticks([])
|
||||
n += 1
|
||||
r = np.random.randint(0 , train_data.shape[0] , 1)
|
||||
plt.subplot(4 , 4 , n)
|
||||
plt.subplots_adjust(hspace = 0.5 , wspace = 0.5)
|
||||
plt.imshow(train_data[r[0]]/255.)
|
||||
plt.title('{}'.format(train_labels[r[0]]))
|
||||
plt.xticks([]) , plt.yticks([])
|
||||
```
|
||||
|
||||
![Malaria cell samples][16]
|
||||
@ -270,7 +270,6 @@ plt.xticks([]) , plt.yticks([])
|
||||
|
||||
开始我们的模型训练前,我们必须建立一些基础的配置设置。
|
||||
|
||||
|
||||
```
|
||||
BATCH_SIZE = 64
|
||||
NUM_CLASSES = 2
|
||||
@ -290,12 +289,12 @@ val_labels_enc = le.transform(val_labels)
|
||||
|
||||
print(train_labels[:6], train_labels_enc[:6])
|
||||
|
||||
|
||||
# Output
|
||||
['malaria' 'malaria' 'malaria' 'healthy' 'healthy' 'malaria'] [1 1 1 0 0 1]
|
||||
```
|
||||
|
||||
我们修复我们的图像维度,批大小,和历元并编码我们的分类类标签。TensorFlow 2.0 于 2019 年三月发布,这个练习是非常好的借口来试用它。
|
||||
|
||||
我们修复我们的图像尺寸、批量大小,和纪元,并编码我们的分类的类标签。TensorFlow 2.0 于 2019 年三月发布,这个练习是尝试它的完美理由。
|
||||
|
||||
```
|
||||
import tensorflow as tf
|
||||
@ -312,24 +311,23 @@ tf.__version__
|
||||
|
||||
### 深度学习训练
|
||||
|
||||
在模型训练阶段,我们将构建三个深度训练模型,使用我们的训练集训练,使用验证数据比较它们的性能。我们然后保存这些模型并在之后的模型评估阶段使用它们。
|
||||
在模型训练阶段,我们将构建三个深度训练模型,使用我们的训练集训练,使用验证数据比较它们的性能。然后,我们保存这些模型并在之后的模型评估阶段使用它们。
|
||||
|
||||
#### 模型 1:从头开始的 CNN
|
||||
|
||||
我们的第一个疟疾检测模型将从头开始构建和训练一个基础的 CNN。首先,让我们定义我们的模型架构,
|
||||
|
||||
|
||||
```
|
||||
inp = tf.keras.layers.Input(shape=INPUT_SHAPE)
|
||||
|
||||
conv1 = tf.keras.layers.Conv2D(32, kernel_size=(3, 3),
|
||||
activation='relu', padding='same')(inp)
|
||||
conv1 = tf.keras.layers.Conv2D(32, kernel_size=(3, 3),
|
||||
activation='relu', padding='same')(inp)
|
||||
pool1 = tf.keras.layers.MaxPooling2D(pool_size=(2, 2))(conv1)
|
||||
conv2 = tf.keras.layers.Conv2D(64, kernel_size=(3, 3),
|
||||
activation='relu', padding='same')(pool1)
|
||||
conv2 = tf.keras.layers.Conv2D(64, kernel_size=(3, 3),
|
||||
activation='relu', padding='same')(pool1)
|
||||
pool2 = tf.keras.layers.MaxPooling2D(pool_size=(2, 2))(conv2)
|
||||
conv3 = tf.keras.layers.Conv2D(128, kernel_size=(3, 3),
|
||||
activation='relu', padding='same')(pool2)
|
||||
conv3 = tf.keras.layers.Conv2D(128, kernel_size=(3, 3),
|
||||
activation='relu', padding='same')(pool2)
|
||||
pool3 = tf.keras.layers.MaxPooling2D(pool_size=(2, 2))(conv3)
|
||||
|
||||
flat = tf.keras.layers.Flatten()(pool3)
|
||||
@ -343,31 +341,32 @@ out = tf.keras.layers.Dense(1, activation='sigmoid')(drop2)
|
||||
|
||||
model = tf.keras.Model(inputs=inp, outputs=out)
|
||||
model.compile(optimizer='adam',
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
model.summary()
|
||||
|
||||
|
||||
# Output
|
||||
Model: "model"
|
||||
_________________________________________________________________
|
||||
Layer (type) Output Shape Param #
|
||||
Layer (type) Output Shape Param #
|
||||
=================================================================
|
||||
input_1 (InputLayer) [(None, 125, 125, 3)] 0
|
||||
input_1 (InputLayer) [(None, 125, 125, 3)] 0
|
||||
_________________________________________________________________
|
||||
conv2d (Conv2D) (None, 125, 125, 32) 896
|
||||
conv2d (Conv2D) (None, 125, 125, 32) 896
|
||||
_________________________________________________________________
|
||||
max_pooling2d (MaxPooling2D) (None, 62, 62, 32) 0
|
||||
max_pooling2d (MaxPooling2D) (None, 62, 62, 32) 0
|
||||
_________________________________________________________________
|
||||
conv2d_1 (Conv2D) (None, 62, 62, 64) 18496
|
||||
conv2d_1 (Conv2D) (None, 62, 62, 64) 18496
|
||||
_________________________________________________________________
|
||||
...
|
||||
...
|
||||
_________________________________________________________________
|
||||
dense_1 (Dense) (None, 512) 262656
|
||||
dense_1 (Dense) (None, 512) 262656
|
||||
_________________________________________________________________
|
||||
dropout_1 (Dropout) (None, 512) 0
|
||||
dropout_1 (Dropout) (None, 512) 0
|
||||
_________________________________________________________________
|
||||
dense_2 (Dense) (None, 1) 513
|
||||
dense_2 (Dense) (None, 1) 513
|
||||
=================================================================
|
||||
Total params: 15,102,529
|
||||
Trainable params: 15,102,529
|
||||
@ -375,26 +374,26 @@ Non-trainable params: 0
|
||||
_________________________________________________________________
|
||||
```
|
||||
|
||||
基于这些代码的架构,我们的 CNN 模型有三个卷积和一个池化层,跟随两个致密层,以及用于正则化的丢失。让我们训练我们的模型。
|
||||
基于这些代码的架构,我们的 CNN 模型有三个卷积和一个池化层,其后是两个致密层,以及用于正则化的失活。让我们训练我们的模型。
|
||||
|
||||
|
||||
```
|
||||
import datetime
|
||||
|
||||
logdir = os.path.join('/home/dipanzan_sarkar/projects/tensorboard_logs',
|
||||
datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))
|
||||
logdir = os.path.join('/home/dipanzan_sarkar/projects/tensorboard_logs',
|
||||
datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))
|
||||
tensorboard_callback = tf.keras.callbacks.TensorBoard(logdir, histogram_freq=1)
|
||||
reduce_lr = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5,
|
||||
patience=2, min_lr=0.000001)
|
||||
patience=2, min_lr=0.000001)
|
||||
callbacks = [reduce_lr, tensorboard_callback]
|
||||
|
||||
history = model.fit(x=train_imgs_scaled, y=train_labels_enc,
|
||||
batch_size=BATCH_SIZE,
|
||||
epochs=EPOCHS,
|
||||
validation_data=(val_imgs_scaled, val_labels_enc),
|
||||
callbacks=callbacks,
|
||||
verbose=1)
|
||||
|
||||
history = model.fit(x=train_imgs_scaled, y=train_labels_enc,
|
||||
batch_size=BATCH_SIZE,
|
||||
epochs=EPOCHS,
|
||||
validation_data=(val_imgs_scaled, val_labels_enc),
|
||||
callbacks=callbacks,
|
||||
verbose=1)
|
||||
|
||||
|
||||
# Output
|
||||
Train on 17361 samples, validate on 1929 samples
|
||||
@ -439,57 +438,53 @@ l2 = ax2.legend(loc="best")
|
||||
|
||||
![Learning curves for basic CNN][17]
|
||||
|
||||
基础 CNN 学习曲线
|
||||
|
||||
我们可以看在在第五个历元,情况并没有改善很多。让我们保存这个模型用于将来的评估。
|
||||
*基础 CNN 学习曲线*
|
||||
|
||||
我们可以看在在第五个纪元,情况并没有改善很多。让我们保存这个模型用于将来的评估。
|
||||
|
||||
```
|
||||
`model.save('basic_cnn.h5')`
|
||||
model.save('basic_cnn.h5')
|
||||
```
|
||||
|
||||
#### 深度迁移学习
|
||||
|
||||
就像人类有与生俱来的能力在不同任务间传输知识,迁移学习允许我们利用从以前任务学到的知识用到新的任务,相关的任务,甚至在机器学习或深度学习的上下文中。如果想深入探究迁移学习,你应该看我的文章“[一个易于理解与现实应用一起学习深度学习中的迁移学习的指导实践][18]”和我的书[ Python 迁移学习实践][19]。
|
||||
就像人类有与生俱来在不同任务间传输知识的能力一样,迁移学习允许我们利用从以前任务学到的知识用到新的相关的任务,即使在机器学习或深度学习的情况下也是如此。如果想深入探究迁移学习,你应该看我的文章“[一个易于理解与现实应用一起学习深度学习中的迁移学习的指导实践][18]”和我的书《[Python 迁移学习实践][19]》。
|
||||
|
||||
![深度迁移学习的想法][20]
|
||||
|
||||
在这篇实践中我们想要探索的想法是:
|
||||
|
||||
> 在我们的问题上下文中,我们能够利用一个预训练深度学习模型(在大数据集上训练的,像 ImageNet)通过应用和迁移知识来解决疟疾检测的问题吗?
|
||||
> 在我们的问题背景下,我们能够利用一个预训练深度学习模型(在大数据集上训练的,像 ImageNet)通过应用和迁移知识来解决疟疾检测的问题吗?
|
||||
|
||||
我们将应用两个深度迁移学习的最流行的策略。
|
||||
我们将应用两个最流行的深度迁移学习策略。
|
||||
|
||||
* 预训练模型作为特征提取器
|
||||
* 微调的预训练模型
|
||||
|
||||
|
||||
|
||||
我们将使用预训练的 VGG-19 深度训练模型,由剑桥大学的视觉几何组(VGG)开发,作为我们的实验。一个像 VGG-19 的预训练模型在一个大的数据集上使用了很多不同的图像分类训练([Imagenet][21])。因此,这个模型应该已经学习到了鲁棒的特征层级结构,相对于你的 CNN 模型学到的特征,是空间不变的,转动不变的,平移不变的。因此,这个模型,已经从百万幅图片中学习到了一个好的特征显示,对于像疟疾检测这样的计算机视觉问题,可以作为一个好的合适新图像的特征提取器。在我们的问题中释放迁移学习的能力之前,让我们先讨论 VGG-19 模型。
|
||||
我们将使用预训练的 VGG-19 深度训练模型(由剑桥大学的视觉几何组(VGG)开发)进行我们的实验。像 VGG-19 这样的预训练模型是在一个大的数据集([Imagenet][21])上使用了很多不同的图像分类训练的。因此,这个模型应该已经学习到了健壮的特征层级结构,相对于你的 CNN 模型学到的特征,是空间不变的、转动不变的、平移不变的。因此,这个模型,已经从百万幅图片中学习到了一个好的特征显示,对于像疟疾检测这样的计算机视觉问题,可以作为一个好的合适新图像的特征提取器。在我们的问题中发挥迁移学习的能力之前,让我们先讨论 VGG-19 模型。
|
||||
|
||||
##### 理解 VGG-19 模型
|
||||
|
||||
VGG-19 模型是一个构建在 ImageNet 数据库之上的 19 层(卷积和全连接的)的深度学习网络,该数据库为了图像识别和分类的目的而开发。该模型由 Karen Simonyan 和 Andrew Zisserman 构建,在它们的论文”[大规模图像识别的非常深的卷积网络][22]“中描述。VGG-19 的架构模型是:
|
||||
VGG-19 模型是一个构建在 ImageNet 数据库之上的 19 层(卷积和全连接的)的深度学习网络,ImageNet 数据库为了图像识别和分类的目的而开发。该模型是由 Karen Simonyan 和 Andrew Zisserman 构建的,在他们的论文“[大规模图像识别的非常深的卷积网络][22]”中进行了描述。VGG-19 的架构模型是:
|
||||
|
||||
![VGG-19 模型架构][23]
|
||||
|
||||
你可以看到我们总共有 16 个使用 3x3 卷积过滤器的卷积层,与最大的池化层来下采样,和由 4096 个单元组成的两个全连接的隐藏层,每个隐藏层之后跟随一个由 1000 个单元组成的致密层,每个单元代表 ImageNet 数据库中的一个分类。我们不需要最后三层,因为我们将使用我们自己的全连接致密层来预测疟疾。我们更关心前五块,因此我们可以利用 VGG 模型作为一个有效的特征提取器。
|
||||
你可以看到我们总共有 16 个使用 3x3 卷积过滤器的卷积层,与最大的池化层来下采样,和由 4096 个单元组成的两个全连接的隐藏层,每个隐藏层之后跟随一个由 1000 个单元组成的致密层,每个单元代表 ImageNet 数据库中的一个分类。我们不需要最后三层,因为我们将使用我们自己的全连接致密层来预测疟疾。我们更关心前五个块,因此我们可以利用 VGG 模型作为一个有效的特征提取器。
|
||||
|
||||
我们将使用模型之一作为一个简单的特征提取器通过冻结五个卷积块的方式来确保它们的位权在每个时期后不会更新。对于最后一个模型,我们会应用微调到 VGG 模型,我们会解冻最后两个块(第 4 和第 5)因此当我们训练我们的模型时,它们的位权在每个时期(每批数据)被更新。
|
||||
我们将使用模型之一作为一个简单的特征提取器,通过冻结五个卷积块的方式来确保它们的位权在每个纪元后不会更新。对于最后一个模型,我们会对 VGG 模型进行微调,我们会解冻最后两个块(第 4 和第 5)因此当我们训练我们的模型时,它们的位权在每个时期(每批数据)被更新。
|
||||
|
||||
#### 模型 2:预训练的模型作为一个特征提取器
|
||||
|
||||
为了构建这个模型,我们将利用 TensorFlow 载入 VGG-19 模型并且冻结卷积块因此我们用够将他们用作特征提取器。我们插入我们自己的致密层在末尾来执行分类任务。
|
||||
|
||||
为了构建这个模型,我们将利用 TensorFlow 载入 VGG-19 模型并冻结卷积块,因此我们能够将它们用作特征提取器。我们在末尾插入我们自己的致密层来执行分类任务。
|
||||
|
||||
```
|
||||
vgg = tf.keras.applications.vgg19.VGG19(include_top=False, weights='imagenet',
|
||||
input_shape=INPUT_SHAPE)
|
||||
vgg = tf.keras.applications.vgg19.VGG19(include_top=False, weights='imagenet',
|
||||
input_shape=INPUT_SHAPE)
|
||||
vgg.trainable = False
|
||||
# Freeze the layers
|
||||
for layer in vgg.layers:
|
||||
layer.trainable = False
|
||||
|
||||
layer.trainable = False
|
||||
|
||||
base_vgg = vgg
|
||||
base_out = base_vgg.output
|
||||
pool_out = tf.keras.layers.Flatten()(base_out)
|
||||
@ -502,37 +497,38 @@ out = tf.keras.layers.Dense(1, activation='sigmoid')(drop2)
|
||||
|
||||
model = tf.keras.Model(inputs=base_vgg.input, outputs=out)
|
||||
model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=1e-4),
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
model.summary()
|
||||
|
||||
|
||||
# Output
|
||||
Model: "model_1"
|
||||
_________________________________________________________________
|
||||
Layer (type) Output Shape Param #
|
||||
Layer (type) Output Shape Param #
|
||||
=================================================================
|
||||
input_2 (InputLayer) [(None, 125, 125, 3)] 0
|
||||
input_2 (InputLayer) [(None, 125, 125, 3)] 0
|
||||
_________________________________________________________________
|
||||
block1_conv1 (Conv2D) (None, 125, 125, 64) 1792
|
||||
block1_conv1 (Conv2D) (None, 125, 125, 64) 1792
|
||||
_________________________________________________________________
|
||||
block1_conv2 (Conv2D) (None, 125, 125, 64) 36928
|
||||
block1_conv2 (Conv2D) (None, 125, 125, 64) 36928
|
||||
_________________________________________________________________
|
||||
...
|
||||
...
|
||||
_________________________________________________________________
|
||||
block5_pool (MaxPooling2D) (None, 3, 3, 512) 0
|
||||
block5_pool (MaxPooling2D) (None, 3, 3, 512) 0
|
||||
_________________________________________________________________
|
||||
flatten_1 (Flatten) (None, 4608) 0
|
||||
flatten_1 (Flatten) (None, 4608) 0
|
||||
_________________________________________________________________
|
||||
dense_3 (Dense) (None, 512) 2359808
|
||||
dense_3 (Dense) (None, 512) 2359808
|
||||
_________________________________________________________________
|
||||
dropout_2 (Dropout) (None, 512) 0
|
||||
dropout_2 (Dropout) (None, 512) 0
|
||||
_________________________________________________________________
|
||||
dense_4 (Dense) (None, 512) 262656
|
||||
dense_4 (Dense) (None, 512) 262656
|
||||
_________________________________________________________________
|
||||
dropout_3 (Dropout) (None, 512) 0
|
||||
dropout_3 (Dropout) (None, 512) 0
|
||||
_________________________________________________________________
|
||||
dense_5 (Dense) (None, 1) 513
|
||||
dense_5 (Dense) (None, 1) 513
|
||||
=================================================================
|
||||
Total params: 22,647,361
|
||||
Trainable params: 2,622,977
|
||||
@ -540,45 +536,42 @@ Non-trainable params: 20,024,384
|
||||
_________________________________________________________________
|
||||
```
|
||||
|
||||
输出是很明白的,在我们的模型中我们有了很多层,我们将只利用 VGG-19 模型的冻结层作为特征提取器。你可以使用下列代码来验证我们的模型有多少层是实际训练的,我们的网络中总共存在多少层。
|
||||
|
||||
从整个输出可以明显看出,在我们的模型中我们有了很多层,我们将只利用 VGG-19 模型的冻结层作为特征提取器。你可以使用下列代码来验证我们的模型有多少层是实际可训练的,以及我们的网络中总共存在多少层。
|
||||
|
||||
```
|
||||
print("Total Layers:", len(model.layers))
|
||||
print("Total trainable layers:",
|
||||
sum([1 for l in model.layers if l.trainable]))
|
||||
print("Total trainable layers:",
|
||||
sum([1 for l in model.layers if l.trainable]))
|
||||
|
||||
# Output
|
||||
Total Layers: 28
|
||||
Total trainable layers: 6
|
||||
```
|
||||
|
||||
我们将使用和我们之前的模型相似的配置和回调来训练我们的模型。参考 [我的 GitHub 仓库][24] 获取训练模型的完整代码。我们观察下列显示模型精确度和损失曲线。
|
||||
我们将使用和我们之前的模型相似的配置和回调来训练我们的模型。参考[我的 GitHub 仓库][24]以获取训练模型的完整代码。我们观察下列图表,以显示模型精确度和损失曲线。
|
||||
|
||||
![Learning curves for frozen pre-trained CNN][25]
|
||||
|
||||
冻结的预训练的 CNN 的学习曲线
|
||||
|
||||
这显示了我们的模型没有像我们的基础 CNN 模型那样过拟合,但是性能有点不如我们的基础的 CNN 模型。让我们保存这个模型用户将来的评估。
|
||||
*冻结的预训练的 CNN 的学习曲线*
|
||||
|
||||
这表明我们的模型没有像我们的基础 CNN 模型那样过拟合,但是性能有点不如我们的基础的 CNN 模型。让我们保存这个模型,以备将来的评估。
|
||||
|
||||
```
|
||||
`model.save('vgg_frozen.h5')`
|
||||
model.save('vgg_frozen.h5')
|
||||
```
|
||||
|
||||
#### 模型 3:使用图像增强来微调预训练的模型
|
||||
|
||||
在我们的最后一个模型中,我们微调预定义好的 VGG-19 模型的最后两个块中层的位权。我们同样引入图像增强的概念。图像增强背后的想法和名字一样。我们从训练数据集中载入已存在的图像,并且应用转换操作,例如旋转,裁剪,转换,放大缩小,等等,来产生新的,改变的版本。由于这些随机的转换,我们每次获取到的图像不一样。我们将应用一个在 **tf.keras** 的优秀的工具叫做 **ImageDataGenerator** 来帮助构建图像增强器。
|
||||
|
||||
在我们的最后一个模型中,我们将在预定义好的 VGG-19 模型的最后两个块中微调层的位权。我们同样引入了图像增强的概念。图像增强背后的想法和其名字一样。我们从训练数据集中载入现有图像,并且应用转换操作,例如旋转、裁剪、转换、放大缩小等等,来产生新的、改变过的版本。由于这些随机转换,我们每次获取到的图像不一样。我们将应用 tf.keras 中的一个名为 ImageDataGenerator 的优秀工具来帮助构建图像增强器。
|
||||
|
||||
```
|
||||
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255,
|
||||
zoom_range=0.05,
|
||||
rotation_range=25,
|
||||
width_shift_range=0.05,
|
||||
height_shift_range=0.05,
|
||||
shear_range=0.05, horizontal_flip=True,
|
||||
fill_mode='nearest')
|
||||
zoom_range=0.05,
|
||||
rotation_range=25,
|
||||
width_shift_range=0.05,
|
||||
height_shift_range=0.05,
|
||||
shear_range=0.05, horizontal_flip=True,
|
||||
fill_mode='nearest')
|
||||
|
||||
val_datagen = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255)
|
||||
|
||||
@ -587,13 +580,12 @@ train_generator = train_datagen.flow(train_data, train_labels_enc, batch_size=BA
|
||||
val_generator = val_datagen.flow(val_data, val_labels_enc, batch_size=BATCH_SIZE, shuffle=False)
|
||||
```
|
||||
|
||||
我们不会应用任何转换在我们的验证数据集上(除非是调整大小,它是强制性适应的)因为我们将在每个时期来评估我们的模型性能。对于在传输学习上下文中的图像增强的详细解释,请自由查看我们上述引用的[文章][18]。让我们从一批图像增强转换中查看一些样本结果。
|
||||
|
||||
我们不会对我们的验证数据集应用任何转换(除非是调整大小,因为这是必须的),因为我们将使用它评估每个纪元的模型性能。对于在传输学习环境中的图像增强的详细解释,请随时查看我上面引用的[文章][18]。让我们从一批图像增强转换中查看一些样本结果。
|
||||
|
||||
```
|
||||
img_id = 0
|
||||
sample_generator = train_datagen.flow(train_data[img_id:img_id+1], train_labels[img_id:img_id+1],
|
||||
batch_size=1)
|
||||
batch_size=1)
|
||||
sample = [next(sample_generator) for i in range(0,5)]
|
||||
fig, ax = plt.subplots(1,5, figsize=(16, 6))
|
||||
print('Labels:', [item[1][0] for item in sample])
|
||||
@ -602,24 +594,23 @@ l = [ax[i].imshow(sample[i][0][0]) for i in range(0,5)]
|
||||
|
||||
![Sample augmented images][26]
|
||||
|
||||
你可以清晰的看到与之前的输出中我们图像的轻微变化。我们现在构建我们的学习模型,确保 VGG-19 模型的最后两块是可以训练的。
|
||||
|
||||
你可以清晰的看到与之前的输出的我们图像的轻微变化。我们现在构建我们的学习模型,确保 VGG-19 模型的最后两块是可以训练的。
|
||||
|
||||
```
|
||||
vgg = tf.keras.applications.vgg19.VGG19(include_top=False, weights='imagenet',
|
||||
input_shape=INPUT_SHAPE)
|
||||
vgg = tf.keras.applications.vgg19.VGG19(include_top=False, weights='imagenet',
|
||||
input_shape=INPUT_SHAPE)
|
||||
# Freeze the layers
|
||||
vgg.trainable = True
|
||||
|
||||
set_trainable = False
|
||||
for layer in vgg.layers:
|
||||
if layer.name in ['block5_conv1', 'block4_conv1']:
|
||||
set_trainable = True
|
||||
if set_trainable:
|
||||
layer.trainable = True
|
||||
else:
|
||||
layer.trainable = False
|
||||
|
||||
if layer.name in ['block5_conv1', 'block4_conv1']:
|
||||
set_trainable = True
|
||||
if set_trainable:
|
||||
layer.trainable = True
|
||||
else:
|
||||
layer.trainable = False
|
||||
|
||||
base_vgg = vgg
|
||||
base_out = base_vgg.output
|
||||
pool_out = tf.keras.layers.Flatten()(base_out)
|
||||
@ -632,31 +623,32 @@ out = tf.keras.layers.Dense(1, activation='sigmoid')(drop2)
|
||||
|
||||
model = tf.keras.Model(inputs=base_vgg.input, outputs=out)
|
||||
model.compile(optimizer=tf.keras.optimizers.RMSprop(lr=1e-5),
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
loss='binary_crossentropy',
|
||||
metrics=['accuracy'])
|
||||
|
||||
print("Total Layers:", len(model.layers))
|
||||
print("Total trainable layers:", sum([1 for l in model.layers if l.trainable]))
|
||||
|
||||
|
||||
# Output
|
||||
Total Layers: 28
|
||||
Total trainable layers: 16
|
||||
```
|
||||
|
||||
在我们的模型中我们降低了学习率,因为我们微调的时候不想在预训练的数据集上做大的位权更新。模型的训练过程可能有轻微的不同,因为我们使用了数据生成器,因此我们应用了 **fit_generator(...)** 函数。
|
||||
|
||||
在我们的模型中我们降低了学习率,因为我们不想在微调的时候对预训练的层做大的位权更新。模型的训练过程可能有轻微的不同,因为我们使用了数据生成器,因此我们将应用 `fit_generator(...)` 函数。
|
||||
|
||||
```
|
||||
tensorboard_callback = tf.keras.callbacks.TensorBoard(logdir, histogram_freq=1)
|
||||
reduce_lr = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5,
|
||||
patience=2, min_lr=0.000001)
|
||||
patience=2, min_lr=0.000001)
|
||||
|
||||
callbacks = [reduce_lr, tensorboard_callback]
|
||||
train_steps_per_epoch = train_generator.n // train_generator.batch_size
|
||||
val_steps_per_epoch = val_generator.n // val_generator.batch_size
|
||||
history = model.fit_generator(train_generator, steps_per_epoch=train_steps_per_epoch, epochs=EPOCHS,
|
||||
validation_data=val_generator, validation_steps=val_steps_per_epoch,
|
||||
verbose=1)
|
||||
validation_data=val_generator, validation_steps=val_steps_per_epoch,
|
||||
verbose=1)
|
||||
|
||||
|
||||
# Output
|
||||
Epoch 1/25
|
||||
@ -675,21 +667,20 @@ Epoch 25/25
|
||||
|
||||
![Learning curves for fine-tuned pre-trained CNN][27]
|
||||
|
||||
微调预训练的 CNN 的学习曲线
|
||||
*微调过的预训练 CNN 的学习曲线*
|
||||
|
||||
让我们保存这个模型,因此我们能够在测试集上使用。
|
||||
|
||||
|
||||
```
|
||||
`model.save('vgg_finetuned.h5')`
|
||||
model.save('vgg_finetuned.h5')
|
||||
```
|
||||
|
||||
这完成了我们的模型训练阶段。我们准备好在测试集上测试我们模型的性能。
|
||||
这就完成了我们的模型训练阶段。现在我们准备好了在测试集上测试我们模型的性能。
|
||||
|
||||
### 深度学习模型性能评估
|
||||
|
||||
我们将评估我们在训练阶段构建的三个模型,通过在我们的测试集上做预测,因为仅仅验证是不够的!我们同样构建了一个检测工具模块叫做 **model_evaluation_utils**,我们可以使用相关分类指标用来评估使用我们深度学习模型的性能。第一步是测量我们的数据集。
|
||||
|
||||
我们将通过在我们的测试集上做预测来评估我们在训练阶段构建的三个模型,因为仅仅验证是不够的!我们同样构建了一个检测工具模块叫做 `model_evaluation_utils`,我们可以使用相关分类指标用来评估使用我们深度学习模型的性能。第一步是扩展我们的数据集。
|
||||
|
||||
```
|
||||
test_imgs_scaled = test_data / 255.
|
||||
@ -701,7 +692,6 @@ test_imgs_scaled.shape, test_labels.shape
|
||||
|
||||
下一步包括载入我们保存的深度学习模型,在测试集上预测。
|
||||
|
||||
|
||||
```
|
||||
# Load Saved Deep Learning Models
|
||||
basic_cnn = tf.keras.models.load_model('./basic_cnn.h5')
|
||||
@ -713,16 +703,15 @@ basic_cnn_preds = basic_cnn.predict(test_imgs_scaled, batch_size=512)
|
||||
vgg_frz_preds = vgg_frz.predict(test_imgs_scaled, batch_size=512)
|
||||
vgg_ft_preds = vgg_ft.predict(test_imgs_scaled, batch_size=512)
|
||||
|
||||
basic_cnn_pred_labels = le.inverse_transform([1 if pred > 0.5 else 0
|
||||
for pred in basic_cnn_preds.ravel()])
|
||||
vgg_frz_pred_labels = le.inverse_transform([1 if pred > 0.5 else 0
|
||||
for pred in vgg_frz_preds.ravel()])
|
||||
vgg_ft_pred_labels = le.inverse_transform([1 if pred > 0.5 else 0
|
||||
for pred in vgg_ft_preds.ravel()])
|
||||
basic_cnn_pred_labels = le.inverse_transform([1 if pred > 0.5 else 0
|
||||
for pred in basic_cnn_preds.ravel()])
|
||||
vgg_frz_pred_labels = le.inverse_transform([1 if pred > 0.5 else 0
|
||||
for pred in vgg_frz_preds.ravel()])
|
||||
vgg_ft_pred_labels = le.inverse_transform([1 if pred > 0.5 else 0
|
||||
for pred in vgg_ft_preds.ravel()])
|
||||
```
|
||||
|
||||
下一步是应用我们的 **model_evaluation_utils** 模块根据相应分类指标来检查每个模块的性能。
|
||||
|
||||
下一步是应用我们的 `model_evaluation_utils` 模块根据相应分类指标来检查每个模块的性能。
|
||||
|
||||
```
|
||||
import model_evaluation_utils as meu
|
||||
@ -732,30 +721,30 @@ basic_cnn_metrics = meu.get_metrics(true_labels=test_labels, predicted_labels=ba
|
||||
vgg_frz_metrics = meu.get_metrics(true_labels=test_labels, predicted_labels=vgg_frz_pred_labels)
|
||||
vgg_ft_metrics = meu.get_metrics(true_labels=test_labels, predicted_labels=vgg_ft_pred_labels)
|
||||
|
||||
pd.DataFrame([basic_cnn_metrics, vgg_frz_metrics, vgg_ft_metrics],
|
||||
index=['Basic CNN', 'VGG-19 Frozen', 'VGG-19 Fine-tuned'])
|
||||
pd.DataFrame([basic_cnn_metrics, vgg_frz_metrics, vgg_ft_metrics],
|
||||
index=['Basic CNN', 'VGG-19 Frozen', 'VGG-19 Fine-tuned'])
|
||||
```
|
||||
|
||||
![Model accuracy][28]
|
||||
|
||||
看起来我们的第三个模型在我们的测试集上执行的最好,给出了一个模型精确性为 96% 的 F1得分,比起上述我们早期引用的研究论文和文章中提及的复杂的模型是相当好的。
|
||||
看起来我们的第三个模型在我们的测试集上执行的最好,给出了一个模型精确性为 96% 的 F1 得分,这非常好,与我们之前提到的研究论文和文章中的更复杂的模型相当。
|
||||
|
||||
### 总结
|
||||
|
||||
疟疾检测不是一个简单的程序,全球的合格的人员的可获得性在样例诊断和治疗当中是一个严重的问题。我们看到一个关于疟疾的有趣的真实世界的医学影像案例。易于构建的,开源的技术利用 AI 在检测疟疾方面可以给我们最先进的精确性,因此允许 AI 对社会是有益的。
|
||||
疟疾检测不是一个简单的过程,全球的合格人员的不足在病例诊断和治疗当中是一个严重的问题。我们研究了一个关于疟疾的有趣的真实世界的医学影像案例。利用 AI 的、易于构建的、开源的技术在检测疟疾方面可以为我们提供最先进的精确性,因此使 AI 具有社会效益。
|
||||
|
||||
我鼓励你检查这片文章中提到的文章和研究论文,没有它们,我就不能形成概念并写出来。如果你对运行和采纳这些技术感兴趣,本篇文章所有的代码都可以在[我的 GitHub 仓库][24]获得。记得从[官方网站][11]下载数据。
|
||||
我鼓励你查看这篇文章中提到的文章和研究论文,没有它们,我就不能形成概念并写出来。如果你对运行和采纳这些技术感兴趣,本篇文章所有的代码都可以在[我的 GitHub 仓库][24]获得。记得从[官方网站][11]下载数据。
|
||||
|
||||
让我们希望在健康医疗方面更多的采纳开源的 AI 能力,使它在世界范围内变得便宜些,易用些。
|
||||
让我们希望在健康医疗方面更多的采纳开源的 AI 能力,使它在世界范围内变得更便宜、更易用。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/detecting-malaria-deep-learning
|
||||
|
||||
作者:[Dipanjan (DJ) Sarkar (Red Hat)][a]
|
||||
作者:[Dipanjan (DJ) Sarkar][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[warmfrog](https://github.com/warmfrog)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,578 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (FSSlc)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10845-1.html)
|
||||
[#]: subject: (Inter-process communication in Linux: Using pipes and message queues)
|
||||
[#]: via: (https://opensource.com/article/19/4/interprocess-communication-linux-channels)
|
||||
[#]: author: (Marty Kalin https://opensource.com/users/mkalindepauledu)
|
||||
|
||||
Linux 下的进程间通信:使用管道和消息队列
|
||||
======
|
||||
|
||||
> 学习在 Linux 中进程是如何与其他进程进行同步的。
|
||||
|
||||
![Chat bubbles][1]
|
||||
|
||||
本篇是 Linux 下[进程间通信][2](IPC)系列的第二篇文章。[第一篇文章][3] 聚焦于通过共享文件和共享内存段这样的共享存储来进行 IPC。这篇文件的重点将转向管道,它是连接需要通信的进程之间的通道。管道拥有一个*写端*用于写入字节数据,还有一个*读端*用于按照先入先出的顺序读入这些字节数据。而这些字节数据可能代表任何东西:数字、员工记录、数字电影等等。
|
||||
|
||||
管道有两种类型,命名管道和无名管道,都可以交互式的在命令行或程序中使用它们;相关的例子在下面展示。这篇文章也将介绍内存队列,尽管它们有些过时了,但它们不应该受这样的待遇。
|
||||
|
||||
在本系列的第一篇文章中的示例代码承认了在 IPC 中可能受到竞争条件(不管是基于文件的还是基于内存的)的威胁。自然地我们也会考虑基于管道的 IPC 的安全并发问题,这个也将在本文中提及。针对管道和内存队列的例子将会使用 POSIX 推荐使用的 API,POSIX 的一个核心目标就是线程安全。
|
||||
|
||||
请查看一些 [mq_open 函数的 man 页][4],这个函数属于内存队列的 API。这个 man 页中有关 [特性][5] 的章节带有一个小表格:
|
||||
|
||||
接口 | 特性 | 值
|
||||
---|---|---
|
||||
`mq_open()` | 线程安全 | MT-Safe
|
||||
|
||||
上面的 MT-Safe(MT 指的是<ruby>多线程<rt>multi-threaded</rt></ruby>)意味着 `mq_open` 函数是线程安全的,进而暗示是进程安全的:一个进程的执行和它的一个线程执行的过程类似,假如竞争条件不会发生在处于*相同*进程的线程中,那么这样的条件也不会发生在处于不同进程的线程中。MT-Safe 特性保证了调用 `mq_open` 时不会出现竞争条件。一般来说,基于通道的 IPC 是并发安全的,尽管在下面例子中会出现一个有关警告的注意事项。
|
||||
|
||||
### 无名管道
|
||||
|
||||
首先让我们通过一个特意构造的命令行例子来展示无名管道是如何工作的。在所有的现代系统中,符号 `|` 在命令行中都代表一个无名管道。假设我们的命令行提示符为 `%`,接下来考虑下面的命令:
|
||||
|
||||
```shell
|
||||
## 写入方在 | 左边,读取方在右边
|
||||
% sleep 5 | echo "Hello, world!"
|
||||
```
|
||||
|
||||
`sleep` 和 `echo` 程序以不同的进程执行,无名管道允许它们进行通信。但是上面的例子被特意设计为没有通信发生。问候语 “Hello, world!” 出现在屏幕中,然后过了 5 秒后,命令行返回,暗示 `sleep` 和 `echo` 进程都已经结束了。这期间发生了什么呢?
|
||||
|
||||
在命令行中的竖线 `|` 的语法中,左边的进程(`sleep`)是写入方,右边的进程(`echo`)为读取方。默认情况下,读取方将会阻塞,直到从通道中能够读取到字节数据,而写入方在写完它的字节数据后,将发送 <ruby>流已终止<rt>end-of-stream</rt></ruby>的标志。(即便写入方过早终止了,一个流已终止的标志还是会发给读取方。)无名管道将保持到写入方和读取方都停止的那个时刻。
|
||||
|
||||
在上面的例子中,`sleep` 进程并没有向通道写入任何的字节数据,但在 5 秒后就终止了,这时将向通道发送一个流已终止的标志。与此同时,`echo` 进程立即向标准输出(屏幕)写入问候语,因为这个进程并不从通道中读入任何字节,所以它并没有等待。一旦 `sleep` 和 `echo` 进程都终止了,不会再用作通信的无名管道将会消失然后返回命令行提示符。
|
||||
|
||||
下面这个更加实用的示例将使用两个无名管道。我们假定文件 `test.dat` 的内容如下:
|
||||
|
||||
```
|
||||
this
|
||||
is
|
||||
the
|
||||
way
|
||||
the
|
||||
world
|
||||
ends
|
||||
```
|
||||
|
||||
下面的命令:
|
||||
|
||||
```
|
||||
% cat test.dat | sort | uniq
|
||||
```
|
||||
|
||||
会将 `cat`(<ruby>连接<rt>concatenate</rt></ruby>的缩写)进程的输出通过管道传给 `sort` 进程以生成排序后的输出,然后将排序后的输出通过管道传给 `uniq` 进程以消除重复的记录(在本例中,会将两次出现的 “the” 缩减为一个):
|
||||
|
||||
```
|
||||
ends
|
||||
is
|
||||
the
|
||||
this
|
||||
way
|
||||
world
|
||||
```
|
||||
|
||||
下面展示的情景展示的是一个带有两个进程的程序通过一个无名管道通信来进行通信。
|
||||
|
||||
#### 示例 1. 两个进程通过一个无名管道来进行通信
|
||||
|
||||
```c
|
||||
#include <sys/wait.h> /* wait */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> /* exit functions */
|
||||
#include <unistd.h> /* read, write, pipe, _exit */
|
||||
#include <string.h>
|
||||
|
||||
#define ReadEnd 0
|
||||
#define WriteEnd 1
|
||||
|
||||
void report_and_exit(const char* msg) {
|
||||
perror(msg);
|
||||
exit(-1); /** failure **/
|
||||
}
|
||||
|
||||
int main() {
|
||||
int pipeFDs[2]; /* two file descriptors */
|
||||
char buf; /* 1-byte buffer */
|
||||
const char* msg = "Nature's first green is gold\n"; /* bytes to write */
|
||||
|
||||
if (pipe(pipeFDs) < 0) report_and_exit("pipeFD");
|
||||
pid_t cpid = fork(); /* fork a child process */
|
||||
if (cpid < 0) report_and_exit("fork"); /* check for failure */
|
||||
|
||||
if (0 == cpid) { /*** child ***/ /* child process */
|
||||
close(pipeFDs[WriteEnd]); /* child reads, doesn't write */
|
||||
|
||||
while (read(pipeFDs[ReadEnd], &buf, 1) > 0) /* read until end of byte stream */
|
||||
write(STDOUT_FILENO, &buf, sizeof(buf)); /* echo to the standard output */
|
||||
|
||||
close(pipeFDs[ReadEnd]); /* close the ReadEnd: all done */
|
||||
_exit(0); /* exit and notify parent at once */
|
||||
}
|
||||
else { /*** parent ***/
|
||||
close(pipeFDs[ReadEnd]); /* parent writes, doesn't read */
|
||||
|
||||
write(pipeFDs[WriteEnd], msg, strlen(msg)); /* write the bytes to the pipe */
|
||||
close(pipeFDs[WriteEnd]); /* done writing: generate eof */
|
||||
|
||||
wait(NULL); /* wait for child to exit */
|
||||
exit(0); /* exit normally */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
上面名为 `pipeUN` 的程序使用系统函数 `fork` 来创建一个进程。尽管这个程序只有一个单一的源文件,在它正确执行的情况下将会发生多进程的情况。
|
||||
|
||||
> 下面的内容是对库函数 `fork` 如何工作的一个简要回顾:
|
||||
|
||||
> * `fork` 函数由*父*进程调用,在失败时返回 `-1` 给父进程。在 `pipeUN` 这个例子中,相应的调用是:
|
||||
|
||||
> ```c
|
||||
pid_t cpid = fork(); /* called in parent */
|
||||
```
|
||||
|
||||
> 函数调用后的返回值也被保存下来了。在这个例子中,保存在整数类型 `pid_t` 的变量 `cpid` 中。(每个进程有它自己的*进程 ID*,这是一个非负的整数,用来标记进程)。复刻一个新的进程可能会因为多种原因而失败,包括*进程表*满了的原因,这个结构由系统维持,以此来追踪进程状态。明确地说,僵尸进程假如没有被处理掉,将可能引起进程表被填满的错误。
|
||||
> * 假如 `fork` 调用成功,则它将创建一个新的子进程,向父进程返回一个值,向子进程返回另外的一个值。在调用 `fork` 后父进程和子进程都将执行相同的代码。(子进程继承了到此为止父进程中声明的所有变量的拷贝),特别地,一次成功的 `fork` 调用将返回如下的东西:
|
||||
> * 向子进程返回 `0`
|
||||
> * 向父进程返回子进程的进程 ID
|
||||
> * 在一次成功的 `fork` 调用后,一个 `if`/`else` 或等价的结构将会被用来隔离针对父进程和子进程的代码。在这个例子中,相应的声明为:
|
||||
|
||||
> ```c
|
||||
if (0 == cpid) { /*** child ***/
|
||||
...
|
||||
}
|
||||
else { /*** parent ***/
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
假如成功地复刻出了一个子进程,`pipeUN` 程序将像下面这样去执行。在一个整数的数列里:
|
||||
|
||||
```c
|
||||
int pipeFDs[2]; /* two file descriptors */
|
||||
```
|
||||
|
||||
来保存两个文件描述符,一个用来向管道中写入,另一个从管道中写入。(数组元素 `pipeFDs[0]` 是读端的文件描述符,元素 `pipeFDs[1]` 是写端的文件描述符。)在调用 `fork` 之前,对系统 `pipe` 函数的成功调用,将立刻使得这个数组获得两个文件描述符:
|
||||
|
||||
```c
|
||||
if (pipe(pipeFDs) < 0) report_and_exit("pipeFD");
|
||||
```
|
||||
|
||||
父进程和子进程现在都有了文件描述符的副本。但*分离关注点*模式意味着每个进程恰好只需要一个描述符。在这个例子中,父进程负责写入,而子进程负责读取,尽管这样的角色分配可以反过来。在 `if` 子句中的第一个语句将用于关闭管道的读端:
|
||||
|
||||
```c
|
||||
close(pipeFDs[WriteEnd]); /* called in child code */
|
||||
```
|
||||
|
||||
在父进程中的 `else` 子句将会关闭管道的读端:
|
||||
|
||||
```c
|
||||
close(pipeFDs[ReadEnd]); /* called in parent code */
|
||||
```
|
||||
|
||||
然后父进程将向无名管道中写入某些字节数据(ASCII 代码),子进程读取这些数据,然后向标准输出中回放它们。
|
||||
|
||||
在这个程序中还需要澄清的一点是在父进程代码中的 `wait` 函数。一旦被创建后,子进程很大程度上独立于它的父进程,正如简短的 `pipeUN` 程序所展示的那样。子进程可以执行任意的代码,而它们可能与父进程完全没有关系。但是,假如当子进程终止时,系统将会通过一个信号来通知父进程。
|
||||
|
||||
要是父进程在子进程之前终止又该如何呢?在这种情形下,除非采取了预防措施,子进程将会变成在进程表中的一个*僵尸*进程。预防措施有两大类型:第一种是让父进程去通知系统,告诉系统它对子进程的终止没有任何兴趣:
|
||||
|
||||
```c
|
||||
signal(SIGCHLD, SIG_IGN); /* in parent: ignore notification */
|
||||
```
|
||||
|
||||
第二种方法是在子进程终止时,让父进程执行一个 `wait`。这样就确保了父进程可以独立于子进程而存在。在 `pipeUN` 程序中使用了第二种方法,其中父进程的代码使用的是下面的调用:
|
||||
|
||||
```c
|
||||
wait(NULL); /* called in parent */
|
||||
```
|
||||
|
||||
这个对 `wait` 的调用意味着*一直等待直到任意一个子进程的终止发生*,因此在 `pipeUN` 程序中,只有一个子进程。(其中的 `NULL` 参数可以被替换为一个保存有子程序退出状态的整数变量的地址。)对于更细粒度的控制,还可以使用更灵活的 `waitpid` 函数,例如特别指定多个子进程中的某一个。
|
||||
|
||||
`pipeUN` 将会采取另一个预防措施。当父进程结束了等待,父进程将会调用常规的 `exit` 函数去退出。对应的,子进程将会调用 `_exit` 变种来退出,这类变种将快速跟踪终止相关的通知。在效果上,子进程会告诉系统立刻去通知父进程它的这个子进程已经终止了。
|
||||
|
||||
假如两个进程向相同的无名管道中写入内容,字节数据会交错吗?例如,假如进程 P1 向管道写入内容:
|
||||
|
||||
```
|
||||
foo bar
|
||||
```
|
||||
|
||||
同时进程 P2 并发地写入:
|
||||
|
||||
```
|
||||
baz baz
|
||||
```
|
||||
|
||||
到相同的管道,最后的结果似乎是管道中的内容将会是任意错乱的,例如像这样:
|
||||
|
||||
```
|
||||
baz foo baz bar
|
||||
```
|
||||
|
||||
只要没有写入超过 `PIPE_BUF` 字节,POSIX 标准就能确保写入不会交错。在 Linux 系统中, `PIPE_BUF` 的大小是 4096 字节。对于管道我更喜欢只有一个写入方和一个读取方,从而绕过这个问题。
|
||||
|
||||
### 命名管道
|
||||
|
||||
无名管道没有备份文件:系统将维持一个内存缓存来将字节数据从写方传给读方。一旦写方和读方终止,这个缓存将会被回收,进而无名管道消失。相反的,命名管道有备份文件和一个不同的 API。
|
||||
|
||||
下面让我们通过另一个命令行示例来了解命名管道的要点。下面是具体的步骤:
|
||||
|
||||
* 开启两个终端。这两个终端的工作目录应该相同。
|
||||
* 在其中一个终端中,键入下面的两个命令(命令行提示符仍然是 `%`,我的注释以 `##` 打头。):
|
||||
|
||||
```shell
|
||||
% mkfifo tester ## 创建一个备份文件,名为 tester
|
||||
% cat tester ## 将管道的内容输出到 stdout
|
||||
```
|
||||
|
||||
在最开始,没有任何东西会出现在终端中,因为到现在为止没有在命名管道中写入任何东西。
|
||||
* 在第二个终端中输入下面的命令:
|
||||
|
||||
```shell
|
||||
% cat > tester ## redirect keyboard input to the pipe
|
||||
hello, world! ## then hit Return key
|
||||
bye, bye ## ditto
|
||||
<Control-C> ## terminate session with a Control-C
|
||||
```
|
||||
|
||||
无论在这个终端中输入什么,它都会在另一个终端中显示出来。一旦键入 `Ctrl+C`,就会回到正常的命令行提示符,因为管道已经被关闭了。
|
||||
* 通过移除实现命名管道的文件来进行清理:
|
||||
|
||||
```shell
|
||||
% unlink tester
|
||||
```
|
||||
|
||||
正如 `mkfifo` 程序的名字所暗示的那样,命名管道也被叫做 FIFO,因为第一个进入的字节,就会第一个出,其他的类似。有一个名为 `mkfifo` 的库函数,用它可以在程序中创建一个命名管道,它将在下一个示例中被用到,该示例由两个进程组成:一个向命名管道写入,而另一个从该管道读取。
|
||||
|
||||
#### 示例 2. fifoWriter 程序
|
||||
|
||||
```c
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define MaxLoops 12000 /* outer loop */
|
||||
#define ChunkSize 16 /* how many written at a time */
|
||||
#define IntsPerChunk 4 /* four 4-byte ints per chunk */
|
||||
#define MaxZs 250 /* max microseconds to sleep */
|
||||
|
||||
int main() {
|
||||
const char* pipeName = "./fifoChannel";
|
||||
mkfifo(pipeName, 0666); /* read/write for user/group/others */
|
||||
int fd = open(pipeName, O_CREAT | O_WRONLY); /* open as write-only */
|
||||
if (fd < 0) return -1; /* can't go on */
|
||||
|
||||
int i;
|
||||
for (i = 0; i < MaxLoops; i++) { /* write MaxWrites times */
|
||||
int j;
|
||||
for (j = 0; j < ChunkSize; j++) { /* each time, write ChunkSize bytes */
|
||||
int k;
|
||||
int chunk[IntsPerChunk];
|
||||
for (k = 0; k < IntsPerChunk; k++)
|
||||
chunk[k] = rand();
|
||||
write(fd, chunk, sizeof(chunk));
|
||||
}
|
||||
usleep((rand() % MaxZs) + 1); /* pause a bit for realism */
|
||||
}
|
||||
|
||||
close(fd); /* close pipe: generates an end-of-stream marker */
|
||||
unlink(pipeName); /* unlink from the implementing file */
|
||||
printf("%i ints sent to the pipe.\n", MaxLoops * ChunkSize * IntsPerChunk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
上面的 `fifoWriter` 程序可以被总结为如下:
|
||||
|
||||
* 首先程序创建了一个命名管道用来写入数据:
|
||||
|
||||
```c
|
||||
mkfifo(pipeName, 0666); /* read/write perms for user/group/others */
|
||||
int fd = open(pipeName, O_CREAT | O_WRONLY);
|
||||
```
|
||||
|
||||
其中的 `pipeName` 是备份文件的名字,传递给 `mkfifo` 作为它的第一个参数。接着命名管道通过我们熟悉的 `open` 函数调用被打开,而这个函数将会返回一个文件描述符。
|
||||
* 在实现层面上,`fifoWriter` 不会一次性将所有的数据都写入,而是写入一个块,然后休息随机数目的微秒时间,接着再循环往复。总的来说,有 768000 个 4 字节整数值被写入到命名管道中。
|
||||
* 在关闭命名管道后,`fifoWriter` 也将使用 `unlink` 取消对该文件的连接。
|
||||
|
||||
```c
|
||||
close(fd); /* close pipe: generates end-of-stream marker */
|
||||
unlink(pipeName); /* unlink from the implementing file */
|
||||
```
|
||||
|
||||
一旦连接到管道的每个进程都执行了 `unlink` 操作后,系统将回收这些备份文件。在这个例子中,只有两个这样的进程 `fifoWriter` 和 `fifoReader`,它们都做了 `unlink` 操作。
|
||||
|
||||
这个两个程序应该在不同终端的相同工作目录中执行。但是 `fifoWriter` 应该在 `fifoReader` 之前被启动,因为需要 `fifoWriter` 去创建管道。然后 `fifoReader` 才能够获取到刚被创建的命名管道。
|
||||
|
||||
#### 示例 3. fifoReader 程序
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
unsigned is_prime(unsigned n) { /* not pretty, but efficient */
|
||||
if (n <= 3) return n > 1;
|
||||
if (0 == (n % 2) || 0 == (n % 3)) return 0;
|
||||
|
||||
unsigned i;
|
||||
for (i = 5; (i * i) <= n; i += 6)
|
||||
if (0 == (n % i) || 0 == (n % (i + 2))) return 0;
|
||||
|
||||
return 1; /* found a prime! */
|
||||
}
|
||||
|
||||
int main() {
|
||||
const char* file = "./fifoChannel";
|
||||
int fd = open(file, O_RDONLY);
|
||||
if (fd < 0) return -1; /* no point in continuing */
|
||||
unsigned count = 0, total = 0, primes_count = 0;
|
||||
|
||||
while (1) {
|
||||
int next;
|
||||
int i;
|
||||
|
||||
ssize_t count = read(fd, &next, sizeof(int));
|
||||
if (0 == count) break; /* end of stream */
|
||||
else if (count == sizeof(int)) { /* read a 4-byte int value */
|
||||
total++;
|
||||
if (is_prime(next)) primes_count++;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd); /* close pipe from read end */
|
||||
unlink(file); /* unlink from the underlying file */
|
||||
printf("Received ints: %u, primes: %u\n", total, primes_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
上面的 `fifoReader` 的内容可以总结为如下:
|
||||
|
||||
* 因为 `fifoWriter` 已经创建了命名管道,所以 `fifoReader` 只需要利用标准的 `open` 调用来通过备份文件来获取到管道中的内容:
|
||||
|
||||
```c
|
||||
const char* file = "./fifoChannel";
|
||||
int fd = open(file, O_RDONLY);
|
||||
```
|
||||
|
||||
这个文件的是以只读打开的。
|
||||
* 然后这个程序进入一个潜在的无限循环,在每次循环时,尝试读取 4 字节的块。`read` 调用:
|
||||
|
||||
```c
|
||||
ssize_t count = read(fd, &next, sizeof(int));
|
||||
```
|
||||
|
||||
返回 0 来暗示该流的结束。在这种情况下,`fifoReader` 跳出循环,关闭命名管道,并在终止前 `unlink` 备份文件。
|
||||
* 在读入 4 字节整数后,`fifoReader` 检查这个数是否为质数。这个操作代表了一个生产级别的读取器可能在接收到的字节数据上执行的逻辑操作。在示例运行中,在接收到的 768000 个整数中有 37682 个质数。
|
||||
|
||||
重复运行示例, `fifoReader` 将成功地读取 `fifoWriter` 写入的所有字节。这不是很让人惊讶的。这两个进程在相同的机器上执行,从而可以不用考虑网络相关的问题。命名管道是一个可信且高效的 IPC 机制,因而被广泛使用。
|
||||
|
||||
下面是这两个程序的输出,它们在不同的终端中启动,但处于相同的工作目录:
|
||||
|
||||
```shell
|
||||
% ./fifoWriter
|
||||
768000 ints sent to the pipe.
|
||||
###
|
||||
% ./fifoReader
|
||||
Received ints: 768000, primes: 37682
|
||||
```
|
||||
|
||||
### 消息队列
|
||||
|
||||
管道有着严格的先入先出行为:第一个被写入的字节将会第一个被读,第二个写入的字节将第二个被读,以此类推。消息队列可以做出相同的表现,但它又足够灵活,可以使得字节块可以不以先入先出的次序来接收。
|
||||
|
||||
正如它的名字所提示的那样,消息队列是一系列的消息,每个消息包含两部分:
|
||||
|
||||
* 荷载,一个字节序列(在 C 中是 char)
|
||||
* 类型,以一个正整数值的形式给定,类型用来分类消息,为了更灵活的回收
|
||||
|
||||
看一下下面对一个消息队列的描述,每个消息由一个整数类型标记:
|
||||
|
||||
```
|
||||
+-+ +-+ +-+ +-+
|
||||
sender--->|3|--->|2|--->|2|--->|1|--->receiver
|
||||
+-+ +-+ +-+ +-+
|
||||
```
|
||||
|
||||
在上面展示的 4 个消息中,标记为 1 的是开头,即最接近接收端,然后另个标记为 2 的消息,最后接着一个标记为 3 的消息。假如按照严格的 FIFO 行为执行,消息将会以 1-2-2-3 这样的次序被接收。但是消息队列允许其他收取次序。例如,消息可以被接收方以 3-2-1-2 的次序接收。
|
||||
|
||||
`mqueue` 示例包含两个程序,`sender` 将向消息队列中写入数据,而 `receiver` 将从这个队列中读取数据。这两个程序都包含的头文件 `queue.h` 如下所示:
|
||||
|
||||
#### 示例 4. 头文件 queue.h
|
||||
|
||||
```c
|
||||
#define ProjectId 123
|
||||
#define PathName "queue.h" /* any existing, accessible file would do */
|
||||
#define MsgLen 4
|
||||
#define MsgCount 6
|
||||
|
||||
typedef struct {
|
||||
long type; /* must be of type long */
|
||||
char payload[MsgLen + 1]; /* bytes in the message */
|
||||
} queuedMessage;
|
||||
```
|
||||
|
||||
上面的头文件定义了一个名为 `queuedMessage` 的结构类型,它带有 `payload`(字节数组)和 `type`(整数)这两个域。该文件也定义了一些符号常数(使用 `#define` 语句),前两个常数被用来生成一个 `key`,而这个 `key` 反过来被用来获取一个消息队列的 ID。`ProjectId` 可以是任何正整数值,而 `PathName` 必须是一个存在的、可访问的文件,在这个示例中,指的是文件 `queue.h`。在 `sender` 和 `receiver` 中,它们都有的设定语句为:
|
||||
|
||||
```c
|
||||
key_t key = ftok(PathName, ProjectId); /* generate key */
|
||||
int qid = msgget(key, 0666 | IPC_CREAT); /* use key to get queue id */
|
||||
```
|
||||
|
||||
ID `qid` 在效果上是消息队列文件描述符的对应物。
|
||||
|
||||
#### 示例 5. sender 程序
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/msg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "queue.h"
|
||||
|
||||
void report_and_exit(const char* msg) {
|
||||
perror(msg);
|
||||
exit(-1); /* EXIT_FAILURE */
|
||||
}
|
||||
|
||||
int main() {
|
||||
key_t key = ftok(PathName, ProjectId);
|
||||
if (key < 0) report_and_exit("couldn't get key...");
|
||||
|
||||
int qid = msgget(key, 0666 | IPC_CREAT);
|
||||
if (qid < 0) report_and_exit("couldn't get queue id...");
|
||||
|
||||
char* payloads[] = {"msg1", "msg2", "msg3", "msg4", "msg5", "msg6"};
|
||||
int types[] = {1, 1, 2, 2, 3, 3}; /* each must be > 0 */
|
||||
int i;
|
||||
for (i = 0; i < MsgCount; i++) {
|
||||
/* build the message */
|
||||
queuedMessage msg;
|
||||
msg.type = types[i];
|
||||
strcpy(msg.payload, payloads[i]);
|
||||
|
||||
/* send the message */
|
||||
msgsnd(qid, &msg, sizeof(msg), IPC_NOWAIT); /* don't block */
|
||||
printf("%s sent as type %i\n", msg.payload, (int) msg.type);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
上面的 `sender` 程序将发送出 6 个消息,每两个为一个类型:前两个是类型 1,接着的连个是类型 2,最后的两个为类型 3。发送的语句:
|
||||
|
||||
```c
|
||||
msgsnd(qid, &msg, sizeof(msg), IPC_NOWAIT);
|
||||
```
|
||||
|
||||
被配置为非阻塞的(`IPC_NOWAIT` 标志),是因为这里的消息体量上都很小。唯一的危险在于一个完整的序列将可能导致发送失败,而这个例子不会。下面的 `receiver` 程序也将使用 `IPC_NOWAIT` 标志来接收消息。
|
||||
|
||||
#### 示例 6. receiver 程序
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/msg.h>
|
||||
#include <stdlib.h>
|
||||
#include "queue.h"
|
||||
|
||||
void report_and_exit(const char* msg) {
|
||||
perror(msg);
|
||||
exit(-1); /* EXIT_FAILURE */
|
||||
}
|
||||
|
||||
int main() {
|
||||
key_t key= ftok(PathName, ProjectId); /* key to identify the queue */
|
||||
if (key < 0) report_and_exit("key not gotten...");
|
||||
|
||||
int qid = msgget(key, 0666 | IPC_CREAT); /* access if created already */
|
||||
if (qid < 0) report_and_exit("no access to queue...");
|
||||
|
||||
int types[] = {3, 1, 2, 1, 3, 2}; /* different than in sender */
|
||||
int i;
|
||||
for (i = 0; i < MsgCount; i++) {
|
||||
queuedMessage msg; /* defined in queue.h */
|
||||
if (msgrcv(qid, &msg, sizeof(msg), types[i], MSG_NOERROR | IPC_NOWAIT) < 0)
|
||||
puts("msgrcv trouble...");
|
||||
printf("%s received as type %i\n", msg.payload, (int) msg.type);
|
||||
}
|
||||
|
||||
/** remove the queue **/
|
||||
if (msgctl(qid, IPC_RMID, NULL) < 0) /* NULL = 'no flags' */
|
||||
report_and_exit("trouble removing queue...");
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
这个 `receiver` 程序不会创建消息队列,尽管 API 尽管建议那样。在 `receiver` 中,对
|
||||
|
||||
```c
|
||||
int qid = msgget(key, 0666 | IPC_CREAT);
|
||||
```
|
||||
|
||||
的调用可能因为带有 `IPC_CREAT` 标志而具有误导性,但是这个标志的真实意义是*如果需要就创建,否则直接获取*。`sender` 程序调用 `msgsnd` 来发送消息,而 `receiver` 调用 `msgrcv` 来接收它们。在这个例子中,`sender` 以 1-1-2-2-3-3 的次序发送消息,但 `receiver` 接收它们的次序为 3-1-2-1-3-2,这显示消息队列没有被严格的 FIFO 行为所拘泥:
|
||||
|
||||
```shell
|
||||
% ./sender
|
||||
msg1 sent as type 1
|
||||
msg2 sent as type 1
|
||||
msg3 sent as type 2
|
||||
msg4 sent as type 2
|
||||
msg5 sent as type 3
|
||||
msg6 sent as type 3
|
||||
|
||||
% ./receiver
|
||||
msg5 received as type 3
|
||||
msg1 received as type 1
|
||||
msg3 received as type 2
|
||||
msg2 received as type 1
|
||||
msg6 received as type 3
|
||||
msg4 received as type 2
|
||||
```
|
||||
|
||||
上面的输出显示 `sender` 和 `receiver` 可以在同一个终端中启动。输出也显示消息队列是持久的,即便 `sender` 进程在完成创建队列、向队列写数据、然后退出的整个过程后,该队列仍然存在。只有在 `receiver` 进程显式地调用 `msgctl` 来移除该队列,这个队列才会消失:
|
||||
|
||||
```c
|
||||
if (msgctl(qid, IPC_RMID, NULL) < 0) /* remove queue */
|
||||
```
|
||||
|
||||
### 总结
|
||||
|
||||
管道和消息队列的 API 在根本上来说都是单向的:一个进程写,然后另一个进程读。当然还存在双向命名管道的实现,但我认为这个 IPC 机制在它最为简单的时候反而是最佳的。正如前面提到的那样,消息队列已经不大受欢迎了,尽管没有找到什么特别好的原因来解释这个现象;而队列仍然是 IPC 工具箱中的一个工具。这个快速的 IPC 工具箱之旅将以第 3 部分(通过套接字和信号来示例 IPC)来终结。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/interprocess-communication-linux-channels
|
||||
|
||||
作者:[Marty Kalin][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[FSSlc](https://github.com/FSSlc)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/mkalindepauledu
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/talk_chat_communication_team.png?itok=CYfZ_gE7 (Chat bubbles)
|
||||
[2]: https://en.wikipedia.org/wiki/Inter-process_communication
|
||||
[3]: https://linux.cn/article-10826-1.html
|
||||
[4]: http://man7.org/linux/man-pages/man2/mq_open.2.html
|
||||
[5]: http://man7.org/linux/man-pages/man2/mq_open.2.html#ATTRIBUTES
|
||||
[6]: http://www.opengroup.org/onlinepubs/009695399/functions/perror.html
|
||||
[7]: http://www.opengroup.org/onlinepubs/009695399/functions/exit.html
|
||||
[8]: http://www.opengroup.org/onlinepubs/009695399/functions/strlen.html
|
||||
[9]: http://www.opengroup.org/onlinepubs/009695399/functions/rand.html
|
||||
[10]: http://www.opengroup.org/onlinepubs/009695399/functions/printf.html
|
||||
[11]: http://www.opengroup.org/onlinepubs/009695399/functions/strcpy.html
|
||||
[12]: http://www.opengroup.org/onlinepubs/009695399/functions/puts.html
|
@ -1,16 +1,18 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (MjSeven)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10867-1.html)
|
||||
[#]: subject: (Building scalable social media sentiment analysis services in Python)
|
||||
[#]: via: (https://opensource.com/article/19/4/social-media-sentiment-analysis-python-scalable)
|
||||
[#]: author: (Michael McCune https://opensource.com/users/elmiko/users/jschlessman)
|
||||
|
||||
使用 Python 构建可扩展的社交媒体情感分析服务
|
||||
======
|
||||
学习如何使用 spaCy、vaderSentiment、Flask 和 Python 来为你的工作添加情感分析能力。
|
||||
![Tall building with windows][1]
|
||||
|
||||
> 学习如何使用 spaCy、vaderSentiment、Flask 和 Python 来为你的作品添加情感分析能力。
|
||||
|
||||
![Tall building with windows](https://img.linux.net.cn/data/attachment/album/201905/17/235241zr0cs4czu4psmrl6.jpg)
|
||||
|
||||
本系列的[第一部分][2]提供了情感分析工作原理的一些背景知识,现在让我们研究如何将这些功能添加到你的设计中。
|
||||
|
||||
@ -20,7 +22,7 @@
|
||||
|
||||
* 一个终端 shell
|
||||
* shell 中的 Python 语言二进制文件(3.4+ 版本)
|
||||
* 用于安装 Python 包的 **pip** 命令
|
||||
* 用于安装 Python 包的 `pip` 命令
|
||||
* (可选)一个 [Python 虚拟环境][3]使你的工作与系统隔离开来
|
||||
|
||||
#### 配置环境
|
||||
@ -53,33 +55,44 @@ Type "help", "copyright", "credits" or "license" for more information.
|
||||
>>>
|
||||
```
|
||||
|
||||
_(你的 Python 解释器版本打印可能与此不同。)_
|
||||
*(你的 Python 解释器版本打印可能与此不同。)*
|
||||
|
||||
1. 导入所需模块:
|
||||
```
|
||||
>>> import spacy
|
||||
>>> from vaderSentiment import vaderSentiment
|
||||
```
|
||||
2. 从 spaCy 加载英语语言模型:
|
||||
```
|
||||
>>> english = spacy.load("en_core_web_sm")
|
||||
```
|
||||
3. 处理一段文本。本例展示了一个非常简单的句子,我们希望它能给我们带来些许积极的情感:
|
||||
```
|
||||
>>> result = english("I like to eat applesauce with sugar and cinnamon.")
|
||||
```
|
||||
4. 从处理后的结果中收集句子。SpaCy 已识别并处理短语中的实体,这一步为每个句子生成情感(即时在本例中只有一个句子):
|
||||
```
|
||||
>>> sentences = [str(s) for s in result.sents]
|
||||
```
|
||||
5. 使用 vaderSentiments 创建一个分析器:
|
||||
```
|
||||
>>> analyzer = vaderSentiment.SentimentIntensityAnalyzer()
|
||||
```
|
||||
6. 对句子进行情感分析:
|
||||
```
|
||||
>>> sentiment = [analyzer.polarity_scores(str(s)) for s in sentences]
|
||||
```
|
||||
1、导入所需模块:
|
||||
|
||||
```
|
||||
>>> import spacy
|
||||
>>> from vaderSentiment import vaderSentiment
|
||||
```
|
||||
|
||||
2、从 spaCy 加载英语语言模型:
|
||||
|
||||
```
|
||||
>>> english = spacy.load("en_core_web_sm")
|
||||
```
|
||||
|
||||
3、处理一段文本。本例展示了一个非常简单的句子,我们希望它能给我们带来些许积极的情感:
|
||||
|
||||
```
|
||||
>>> result = english("I like to eat applesauce with sugar and cinnamon.")
|
||||
```
|
||||
|
||||
4、从处理后的结果中收集句子。SpaCy 已识别并处理短语中的实体,这一步为每个句子生成情感(即时在本例中只有一个句子):
|
||||
|
||||
```
|
||||
>>> sentences = [str(s) for s in result.sents]
|
||||
```
|
||||
|
||||
5、使用 vaderSentiments 创建一个分析器:
|
||||
|
||||
```
|
||||
>>> analyzer = vaderSentiment.SentimentIntensityAnalyzer()
|
||||
```
|
||||
|
||||
6、对句子进行情感分析:
|
||||
|
||||
```
|
||||
>>> sentiment = [analyzer.polarity_scores(str(s)) for s in sentences]
|
||||
```
|
||||
|
||||
`sentiment` 变量现在包含例句的极性分数。打印出这个值,看看它是如何分析这个句子的。
|
||||
|
||||
@ -90,7 +103,7 @@ _(你的 Python 解释器版本打印可能与此不同。)_
|
||||
|
||||
这个结构是什么意思?
|
||||
|
||||
表面上,这是一个只有一个字典对象的数组。如果有多个句子,那么每个句子都会对应一个字典对象。字典中有四个键对应不同类型的情感。**neg** 键表示负面情感,因为在本例中没有报告任何负面情感,**0.0** 值证明了这一点。**neu** 键表示中性情感,它的得分相当高,为**0.737**(最高为 **1.0**)。**pos** 键代表积极情感,得分适中,为 **0.263**。最后,**cmpound** 键代表文本的总体得分,它可以从负数到正数,**0.3612** 表示积极方面的情感多一点。
|
||||
表面上,这是一个只有一个字典对象的数组。如果有多个句子,那么每个句子都会对应一个字典对象。字典中有四个键对应不同类型的情感。`neg` 键表示负面情感,因为在本例中没有报告任何负面情感,`0.0` 值证明了这一点。`neu` 键表示中性情感,它的得分相当高,为 `0.737`(最高为 `1.0`)。`pos` 键代表积极情感,得分适中,为 `0.263`。最后,`cmpound` 键代表文本的总体得分,它可以从负数到正数,`0.3612` 表示积极方面的情感多一点。
|
||||
|
||||
要查看这些值可能如何变化,你可以使用已输入的代码做一个小实验。以下代码块显示了如何对类似句子的情感评分的评估。
|
||||
|
||||
@ -113,9 +126,9 @@ _(你的 Python 解释器版本打印可能与此不同。)_
|
||||
#### 前提条件
|
||||
|
||||
* 一个终端 shell
|
||||
* shell 中的 Python 语言二进制文件(3.4+版本)
|
||||
* 安装 Python 包的 **pip** 命令
|
||||
* **curl** 命令
|
||||
* shell 中的 Python 语言二进制文件(3.4+ 版本)
|
||||
* 安装 Python 包的 `pip` 命令
|
||||
* `curl` 命令
|
||||
* 一个文本编辑器
|
||||
* (可选) 一个 [Python 虚拟环境][3]使你的工作与系统隔离开来
|
||||
|
||||
@ -123,19 +136,21 @@ _(你的 Python 解释器版本打印可能与此不同。)_
|
||||
|
||||
这个环境几乎与上一节中的环境相同,唯一的区别是在 Python 环境中添加了 Flask 包。
|
||||
|
||||
1. 安装所需依赖项:
|
||||
```
|
||||
pip install spacy vaderSentiment flask
|
||||
```
|
||||
2. 安装 spaCy 的英语语言模型:
|
||||
```
|
||||
python -m spacy download en_core_web_sm
|
||||
```
|
||||
1、安装所需依赖项:
|
||||
|
||||
```
|
||||
pip install spacy vaderSentiment flask
|
||||
```
|
||||
|
||||
2、安装 spaCy 的英语语言模型:
|
||||
|
||||
```
|
||||
python -m spacy download en_core_web_sm
|
||||
```
|
||||
|
||||
#### 创建应用程序文件
|
||||
|
||||
打开编辑器,创建一个名为 **app.py** 的文件。添加以下内容 _(不用担心,我们将解释每一行)_ :
|
||||
打开编辑器,创建一个名为 `app.py` 的文件。添加以下内容 *(不用担心,我们将解释每一行)*:
|
||||
|
||||
|
||||
```
|
||||
@ -179,10 +194,9 @@ analyzer = vader.SentimentIntensityAnalyzer()
|
||||
english = spacy.load("en_core_web_sm")
|
||||
```
|
||||
|
||||
接下来的三行代码创建了一些全局变量。第一个变量 **app**,它是 Flask 用于创建 HTTP 路由的主要入口点。第二个变量 **analyzer** 与上一个示例中使用的类型相同,它将用于生成情感分数。最后一个变量 **english** 也与上一个示例中使用的类型相同,它将用于注释和标记初始文本输入。
|
||||
|
||||
你可能想知道为什么全局声明这些变量。对于 **app** 变量,这是许多 Flask 应用程序的标准过程。但是,对于 **analyzer** 和 **english** 变量,将它们设置为全局变量的决定是基于与所涉及的类关联的加载时间。虽然加载时间可能看起来很短,但是当它在 HTTP 服务器的上下文中运行时,这些延迟会对性能产生负面影响。
|
||||
接下来的三行代码创建了一些全局变量。第一个变量 `app`,它是 Flask 用于创建 HTTP 路由的主要入口点。第二个变量 `analyzer` 与上一个示例中使用的类型相同,它将用于生成情感分数。最后一个变量 `english` 也与上一个示例中使用的类型相同,它将用于注释和标记初始文本输入。
|
||||
|
||||
你可能想知道为什么全局声明这些变量。对于 `app` 变量,这是许多 Flask 应用程序的标准过程。但是,对于 `analyzer` 和 `english` 变量,将它们设置为全局变量的决定是基于与所涉及的类关联的加载时间。虽然加载时间可能看起来很短,但是当它在 HTTP 服务器的上下文中运行时,这些延迟会对性能产生负面影响。
|
||||
|
||||
```
|
||||
def get_sentiments(text):
|
||||
@ -192,8 +206,7 @@ def get_sentiments(text):
|
||||
return sentiments
|
||||
```
|
||||
|
||||
这部分是服务的核心 -- 一个用于从一串文本生成情感值的函数。你可以看到此函数中的操作对应于你之前在 Python 解释器中运行的命令。这里它们被封装在一个函数定义中,**text** 源作为文本变量传入,最后 **sentiments** 变量返回给调用者。
|
||||
|
||||
这部分是服务的核心 —— 一个用于从一串文本生成情感值的函数。你可以看到此函数中的操作对应于你之前在 Python 解释器中运行的命令。这里它们被封装在一个函数定义中,`text` 源作为文本变量传入,最后 `sentiments` 变量返回给调用者。
|
||||
|
||||
```
|
||||
@app.route("/", methods=["POST", "GET"])
|
||||
@ -206,11 +219,11 @@ def index():
|
||||
return flask.json.dumps(sentiments)
|
||||
```
|
||||
|
||||
源文件的最后一个函数包含了指导 Flask 如何为服务配置 HTTP 服务器的逻辑。它从一行开始,该行将 HTTP 路由 **/** 与请求方法 **POST** 和 **GET** 相关联。
|
||||
源文件的最后一个函数包含了指导 Flask 如何为服务配置 HTTP 服务器的逻辑。它从一行开始,该行将 HTTP 路由 `/` 与请求方法 `POST` 和 `GET` 相关联。
|
||||
|
||||
在函数定义行之后,**if** 子句将检测请求方法是否为 **GET**。如果用户向服务发送此请求,那么下面的行将返回一条指示如何访问服务器的文本消息。这主要是为了方便最终用户。
|
||||
在函数定义行之后,`if` 子句将检测请求方法是否为 `GET`。如果用户向服务发送此请求,那么下面的行将返回一条指示如何访问服务器的文本消息。这主要是为了方便最终用户。
|
||||
|
||||
下一行使用 **flask.request** 对象来获取请求的主体,该主体应包含要处理的文本字符串。**decode** 函数将字节数组转换为可用的格式化字符串。经过解码的文本消息被传递给 **get_sentiments** 函数以生成情感分数。最后,分数通过 HTTP 框架返回给用户。
|
||||
下一行使用 `flask.request` 对象来获取请求的主体,该主体应包含要处理的文本字符串。`decode` 函数将字节数组转换为可用的格式化字符串。经过解码的文本消息被传递给 `get_sentiments` 函数以生成情感分数。最后,分数通过 HTTP 框架返回给用户。
|
||||
|
||||
你现在应该保存文件,如果尚未保存,那么返回 shell。
|
||||
|
||||
@ -222,7 +235,7 @@ def index():
|
||||
FLASK_APP=app.py flask run
|
||||
```
|
||||
|
||||
现在,你将在 shell 中看到来自服务器的一些输出,并且服务器将处于运行状态。要测试服务器是否正在运行,你需要打开第二个 shell 并使用 **curl** 命令。
|
||||
现在,你将在 shell 中看到来自服务器的一些输出,并且服务器将处于运行状态。要测试服务器是否正在运行,你需要打开第二个 shell 并使用 `curl` 命令。
|
||||
|
||||
首先,输入以下命令检查是否打印了指令信息:
|
||||
|
||||
@ -252,11 +265,11 @@ curl http://localhost:5000 --header "Content-Type: application/json" --data "I l
|
||||
|
||||
### 继续探索
|
||||
|
||||
现在你已经了解了自然语言处理和情感分析背后的原理和机制,下面是进一步发现探索主题的一些方法。
|
||||
现在你已经了解了自然语言处理和情感分析背后的原理和机制,下面是进一步发现探索该主题的一些方法。
|
||||
|
||||
#### 在 OpenShift 上创建流式情感分析器
|
||||
|
||||
虽然创建本地应用程序来研究情绪分析很方便,但是接下来需要能够部署应用程序以实现更广泛的用途。按照[ Radnaalytics.io][11] 提供的指导和代码进行操作,你将学习如何创建一个情感分析仪,可以集装箱化并部署到 Kubernetes 平台。你还将了解如何将 APache Kafka 用作事件驱动消息传递的框架,以及如何将 Apache Spark 用作情绪分析的分布式计算平台。
|
||||
虽然创建本地应用程序来研究情绪分析很方便,但是接下来需要能够部署应用程序以实现更广泛的用途。按照[Radnaalytics.io][11] 提供的指导和代码进行操作,你将学习如何创建一个情感分析仪,可以容器化并部署到 Kubernetes 平台。你还将了解如何将 Apache Kafka 用作事件驱动消息传递的框架,以及如何将 Apache Spark 用作情绪分析的分布式计算平台。
|
||||
|
||||
#### 使用 Twitter API 发现实时数据
|
||||
|
||||
@ -266,17 +279,17 @@ curl http://localhost:5000 --header "Content-Type: application/json" --data "I l
|
||||
|
||||
via: https://opensource.com/article/19/4/social-media-sentiment-analysis-python-scalable
|
||||
|
||||
作者:[Michael McCune ][a]
|
||||
作者:[Michael McCune][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/elmiko/users/jschlessman
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/windows_building_sky_scale.jpg?itok=mH6CAX29 (Tall building with windows)
|
||||
[2]: https://opensource.com/article/19/4/social-media-sentiment-analysis-python-part-1
|
||||
[2]: https://linux.cn/article-10852-1.html
|
||||
[3]: https://virtualenv.pypa.io/en/stable/
|
||||
[4]: https://pypi.org/project/spacy/
|
||||
[5]: https://pypi.org/project/vaderSentiment/
|
@ -0,0 +1,116 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (MjSeven)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10852-1.html)
|
||||
[#]: subject: (Getting started with social media sentiment analysis in Python)
|
||||
[#]: via: (https://opensource.com/article/19/4/social-media-sentiment-analysis-python)
|
||||
[#]: author: (Michael McCune https://opensource.com/users/elmiko/users/jschlessman)
|
||||
|
||||
使用 Python 进行社交媒体情感分析入门
|
||||
======
|
||||
|
||||
> 学习自然语言处理的基础知识并探索两个有用的 Python 包。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201905/14/002943t6udxhhcq1zoxu15.jpg)
|
||||
|
||||
自然语言处理(NLP)是机器学习的一种,它解决了口语或书面语言和计算机辅助分析这些语言之间的相关性。日常生活中我们经历了无数的 NLP 创新,从写作帮助和建议到实时语音翻译,还有口译。
|
||||
|
||||
本文研究了 NLP 的一个特定领域:情感分析。重点是确定输入语言的积极、消极或中性性质。本部分将解释 NLP 和情感分析的背景,并探讨两个开源的 Python 包。[第 2 部分][2]将演示如何开始构建自己的可扩展情感分析服务。
|
||||
|
||||
在学习情感分析时,对 NLP 有一个大体了解是有帮助的。本文不会深入研究数学本质。相反,我们的目标是阐明 NLP 中的关键概念,这些概念对于将这些方法实际结合到你的解决方案中至关重要。
|
||||
|
||||
### 自然语言和文本数据
|
||||
|
||||
合理的起点是从定义开始:“什么是自然语言?”它是我们人类相互交流的方式,沟通的主要方式是口语和文字。我们可以更进一步,只关注文本交流。毕竟,生活在 Siri、Alexa 等无处不在的时代,我们知道语音是一组与文本无关的计算。
|
||||
|
||||
### 数据前景和挑战
|
||||
|
||||
我们只考虑使用文本数据,我们可以对语言和文本做什么呢?首先是语言,特别是英语,除了规则还有很多例外,含义的多样性和语境差异,这些都可能使人类口译员感到困惑,更不用说计算机翻译了。在小学,我们学习文章和标点符号,通过讲母语,我们获得了寻找直觉上表示唯一意义的词的能力。比如,出现诸如 “a”、“the” 和 “or” 之类的文章,它们在 NLP 中被称为*停止词*,因为传统上 NLP 算法是在一个序列中找到这些词时意味着搜索停止。
|
||||
|
||||
由于我们的目标是自动将文本分类为情感类,因此我们需要一种以计算方式处理文本数据的方法。因此,我们必须考虑如何向机器表示文本数据。众所周知,利用和解释语言的规则很复杂,输入文本的大小和结构可能会有很大差异。我们需要将文本数据转换为数字数据,这是机器和数学的首选方式。这种转变属于*特征提取*的范畴。
|
||||
|
||||
在提取输入文本数据的数字表示形式后,一个改进可能是:给定一个文本输入体,为上面列出的文章确定一组向量统计数据,并根据这些数据对文档进行分类。例如,过多的副词可能会使撰稿人感到愤怒,或者过度使用停止词可能有助于识别带有内容填充的学期论文。诚然,这可能与我们情感分析的目标没有太大关系。
|
||||
|
||||
### 词袋
|
||||
|
||||
当你评估一个文本陈述是积极还是消极的时候,你使用哪些上下文来评估它的极性?(例如,文本中是否具有积极的、消极的或中性的情感)一种方式是隐含形容词:被称为 “disgusting”(恶心) 的东西被认为是消极的,但如果同样的东西被称为 “beautiful”(漂亮),你会认为它是积极的。从定义上讲,俗语给人一种熟悉感,通常是积极的,而脏话可能是敌意的表现。文本数据也可以包括表情符号,它带有固定的情感。
|
||||
|
||||
理解单个单词的极性影响为文本的<ruby>[词袋][3]<rt>bag-of-words</rt></ruby>(BoW)模型提供了基础。它分析一组单词或词汇表,并提取关于这些单词在输入文本中是否存在的度量。词汇表是通过处理已知极性的文本形成称为*标记的训练数据*。从这组标记数据中提取特征,然后分析特征之间的关系,并将标记与数据关联起来。
|
||||
|
||||
“词袋”这个名称说明了它的用途:即不考虑空间位置或上下文的的单个词。词汇表通常是由训练集中出现的所有单词构建的,训练后往往会被修剪。如果在训练之前没有清理停止词,那么停止词会因为其高频率和低语境而被移除。很少使用的单词也可以删除,因为缺乏为一般输入实例提供的信息。
|
||||
|
||||
但是,重要的是要注意,你可以(并且应该)进一步考虑单词在单个训练数据实例之外的情形,这称为<ruby>[词频][4]<rt>term frequency</rt></ruby>(TF)。你还应该考虑输入数据在所有训练实例中的单词计数,通常,出现在所有文档中的低频词更重要,这被称为<ruby>[逆文本频率指数][5]<rt>inverse document frequency</rt></ruby>(IDF)。这些指标一定会在本主题系列的其他文章和软件包中提及,因此了解它们会有所帮助。
|
||||
|
||||
词袋在许多文档分类应用程序中很有用。然而,在情感分析中,当缺乏情境意识的问题被利用时,事情就可以解决。考虑以下句子:
|
||||
|
||||
* 我们不喜欢这场战争。
|
||||
* 我讨厌下雨天,好事是今天是晴天。
|
||||
* 这不是生死攸关的问题。
|
||||
|
||||
这些短语的情感对于人类口译员来说是有难度的,而且通过严格关注单个词汇的实例,对于机器翻译来说也是困难的。
|
||||
|
||||
在 NLP 中也可以使用称为 “n-grams” 的单词分组。一个二元组考虑两个相邻单词组成的组而不是(或除了)单个词袋。这应该可以缓解诸如上述“不喜欢”之类的情况,但由于缺乏语境意思,它仍然是个问题。此外,在上面的第二句中,下半句的情感语境可以被理解为否定前半部分。因此,这种方法中也会丢失上下文线索的空间局部性。从实用角度来看,使问题复杂化的是从给定输入文本中提取的特征的稀疏性。对于一个完整的大型词汇表,每个单词都有一个计数,可以将其视为一个整数向量。大多数文档的向量中都有大量的零计数向量,这给操作增加了不必要的空间和时间复杂度。虽然已经提出了许多用于降低这种复杂性的简便方法,但它仍然是一个问题。
|
||||
|
||||
### 词嵌入
|
||||
|
||||
<ruby>词嵌入<rt>Word embedding</rt></ruby>是一种分布式表示,它允许具有相似含义的单词具有相似的表示。这是基于使用实值向量来与它们周围相关联。重点在于使用单词的方式,而不仅仅是它们的存在与否。此外,词嵌入的一个巨大实用优势是它们关注于密集向量。通过摆脱具有相应数量的零值向量元素的单词计数模型,词嵌入在时间和存储方面提供了一个更有效的计算范例。
|
||||
|
||||
以下是两个优秀的词嵌入方法。
|
||||
|
||||
#### Word2vec
|
||||
|
||||
第一个是 [Word2vec][6],它是由 Google 开发的。随着你对 NLP 和情绪分析研究的深入,你可能会看到这种嵌入方法。它要么使用一个<ruby>连续的词袋<rt>continuous bag of words</rt></ruby>(CBOW),要么使用一个连续 skip-gram 模型。在 CBOW 中,一个单词的上下文是在训练中根据围绕它的单词来学习的。连续 skip-gram 学习倾向于围绕给定的单词学习单词。虽然这可能超出了你需要解决的问题,但是如果你曾经面对必须生成自己的词嵌入情况,那么 Word2vec 的作者就提倡使用 CBOW 方法来提高速度并评估频繁的单词,而 skip-gram 方法更适合嵌入稀有单词更重要的嵌入。
|
||||
|
||||
#### GloVe
|
||||
|
||||
第二个是<ruby>[用于词表示的全局向量][7]<rt>Global Vectors for Word Representation</rt></ruby>(GloVe),它是斯坦福大学开发的。它是 Word2vec 方法的扩展,试图通过将经典的全局文本统计特征提取获得的信息与 Word2vec 确定的本地上下文信息相结合。实际上,在一些应用程序中,GloVe 性能优于 Word2vec,而在另一些应用程序中则不如 Word2vec。最终,用于词嵌入的目标数据集将决定哪种方法最优。因此,最好了解它们的存在性和高级机制,因为你很可能会遇到它们。
|
||||
|
||||
#### 创建和使用词嵌入
|
||||
|
||||
最后,知道如何获得词嵌入是有用的。在第 2 部分中,你将看到我们通过利用社区中其他人的实质性工作,站到了巨人的肩膀上。这是获取词嵌入的一种方法:即使用现有的经过训练和验证的模型。实际上,有无数的模型适用于英语和其他语言,一定会有一种模型可以满足你的应用程序,让你开箱即用!
|
||||
|
||||
如果没有的话,就开发工作而言,另一个极端是培训你自己的独立模型,而不考虑你的应用程序。实质上,你将获得大量标记的训练数据,并可能使用上述方法之一来训练模型。即使这样,你仍然只是在理解你输入文本数据。然后,你需要为你应用程序开发一个特定的模型(例如,分析软件版本控制消息中的情感价值),这反过来又需要自己的时间和精力。
|
||||
|
||||
你还可以对针对你的应用程序的数据训练一个词嵌入,虽然这可以减少时间和精力,但这个词嵌入将是特定于应用程序的,这将会降低它的可重用性。
|
||||
|
||||
### 可用的工具选项
|
||||
|
||||
考虑到所需的大量时间和计算能力,你可能想知道如何才能找到解决问题的方法。的确,开发可靠模型的复杂性可能令人望而生畏。但是,有一个好消息:已经有许多经过验证的模型、工具和软件库可以为我们提供所需的大部分内容。我们将重点关注 [Python][8],因为它为这些应用程序提供了大量方便的工具。
|
||||
|
||||
#### SpaCy
|
||||
|
||||
[SpaCy][9] 提供了许多用于解析输入文本数据和提取特征的语言模型。它经过了高度优化,并被誉为同类中最快的库。最棒的是,它是开源的!SpaCy 会执行标识化、词性分类和依赖项注释。它包含了用于执行此功能的词嵌入模型,还有用于为超过 46 种语言的其他特征提取操作。在本系列的第二篇文章中,你将看到它如何用于文本分析和特征提取。
|
||||
|
||||
#### vaderSentiment
|
||||
|
||||
[vaderSentiment][10] 包提供了积极、消极和中性情绪的衡量标准。正如 [原论文][11] 的标题(《VADER:一个基于规则的社交媒体文本情感分析模型》)所示,这些模型是专门为社交媒体文本数据开发和调整的。VADER 接受了一组完整的人类标记过的数据的训练,包括常见的表情符号、UTF-8 编码的表情符号以及口语术语和缩写(例如 meh、lol、sux)。
|
||||
|
||||
对于给定的输入文本数据,vaderSentiment 返回一个极性分数百分比的三元组。它还提供了一个单个的评分标准,称为 *vaderSentiment 复合指标*。这是一个在 `[-1, 1]` 范围内的实值,其中对于分值大于 `0.05` 的情绪被认为是积极的,对于分值小于 `-0.05` 的被认为是消极的,否则为中性。
|
||||
|
||||
在[第 2 部分][2]中,你将学习如何使用这些工具为你的设计添加情感分析功能。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/4/social-media-sentiment-analysis-python
|
||||
|
||||
作者:[Michael McCune][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/elmiko/users/jschlessman
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/getting_started_with_python.png?itok=MFEKm3gl (Raspberry Pi and Python)
|
||||
[2]: https://opensource.com/article/19/4/social-media-sentiment-analysis-python-part-2
|
||||
[3]: https://en.wikipedia.org/wiki/Bag-of-words_model
|
||||
[4]: https://en.wikipedia.org/wiki/Tf%E2%80%93idf#Term_frequency
|
||||
[5]: https://en.wikipedia.org/wiki/Tf%E2%80%93idf#Inverse_document_frequency
|
||||
[6]: https://en.wikipedia.org/wiki/Word2vec
|
||||
[7]: https://en.wikipedia.org/wiki/GloVe_(machine_learning)
|
||||
[8]: https://www.python.org/
|
||||
[9]: https://pypi.org/project/spacy/
|
||||
[10]: https://pypi.org/project/vaderSentiment/
|
||||
[11]: http://comp.social.gatech.edu/papers/icwsm14.vader.hutto.pdf
|
@ -0,0 +1,140 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10841-1.html)
|
||||
[#]: subject: (2 new apps for music tweakers on Fedora Workstation)
|
||||
[#]: via: (https://fedoramagazine.org/2-new-apps-for-music-tweakers-on-fedora-workstation/)
|
||||
[#]: author: (Justin W. Flory https://fedoramagazine.org/author/jflory7/)
|
||||
|
||||
2 个给使用 Fedora 工作站的音乐爱好者的新应用
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
Linux 操作系统非常适合进行独特的自定义和调整,以使你的计算机更好地为你工作。例如,[i3 窗口管理器][2] 就让用户认识到了构成现代 Linux 桌面的各种组件和部分。
|
||||
|
||||
Fedora 上有两个音乐爱好者会感兴趣的新软件包:mpris-scrobbler 和 playerctl。mpris-scrobbler 可以在 Last.fm 和/或 ListenBrainz 等音乐跟踪服务上[跟踪你的音乐收听历史][3]。 playerctl 是一个命令行的[音乐播放器的控制器][4]。
|
||||
|
||||
### mpris-scrobbler:记录你的音乐收听趋势
|
||||
|
||||
mpris-scrobbler 是一个命令行应用程序,用于将音乐的播放历史记录提交给 [Last.fm][5]、[Libre.fm][6] 或 [ListenBrainz][7] 等服务。它监听 [MPRIS D-Bus 接口][8] 以检测正在播放的内容。它可以连接几个不同的音乐客户端,如 spotify 客户端、[vlc][9]、audacious、bmp、[cmus][10] 等。
|
||||
|
||||
![Last.fm last week in music report. Generated from user-submitted listening history.][11]
|
||||
|
||||
#### 安装和配置 mpris-scrobbler
|
||||
|
||||
mpris-scrobbler 在 Fedora 28 或更高版本以及 EPEL 7 存储库中可用。在终端中运行以下命令进行安装:
|
||||
|
||||
```
|
||||
sudo dnf install mpris-scrobbler
|
||||
```
|
||||
|
||||
安装完成后,使用 `systemctl` 启动并启用该服务。以下命令启动 mpris-scrobbler 并始终在系统重启后启动它:
|
||||
|
||||
```
|
||||
systemctl --user enable --now mpris-scrobbler.service
|
||||
```
|
||||
|
||||
#### 提交播放信息给 ListenBrainz
|
||||
|
||||
这里将介绍如何将 mpris-scrobbler 与 ListenBrainz 帐户相关联。要使用 Last.fm 或 Libre.fm,请参阅其[上游文档][12]。
|
||||
|
||||
要将播放信息提交到 ListenBrainz 服务器,你需要有一个 ListenBrainz API 令牌。如果你有帐户,请从[个人资料设置页面][13]中获取该令牌。如果有了令牌,请运行此命令以使用 ListenBrainz API 令牌进行身份验证:
|
||||
|
||||
```
|
||||
$ mpris-scrobbler-signon token listenbrainz
|
||||
Token for listenbrainz.org:
|
||||
```
|
||||
|
||||
最后,通过在 Fedora 上用你的音乐客户端播放一首歌来测试它。你播放的歌曲会出现在 ListenBrainz 个人资料页中。
|
||||
|
||||
![Basic statistics and play history from a user profile on ListenBrainz. The current track is playing on a Fedora Workstation laptop with mpris-scrobbler.][14]
|
||||
|
||||
### playerctl 可以控制音乐回放
|
||||
|
||||
`playerctl` 是一个命令行工具,它可以控制任何实现了 MPRIS D-Bus 接口的音乐播放器。你可以轻松地将其绑定到键盘快捷键或媒体热键上。以下是如何在命令行中安装、使用它,以及为 i3 窗口管理器创建键绑定的方法。
|
||||
|
||||
#### 安装和使用 playerctl
|
||||
|
||||
`playerctl` 在 Fedora 28 或更高版本中可用。在终端运行如下命令以安装:
|
||||
|
||||
```
|
||||
sudo dnf install playerctl
|
||||
```
|
||||
|
||||
现在已安装好,你可以立即使用它。在 Fedora 上打开你的音乐播放器。接下来,尝试用以下命令来控制终端的播放。
|
||||
|
||||
播放或暂停当前播放的曲目:
|
||||
|
||||
```
|
||||
playerctl play-pause
|
||||
```
|
||||
|
||||
如果你想跳过下一首曲目:
|
||||
|
||||
```
|
||||
playerctl next
|
||||
```
|
||||
|
||||
列出所有正在运行的播放器:
|
||||
|
||||
```
|
||||
playerctl -l
|
||||
```
|
||||
|
||||
仅使用 spotify 客户端播放或暂停当前播放的内容:
|
||||
|
||||
```
|
||||
playerctl -p spotify play-pause
|
||||
```
|
||||
|
||||
#### 在 i3wm 中创建 playerctl 键绑定
|
||||
|
||||
你是否使用窗口管理器,比如 [i3 窗口管理器][2]?尝试使用 `playerctl` 进行键绑定。你可以将不同的命令绑定到不同的快捷键,例如键盘上的播放/暂停按钮。参照下面的 [i3wm 配置摘录][15] 看看如何做:
|
||||
|
||||
```
|
||||
# Media player controls
|
||||
bindsym XF86AudioPlay exec "playerctl play-pause"
|
||||
bindsym XF86AudioNext exec "playerctl next"
|
||||
bindsym XF86AudioPrev exec "playerctl previous"
|
||||
```
|
||||
|
||||
### 体验一下音乐播放器
|
||||
|
||||
想了解关于在 Fedora 上定制音乐聆听体验的更多信息吗?Fedora Magazine 为你提供服务。看看 Fedora 上这[五个很酷的音乐播放器][16]。
|
||||
|
||||
也可以通过使用 MusicBrainz Picard 对音乐库进行排序和组织,[为你的混乱的音乐库带来秩序][17]。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/2-new-apps-for-music-tweakers-on-fedora-workstation/
|
||||
|
||||
作者:[Justin W. Flory][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/jflory7/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2019/04/2-music-tweak-apps-816x345.jpg
|
||||
[2]: https://fedoramagazine.org/getting-started-i3-window-manager/
|
||||
[3]: https://github.com/mariusor/mpris-scrobbler
|
||||
[4]: https://github.com/acrisci/playerctl
|
||||
[5]: https://www.last.fm/
|
||||
[6]: https://libre.fm/
|
||||
[7]: https://listenbrainz.org/
|
||||
[8]: https://specifications.freedesktop.org/mpris-spec/latest/
|
||||
[9]: https://www.videolan.org/vlc/
|
||||
[10]: https://cmus.github.io/
|
||||
[11]: https://fedoramagazine.org/wp-content/uploads/2019/02/Screenshot_2019-04-13-jflory7%E2%80%99s-week-in-music2-1024x500.png
|
||||
[12]: https://github.com/mariusor/mpris-scrobbler#authenticate-to-the-service
|
||||
[13]: https://listenbrainz.org/profile/
|
||||
[14]: https://fedoramagazine.org/wp-content/uploads/2019/04/Screenshot_2019-04-13-User-jflory-ListenBrainz.png
|
||||
[15]: https://github.com/jwflory/swiss-army/blob/ba6ac0c71855e33e3caa1ee1fe51c05d2df0529d/roles/apps/i3wm/files/config#L207-L210
|
||||
[16]: https://fedoramagazine.org/5-cool-music-player-apps/
|
||||
[17]: https://fedoramagazine.org/picard-brings-order-music-library/
|
||||
[18]: https://unsplash.com/photos/Qrspubmx6kE?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||
[19]: https://unsplash.com/search/photos/music?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
@ -1,74 +1,62 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (warmfrog)
|
||||
[#]: reviewer: ( )
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10815-1.html)
|
||||
[#]: subject: (How To Monitor Disk I/O Activity Using iotop And iostat Commands In Linux?)
|
||||
[#]: via: (https://www.2daygeek.com/check-monitor-disk-io-in-linux-using-iotop-iostat-command/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
在 Linux 中如何使用 iotop 和 iostat 监控磁盘 I/O 活动?
|
||||
===================================================
|
||||
======================================
|
||||
|
||||
你知道在 Linux 中我们使用什么工具检修和监控实时的磁盘活动吗?
|
||||
你知道在 Linux 中我们使用什么工具检修和监控实时的磁盘活动吗?如果 [Linux 系统性能][1]变慢,我们会用 [top 命令][2] 来查看系统性能。它被用来检查是什么进程在服务器上占有如此高的使用率,对于大多数 Linux 系统管理员来说很常见,现实世界中被 Linux 系统管理员广泛采用。
|
||||
|
||||
如果 **[Linux 系统性能][1]**变慢,我们会用 **[top 命令][12]** 来查看系统性能。
|
||||
|
||||
它被用来检查是什么进程在服务器上占有如此高的使用率。
|
||||
|
||||
对于大多数 Linux 系统管理员来说很常见。
|
||||
|
||||
现实世界中被 Linux 系统管理员广泛采用。
|
||||
|
||||
如果在进程输出中你没有看到很大的不同,你仍然有选择查看其他东西。
|
||||
|
||||
我会建议你在 top 输出中检查 `wa` 状态因为大多数时间服务器性能由于在硬盘上的高 I/O 读和写降低了性能。
|
||||
|
||||
如果它很高或者波动,很可能就是它造成的。因此,我们需要检查硬盘上的 I/O 活动。
|
||||
如果在进程输出中你没有看到很大的不同,你仍然有选择查看其他东西。我会建议你在 `top` 输出中检查 `wa` 状态,因为大多数时间里服务器性能由于在硬盘上的高 I/O 读和写降低了性能。如果它很高或者波动,很可能就是它造成的。因此,我们需要检查硬盘上的 I/O 活动。
|
||||
|
||||
我们可以在 Linux 中使用 `iotop` 和 `iostat` 命令监控所有的磁盘和文件系统的磁盘 I/O 统计。
|
||||
|
||||
### 什么是 iotop?
|
||||
|
||||
iotop 是一个类似 top 的工具来显示实时的磁盘活动。
|
||||
`iotop` 是一个类似 `top` 的工具,用来显示实时的磁盘活动。
|
||||
|
||||
iotop 监控 Linux 内核输出的 I/O 使用信息并且显示一个系统中进程或线程的当前 I/O 使用情况。
|
||||
`iotop` 监控 Linux 内核输出的 I/O 使用信息,并且显示一个系统中进程或线程的当前 I/O 使用情况。
|
||||
|
||||
它显示每个进程/线程读写 I/O 带宽。它同样显示当等待换入和等待 I/O 的线程/进程 时间花费的百分比。
|
||||
它显示每个进程/线程读写 I/O 带宽。它同样显示当等待换入和等待 I/O 的线程/进程花费的时间的百分比。
|
||||
|
||||
Total DISK READ 和 Total DISK WRITE 的值表示了一方面进程和内核线程之间的总的读写带宽,另一方面表示内核块设备子系统的。
|
||||
`Total DISK READ` 和 `Total DISK WRITE` 的值一方面表示了进程和内核线程之间的总的读写带宽,另一方面也表示内核块设备子系统的。
|
||||
|
||||
Actual DISK READ 和 Actual DISK WRITE 的值表示在内核块设备子系统和下面硬件(HDD,SSD,等等。)对应的实际磁盘 I/O 带宽。
|
||||
`Actual DISK READ` 和 `Actual DISK WRITE` 的值表示在内核块设备子系统和下面硬件(HDD、SSD 等等)对应的实际磁盘 I/O 带宽。
|
||||
|
||||
### 如何在 Linux 中安装 iotop ?
|
||||
|
||||
我们可以轻松在包管理器的帮助下安装,因为该软件包在所有的 Linux 发行版仓库中都可以获得。
|
||||
|
||||
对于 **`Fedora`** 系统,使用 **[DNF 命令][3]** 来安装 iotop。
|
||||
对于 Fedora 系统,使用 [DNF 命令][3] 来安装 `iotop`。
|
||||
|
||||
```
|
||||
$ sudo dnf install iotop
|
||||
```
|
||||
|
||||
对于 **`Debian/Ubuntu`** 系统,使用 **[API-GET 命令][4]** 或者 **[APT 命令][5]** 来安装 iotop。
|
||||
对于 Debian/Ubuntu 系统,使用 [API-GET 命令][4] 或者 [APT 命令][5] 来安装 `iotop`。
|
||||
|
||||
```
|
||||
$ sudo apt install iotop
|
||||
```
|
||||
|
||||
对于基于 **`Arch Linux`** 的系统,使用 **[Pacman Command][6]** 来安装 iotop。
|
||||
对于基于 Arch Linux 的系统,使用 [Pacman Command][6] 来安装 `iotop`。
|
||||
|
||||
```
|
||||
$ sudo pacman -S iotop
|
||||
```
|
||||
|
||||
对于 **`RHEL/CentOS`** 的系统,使用 **[YUM Command][7]** 来安装 iotop。
|
||||
对于 RHEL/CentOS 的系统,使用 [YUM Command][7] 来安装 `iotop`。
|
||||
|
||||
```
|
||||
$ sudo yum install iotop
|
||||
```
|
||||
|
||||
对于使用 **`openSUSE Leap`** 的系统,使用 **[Zypper Command][8]** 来安装 iotop。
|
||||
对于使用 openSUSE Leap 的系统,使用 [Zypper Command][8] 来安装 `iotop`。
|
||||
|
||||
```
|
||||
$ sudo zypper install iotop
|
||||
@ -76,72 +64,70 @@ $ sudo zypper install iotop
|
||||
|
||||
### 在 Linux 中如何使用 iotop 命令来监控磁盘 I/O 活动/统计?
|
||||
|
||||
iotop 命令有很多参数来检查关于磁盘 I/O 的变化
|
||||
`iotop` 命令有很多参数来检查关于磁盘 I/O 的变化:
|
||||
|
||||
```
|
||||
# iotop
|
||||
```
|
||||
|
||||
[![][9]![][9]][10]
|
||||
![10]
|
||||
|
||||
如果你想检查那个进程实际在做 I/O,那么运行 iotop 命令加上 `-o` 或者 `--only` 参数。
|
||||
如果你想检查那个进程实际在做 I/O,那么运行 `iotop` 命令加上 `-o` 或者 `--only` 参数。
|
||||
|
||||
```
|
||||
# iotop --only
|
||||
```
|
||||
|
||||
[![][9]![][9]][11]
|
||||
|
||||
**细节:**
|
||||
|
||||
* **`IO:`** 它显示每个进程的 I/O 利用率,包含磁盘和交换。
|
||||
* **`SWAPIN:`** 它只显示每个进程的交换使用率。
|
||||
![11]
|
||||
|
||||
细节:
|
||||
|
||||
* `IO`:它显示每个进程的 I/O 利用率,包含磁盘和交换。
|
||||
* `SWAPIN`: 它只显示每个进程的交换使用率。
|
||||
|
||||
### 什么是 iostat?
|
||||
|
||||
iostat 被用来报告中央处理单元(CPU)的统计和设备与分区的输出/输出的统计。
|
||||
`iostat` 被用来报告中央处理单元(CPU)的统计和设备与分区的输出/输出的统计。
|
||||
|
||||
iostat 命令通过观察与他们平均传输率相关的设备活跃时间来监控系统输入/输出设备载入。
|
||||
`iostat` 命令通过观察与它们平均传输率相关的设备活跃时间来监控系统输入/输出设备负载。
|
||||
|
||||
iostat 命令生成的报告可以被用来改变系统配置来更好的平衡物理磁盘之间的输入/输出负载。
|
||||
`iostat` 命令生成的报告可以被用来改变系统配置来更好的平衡物理磁盘之间的输入/输出负载。
|
||||
|
||||
所有的统计都在 iostat 命令每次运行时被报告。该报告包含一个 CPU 头部,后面是一行 CPU 统计。
|
||||
所有的统计都在 `iostat` 命令每次运行时被报告。该报告包含一个 CPU 头部,后面是一行 CPU 统计。
|
||||
|
||||
在多处理器系统中,CPU 统计被计算为系统层面的所有处理器的平均值。一个设备头行显示后紧跟一行每个配置设备的统计。
|
||||
在多处理器系统中,CPU 统计被计算为系统层面的所有处理器的平均值。设备头行后紧跟显示每个配置的设备一行的统计。
|
||||
|
||||
iostat 命令生成两种类型的报告,CPU 利用率报告和设备利用率报告。
|
||||
`iostat` 命令生成两种类型的报告,CPU 利用率报告和设备利用率报告。
|
||||
|
||||
### 在 Linux 中怎样安装 iostat?
|
||||
|
||||
iostat 工具是 sysstat 包的一部分,所以我们可以轻松地在包管理器地帮助下安装因为在所有的 Linux 发行版的仓库都是可以获得的。
|
||||
`iostat` 工具是 `sysstat` 包的一部分,所以我们可以轻松地在包管理器地帮助下安装,因为在所有的 Linux 发行版的仓库都是可以获得的。
|
||||
|
||||
对于 **`Fedora`** 系统,使用 **[DNF Command][3]** 来安装 sysstat。
|
||||
对于 Fedora 系统,使用 [DNF Command][3] 来安装 `sysstat`。
|
||||
|
||||
```
|
||||
$ sudo dnf install sysstat
|
||||
```
|
||||
|
||||
对于 **`Debian/Ubuntu`** 系统,使用 **[APT-GET Command][4]** 或者 **[APT Command][5]** 来安装 sysstat。
|
||||
对于 Debian/Ubuntu 系统,使用 [APT-GET Command][4] 或者 [APT Command][5] 来安装 `sysstat`。
|
||||
|
||||
```
|
||||
$ sudo apt install sysstat
|
||||
```
|
||||
|
||||
对于基于 **`Arch Linux`** 的系统,使用 **[Pacman Command][6]** 来安装 sysstat。
|
||||
对于基于 Arch Linux 的系统,使用 [Pacman Command][6] 来安装 `sysstat`。
|
||||
|
||||
```
|
||||
$ sudo pacman -S sysstat
|
||||
```
|
||||
|
||||
对于 **`RHEL/CentOS`** 系统,使用 **[YUM Command][7]** 来安装 sysstat。
|
||||
对于 RHEL/CentOS 系统,使用 [YUM Command][7] 来安装 `sysstat`。
|
||||
|
||||
```
|
||||
$ sudo yum install sysstat
|
||||
```
|
||||
|
||||
对于 **`openSUSE Leap`** 系统,使用 **[Zypper Command][8]** 来安装 sysstat。
|
||||
对于 openSUSE Leap 系统,使用 [Zypper Command][8] 来安装 `sysstat`。
|
||||
|
||||
```
|
||||
$ sudo zypper install sysstat
|
||||
@ -149,9 +135,9 @@ $ sudo zypper install sysstat
|
||||
|
||||
### 在 Linux 中如何使用 sysstat 命令监控磁盘 I/O 活动/统计?
|
||||
|
||||
在 iostat 命令中有很多参数来检查关于 I/O 和 CPU 的变化统计信息。
|
||||
在 `iostat` 命令中有很多参数来检查关于 I/O 和 CPU 的变化统计信息。
|
||||
|
||||
不加参数运行 iostat 命令会看到完整的系统统计。
|
||||
不加参数运行 `iostat` 命令会看到完整的系统统计。
|
||||
|
||||
```
|
||||
# iostat
|
||||
@ -169,7 +155,7 @@ loop1 0.00 0.00 0.00 0.00 1093
|
||||
loop2 0.00 0.00 0.00 0.00 1077 0 0
|
||||
```
|
||||
|
||||
运行 iostat 命令加上 `-d` 参数查看所有设备的 I/O 统计。
|
||||
运行 `iostat` 命令加上 `-d` 参数查看所有设备的 I/O 统计。
|
||||
|
||||
```
|
||||
# iostat -d
|
||||
@ -184,7 +170,7 @@ loop1 0.00 0.00 0.00 0.00 1093
|
||||
loop2 0.00 0.00 0.00 0.00 1077 0 0
|
||||
```
|
||||
|
||||
运行 iostat 命令加上 `-p` 参数查看所有的设备和分区的 I/O 统计。
|
||||
运行 `iostat` 命令加上 `-p` 参数查看所有的设备和分区的 I/O 统计。
|
||||
|
||||
```
|
||||
# iostat -p
|
||||
@ -206,7 +192,7 @@ loop1 0.00 0.00 0.00 0.00 1093
|
||||
loop2 0.00 0.00 0.00 0.00 1077 0 0
|
||||
```
|
||||
|
||||
运行 iostat 命令加上 `-x` 参数显示所有设备的详细的 I/O 统计信息。
|
||||
运行 `iostat` 命令加上 `-x` 参数显示所有设备的详细的 I/O 统计信息。
|
||||
|
||||
```
|
||||
# iostat -x
|
||||
@ -224,7 +210,7 @@ loop1 0.00 0.00 0.00 0.00 0.40 12.86 0.00 0.
|
||||
loop2 0.00 0.00 0.00 0.00 0.38 19.58 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
|
||||
```
|
||||
|
||||
运行 iostat 命令加上 `-d [设备名]` 参数查看具体设备和它的分区的 I/O 统计信息。
|
||||
运行 `iostat` 命令加上 `-d [设备名]` 参数查看具体设备和它的分区的 I/O 统计信息。
|
||||
|
||||
```
|
||||
# iostat -p [Device_Name]
|
||||
@ -242,7 +228,7 @@ sda2 0.18 6.76 80.21 0.00 3112916 36924
|
||||
sda1 0.00 0.01 0.00 0.00 3224 0 0
|
||||
```
|
||||
|
||||
运行 iostat 命令加上 `-m` 参数以 `MB` 为单位而不是 `KB` 查看所有设备的统计。默认以 KB 显示输出。
|
||||
运行 `iostat` 命令加上 `-m` 参数以 MB 为单位而不是 KB 查看所有设备的统计。默认以 KB 显示输出。
|
||||
|
||||
```
|
||||
# iostat -m
|
||||
@ -260,7 +246,7 @@ loop1 0.00 0.00 0.00 0.00 1
|
||||
loop2 0.00 0.00 0.00 0.00 1 0 0
|
||||
```
|
||||
|
||||
运行 iostat 命令使用特定的间隔使用如下的格式。在这个例子中,我们打算以 5 秒捕获的间隔捕获两个报告。
|
||||
运行 `iostat` 命令使用特定的间隔使用如下的格式。在这个例子中,我们打算以 5 秒捕获的间隔捕获两个报告。
|
||||
|
||||
```
|
||||
# iostat [Interval] [Number Of Reports]
|
||||
@ -290,7 +276,7 @@ loop1 0.00 0.00 0.00 0.00 0
|
||||
loop2 0.00 0.00 0.00 0.00 0 0 0
|
||||
```
|
||||
|
||||
运行 iostat 命令 与 `-N` 参数来查看 LVM 磁盘 I/O 统计报告。
|
||||
运行 `iostat` 命令与 `-N` 参数来查看 LVM 磁盘 I/O 统计报告。
|
||||
|
||||
```
|
||||
# iostat -N
|
||||
@ -307,7 +293,7 @@ sdc 0.01 0.12 0.00 2108 0
|
||||
2g-2gvol1 0.00 0.07 0.00 1204 0
|
||||
```
|
||||
|
||||
运行 nfsiostat 命令来查看 Network File System(NFS)的 I/O 统计。
|
||||
运行 `nfsiostat` 命令来查看 Network File System(NFS)的 I/O 统计。
|
||||
|
||||
```
|
||||
# nfsiostat
|
||||
@ -320,7 +306,7 @@ via: https://www.2daygeek.com/check-monitor-disk-io-in-linux-using-iotop-iostat-
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[warmfrog](https://github.com/warmfrog)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
@ -0,0 +1,132 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10875-1.html)
|
||||
[#]: subject: (Automate backups with restic and systemd)
|
||||
[#]: via: (https://fedoramagazine.org/automate-backups-with-restic-and-systemd/)
|
||||
[#]: author: (Link Dupont https://fedoramagazine.org/author/linkdupont/)
|
||||
|
||||
使用 restic 和 systemd 自动备份
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
及时备份很重要。即使在 [Fedora Magazine][3] 中,[备份软件][2] 也是一个常见的讨论话题。本文演示了如何仅使用 systemd 以及 `restic` 来自动备份。
|
||||
|
||||
有关 `restic` 的介绍,请查看我们的文章[在 Fedora 上使用 restic 进行加密备份][4]。然后继续阅读以了解更多详情。
|
||||
|
||||
为了自动创建快照以及清理数据,需要运行两个 systemd 服务。第一个运行*备份*命令的服务需要以常规频率运行。第二个服务负责数据清理。
|
||||
|
||||
如果你根本不熟悉 systemd,那么这是个很好的学习机会。查看 [Magazine 上关于 systemd 的系列文章] [5],从单元文件的这个入门开始:
|
||||
|
||||
- [systemd 单元文件基础][6]
|
||||
|
||||
如果你还没有安装 `restic`,请注意它在官方的 Fedora 仓库中。要安装它,请[带上 sudo][7] 运行此命令:
|
||||
|
||||
```
|
||||
$ sudo dnf install restic
|
||||
```
|
||||
|
||||
### 备份
|
||||
|
||||
首先,创建 `~/.config/systemd/user/restic-backup.service`。将下面的文本复制并粘贴到文件中以获得最佳效果。
|
||||
|
||||
```
|
||||
[Unit]
|
||||
Description=Restic backup service
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=restic backup --verbose --one-file-system --tag systemd.timer $BACKUP_EXCLUDES $BACKUP_PATHS
|
||||
ExecStartPost=restic forget --verbose --tag systemd.timer --group-by "paths,tags" --keep-daily $RETENTION_DAYS --keep-weekly $RETENTION_WEEKS --keep-monthly $RETENTION_MONTHS --keep-yearly $RETENTION_YEARS
|
||||
EnvironmentFile=%h/.config/restic-backup.conf
|
||||
```
|
||||
|
||||
此服务引用环境文件来加载密钥(例如 `RESTIC_PASSWORD`)。创建 `~/.config/restic-backup.conf`。复制并粘贴以下内容以获得最佳效果。此示例使用 BackBlaze B2 存储。请相应地调整 ID、密钥、仓库和密码值。
|
||||
|
||||
```
|
||||
BACKUP_PATHS="/home/rupert"
|
||||
BACKUP_EXCLUDES="--exclude-file /home/rupert/.restic_excludes --exclude-if-present .exclude_from_backup"
|
||||
RETENTION_DAYS=7
|
||||
RETENTION_WEEKS=4
|
||||
RETENTION_MONTHS=6
|
||||
RETENTION_YEARS=3
|
||||
B2_ACCOUNT_ID=XXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
B2_ACCOUNT_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
RESTIC_REPOSITORY=b2:XXXXXXXXXXXXXXXXXX:/
|
||||
RESTIC_PASSWORD=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
```
|
||||
|
||||
现在已安装该服务,请重新加载 systemd:`systemctl -user daemon-reload`。尝试手动运行该服务以创建备份:`systemctl -user start restic-backup`。
|
||||
|
||||
因为该服务类型是*一次性*,它将运行一次并退出。验证服务运行并根据需要创建快照后,设置计时器以定期运行此服务。例如,要每天运行 `restic-backup.service`,请按如下所示创建 `~/.config/systemd/user/restic-backup.timer`。再次复制并粘贴此文本:
|
||||
|
||||
```
|
||||
[Unit]
|
||||
Description=Backup with restic daily
|
||||
[Timer]
|
||||
OnCalendar=daily
|
||||
Persistent=true
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
```
|
||||
|
||||
运行以下命令启用:
|
||||
|
||||
```
|
||||
$ systemctl --user enable --now restic-backup.timer
|
||||
```
|
||||
|
||||
### 清理
|
||||
|
||||
虽然主服务运行 `forget` 命令仅保留保留策略中的快照,但实际上并未从 `restic` 仓库中删除数据。 `prune` 命令检查仓库和当前快照,并删除与快照无关的所有数据。由于 `prune` 可能是一个耗时的过程,因此无需在每次运行备份时运行。这是第二个服务和计时器的场景。首先,通过复制和粘贴此文本来创建文件 `~/.config/systemd/user/restic-prune.service`:
|
||||
|
||||
```
|
||||
[Unit]
|
||||
Description=Restic backup service (data pruning)
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=restic prune
|
||||
EnvironmentFile=%h/.config/restic-backup.conf
|
||||
```
|
||||
|
||||
与主 `restic-backup.service` 服务类似,`restic-prune` 也是一次性服务,并且可以手动运行。设置完服务后,创建 `~/.config/systemd/user/restic-prune.timer` 并启用相应的计时器:
|
||||
|
||||
```
|
||||
[Unit]
|
||||
Description=Prune data from the restic repository monthly
|
||||
[Timer]
|
||||
OnCalendar=monthly
|
||||
Persistent=true
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
```
|
||||
|
||||
就是这些了!`restic` 将会每日运行并按月清理数据。
|
||||
|
||||
* * *
|
||||
|
||||
图片来自 [Unsplash][9] 由 [Samuel Zeller][8] 拍摄。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/automate-backups-with-restic-and-systemd/
|
||||
|
||||
作者:[Link Dupont][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/linkdupont/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2019/04/restic-systemd-816x345.jpg
|
||||
[2]: https://restic.net/
|
||||
[3]: https://fedoramagazine.org/?s=backup
|
||||
[4]: https://fedoramagazine.org/use-restic-encrypted-backups/
|
||||
[5]: https://fedoramagazine.org/series/systemd-series/
|
||||
[6]: https://fedoramagazine.org/systemd-getting-a-grip-on-units/
|
||||
[7]: https://fedoramagazine.org/howto-use-sudo/
|
||||
[8]: https://unsplash.com/photos/JuFcQxgCXwA?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||
[9]: https://unsplash.com/search/photos/archive?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
@ -0,0 +1,96 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10854-1.html)
|
||||
[#]: subject: (Upgrading Fedora 29 to Fedora 30)
|
||||
[#]: via: (https://fedoramagazine.org/upgrading-fedora-29-to-fedora-30/)
|
||||
[#]: author: (Ryan Lerch https://fedoramagazine.org/author/ryanlerch/)
|
||||
|
||||
将 Fedora 29 升级到 Fedora 30
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
Fedora 30 [已经发布了][2]。你可能希望将系统升级到最新版本的 Fedora。Fedora 工作站版本有图形化升级的方法。另外,Fedora 也提供了一个命令行方法,用于将 Fedora 29 升级到 Fedora 30。
|
||||
|
||||
### 将 Fedora 29 工作站版本升级到 Fedora 30
|
||||
|
||||
在发布不久后,桌面会显示一条通知告诉你可以升级。你可以单击通知启动 “GNOME 软件” 应用。或者你可以从 GNOME Shell 中选择“软件”。
|
||||
|
||||
在 “GNOME 软件” 中选择*更新*选项卡,你会看到一个页面通知你可以更新 Fedora 30。
|
||||
|
||||
如果你在屏幕上看不到任何内容,请尝试点击左上角的重新加载按钮。发布后,所有系统都可能需要一段时间才能看到可用的升级。
|
||||
|
||||
选择“下载”获取升级包。你可以继续做其他的事直到下载完成。然后使用 “GNOME 软件” 重启系统并应用升级。升级需要时间,因此你可以喝杯咖啡,稍后再回来。
|
||||
|
||||
### 使用命令行
|
||||
|
||||
如果你过去升级过 Fedora 版本,你可能熟悉 `dnf upgrade` 插件。这是从 Fedora 29 升级到 Fedora 30 的推荐和支持的方式。使用这个插件将使你的 Fedora 30 升级简单易行。
|
||||
|
||||
#### 1、更新软件并备份系统
|
||||
|
||||
在你执行任何操作之前,你需要确保在开始升级之前拥有 Fedora 29 的最新软件。要更新软件,请使用 “GNOME 软件” 或在终端中输入以下命令。
|
||||
|
||||
```
|
||||
sudo dnf upgrade --refresh
|
||||
```
|
||||
|
||||
此外,请确保在继续之前备份系统。关于备份的帮助,请参阅 Fedora Magazine 上的[备份系列][3]。
|
||||
|
||||
#### 2、安装 DNF 插件
|
||||
|
||||
接下来,打开终端并输入以下命令来安装插件:
|
||||
|
||||
```
|
||||
sudo dnf install dnf-plugin-system-upgrade
|
||||
```
|
||||
|
||||
#### 3、使用 DNF 开始更新
|
||||
|
||||
现在你的系统是最新的,完成了备份,并且已安装 DNF 插件,你可以在终端中使用以下命令开始升级:
|
||||
|
||||
```
|
||||
sudo dnf system-upgrade download --releasever=30
|
||||
```
|
||||
|
||||
此命令将开始在本地下载所有升级文件以准备升级。如果你因为没有更新包、错误的依赖,或过时的包在升级时遇到问题,请在输入上面的命令时添加 `-- allowerasing` 标志。这将允许 DNF 删除可能阻止系统升级的软件包。
|
||||
|
||||
#### 4、重启并升级
|
||||
|
||||
当前面的命令完成下载所有升级文件后,你的系统就可以重启了。要将系统引导至升级过程,请在终端中输入以下命令:
|
||||
|
||||
```
|
||||
sudo dnf system-upgrade reboot
|
||||
```
|
||||
|
||||
此后你的系统将重启。在许多版本之前,`fedup` 工具将在内核选择/引导页面上创建一个新选项。使用 `dnf-plugin-system-upgrade` 包,你的系统将使用当前 Fedora 29 安装的内核重启。这个是正常的。在内核选择页面后不久,系统开始升级过程。
|
||||
|
||||
现在可以休息一下了!完成后你的系统将重启,你就可以登录新升级的 Fedora 30 了。
|
||||
|
||||
![][4]
|
||||
|
||||
### 解决升级问题
|
||||
|
||||
升级系统时偶尔可能会出现意外问题。如果你遇到任何问题,请访问 [DNF 系统升级的维基页面][5],以获取有关出现问题时的故障排除的更多信息。
|
||||
|
||||
如果你在升级时遇到问题并在系统上安装了第三方仓库,那么可能需要在升级时禁用这些仓库。有关 Fedora 对未提供仓库的支持,请与仓库的提供商联系。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/upgrading-fedora-29-to-fedora-30/
|
||||
|
||||
作者:[Ryan Lerch][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/ryanlerch/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2019/04/29-30-816x345.jpg
|
||||
[2]: https://fedoramagazine.org/announcing-fedora-30/
|
||||
[3]: https://fedoramagazine.org/taking-smart-backups-duplicity/
|
||||
[4]: https://cdn.fedoramagazine.org/wp-content/uploads/2016/06/Screenshot_f23-ws-upgrade-test_2016-06-10_110906-1024x768.png
|
||||
[5]: https://fedoraproject.org/wiki/DNF_system_upgrade#Resolving_post-upgrade_issues
|
@ -0,0 +1,73 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10903-1.html)
|
||||
[#]: subject: (3 apps to manage personal finances in Fedora)
|
||||
[#]: via: (https://fedoramagazine.org/3-apps-to-manage-personal-finances-in-fedora/)
|
||||
[#]: author: (Paul W. Frields https://fedoramagazine.org/author/pfrields/)
|
||||
|
||||
3 款在 Fedora 中管理个人财务的应用
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
网上有很多可以用来管理你个人财务的服务。虽然它们可能很方便,但这通常也意味着将你最宝贵的个人数据放在你无法监控的公司。也有些人对这些不太在意。
|
||||
|
||||
无论你是否在意,你可能会对你自己系统上的应用感兴趣。这意味着如果你不想,你的数据永远不会离开自己的计算机。这三款之一可能就是你想找的。
|
||||
|
||||
### HomeBank
|
||||
|
||||
HomeBank 是一款可以管理多个账户的全功能软件。它很容易设置并保持更新。它有多种方式画出你的分类和负债,以便你可以看到资金流向何处。它可以通过官方 Fedora 仓库下载。
|
||||
|
||||
![A simple account set up in HomeBank with a few transactions.][2]
|
||||
|
||||
要安装 HomeBank,请打开“软件中心”,搜索 “HomeBank”,然后选择该应用。单击“安装”将其添加到你的系统中。HomeBank 也可以通过 Flatpak 安装。
|
||||
|
||||
### KMyMoney
|
||||
|
||||
KMyMoney 是一个成熟的应用,它已经存在了很长一段时间。它有一系列稳定的功能,可帮助你管理多个帐户,包括资产、负债、税收等。KMyMoney 包含一整套用于管理投资和进行预测的工具。它还提供大量报告,以了解你的资金运作方式。
|
||||
|
||||
![A subset of the many reports available in KMyMoney.][3]
|
||||
|
||||
要安装它,请使用软件中心,或使用命令行:
|
||||
|
||||
```
|
||||
$ sudo dnf install kmymoney
|
||||
```
|
||||
|
||||
### GnuCash
|
||||
|
||||
用于个人财务的最受欢迎的免费 GUI 应用之一是 GnuCash。GnuCash 不仅可以用于个人财务。它还有管理企业收入、资产和负债的功能。这并不意味着你不能用它来管理自己的账户。从查看[在线教程和指南][4]开始了解。
|
||||
|
||||
![Checking account records shown in GnuCash.][5]
|
||||
|
||||
打开“软件中心”,搜索 “GnuCash”,然后选择应用。单击“安装”将其添加到你的系统中。或者如上所述使用 `dnf install` 来安装 “gnucash” 包。
|
||||
|
||||
它现在可以通过 Flathub 安装,这使得安装变得简单。如果你没有安装 Flathub,请查看 [Fedora Magazine 上的这篇文章][6]了解如何使用它。这样你也可以在终端使用 `flatpak install gnucash` 命令。
|
||||
|
||||
* * *
|
||||
|
||||
照片由 [Fabian Blank][7] 拍摄,发布在 [Unsplash][8] 上。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/3-apps-to-manage-personal-finances-in-fedora/
|
||||
|
||||
作者:[Paul W. Frields][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/pfrields/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2019/04/personal-finance-3-apps-816x345.jpg
|
||||
[2]: https://fedoramagazine.org/wp-content/uploads/2019/04/Screenshot-from-2019-04-28-16-16-16-1024x637.png
|
||||
[3]: https://fedoramagazine.org/wp-content/uploads/2019/04/Screenshot-from-2019-04-28-16-27-10-1-1024x649.png
|
||||
[4]: https://www.gnucash.org/viewdoc.phtml?rev=3&lang=C&doc=guide
|
||||
[5]: https://fedoramagazine.org/wp-content/uploads/2019/04/Screenshot-from-2019-04-28-16-41-27-1024x631.png
|
||||
[6]: https://fedoramagazine.org/install-flathub-apps-fedora/
|
||||
[7]: https://unsplash.com/photos/pElSkGRA2NU?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
||||
[8]: https://unsplash.com/search/photos/money?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText
|
@ -0,0 +1,75 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (hopefully2333)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10876-1.html)
|
||||
[#]: subject: (Cisco issues critical security warning for Nexus data-center switches)
|
||||
[#]: via: (https://www.networkworld.com/article/3392858/cisco-issues-critical-security-warning-for-nexus-data-center-switches.html)
|
||||
[#]: author: (Michael Cooney https://www.networkworld.com/author/Michael-Cooney/)
|
||||
|
||||
思科针对 Nexus 数据中心交换机发出危急安全预警
|
||||
======
|
||||
|
||||
> 思科围绕着 Nexus 的交换机、Firepower 防火墙和其他设备,发布了 40 个安全报告。
|
||||
|
||||
![Thinkstock][1]
|
||||
|
||||
日前,思科发布了 40 个左右的安全报告,但只有其中的一个被评定为“[危急][2]”:思科 Nexus 9000 系列应用中心基础设施(ACI)模式数据中心交换机中的一个漏洞,可能会让攻击者隐秘地访问到系统资源。
|
||||
|
||||
这个新发现的漏洞,被通用漏洞评分系统给到了 9.8 分(满分 10 分),思科表示,它是思科 Nexus 9000 系列的安全 shell (ssh)密钥管理方面的问题,这个漏洞允许远程攻击者以 root 用户的权限来连接到受影响的系统。
|
||||
|
||||
思科表示,“**这个漏洞是因为所有的设备都存在一对默认的 ssh 密钥对**,攻击者可以使用提取到的密钥材料,并通过 IPv6 来创建连接到目标设备的 SSH 连接。这个漏洞仅能通过 IPv6 来进行利用,IPv4 不会被攻击”。
|
||||
|
||||
型号为 Nexus 9000 系列且 NX-OS 软件版本在 14.1 之前的设备会受此漏洞的影响,该公司表示没有解决这个问题的变通办法。
|
||||
|
||||
然而,思科公司已经为解决这个漏洞[发布了免费的软件更新][4]。
|
||||
|
||||
该公司同样对 Nexus 9000 系列发布了一个“高危”级别的安全预警报告,报告中表示存在一种攻击,允许攻击者以 root 用户权限在受影响的设备上执行任意操作系统命令。思科表示,如果要用这种方式攻击成功,攻击者需要对应设备的有效的管理员用户凭证。
|
||||
|
||||
[思科表示][5],这个漏洞是由于过于宽泛的系统文件权限造成的。攻击者可以通过向受影响的设备进行认证,构造一个精心设计的命令字符串,并将这个字符串写入到特定位置的文件里。攻击者通过这种方式来利用这个漏洞。
|
||||
|
||||
思科发布了解决这个漏洞的软件更新。
|
||||
|
||||
另外两个被评为“高危”级别的漏洞的影响范围同样包括 Nexus 9000 系列:
|
||||
|
||||
- 思科 Nexus 9000 系列软件后台操作功能中的[漏洞][7],能够允许一个已认证的本地攻击者在受影响的设备上提权到 root 权限。这个漏洞是由于在受影响的设备上用户提供的文件验证不充分。思科表示,攻击者可以通过登录到受影响设备的命令行界面,并在文件系统的特定目录中构造一个精心设计过的文件,以此来利用这个漏洞。
|
||||
- 交换机软件后台操作功能中的[弱点][7]能够允许攻击者登录到受影响设备的命令行界面,并在文件系统的特定目录里创建一个精心构造过的文件。思科表示,这个漏洞是由于在受影响的设备上用户提供的文件验证不充分。
|
||||
|
||||
思科同样为这些漏洞[发布了软件更新][4]。
|
||||
|
||||
此外,这些安全警告中的一部分是针对思科 FirePower 防火墙系列中大量的“高危”漏洞警告。
|
||||
|
||||
例如,思科[写道][8],思科 Firepower 威胁防御软件的 SMB 协议预处理检测引擎中的多个漏洞能够允许未认证的相邻、远程攻击者造成拒绝服务攻击(DoS)的情况。
|
||||
|
||||
思科表示,思科 Firepower 2100 系列中思科 Firepower 软件里的内部数据包处理功能有[另一个漏洞][9],能够让未认证的远程攻击者造成受影响的设备停止处理流量,从而导致 DOS 的情况。
|
||||
|
||||
[软件补丁][4]可用于这些漏洞。
|
||||
|
||||
其他的产品,比如思科[自适应安全虚拟设备][10]和 [web 安全设备][11]同样也有高优先级的补丁。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3392858/cisco-issues-critical-security-warning-for-nexus-data-center-switches.html
|
||||
|
||||
作者:[Michael Cooney][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[hopefully2333](https://github.com/hopefully2333)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Michael-Cooney/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://images.idgesg.net/images/article/2018/02/lock_broken_unlocked_binary_code_security_circuits_protection_privacy_thinkstock_873916354-100750739-large.jpg
|
||||
[2]: https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190501-nexus9k-sshkey
|
||||
[3]: https://www.networkworld.com/article/3284352/data-center/how-to-plan-a-software-defined-data-center-network.html
|
||||
[4]: https://www.cisco.com/c/en/us/about/legal/cloud-and-software/end_user_license_agreement.html
|
||||
[5]: https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190501-nexus9k-rpe
|
||||
[6]: https://pluralsight.pxf.io/c/321564/424552/7490?u=https%3A%2F%2Fwww.pluralsight.com%2Fpaths%2Fcertified-information-systems-security-professional-cisspr
|
||||
[7]: https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190501-aci-hw-clock-util
|
||||
[8]: https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190501-frpwr-smb-snort
|
||||
[9]: https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190501-frpwr-dos
|
||||
[10]: https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190501-asa-ipsec-dos
|
||||
[11]: https://tools.cisco.com/security/center/content/CiscoSecurityAdvisory/cisco-sa-20190501-wsa-privesc
|
||||
[12]: https://www.facebook.com/NetworkWorld/
|
||||
[13]: https://www.linkedin.com/company/network-world
|
@ -0,0 +1,81 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10859-1.html)
|
||||
[#]: subject: (Write faster C extensions for Python with Cython)
|
||||
[#]: via: (https://opensource.com/article/19/5/python-cython)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshez/users/moshez/users/foundjem/users/jugmac00)
|
||||
|
||||
使用 Cython 为 Python 编写更快的 C 扩展
|
||||
======
|
||||
|
||||
> 在我们这个包含了 7 个 PyPI 库的系列文章中学习解决常见的 Python 问题的方法。
|
||||
|
||||
![Hand drawing out the word "code"](https://img.linux.net.cn/data/attachment/album/201905/15/225506fnn2mz6l3u122n70.jpg)
|
||||
|
||||
Python 是当今使用最多的[流行编程语言][2]之一,因为:它是开源的,它有广泛的用途(例如 Web 编程、业务应用、游戏、科学编程等等),它有一个充满活力和专注的社区支持它。这个社区可以让我们在 [Python Package Index][3](PyPI)中有如此庞大、多样化的软件包,用以扩展和改进 Python 并解决不可避免的问题。
|
||||
|
||||
在本系列中,我们将介绍七个可以帮助你解决常见 Python 问题的 PyPI 库。首先是 [Cython][4],一个简化 Python 编写 C 扩展的语言。
|
||||
|
||||
### Cython
|
||||
|
||||
使用 Python 很有趣,但有时,用它编写的程序可能很慢。所有的运行时动态调度会带来很大的代价:有时它比用 C 或 Rust 等系统语言编写的等效代码慢 10 倍。
|
||||
|
||||
将代码迁移到一种全新的语言可能会在成本和可靠性方面付出巨大代价:所有的手工重写工作都将不可避免地引入错误。我们可以两者兼得么?
|
||||
|
||||
为了练习一下优化,我们需要一些慢代码。有什么比斐波那契数列的意外指数实现更慢?
|
||||
|
||||
```
|
||||
def fib(n):
|
||||
if n < 2:
|
||||
return 1
|
||||
return fib(n-1) + fib(n-2)
|
||||
```
|
||||
|
||||
由于对 `fib` 的调用会导致两次再次调用,因此这种效率极低的算法需要很长时间才能执行。例如,在我的新笔记本电脑上,`fib(36)` 需要大约 4.5 秒。这个 4.5 秒会成为我们探索 Python 的 Cython 扩展能提供的帮助的基准。
|
||||
|
||||
使用 Cython 的正确方法是将其集成到 `setup.py` 中。然而,使用 `pyximport` 可以快速地进行尝试。让我们将 `fib` 代码放在 `fib.pyx` 中并使用 Cython 运行它。
|
||||
|
||||
```
|
||||
>>> import pyximport; pyximport.install()
|
||||
>>> import fib
|
||||
>>> fib.fib(36)
|
||||
```
|
||||
|
||||
只使用 Cython 而不*修改*代码,这个算法在我笔记本上花费的时间减少到大约 2.5 秒。几乎无需任何努力,这几乎减少了 50% 的运行时间。当然,得到了一个不错的成果。
|
||||
|
||||
加把劲,我们可以让它变得更快。
|
||||
|
||||
```
|
||||
cpdef int fib(int n):
|
||||
if n < 2:
|
||||
return 1
|
||||
return fib(n - 1) + fib(n - 2)
|
||||
```
|
||||
|
||||
我们将 `fib` 中的代码变成用 `cpdef` 定义的函数,并添加了两个类型注释:它接受一个整数并返回一个整数。
|
||||
|
||||
这个变得快*多*了,大约只用了 0.05 秒。它是如此之快,以至于我可能开始怀疑我的测量方法包含噪声:之前,这种噪声在信号中丢失了。
|
||||
|
||||
当下次你的 Python 代码花费太多 CPU 时间时,也许会导致风扇狂转,为何不看看 Cython 是否可以解决问题呢?
|
||||
|
||||
在本系列的下一篇文章中,我们将看一下 Black,一个自动纠正代码格式错误的项目。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/python-cython
|
||||
|
||||
作者:[Moshe Zadka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/moshez/users/moshez/users/foundjem/users/jugmac00
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/code_hand_draw.png?itok=dpAf--Db (Hand drawing out the word "code")
|
||||
[2]: https://opensource.com/article/18/5/numbers-python-community-trends
|
||||
[3]: https://pypi.org/
|
||||
[4]: https://pypi.org/project/Cython/
|
@ -0,0 +1,99 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10864-1.html)
|
||||
[#]: subject: (Format Python however you like with Black)
|
||||
[#]: via: (https://opensource.com/article/19/5/python-black)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshez/users/moshez/users/moshez)
|
||||
|
||||
使用 Black 自由格式化 Python
|
||||
======
|
||||
|
||||
> 在我们覆盖 7 个 PyPI 库的系列文章中了解解决 Python 问题的更多信息。
|
||||
|
||||
![OpenStack source code \(Python\) in VIM](https://img.linux.net.cn/data/attachment/album/201905/16/220249ethkikh5h1uib5iy.jpg)
|
||||
|
||||
Python 是当今使用最多的[流行编程语言][2]之一,因为:它是开源的,它有广泛的用途(例如 Web 编程、业务应用、游戏、科学编程等等),它有一个充满活力和专注的社区支持它。这个社区可以让我们在 [Python Package Index][3](PyPI)中有如此庞大、多样化的软件包,用以扩展和改进 Python 并解决不可避免的问题。
|
||||
|
||||
在本系列中,我们将介绍七个可以帮助你解决常见 Python 问题的 PyPI 库。在第一篇文章中,我们了解了 [Cython][4]。今天,我们将使用 [Black][5] 这个代码格式化工具。
|
||||
|
||||
### Black
|
||||
|
||||
有时创意可能是一件美妙的事情。有时它只是一种痛苦。我喜欢创造性地解决难题,但我希望我的 Python 格式尽可能一致。没有人对使用“有趣”缩进的代码印象深刻。
|
||||
|
||||
但是比不一致的格式更糟糕的是除了检查格式之外什么都没有做的代码审查。这对审查者来说很烦人,对于被审查者来说甚至更烦人。当你的 linter 告诉你代码缩进不正确时,但没有提示*正确*的缩进量,这也会令人气愤。
|
||||
|
||||
使用 Black,它不会告诉你*要*做什么,它是一个优良、勤奋的机器人:它将为你修复代码。
|
||||
|
||||
要了解它如何工作的,请随意写一些非常不一致的内容,例如:
|
||||
|
||||
```
|
||||
def add(a, b): return a+b
|
||||
|
||||
def mult(a, b):
|
||||
return \
|
||||
a * b
|
||||
```
|
||||
|
||||
Black 抱怨了么?并没有,它为你修复了!
|
||||
|
||||
```
|
||||
$ black math
|
||||
reformatted math
|
||||
All done! ✨ 🍰 ✨
|
||||
1 file reformatted.
|
||||
$ cat math
|
||||
def add(a, b):
|
||||
return a + b
|
||||
|
||||
|
||||
def mult(a, b):
|
||||
return a * b
|
||||
```
|
||||
|
||||
Black 确实提供了报错而不是修复的选项,甚至还有输出 diff 编辑样式的选项。这些选项在持续集成 (CI)系统中非常有用,可以在本地强制运行 Black。此外,如果 diff 输出被记录到 CI 输出中,你可以直接将其粘贴到 `patch` 中,以便在极少数情况下你需要修复输出,但无法本地安装 Black 使用。
|
||||
|
||||
|
||||
```
|
||||
$ black --check --diff bad
|
||||
--- math 2019-04-09 17:24:22.747815 +0000
|
||||
+++ math 2019-04-09 17:26:04.269451 +0000
|
||||
@@ -1,7 +1,7 @@
|
||||
-def add(a, b): return a + b
|
||||
+def add(a, b):
|
||||
+ return a + b
|
||||
|
||||
|
||||
def mult(a, b):
|
||||
- return \
|
||||
- a * b
|
||||
+ return a * b
|
||||
|
||||
would reformat math
|
||||
All done! 💥 💔 💥
|
||||
1 file would be reformatted.
|
||||
$ echo $?
|
||||
1
|
||||
```
|
||||
|
||||
在本系列的下一篇文章中,我们将介绍 attrs ,这是一个可以帮助你快速编写简洁、正确的代码的库。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/python-black
|
||||
|
||||
作者:[Moshe Zadka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/moshez/users/moshez/users/moshez
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/openstack_python_vim_1.jpg?itok=lHQK5zpm (OpenStack source code (Python) in VIM)
|
||||
[2]: https://opensource.com/article/18/5/numbers-python-community-trends
|
||||
[3]: https://pypi.org/
|
||||
[4]: https://opensource.com/article/19/4/7-python-problems-solved-cython
|
||||
[5]: https://pypi.org/project/black/
|
@ -0,0 +1,61 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10880-1.html)
|
||||
[#]: subject: (Get started with Libki to manage public user computer access)
|
||||
[#]: via: (https://opensource.com/article/19/5/libki-computer-access)
|
||||
[#]: author: (Don Watkins https://opensource.com/users/don-watkins/users/tony-thomas)
|
||||
|
||||
使用 Libki 来管理公共用户访问计算机
|
||||
======
|
||||
> Libki 是一个跨平台的计算机预约和用时管理系统。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201905/20/230201d26yuo261uu6s61i.jpg)
|
||||
|
||||
提供公共计算机的图书馆、学校、学院和其他组织需要一种管理用户访问权限的好方法 —— 否则,就无法阻止某些人独占机器并确保每个人都有公平的用时。这是 [Libki][2] 要解决的问题。
|
||||
|
||||
Libki 是一个面向 Windows 和 Linux PC 的开源、跨平台的计算机预约和用时管理系统。它提供了一个基于 Web 的服务器和一个基于 Web 的管理系统,员工可以使用它来管理计算机访问,包括创建和删除用户、设置帐户用时限制、登出和禁止用户以及设置访问限制。
|
||||
|
||||
根据其首席开发人员 [Kyle Hall][3] 所说,Libki 主要用于 PC 用时控制,作为 Envisionware 出品的专有计算机访问控制软件的开源替代品。当用户登录 Libki 管理的计算机时,他们会有一段使用计算机的时间。时间到了之后,他们就会被登出。时间默认设置为 45 分钟,但可以使用基于 Web 的管理系统轻松调整。一些组织在登出用户之前提供 24 小时访问权限,而有的组织则使用它来跟踪使用情况而不设置用时限制。
|
||||
|
||||
Kyle 目前是 [ByWater Solutions][4] 的首席开发人员,该公司为图书馆提供开源软件解决方案(包括 Libki)。在职业生涯早期,他在宾夕法尼亚州的[米德维尔公共图书馆][5]担任 IT 技术时开发了 Libki。在其他员工的午休期间,偶尔会要求他关注孩子们的房间。图书馆使用纸质注册表来管理对儿童房间计算机的访问,这意味着不断的监督和检查,以确保来到那里的人能够公平地使用。
|
||||
|
||||
Kyle 说,“我发现这很笨拙而不便的,我想找到一个解决方案。这个解决方案需要同时是 FOSS 和跨平台的。最后,没有现有的软件适合我们的特殊需求,那就是为什么我开发了 Libki。“
|
||||
|
||||
或者,正如 Libki 的网站所宣称的那样,“Libki 的诞生是为了避免与青少年打交道(的麻烦),现在允许图书馆员避免与世界各地的青少年打交道(的麻烦)!”
|
||||
|
||||
### 易于安装和使用
|
||||
|
||||
我最近决定在我经常在那里做志愿者的当地的公共图书馆尝试 Libki。我按照[文档][6]在 Ubuntu 18.04 Server 中自动进行了安装,它很快就启动起来了。
|
||||
|
||||
我计划在我们当地的图书馆支持 Libki,但我想知道在那些没有 IT 相关经验的人或者无法构建和部署服务器的图书馆是怎样的。Kyle 说:“ByWater Solutions 可以云端托管 Libki 服务器,这使得每个人的维护和管理变得更加简单。”
|
||||
|
||||
Kyle 表示,ByWater 并不打算将 Libki 与其最受欢迎的产品,开源集成图书馆系统 (ILS)Koha 或其支持的任何其他[项目][7]捆绑在一起。他说: “Libki 和 Koha 是不同[类型]的软件,满足不同的需求,但它们在图书馆中确实很好地协同工作。事实上,我很早就开发了 Libki 的 SIP2 集成,因此它可以支持使用 Koha 进行单点登录。“
|
||||
|
||||
### 如何贡献
|
||||
|
||||
Libki 客户端是 GPLv3 许可,Libki 服务器是 AGPLv3 许可。Kyle 说他希望 Libki 拥有一个更加活跃和强大的社区,项目一直在寻找新人加入其[贡献者][8]。如果你想参加,请访问 [Libki 社区页面][9]并加入邮件列表。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/libki-computer-access
|
||||
|
||||
作者:[Don Watkins][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/don-watkins/users/tony-thomas
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/desk_clock_job_work.jpg?itok=Nj4fuhl6
|
||||
[2]: https://libki.org/
|
||||
[3]: https://www.linkedin.com/in/kylemhallinfo/
|
||||
[4]: https://opensource.com/article/19/4/software-libraries
|
||||
[5]: https://meadvillelibrary.org/
|
||||
[6]: https://manual.libki.org/master/libki-manual.html#_automatic_installation
|
||||
[7]: https://bywatersolutions.com/projects
|
||||
[8]: https://github.com/Libki/libki-server/graphs/contributors
|
||||
[9]: https://libki.org/community/
|
693
published/201905/20190503 API evolution the right way.md
Normal file
693
published/201905/20190503 API evolution the right way.md
Normal file
@ -0,0 +1,693 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (MjSeven)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10900-1.html)
|
||||
[#]: subject: (API evolution the right way)
|
||||
[#]: via: (https://opensource.com/article/19/5/api-evolution-right-way)
|
||||
[#]: author: (A. Jesse https://opensource.com/users/emptysquare)
|
||||
|
||||
API 演进的正确方式
|
||||
======
|
||||
|
||||
> 负责任的库作者与其用户的十个约定。
|
||||
|
||||
![Browser of things](https://img.linux.net.cn/data/attachment/album/201905/26/134131jnymeg7t7gmo6qcy.jpg)
|
||||
|
||||
想象一下你是一个造物主,为一个生物设计一个身体。出于仁慈,你希望它能随着时间进化:首先,因为它必须对环境的变化作出反应;其次,因为你的智慧在增长,你对这个小东西想到了更好的设计,它不应该永远保持一个样子。
|
||||
|
||||
![Serpents][2]
|
||||
|
||||
然而,这个生物可能有赖于其目前解剖学的特征。你不能无所顾忌地添加翅膀或改变它的身材比例。它需要一个有序的过程来适应新的身体。作为一个负责任的设计者,你如何才能温柔地引导这种生物走向更大的进步呢?
|
||||
|
||||
对于负责任的库维护者也是如此。我们向依赖我们代码的人保证我们的承诺:我们会发布 bug 修复和有用的新特性。如果对库的未来有利,我们有时会删除某些特性。我们会不断创新,但我们不会破坏使用我们库的人的代码。我们怎样才能一次实现所有这些目标呢?
|
||||
|
||||
### 添加有用的特性
|
||||
|
||||
你的库不应该永远保持不变:你应该添加一些特性,使你的库更适合用户。例如,如果你有一个爬行动物类,并且如果有个可以飞行的翅膀是有用的,那就去添加吧。
|
||||
|
||||
```
|
||||
class Reptile:
|
||||
@property
|
||||
def teeth(self):
|
||||
return 'sharp fangs'
|
||||
|
||||
# 如果 wings 是有用的,那就添加它!
|
||||
@property
|
||||
def wings(self):
|
||||
return 'majestic wings'
|
||||
```
|
||||
|
||||
但要注意,特性是有风险的。考虑 Python 标准库中以下功能,看看它出了什么问题。
|
||||
|
||||
```
|
||||
bool(datetime.time(9, 30)) == True
|
||||
bool(datetime.time(0, 0)) == False
|
||||
```
|
||||
|
||||
这很奇怪:将任何时间对象转换为布尔值都会得到 True,但午夜时间除外。(更糟糕的是,时区感知时间的规则更加奇怪。)
|
||||
|
||||
我已经写了十多年的 Python 了,但直到上周才发现这条规则。这种奇怪的行为会在用户代码中引起什么样的 bug?
|
||||
|
||||
比如说一个日历应用程序,它带有一个创建事件的函数。如果一个事件有一个结束时间,那么函数也应该要求它有一个开始时间。
|
||||
|
||||
```
|
||||
def create_event(day,
|
||||
start_time=None,
|
||||
end_time=None):
|
||||
if end_time and not start_time:
|
||||
raise ValueError("Can't pass end_time without start_time")
|
||||
|
||||
# 女巫集会从午夜一直开到凌晨 4 点
|
||||
create_event(datetime.date.today(),
|
||||
datetime.time(0, 0),
|
||||
datetime.time(4, 0))
|
||||
```
|
||||
|
||||
不幸的是,对于女巫来说,从午夜开始的事件无法通过校验。当然,一个了解午夜怪癖的细心程序员可以正确地编写这个函数。
|
||||
|
||||
```
|
||||
def create_event(day,
|
||||
start_time=None,
|
||||
end_time=None):
|
||||
if end_time is not None and start_time is None:
|
||||
raise ValueError("Can't pass end_time without start_time")
|
||||
```
|
||||
|
||||
但这种微妙之处令人担忧。如果一个库作者想要创建一个伤害用户的 API,那么像午夜的布尔转换这样的“特性”很有效。
|
||||
|
||||
![Man being chased by an alligator][3]
|
||||
|
||||
但是,负责任的创建者的目标是使你的库易于正确使用。
|
||||
|
||||
这个功能是由 Tim Peters 在 2002 年首次编写 datetime 模块时造成的。即时是像 Tim 这样的奠基 Python 的高手也会犯错误。[这个怪异之处后来被消除了][4],现在所有时间的布尔值都是 True。
|
||||
|
||||
```
|
||||
# Python 3.5 以后
|
||||
|
||||
bool(datetime.time(9, 30)) == True
|
||||
bool(datetime.time(0, 0)) == True
|
||||
```
|
||||
|
||||
不知道午夜怪癖的古怪之处的程序员现在可以从这种晦涩的 bug 中解脱出来,但是一想到任何依赖于古怪的旧行为的代码现在没有注意变化,我就会感到紧张。如果从来没有实现这个糟糕的特性,情况会更好。这就引出了库维护者的第一个承诺:
|
||||
|
||||
#### 第一个约定:避免糟糕的特性
|
||||
|
||||
最痛苦的变化是你必须删除一个特性。一般来说,避免糟糕特性的一种方法是少添加特性!没有充分的理由,不要使用公共方法、类、功能或属性。因此:
|
||||
|
||||
#### 第二个约定:最小化特性
|
||||
|
||||
特性就像孩子:在充满激情的瞬间孕育,但是它们必须要支持多年(LCTT 译注:我怀疑作者在开车,可是我没有证据)。不要因为你能做傻事就去做傻事。不要画蛇添足!
|
||||
|
||||
![Serpents with and without feathers][5]
|
||||
|
||||
但是,当然,在很多情况下,用户需要你的库中尚未提供的东西,你如何选择合适的功能给他们?以下另一个警示故事。
|
||||
|
||||
### 一个来自 asyncio 的警示故事
|
||||
|
||||
你可能知道,当你调用一个协程函数,它会返回一个协程对象:
|
||||
|
||||
```
|
||||
async def my_coroutine():
|
||||
pass
|
||||
|
||||
print(my_coroutine())
|
||||
```
|
||||
|
||||
```
|
||||
<coroutine object my_coroutine at 0x10bfcbac8>
|
||||
```
|
||||
|
||||
你的代码必须 “<ruby>等待<rt>await</rt></ruby>” 这个对象以此来运行协程。人们很容易忘记这一点,所以 asyncio 的开发人员想要一个“调试模式”来捕捉这个错误。当协程在没有等待的情况下被销毁时,调试模式将打印一个警告,并在其创建的行上进行回溯。
|
||||
|
||||
当 Yury Selivanov 实现调试模式时,他添加了一个“协程装饰器”的基础特性。装饰器是一个函数,它接收一个协程并返回任何内容。Yury 使用它在每个协程上接入警告逻辑,但是其他人可以使用它将协程转换为字符串 “hi!”。
|
||||
|
||||
```
|
||||
import sys
|
||||
|
||||
def my_wrapper(coro):
|
||||
return 'hi!'
|
||||
|
||||
sys.set_coroutine_wrapper(my_wrapper)
|
||||
|
||||
async def my_coroutine():
|
||||
pass
|
||||
|
||||
print(my_coroutine())
|
||||
```
|
||||
|
||||
```
|
||||
hi!
|
||||
```
|
||||
|
||||
这是一个地狱般的定制。它改变了 “<ruby>异步<rt>async</rt></ruby>" 的含义。调用一次 `set_coroutine_wrapper` 将在全局永久改变所有的协程函数。正如 [Nathaniel Smith 所说][6]:“一个有问题的 API” 很容易被误用,必须被删除。如果 asyncio 开发人员能够更好地按照其目标来设计该特性,他们就可以避免删除该特性的痛苦。负责任的创建者必须牢记这一点:
|
||||
|
||||
#### 第三个约定:保持特性单一
|
||||
|
||||
幸运的是,Yury 有良好的判断力,他将该特性标记为临时,所以 asyncio 用户知道不能依赖它。Nathaniel 可以用更单一的功能替换 `set_coroutine_wrapper`,该特性只定制回溯深度。
|
||||
|
||||
```
|
||||
import sys
|
||||
|
||||
sys.set_coroutine_origin_tracking_depth(2)
|
||||
|
||||
async def my_coroutine():
|
||||
pass
|
||||
|
||||
print(my_coroutine())
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
<coroutine object my_coroutine at 0x10bfcbac8>
|
||||
|
||||
RuntimeWarning:'my_coroutine' was never awaited
|
||||
|
||||
Coroutine created at (most recent call last)
|
||||
File "script.py", line 8, in <module>
|
||||
print(my_coroutine())
|
||||
```
|
||||
|
||||
这样好多了。没有可以更改协程的类型的其他全局设置,因此 asyncio 用户无需编写防御代码。造物主应该像 Yury 一样有远见。
|
||||
|
||||
#### 第四个约定:标记实验特征“临时”
|
||||
|
||||
如果你只是预感你的生物需要犄角和四叉舌,那就引入这些特性,但将它们标记为“临时”。
|
||||
|
||||
![Serpent with horns][7]
|
||||
|
||||
你可能会发现犄角是无关紧要的,但是四叉舌是有用的。在库的下一个版本中,你可以删除前者并标记后者为正式的。
|
||||
|
||||
### 删除特性
|
||||
|
||||
无论我们如何明智地指导我们的生物进化,总会有一天想要删除一个正式特征。例如,你可能已经创建了一只蜥蜴,现在你选择删除它的腿。也许你想把这个笨拙的家伙变成一条时尚而现代的蟒蛇。
|
||||
|
||||
![Lizard transformed to snake][8]
|
||||
|
||||
删除特性主要有两个原因。首先,通过用户反馈或者你自己不断增长的智慧,你可能会发现某个特性是个坏主意。午夜怪癖的古怪行为就是这种情况。或者,最初该特性可能已经很好地适应了你的库环境,但现在生态环境发生了变化,也许另一个神发明了哺乳动物,你的生物想要挤进哺乳动物的小洞穴里,吃掉里面美味的哺乳动物,所以它不得不失去双腿。
|
||||
|
||||
![A mouse][9]
|
||||
|
||||
同样,Python 标准库会根据语言本身的变化删除特性。考虑 asyncio 的 Lock 功能,在把 `await` 作为一个关键字添加进来之前,它一直在等待:
|
||||
|
||||
```
|
||||
lock = asyncio.Lock()
|
||||
|
||||
async def critical_section():
|
||||
await lock
|
||||
try:
|
||||
print('holding lock')
|
||||
finally:
|
||||
lock.release()
|
||||
```
|
||||
|
||||
但是现在,我们可以做“异步锁”:
|
||||
|
||||
|
||||
```
|
||||
lock = asyncio.Lock()
|
||||
|
||||
async def critical_section():
|
||||
async with lock:
|
||||
print('holding lock')
|
||||
```
|
||||
|
||||
新方法好多了!很短,并且在一个大函数中使用其他 try-except 块时不容易出错。因为“尽量找一种,最好是唯一一种明显的解决方案”,[旧语法在 Python 3.7 中被弃用][10],并且很快就会被禁止。
|
||||
|
||||
不可避免的是,生态变化会对你的代码产生影响,因此要学会温柔地删除特性。在此之前,请考虑删除它的成本或好处。负责任的维护者不会愿意让用户更改大量代码或逻辑。(还记得 Python 3 在重新添加会 `u` 字符串前缀之前删除它是多么痛苦吗?)如果代码删除是机械性的动作,就像一个简单的搜索和替换,或者如果该特性是危险的,那么它可能值得删除。
|
||||
|
||||
#### 是否删除特性
|
||||
|
||||
![Balance scales][11]
|
||||
|
||||
反对 | 支持
|
||||
---|---
|
||||
代码必须改变 | 改变是机械性的
|
||||
逻辑必须改变 | 特性是危险的
|
||||
|
||||
就我们饥饿的蜥蜴而言,我们决定删除它的腿,这样它就可以滑进老鼠洞里吃掉它。我们该怎么做呢?我们可以删除 `walk` 方法,像下面一样修改代码:
|
||||
|
||||
```
|
||||
class Reptile:
|
||||
def walk(self):
|
||||
print('step step step')
|
||||
```
|
||||
|
||||
变成这样:
|
||||
|
||||
```
|
||||
class Reptile:
|
||||
def slither(self):
|
||||
print('slide slide slide')
|
||||
```
|
||||
|
||||
这不是一个好主意,这个生物习惯于走路!或者,就库而言,你的用户拥有依赖于现有方法的代码。当他们升级到最新库版本时,他们的代码将会崩溃。
|
||||
|
||||
```
|
||||
# 用户的代码,哦,不!
|
||||
Reptile.walk()
|
||||
```
|
||||
|
||||
因此,负责任的创建者承诺:
|
||||
|
||||
#### 第五条预定:温柔地删除
|
||||
|
||||
温柔地删除一个特性需要几个步骤。从用腿走路的蜥蜴开始,首先添加新方法 `slither`。接下来,弃用旧方法。
|
||||
|
||||
```
|
||||
import warnings
|
||||
|
||||
class Reptile:
|
||||
def walk(self):
|
||||
warnings.warn(
|
||||
"walk is deprecated, use slither",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
print('step step step')
|
||||
|
||||
def slither(self):
|
||||
print('slide slide slide')
|
||||
```
|
||||
|
||||
Python 的 warnings 模块非常强大。默认情况下,它会将警告输出到 stderr,每个代码位置只显示一次,但你可以禁用警告或将其转换为异常,以及其它选项。
|
||||
|
||||
一旦将这个警告添加到库中,PyCharm 和其他 IDE 就会使用删除线呈现这个被弃用的方法。用户马上就知道该删除这个方法。
|
||||
|
||||
> Reptile().~~walk()~~
|
||||
|
||||
当他们使用升级后的库运行代码时会发生什么?
|
||||
|
||||
```
|
||||
$ python3 script.py
|
||||
|
||||
DeprecationWarning: walk is deprecated, use slither
|
||||
script.py:14: Reptile().walk()
|
||||
|
||||
step step step
|
||||
```
|
||||
|
||||
默认情况下,他们会在 stderr 上看到警告,但脚本会成功并打印 “step step step”。警告的回溯显示必须修复用户代码的哪一行。(这就是 `stacklevel` 参数的作用:它显示了用户需要更改的调用,而不是库中生成警告的行。)请注意,错误消息有指导意义,它描述了库用户迁移到新版本必须做的事情。
|
||||
|
||||
你的用户可能会希望测试他们的代码,并证明他们没有调用弃用的库方法。仅警告不会使单元测试失败,但异常会失败。Python 有一个命令行选项,可以将弃用警告转换为异常。
|
||||
|
||||
```
|
||||
> python3 -Werror::DeprecationWarning script.py
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "script.py", line 14, in <module>
|
||||
Reptile().walk()
|
||||
File "script.py", line 8, in walk
|
||||
DeprecationWarning, stacklevel=2)
|
||||
DeprecationWarning: walk is deprecated, use slither
|
||||
```
|
||||
|
||||
现在,“step step step” 没有输出出来,因为脚本以一个错误终止。
|
||||
|
||||
因此,一旦你发布了库的一个版本,该版本会警告已启用的 `walk` 方法,你就可以在下一个版本中安全地删除它。对吧?
|
||||
|
||||
考虑一下你的库用户在他们项目的 `requirements` 中可能有什么。
|
||||
|
||||
```
|
||||
# 用户的 requirements.txt 显示 reptile 包的依赖关系
|
||||
reptile
|
||||
```
|
||||
|
||||
下次他们部署代码时,他们将安装最新版本的库。如果他们尚未处理所有的弃用,那么他们的代码将会崩溃,因为代码仍然依赖 `walk`。你需要温柔一点,你必须向用户做出三个承诺:维护更改日志,选择版本化方案和编写升级指南。
|
||||
|
||||
#### 第六个约定:维护变更日志
|
||||
|
||||
你的库必须有更改日志,其主要目的是宣布用户所依赖的功能何时被弃用或删除。
|
||||
|
||||
> **版本 1.1 中的更改**
|
||||
>
|
||||
> **新特性**
|
||||
>
|
||||
> * 新功能 Reptile.slither()
|
||||
>
|
||||
> **弃用**
|
||||
>
|
||||
> * Reptile.walk() 已弃用,将在 2.0 版本中删除,请使用 slither()
|
||||
|
||||
负责任的创建者会使用版本号来表示库发生了怎样的变化,以便用户能够对升级做出明智的决定。“版本化方案”是一种用于交流变化速度的语言。
|
||||
|
||||
#### 第七个约定:选择一个版本化方案
|
||||
|
||||
有两种广泛使用的方案,[语义版本控制][12]和基于时间的版本控制。我推荐任何库都进行语义版本控制。Python 的风格在 [PEP 440][13] 中定义,像 `pip` 这样的工具可以理解语义版本号。
|
||||
|
||||
如果你为库选择语义版本控制,你可以使用版本号温柔地删除腿,例如:
|
||||
|
||||
> 1.0: 第一个“稳定”版,带有 `walk()`
|
||||
> 1.1: 添加 `slither()`,废弃 `walk()`
|
||||
> 2.0: 删除 `walk()`
|
||||
|
||||
你的用户依赖于你的库的版本应该有一个范围,例如:
|
||||
|
||||
```
|
||||
# 用户的 requirements.txt
|
||||
reptile>=1,<2
|
||||
```
|
||||
|
||||
这允许他们在主要版本中自动升级,接收错误修正并可能引发一些弃用警告,但不会升级到**下**个主要版本并冒着更改破坏其代码的风险。
|
||||
|
||||
如果你遵循基于时间的版本控制,则你的版本可能会编号:
|
||||
|
||||
> 2017.06.0: 2017 年 6 月的版本
|
||||
> 2018.11.0: 添加 `slither()`,废弃 `walk()`
|
||||
> 2019.04.0: 删除 `walk()`
|
||||
|
||||
用户可以这样依赖于你的库:
|
||||
|
||||
```
|
||||
# 用户的 requirements.txt,基于时间控制的版本
|
||||
reptile==2018.11.*
|
||||
```
|
||||
|
||||
这非常棒,但你的用户如何知道你的版本方案,以及如何测试代码来进行弃用呢?你必须告诉他们如何升级。
|
||||
|
||||
#### 第八个约定:写一个升级指南
|
||||
|
||||
下面是一个负责任的库创建者如何指导用户:
|
||||
|
||||
> **升级到 2.0**
|
||||
>
|
||||
> **从弃用的 API 迁移**
|
||||
>
|
||||
> 请参阅更改日志以了解已弃用的特性。
|
||||
>
|
||||
> **启用弃用警告**
|
||||
>
|
||||
> 升级到 1.1 并使用以下代码测试代码:
|
||||
>
|
||||
> `python -Werror::DeprecationWarning`
|
||||
>
|
||||
> 现在可以安全地升级了。
|
||||
|
||||
你必须通过向用户显示命令行选项来教会用户如何处理弃用警告。并非所有 Python 程序员都知道这一点 —— 我自己就每次都得查找这个语法。注意,你必须*发布*一个版本,它输出来自每个弃用的 API 的警告,以便用户可以在再次升级之前使用该版本进行测试。在本例中,1.1 版本是小版本。它允许你的用户逐步重写代码,分别修复每个弃用警告,直到他们完全迁移到最新的 API。他们可以彼此独立地测试代码和库的更改,并隔离 bug 的原因。
|
||||
|
||||
如果你选择语义版本控制,则此过渡期将持续到下一个主要版本,从 1.x 到 2.0,或从 2.x 到 3.0 以此类推。删除生物腿部的温柔方法是至少给它一个版本来调整其生活方式。不要一次性把腿删掉!
|
||||
|
||||
![A skink][14]
|
||||
|
||||
版本号、弃用警告、更改日志和升级指南可以协同工作,在不违背与用户约定的情况下温柔地改进你的库。[Twisted 项目的兼容性政策][15] 解释的很漂亮:
|
||||
|
||||
> “先行者总是自由的”
|
||||
>
|
||||
> 运行的应用程序在没有任何警告的情况下都可以升级为 Twisted 的一个次要版本。
|
||||
>
|
||||
> 换句话说,任何运行其测试而不触发 Twisted 警告的应用程序应该能够将其 Twisted 版本升级至少一次,除了可能产生新警告之外没有任何不良影响。
|
||||
>
|
||||
|
||||
现在,我们的造物主已经获得了智慧和力量,可以通过添加方法来添加特性,并温柔地删除它们。我们还可以通过添加参数来添加特性,但这带来了新的难度。你准备好了吗?
|
||||
|
||||
### 添加参数
|
||||
|
||||
想象一下,你只是给了你的蛇形生物一对翅膀。现在你必须允许它选择是滑行还是飞行。目前它的 `move` 功能只接受一个参数。
|
||||
|
||||
```
|
||||
# 你的库代码
|
||||
def move(direction):
|
||||
print(f'slither {direction}')
|
||||
|
||||
# 用户的应用
|
||||
move('north')
|
||||
```
|
||||
|
||||
你想要添加一个 `mode` 参数,但如果用户升级库,这会破坏他们的代码,因为他们只传递了一个参数。
|
||||
|
||||
```
|
||||
# 你的库代码
|
||||
def move(direction, mode):
|
||||
assert mode in ('slither', 'fly')
|
||||
print(f'{mode} {direction}')
|
||||
|
||||
# 一个用户的代码,出现错误!
|
||||
move('north')
|
||||
```
|
||||
|
||||
一个真正聪明的创建者者会承诺不会以这种方式破坏用户的代码。
|
||||
|
||||
#### 第九条约定:兼容地添加参数
|
||||
|
||||
要保持这个约定,请使用保留原始行为的默认值添加每个新参数。
|
||||
|
||||
```
|
||||
# 你的库代码
|
||||
def move(direction, mode='slither'):
|
||||
assert mode in ('slither', 'fly')
|
||||
print(f'{mode} {direction}')
|
||||
|
||||
# 用户的应用
|
||||
move('north')
|
||||
```
|
||||
|
||||
随着时间推移,参数是函数演化的自然历史。它们首先列出最老的参数,每个都有默认值。库用户可以传递关键字参数以选择特定的新行为,并接受所有其他行为的默认值。
|
||||
|
||||
```
|
||||
# 你的库代码
|
||||
def move(direction,
|
||||
mode='slither',
|
||||
turbo=False,
|
||||
extra_sinuous=False,
|
||||
hail_lyft=False):
|
||||
# ...
|
||||
|
||||
# 用户应用
|
||||
move('north', extra_sinuous=True)
|
||||
```
|
||||
|
||||
但是有一个危险,用户可能会编写如下代码:
|
||||
|
||||
```
|
||||
# 用户应用,简写
|
||||
move('north', 'slither', False, True)
|
||||
```
|
||||
|
||||
如果在你在库的下一个主要版本中去掉其中一个参数,例如 `turbo`,会发生什么?
|
||||
|
||||
```
|
||||
# 你的库代码,下一个主要版本中 "turbo" 被删除
|
||||
def move(direction,
|
||||
mode='slither',
|
||||
extra_sinuous=False,
|
||||
hail_lyft=False):
|
||||
# ...
|
||||
|
||||
# 用户应用,简写
|
||||
move('north', 'slither', False, True)
|
||||
```
|
||||
|
||||
用户的代码仍然能编译,这是一件坏事。代码停止了曲折的移动并开始招呼 Lyft,这不是它的本意。我相信你可以预测我接下来要说的内容:删除参数需要几个步骤。当然,首先弃用 `trubo` 参数。我喜欢这种技术,它可以检测任何用户的代码是否依赖于这个参数。
|
||||
|
||||
```
|
||||
# 你的库代码
|
||||
_turbo_default = object()
|
||||
|
||||
def move(direction,
|
||||
mode='slither',
|
||||
turbo=_turbo_default,
|
||||
extra_sinuous=False,
|
||||
hail_lyft=False):
|
||||
if turbo is not _turbo_default:
|
||||
warnings.warn(
|
||||
"'turbo' is deprecated",
|
||||
DeprecationWarning,
|
||||
stacklevel=2)
|
||||
else:
|
||||
# The old default.
|
||||
turbo = False
|
||||
```
|
||||
|
||||
但是你的用户可能不会注意到警告。警告声音不是很大:它们可以在日志文件中被抑制或丢失。用户可能会漫不经心地升级到库的下一个主要版本——那个删除 `turbo` 的版本。他们的代码运行时将没有错误、默默做错误的事情!正如 Python 之禅所说:“错误绝不应该被默默 pass”。实际上,爬行动物的听力很差,所有当它们犯错误时,你必须非常大声地纠正它们。
|
||||
|
||||
![Woman riding an alligator][16]
|
||||
|
||||
保护用户的最佳方法是使用 Python 3 的星型语法,它要求调用者传递关键字参数。
|
||||
|
||||
```
|
||||
# 你的库代码
|
||||
# 所有 “*” 后的参数必须以关键字方式传输。
|
||||
def move(direction,
|
||||
*,
|
||||
mode='slither',
|
||||
turbo=False,
|
||||
extra_sinuous=False,
|
||||
hail_lyft=False):
|
||||
# ...
|
||||
|
||||
# 用户代码,简写
|
||||
# 错误!不能使用位置参数,关键字参数是必须的
|
||||
move('north', 'slither', False, True)
|
||||
```
|
||||
|
||||
有了这个星,以下是唯一允许的语法:
|
||||
|
||||
```
|
||||
# 用户代码
|
||||
move('north', extra_sinuous=True)
|
||||
```
|
||||
|
||||
现在,当你删除 `turbo` 时,你可以确定任何依赖于它的用户代码都会明显地提示失败。如果你的库也支持 Python2,这没有什么大不了。你可以模拟星型语法([归功于 Brett Slatkin][17]):
|
||||
|
||||
```
|
||||
# 你的库代码,兼容 Python 2
|
||||
def move(direction, **kwargs):
|
||||
mode = kwargs.pop('mode', 'slither')
|
||||
turbo = kwargs.pop('turbo', False)
|
||||
sinuous = kwargs.pop('extra_sinuous', False)
|
||||
lyft = kwargs.pop('hail_lyft', False)
|
||||
|
||||
if kwargs:
|
||||
raise TypeError('Unexpected kwargs: %r'
|
||||
% kwargs)
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
要求关键字参数是一个明智的选择,但它需要远见。如果允许按位置传递参数,则不能仅在以后的版本中将其转换为仅关键字。所以,现在加上星号。你可以在 asyncio API 中观察到,它在构造函数、方法和函数中普遍使用星号。尽管到目前为止,`Lock` 只接受一个可选参数,但 asyncio 开发人员立即添加了星号。这是幸运的。
|
||||
|
||||
```
|
||||
# In asyncio.
|
||||
class Lock:
|
||||
def __init__(self, *, loop=None):
|
||||
# ...
|
||||
```
|
||||
|
||||
现在,我们已经获得了改变方法和参数的智慧,同时保持与用户的约定。现在是时候尝试最具挑战性的进化了:在不改变方法或参数的情况下改变行为。
|
||||
|
||||
### 改变行为
|
||||
|
||||
假设你创造的生物是一条响尾蛇,你想教它一种新行为。
|
||||
|
||||
![Rattlesnake][18]
|
||||
|
||||
横向移动!这个生物的身体看起来是一样的,但它的行为会发生变化。我们如何为这一进化步骤做好准备?
|
||||
|
||||
![][19]
|
||||
|
||||
*Image by HCA [[CC BY-SA 4.0][20]], [via Wikimedia Commons][21], 由 Opensource.com 修改*
|
||||
|
||||
当行为在没有新函数或新参数的情况下发生更改时,负责任的创建者可以从 Python 标准库中学习。很久以前,os 模块引入了 `stat` 函数来获取文件统计信息,比如创建时间。起初,这个时间总是整数。
|
||||
|
||||
```
|
||||
>>> os.stat('file.txt').st_ctime
|
||||
1540817862
|
||||
```
|
||||
|
||||
有一天,核心开发人员决定在 `os.stat` 中使用浮点数来提供亚秒级精度。但他们担心现有的用户代码还没有做好准备更改。于是他们在 Python 2.3 中创建了一个设置 `stat_float_times`,默认情况下是 `False` 。用户可以将其设置为 True 来选择浮点时间戳。
|
||||
|
||||
```
|
||||
>>> # Python 2.3.
|
||||
>>> os.stat_float_times(True)
|
||||
>>> os.stat('file.txt').st_ctime
|
||||
1540817862.598021
|
||||
```
|
||||
|
||||
从 Python 2.5 开始,浮点时间成为默认值,因此 2.5 及之后版本编写的任何新代码都可以忽略该设置并期望得到浮点数。当然,你可以将其设置为 `False` 以保持旧行为,或将其设置为 `True` 以确保所有 Python 版本都得到浮点数,并为删除 `stat_float_times` 的那一天准备代码。
|
||||
|
||||
多年过去了,在 Python 3.1 中,该设置已被弃用,以便为人们为遥远的未来做好准备,最后,经过数十年的旅程,[这个设置被删除][22]。浮点时间现在是唯一的选择。这是一个漫长的过程,但负责任的神灵是有耐心的,因为我们知道这个渐进的过程很有可能于意外的行为变化拯救用户。
|
||||
|
||||
#### 第十个约定:逐渐改变行为
|
||||
|
||||
以下是步骤:
|
||||
|
||||
* 添加一个标志来选择新行为,默认为 `False`,如果为 `False` 则发出警告
|
||||
* 将默认值更改为 `True`,表示完全弃用标记
|
||||
* 删除该标志
|
||||
|
||||
如果你遵循语义版本控制,版本可能如下:
|
||||
|
||||
库版本 | 库 API | 用户代码
|
||||
---|---|---
|
||||
1.0 | 没有标志 | 预期的旧行为
|
||||
1.1 | 添加标志,默认为 `False`,如果是 `False`,则警告 | 设置标志为 `True`,处理新行为
|
||||
2.0 | 改变默认为 `True`,完全弃用标志 | 处理新行为
|
||||
3.0 | 移除标志 | 处理新行为
|
||||
|
||||
你需要**两**个主要版本来完成该操作。如果你直接从“添加标志,默认为 `False`,如果是 `False` 则发出警告”变到“删除标志”,而没有中间版本,那么用户的代码将无法升级。为 1.1 正确编写的用户代码必须能够升级到下一个版本,除了新警告之外,没有任何不良影响,但如果在下一个版本中删除了该标志,那么该代码将崩溃。一个负责任的神明从不违反扭曲的政策:“先行者总是自由的”。
|
||||
|
||||
### 负责任的创建者
|
||||
|
||||
![Demeter][23]
|
||||
|
||||
我们的 10 个约定大致可以分为三类:
|
||||
|
||||
**谨慎发展**
|
||||
|
||||
1. 避免不良功能
|
||||
2. 最小化特性
|
||||
3. 保持功能单一
|
||||
4. 标记实验特征“临时”
|
||||
5. 温柔删除功能
|
||||
|
||||
**严格记录历史**
|
||||
|
||||
1. 维护更改日志
|
||||
2. 选择版本方案
|
||||
3. 编写升级指南
|
||||
|
||||
**缓慢而明显地改变**
|
||||
|
||||
1. 兼容添加参数
|
||||
2. 逐渐改变行为
|
||||
|
||||
如果你对你所创造的物种保持这些约定,你将成为一个负责任的造物主。你的生物的身体可以随着时间的推移而进化,一直在改善和适应环境的变化,而不是在生物没有准备好就突然改变。如果你维护一个库,请向用户保留这些承诺,这样你就可以在不破坏依赖该库的代码的情况下对库进行更新。
|
||||
|
||||
* * *
|
||||
|
||||
_这篇文章最初是在 [A. Jesse Jiryu Davis 的博客上'][24]出现的,经允许转载。_
|
||||
|
||||
插图参考:
|
||||
|
||||
* [《世界进步》, Delphian Society, 1913][25]
|
||||
* [《走进蛇的历史》, Charles Owen, 1742][26]
|
||||
* [关于哥斯达黎加的 batrachia 和爬行动物,关于尼加拉瓜和秘鲁的爬行动物和鱼类学的记录, Edward Drinker Cope, 1875][27]
|
||||
* [《自然史》, Richard Lydekker et. al., 1897][28]
|
||||
* [Mes Prisons, Silvio Pellico, 1843][29]
|
||||
* [Tierfotoagentur / m.blue-shadow][30]
|
||||
* [洛杉矶公共图书馆, 1930][31]
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/api-evolution-right-way
|
||||
|
||||
作者:[A. Jesse][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/emptysquare
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/browser_desktop_website_checklist_metrics.png?itok=OKKbl1UR (Browser of things)
|
||||
[2]: https://opensource.com/sites/default/files/uploads/praise-the-creator.jpg (Serpents)
|
||||
[3]: https://opensource.com/sites/default/files/uploads/bite.jpg (Man being chased by an alligator)
|
||||
[4]: https://bugs.python.org/issue13936
|
||||
[5]: https://opensource.com/sites/default/files/uploads/feathers.jpg (Serpents with and without feathers)
|
||||
[6]: https://bugs.python.org/issue32591
|
||||
[7]: https://opensource.com/sites/default/files/uploads/horns.jpg (Serpent with horns)
|
||||
[8]: https://opensource.com/sites/default/files/uploads/lizard-to-snake.jpg (Lizard transformed to snake)
|
||||
[9]: https://opensource.com/sites/default/files/uploads/mammal.jpg (A mouse)
|
||||
[10]: https://bugs.python.org/issue32253
|
||||
[11]: https://opensource.com/sites/default/files/uploads/scale.jpg (Balance scales)
|
||||
[12]: https://semver.org
|
||||
[13]: https://www.python.org/dev/peps/pep-0440/
|
||||
[14]: https://opensource.com/sites/default/files/uploads/skink.jpg (A skink)
|
||||
[15]: https://twistedmatrix.com/documents/current/core/development/policy/compatibility-policy.html
|
||||
[16]: https://opensource.com/sites/default/files/uploads/loudly.jpg (Woman riding an alligator)
|
||||
[17]: http://www.informit.com/articles/article.aspx?p=2314818
|
||||
[18]: https://opensource.com/sites/default/files/uploads/rattlesnake.jpg (Rattlesnake)
|
||||
[19]: https://opensource.com/sites/default/files/articles/neonate_sidewinder_sidewinding_with_tracks_unlabeled.png
|
||||
[20]: https://creativecommons.org/licenses/by-sa/4.0
|
||||
[21]: https://commons.wikimedia.org/wiki/File:Neonate_sidewinder_sidewinding_with_tracks_unlabeled.jpg
|
||||
[22]: https://bugs.python.org/issue31827
|
||||
[23]: https://opensource.com/sites/default/files/uploads/demeter.jpg (Demeter)
|
||||
[24]: https://emptysqua.re/blog/api-evolution-the-right-way/
|
||||
[25]: https://www.gutenberg.org/files/42224/42224-h/42224-h.htm
|
||||
[26]: https://publicdomainreview.org/product-att/artist/charles-owen/
|
||||
[27]: https://archive.org/details/onbatrachiarepti00cope/page/n3
|
||||
[28]: https://www.flickr.com/photos/internetarchivebookimages/20556001490
|
||||
[29]: https://www.oldbookillustrations.com/illustrations/stationery/
|
||||
[30]: https://www.alamy.com/mediacomp/ImageDetails.aspx?ref=D7Y61W
|
||||
[31]: https://www.vintag.es/2013/06/riding-alligator-c-1930s.html
|
@ -0,0 +1,82 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10895-1.html)
|
||||
[#]: subject: (Check your spelling at the command line with Ispell)
|
||||
[#]: via: (https://opensource.com/article/19/5/spelling-command-line-ispell)
|
||||
[#]: author: (Scott Nesbitt https://opensource.com/users/scottnesbitt)
|
||||
|
||||
使用 Ispell 在命令行中检查拼写
|
||||
======
|
||||
|
||||
> Ispell 可以帮助你在纯文本中消除超过 50 种语言的拼写错误。
|
||||
|
||||
![Command line prompt](https://img.linux.net.cn/data/attachment/album/201905/24/192644wqqv6d0lztmqoqyl.jpg)
|
||||
|
||||
好的拼写是一种技巧。它是一项需要时间学习和掌握的技能。也就是说,有些人从来没有完全掌握这种技能,我知道有两三个出色的作家就无法完全掌握拼写。
|
||||
|
||||
即使你拼写得很好,偶尔也会输入错字。特别是在最后期限前如果你快速敲击键盘,那就更是如此。无论你的拼写的是什么,通过拼写检查器检查你所写的内容总是一个好主意。
|
||||
|
||||
我用[纯文本][2]完成了我的大部分写作,并经常使用名为 [Aspell][3] 的命令行拼写检查器来完成这项工作。Aspell 不是唯一的工具。你可能还想要看下不错的 [Ispell][4]。
|
||||
|
||||
### 入门
|
||||
|
||||
自 1971 年以来,Ispell 就以各种形式出现过。不要被它的年龄欺骗。Ispell 仍然是一个可以在 21 世纪高效使用的应用。
|
||||
|
||||
在开始之前,请打开终端窗口并输入 `which ispell` 来检查计算机上是否安装了 Ispell。如果未安装,请打开发行版的软件包管理器并从那里安装 Ispell。
|
||||
|
||||
不要忘记为你使用的语言安装词典。我唯一使用的语言是英语,所以我只需下载美国和英国英语字典。你可以不局限于我的(也是唯一的)母语。Ispell 有[超过 50 种语言的词典][5]。
|
||||
|
||||
![Installing Ispell dictionaries][6]
|
||||
|
||||
### 使用 Ispell
|
||||
|
||||
如果你还没有猜到,Ispell 只能用在文本文件。这包括用 HTML、LaTeX 和 [nroff 或 troff][7] 标记的文档。之后会有更多相关内容。
|
||||
|
||||
要开始使用,请打开终端窗口并进入包含要运行拼写检查的文件的目录。输入 `ispell` 后跟文件名,然后按回车键。
|
||||
|
||||
![Checking spelling with Ispell][8]
|
||||
|
||||
Ispell 高亮了它无法识别的第一个词。如果单词拼写错误,Ispell 通常会提供一个或多个备选方案。按下 `R`,然后按下正确选择旁边的数字。在上面的截图中,我按了 `R` 和 `0` 来修复错误。
|
||||
|
||||
另一方面,如果单词拼写正确,请按下 `A` 然后移动到下一个拼写错误的单词。
|
||||
|
||||
继续这样做直到到达文件的末尾。Ispell 会保存你的更改,创建你刚检查的文件的备份(扩展名为 `.bak`),然后关闭。
|
||||
|
||||
### 其他几个选项
|
||||
|
||||
此示例说明了 Ispell 的基本用法。这个程序有[很多选项][9],有些你*可能*会用到,而另一些你*可能永远*不会使用。让我们快速看下我经常使用的一些。
|
||||
|
||||
之前我提到过 Ispell 可以用于某些标记语言。你需要告诉它文件的格式。启动 Ispell 时,为 TeX 或 LaTeX 文件添加 `-t`,为 HTML 文件添加 `-H`,对于 groff 或 troff 文件添加 `-n`。例如,如果输入 `ispell -t myReport.tex`,Ispell 将忽略所有标记。
|
||||
|
||||
如果你不想在检查文件后创建备份文件,请将 `-x` 添加到命令行。例如,`ispell -x myFile.txt`。
|
||||
|
||||
如果 Ispell 遇到拼写正确但不在其字典中的单词,比如名字,会发生什么?你可以按 `I` 将该单词添加到个人单词列表中。这会将单词保存到 `/home` 目录下的 `.ispell_default` 的文件中。
|
||||
|
||||
这些是我在使用 Ispell 时最有用的选项,但请查看 [Ispell 的手册页][9]以了解其所有选项。
|
||||
|
||||
Ispell 比 Aspell 或其他命令行拼写检查器更好或者更快么?我会说它不比其他的差或者慢。Ispell 不是适合所有人。它也许也不适合你。但有更多选择也不错,不是么?
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/spelling-command-line-ispell
|
||||
|
||||
作者:[Scott Nesbitt][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/scottnesbitt
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/command_line_prompt.png?itok=wbGiJ_yg (Command line prompt)
|
||||
[2]: https://plaintextproject.online
|
||||
[3]: https://opensource.com/article/18/2/how-check-spelling-linux-command-line-aspell
|
||||
[4]: https://www.cs.hmc.edu/~geoff/ispell.html
|
||||
[5]: https://www.cs.hmc.edu/~geoff/ispell-dictionaries.html
|
||||
[6]: https://opensource.com/sites/default/files/uploads/ispell-install-dictionaries.png (Installing Ispell dictionaries)
|
||||
[7]: https://opensource.com/article/18/2/how-format-academic-papers-linux-groff-me
|
||||
[8]: https://opensource.com/sites/default/files/uploads/ispell-checking.png (Checking spelling with Ispell)
|
||||
[9]: https://www.cs.hmc.edu/~geoff/ispell-man.html
|
@ -0,0 +1,105 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: ( )
|
||||
[#]: url: ( )
|
||||
[#]: subject: (Say goodbye to boilerplate in Python with attrs)
|
||||
[#]: via: (https://opensource.com/article/19/5/python-attrs)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshez/users/moshez)
|
||||
|
||||
使用 attrs 来告别 Python 中的样板
|
||||
======
|
||||
|
||||
> 在我们覆盖 7 个 PyPI 库的系列文章中了解更多解决 Python 问题的信息。
|
||||
|
||||
![Programming at a browser, orange hands](https://img.linux.net.cn/data/attachment/album/201905/18/211211lhqqbemqwkeqc2bb.jpg)
|
||||
|
||||
Python是当今使用最多[流行的编程语言][2]之一,因为:它是开源的,它具有广泛的用途(例如 Web 编程、业务应用、游戏、科学编程等等),它有一个充满活力和专注的社区支持它。这个社区是我们在 [Python Package Index][3](PyPI)中提供如此庞大、多样化的软件包的原因,用以扩展和改进 Python。并解决不可避免的问题。
|
||||
|
||||
在本系列中,我们将介绍七个可以帮助你解决常见 Python 问题的 PyPI 库。今天,我们将研究 [attrs][4],这是一个帮助你快速编写简洁、正确的代码的 Python 包。
|
||||
|
||||
### attrs
|
||||
|
||||
如果你已经写过一段时间的 Python,那么你可能习惯这样写代码:
|
||||
|
||||
```
|
||||
class Book(object):
|
||||
|
||||
def __init__(self, isbn, name, author):
|
||||
self.isbn = isbn
|
||||
self.name = name
|
||||
self.author = author
|
||||
```
|
||||
|
||||
接着写一个 `__repr__` 函数。否则,很难记录 `Book` 的实例:
|
||||
|
||||
|
||||
```
|
||||
def __repr__(self):
|
||||
return f"Book({self.isbn}, {self.name}, {self.author})"
|
||||
```
|
||||
|
||||
接下来你会写一个好看的 docstring 来记录期望的类型。但是你注意到你忘了添加 `edition` 和 `published_year` 属性,所以你必须在五个地方修改它们。
|
||||
|
||||
如果你不必这么做如何?
|
||||
|
||||
```
|
||||
@attr.s(auto_attribs=True)
|
||||
class Book(object):
|
||||
isbn: str
|
||||
name: str
|
||||
author: str
|
||||
published_year: int
|
||||
edition: int
|
||||
```
|
||||
|
||||
使用新的类型注释语法注释类型属性,`attrs` 会检测注释并创建一个类。
|
||||
|
||||
ISBN 有特定格式。如果我们想强行使用该格式怎么办?
|
||||
|
||||
```
|
||||
@attr.s(auto_attribs=True)
|
||||
class Book(object):
|
||||
isbn: str = attr.ib()
|
||||
@isbn.validator
|
||||
def pattern_match(self, attribute, value):
|
||||
m = re.match(r"^(\d{3}-)\d{1,3}-\d{2,3}-\d{1,7}-\d$", value)
|
||||
if not m:
|
||||
raise ValueError("incorrect format for isbn", value)
|
||||
name: str
|
||||
author: str
|
||||
published_year: int
|
||||
edition: int
|
||||
```
|
||||
|
||||
`attrs` 库也对[不可变式编程][5]支持良好。将第一行改成 `@attr.s(auto_attribs=True, frozen=True)` 意味着 `Book` 现在是不可变的:尝试修改一个属性将会引发一个异常。相反,比如,如果希望将发布日期向后一年,我们可以修改成 `attr.evolve(old_book, published_year=old_book.published_year+1)` 来得到一个*新的*实例。
|
||||
|
||||
本系列的下一篇文章我们将来看下 `singledispatch`,一个能让你向 Python 库添加方法的库。
|
||||
|
||||
#### 查看本系列先前的文章
|
||||
|
||||
* [Cython][6]
|
||||
* [Black][7]
|
||||
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/python-attrs
|
||||
|
||||
作者:[Moshe Zadka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/moshez/users/moshez
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/programming_code_keyboard_orange_hands.png?itok=G6tJ_64Y (Programming at a browser, orange hands)
|
||||
[2]: https://opensource.com/article/18/5/numbers-python-community-trends
|
||||
[3]: https://pypi.org/
|
||||
[4]: https://pypi.org/project/attrs/
|
||||
[5]: https://opensource.com/article/18/10/functional-programming-python-immutable-data-structures
|
||||
[6]: https://linux.cn/article-10859-1.html
|
||||
[7]: https://linux.cn/article-10864-1.html
|
@ -0,0 +1,105 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10887-1.html)
|
||||
[#]: subject: (Add methods retroactively in Python with singledispatch)
|
||||
[#]: via: (https://opensource.com/article/19/5/python-singledispatch)
|
||||
[#]: author: (Moshe Zadka https://opensource.com/users/moshez)
|
||||
|
||||
使用 singledispatch 在 Python 中追溯地添加方法
|
||||
======
|
||||
|
||||
> 在我们覆盖 7 个 PyPI 库的系列文章中了解更多解决 Python 问题的信息。
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201905/23/093515sgmu4auml9caz54l.jpg)
|
||||
|
||||
Python 是当今使用最多[流行的编程语言][2]之一,因为:它是开源的,它具有广泛的用途(例如 Web 编程、业务应用、游戏、科学编程等等),它有一个充满活力和专注的社区支持它。这个社区是我们在 [Python Package Index][3](PyPI)中提供如此庞大、多样化的软件包的原因,用以扩展和改进 Python。并解决不可避免的问题。
|
||||
|
||||
在本系列中,我们将介绍七个可以帮助你解决常见 Python 问题的 PyPI 库。今天,我们将研究 [singledispatch][4],这是一个能让你追溯地向 Python 库添加方法的库。
|
||||
|
||||
### singledispatch
|
||||
|
||||
想象一下,你有一个有 Circle、Square 等类的“形状”库。
|
||||
|
||||
Circle 类有半径、Square 有边、Rectangle 有高和宽。我们的库已经存在,我们不想改变它。
|
||||
|
||||
然而,我们想给库添加一个面积计算。如果我们不会和其他人共享这个库,我们只需添加 `area` 方法,这样我们就能调用 `shape.area()` 而无需关心是什么形状。
|
||||
|
||||
虽然可以进入类并添加一个方法,但这是一个坏主意:没有人希望他们的类会被添加新的方法,程序会因奇怪的方式出错。
|
||||
|
||||
相反,functools 中的 `singledispatch` 函数可以帮助我们。
|
||||
|
||||
|
||||
```
|
||||
@singledispatch
|
||||
def get_area(shape):
|
||||
raise NotImplementedError("cannot calculate area for unknown shape",
|
||||
shape)
|
||||
```
|
||||
|
||||
`get_area` 函数的“基类”实现会报错。这保证了如果我们出现一个新的形状时,我们会明确地报错而不是返回一个无意义的结果。
|
||||
|
||||
|
||||
```
|
||||
@get_area.register(Square)
|
||||
def _get_area_square(shape):
|
||||
return shape.side ** 2
|
||||
@get_area.register(Circle)
|
||||
def _get_area_circle(shape):
|
||||
return math.pi * (shape.radius ** 2)
|
||||
```
|
||||
|
||||
这种方式的好处是如果某人写了一个匹配我们代码的*新*形状,它们可以自己实现 `get_area`。
|
||||
|
||||
|
||||
```
|
||||
from area_calculator import get_area
|
||||
|
||||
@attr.s(auto_attribs=True, frozen=True)
|
||||
class Ellipse:
|
||||
horizontal_axis: float
|
||||
vertical_axis: float
|
||||
|
||||
@get_area.register(Ellipse)
|
||||
def _get_area_ellipse(shape):
|
||||
return math.pi * shape.horizontal_axis * shape.vertical_axis
|
||||
```
|
||||
|
||||
*调用* `get_area` 很直接。
|
||||
|
||||
|
||||
```
|
||||
print(get_area(shape))
|
||||
```
|
||||
|
||||
这意味着我们可以将大量的 `if isintance()`/`elif isinstance()` 的代码以这种方式修改,而无需修改接口。下一次你要修改 if isinstance,你试试 `singledispatch!
|
||||
|
||||
在本系列的下一篇文章中,我们将介绍 tox,一个用于自动化 Python 代码测试的工具。
|
||||
|
||||
#### 回顾本系列的前几篇文章:
|
||||
|
||||
* [Cython][5]
|
||||
* [Black][6]
|
||||
* [attrs][7]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/python-singledispatch
|
||||
|
||||
作者:[Moshe Zadka][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/moshez
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/computer_code_programming_laptop.jpg?itok=ormv35tV
|
||||
[2]: https://opensource.com/article/18/5/numbers-python-community-trends
|
||||
[3]: https://pypi.org/
|
||||
[4]: https://pypi.org/project/singledispatch/
|
||||
[5]: https://linux.cn/article-10859-1.html
|
||||
[6]: https://linux.cn/article-10864-1.html
|
||||
[7]: https://linux.cn/article-10871-1.html
|
@ -0,0 +1,219 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (Moelf)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10881-1.html)
|
||||
[#]: subject: (Using the force at the Linux command line)
|
||||
[#]: via: (https://opensource.com/article/19/5/may-the-force-linux)
|
||||
[#]: author: (Alan Formy-Duval https://opensource.com/users/alanfdoss)
|
||||
|
||||
在 Linux 命令行下使用“原力”
|
||||
======
|
||||
|
||||
> 和绝地武士的原力一样,`-f` 参数是很强大的,并伴随着潜在的毁灭性,在你能用好的时候又很便利。
|
||||
|
||||
![Fireworks](https://img.linux.net.cn/data/attachment/album/201905/21/083913jqbwn4ywq1jqnb9y.jpg)
|
||||
|
||||
近些年来,科幻发烧友开始在每年的 5 月 4 日庆祝[星战节][2],其口号是绝地武士的祝福语”愿<ruby>原力<rt>Force</rt></ruby>和你同在“。虽然大多数 Linux 用户可能不是绝地武士,但我们依然可以使用<ruby>原力<rt>Force</rt></ruby>。自然,如果尤达大师只是叫天行者卢克输入什么 “man X-Wing 战机“、“man 原力”,或者 RTFM(去读原力手册,肯定是这个意思对不对),那这电影肯定没啥意思。(LCTT 译注:RTFM 是 “Read The Fucking Manual” 的缩写 —— 读读该死的手册吧)。
|
||||
|
||||
很多 Linux 命令都有 `-f` 选项,意思你现在肯定也知道了,原力(LCTT 译注:force 选项原意是“强制”)!很多时候你先尝试执行命令然后失败了,或者提示你需要补充输入更多选项。通常这都是为了保护你试着改变的文件,或者告诉用户该设备正忙或文件已经存在之类的。
|
||||
|
||||
如果你不想被这些提醒打扰或者压根就不在乎,就使用原力吧!
|
||||
|
||||
不过要小心,通常使用原力选项是摧毁性的。所以用户一定要格外注意!并且确保你知道自己在做什么!用原力就要承担后果!
|
||||
|
||||
以下是一些常见 Linux 命令的原力选项和它们的效果,以及常见使用场景。
|
||||
|
||||
### cp
|
||||
|
||||
`cp` 是 “copy” 的缩写,这是个被用来复制文件或者目录的命令。其 [man 页面][3] 说:
|
||||
|
||||
> -f, --force
|
||||
>
|
||||
> 如果已经存在的目标文件无法被打开,删除它并重试
|
||||
|
||||
你可能会用它来处理只读状态的文件:
|
||||
|
||||
```
|
||||
[alan@workstation ~]$ ls -l
|
||||
total 8
|
||||
-rw-rw---- 1 alan alan 13 May 1 12:24 Hoth
|
||||
-r--r----- 1 alan alan 14 May 1 12:23 Naboo
|
||||
[alan@workstation ~]$ cat Hoth Naboo
|
||||
Icy Planet
|
||||
|
||||
Green Planet
|
||||
```
|
||||
|
||||
如果你想要复制一个叫做 `Hoth` 的文件到 `Naboo`,但因为 `Naboo` 目前是只读状态,`cp` 命令不会执行:
|
||||
|
||||
```
|
||||
[alan@workstation ~]$ cp Hoth Naboo
|
||||
cp: cannot create regular file 'Naboo': Permission denied
|
||||
```
|
||||
|
||||
但通过使用原力,`cp` 会强制执行。`Hoth` 的内容和文件权限会直接被复制到 `Naboo`:
|
||||
|
||||
|
||||
```
|
||||
[alan@workstation ~]$ cp -f Hoth Naboo
|
||||
[alan@workstation ~]$ cat Hoth Naboo
|
||||
Icy Planet
|
||||
|
||||
Icy Planet
|
||||
|
||||
[alan@workstation ~]$ ls -l
|
||||
total 8
|
||||
-rw-rw---- 1 alan alan 12 May 1 12:32 Hoth
|
||||
-rw-rw---- 1 alan alan 12 May 1 12:38 Naboo
|
||||
```
|
||||
|
||||
### ln
|
||||
|
||||
`ln` 命令是用来在文件之间建立链接的,其 [man 页面][4] 描述的原力选项如下:
|
||||
|
||||
|
||||
> -f, --force
|
||||
>
|
||||
> 移除当前存在的文件
|
||||
|
||||
|
||||
假设莱娅公主在维护一个 Java 应用服务器,并且她又一个存放这所有 Java 版本的目录,比如:
|
||||
|
||||
```
|
||||
leia@workstation:/usr/lib/java$ ls -lt
|
||||
total 28
|
||||
lrwxrwxrwx 1 leia leia 12 Mar 5 2018 jdk -> jdk1.8.0_162
|
||||
drwxr-xr-x 8 leia leia 4096 Mar 5 2018 jdk1.8.0_162
|
||||
drwxr-xr-x 8 leia leia 4096 Aug 28 2017 jdk1.8.0_144
|
||||
```
|
||||
|
||||
正如你所看到的,这里有很多个版本的 JDK,并有一个符号链接指向最新版的 JDK。她接着用一个脚本来安装最新版本的 JDK。但是如果没有原力选项的话以下命令是不会成功的:
|
||||
|
||||
```
|
||||
tar xvzmf jdk1.8.0_181.tar.gz -C jdk1.8.0_181/
|
||||
ln -vs jdk1.8.0_181 jdk
|
||||
```
|
||||
|
||||
`tar` 命令会解压 .gz 文件到一个特定的目标目录,但 `ln` 命令会失败,因为这个链接已经存在了。这样的结果是该符号链接不会指向最新版本的 JDK:
|
||||
|
||||
```
|
||||
leia@workstation:/usr/lib/java$ ln -vs jdk1.8.0_181 jdk
|
||||
ln: failed to create symbolic link 'jdk/jdk1.8.0_181': File exists
|
||||
leia@workstation:/usr/lib/java$ ls -lt
|
||||
total 28
|
||||
drwxr-x--- 2 leia leia 4096 May 1 15:44 jdk1.8.0_181
|
||||
lrwxrwxrwx 1 leia leia 12 Mar 5 2018 jdk -> jdk1.8.0_162
|
||||
drwxr-xr-x 8 leia leia 4096 Mar 5 2018 jdk1.8.0_162
|
||||
drwxr-xr-x 8 leia leia 4096 Aug 28 2017 jdk1.8.0_144
|
||||
```
|
||||
|
||||
她可以通过使用原力选项强制 `ln` 更新链接,但这里她还需要使用 `-n`,`-n` 是因为这个情况下链接其实指向一个目录而非文件。这样的话,链接就会正确指向最新版本的JDK了。
|
||||
|
||||
```
|
||||
leia@workstation:/usr/lib/java$ ln -vsnf jdk1.8.0_181 jdk
|
||||
'jdk' -> 'jdk1.8.0_181'
|
||||
leia@workstation:/usr/lib/java$ ls -lt
|
||||
total 28
|
||||
lrwxrwxrwx 1 leia leia 12 May 1 16:13 jdk -> jdk1.8.0_181
|
||||
drwxr-x--- 2 leia leia 4096 May 1 15:44 jdk1.8.0_181
|
||||
drwxr-xr-x 8 leia leia 4096 Mar 5 2018 jdk1.8.0_162
|
||||
drwxr-xr-x 8 leia leia 4096 Aug 28 2017 jdk1.8.0_144
|
||||
```
|
||||
|
||||
你可以配置 Java 应用使其一直使用在 `/usr/lib/java/jdk` 处的 JDK,而不用每次升级都更新。
|
||||
|
||||
### rm
|
||||
|
||||
`rm` 命令是 “remove” 的缩写(也叫做删除,因为某些系统 `del` 命令也干这事)。其 [man 页面][5] 对原力选项的描述如下:
|
||||
|
||||
|
||||
> -f, --force
|
||||
>
|
||||
> 无视不存在的文件或者参数,不向用户确认
|
||||
|
||||
如果你尝试删除一个只读的文件,`rm` 会寻求用户的确认:
|
||||
|
||||
```
|
||||
[alan@workstation ~]$ ls -l
|
||||
total 4
|
||||
-r--r----- 1 alan alan 16 May 1 11:38 B-wing
|
||||
[alan@workstation ~]$ rm B-wing
|
||||
rm: remove write-protected regular file 'B-wing'?
|
||||
```
|
||||
|
||||
你一定要输入 `y` 或者 `n` 来回答确认才能让 `rm` 命令继续。如果你使用原力选项,`rm` 就不会寻求你的确认而直接删除文件:
|
||||
|
||||
```
|
||||
[alan@workstation ~]$ rm -f B-wing
|
||||
[alan@workstation ~]$ ls -l
|
||||
total 0
|
||||
[alan@workstation ~]$
|
||||
```
|
||||
|
||||
最常见的 `rm` 原力选项用法是用来删除目录。 `-r`(递归)选项会让 `rm` 删除目录,当和原力选项结合起来,它会删除这个文件夹及其内容而无需用户确认。
|
||||
|
||||
`rm` 命令和一些选项结合起来是致命的,一直以来互联网上都有关于误用 `rm` 删除整个系统之类的玩笑和鬼故事。比如最出名的一不当心执行 `rm -rf .` 会直接删除目录和文件(没有用户确认)。(LCTT 译注:真的这么干过的校对飘过~~请按下回车前再三确认:我是谁,我在哪里,我在干什么)
|
||||
|
||||
### userdel
|
||||
|
||||
`userdel` 命令使用来删除用户的。其 [man 页面][6] 是这样描述它的原力选项的:
|
||||
|
||||
> -f, --force
|
||||
>
|
||||
> 这个选项会强制移除用户,即便用户当前处于登入状态。它同时还会强制
|
||||
删除用户的目录和邮件存储,即便这个用户目录被别人共享或者邮件存储并不
|
||||
属于这个用户。如果 `USERGROUPS_ENAB` 在 `/etc/login.defs` 里是 `yes`
|
||||
并且有一个组和此用户同名的话,这个组也会被移除,即便这个组还是别
|
||||
的用户的主要用户组也一样。
|
||||
>
|
||||
> 注意:这个选项有风险并可能让系统处于不稳定状态。
|
||||
|
||||
当欧比旺抵达穆斯塔法星的时候,他知道自己的使命。他需要删掉达斯·维达的用户账户——而达斯还在里面呢。
|
||||
|
||||
```
|
||||
[root@workstation ~]# ps -fu darth
|
||||
UID PID PPID C STIME TTY TIME CMD
|
||||
darth 7663 7655 0 13:28 pts/3 00:00:00 -bash
|
||||
[root@workstation ~]# userdel darth
|
||||
userdel: user darth is currently used by process 7663
|
||||
```
|
||||
|
||||
因为达斯还登在系统里,欧比旺需要使用原力选项操作 `userdel`。这能强制删除当前登入的用户。
|
||||
|
||||
```
|
||||
[root@workstation ~]# userdel -f darth
|
||||
userdel: user darth is currently used by process 7663
|
||||
[root@workstation ~]# finger darth
|
||||
finger: darth: no such user.
|
||||
[root@workstation ~]# ps -fu darth
|
||||
error: user name does not exist
|
||||
```
|
||||
|
||||
正如我们所见到的一样,`finger` 和 `ps` 命令让我们确认了达斯已经被删除了。
|
||||
|
||||
### 在 Shell 脚本里使用原力
|
||||
|
||||
很多命令都有原力选项,而在 shell 脚本里他们特别有用。因为我们经常使用脚本完成定期或者自动化的任务,避免用户输入至关重要,不然的话自动任务就无法完成了
|
||||
|
||||
我希望上面的几个例子能帮你理解一些需要使用原力的情况。你在命令行使用原力或把它们写入脚本之前应当完全理解它们的作用。误用原力会有毁灭性的后果——时常是对整个系统,甚至不仅限于一台设备。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/may-the-force-linux
|
||||
|
||||
作者:[Alan Formy-Duval][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[Jerry Ling](https://github.com/Moelf)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/alanfdoss
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/fireworks_light_art_design.jpg?itok=hfx9i4By (Fireworks)
|
||||
[2]: https://www.starwars.com/star-wars-day
|
||||
[3]: http://man7.org/linux/man-pages/man1/cp.1.html
|
||||
[4]: http://man7.org/linux/man-pages/man1/ln.1.html
|
||||
[5]: http://man7.org/linux/man-pages/man1/rm.1.html
|
||||
[6]: http://man7.org/linux/man-pages/man8/userdel.8.html
|
@ -0,0 +1,244 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (tomjlw)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10892-1.html)
|
||||
[#]: subject: (Duc – A Collection Of Tools To Inspect And Visualize Disk Usage)
|
||||
[#]: via: (https://www.ostechnix.com/duc-a-collection-of-tools-to-inspect-and-visualize-disk-usage/)
|
||||
[#]: author: (sk https://www.ostechnix.com/author/sk/)
|
||||
|
||||
Duc:一个能够可视化洞察硬盘使用情况的工具包
|
||||
======
|
||||
|
||||
![Duc:一个能够洞察并可视化硬盘使用情况的工具包][1]
|
||||
|
||||
Duc 是一个在类 Unix 操作系统上可以用来索引、洞察及可视化硬盘使用情况的工具包。别把它当成一个仅能用漂亮图表展现硬盘使用情况的 CLI 工具。它对巨大的文件系统也支持的很好。Duc 已在由超过五亿个文件和几 PB 的存储组成的系统上测试过,没有任何问题。
|
||||
|
||||
Duc 是一个快速而且灵活的工具。它将你的硬盘使用情况存在一个优化过的数据库里,这样你就可以在索引完成后迅速找到你的数据。此外,它自带不同的用户交互界面与后端以访问数据库并绘制图表。
|
||||
|
||||
以下列出的是目前支持的用户界面(UI):
|
||||
|
||||
1. 命令行界面(`duc ls`)
|
||||
2. Ncurses 控制台界面(`duc ui`)
|
||||
3. X11 GUI(`duc gui`)
|
||||
4. OpenGL GUI(`duc gui`)
|
||||
|
||||
支持的后端数据库:
|
||||
|
||||
* Tokyocabinet
|
||||
* Leveldb
|
||||
* Sqlite3
|
||||
|
||||
Duc 默认使用 Tokyocabinet 作为后端数据库。
|
||||
|
||||
### 安装 Duc
|
||||
|
||||
Duc 可以从 Debian 以及其衍生品例如 Ubuntu 的默认仓库中获取。因此在基于 DEB 的系统上安装 Duc 是小菜一碟。
|
||||
|
||||
```
|
||||
$ sudo apt-get install duc
|
||||
```
|
||||
|
||||
在其它 Linux 发行版上你需要像以下所展示的那样手动从源代码编译安装 Duc。
|
||||
|
||||
可以从 Github 上的[发行][2]页面下载最新的 Duc 源代码的 .tgz 文件。在写这篇教程的时候,最新的版本是1.4.4。
|
||||
|
||||
```
|
||||
$ wget https://github.com/zevv/duc/releases/download/1.4.4/duc-1.4.4.tar.gz
|
||||
```
|
||||
|
||||
然后一个接一个地运行以下命令来安装 DUC。
|
||||
|
||||
```
|
||||
$ tar -xzf duc-1.4.4.tar.gz
|
||||
$ cd duc-1.4.4
|
||||
$ ./configure
|
||||
$ make
|
||||
$ sudo make install
|
||||
```
|
||||
|
||||
### 使用 Duc
|
||||
|
||||
`duc` 的典型用法是:
|
||||
|
||||
```
|
||||
$ duc <subcommand> <options>
|
||||
```
|
||||
|
||||
你可以通过运行以下命令来浏览总的选项列表以及子命令:
|
||||
|
||||
```
|
||||
$ duc help
|
||||
```
|
||||
|
||||
你也可以像下面这样了解一个特定子命令的用法。
|
||||
|
||||
```
|
||||
$ duc help <subcommand>
|
||||
```
|
||||
|
||||
要查看所有命令与其选项的列表,仅需运行:
|
||||
|
||||
```
|
||||
$ duc help --all
|
||||
```
|
||||
|
||||
让我们看看一些 `duc` 工具的特定用法。
|
||||
|
||||
### 创建索引(数据库)
|
||||
|
||||
首先,你需要创建一个你文件系统的索引文件(数据库)。使用 `duc index` 命令以创建索引文件。
|
||||
|
||||
比如说,要创建你的 `/home` 目录的索引,仅需运行:
|
||||
|
||||
```
|
||||
$ duc index /home
|
||||
```
|
||||
|
||||
上述命令将会创建你的 `/home` 目录的索引,并将其保存在 `$HOME/.duc.db` 文件中。如果你以后需要往 `/home` 目录添加新的文件或目录,只要在之后重新运行一下上面的命令来重建索引。
|
||||
|
||||
### 查询索引
|
||||
|
||||
Duc 有不同的子命令来查询并探索索引。
|
||||
|
||||
要查看可访问的索引列表,运行:
|
||||
|
||||
```
|
||||
$ duc info
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
```
|
||||
Date Time Files Dirs Size Path
|
||||
2019-04-09 15:45:55 3.5K 305 654.6M /home
|
||||
```
|
||||
|
||||
如你在上述输出所见,我已经索引好了 `/home` 目录。
|
||||
|
||||
要列出当前工作目录中所有的文件和目录,你可以这样做:
|
||||
|
||||
```
|
||||
$ duc ls
|
||||
```
|
||||
|
||||
要列出指定的目录,例如 `/home/sk/Downloads` 中的文件/目录,仅需像下面这样将路径作为参数传过去。
|
||||
|
||||
```
|
||||
$ duc ls /home/sk/Downloads
|
||||
```
|
||||
|
||||
类似的,运行 `duc ui` 命令来打开基于 ncurses 的控制台用户界面以探索文件系统使用情况,运行`duc gui` 以打开图形界面(X11)来探索文件系统。
|
||||
|
||||
要了解更多子命令的用法,仅需参考帮助部分。
|
||||
|
||||
```
|
||||
$ duc help ls
|
||||
```
|
||||
|
||||
上述命令将会展现 `ls` 子命令的帮助部分。
|
||||
|
||||
### 可视化硬盘使用状况
|
||||
|
||||
在之前的部分我们以及看到如何用 duc 子命令列出文件和目录。在此之外,你甚至可以用一张漂亮的图表展示文件大小。
|
||||
|
||||
要展示所提供目录的图表,像以下这样使用 `ls` 子命令。
|
||||
|
||||
```
|
||||
$ duc ls -Fg /home/sk
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
![使用 “duc ls” 命令可视化硬盘使用情况][3]
|
||||
|
||||
如你在上述输出所见,`ls` 子命令查询 duc 数据库并列出了所提供目录包含的文件与目录的大小,在这里就是 `/home/sk/`。
|
||||
|
||||
这里 `-F` 选项是往条目中用来添加文件类型指示符(`/`),`-g` 选项是用来绘制每个条目相对大小的图表。
|
||||
|
||||
请注意如果未提供任何路径,就会使用当前工作目录。
|
||||
|
||||
你可以使用 `-R` 选项来用[树状结构][4]浏览硬盘使用情况。
|
||||
|
||||
```
|
||||
$ duc ls -R /home/sk
|
||||
```
|
||||
|
||||
![用树状结构可视化硬盘使用情况][5]
|
||||
|
||||
要查询 duc 数据库并打开基于 ncurses 的控制台以探索所提供的目录,像以下这样使用 `ui` 子命令。
|
||||
|
||||
```
|
||||
$ duc ui /home/sk
|
||||
```
|
||||
|
||||
![][6]
|
||||
|
||||
类似的,我们使用 `gui *` 子命令来查询 duc 数据库以及打开一个图形界面(X11)来了解指定路径的硬盘使用情况。
|
||||
|
||||
```
|
||||
$ duc gui /home/sk
|
||||
```
|
||||
|
||||
![][7]
|
||||
|
||||
像我之前所提到的,我们可以像下面这样了解更多关于特定子命令的用法。
|
||||
|
||||
```
|
||||
$ duc help <子命令名字>
|
||||
```
|
||||
|
||||
我仅仅覆盖了基本用法的部分,参考 man 页面了解关于 `duc` 工具的更多细节。
|
||||
|
||||
```
|
||||
$ man duc
|
||||
```
|
||||
|
||||
相关阅读:
|
||||
|
||||
* [Filelight – 在你的 Linux 系统上可视化硬盘使用情况][8]
|
||||
* [一些好的 du 命令的替代品][9]
|
||||
* [如何在 Linux 中用 Ncdu 检查硬盘使用情况][10]
|
||||
* [Agedu——发现 Linux 中被浪费的硬盘空间][11]
|
||||
* [如何在 Linux 中找到目录大小][12]
|
||||
* [为初学者打造的带有示例的 df 命令教程][13]
|
||||
|
||||
### 总结
|
||||
|
||||
Duc 是一款简单却有用的硬盘用量查看器。如果你想要快速简便地知道哪个文件/目录占用你的硬盘空间,Duc 可能是一个好的选择。你还等什么呢?获取这个工具,扫描你的文件系统,摆脱无用的文件/目录。
|
||||
|
||||
现在就到此为止了。希望这篇文章有用处。更多好东西马上就到。保持关注!
|
||||
|
||||
欢呼吧!
|
||||
|
||||
资源:
|
||||
|
||||
* [Duc 网站][14]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/duc-a-collection-of-tools-to-inspect-and-visualize-disk-usage/
|
||||
|
||||
作者:[sk][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[tomjlw](https://github.com/tomjlw)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/sk/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ostechnix.com/wp-content/uploads/2019/04/duc-720x340.png
|
||||
[2]: https://github.com/zevv/duc/releases
|
||||
[3]: http://www.ostechnix.com/wp-content/uploads/2019/04/duc-1-1.png
|
||||
[4]: https://www.ostechnix.com/view-directory-tree-structure-linux/
|
||||
[5]: http://www.ostechnix.com/wp-content/uploads/2019/04/duc-2.png
|
||||
[6]: http://www.ostechnix.com/wp-content/uploads/2019/04/duc-3.png
|
||||
[7]: http://www.ostechnix.com/wp-content/uploads/2019/04/duc-4.png
|
||||
[8]: https://www.ostechnix.com/filelight-visualize-disk-usage-on-your-linux-system/
|
||||
[9]: https://www.ostechnix.com/some-good-alternatives-to-du-command/
|
||||
[10]: https://www.ostechnix.com/check-disk-space-usage-linux-using-ncdu/
|
||||
[11]: https://www.ostechnix.com/agedu-find-out-wasted-disk-space-in-linux/
|
||||
[12]: https://www.ostechnix.com/find-size-directory-linux/
|
||||
[13]: https://www.ostechnix.com/the-df-command-tutorial-with-examples-for-beginners/
|
||||
[14]: https://duc.zevv.nl/
|
197
published/201905/20190505 How To Create SSH Alias In Linux.md
Normal file
197
published/201905/20190505 How To Create SSH Alias In Linux.md
Normal file
@ -0,0 +1,197 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (MjSeven)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10851-1.html)
|
||||
[#]: subject: (How To Create SSH Alias In Linux)
|
||||
[#]: via: (https://www.ostechnix.com/how-to-create-ssh-alias-in-linux/)
|
||||
[#]: author: (sk https://www.ostechnix.com/author/sk/)
|
||||
|
||||
如何在 Linux 中创建 SSH 别名
|
||||
======
|
||||
|
||||
![How To Create SSH Alias In Linux](https://img.linux.net.cn/data/attachment/album/201905/13/222910h2uwy06um3byr68r.jpg)
|
||||
|
||||
如果你经常通过 SSH 访问许多不同的远程系统,这个技巧将为你节省一些时间。你可以通过 SSH 为频繁访问的系统创建 SSH 别名,这样你就不必记住所有不同的用户名、主机名、SSH 端口号和 IP 地址等。此外,它避免了在 SSH 到 Linux 服务器时重复输入相同的用户名、主机名、IP 地址、端口号。
|
||||
|
||||
### 在 Linux 中创建 SSH 别名
|
||||
|
||||
在我知道这个技巧之前,我通常使用以下任意一种方式通过 SSH 连接到远程系统。
|
||||
|
||||
使用 IP 地址:
|
||||
|
||||
```
|
||||
$ ssh 192.168.225.22
|
||||
```
|
||||
|
||||
或使用端口号、用户名和 IP 地址:
|
||||
|
||||
```
|
||||
$ ssh -p 22 sk@192.168.225.22
|
||||
```
|
||||
|
||||
或使用端口号、用户名和主机名:
|
||||
|
||||
```
|
||||
$ ssh -p 22 sk@server.example.com
|
||||
```
|
||||
|
||||
这里
|
||||
|
||||
* `22` 是端口号,
|
||||
* `sk` 是远程系统的用户名,
|
||||
* `192.168.225.22` 是我远程系统的 IP,
|
||||
* `server.example.com` 是远程系统的主机名。
|
||||
|
||||
我相信大多数 Linux 新手和(或一些)管理员都会以这种方式通过 SSH 连接到远程系统。但是,如果你通过 SSH 连接到多个不同的系统,记住所有主机名或 IP 地址,还有用户名是困难的,除非你将它们写在纸上或者将其保存在文本文件中。别担心!这可以通过为 SSH 连接创建别名(或快捷方式)轻松解决。
|
||||
|
||||
我们可以用两种方法为 SSH 命令创建别名。
|
||||
|
||||
#### 方法 1 – 使用 SSH 配置文件
|
||||
|
||||
这是我创建别名的首选方法。
|
||||
|
||||
我们可以使用 SSH 默认配置文件来创建 SSH 别名。为此,编辑 `~/.ssh/config` 文件(如果此文件不存在,只需创建一个):
|
||||
|
||||
```
|
||||
$ vi ~/.ssh/config
|
||||
```
|
||||
|
||||
添加所有远程主机的详细信息,如下所示:
|
||||
|
||||
```
|
||||
Host webserver
|
||||
HostName 192.168.225.22
|
||||
User sk
|
||||
|
||||
Host dns
|
||||
HostName server.example.com
|
||||
User root
|
||||
|
||||
Host dhcp
|
||||
HostName 192.168.225.25
|
||||
User ostechnix
|
||||
Port 2233
|
||||
```
|
||||
|
||||
![][2]
|
||||
|
||||
*使用 SSH 配置文件在 Linux 中创建 SSH 别名*
|
||||
|
||||
将 `Host`、`Hostname`、`User` 和 `Port` 配置的值替换为你自己的值。添加所有远程主机的详细信息后,保存并退出该文件。
|
||||
|
||||
现在你可以使用以下命令通过 SSH 进入系统:
|
||||
|
||||
```
|
||||
$ ssh webserver
|
||||
$ ssh dns
|
||||
$ ssh dhcp
|
||||
```
|
||||
|
||||
就是这么简单!
|
||||
|
||||
看看下面的截图。
|
||||
|
||||
![][3]
|
||||
|
||||
*使用 SSH 别名访问远程系统*
|
||||
|
||||
看到了吗?我只使用别名(例如 `webserver`)来访问 IP 地址为 `192.168.225.22` 的远程系统。
|
||||
|
||||
请注意,这只使用于当前用户。如果要为所有用户(系统范围内)提供别名,请在 `/etc/ssh/ssh_config` 文件中添加以上行。
|
||||
|
||||
你还可以在 SSH 配置文件中添加许多其他内容。例如,如果你[已配置基于 SSH 密钥的身份验证][4],说明 SSH 密钥文件的位置,如下所示:
|
||||
|
||||
```
|
||||
Host ubuntu
|
||||
HostName 192.168.225.50
|
||||
User senthil
|
||||
IdentityFIle ~/.ssh/id_rsa_remotesystem
|
||||
```
|
||||
|
||||
确保已使用你自己的值替换主机名、用户名和 SSH 密钥文件路径。
|
||||
|
||||
现在使用以下命令连接到远程服务器:
|
||||
|
||||
```
|
||||
$ ssh ubuntu
|
||||
```
|
||||
|
||||
这样,你可以添加希望通过 SSH 访问的任意多台远程主机,并使用别名快速访问它们。
|
||||
|
||||
#### 方法 2 – 使用 Bash 别名
|
||||
|
||||
这是创建 SSH 别名的一种应急变通的方法,可以加快通信的速度。你可以使用 [alias 命令][5]使这项任务更容易。
|
||||
|
||||
打开 `~/.bashrc` 或者 `~/.bash_profile` 文件:
|
||||
|
||||
```
|
||||
alias webserver='ssh sk@server.example.com'
|
||||
alias dns='ssh sk@server.example.com'
|
||||
alias dhcp='ssh sk@server.example.com -p 2233'
|
||||
alias ubuntu='ssh sk@server.example.com -i ~/.ssh/id_rsa_remotesystem'
|
||||
```
|
||||
|
||||
再次确保你已使用自己的值替换主机、主机名、端口号和 IP 地址。保存文件并退出。
|
||||
|
||||
然后,使用命令应用更改:
|
||||
|
||||
```
|
||||
$ source ~/.bashrc
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```
|
||||
$ source ~/.bash_profile
|
||||
```
|
||||
|
||||
在此方法中,你甚至不需要使用 `ssh 别名` 命令。相反,只需使用别名,如下所示。
|
||||
|
||||
```
|
||||
$ webserver
|
||||
$ dns
|
||||
$ dhcp
|
||||
$ ubuntu
|
||||
```
|
||||
|
||||
![][6]
|
||||
|
||||
这两种方法非常简单,但对于经常通过 SSH 连接到多个不同系统的人来说非常有用,而且非常方便。使用适合你的上述任何一种方法,通过 SSH 快速访问远程 Linux 系统。
|
||||
|
||||
建议阅读:
|
||||
|
||||
* [允许或拒绝 SSH 访问 Linux 中的特定用户或组][7]
|
||||
* [如何在 Linux 上 SSH 到特定目录][8]
|
||||
* [如何在 Linux 中断开 SSH 会话][9]
|
||||
* [4 种方式在退出 SSH 会话后保持命令运行][10]
|
||||
* [SSLH – 共享相同端口的 HTTPS 和 SSH][11]
|
||||
|
||||
目前这就是全部了,希望它对你有帮助。更多好东西要来了,敬请关注!
|
||||
|
||||
干杯!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/how-to-create-ssh-alias-in-linux/
|
||||
|
||||
作者:[sk][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[MjSeven](https://github.com/MjSeven)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/sk/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ostechnix.com/wp-content/uploads/2019/04/ssh-alias-720x340.png
|
||||
[2]: http://www.ostechnix.com/wp-content/uploads/2019/04/Create-SSH-Alias-In-Linux.png
|
||||
[3]: http://www.ostechnix.com/wp-content/uploads/2019/04/create-ssh-alias.png
|
||||
[4]: https://www.ostechnix.com/configure-ssh-key-based-authentication-linux/
|
||||
[5]: https://www.ostechnix.com/the-alias-and-unalias-commands-explained-with-examples/
|
||||
[6]: http://www.ostechnix.com/wp-content/uploads/2019/04/create-ssh-alias-1.png
|
||||
[7]: https://www.ostechnix.com/allow-deny-ssh-access-particular-user-group-linux/
|
||||
[8]: https://www.ostechnix.com/how-to-ssh-into-a-particular-directory-on-linux/
|
||||
[9]: https://www.ostechnix.com/how-to-stop-ssh-session-from-disconnecting-in-linux/
|
||||
[10]: https://www.ostechnix.com/4-ways-keep-command-running-log-ssh-session/
|
||||
[11]: https://www.ostechnix.com/sslh-share-port-https-ssh/
|
@ -0,0 +1,142 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10847-1.html)
|
||||
[#]: subject: (Kindd – A Graphical Frontend To dd Command)
|
||||
[#]: via: (https://www.ostechnix.com/kindd-a-graphical-frontend-to-dd-command/)
|
||||
[#]: author: (sk https://www.ostechnix.com/author/sk/)
|
||||
|
||||
Kindd:一个图形化 dd 命令前端
|
||||
======
|
||||
|
||||
![Kindd - A Graphical Frontend To dd Command][1]
|
||||
|
||||
前不久,我们已经学习如何在类 Unix 系统中 [使用 dd 命令创建可启动的 ISO][2]。请记住,`dd` 命令是最具危险性和破坏性的命令之一。如果你不确定你实际在做什么,你可能会在几分钟内意外地擦除你的硬盘数据。`dd` 命令仅仅从 `if` 参数获取数据,并写入数据到 `of` 参数。它将不关心它正在覆盖什么,它也不关心是否在磁道上有一个分区表,或一个启动扇区,或者一个家文件夹,或者任何重要的东西。它将简单地做它被告诉去做的事。如果你是初学者,一般地尝试避免使用 `dd` 命令来做实验。幸好,这有一个支持 `dd` 命令的简单的 GUI 实用程序。向 “Kindd” 问好,一个属于 `dd` 命令的图形化前端。它是自由开源的、用 Qt Quick 所写的工具。总的来说,这个工具对那些对命令行不适应的初学者是非常有用的。
|
||||
|
||||
它的开发者创建这个工具主要是为了提供:
|
||||
|
||||
1. 一个用于 `dd` 命令的现代化的、简单而安全的图形化用户界面,
|
||||
2. 一种简单地创建可启动设备的图形化方法,而不必使用终端。
|
||||
|
||||
### 安装 Kindd
|
||||
|
||||
Kindd 在 [AUR][3] 中是可用的。所以,如果你是 Arch 用户,使用任一的 AUR 助手工具来安装它,例如 [Yay][4] 。
|
||||
|
||||
要安装其 Git 发布版,运行:
|
||||
|
||||
```
|
||||
$ yay -S kindd-git
|
||||
```
|
||||
|
||||
要安装正式发布版,运行:
|
||||
|
||||
```
|
||||
$ yay -S kindd
|
||||
```
|
||||
|
||||
在安装后,从菜单或应用程序启动器启动 Kindd。
|
||||
|
||||
对于其它的发行版,你需要从源文件手动编译和安装它,像下面所示。
|
||||
|
||||
确保你已经安装下面的必要条件。
|
||||
|
||||
* git
|
||||
* coreutils
|
||||
* polkit
|
||||
* qt5-base
|
||||
* qt5-quickcontrols
|
||||
* qt5-quickcontrols2
|
||||
* qt5-graphicaleffects
|
||||
|
||||
一旦所有必要条件安装,使用 `git` 克隆 Kindd 储存库:
|
||||
|
||||
```
|
||||
git clone https://github.com/LinArcX/Kindd/
|
||||
```
|
||||
|
||||
转到你刚刚克隆 Kindd 的目录,并编译和安装它:
|
||||
|
||||
```
|
||||
cd Kindd
|
||||
qmake
|
||||
make
|
||||
```
|
||||
|
||||
最后运行下面的命令来启动 Kindd 应用程序:
|
||||
|
||||
```
|
||||
./kindd
|
||||
```
|
||||
|
||||
Kindd 内部使用 pkexec。pkexec 代理被默认安装在大多数桌面环境中。但是,如果你使用 i3 (或者可能还有一些其它的桌面环境),你应该首先安装 polkit-gnome ,然后粘贴下面的行到 i3 配置文件:
|
||||
|
||||
```
|
||||
exec /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 &
|
||||
```
|
||||
|
||||
### 使用 Kindd 创建可启动的 ISO
|
||||
|
||||
为从一个 ISO 创建一个可启动的 USB,插入 USB 驱动器。然后,从菜单或终端启动 Kindd 。
|
||||
|
||||
这是 Kindd 默认界面的外观:
|
||||
|
||||
![][5]
|
||||
|
||||
*Kindd 界面*
|
||||
|
||||
正如你所能看到的,Kindd 界面是非常简单的和明白易懂的。这里仅有两部分,即设备列表,它显示你的系统上的可用的设备(hdd 和 Usb),并创建可启动的 .iso 。默认情况下,你将在“创建可启动 .iso”部分。
|
||||
|
||||
在第一列中输入块大小,在第二列中选择 ISO 文件的路径,并在第三列中选择正确的设备(USB 驱动器路径)。单击“转换/复制”按钮来开始创建可启动的 ISO 。
|
||||
|
||||
![][6]
|
||||
|
||||
一旦进程被完成,你将看到成功的信息。
|
||||
|
||||
![][7]
|
||||
|
||||
现在,拔出 USB 驱动器,并用该 USB 启动器启动你的系统,来检查它是否真地工作。
|
||||
|
||||
如果你不知道真实的设备名称(目标路径),只需要在列出的设备上单击,并检查 USB 驱动器名称。
|
||||
|
||||
![][8]
|
||||
|
||||
Kindd 还处在早期开发阶段。因此,可能有错误。如果你找到一些错误,请在这篇的指南的结尾所给的 GitHub 页面报告它们。
|
||||
|
||||
这就是全部。希望这是有用的。更多的好东西将会来。敬请期待!
|
||||
|
||||
谢谢!
|
||||
|
||||
资源:
|
||||
|
||||
* [Kindd GitHub 储存库][11]
|
||||
|
||||
相关阅读:
|
||||
|
||||
* [Etcher:一个来创建可启动 SD 卡或 USB 驱动器的漂亮的应用程序][9]
|
||||
* [Bootiso 让你安全地创建可启动的 USB 驱动器][10]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/kindd-a-graphical-frontend-to-dd-command/
|
||||
|
||||
作者:[sk][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/sk/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ostechnix.com/wp-content/uploads/2019/04/kindd-720x340.png
|
||||
[2]: https://www.ostechnix.com/how-to-create-bootable-usb-drive-using-dd-command/
|
||||
[3]: https://aur.archlinux.org/packages/kindd-git/
|
||||
[4]: https://www.ostechnix.com/yay-found-yet-another-reliable-aur-helper/
|
||||
[5]: http://www.ostechnix.com/wp-content/uploads/2019/04/kindd-interface.png
|
||||
[6]: http://www.ostechnix.com/wp-content/uploads/2019/04/kindd-1.png
|
||||
[7]: http://www.ostechnix.com/wp-content/uploads/2019/04/kindd-2.png
|
||||
[8]: http://www.ostechnix.com/wp-content/uploads/2019/04/kindd-3.png
|
||||
[9]: https://www.ostechnix.com/etcher-beauitiful-app-create-bootable-sd-cards-usb-drives/
|
||||
[10]: https://www.ostechnix.com/bootiso-lets-you-safely-create-bootable-usb-drive/
|
||||
[11]: https://github.com/LinArcX/Kindd
|
@ -0,0 +1,198 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (warmfrog)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10868-1.html)
|
||||
[#]: subject: (Linux Shell Script To Monitor Disk Space Usage And Send Email)
|
||||
[#]: via: (https://www.2daygeek.com/linux-shell-script-to-monitor-disk-space-usage-and-send-email/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
用 Linux Shell 脚本来监控磁盘使用情况并发送邮件
|
||||
============================================
|
||||
|
||||
市场上有很多用来监控 Linux 系统的监控工具,当系统到达阀值后它将发送一封邮件。它监控所有的东西例如 CPU 利用率、内存利用率、交换空间利用率、磁盘空间利用率等等。然而,它更适合小环境和大环境。
|
||||
|
||||
想一想如果你只有少量系统,那么什么是最好的方式来应对这种情况。
|
||||
|
||||
是的,我们想要写一个 [shell 脚本][1] 来实现。
|
||||
|
||||
在这篇指南中我们打算写一个 shell 脚本来监控系统的磁盘空间使用率。当系统到达给定的阀值,它将给对应的邮件地址发送一封邮件。在这篇文章中我们总共添加了四个 shell 脚本,每个用于不同的目的。之后,我们会想出其他 shell 脚本来监控 CPU,内存和交换空间利用率。
|
||||
|
||||
在此之前,我想澄清一件事,根据我观察的磁盘空间使用率 shell 脚本使用情况。
|
||||
|
||||
大多数用户在多篇博客中评论说,当他们运行磁盘空间使用率脚本时他们获得了以下错误。
|
||||
|
||||
```
|
||||
# sh /opt/script/disk-usage-alert-old.sh
|
||||
|
||||
/dev/mapper/vg_2g-lv_root
|
||||
test-script.sh: line 7: [: /dev/mapper/vg_2g-lv_root: integer expression expected
|
||||
/ 9.8G
|
||||
```
|
||||
|
||||
是的,这是对的。甚至,当我第一次运行这个脚本的时候我遇到了相同的问题。之后,我发现了根本原因。
|
||||
|
||||
当你在基于 RHEL 5 & RHEL 6 的系统上运行包含用于磁盘空间警告的 `df -h` 或 `df -H` 的 shell 脚本中时,你会发现上述错误信息,因为输出格式不对,查看下列输出。
|
||||
|
||||
为了解决这个问题,我们需要用 `df -Ph` (POSIX 输出格式),但是默认的 `df -h` 在基于 RHEL 7 的系统上运行的很好。
|
||||
|
||||
```
|
||||
# df -h
|
||||
|
||||
Filesystem Size Used Avail Use% Mounted on
|
||||
/dev/mapper/vg_2g-lv_root
|
||||
10G 6.7G 3.4G 67% /
|
||||
tmpfs 7.8G 0 7.8G 0% /dev/shm
|
||||
/dev/sda1 976M 95M 830M 11% /boot
|
||||
/dev/mapper/vg_2g-lv_home
|
||||
5.0G 4.3G 784M 85% /home
|
||||
/dev/mapper/vg_2g-lv_tmp
|
||||
4.8G 14M 4.6G 1% /tmp
|
||||
```
|
||||
|
||||
### 方法一:Linux Shell 脚本来监控磁盘空间使用率和发送邮件
|
||||
|
||||
你可以使用下列 shell 脚本在 Linux 系统中来监控磁盘空间使用率。
|
||||
|
||||
当系统到达给定的阀值限制时,它将发送一封邮件。在这个例子中,我们设置阀值为 60% 用于测试目的,你可以改变这个限制来符合你的需求。
|
||||
|
||||
如果超过一个文件系统到达给定的阀值,它将发送多封邮件,因为这个脚本使用了循环。
|
||||
|
||||
同样,替换你的邮件地址来获取这份警告。
|
||||
|
||||
```
|
||||
# vi /opt/script/disk-usage-alert.sh
|
||||
|
||||
#!/bin/sh
|
||||
df -Ph | grep -vE '^Filesystem|tmpfs|cdrom' | awk '{ print $5,$1 }' | while read output;
|
||||
do
|
||||
echo $output
|
||||
used=$(echo $output | awk '{print $1}' | sed s/%//g)
|
||||
partition=$(echo $output | awk '{print $2}')
|
||||
if [ $used -ge 60 ]; then
|
||||
echo "The partition \"$partition\" on $(hostname) has used $used% at $(date)" | mail -s "Disk Space Alert: $used% Used On $(hostname)" [email protected]
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
输出:我获得了下列两封邮件警告。
|
||||
|
||||
```
|
||||
The partition "/dev/mapper/vg_2g-lv_home" on 2g.CentOS7 has used 85% at Mon Apr 29 06:16:14 IST 2019
|
||||
|
||||
The partition "/dev/mapper/vg_2g-lv_root" on 2g.CentOS7 has used 67% at Mon Apr 29 06:16:14 IST 2019
|
||||
```
|
||||
|
||||
最终添加了一个 [cronjob][2] 来自动完成。它会每 10 分钟运行一次。
|
||||
|
||||
```
|
||||
# crontab -e
|
||||
*/10 * * * * /bin/bash /opt/script/disk-usage-alert.sh
|
||||
```
|
||||
|
||||
### 方法二:Linux Shell 脚本来监控磁盘空间使用率和发送邮件
|
||||
|
||||
作为代替,你可以使用下列的 shell 脚本。对比上面的脚本我们做了少量改变。
|
||||
|
||||
```
|
||||
# vi /opt/script/disk-usage-alert-1.sh
|
||||
|
||||
#!/bin/sh
|
||||
df -Ph | grep -vE '^Filesystem|tmpfs|cdrom' | awk '{ print $5,$1 }' | while read output;
|
||||
do
|
||||
max=60%
|
||||
echo $output
|
||||
used=$(echo $output | awk '{print $1}')
|
||||
partition=$(echo $output | awk '{print $2}')
|
||||
if [ ${used%?} -ge ${max%?} ]; then
|
||||
echo "The partition \"$partition\" on $(hostname) has used $used at $(date)" | mail -s "Disk Space Alert: $used Used On $(hostname)" [email protected]
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
输出:我获得了下列两封邮件警告。
|
||||
|
||||
|
||||
```
|
||||
The partition "/dev/mapper/vg_2g-lv_home" on 2g.CentOS7 has used 85% at Mon Apr 29 06:16:14 IST 2019
|
||||
|
||||
The partition "/dev/mapper/vg_2g-lv_root" on 2g.CentOS7 has used 67% at Mon Apr 29 06:16:14 IST 2019
|
||||
```
|
||||
|
||||
最终添加了一个 [cronjob][2] 来自动完成。它会每 10 分钟运行一次。
|
||||
|
||||
```
|
||||
# crontab -e
|
||||
*/10 * * * * /bin/bash /opt/script/disk-usage-alert-1.sh
|
||||
```
|
||||
|
||||
### 方法三:Linux Shell 脚本来监控磁盘空间使用率和发送邮件
|
||||
|
||||
我更喜欢这种方法。因为,它工作起来很有魔力,你只会收到一封关于所有事的邮件。
|
||||
|
||||
这相当简单和直接。
|
||||
|
||||
```
|
||||
*/10 * * * * df -Ph | sed s/%//g | awk '{ if($5 > 60) print $0;}' | mail -s "Disk Space Alert On $(hostname)" [email protected]
|
||||
```
|
||||
|
||||
输出: 我获得了一封关于所有警告的邮件。
|
||||
|
||||
```
|
||||
Filesystem Size Used Avail Use Mounted on
|
||||
/dev/mapper/vg_2g-lv_root 10G 6.7G 3.4G 67 /
|
||||
/dev/mapper/vg_2g-lv_home 5.0G 4.3G 784M 85 /home
|
||||
```
|
||||
|
||||
### 方法四:Linux Shell 脚本来监控某个分区的磁盘空间使用情况和发送邮件
|
||||
|
||||
```
|
||||
# vi /opt/script/disk-usage-alert-2.sh
|
||||
|
||||
#!/bin/bash
|
||||
used=$(df -Ph | grep '/dev/mapper/vg_2g-lv_dbs' | awk {'print $5'})
|
||||
max=80%
|
||||
if [ ${used%?} -ge ${max%?} ]; then
|
||||
echo "The Mount Point "/DB" on $(hostname) has used $used at $(date)" | mail -s "Disk space alert on $(hostname): $used used" [email protected]
|
||||
fi
|
||||
```
|
||||
|
||||
输出: 我得到了下面的邮件警告。
|
||||
|
||||
```
|
||||
The partition /dev/mapper/vg_2g-lv_dbs on 2g.CentOS6 has used 82% at Mon Apr 29 06:16:14 IST 2019
|
||||
```
|
||||
|
||||
最终添加了一个 [cronjob][2] 来自动完成这些工作。它将每 10 分钟运行一次。
|
||||
|
||||
```
|
||||
# crontab -e
|
||||
*/10 * * * * /bin/bash /opt/script/disk-usage-alert-2.sh
|
||||
```
|
||||
|
||||
注意: 你将在 10 分钟后收到一封邮件警告,因为这个脚本被计划为每 10 分钟运行一次(但也不是精确的 10 分钟,取决于时间)。
|
||||
|
||||
例如这个例子。如果你的系统在 8:25 到达了限制,你将在 5 分钟后收到邮件警告。希望现在讲清楚了。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/linux-shell-script-to-monitor-disk-space-usage-and-send-email/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[warmfrog](https://github.com/warmfrog)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.2daygeek.com/category/shell-script/
|
||||
[2]: https://www.2daygeek.com/crontab-cronjob-to-schedule-jobs-in-linux/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,78 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10824-1.html)
|
||||
[#]: subject: (Ping Multiple Servers And Show The Output In Top-like Text UI)
|
||||
[#]: via: (https://www.ostechnix.com/ping-multiple-servers-and-show-the-output-in-top-like-text-ui/)
|
||||
[#]: author: (sk https://www.ostechnix.com/author/sk/)
|
||||
|
||||
ping 多台服务器并在类似 top 的界面中显示
|
||||
======
|
||||
|
||||
![Ping Multiple Servers And Show The Output In Top-like Text UI][1]
|
||||
|
||||
不久前,我们写了篇关于 [fping][2] 的文章,该程序能使我们能够同时 ping 多台主机。与传统的 `ping` 不同,`fping` 不会等待一台主机的超时。它使用循环法,这表示它将 ICMP Echo 请求发送到一台主机,然后转到另一台主机,最后一次显示哪些主机开启或关闭。今天,我们将讨论一个名为 `pingtop` 的类似程序。顾名思义,它会一次 ping 多台服务器,并在类似 `top` 的终端 UI 中显示结果。它是用 Python 写的自由开源程序。
|
||||
|
||||
### 安装 pingtop
|
||||
|
||||
可以使用 `pip` 安装 `pingtop`,`pip` 是一个软件包管理器,用于安装用 Python 开发的程序。确保已在 Linux 中安装了 Python 3.7.x 和 pip。
|
||||
|
||||
要在 Linux 上安装 `pip`,请参阅以下链接。
|
||||
|
||||
* [如何使用 pip 管理 Python 包][3]
|
||||
|
||||
安装 `pip` 后,运行以下命令安装 `pingtop`:
|
||||
|
||||
```
|
||||
$ pip install pingtop
|
||||
```
|
||||
|
||||
现在让我们继续使用 `pingtop` ping 多个系统。
|
||||
|
||||
### ping 多台服务器并在类似 top 的终端 UI 中显示
|
||||
|
||||
要 ping 多个主机/系统,请运行:
|
||||
|
||||
```
|
||||
$ pingtop ostechnix.com google.com facebook.com twitter.com
|
||||
```
|
||||
|
||||
现在,你将在一个漂亮的类似 `top` 的终端 UI 中看到结果,如下所示。
|
||||
|
||||
![][4]
|
||||
|
||||
*使用 pingtop ping 多台服务器*
|
||||
|
||||
|
||||
建议阅读:
|
||||
|
||||
* [一些你可能想知道的替代 “top” 命令的程序][5]
|
||||
|
||||
我个人目前没有使用 pingtop 的情况。但我喜欢在这个在文本界面中展示 ping 命令输出的想法。试试看它,也许有帮助。
|
||||
|
||||
就是这些了。还有更多好东西。敬请期待!干杯!
|
||||
|
||||
资源:
|
||||
|
||||
* [pingtop GitHub 仓库][6]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/ping-multiple-servers-and-show-the-output-in-top-like-text-ui/
|
||||
|
||||
作者:[sk][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/sk/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ostechnix.com/wp-content/uploads/2019/04/pingtop-720x340.png
|
||||
[2]: https://www.ostechnix.com/ping-multiple-hosts-linux/
|
||||
[3]: https://linux.cn/article-10110-1.html
|
||||
[4]: http://www.ostechnix.com/wp-content/uploads/2019/04/pingtop-1.gif
|
||||
[5]: https://www.ostechnix.com/some-alternatives-to-top-command-line-utility-you-might-want-to-know/
|
||||
[6]: https://github.com/laixintao/pingtop
|
@ -0,0 +1,111 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10837-1.html)
|
||||
[#]: subject: (apt-clone : Backup Installed Packages And Restore Those On Fresh Ubuntu System)
|
||||
[#]: via: (https://www.2daygeek.com/apt-clone-backup-installed-packages-and-restore-them-on-fresh-ubuntu-system/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
apt-clone:备份已安装的软件包并在新的 Ubuntu 系统上恢复它们
|
||||
======
|
||||
|
||||
当我们在基于 Ubuntu/Debian 的系统上使用 `apt-clone`,包安装会变得更加容易。如果你需要在少量系统上安装相同的软件包时,`apt-clone` 会适合你。
|
||||
|
||||
如果你想在每个系统上手动构建和安装必要的软件包,这是一个耗时的过程。它可以通过多种方式实现,Linux 中有许多程序可用。我们过去曾写过一篇关于 [Aptik][1] 的文章。它是能让 Ubuntu 用户备份和恢复系统设置和数据的程序之一。
|
||||
|
||||
### 什么是 apt-clone?
|
||||
|
||||
[apt-clone][2] 能让你为 Debian/Ubuntu 系统创建所有已安装软件包的备份,这些软件包可以在新安装的系统(或容器)或目录中恢复。
|
||||
|
||||
该备份可以在相同操作系统版本和架构的多个系统上还原。
|
||||
|
||||
### 如何安装 apt-clone?
|
||||
|
||||
`apt-clone` 包可以在 Ubuntu/Debian 的官方仓库中找到,所以,使用 [apt 包管理器][3] 或 [apt-get 包管理器][4] 来安装它。
|
||||
|
||||
使用 `apt` 包管理器安装 `apt-clone`。
|
||||
|
||||
```
|
||||
$ sudo apt install apt-clone
|
||||
```
|
||||
|
||||
使用 `apt-get` 包管理器安装 `apt-clone`。
|
||||
|
||||
```
|
||||
$ sudo apt-get install apt-clone
|
||||
```
|
||||
|
||||
### 如何使用 apt-clone 备份已安装的软件包?
|
||||
|
||||
成功安装 `apt-clone` 之后。只需提供一个保存备份文件的位置。我们将在 `/backup` 目录下保存已安装的软件包备份。
|
||||
|
||||
`apt-clone` 会将已安装的软件包列表保存到 `apt-clone-state-Ubuntu18.2daygeek.com.tar.gz` 中。
|
||||
|
||||
```
|
||||
$ sudo apt-clone clone /backup
|
||||
```
|
||||
|
||||
我们同样可以通过运行 `ls` 命令来检查。
|
||||
|
||||
```
|
||||
$ ls -lh /backup/
|
||||
total 32K
|
||||
-rw-r--r-- 1 root root 29K Apr 20 19:06 apt-clone-state-Ubuntu18.2daygeek.com.tar.gz
|
||||
```
|
||||
|
||||
运行以下命令,查看备份文件的详细信息。
|
||||
|
||||
```
|
||||
$ apt-clone info /backup/apt-clone-state-Ubuntu18.2daygeek.com.tar.gz
|
||||
Hostname: Ubuntu18.2daygeek.com
|
||||
Arch: amd64
|
||||
Distro: bionic
|
||||
Meta: libunity-scopes-json-def-desktop, ubuntu-desktop
|
||||
Installed: 1792 pkgs (194 automatic)
|
||||
Date: Sat Apr 20 19:06:43 2019
|
||||
```
|
||||
|
||||
根据上面的输出,备份文件中总共有 1792 个包。
|
||||
|
||||
### 如何恢复使用 apt-clone 进行备份的软件包?
|
||||
|
||||
你可以使用任何远程复制程序来复制远程服务器上的文件。
|
||||
|
||||
```
|
||||
$ scp /backup/apt-clone-state-ubunt-18-04.tar.gz Destination-Server:/opt
|
||||
```
|
||||
|
||||
复制完成后,使用 `apt-clone` 执行还原。
|
||||
|
||||
使用以下命令进行还原。
|
||||
|
||||
```
|
||||
$ sudo apt-clone restore /opt/apt-clone-state-Ubuntu18.2daygeek.com.tar.gz
|
||||
```
|
||||
|
||||
请注意,还原将覆盖现有的 `/etc/apt/sources.list` 并安装/删除包。所以要小心。
|
||||
|
||||
如果你要将所有软件包还原到文件夹而不是实际还原,可以使用以下命令。
|
||||
|
||||
```
|
||||
$ sudo apt-clone restore /opt/apt-clone-state-Ubuntu18.2daygeek.com.tar.gz --destination /opt/oldubuntu
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/apt-clone-backup-installed-packages-and-restore-them-on-fresh-ubuntu-system/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.2daygeek.com/aptik-backup-restore-ppas-installed-apps-users-data/
|
||||
[2]: https://github.com/mvo5/apt-clone
|
||||
[3]: https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[4]: https://www.2daygeek.com/apt-get-apt-cache-command-examples-manage-packages-debian-ubuntu-systems/
|
@ -0,0 +1,118 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (warmfrog)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10872-1.html)
|
||||
[#]: subject: (How to Add Application Shortcuts on Ubuntu Desktop)
|
||||
[#]: via: (https://itsfoss.com/ubuntu-desktop-shortcut/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
|
||||
如何在 Ubuntu 桌面手动添加应用快捷方式
|
||||
===============================
|
||||
|
||||
> 在这篇快速指南中,你将学到如何在 Ubuntu 桌面和其他使用 GNOME 桌面的发行版中添加应用图标。
|
||||
|
||||
一个经典的桌面操作系统在“桌面屏”上总是有图标的。这些桌面图标包括文件管理器、回收站和应用图标。
|
||||
|
||||
当在 Windows 中安装应用时,一些程序会询问你是否在桌面创建一个快捷方式。但在 Linux 系统中不是这样。
|
||||
|
||||
但是如果你热衷于这个特点,让我给你展示如何在 Ubuntu 桌面和其他使用 GNOME 桌面的发行版中创建应用的快捷方式。
|
||||
|
||||
![Application Shortcuts on Desktop in Ubuntu with GNOME desktop][2]
|
||||
|
||||
如果你想知道我的桌面外观,我正在使用 Ant 主题和 Tela 图标集。你可以获取一些 [GTK 主题][3] 和 [为 Ubuntu 准备的图标集][4]并换成你喜欢的。
|
||||
|
||||
### 在 Ubuntu 中添加桌面快捷方式
|
||||
|
||||
![][5]
|
||||
|
||||
个人来讲,我更喜欢为应用图标准备的 Ubuntu 启动器方式。如果我经常使用一个程序,我会添加到启动器。但是我知道不是每个人都有相同的偏好,可能少数人更喜欢桌面的快捷方式。
|
||||
|
||||
让我们看在桌面中创建应用快捷方式的最简单方式。
|
||||
|
||||
> 免责声明
|
||||
|
||||
> 这篇指南已经在 Ubuntu 18.04 LTS 的 GNOME 桌面上测试过了。它可能在其他发行版和桌面环境上也能发挥作用,但你必须自己尝试。一些 GNOME 特定步骤可能会变,所以请在[其他桌面环境][7]尝试时注意。
|
||||
|
||||
#### 准备
|
||||
|
||||
首先最重要的事是确保你有 GNOME 桌面的图标权限。
|
||||
|
||||
如果你跟随 Ubuntu 18.04 自定义提示,你会知道如何安装 GNOME Tweaks 工具。在这个工具中,确保你设置“Show Icons”选项为启用。
|
||||
|
||||
![Allow icons on desktop in GNOME][9]
|
||||
|
||||
一旦你确保已经设置,是时候在桌面添加应用快捷方式了。
|
||||
|
||||
#### 第一步:定位应用的 .desktop 文件
|
||||
|
||||
到 “Files -> Other Location -> Computer”。
|
||||
|
||||
![Go to Other Locations -> Computer][11]
|
||||
|
||||
从这里,到目录 “usr -> share -> applications”。你会在这里看到几个你已经安装的 [Ubuntu 应用][12]。即使你没有看到图标,你应该看到被命名为“应用名.desktop”形式的文件。
|
||||
|
||||
![Application Shortcuts][13]
|
||||
|
||||
#### 第二步:拷贝 .desktop 文件到桌面
|
||||
|
||||
现在你要做的只是查找应用图标(或者它的 desktop 文件)。当你找到后,拖文件到桌面或者拷贝文件(使用 `Ctrl+C` 快捷方式)并在桌面粘贴(使用 `Ctrl+V` 快捷方式)。
|
||||
|
||||
![Add .desktop file to the desktop][14]
|
||||
|
||||
#### 第三步:运行 desktop 文件
|
||||
|
||||
当你这么做,你应该在桌面上看到一个图标的文本文件而不是应用 logo。别担心,一会就不一样了。
|
||||
|
||||
你要做的就是双击桌面的那个文件。它将警告你它是一个“未信任的应用启动器’,点击“信任并启动”。
|
||||
|
||||
![Launch Desktop Shortcut][15]
|
||||
|
||||
这个应用像往常一样启动,好事是你会察觉到 .desktop 文件现在已经变成应用图标了。我相信你喜欢应用图标的方式,不是吗?
|
||||
|
||||
![Application shortcut on the desktop][16]
|
||||
|
||||
#### Ubuntu 19.04 或者 GNOME 3.32 用户的疑难杂症
|
||||
|
||||
如果你使用 Ubuntu 19.04 或者 GNOME 3.32,你的 .desktop 文件可能根本不会启动。你应该右击 .desktop 文件并选择 “允许启动”。
|
||||
|
||||
在这之后,你应该能够启动应用并且桌面上的应用快捷方式能够正常显示了。
|
||||
|
||||
### 总结
|
||||
|
||||
如果你不喜欢桌面的某个应用启动器,选择删除就是了。它会删除应用快捷方式,但是应用仍安全的保留在你的系统中。
|
||||
|
||||
我希望你发现这篇快速指南有帮助并喜欢在 Ubuntu 桌面上的应用快捷方式。
|
||||
|
||||
如果你有问题或建议,请在下方评论让我知道。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/ubuntu-desktop-shortcut/
|
||||
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[warmfrog](https://github.com/warmfrog)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/abhishek/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ubuntu.com/
|
||||
[2]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/05/app-shortcut-on-ubuntu-desktop.jpeg?resize=800%2C450&ssl=1
|
||||
[3]: https://itsfoss.com/best-gtk-themes/
|
||||
[4]: https://itsfoss.com/best-icon-themes-ubuntu-16-04/
|
||||
[5]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/add-ubuntu-desktop-shortcut.jpeg?resize=800%2C450&ssl=1
|
||||
[6]: https://www.gnome.org/
|
||||
[7]: https://itsfoss.com/best-linux-desktop-environments/
|
||||
[8]: https://www.youtube.com/c/itsfoss?sub_confirmation=1
|
||||
[9]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/allow-icons-on-desktop-gnome.jpg?ssl=1
|
||||
[10]: https://itsfoss.com/replace-linux-from-dual-boot/
|
||||
[11]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/Adding-desktop-shortcut-Ubuntu-gnome-1.png?resize=800%2C436&ssl=1
|
||||
[12]: https://itsfoss.com/best-ubuntu-apps/
|
||||
[13]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/application-shortcuts-in-ubuntu.png?resize=800%2C422&ssl=1
|
||||
[14]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/add-desktop-file-to-desktop.jpeg?resize=800%2C458&ssl=1
|
||||
[15]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/05/launch-desktop-shortcut-.jpeg?resize=800%2C349&ssl=1
|
||||
[16]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/05/app-shortcut-on-desktop-ubuntu-gnome.jpeg?resize=800%2C375&ssl=1
|
||||
[17]: https://itsfoss.com/install-nemo-file-manager-ubuntu/
|
@ -0,0 +1,139 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (warmfrog)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10865-1.html)
|
||||
[#]: subject: (How to use advanced rsync for large Linux backups)
|
||||
[#]: via: (https://opensource.com/article/19/5/advanced-rsync)
|
||||
[#]: author: (Alan Formy-Duval https://opensource.com/users/alanfdoss/users/marcobravo)
|
||||
|
||||
如何使用 rsync 的高级用法进行大型备份
|
||||
=====================================
|
||||
|
||||
> 基础的 `rsync` 命令通常足够来管理你的 Linux 备份,但是额外的选项使大型备份集更快、更强大。
|
||||
|
||||
![Filing papers and documents][1]
|
||||
|
||||
很明显,备份一直是 Linux 世界的热门话题。回到 2017,David Both 为 [Opensource.com][2] 的读者在[使用 rsync 备份 Linux 系统][3]方面提了一些建议,在这年的更早时候,他发起了一项问卷调查询问大家,[在 Linux 中你的 /home 目录的主要备份策略是什么][4],在今年的另一个问卷调查中,Don Watkins 问到,[你使用哪种开源备份解决方案][5]。
|
||||
|
||||
我的回复是 [rsync][6]。我真的非常喜欢 rsync!市场上有大量大而复杂的工具,对于管理磁带机或者存储库设备,这些可能是必要的,但是可能你需要的只是一个简单的开源命令行工具。
|
||||
|
||||
### rsync 基础
|
||||
|
||||
我为一个大概拥有 35,000 开发者并有着几十 TB 文件的全球性机构管理二进制仓库。我经常一次移动或者归档上百 GB 的数据。使用的是 `rsync`。这种经历使我对这个简单的工具充满信心。(所以,是的,我在家使用它来备份我的 Linux 系统)
|
||||
|
||||
基础的 `rsync` 命令很简单。
|
||||
|
||||
|
||||
```
|
||||
rsync -av 源目录 目的地目录
|
||||
```
|
||||
|
||||
实际上,在各种指南中教的 `rsync` 命令在大多数通用情况下都运行的很好。然而,假设我们需要备份大量的数据。例如包含 2,000 个子目录的目录,每个包含 50GB 到 700GB 的数据。在这个目录运行 `rsync` 可能需要大量时间,尤其是当你使用校验选项时(我倾向使用)。
|
||||
|
||||
当我们试图同步大量数据或者通过慢的网络连接时,可能遇到性能问题。让我给你展示一些我使用的方法来确保好的性能和可靠性。
|
||||
|
||||
### rsync 高级用法
|
||||
|
||||
`rsync` 运行时出现的第一行是:“正在发送增量文件列表。” 如果你在网上搜索这一行,你将看到很多类似的问题:为什么它一直运行,或者为什么它似乎挂起了。
|
||||
|
||||
这里是一个基于这个场景的例子。假设我们有一个 `/storage` 的目录,我们想要备份到一个外部 USB 磁盘,我们可以使用下面的命令:
|
||||
|
||||
```
|
||||
rsync -cav /storage /media/WDPassport
|
||||
```
|
||||
|
||||
`-c` 选项告诉 `rsync` 使用文件校验和而不是时间戳来决定改变的文件,这通常消耗的时间更久。为了分解 `/storage` 目录,我通过子目录同步,使用 `find` 命令。这是一个例子:
|
||||
|
||||
|
||||
```
|
||||
find /storage -type d -exec rsync -cav {} /media/WDPassport \;
|
||||
```
|
||||
|
||||
这看起来可以,但是如果 `/storage` 目录有任何文件,它们将被跳过。因此,我们如何同步 `/storage` 目录中的文件呢?同样有一个细微的差别是这些选项将造成 `rsync` 会同步 `.` 目录,该目录是源目录自身;这意味着它会同步子目录两次,这并不是我们想要的。
|
||||
|
||||
长话短说,我的解决方案是一个 “双-递增”脚本。这允许我分解一个目录,例如,当你的家目录有多个大的目录,例如音乐或者家庭照片时,分解 `/home` 目录为单个的用户家目录。
|
||||
|
||||
这是我的脚本的一个例子:
|
||||
|
||||
```
|
||||
HOMES="alan"
|
||||
DRIVE="/media/WDPassport"
|
||||
|
||||
for HOME in $HOMES; do
|
||||
cd /home/$HOME
|
||||
rsync -cdlptgov --delete . /$DRIVE/$HOME
|
||||
find . -maxdepth 1 -type d -not -name "." -exec rsync -crlptgov --delete {} /$DRIVE/$HOME \;
|
||||
done
|
||||
```
|
||||
|
||||
第一个 `rsync` 命令拷贝它在源目录中发现的文件和目录。然而,它将目录留着不处理,因此我们能够通过 `find` 命令迭代它们。这通过传递 `-d` 参数来完成,它告诉 `rsync` 不要递归目录。
|
||||
|
||||
|
||||
```
|
||||
-d, --dirs 传输目录而不递归
|
||||
```
|
||||
|
||||
然后 `find` 命令传递每个目录来单独运行 `rsync`。之后 `rsync` 拷贝目录的内容。这通过传递 `-r` 参数来完成,它告诉 `rsync` 要递归目录。
|
||||
|
||||
|
||||
```
|
||||
-r, --recursive 递归进入目录
|
||||
```
|
||||
|
||||
这使得 `rsync` 使用的增量文件保持在一个合理的大小。
|
||||
|
||||
大多数 `rsync` 指南为了简便使用 `-a` (或者 `archive`) 参数。这实际是一个复合参数。
|
||||
|
||||
```
|
||||
-a, --archive 归档模式;等价于 -rlptgoD(没有 -H,-A,-X)
|
||||
```
|
||||
|
||||
我传递的其他参数包含在 `a` 中;这些是 `-l`、`-p`、`-t`、`-g`和 `-o`。
|
||||
|
||||
|
||||
```
|
||||
-l, --links 复制符号链接作为符号链接
|
||||
-p, --perms 保留权限
|
||||
-t, --times 保留修改时间
|
||||
-g, --group 保留组
|
||||
-o, --owner 保留拥有者(只适用于超级管理员)
|
||||
```
|
||||
|
||||
`--delete` 选项告诉 `rsync` 删除目的地目录中所有在源目录不存在的任意文件。这种方式,运行的结果仅仅是复制。你同样可以排除 `.Trash` 目录或者 MacOS 创建的 `.DS_Store` 文件。
|
||||
|
||||
|
||||
```
|
||||
-not -name ".Trash*" -not -name ".DS_Store"
|
||||
```
|
||||
|
||||
### 注意
|
||||
|
||||
最后一条建议: `rsync` 可以是破坏性的命令。幸运的是,它的睿智的创造者提供了 “空运行” 的能力。如果我们加入 `n` 选项,rsync 会显示预期的输出但不写任何数据。
|
||||
|
||||
|
||||
```
|
||||
`rsync -cdlptgovn --delete . /$DRIVE/$HOME`
|
||||
```
|
||||
|
||||
这个脚本适用于非常大的存储规模和高延迟或者慢链接的情况。一如既往,我确信仍有提升的空间。如果你有任何建议,请在下方评论中分享。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/advanced-rsync
|
||||
|
||||
作者:[Alan Formy-Duval][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[warmfrog](https://github.com/warmfrog)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/alanfdoss/users/marcobravo
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/documents_papers_file_storage_work.png?itok=YlXpAqAJ (Filing papers and documents)
|
||||
[2]: http://Opensource.com
|
||||
[3]: https://linux.cn/article-8237-1.html
|
||||
[4]: https://opensource.com/poll/19/4/backup-strategy-home-directory-linux
|
||||
[5]: https://opensource.com/article/19/2/linux-backup-solutions
|
||||
[6]: https://en.wikipedia.org/wiki/Rsync
|
@ -0,0 +1,263 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10860-1.html)
|
||||
[#]: subject: (21 Best Kali Linux Tools for Hacking and Penetration Testing)
|
||||
[#]: via: (https://itsfoss.com/best-kali-linux-tools/)
|
||||
[#]: author: (Ankush Das https://itsfoss.com/author/ankush/)
|
||||
|
||||
用于黑客渗透测试的 21 个最佳 Kali Linux 工具
|
||||
======
|
||||
|
||||
> 这里是最好的 Kali Linux 工具列表,它们可以让你评估 Web 服务器的安全性,并帮助你执行黑客渗透测试。
|
||||
|
||||
如果你读过 [Kali Linux 点评][1],你就知道为什么它被认为是[最好的黑客渗透测试的 Linux 发行版][2]之一,而且名副其实。它带有许多工具,使你可以更轻松地测试、破解以及进行与数字取证相关的任何其他工作。
|
||||
|
||||
它是<ruby>道德黑客<rt>ethical hacker</rt></ruby>最推荐的 Linux 发行版之一。即使你不是黑客而是网站管理员 —— 你仍然可以利用其中某些工具轻松地扫描你的网络服务器或网页。
|
||||
|
||||
在任何一种情况下,无论你的目的是什么 —— 让我们来看看你应该使用的一些最好的 Kali Linux 工具。
|
||||
|
||||
*注意:这里不是所提及的所有工具都是开源的。*
|
||||
|
||||
### 用于黑客渗透测试的 Kali Linux 工具
|
||||
|
||||
![Kali Linux](https://img.linux.net.cn/data/attachment/album/201905/15/234125c22rx77mmz9m37zo.jpg)
|
||||
|
||||
Kali Linux 预装了几种类型的工具。如果你发现有的工具没有安装,只需下载并进行设置即可。这很简单。
|
||||
|
||||
#### 1、Nmap
|
||||
|
||||
![Kali Linux Nmap][4]
|
||||
|
||||
[Nmap][5] (即 “<ruby>网络映射器<rt>Network Mapper</rt></ruby>”)是 Kali Linux 上最受欢迎的信息收集工具之一。换句话说,它可以获取有关主机的信息:其 IP 地址、操作系统检测以及网络安全的详细信息(如开放的端口数量及其含义)。
|
||||
|
||||
它还提供防火墙规避和欺骗功能。
|
||||
|
||||
#### 2、Lynis
|
||||
|
||||
![Lynis Kali Linux Tool][6]
|
||||
|
||||
[Lynis][7] 是安全审计、合规性测试和系统强化的强大工具。当然,你也可以将其用于漏洞检测和渗透测试。
|
||||
|
||||
它将根据检测到的组件扫描系统。例如,如果它检测到 Apache —— 它将针对入口信息运行与 Apache 相关的测试。
|
||||
|
||||
#### 3、WPScan
|
||||
|
||||
![][8]
|
||||
|
||||
WordPress 是[最好的开源 CMS][9]之一,而这个工具是最好的免费 WordpPress 安全审计工具。它是免费的,但不是开源的。
|
||||
|
||||
如果你想知道一个 WordPress 博客是否在某种程度上容易受到攻击,[WPScan][10] 就是你的朋友。
|
||||
|
||||
此外,它还为你提供了所用的插件的详细信息。当然,一个安全性很好的博客可能不会暴露给你很多细节,但它仍然是 WordPress 安全扫描找到潜在漏洞的最佳工具。
|
||||
|
||||
#### 4、Aircrack-ng
|
||||
|
||||
![][11]
|
||||
|
||||
[Aircrack-ng][12] 是评估 WiFi 网络安全性的工具集合。它不仅限于监控和获取信息 —— 还包括破坏网络(WEP、WPA 1 和 WPA 2)的能力。
|
||||
|
||||
如果你忘记了自己的 WiFi 网络的密码,可以尝试使用它来重新获得访问权限。它还包括各种无线攻击能力,你可以使用它们来定位和监控 WiFi 网络以增强其安全性。
|
||||
|
||||
#### 5、Hydra
|
||||
|
||||
![][13]
|
||||
|
||||
如果你正在寻找一个有趣的工具来破解登录密码,[Hydra][14] 将是 Kali Linux 预装的最好的工具之一。
|
||||
|
||||
它可能不再被积极维护,但它现在放在 [GitHub][15] 上,所以你也可以为它做贡献。
|
||||
|
||||
#### 6、Wireshark
|
||||
|
||||
![][17]
|
||||
|
||||
[Wireshark][18] 是 Kali Linux 上最受欢迎的网络分析仪。它也可以归类为用于网络嗅探的最佳 Kali Linux 工具之一。
|
||||
|
||||
它正在积极维护,所以我肯定会建议你试试它。
|
||||
|
||||
#### 7、Metasploit Framework
|
||||
|
||||
![][19]
|
||||
|
||||
[Metsploit Framework][20](MSF)是最常用的渗透测试框架。它提供两个版本:一个开源版,另外一个是其专业版。使用此工具,你可以验证漏洞、测试已知漏洞并执行完整的安全评估。
|
||||
|
||||
当然,免费版本不具备所有功能,所以如果你在意它们的区别,你应该在[这里][21]比较一下版本。
|
||||
|
||||
#### 8、Skipfish
|
||||
|
||||
![][22]
|
||||
|
||||
与 WPScan 类似,但它不仅仅专注于 WordPress。[Skipfish][23] 是一个 Web 应用扫描程序,可以为你提供几乎所有类型的 Web 应用程序的洞察信息。它快速且易于使用。此外,它的递归爬取方法使它更好用。
|
||||
|
||||
Skipfish 生成的报告可以用于专业的 Web 应用程序安全评估。
|
||||
|
||||
#### 9、Maltego
|
||||
|
||||
![][24]
|
||||
|
||||
[Maltego][25] 是一种令人印象深刻的数据挖掘工具,用于在线分析信息并连接信息点(如果有的话)。 根据这些信息,它创建了一个有向图,以帮助分析这些数据之间的链接。
|
||||
|
||||
请注意,这不是一个开源工具。
|
||||
|
||||
它已预装,但你必须注册才能选择要使用的版本。如果个人使用,社区版就足够了(只需要注册一个帐户),但如果想用于商业用途,则需要订阅 classic 或 XL 版本。
|
||||
|
||||
#### 10、Nessus
|
||||
|
||||
![Nessus][26]
|
||||
|
||||
如果你的计算机连接到了网络,Nessus 可以帮助你找到潜在攻击者可能利用的漏洞。当然,如果你是多台连接到网络的计算机的管理员,则可以使用它并保护这些计算机。
|
||||
|
||||
但是,它不再是免费的工具了,你可以从[官方网站][27]免费试用 7 天。
|
||||
|
||||
#### 11、Burp Suite Scanner
|
||||
|
||||
![][28]
|
||||
|
||||
[Burp Suite Scanner][29] 是一款出色的网络安全分析工具。与其它 Web 应用程序安全扫描程序不同,Burp 提供了 GUI 和一些高级工具。
|
||||
|
||||
社区版仅将功能限制为一些基本的手动工具。对于专业人士,你必须考虑升级。与前面的工具类似,这也不是开源的。
|
||||
|
||||
我使用过免费版本,但是如果你想了解更多细节,你应该查看他们[官方网站][29]上提供的功能。
|
||||
|
||||
#### 12、BeEF
|
||||
|
||||
![][30]
|
||||
|
||||
BeEF(<ruby>浏览器利用框架<rt>Browser Exploitation Framework</rt></ruby>)是另一个令人印象深刻的工具。它专为渗透测试人员量身定制,用于评估 Web 浏览器的安全性。
|
||||
|
||||
这是最好的 Kali Linux 工具之一,因为很多用户在谈论 Web 安全时希望了解并修复客户端的问题。
|
||||
|
||||
#### 13、Apktool
|
||||
|
||||
![][31]
|
||||
|
||||
[Apktool][32] 确实是 Kali Linux 上用于逆向工程 Android 应用程序的流行工具之一。当然,你应该正确利用它 —— 出于教育目的。
|
||||
|
||||
使用此工具,你可以自己尝试一下,并让原开发人员了解你的想法。你认为你会用它做什么?
|
||||
|
||||
#### 14、sqlmap
|
||||
|
||||
![][34]
|
||||
|
||||
如果你正在寻找一个开源渗透测试工具 —— [sqlmap][35] 是最好的之一。它可以自动化利用 SQL 注入漏洞的过程,并帮助你接管数据库服务器。
|
||||
|
||||
#### 15、John the Ripper
|
||||
|
||||
![John The Ripper][36]
|
||||
|
||||
[John the Ripper][37] 是 Kali Linux 上流行的密码破解工具。它也是自由开源的。但是,如果你对[社区增强版][37]不感兴趣,可以用于商业用途的[专业版][38]。
|
||||
|
||||
#### 16、Snort
|
||||
|
||||
想要实时流量分析和数据包记录功能吗?[Snort][39] 可以鼎力支持你。即使它是一个开源的入侵防御系统,也有很多东西可以提供。
|
||||
|
||||
如果你还没有安装它,[官方网站][40]提及了安装过程。
|
||||
|
||||
#### 17、Autopsy Forensic Browser
|
||||
|
||||
![][41]
|
||||
|
||||
[Autopsy][42] 是一个数字取证工具,用于调查计算机上发生的事情。那么,你也可以使用它从 SD 卡恢复图像。它也被执法官员使用。你可以阅读[文档][43]来探索可以用它做什么。
|
||||
|
||||
你还应该查看他们的 [GitHub 页面][44]。
|
||||
|
||||
#### 18、King Phisher
|
||||
|
||||
![King Phisher][45]
|
||||
|
||||
网络钓鱼攻击现在非常普遍。[King Phisher 工具][46]可以通过模拟真实的网络钓鱼攻击来帮助测试和提升用户意识。出于显而易见的原因,在模拟一个组织的服务器内容前,你需要获得许可。
|
||||
|
||||
#### 19、Nikto
|
||||
|
||||
![Nikto][47]
|
||||
|
||||
[Nikto][48] 是一款功能强大的 Web 服务器扫描程序 —— 这使其成为最好的 Kali Linux 工具之一。 它会检查存在潜在危险的文件/程序、过时的服务器版本等等。
|
||||
|
||||
#### 20、Yersinia
|
||||
|
||||
![][49]
|
||||
|
||||
[Yersinia][50] 是一个有趣的框架,用于在网络上执行第 2 层攻击(第 2 层是指 [OSI 模型][51]的数据链路层)。当然,如果你希望你的网络安全,则必须考虑所有七个层。但是,此工具侧重于第 2 层和各种网络协议,包括 STP、CDP,DTP 等。
|
||||
|
||||
#### 21、Social Engineering Toolkit (SET)
|
||||
|
||||
![][52]
|
||||
|
||||
如果你正在进行相当严格的渗透测试,那么这应该是你应该检查的最佳工具之一。社交工程是一个大问题,使用 [SET][53] 工具,你可以帮助防止此类攻击。
|
||||
|
||||
### 总结
|
||||
|
||||
实际上 Kali Linux 捆绑了很多工具。请参考 Kali Linux 的[官方工具列表页面][54]来查找所有内容。
|
||||
|
||||
你会发现其中一些是完全自由开源的,而有些则是专有解决方案(但是免费)。但是,出于商业目的,你应该始终选择高级版本。
|
||||
|
||||
我们可能错过了你最喜欢的某个 Kali Linux 工具。请在下面的评论部分告诉我们。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/best-kali-linux-tools/
|
||||
|
||||
作者:[Ankush Das][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/ankush/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://itsfoss.com/kali-linux-review/
|
||||
[2]: https://itsfoss.com/linux-hacking-penetration-testing/
|
||||
[3]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/04/kali-linux-tools.jpg?resize=800%2C518&ssl=1
|
||||
[4]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/04/kali-linux-nmap.jpg?resize=800%2C559&ssl=1
|
||||
[5]: https://nmap.org/
|
||||
[6]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/04/lynis-kali-linux-tool.jpg?resize=800%2C525&ssl=1
|
||||
[7]: https://cisofy.com/lynis/
|
||||
[8]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/04/wpscan-kali-linux.jpg?resize=800%2C545&ssl=1
|
||||
[9]: https://itsfoss.com/open-source-cms/
|
||||
[10]: https://wpscan.org/
|
||||
[11]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/04/aircrack-ng-kali-linux-tool.jpg?resize=800%2C514&ssl=1
|
||||
[12]: https://www.aircrack-ng.org/
|
||||
[13]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/04/hydra-kali-linux.jpg?resize=800%2C529&ssl=1
|
||||
[14]: https://github.com/vanhauser-thc/thc-hydra
|
||||
[15]: https://github.com/vanhauser-thc/THC-Archive
|
||||
[16]: https://itsfoss.com/new-linux-distros-2013/
|
||||
[17]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/04/wireshark-network-analyzer.jpg?resize=800%2C556&ssl=1
|
||||
[18]: https://www.wireshark.org/
|
||||
[19]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/04/metasploit-framework.jpg?resize=800%2C561&ssl=1
|
||||
[20]: https://github.com/rapid7/metasploit-framework
|
||||
[21]: https://www.rapid7.com/products/metasploit/download/editions/
|
||||
[22]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/04/skipfish-kali-linux-tool.jpg?resize=800%2C515&ssl=1
|
||||
[23]: https://gitlab.com/kalilinux/packages/skipfish/
|
||||
[24]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/04/maltego.jpg?resize=800%2C403&ssl=1
|
||||
[25]: https://www.paterva.com/web7/buy/maltego-clients.php
|
||||
[26]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/05/nessus.jpg?resize=800%2C456&ssl=1
|
||||
[27]: https://www.tenable.com/try
|
||||
[28]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/04/burp-suite-community-edition-800x582.jpg?resize=800%2C582&ssl=1
|
||||
[29]: https://portswigger.net/burp
|
||||
[30]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/04/beef-framework.jpg?resize=800%2C339&ssl=1
|
||||
[31]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/04/apktool.jpg?resize=800%2C504&ssl=1
|
||||
[32]: https://github.com/iBotPeaches/Apktool
|
||||
[33]: https://itsfoss.com/format-factory-alternative-linux/
|
||||
[34]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/sqlmap.jpg?resize=800%2C528&ssl=1
|
||||
[35]: http://sqlmap.org/
|
||||
[36]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/05/john-the-ripper.jpg?ssl=1
|
||||
[37]: https://github.com/magnumripper/JohnTheRipper
|
||||
[38]: https://www.openwall.com/john/pro/
|
||||
[39]: https://www.snort.org/
|
||||
[40]: https://www.snort.org/#get-started
|
||||
[41]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/autopsy-forensic-browser.jpg?resize=800%2C319&ssl=1
|
||||
[42]: https://www.sleuthkit.org/autopsy/
|
||||
[43]: https://www.sleuthkit.org/autopsy/docs.php
|
||||
[44]: https://github.com/sleuthkit/autopsy
|
||||
[45]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/05/king-phisher.jpg?resize=800%2C626&ssl=1
|
||||
[46]: https://github.com/securestate/king-phisher
|
||||
[47]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/05/nikto.jpg?resize=800%2C511&ssl=1
|
||||
[48]: https://gitlab.com/kalilinux/packages/nikto/
|
||||
[49]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/yersinia.jpg?resize=800%2C516&ssl=1
|
||||
[50]: https://github.com/tomac/yersinia
|
||||
[51]: https://en.wikipedia.org/wiki/OSI_model
|
||||
[52]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/social-engineering-toolkit.jpg?resize=800%2C511&ssl=1
|
||||
[53]: https://www.trustedsec.com/social-engineer-toolkit-set/
|
||||
[54]: https://tools.kali.org/tools-listing
|
@ -0,0 +1,109 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (warmfrog)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10855-1.html)
|
||||
[#]: subject: (How to Use 7Zip in Ubuntu and Other Linux [Quick Tip])
|
||||
[#]: via: (https://itsfoss.com/use-7zip-ubuntu-linux/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
|
||||
如何在 Ubuntu 和其他 Linux 发行版上使用 7Zip
|
||||
==============================================
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201905/14/154515xqy7nbq6eyjzu7qj.jpg)
|
||||
|
||||
> 不能在 Linux 中提取 .7z 文件?学习如何在 Ubuntu 和其他 Linux 发行版中安装和使用 7zip。
|
||||
|
||||
[7Zip][1](更适当的写法是 7-Zip)是一种在 Windows 用户中广泛流行的归档格式。一个 7Zip 归档文件通常以 .7z 扩展结尾。它大部分是开源的,除了包含一些少量解压 rar 文件的代码。
|
||||
|
||||
默认大多数 Linux 发行版不支持 7Zip。如果你试图提取它,你会看见这个错误:
|
||||
|
||||
> 不能打开这种文件类型
|
||||
|
||||
> 没有已安装的适用 7-zip 归档文件的命令。你想搜索一个命令来打开这个文件吗?
|
||||
|
||||
![][2]
|
||||
|
||||
不要担心,你可以轻松的在 Ubuntu 和其他 Linux 发行版中安装 7zip。
|
||||
|
||||
一个问题是你会注意到如果你试图用 [apt-get install 命令][3],你会发现没有以 7zip 开头的候选安装。因为在 Linux 中 7Zip 包的名字是 [p7zip][4]。以字母 “p” 开头而不是预期的数字 “7”。
|
||||
|
||||
让我们看一下如何在 Ubuntu 和其他 Linux 发行版中安装 7zip。
|
||||
|
||||
### 在 Ubuntu Linux 中安装 7Zip
|
||||
|
||||
你需要做的第一件事是安装 p7zip 包。你会在 Ubuntu 中发现 3 个包:p7zip、p7zip-full 和 pzip-rar。
|
||||
|
||||
pzip 和 p7zip-full 的不同是 pzip 是一个轻量级的版本,仅仅对 .7z 文件提供支持,而 p7zip-full 提供了更多的 7z 压缩算法(例如音频文件)。
|
||||
|
||||
p7zip-rar 包在 7z 中提供了对 [RAR 文件][6] 的支持
|
||||
|
||||
在大多数情况下安装 p7zip-full 就足够了,但是你可能想安装 p7zip-rar 来支持 rar 文件的解压。
|
||||
|
||||
p7zip 包在 [Ubuntu 的 universe 仓库][7] 因此保证你可以使用以下命令:
|
||||
|
||||
```
|
||||
sudo add-apt-repository universe
|
||||
sudo apt update
|
||||
```
|
||||
|
||||
在 Ubuntu 和基于 Debian 的发行版中使用以下命令。
|
||||
|
||||
```
|
||||
sudo apt install p7zip-full p7zip-rar
|
||||
```
|
||||
|
||||
这很好。现在在你的系统就有了 7zip 归档的支持。
|
||||
|
||||
### 在 Linux 中提取 7Zip 归档文件
|
||||
|
||||
安装了 7Zip 后,在 Linux 中,你可以在图形用户界面或者 命令行中提取 7zip 文件。
|
||||
|
||||
在图形用户界面,你可以像提取其他压缩文件一样提取 .7z 文件。右击文件来提取它。
|
||||
|
||||
在终端中,你可以使用下列命令提取 .7z 归档文件:
|
||||
|
||||
```
|
||||
7z e file.7z
|
||||
```
|
||||
|
||||
### 在 Linux 中压缩文件为 7zip 归档格式
|
||||
|
||||
你可以在图形界面压缩文件为 7zip 归档格式。简单的在文件或目录上右击,选择“压缩”。你应该看到几种类型的文件格式选项。选择 .7z。
|
||||
|
||||
![7zip Archive Ubuntu][9]
|
||||
|
||||
作为替换,你也可以在命令行中使用。这里是你可以用来压缩的命令:
|
||||
|
||||
```
|
||||
7z a 输出的文件名 要压缩的文件
|
||||
```
|
||||
|
||||
默认,归档文件有 .7z 扩展。你可以通过在指定输出文件扩展名为 .zip 以压缩为 zip 格式。
|
||||
|
||||
### 总结
|
||||
|
||||
就是这样。看,在 Linux 中使用 7zip 多简单?我希望你喜欢这个快速指南。如果你有问题或者建议,请随意在下方评论让我知道。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/use-7zip-ubuntu-linux/
|
||||
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[warmfrog](https://github.com/warmfrog)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/abhishek/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.7-zip.org/
|
||||
[2]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2015/07/Install_7zip_ubuntu_1.png?ssl=1
|
||||
[3]: https://itsfoss.com/apt-get-linux-guide/
|
||||
[4]: https://sourceforge.net/projects/p7zip/
|
||||
[5]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/05/7zip-linux.png?resize=800%2C450&ssl=1
|
||||
[6]: https://itsfoss.com/use-rar-ubuntu-linux/
|
||||
[7]: https://itsfoss.com/ubuntu-repositories/
|
||||
[8]: https://itsfoss.com/easily-share-files-linux-windows-mac-nitroshare/
|
||||
[9]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/05/7zip-archive-ubuntu.png?resize=800%2C239&ssl=1
|
169
published/201905/20190510 PHP in 2019.md
Normal file
169
published/201905/20190510 PHP in 2019.md
Normal file
@ -0,0 +1,169 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10870-1.html)
|
||||
[#]: subject: (PHP in 2019)
|
||||
[#]: via: (https://stitcher.io/blog/php-in-2019)
|
||||
[#]: author: (Brent https://stitcher.io/blog/php-in-2019)
|
||||
|
||||
9102 年的 PHP
|
||||
======
|
||||
|
||||
你还记得篇流行的博客文章《[PHP:设计糟糕的分形][3]》吗?我第一次读到它时,我在一个有很多遗留的 PHP 项目的糟糕地方工作。这篇文章让我觉得我是否应该放弃,并去做与编程完全不同的事情。
|
||||
|
||||
还好,我之后很快就换了工作,更重要的是,自从 5.x 版本以来,PHP 成功地进步了很多。今天,我在向那些不再使用 PHP 编程,或者陷入遗留项目的人们致意。
|
||||
|
||||
剧透:今天有些事情仍然很糟糕,就像几乎每种编程语言都有它的怪癖一样。许多核心功能仍然有不一致的调用方法,仍然有令人困惑的配置设置,仍然有许多开发人员在那里写蹩脚的代码 —— 因为他们必须如此,或是他们不知道更好的写法。
|
||||
|
||||
今天我想看看好的一面:让我们关注已经发生变化的事情,以及编写干净而可维护的 PHP 代码的方法。在此之前,我想请你暂时搁置任何偏见。
|
||||
|
||||
然后,你可以像以前一样对 PHP 自由吐槽。虽然,你可能会对 PHP 在过去的几年里的一些改进感到惊讶。(LCTT 译注:说实话,我是真的感到吃惊)
|
||||
|
||||
### 提前看结论
|
||||
|
||||
* PHP 在积极地开发,每年都有新版本
|
||||
* 自 PHP 5 时代以来的性能已经翻倍,如果不是三倍的话
|
||||
* 有一个非常活跃的框架、包和平台的生态系统
|
||||
* PHP 在过去几年中添加了许多新功能,并且这种语言在不断发展
|
||||
* 像静态分析这样的工具在过去几年中已经成熟,并且一直保持增长
|
||||
|
||||
更新:人们让我展示一些实际的代码。我觉得这没问题!这是我的一个业余项目的[源代码][4],用 PHP 和 Laravel 编写的;[这里][5]列出了我们在办公室维护的几百个自由开源软件包。这两者都是现代 PHP 项目的好例子。
|
||||
|
||||
那让我们开始吧。
|
||||
|
||||
### 历史总结
|
||||
|
||||
出于更好地衡量的目的,让我们快速回顾一下如今的 PHP 发布周期。我们现在的 PHP 为 7.3,预计在 2019 年底为 7.4。PHP 8.0 将是 7.4 之后的下一个版本。
|
||||
|
||||
自从 5.x 时代以来,核心团队试图保持每年发布一个版本的周期,并且他们在过去的四年中成功地做到了这一点。
|
||||
|
||||
一般来说,每个新版本都会在两年内得到积极支持,并再获得一年以上的“安全修复”。其目标是激励 PHP 开发人员尽可能保持最新:例如,每年进行小规模升级比在 5.4 到 7.0 之间跳转更容易。
|
||||
|
||||
可以在 [这里][6] 找到 PHP 时间轴的活动概述。
|
||||
|
||||
最后,PHP 5.6 是最新的 5.x 版本,而 8.0 是当前的下一个大版本。如果你想知道 PHP 6 发生了什么,你可以听听 [PHP Roundtable 播客][7]。
|
||||
|
||||
了解了这个,让我们揭穿一些关于现代 PHP 的常见误解。
|
||||
|
||||
### PHP 的性能
|
||||
|
||||
早在 5.x 时代,PHP 的表现就是……嗯,平均水平。但是在 7.0 版本中,PHP 从头开始重写了核心部分,导致其性能提升了两到三倍!
|
||||
|
||||
但光是嘴说是不够的。让我们来看看基准测试。幸运的是,人们花了很多时间对 PHP 性能进行了基准测试。 我发现 [Kinsta][8] 有一个很好的更新的测试列表。
|
||||
|
||||
自 7.0 升级以来,性能就一直在提升而没有回退。PHP Web 应用程序的性能可与其它语言中的 Web 框架相提并论,甚至在某些情况下更好。你可以看看这个[广泛的基准测试套件][9]。
|
||||
|
||||
当然 PHP 框架不会胜过 C 和 Rust,但它们比 Rails 或 Django 要好得多,并且与 ExpressJS 相当。
|
||||
|
||||
### 框架和生态系统
|
||||
|
||||
说到框架:PHP 可不仅仅是 WordPress。让我告诉你 —— 某些专业的 PHP 开发人员:WordPress 绝不代表当代的 PHP 生态系统。
|
||||
|
||||
一般来说,有两个主要的 Web 应用程序框架,[Symfony][10] 和 [Laravel][11],以及一些较小的应用程序框架。当然还有 Zend、Yii、Cake、Code Igniter 等等,但是如果你想知道现代 PHP 开发是怎么样的,这两者之一都是很好的选择。
|
||||
|
||||
这两个框架都有一个庞大的包和产品的生态系统。从管理面板和 CRM 到独立软件包,从 CI 到分析器,以及几个 Web 套接字服务器、队列管理器、支付集成等众多服务。老实说,要列出的内容太多了。
|
||||
|
||||
这些框架虽然适用于实际开发。如果你只是需要个内容管理系统(CMS),WordPress 和 CraftCMS 等平台就够了。
|
||||
|
||||
衡量 PHP 生态系统当前状态的一种方法是查看 Packagist,这是 PHP 主要的软件包存储库。它现在呈指数级增长。每天下载量达到了 2500 万次,可以说 PHP 生态系统已不再是以前的小型弱势群体了。
|
||||
|
||||
请查看此图表,它列出一段时间内的软件包和版本数量变化。它也可以在 [Packagist 网站][12]上找到它。
|
||||
|
||||
![][13]
|
||||
|
||||
除了应用程序框架和 CMS 之外,我们还看到过去几年里异步框架的兴起。
|
||||
|
||||
这些是用 PHP 或其他语言编写的框架和服务器,允许用户运行真正的异步 PHP,这些例子包括 [Swoole][14](创始人韩天峰),以及 [Amp][15] 和 [ReactPHP][16]。
|
||||
|
||||
我们已经进入了异步的世界,像 Web 套接字和具有大量 I/O 的应用程序之类的东西在 PHP 世界中已经变得非常重要。
|
||||
|
||||
在内部邮件列表里(PHP 核心开发人员讨论语言开发的地方)已经谈到了[将 libuv 添加到核心][17]。如果你还不知道 libuv:Node.js 全有赖它提供异步性。
|
||||
|
||||
### 语言本身
|
||||
|
||||
虽然尚未提供 `async` 和 `await`,但在过去几年中,PHP 语言本身已经有了很多改进。这是 PHP 中新功能的非详尽列表:
|
||||
|
||||
|
||||
+ [短闭包](https://stitcher.io/blog/short-closures-in-php)(箭头函数)
|
||||
+ [Null 合并操作符](https://stitcher.io/blog/shorthand-comparisons-in-php#null-coalescing-operator)(`??`)
|
||||
+ [Trait](https://www.php.net/manual/en/language.oop5.traits.php)(一种代码重用方式)
|
||||
+ [属性类型](https://stitcher.io/blog/new-in-php-74#typed-properties-rfc)
|
||||
+ [展开操作符](https://wiki.php.net/rfc/argument_unpacking)(参数解包 `...`)
|
||||
+ [JIT 编译器](https://wiki.php.net/rfc/jit)(即时编译器)
|
||||
+ [FFI](https://wiki.php.net/rfc/ffi)(外部函数接口)
|
||||
+ [匿名类](https://www.php.net/manual/en/language.oop5.anonymous.php)
|
||||
+ [返回类型声明](https://www.php.net/manual/en/functions.returning-values.php#functions.returning-values.type-declaration)
|
||||
+ [现代化的加密支持](https://wiki.php.net/rfc/libsodium)
|
||||
+ [生成器](https://wiki.php.net/rfc/generators)
|
||||
+ [等等](https://www.php.net/ChangeLog-7.php)
|
||||
|
||||
当我们讨论语言功能时,我们还要谈谈当今该语言的发展过程。虽然社区可以提出 RFC,但是得有一个活跃的志愿者核心团队才能推着它前进。
|
||||
|
||||
接下来,这些 RFC 将在“内部”邮件列表中进行讨论,这个邮件列表也可以[在线阅读][18]。在添加新的语言特性之前,必须进行投票。只有得到了至少 2/3 多数同意的 RFC 才能进入核心。
|
||||
|
||||
可能有大约 100 人能够投票,但不需要每个人对每个 RFC 进行投票。核心团队的成员当然可以投票,他们是维护代码库的人。除了他们之外,还有一群人从 PHP 社区中被单独挑选出来。这些人包括 PHP 文档的维护者,对 PHP 项目整体有贡献的人,以及 PHP 社区中的杰出开发人员。
|
||||
|
||||
虽然大多数核心开发都是在自愿的基础上完成的,但其中一位核心 PHP 开发人员 Nikita Popov 最近受雇于 [JetBrains][19] 全职从事于 PHP 语言的开发。另一个例子是 Linux 基金会最近决定[投资 Zend 框架][20]。像这样的雇佣和收购确保了 PHP 未来发展的稳定性。
|
||||
|
||||
### 工具
|
||||
|
||||
除了核心本身,我们看到过去几年中围绕它的工具有所增加。首先浮现于我脑海中的是静态分析器,比如由 Vimeo 创建 [Psalm][21],以及 [Phan][22] 和 [PHPStan][23]。
|
||||
|
||||
这些工具将静态分析你的 PHP 代码并报告任何类型错误和可能的错误等。在某种程度上,它们提供的功能可以与 TypeScript 进行比较,但是现在这种语言不能<ruby>转译<rt>transpiling</rt></ruby>,因此不支持使用自定义语法。
|
||||
|
||||
尽管这意味着我们需要依赖 docblocks,但是 PHP 之父 Rasmus Lerdorf 确实提到了[添加静态分析引擎][24]到核心的想法。虽然会有很多潜力,但这是一项艰巨的任务。
|
||||
|
||||
说到转译,以及受到 JavaScript 社区的启发;他们已经努力在用户领域中扩展 PHP 语法。一个名为 [Pre][25] 的项目正是如此:允许将新的 PHP 语法转译为普通的 PHP 代码。
|
||||
|
||||
虽然这个思路已经在 JavaScript 世界中被证明了,但如果提供了适当的 IDE 和静态分析支持,它就能在 PHP 中工作了。这是一个非常有趣的想法,但必须发展起来才能称之为“主流”。
|
||||
|
||||
### 结语
|
||||
|
||||
尽管如此,你仍然可以将 PHP 视为一种糟糕的语言。虽然这种语言肯定有它的缺点和背负了 20 年的遗产;但我可以放胆地说,我喜欢用它工作。
|
||||
|
||||
根据我的经验,我能够创建可靠、可维护和高质量的软件。我工作的客户对最终结果感到满意,“俺也一样”。
|
||||
|
||||
尽管仍然可以用 PHP 做很多乱七八糟的事情,但我认为如果明智和正确地使用的话,它是 Web 开发的绝佳选择。
|
||||
|
||||
你不同意吗?让我知道为什么!你可以通过 [Twitter][2] 或 [电子邮件][26] 与我联系。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://stitcher.io/blog/php-in-2019
|
||||
|
||||
作者:[Brent][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://stitcher.io/blog/php-in-2019
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://stitcher.io/
|
||||
[2]: https://twitter.com/brendt_gd
|
||||
[3]: https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/
|
||||
[4]: https://github.com/brendt/aggregate.stitcher.io
|
||||
[5]: https://spatie.be/open-source/packages
|
||||
[6]: https://www.php.net/supported-versions.php
|
||||
[7]: https://www.phproundtable.com/episode/what-happened-to-php-6
|
||||
[8]: https://kinsta.com/blog/php-benchmarks/
|
||||
[9]: https://github.com/the-benchmarker/web-frameworks
|
||||
[10]: https://symfony.com/
|
||||
[11]: https://laravel.com/
|
||||
[12]: https://packagist.org/statistics
|
||||
[13]: https://stitcher.io/resources/img/blog/php-in-2019/packagist.png
|
||||
[14]: https://www.swoole.co.uk/
|
||||
[15]: https://amphp.org/
|
||||
[16]: https://reactphp.org/
|
||||
[17]: https://externals.io/message/102415#102415
|
||||
[18]: https://externals.io/
|
||||
[19]: https://blog.jetbrains.com/phpstorm/2019/01/nikita-popov-joins-phpstorm-team/
|
||||
[20]: https://getlaminas.org/
|
||||
[21]: https://github.com/vimeo/psalm
|
||||
[22]: https://github.com/phan/phan
|
||||
[23]: https://github.com/phpstan/phpstan
|
||||
[24]: https://externals.io/message/101477#101592
|
||||
[25]: https://preprocess.io/
|
||||
[26]: mailto:brendt@stitcher.io
|
@ -0,0 +1,120 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (tomjlw)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10888-1.html)
|
||||
[#]: subject: (How to SSH into a Raspberry Pi [Beginner’s Tip])
|
||||
[#]: via: (https://itsfoss.com/ssh-into-raspberry/)
|
||||
[#]: author: (Chinmay https://itsfoss.com/author/chinmay/)
|
||||
|
||||
新手教程:如何 SSH 进入树莓派
|
||||
======
|
||||
|
||||
> 在这篇树莓派文章中,你将学到如何在树莓派中启用 SSH 以及之后如何通过 SSH 进入树莓派。
|
||||
|
||||
在你可以用[树莓派][1]做的所有事情中,将其作为一个家庭网络的服务器是十分流行的做法。小体积与低功耗使它成为运行轻量级服务器的完美设备。
|
||||
|
||||
在这种情况下你做得到的事情之一是能够每次在树莓派上无须接上显示器、键盘、鼠标以及走到放置你的树莓派的地方就可以运行指令。
|
||||
|
||||
你可以从其它任意电脑、笔记本、台式机甚至你的手机通过 SSH([Secure Shell][2])登入你的树莓派来做到这一点。让我展示给你看:
|
||||
|
||||
### 如何 SSH 进入树莓派
|
||||
|
||||
![][3]
|
||||
|
||||
我假设你已经[在你的树莓派上运行 Raspbian][4] 并已经成功通过有线或者无线网连进网络了。你的树莓派接入网络这点是很重要的,否则你无法通过 SSH 连接树莓派(抱歉说出这种显而易见的事实)。
|
||||
|
||||
#### 步骤一:在树莓派上启用 SSH
|
||||
|
||||
SSH 在树莓派上是默认关闭的,因此在你安装好全新的 Raspbian 后打开树莓派时,你需要启用它。
|
||||
|
||||
首先通过菜单进入树莓派的配置界面。
|
||||
|
||||
![树莓派菜单,树莓派配置][5]
|
||||
|
||||
现在进入<ruby>接口<rt>interfaces</rt></ruby>标签,启动 SSH 并重启你的树莓派。
|
||||
|
||||
![在树莓派上启动 SSH][6]
|
||||
|
||||
你也可以通过终端直接启动 SSH。仅需输入命令 `sudo raspi-config` 然后进入高级设置以启用 SSH。
|
||||
|
||||
#### 步骤二: 找到树莓派的 IP 地址
|
||||
|
||||
在大多数情况下,你的树莓派会被分配一个看起来长得像 `192.168.x.x` 或者 `10.x.x.x` 的本地 IP 地址。你可以[使用多种 Linux 命令来找到 IP 地址][7]。
|
||||
|
||||
我在这使用古老而好用的 `ifconfig` 命令,但是你也可以使用 `ip address`。
|
||||
|
||||
```
|
||||
ifconfig
|
||||
```
|
||||
|
||||
![树莓派网络配置][9]
|
||||
|
||||
这行命令展现了所有活跃中的网络适配器以及其配置的列表。第一个条目(`eth0`)展示了例如`192.168.2.105` 的有效 IP 地址。我用有线网将我的树莓派连入网络,因此这里显示的是 `eth0`。如果你用无线网的话在叫做 `wlan0` 的条目下查看。
|
||||
|
||||
你也可以用其他方法例如查看你的路由器或者调制解调器的网络设备表以找到 IP 地址。
|
||||
|
||||
#### 步骤三:SSH 进你的树莓派
|
||||
|
||||
既然你已经启用了 SSH 功能并且找到了 IP 地址,你可以从任何电脑 SSH 进入你的树莓派。你同样需要树莓派的用户名和密码。
|
||||
|
||||
默认用户名和密码是:
|
||||
|
||||
* 用户名:`pi`
|
||||
* 密码:`raspberry`
|
||||
|
||||
如果你已改变了默认的密码,那就使用新的而不是以上的密码。理想状态下你必须改变默认的密码。在过去,有一款[恶意软件感染数千使用默认用户名和密码的树莓派设备][8]。
|
||||
|
||||
(在 Mac 或 Linux 上)从你想要 SSH 进树莓派的电脑上打开终端输入以下命令,在 Windows 上,你可以用类似 [Putty][10] 的 SSH 客户端。
|
||||
|
||||
这里,使用你在之前步骤中找到的 IP 地址。
|
||||
|
||||
```
|
||||
ssh [受保护的邮件]
|
||||
```
|
||||
|
||||
> 注意: 确保你的树莓派和你用来 SSH 进入树莓派的电脑接入了同一个网络。
|
||||
|
||||
![通过命令行 SSH][11]
|
||||
|
||||
第一次你会看到一个警告,输入 `yes` 并按下回车。
|
||||
|
||||
![输入密码 \(默认是 ‘raspberry‘\)][12]
|
||||
|
||||
现在,输入密码按下回车。
|
||||
|
||||
![成功通过 SSH 登入][13]
|
||||
|
||||
成功登入你将会看到树莓派的终端。现在你可以通过这个终端无需物理上访问你的树莓派就可以远程(在当前网络内)在它上面运行指令。
|
||||
|
||||
在此之上你也可以设置 SSH 密钥这样每次通过 SSH 登入时就可以无需输入密码,但那完全是另一个话题了。
|
||||
|
||||
我希望你通过跟着这个教程已能够 SSH 进入你的树莓派。在下方评论中让我知道你打算用你的树莓派做些什么!
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/ssh-into-raspberry/
|
||||
|
||||
作者:[Chinmay][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[tomjlw](https://github.com/tomjlw)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/chinmay/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.raspberrypi.org/
|
||||
[2]: https://en.wikipedia.org/wiki/Secure_Shell
|
||||
[3]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/05/ssh-into-raspberry-pi.png?resize=800%2C450&ssl=1
|
||||
[4]: https://itsfoss.com/tutorial-how-to-install-raspberry-pi-os-raspbian-wheezy/
|
||||
[5]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/Raspberry-pi-configuration.png?ssl=1
|
||||
[6]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/enable-ssh-raspberry-pi.png?ssl=1
|
||||
[7]: https://linuxhandbook.com/find-ip-address/
|
||||
[8]: https://itsfoss.com/raspberry-pi-malware-threat/
|
||||
[9]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/ifconfig-rapberry-pi.png?ssl=1
|
||||
[10]: https://itsfoss.com/putty-linux/
|
||||
[11]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/SSH-into-pi-warning.png?fit=800%2C199&ssl=1
|
||||
[12]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/SSH-into-pi-password.png?fit=800%2C202&ssl=1
|
||||
[13]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/SSH-into-Pi-successful-login.png?fit=800%2C306&ssl=1
|
||||
[14]: https://itsfoss.com/speed-up-ubuntu-unity-on-low-end-system/
|
117
published/201905/20190516 Building Smaller Container Images.md
Normal file
117
published/201905/20190516 Building Smaller Container Images.md
Normal file
@ -0,0 +1,117 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10885-1.html)
|
||||
[#]: subject: (Building Smaller Container Images)
|
||||
[#]: via: (https://fedoramagazine.org/building-smaller-container-images/)
|
||||
[#]: author: (Muayyad Alsadi https://fedoramagazine.org/author/alsadi/)
|
||||
|
||||
构建更小的容器镜像的技巧
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
Linux 容器已经成为一个热门话题,保证容器镜像较小被认为是一个好习惯。本文提供了有关如何构建较小 Fedora 容器镜像的一些技巧。
|
||||
|
||||
### microdnf
|
||||
|
||||
Fedora 的 DNF 是用 Python 编写的,因为它有各种各样的插件,因此它的设计是可扩展的。但是 有一个 Fedora 基本容器镜像替代品,它使用一个较小的名为 [microdnf][2] 的包管理器,使用 C 编写。要在 Dockerfile 中使用这个最小的镜像,`FROM` 行应该如下所示:
|
||||
|
||||
```
|
||||
FROM registry.fedoraproject.org/fedora-minimal:30
|
||||
```
|
||||
|
||||
如果你的镜像不需要像 Python 这样的典型 DNF 依赖项,例如,如果你在制作 NodeJS 镜像时,那么这是一个重要的节省项。
|
||||
|
||||
### 在一个层中安装和清理
|
||||
|
||||
为了节省空间,使用 `dnf clean all` 或其 microdnf 等效的 `microdnf clean all` 删除仓库元数据非常重要。但是你不应该分两步执行此操作,因为这实际上会将这些文件保存在容器镜像中,然后在另一层中将其标记为删除。要正确地执行此操作,你应该像这样一步完成安装和清理:
|
||||
|
||||
```
|
||||
FROM registry.fedoraproject.org/fedora-minimal:30
|
||||
RUN microdnf install nodejs && microdnf clean all
|
||||
```
|
||||
|
||||
### 使用 microdnf 进行模块化
|
||||
|
||||
模块化是一种给你选择不同堆栈版本的方法。例如,你可能需要在项目中用非 LTS 的 NodeJS v11,旧的 LTS NodeJS v8 用于另一个,最新的 LTS NodeJS v10 用于另一个。你可以使用冒号指定流。
|
||||
|
||||
```
|
||||
# dnf module list
|
||||
# dnf module install nodejs:8
|
||||
```
|
||||
|
||||
`dnf module install` 命令意味着两个命令,一个启用流,另一个是从它安装 nodejs。
|
||||
|
||||
```
|
||||
# dnf module enable nodejs:8
|
||||
# dnf install nodejs
|
||||
```
|
||||
|
||||
尽管 `microdnf` 不提供与模块化相关的任何命令,但是可以启用带有配置文件的模块,并且 libdnf(被 microdnf 使用)[似乎][3]支持模块化流。该文件看起来像这样:
|
||||
|
||||
```
|
||||
/etc/dnf/modules.d/nodejs.module
|
||||
[nodejs]
|
||||
name=nodejs
|
||||
stream=8
|
||||
profiles=
|
||||
state=enabled
|
||||
```
|
||||
|
||||
使用模块化的 `microdnf` 的完整 Dockerfile 如下所示:
|
||||
|
||||
```
|
||||
FROM registry.fedoraproject.org/fedora-minimal:30
|
||||
RUN \
|
||||
echo -e "[nodejs]\nname=nodejs\nstream=8\nprofiles=\nstate=enabled\n" > /etc/dnf/modules.d/nodejs.module && \
|
||||
microdnf install nodejs zopfli findutils busybox && \
|
||||
microdnf clean all
|
||||
```
|
||||
|
||||
### 多阶段构建
|
||||
|
||||
在许多情况下,你可能需要大量的无需用于运行软件的构建时依赖项,例如构建一个静态链接依赖项的 Go 二进制文件。多阶段构建是分离应用构建和应用运行时的有效方法。
|
||||
|
||||
例如,下面的 Dockerfile 构建了一个 Go 应用 [confd][4]。
|
||||
|
||||
```
|
||||
# building container
|
||||
FROM registry.fedoraproject.org/fedora-minimal AS build
|
||||
RUN mkdir /go && microdnf install golang && microdnf clean all
|
||||
WORKDIR /go
|
||||
RUN export GOPATH=/go; CGO_ENABLED=0 go get github.com/kelseyhightower/confd
|
||||
|
||||
FROM registry.fedoraproject.org/fedora-minimal
|
||||
WORKDIR /
|
||||
COPY --from=build /go/bin/confd /usr/local/bin
|
||||
CMD ["confd"]
|
||||
```
|
||||
|
||||
通过在 `FROM` 指令之后添加 `AS` 并从基本容器镜像中添加另一个 `FROM` 然后使用 `COPY --from=` 指令将内容从*构建*的容器复制到第二个容器来完成多阶段构建。
|
||||
|
||||
可以使用 `podman` 构建并运行此 Dockerfile:
|
||||
|
||||
```
|
||||
$ podman build -t myconfd .
|
||||
$ podman run -it myconfd
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/building-smaller-container-images/
|
||||
|
||||
作者:[Muayyad Alsadi][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/alsadi/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2019/05/smaller-container-images-816x345.jpg
|
||||
[2]: https://github.com/rpm-software-management/microdnf
|
||||
[3]: https://bugzilla.redhat.com/show_bug.cgi?id=1575626
|
||||
[4]: https://github.com/kelseyhightower/confd
|
@ -0,0 +1,156 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10906-1.html)
|
||||
[#]: subject: (Querying 10 years of GitHub data with GHTorrent and Libraries.io)
|
||||
[#]: via: (https://opensource.com/article/19/5/chaossearch-github-ghtorrent)
|
||||
[#]: author: (Pete Cheslock https://opensource.com/users/petecheslock/users/ghaff/users/payalsingh/users/davidmstokes)
|
||||
|
||||
用 GHTorrent 和 Libraries.io 查询 10 年的 GitHub 数据
|
||||
======
|
||||
|
||||
> 有一种方法可以在没有任何本地基础设施的情况下使用开源数据集探索 GitHub 数据。
|
||||
|
||||
![magnifying glass on computer screen](https://img.linux.net.cn/data/attachment/album/201905/27/220200jlzrlz333vkfl8ok.jpg)
|
||||
|
||||
我一直在寻找新的数据集,以用它们来展示我们团队工作的力量。[CHAOSSEARCH][2] 可以将你的 [Amazon S3][3] 对象存储数据转换为完全可搜索的 [Elasticsearch][4] 式集群。使用 Elasticsearch API 或 [Kibana][5] 等工具,你可以查询你所要找的任何数据。
|
||||
|
||||
当我找到 [GHTorrent][6] 项目进行探索时,我很兴奋。GHTorrent 旨在通过 GitHub API 构建所有可用数据的离线版本。如果你喜欢数据集,这是一个值得一看的项目,甚至你可以考虑[捐赠一个 GitHub API 密钥][7]。
|
||||
|
||||
### 访问 GHTorrent 数据
|
||||
|
||||
有许多方法可以访问和使用 [GHTorrent 的数据][8],它以 [NDJSON][9] 格式提供。这个项目可以以多种形式提供数据,包括用于恢复到 [MySQL][11] 数据库的 [CSV][10],可以转储所有对象的 [MongoDB][12],以及用于将数据直接导出到 Google 对象存储中的 Google Big Query(免费)。 有一点需要注意:这个数据集有从 2008 年到 2017 年几乎完整的数据集,但从 2017 年到现在的数据还不完整。这将影响我们确定性查询的能力,但它仍然是一个令人兴奋的信息量。
|
||||
|
||||
我选择 Google Big Query 来避免自己运行任何数据库,那么我就可以很快下载包括用户和项目在内的完整数据库。CHAOSSEARCH 可以原生分析 NDJSON 格式,因此在将数据上传到 Amazon S3 之后,我能够在几分钟内对其进行索引。CHAOSSEARCH 平台不要求用户设置索引模式或定义其数据的映射,它可以发现所有字段本身(字符串、整数等)。
|
||||
|
||||
随着我的数据完全索引并准备好进行搜索和聚合,我想深入了解看看我们可以发现什么,比如哪些软件语言是 GitHub 项目最受欢迎的。
|
||||
|
||||
(关于格式化的说明:下面这是一个有效的 JSON 查询,我们不会在这里正确格式化以避免滚动疲劳。要正确格式化它,你可以在本地复制它并发送到命令行实用程序,如 [jq][13]。)
|
||||
|
||||
```
|
||||
{"aggs":{"2":{"date_histogram":{"field":"root.created_at","interval":"1M","time_zone":"America/New_York","min_doc_count":1}}},"size":0,"_source":{"excludes":[]},"stored_fields":["*"],"script_fields":{},"docvalue_fields":["root.created_at","root.updated_at"],"query":{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[{"match_phrase":{"root.language":{"query":""}}}]}}}
|
||||
```
|
||||
|
||||
对于那些近年来跟踪开源语言状态的人来说,这个结果并不令人惊讶。
|
||||
|
||||
![Which software languages are the most popular on GitHub.][14]
|
||||
|
||||
[JavaScript][15] 仍然是卫冕冠军,虽然有些人认为 JavaScript 正在逐渐消失,但它仍然是 800 磅重的大猩猩,很可能会保持这种状态一段时间。[Java][16] 面临类似的谣言,但这些数据表明它是开源生态系统的重要组成部分。
|
||||
|
||||
考虑到像 [Docker][17] 和 [Kubernetes][18] 这样的项目的流行,你可能会想,“Go([Golang][19])怎么样?”这是一个提醒的好时机,这里讨论的 GitHub 数据集包含一些空缺,最明显的是在 2017 年之后我看到 Golang 项目随处可见,而这里并没有显示。我希望用完整的 GitHub 数据集重复此搜索,看看它是否会改变排名。
|
||||
|
||||
现在让我们来探讨项目创建的速度。 (提醒:这是为了便于阅读而合并的有效 JSON。)
|
||||
|
||||
```
|
||||
{"aggs":{"2":{"date_histogram":{"field":"root.created_at","interval":"1M","time_zone":"America/New_York","min_doc_count":1}}},"size":0,"_source":{"excludes":[]},"stored_fields":["*"],"script_fields":{},"docvalue_fields":["root.created_at","root.updated_at"],"query":{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[{"match_phrase":{"root.language":{"query":""}}}]}}}
|
||||
```
|
||||
|
||||
我们可以看到创建新项目的速度,也会给人留下深刻的印象,从 2012 年左右开始大幅增长:
|
||||
|
||||
![The rate at which new projects are created on GitHub.][20]
|
||||
|
||||
既然我知道了创建项目的速度以及用于创建这些项目的最流行的语言,我还想知道这些项目选择的开源许可证。遗憾的是,这个 GitHub 项目数据集中并不存在这些数据,但是 [Tidelift][21] 的精彩团队在 [Libraries.io][22] [数据][23] 里发布了一个 GitHub 项目的详细列表,包括使用的许可证以及其中有关开源软件状态的其他详细信息。将此数据集导入 CHAOSSEARCH 只花了几分钟,让我看看哪些开源软件许可证在 GitHub 上最受欢迎:
|
||||
|
||||
(提醒:这是为了便于阅读而合并的有效 JSON。)
|
||||
|
||||
```
|
||||
{"aggs":{"2":{"terms":{"field":"Repository License","size":10,"order":{"_count":"desc"}}}},"size":0,"_source":{"excludes":[]},"stored_fields":["*"],"script_fields":{},"docvalue_fields":["Created Timestamp","Last synced Timestamp","Latest Release Publish Timestamp","Updated Timestamp"],"query":{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[{"match_phrase":{"Repository License":{"query":""}}}]}}}
|
||||
```
|
||||
|
||||
结果显示了一些重要的异常值:
|
||||
|
||||
![Which open source software licenses are the most popular on GitHub.][24]
|
||||
|
||||
如你所见,[MIT 许可证][25] 和 [Apache 2.0 许可证][26] 的开源项目远远超过了其他大多数开源许可证,而 [各种 BSD 和 GPL 许可证][27] 则差得很远。鉴于 GitHub 的开放模式,我不能说我对这些结果感到惊讶。我猜想是用户(而不是公司)创建了大多数项目,并且他们使用 MIT 许可证可以使其他人轻松地使用、共享和贡献。而鉴于有不少公司希望确保其商标得到尊重并为其业务提供开源组件,那么 Apache 2.0 许可证数量高企的背后也是有道理的。
|
||||
|
||||
现在我确定了最受欢迎的许可证,我很想看看最少使用的许可证。通过调整我的上一个查询,我将前 10 名逆转为最后 10 名,并且只找到了两个使用 [伊利诺伊大学 - NCSA 开源许可证][28] 的项目。我之前从未听说过这个许可证,但它与 Apache 2.0 非常接近。看到所有 GitHub 项目中使用了多少个不同的软件许可证,这很有意思。
|
||||
|
||||
![The University of Illinois/NCSA open source license.][29]
|
||||
|
||||
之后,我针对特定语言(JavaScript)来查看最常用的许可证。(提醒:这是为了便于阅读而合并的有效JSON。)
|
||||
|
||||
```
|
||||
{"aggs":{"2":{"terms":{"field":"Repository License","size":10,"order":{"_count":"desc"}}}},"size":0,"_source":{"excludes":[]},"stored_fields":["*"],"script_fields":{},"docvalue_fields":["Created Timestamp","Last synced Timestamp","Latest Release Publish Timestamp","Updated Timestamp"],"query":{"bool":{"must":[{"match_phrase":{"Repository Language":{"query":"JavaScript"}}}],"filter":[{"match_all":{}}],"should":[],"must_not":[{"match_phrase":{"Repository License":{"query":""}}}]}}}
|
||||
```
|
||||
|
||||
这个输出有一些意外。
|
||||
|
||||
![The most popular open source licenses used for GitHub JavaScript projects.][30]
|
||||
|
||||
尽管使用 `npm init` 创建的 [NPM][31] 模块的默认许可证是来自 [Internet Systems Consortium(ISC)][32] 的许可证,但你可以看到相当多的这些项目使用 MIT 以及 Apache 2.0 的开源许可证。
|
||||
|
||||
由于 Libraries.io 数据集中包含丰富的开源项目内容,并且由于 GHTorrent 数据缺少最近几年的数据(因此缺少有关 Golang 项目的任何细节),因此我决定运行类似的查询来查看 Golang 项目是如何许可他们的代码的。
|
||||
|
||||
(提醒:这是为了便于阅读而合并的有效 JSON。)
|
||||
|
||||
```
|
||||
{"aggs":{"2":{"terms":{"field":"Repository License","size":10,"order":{"_count":"desc"}}}},"size":0,"_source":{"excludes":[]},"stored_fields":["*"],"script_fields":{},"docvalue_fields":["Created Timestamp","Last synced Timestamp","Latest Release Publish Timestamp","Updated Timestamp"],"query":{"bool":{"must":[{"match_phrase":{"Repository Language":{"query":"Go"}}}],"filter":[{"match_all":{}}],"should":[],"must_not":[{"match_phrase":{"Repository License":{"query":""}}}]}}}
|
||||
```
|
||||
|
||||
结果与 Javascript 完全不同。
|
||||
|
||||
![How Golang projects license their GitHub code.][33]
|
||||
|
||||
Golang 项目与 JavaScript 项目惊人逆转 —— 使用 Apache 2.0 的 Golang 项目几乎是 MIT 许可证的三倍。虽然很难准确地解释为什么会出现这种情况,但在过去的几年中,Golang 已经出现了大规模的增长,特别是在开源和商业化的项目和软件产品公司中。
|
||||
|
||||
正如我们上面所了解的,这些公司中的许多公司都希望强制执行其商标策略,因此转向 Apache 2.0 许可证是有道理的。
|
||||
|
||||
#### 总结
|
||||
|
||||
最后,我通过深入了解 GitHub 用户和项目的数据找到了一些有趣的结果。其中一些我肯定会猜到,但是一些结果对我来说也是惊喜,特别是像很少使用的 NCSA 许可证这样的异常值。
|
||||
|
||||
总而言之,你可以看到 CHAOSSEARCH 平台能够快速轻松地找到有趣问题的复杂答案。我无需自己运行任何数据库就可以深入研究这个数据集,甚至可以在 Amazon S3 上以低成本的方式存储数据,因此无需维护。 现在,我可以随时查询有关这些数据的任何其他问题。
|
||||
|
||||
你对数据提出了哪些其他问题,以及你使用了哪些数据集?请在评论或推特上告诉我 [@petecheslock] [34]。
|
||||
|
||||
本文的一个版本最初发布在 [CHAOSSEARCH][35],有更多结果可供发现。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/chaossearch-github-ghtorrent
|
||||
|
||||
作者:[Pete Cheslock][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/petecheslock/users/ghaff/users/payalsingh/users/davidmstokes
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/search_find_code_issue_bug_programming.png?itok=XPrh7fa0 (magnifying glass on computer screen)
|
||||
[2]: https://chaossearch.io/
|
||||
[3]: https://aws.amazon.com/s3/
|
||||
[4]: https://www.elastic.co/
|
||||
[5]: https://www.elastic.co/products/kibana
|
||||
[6]: http://ghtorrent.org
|
||||
[7]: http://ghtorrent.org/services.html
|
||||
[8]: http://ghtorrent.org/downloads.html
|
||||
[9]: http://ndjson.org
|
||||
[10]: https://en.wikipedia.org/wiki/Comma-separated_values
|
||||
[11]: https://en.wikipedia.org/wiki/MySQL
|
||||
[12]: https://www.mongodb.com/
|
||||
[13]: https://stedolan.github.io/jq/
|
||||
[14]: https://opensource.com/sites/default/files/uploads/github-1_500.png (Which software languages are the most popular on GitHub.)
|
||||
[15]: https://en.wikipedia.org/wiki/JavaScript
|
||||
[16]: /resources/java
|
||||
[17]: /resources/what-docker
|
||||
[18]: /resources/what-is-kubernetes
|
||||
[19]: https://golang.org/
|
||||
[20]: https://opensource.com/sites/default/files/uploads/github-2_500.png (The rate at which new projects are created on GitHub.)
|
||||
[21]: https://tidelift.com
|
||||
[22]: http://libraries.io/
|
||||
[23]: https://libraries.io/data
|
||||
[24]: https://opensource.com/sites/default/files/uploads/github-3_500.png (Which open source software licenses are the most popular on GitHub.)
|
||||
[25]: https://opensource.org/licenses/MIT
|
||||
[26]: https://opensource.org/licenses/Apache-2.0
|
||||
[27]: https://opensource.org/licenses
|
||||
[28]: https://tldrlegal.com/license/university-of-illinois---ncsa-open-source-license-(ncsa)
|
||||
[29]: https://opensource.com/sites/default/files/uploads/github-4_500_0.png (The University of Illinois/NCSA open source license.)
|
||||
[30]: https://opensource.com/sites/default/files/uploads/github-5_500_0.png (The most popular open source licenses used for GitHub JavaScript projects.)
|
||||
[31]: https://www.npmjs.com/
|
||||
[32]: https://en.wikipedia.org/wiki/ISC_license
|
||||
[33]: https://opensource.com/sites/default/files/uploads/github-6_500.png (How Golang projects license their GitHub code.)
|
||||
[34]: https://twitter.com/petecheslock
|
||||
[35]: https://chaossearch.io/blog/where-are-the-github-users-part-1/
|
@ -0,0 +1,102 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (zhs852)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10897-1.html)
|
||||
[#]: subject: (Change Power Modes in Ubuntu with Slimbook Battery Optimizer)
|
||||
[#]: via: (https://itsfoss.com/slimbook-battry-optimizer-ubuntu/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
|
||||
在 Ubuntu 中使用 Slimbook Battery Optimizer 切换电源模式
|
||||
======
|
||||
|
||||
> Slimbook Battery Optimizer 是一个美观实用的指示器小程序,它可以让你在安装了 Linux 的笔记本上快速切换电源模式来延长续航时间。
|
||||
|
||||
[Slimbook][1] 是一个销售 [预装 Linux 的笔记本电脑][2] 的西班牙电脑制造商,他们发布了一款好用的小程序,用来在基于 Ubuntu 的 Linux 发行版下调整电池性能。
|
||||
|
||||
因为 Slimbook 销售他们自己的 Linux 系统,所以他们制作了一些在 Linux 上用于调整他们自己硬件性能的小工具。Battery Optimizer 就是这样一个工具。
|
||||
|
||||
要使用这个实用小程序,你不必购买 Slimbook 的产品,因为 Slimbook 已经将它在 [他们的官方 PPA 源][3] 发行了。
|
||||
|
||||
### Slimbook Battery Optimizer 简介
|
||||
|
||||
这个程序叫 Slimbook Battery。它是一个常驻顶栏的指示器小程序,使得你可以快速切换电源模式。
|
||||
|
||||
![Slimbook Battery Mode Ubuntu][4]
|
||||
|
||||
你可能在 Windows 中见过类似的程序。Slimbook Battery 和它们一样,提供了类似的电源计划:
|
||||
|
||||
* 节能:最大程度延长电池续航时间
|
||||
* 平衡:性能与节能间的最佳平衡
|
||||
* 高性能:最大程度提高性能
|
||||
|
||||
你可以在高级模式中配置这些模式:
|
||||
|
||||
![配置多种多样的电源模式][5]
|
||||
|
||||
如果你觉得你把设置调乱了,你可以用“恢复默认设置”的按钮还原它。
|
||||
|
||||
你也可以修改像程序自启或默认电源模式这样的通用设置。
|
||||
|
||||
![Slimbook Battery 通用设置][6]
|
||||
|
||||
Slimbook 有专门为多种电源管理参数提供的页面。如果你希望自己配置,请参照 [此页][7]。
|
||||
|
||||
不过,我认为 Slimbook 的界面需要一些改进。例如,某些页面上的“问题标记”的图标应该改为可点击的,以此提供更多信息。然而,在我写这篇文章时,那个标记仍然无法点击。
|
||||
|
||||
总的来说,Slimbook Battery 是一个小巧精美的软件,你可以用它来快速切换电源模式。如果你决定在 Ubuntu 及其衍生发行版上(比如 Linux Mint 或 elementary OS 等),你可以使用官方 [PPA 源][8]。
|
||||
|
||||
#### 在基于 Ubuntu 的发行版上安装 Slimbook Battery
|
||||
|
||||
打开终端,一步一步地使用以下命令:
|
||||
|
||||
```
|
||||
sudo add-apt-repository ppa:slimbook/slimbook
|
||||
sudo apt update
|
||||
sudo apt install slimbookbattery
|
||||
```
|
||||
|
||||
安装好之后,在菜单中搜索 Slimbook Battery:
|
||||
|
||||
![启动 Slimbook Battery Optimizer][10]
|
||||
|
||||
在你点击它之后,你会发现它出现在了顶栏。你可以在这里选择你希望使用的电源模式。
|
||||
|
||||
![Slimbook Battery 电源模式][4]
|
||||
|
||||
#### 卸载 Slimbook Battery
|
||||
|
||||
如果你不再使用它,你可以通过以下命令来卸载它:
|
||||
|
||||
```
|
||||
sudo apt remove slimbookbattery
|
||||
sudo add-apt-repository -r ppa:slimbook/slimbook
|
||||
```
|
||||
|
||||
在我看来,这样的应用程序为某些特定的目的服务,这是值得鼓励的。这个工具给了你一条调整电源模式的捷径,和调整性能的更多选项。
|
||||
|
||||
你用过 Slimbook Battery 吗?你觉得它如何?
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/slimbook-battry-optimizer-ubuntu/
|
||||
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[zhs852](https://github.com/zhs852)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/abhishek/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://slimbook.es/en/
|
||||
[2]: https://itsfoss.com/get-linux-laptops/
|
||||
[3]: https://launchpad.net/~slimbook/+archive/ubuntu/slimbook
|
||||
[4]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/slimbook-battery-mode-ubuntu.jpg?resize=800%2C400&ssl=1
|
||||
[5]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/slimbook-battery-optimizer-2.jpg?ssl=1
|
||||
[6]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/slimbook-battery-optimizer-1.jpg?ssl=1
|
||||
[7]: https://slimbook.es/en/tutoriales/aplicaciones-slimbook/398-slimbook-battery-3-application-for-optimize-battery-of-your-laptop
|
||||
[8]: https://itsfoss.com/ppa-guide/
|
||||
[9]: https://itsfoss.com/ubuntu-forums-hacked-again/
|
||||
[10]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/slimbook-battery-optimizer.jpg?ssl=1
|
@ -0,0 +1,118 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10894-1.html)
|
||||
[#]: subject: (PiShrink – Make Raspberry Pi Images Smaller)
|
||||
[#]: via: (https://www.ostechnix.com/pishrink-make-raspberry-pi-images-smaller/)
|
||||
[#]: author: (sk https://www.ostechnix.com/author/sk/)
|
||||
|
||||
PiShrink:使树莓派镜像更小
|
||||
======
|
||||
|
||||
![Make Raspberry Pi Images Smaller With PiShrink In Linux][1]
|
||||
|
||||
树莓派不需要过多介绍。它是一款小巧、价格实惠,只有信用卡大小的电脑,它可以连接到显示器或电视。我们可以连接一个标准的键盘和鼠标,并将其用作一台成熟的台式计算机来完成日常任务,如互联网浏览、播放视频/玩游戏、文字处理和电子表格制作等。它主要是为学校的计算机科学教学而开发的。如今,树莓派被广泛用于大学、中小型组织和研究所来教授编码。
|
||||
|
||||
如果你有一台树莓派,你可能需要了解一个名为 PiShrink 的 bash 脚本,该脚本可使树莓派镜像更小。PiShrink 将自动缩小镜像,然后在启动时将其调整为 SD 卡的最大大小。这能更快地将镜像复制到 SD 卡中,同时缩小的镜像将更好地压缩。这对于将大容量镜像放入 SD 卡非常有用。在这个简短的指南中,我们将学习如何在类 Unix 系统中将树莓派镜像缩小到更小。
|
||||
|
||||
### 安装 PiShrink
|
||||
|
||||
要在 Linux 机器上安装 PiShrink,请先使用以下命令下载最新版本:
|
||||
|
||||
```
|
||||
$ wget https://raw.githubusercontent.com/Drewsif/PiShrink/master/pishrink.sh
|
||||
```
|
||||
|
||||
接下来,将下载的 PiShrink 变成二进制可执行文件:
|
||||
|
||||
```
|
||||
$ chmod +x pishrink.sh
|
||||
```
|
||||
|
||||
最后,移动到目录:
|
||||
|
||||
```
|
||||
$ sudo mv pishrink.sh /usr/local/bin/
|
||||
```
|
||||
|
||||
### 使树莓派镜像更小
|
||||
|
||||
你可能已经知道,Raspbian 是所有树莓派型号的官方操作系统。树莓派基金会为 PC 和 Mac 开发了树莓派桌面版本。你可以创建一个 live CD,并在虚拟机中运行它,甚至也可以将其安装在桌面上。树莓派也有少量非官方操作系统镜像。为了测试,我从[官方下载页面][2]下载了官方的 Raspbian 系统。
|
||||
|
||||
解压下载的系统镜像:
|
||||
|
||||
```
|
||||
$ unzip 2019-04-08-raspbian-stretch-lite.zip
|
||||
```
|
||||
|
||||
上面的命令将提取当前目录中 `2019-04-08-raspbian-stretch-lite.zip` 文件的内容。
|
||||
|
||||
让我们看下提取文件的实际大小:
|
||||
|
||||
```
|
||||
$ du -h 2019-04-08-raspbian-stretch-lite.img
|
||||
1.7G 2019-04-08-raspbian-stretch-lite.img
|
||||
```
|
||||
|
||||
如你所见,提取的树莓派系统镜像大小为 1.7G。
|
||||
|
||||
现在,使用 PiShrink 缩小此文件的大小,如下所示:
|
||||
|
||||
```
|
||||
$ sudo pishrink.sh 2019-04-08-raspbian-stretch-lite.img
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
```
|
||||
Creating new /etc/rc.local
|
||||
rootfs: 39795/107072 files (0.1% non-contiguous), 239386/428032 blocks
|
||||
resize2fs 1.45.0 (6-Mar-2019)
|
||||
resize2fs 1.45.0 (6-Mar-2019)
|
||||
Resizing the filesystem on /dev/loop1 to 280763 (4k) blocks.
|
||||
Begin pass 3 (max = 14)
|
||||
Scanning inode table XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
Begin pass 4 (max = 3728)
|
||||
Updating inode references XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
The filesystem on /dev/loop1 is now 280763 (4k) blocks long.
|
||||
|
||||
Shrunk 2019-04-08-raspbian-stretch-lite.img from 1.7G to 1.2G
|
||||
```
|
||||
|
||||
正如你在上面的输出中看到的,树莓派镜像的大小已减少到 1.2G。
|
||||
|
||||
你还可以使用 `-s` 标志跳过该过程的自动扩展部分。
|
||||
|
||||
```
|
||||
$ sudo pishrink.sh -s 2019-04-08-raspbian-stretch-lite.img newpi.img
|
||||
```
|
||||
|
||||
这将创建一个源镜像文件(即 `2019-04-08-raspbian-stretch-lite.img`)的副本到一个新镜像文件(`newpi.img`)并进行处理。有关更多详细信息,请查看最后给出的官方 GitHub 页面。
|
||||
|
||||
就是这些了。希望本文有用。还有更多好东西,敬请期待!
|
||||
|
||||
|
||||
资源:
|
||||
|
||||
* [PiShrink 的 GitHub 仓库][4]
|
||||
* [树莓派网站][5]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/pishrink-make-raspberry-pi-images-smaller/
|
||||
|
||||
作者:[sk][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/sk/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ostechnix.com/wp-content/uploads/2019/05/pishrink-720x340.png
|
||||
[2]: https://www.raspberrypi.org/downloads/
|
||||
[3]: http://www.ostechnix.com/wp-content/uploads/2019/05/pishrink-1.png
|
||||
[4]: https://github.com/Drewsif/PiShrink
|
||||
[5]: https://www.raspberrypi.org/
|
@ -0,0 +1,396 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (wxy)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10889-1.html)
|
||||
[#]: subject: (xsos – A Tool To Read SOSReport In Linux)
|
||||
[#]: via: (https://www.2daygeek.com/xsos-a-tool-to-read-sosreport-in-linux/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
xsos:一个在 Linux 上阅读 SOSReport 的工具
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201905/23/133305accwpsvhk1epsisc.jpg)
|
||||
|
||||
我们都已经知道 [SOSReport][1]。它用来收集可用于诊断的系统信息。Redhat 的支持服务建议我们在提交案例时提供 SOSReport 来分析当前的系统状态。
|
||||
|
||||
它会收集全部类型的报告,以帮助用户找出问题的根本原因。我们可以轻松地提取和阅读 SOSReport,但它很难阅读。因为它的每个部分都是一个单独的文件。
|
||||
|
||||
那么,在 Linux 中使用语法高亮显示阅读所有这些内容的最佳方法是什么。是的,这可以通过 `xsos` 工具做到。
|
||||
|
||||
### sosreport
|
||||
|
||||
`sosreport` 命令是一个从运行中的系统(尤其是 RHEL 和 OEL 系统)收集大量配置细节、系统信息和诊断信息的工具。它可以帮助技术支持工程师在很多方面分析系统。
|
||||
|
||||
此报告包含有关系统的大量信息,例如引导信息、文件系统、内存、主机名、已安装的 RPM、系统 IP、网络详细信息、操作系统版本、已安装的内核、已加载的内核模块、打开的文件列表、PCI 设备列表、挂载点及其细节、运行中的进程信息、进程树输出、系统路由、位于 `/etc` 文件夹中的所有配置文件,以及位于 `/var` 文件夹中的所有日志文件。
|
||||
|
||||
这将需要一段时间来生成报告,这取决于你的系统安装和配置。
|
||||
|
||||
完成后,`sosreport` 将在 `/tmp` 目录下生成一个压缩的归档文件。
|
||||
|
||||
### xsos
|
||||
|
||||
[xsos][3] 是一个帮助用户轻松读取 Linux 系统上的 `sosreport` 的工具。另一方面,我们可以说它是 `sosreport` 考官。
|
||||
|
||||
它可以立即从 `sosreport` 或正在运行的系统中汇总系统信息。
|
||||
|
||||
`xsos` 将尝试简化、解析、计算并格式化来自数十个文件(和命令)的数据,以便为你提供有关系统的详细概述。
|
||||
|
||||
你可以通过运行以下命令立即汇总系统信息。
|
||||
|
||||
```
|
||||
# curl -Lo ./xsos bit.ly/xsos-direct; chmod +x ./xsos; ./xsos -ya
|
||||
```
|
||||
|
||||
![][5]
|
||||
|
||||
### 如何在 Linux 上安装 xsos
|
||||
|
||||
我们可以使用以下两种方法轻松安装 `xsos`。
|
||||
|
||||
如果你正在寻找最新的前沿版本。使用以下步骤:
|
||||
|
||||
```
|
||||
# curl -Lo /usr/local/bin/xsos bit.ly/xsos-direct
|
||||
# chmod +x /usr/local/bin/xsos
|
||||
```
|
||||
|
||||
下面是安装 `xsos` 的推荐方法。它将从 rpm 文件安装 `xsos`。
|
||||
|
||||
```
|
||||
# yum install http://people.redhat.com/rsawhill/rpms/latest-rsawaroha-release.rpm
|
||||
# yum install xsos
|
||||
```
|
||||
|
||||
### 如何在 Linux 上使用 xsos
|
||||
|
||||
一旦通过上述方法之一安装了 xsos。只需运行 `xsos` 命令,不带任何选项,它们会显示有关系统的基本信息。
|
||||
|
||||
```
|
||||
# xsos
|
||||
|
||||
OS
|
||||
Hostname: CentOS7.2daygeek.com
|
||||
Distro: [redhat-release] CentOS Linux release 7.6.1810 (Core)
|
||||
[centos-release] CentOS Linux release 7.6.1810 (Core)
|
||||
[os-release] CentOS Linux 7 (Core) 7 (Core)
|
||||
RHN: (missing)
|
||||
RHSM: (missing)
|
||||
YUM: 2 enabled plugins: fastestmirror, langpacks
|
||||
Runlevel: N 5 (default graphical)
|
||||
SELinux: enforcing (default enforcing)
|
||||
Arch: mach=x86_64 cpu=x86_64 platform=x86_64
|
||||
Kernel:
|
||||
Booted kernel: 3.10.0-957.el7.x86_64
|
||||
GRUB default: 3.10.0-957.el7.x86_64
|
||||
Build version:
|
||||
Linux version 3.10.0-957.el7.x86_64 ([email protected]) (gcc version 4.8.5 20150623 (Red
|
||||
Hat 4.8.5-36) (GCC) ) #1 SMP Thu Nov 8 23:39:32 UTC 2018
|
||||
Booted kernel cmdline:
|
||||
root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet
|
||||
LANG=en_US.UTF-8
|
||||
GRUB default kernel cmdline:
|
||||
root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet
|
||||
LANG=en_US.UTF-8
|
||||
Taint-check: 0 (kernel untainted)
|
||||
- - - - - - - - - - - - - - - - - - -
|
||||
Sys time: Sun May 12 10:05:21 CDT 2019
|
||||
Boot time: Sun May 12 09:50:20 CDT 2019 (epoch: 1557672620)
|
||||
Time Zone: America/Chicago
|
||||
Uptime: 15 min, 1 user
|
||||
LoadAvg: [1 CPU] 0.00 (0%), 0.04 (4%), 0.09 (9%)
|
||||
/proc/stat:
|
||||
procs_running: 2 procs_blocked: 0 processes [Since boot]: 6423
|
||||
cpu [Utilization since boot]:
|
||||
us 1%, ni 0%, sys 1%, idle 99%, iowait 0%, irq 0%, sftirq 0%, steal 0%
|
||||
```
|
||||
|
||||
### 如何使用 xsos 命令在 Linux 中查看生成的 SOSReport 输出?
|
||||
|
||||
我们需要份 SOSReport 以使用 `xsos` 命令进一步阅读。
|
||||
|
||||
是的,我已经生成了一个 SOSReport,文件如下。
|
||||
|
||||
```
|
||||
# ls -lls -lh /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa.tar.xz
|
||||
9.8M -rw-------. 1 root root 9.8M May 12 10:13 /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa.tar.xz
|
||||
```
|
||||
|
||||
运行如下命令解开它。
|
||||
|
||||
```
|
||||
# tar xf sosreport-CentOS7-01-1005-2019-05-12-pomeqsa.tar.xz
|
||||
```
|
||||
|
||||
要查看全部信息,带上 `-a` 或 `--all` 开关运行 `xsos`:
|
||||
|
||||
```
|
||||
# xsos --all /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
```
|
||||
|
||||
要查看 BIOS 信息,带上 `-b` 或 `--bios` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --bios /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
DMIDECODE
|
||||
BIOS:
|
||||
Vend: innotek GmbH
|
||||
Vers: VirtualBox
|
||||
Date: 12/01/2006
|
||||
BIOS Rev:
|
||||
FW Rev:
|
||||
System:
|
||||
Mfr: innotek GmbH
|
||||
Prod: VirtualBox
|
||||
Vers: 1.2
|
||||
Ser: 0
|
||||
UUID: 002f47b8-2af2-48f5-be1d-67b67e03514c
|
||||
CPU:
|
||||
0 of 0 CPU sockets populated, 0 cores/0 threads per CPU
|
||||
0 total cores, 0 total threads
|
||||
Mfr:
|
||||
Fam:
|
||||
Freq:
|
||||
Vers:
|
||||
Memory:
|
||||
Total: 0 MiB (0 GiB)
|
||||
DIMMs: 0 of 0 populated
|
||||
MaxCapacity: 0 MiB (0 GiB / 0.00 TiB)
|
||||
```
|
||||
|
||||
要查看系统基本信息,如主机名、发行版、SELinux、内核信息、正常运行时间等,请使用 `-o` 或 `--os` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --os /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
OS
|
||||
Hostname: CentOS7.2daygeek.com
|
||||
Distro: [redhat-release] CentOS Linux release 7.6.1810 (Core)
|
||||
[centos-release] CentOS Linux release 7.6.1810 (Core)
|
||||
[os-release] CentOS Linux 7 (Core) 7 (Core)
|
||||
RHN: (missing)
|
||||
RHSM: (missing)
|
||||
YUM: 2 enabled plugins: fastestmirror, langpacks
|
||||
SELinux: enforcing (default enforcing)
|
||||
Arch: mach=x86_64 cpu=x86_64 platform=x86_64
|
||||
Kernel:
|
||||
Booted kernel: 3.10.0-957.el7.x86_64
|
||||
GRUB default: 3.10.0-957.el7.x86_64
|
||||
Build version:
|
||||
Linux version 3.10.0-957.el7.x86_64 ([email protected]) (gcc version 4.8.5 20150623 (Red
|
||||
Hat 4.8.5-36) (GCC) ) #1 SMP Thu Nov 8 23:39:32 UTC 2018
|
||||
Booted kernel cmdline:
|
||||
root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet
|
||||
LANG=en_US.UTF-8
|
||||
GRUB default kernel cmdline:
|
||||
root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet
|
||||
LANG=en_US.UTF-8
|
||||
Taint-check: 536870912 (see https://access.redhat.com/solutions/40594)
|
||||
29 TECH_PREVIEW: Technology Preview code is loaded
|
||||
- - - - - - - - - - - - - - - - - - -
|
||||
Sys time: Sun May 12 10:12:22 CDT 2019
|
||||
Boot time: Sun May 12 09:50:20 CDT 2019 (epoch: 1557672620)
|
||||
Time Zone: America/Chicago
|
||||
Uptime: 22 min, 1 user
|
||||
LoadAvg: [1 CPU] 1.19 (119%), 0.27 (27%), 0.14 (14%)
|
||||
/proc/stat:
|
||||
procs_running: 8 procs_blocked: 2 processes [Since boot]: 9005
|
||||
cpu [Utilization since boot]:
|
||||
us 1%, ni 0%, sys 1%, idle 99%, iowait 0%, irq 0%, sftirq 0%, steal 0%
|
||||
```
|
||||
|
||||
要查看 kdump 配置,请使用 `-k` 或 `--kdump` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --kdump /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
KDUMP CONFIG
|
||||
kexec-tools rpm version:
|
||||
kexec-tools-2.0.15-21.el7.x86_64
|
||||
Service enablement:
|
||||
UNIT STATE
|
||||
kdump.service enabled
|
||||
kdump initrd/initramfs:
|
||||
13585734 Feb 19 05:51 initramfs-3.10.0-957.el7.x86_64kdump.img
|
||||
Memory reservation config:
|
||||
/proc/cmdline { crashkernel=auto }
|
||||
GRUB default { crashkernel=auto }
|
||||
Actual memory reservation per /proc/iomem:
|
||||
2a000000-340fffff : Crash kernel
|
||||
kdump.conf:
|
||||
path /var/crash
|
||||
core_collector makedumpfile -l --message-level 1 -d 31
|
||||
kdump.conf "path" available space:
|
||||
System MemTotal (uncompressed core size) { 1.80 GiB }
|
||||
Available free space on target path's fs { 22.68 GiB } (fs=/)
|
||||
Panic sysctls:
|
||||
kernel.sysrq [bitmask] = "16" (see proc man page)
|
||||
kernel.panic [secs] = 0 (no autoreboot on panic)
|
||||
kernel.hung_task_panic = 0
|
||||
kernel.panic_on_oops = 1
|
||||
kernel.panic_on_io_nmi = 0
|
||||
kernel.panic_on_unrecovered_nmi = 0
|
||||
kernel.panic_on_stackoverflow = 0
|
||||
kernel.softlockup_panic = 0
|
||||
kernel.unknown_nmi_panic = 0
|
||||
kernel.nmi_watchdog = 1
|
||||
vm.panic_on_oom [0-2] = 0 (no panic)
|
||||
```
|
||||
|
||||
要查看有关 CPU 的信息,请使用 `-c` 或 `--cpu` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --cpu /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
CPU
|
||||
1 logical processors
|
||||
1 Intel Core i7-6700HQ CPU @ 2.60GHz (flags: aes,constant_tsc,ht,lm,nx,pae,rdrand)
|
||||
```
|
||||
|
||||
要查看内存利用情况,请使用 `-m` 或 `--mem` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --mem /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
MEMORY
|
||||
Stats graphed as percent of MemTotal:
|
||||
MemUsed ▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊..................... 58.8%
|
||||
Buffers .................................................. 0.6%
|
||||
Cached ▊▊▊▊▊▊▊▊▊▊▊▊▊▊▊................................... 29.9%
|
||||
HugePages .................................................. 0.0%
|
||||
Dirty .................................................. 0.7%
|
||||
RAM:
|
||||
1.8 GiB total ram
|
||||
1.1 GiB (59%) used
|
||||
0.5 GiB (28%) used excluding Buffers/Cached
|
||||
0.01 GiB (1%) dirty
|
||||
HugePages:
|
||||
No ram pre-allocated to HugePages
|
||||
LowMem/Slab/PageTables/Shmem:
|
||||
0.09 GiB (5%) of total ram used for Slab
|
||||
0.02 GiB (1%) of total ram used for PageTables
|
||||
0.01 GiB (1%) of total ram used for Shmem
|
||||
Swap:
|
||||
0 GiB (0%) used of 2 GiB total
|
||||
```
|
||||
|
||||
要查看添加的磁盘信息,请使用 `-d` 和 `-disks` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --disks /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
STORAGE
|
||||
Whole Disks from /proc/partitions:
|
||||
2 disks, totaling 40 GiB (0.04 TiB)
|
||||
- - - - - - - - - - - - - - - - - - - - -
|
||||
Disk Size in GiB
|
||||
---- -----------
|
||||
sda 30
|
||||
sdb 10
|
||||
```
|
||||
|
||||
要查看网络接口配置,请使用 `-e` 或 `--ethtool` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --ethtool /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
ETHTOOL
|
||||
Interface Status:
|
||||
enp0s10 0000:00:0a.0 link=up 1000Mb/s full (autoneg=Y) rx ring 256/4096 drv e1000 v7.3.21-k8-NAPI / fw UNKNOWN
|
||||
enp0s9 0000:00:09.0 link=up 1000Mb/s full (autoneg=Y) rx ring 256/4096 drv e1000 v7.3.21-k8-NAPI / fw UNKNOWN
|
||||
virbr0 N/A link=DOWN rx ring UNKNOWN drv bridge v2.3 / fw N/A
|
||||
virbr0-nic tap link=DOWN rx ring UNKNOWN drv tun v1.6 / fw UNKNOWN
|
||||
```
|
||||
|
||||
要查看有关 IP 地址的信息,请使用 `-i` 或 `--ip` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --ip /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
IP4
|
||||
Interface Master IF MAC Address MTU State IPv4 Address
|
||||
========= ========= ================= ====== ===== ==================
|
||||
lo - - 65536 up 127.0.0.1/8
|
||||
enp0s9 - 08:00:27:0b:bc:e9 1500 up 192.168.1.8/24
|
||||
enp0s10 - 08:00:27:b2:08:91 1500 up 192.168.1.9/24
|
||||
virbr0 - 52:54:00:ae:01:94 1500 up 192.168.122.1/24
|
||||
virbr0-nic virbr0 52:54:00:ae:01:94 1500 DOWN -
|
||||
|
||||
IP6
|
||||
Interface Master IF MAC Address MTU State IPv6 Address Scope
|
||||
========= ========= ================= ====== ===== =========================================== =====
|
||||
lo - - 65536 up ::1/128 host
|
||||
enp0s9 - 08:00:27:0b:bc:e9 1500 up fe80::945b:8333:f4bc:9723/64 link
|
||||
enp0s10 - 08:00:27:b2:08:91 1500 up fe80::7ed4:1fab:23c3:3790/64 link
|
||||
virbr0 - 52:54:00:ae:01:94 1500 up - -
|
||||
virbr0-nic virbr0 52:54:00:ae:01:94 1500 DOWN - -
|
||||
```
|
||||
|
||||
要通过 `ps` 查看正在运行的进程,请使用 `-p` 或 `--ps` 开关运行 `xsos`。
|
||||
|
||||
```
|
||||
# xsos --ps /var/tmp/sosreport-CentOS7-01-1005-2019-05-12-pomeqsa
|
||||
PS CHECK
|
||||
Total number of threads/processes:
|
||||
501 / 171
|
||||
Top users of CPU & MEM:
|
||||
USER %CPU %MEM RSS
|
||||
root 20.6% 14.1% 0.30 GiB
|
||||
gdm 0.3% 16.8% 0.33 GiB
|
||||
postfix 0.0% 0.6% 0.01 GiB
|
||||
polkitd 0.0% 0.6% 0.01 GiB
|
||||
daygeek 0.0% 0.2% 0.00 GiB
|
||||
colord 0.0% 0.4% 0.01 GiB
|
||||
Uninteruptible sleep threads/processes (0/0):
|
||||
[None]
|
||||
Defunct zombie threads/processes (0/0):
|
||||
[None]
|
||||
Top CPU-using processes:
|
||||
USER PID %CPU %MEM VSZ-MiB RSS-MiB TTY STAT START TIME COMMAND
|
||||
root 6542 15.6 4.2 875 78 pts/0 Sl+ 10:11 0:07 /usr/bin/python /sbin/sosreport
|
||||
root 7582 3.0 0.1 10 2 pts/0 S 10:12 0:00 /bin/bash /usr/sbin/dracut --print-cmdline
|
||||
root 7969 0.7 0.1 95 4 ? Ss 10:12 0:00 /usr/sbin/certmonger -S -p
|
||||
root 7889 0.4 0.2 24 4 ? Ss 10:12 0:00 /usr/lib/systemd/systemd-hostnamed
|
||||
gdm 3866 0.3 7.1 2856 131 ? Sl 09:50 0:04 /usr/bin/gnome-shell
|
||||
root 8553 0.2 0.1 47 3 ? S 10:12 0:00 /usr/lib/systemd/systemd-udevd
|
||||
root 6971 0.2 0.4 342 9 ? Sl 10:12 0:00 /usr/sbin/abrt-dbus -t133
|
||||
root 3200 0.2 0.9 982 18 ? Ssl 09:50 0:02 /usr/sbin/libvirtd
|
||||
root 2855 0.1 0.1 88 3 ? Ss 09:50 0:01 /sbin/rngd -f
|
||||
rtkit 2826 0.0 0.0 194 2 ? SNsl 09:50 0:00 /usr/libexec/rtkit-daemon
|
||||
Top MEM-using processes:
|
||||
USER PID %CPU %MEM VSZ-MiB RSS-MiB TTY STAT START TIME COMMAND
|
||||
gdm 3866 0.3 7.1 2856 131 ? Sl 09:50 0:04 /usr/bin/gnome-shell
|
||||
root 6542 15.6 4.2 875 78 pts/0 Sl+ 10:11 0:07 /usr/bin/python /sbin/sosreport
|
||||
root 3264 0.0 1.2 271 23 tty1 Ssl+ 09:50 0:00 /usr/bin/X :0 -background
|
||||
root 3200 0.2 0.9 982 18 ? Ssl 09:50 0:02 /usr/sbin/libvirtd
|
||||
root 3189 0.0 0.9 560 17 ? Ssl 09:50 0:00 /usr/bin/python2 -Es /usr/sbin/tuned
|
||||
gdm 4072 0.0 0.9 988 17 ? Sl 09:50 0:00 /usr/libexec/gsd-media-keys
|
||||
gdm 4076 0.0 0.8 625 16 ? Sl 09:50 0:00 /usr/libexec/gsd-power
|
||||
gdm 4056 0.0 0.8 697 16 ? Sl 09:50 0:00 /usr/libexec/gsd-color
|
||||
root 2853 0.0 0.7 622 14 ? Ssl 09:50 0:00 /usr/sbin/NetworkManager --no-daemon
|
||||
gdm 4110 0.0 0.7 544 14 ? Sl 09:50 0:00 /usr/libexec/gsd-wacom
|
||||
Top thread-spawning processes:
|
||||
# USER PID %CPU %MEM VSZ-MiB RSS-MiB TTY STAT START TIME COMMAND
|
||||
17 root 3200 0.2 0.9 982 18 ? - 09:50 0:02 /usr/sbin/libvirtd
|
||||
12 root 6542 16.1 4.5 876 83 pts/0 - 10:11 0:07 /usr/bin/python /sbin/sosreport
|
||||
10 gdm 3866 0.3 7.1 2856 131 ? - 09:50 0:04 /usr/bin/gnome-shell
|
||||
7 polkitd 2864 0.0 0.6 602 13 ? - 09:50 0:01 /usr/lib/polkit-1/polkitd --no-debug
|
||||
6 root 2865 0.0 0.0 203 1 ? - 09:50 0:00 /usr/sbin/gssproxy -D
|
||||
5 root 3189 0.0 0.9 560 17 ? - 09:50 0:00 /usr/bin/python2 -Es /usr/sbin/tuned
|
||||
5 root 2823 0.0 0.3 443 6 ? - 09:50 0:00 /usr/libexec/udisks2/udisksd
|
||||
5 gdm 4102 0.0 0.2 461 5 ? - 09:50 0:00 /usr/libexec/gsd-smartcard
|
||||
4 root 3215 0.0 0.2 470 4 ? - 09:50 0:00 /usr/sbin/gdm
|
||||
4 gdm 4106 0.0 0.2 444 5 ? - 09:50 0:00 /usr/libexec/gsd-sound
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/xsos-a-tool-to-read-sosreport-in-linux/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[wxy](https://github.com/wxy)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.2daygeek.com/how-to-create-collect-sosreport-in-linux/
|
||||
[2]: https://www.2daygeek.com/oswbb-how-to-install-and-configure-oswatcher-black-box-for-system-diagnostics/
|
||||
[3]: https://github.com/ryran/xsos
|
||||
[4]: data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
[5]: https://www.2daygeek.com/wp-content/uploads/2019/05/xsos-a-tool-to-read-sosreport-in-linux-1.jpg
|
@ -0,0 +1,331 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (way-ww)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10923-1.html)
|
||||
[#]: subject: (How To Install/Uninstall Listed Packages From A File In Linux?)
|
||||
[#]: via: (https://www.2daygeek.com/how-to-install-uninstall-listed-packages-from-a-file-in-linux/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
如何在 Linux 上安装/卸载一个文件中列出的软件包?
|
||||
======
|
||||
|
||||
在某些情况下,你可能想要将一个服务器上的软件包列表安装到另一个服务器上。例如,你已经在服务器 A 上安装了 15 个软件包并且这些软件包也需要被安装到服务器 B、服务器 C 上等等。
|
||||
|
||||
我们可以手动去安装这些软件但是这将花费大量的时间。你可以手动安装一俩个服务器,但是试想如果你有大概十个服务器呢。在这种情况下你无法手动完成工作,那么怎样才能解决问题呢?
|
||||
|
||||
不要担心我们可以帮你摆脱这样的情况和场景。我们在这篇文章中增加了四种方法来克服困难。
|
||||
|
||||
我希望这可以帮你解决问题。我已经在 Centos7 和 Ubuntu 18.04 上测试了这些命令。
|
||||
|
||||
我也希望这可以在其他发行版上工作。这仅仅需要使用该发行版的官方包管理器命令替代本文中的包管理器命令就行了。
|
||||
|
||||
如果想要 [检查 Linux 系统上已安装的软件包列表][1],请点击链接。
|
||||
|
||||
例如,如果你想要在基于 RHEL 系统上创建软件包列表请使用以下步骤。其他发行版也一样。
|
||||
|
||||
```
|
||||
# rpm -qa --last | head -15 | awk '{print $1}' > /tmp/pack1.txt
|
||||
|
||||
# cat /tmp/pack1.txt
|
||||
mariadb-server-5.5.60-1.el7_5.x86_64
|
||||
perl-DBI-1.627-4.el7.x86_64
|
||||
perl-DBD-MySQL-4.023-6.el7.x86_64
|
||||
perl-PlRPC-0.2020-14.el7.noarch
|
||||
perl-Net-Daemon-0.48-5.el7.noarch
|
||||
perl-IO-Compress-2.061-2.el7.noarch
|
||||
perl-Compress-Raw-Zlib-2.061-4.el7.x86_64
|
||||
mariadb-5.5.60-1.el7_5.x86_64
|
||||
perl-Data-Dumper-2.145-3.el7.x86_64
|
||||
perl-Compress-Raw-Bzip2-2.061-3.el7.x86_64
|
||||
httpd-2.4.6-88.el7.centos.x86_64
|
||||
mailcap-2.1.41-2.el7.noarch
|
||||
httpd-tools-2.4.6-88.el7.centos.x86_64
|
||||
apr-util-1.5.2-6.el7.x86_64
|
||||
apr-1.4.8-3.el7_4.1.x86_64
|
||||
```
|
||||
|
||||
### 方法一:如何在 Linux 上使用 cat 命令安装文件中列出的包?
|
||||
|
||||
为实现这个目标,我将使用简单明了的第一种方法。为此,创建一个文件并添加上你想要安装的包列表。
|
||||
|
||||
出于测试的目的,我们将只添加以下的三个软件包名到文件中。
|
||||
|
||||
```
|
||||
# cat /tmp/pack1.txt
|
||||
|
||||
apache2
|
||||
mariadb-server
|
||||
nano
|
||||
```
|
||||
|
||||
只要简单的运行 [apt 命令][2] 就能在 Ubuntu/Debian 系统上一次性安装所有的软件包。
|
||||
|
||||
```
|
||||
# apt -y install $(cat /tmp/pack1.txt)
|
||||
|
||||
Reading package lists... Done
|
||||
Building dependency tree
|
||||
Reading state information... Done
|
||||
The following packages were automatically installed and are no longer required:
|
||||
libopts25 sntp
|
||||
Use 'sudo apt autoremove' to remove them.
|
||||
Suggested packages:
|
||||
apache2-doc apache2-suexec-pristine | apache2-suexec-custom spell
|
||||
The following NEW packages will be installed:
|
||||
apache2 mariadb-server nano
|
||||
0 upgraded, 3 newly installed, 0 to remove and 24 not upgraded.
|
||||
Need to get 339 kB of archives.
|
||||
After this operation, 1,377 kB of additional disk space will be used.
|
||||
Get:1 http://in.archive.ubuntu.com/ubuntu bionic-updates/main amd64 apache2 amd64 2.4.29-1ubuntu4.6 [95.1 kB]
|
||||
Get:2 http://in.archive.ubuntu.com/ubuntu bionic/main amd64 nano amd64 2.9.3-2 [231 kB]
|
||||
Get:3 http://in.archive.ubuntu.com/ubuntu bionic-updates/universe amd64 mariadb-server all 1:10.1.38-0ubuntu0.18.04.1 [12.9 kB]
|
||||
Fetched 339 kB in 19s (18.0 kB/s)
|
||||
Selecting previously unselected package apache2.
|
||||
(Reading database ... 290926 files and directories currently installed.)
|
||||
Preparing to unpack .../apache2_2.4.29-1ubuntu4.6_amd64.deb ...
|
||||
Unpacking apache2 (2.4.29-1ubuntu4.6) ...
|
||||
Selecting previously unselected package nano.
|
||||
Preparing to unpack .../nano_2.9.3-2_amd64.deb ...
|
||||
Unpacking nano (2.9.3-2) ...
|
||||
Selecting previously unselected package mariadb-server.
|
||||
Preparing to unpack .../mariadb-server_1%3a10.1.38-0ubuntu0.18.04.1_all.deb ...
|
||||
Unpacking mariadb-server (1:10.1.38-0ubuntu0.18.04.1) ...
|
||||
Processing triggers for ufw (0.36-0ubuntu0.18.04.1) ...
|
||||
Setting up apache2 (2.4.29-1ubuntu4.6) ...
|
||||
Processing triggers for ureadahead (0.100.0-20) ...
|
||||
Processing triggers for install-info (6.5.0.dfsg.1-2) ...
|
||||
Setting up nano (2.9.3-2) ...
|
||||
update-alternatives: using /bin/nano to provide /usr/bin/editor (editor) in auto mode
|
||||
update-alternatives: using /bin/nano to provide /usr/bin/pico (pico) in auto mode
|
||||
Processing triggers for systemd (237-3ubuntu10.20) ...
|
||||
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
|
||||
Setting up mariadb-server (1:10.1.38-0ubuntu0.18.04.1) ...
|
||||
```
|
||||
|
||||
至于删除,需要使用相同的命令格式和适当的选项。
|
||||
|
||||
```
|
||||
# apt -y remove $(cat /tmp/pack1.txt)
|
||||
Reading package lists... Done
|
||||
Building dependency tree
|
||||
Reading state information... Done
|
||||
The following packages were automatically installed and are no longer required:
|
||||
apache2-bin apache2-data apache2-utils galera-3 libaio1 libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap libconfig-inifiles-perl libdbd-mysql-perl libdbi-perl libjemalloc1 liblua5.2-0
|
||||
libmysqlclient20 libopts25 libterm-readkey-perl mariadb-client-10.1 mariadb-client-core-10.1 mariadb-common mariadb-server-10.1 mariadb-server-core-10.1 mysql-common sntp socat
|
||||
Use 'apt autoremove' to remove them.
|
||||
The following packages will be REMOVED:
|
||||
apache2 mariadb-server nano
|
||||
0 upgraded, 0 newly installed, 3 to remove and 24 not upgraded.
|
||||
After this operation, 1,377 kB disk space will be freed.
|
||||
(Reading database ... 291046 files and directories currently installed.)
|
||||
Removing apache2 (2.4.29-1ubuntu4.6) ...
|
||||
Removing mariadb-server (1:10.1.38-0ubuntu0.18.04.1) ...
|
||||
Removing nano (2.9.3-2) ...
|
||||
update-alternatives: using /usr/bin/vim.tiny to provide /usr/bin/editor (editor) in auto mode
|
||||
Processing triggers for ufw (0.36-0ubuntu0.18.04.1) ...
|
||||
Processing triggers for install-info (6.5.0.dfsg.1-2) ...
|
||||
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
|
||||
```
|
||||
|
||||
使用 [yum 命令][3] 在基于 RHEL (如 Centos、RHEL (Redhat) 和 OEL (Oracle Enterprise Linux)) 的系统上安装文件中列出的软件包。
|
||||
|
||||
```
|
||||
# yum -y install $(cat /tmp/pack1.txt)
|
||||
```
|
||||
|
||||
使用以命令在基于 RHEL (如 Centos、RHEL (Redhat) 和 OEL (Oracle Enterprise Linux)) 的系统上卸载文件中列出的软件包。
|
||||
|
||||
```
|
||||
# yum -y remove $(cat /tmp/pack1.txt)
|
||||
```
|
||||
|
||||
使用以下 [dnf 命令][4] 在 Fedora 系统上安装文件中列出的软件包。
|
||||
|
||||
```
|
||||
# dnf -y install $(cat /tmp/pack1.txt)
|
||||
```
|
||||
|
||||
使用以下命令在 Fedora 系统上卸载文件中列出的软件包。
|
||||
|
||||
```
|
||||
# dnf -y remove $(cat /tmp/pack1.txt)
|
||||
```
|
||||
|
||||
使用以下 [zypper 命令][5] 在 openSUSE 系统上安装文件中列出的软件包。
|
||||
|
||||
```
|
||||
# zypper -y install $(cat /tmp/pack1.txt)
|
||||
```
|
||||
|
||||
使用以下命令从 openSUSE 系统上卸载文件中列出的软件包。
|
||||
|
||||
```
|
||||
# zypper -y remove $(cat /tmp/pack1.txt)
|
||||
```
|
||||
|
||||
使用以下 [pacman 命令][6] 在基于 Arch Linux (如 Manjaro 和 Antergos) 的系统上安装文件中列出的软件包。
|
||||
|
||||
```
|
||||
# pacman -S $(cat /tmp/pack1.txt)
|
||||
```
|
||||
|
||||
使用以下命令从基于 Arch Linux (如 Manjaro 和 Antergos) 的系统中卸载文件中列出的软件包。
|
||||
|
||||
|
||||
```
|
||||
# pacman -Rs $(cat /tmp/pack1.txt)
|
||||
```
|
||||
|
||||
### 方法二:如何使用 cat 和 xargs 命令在 Linux 中安装文件中列出的软件包。
|
||||
|
||||
甚至,我更喜欢使用这种方法,因为这是一种非常简单直接的方法。
|
||||
|
||||
使用以下 `apt` 命令在基于 Debian 的系统 (如 Debian、Ubuntu 和 Linux Mint) 上安装文件中列出的软件包。
|
||||
|
||||
```
|
||||
# cat /tmp/pack1.txt | xargs apt -y install
|
||||
```
|
||||
|
||||
使用以下 `apt` 命令 从基于 Debian 的系统 (如 Debian、Ubuntu 和 Linux Mint) 上卸载文件中列出的软件包。
|
||||
|
||||
```
|
||||
# cat /tmp/pack1.txt | xargs apt -y remove
|
||||
```
|
||||
|
||||
使用以下 `yum` 命令在基于 RHEL (如 Centos,RHEL (Redhat) 和 OEL (Oracle Enterprise Linux)) 的系统上安装文件中列出的软件包。
|
||||
|
||||
```
|
||||
# cat /tmp/pack1.txt | xargs yum -y install
|
||||
```
|
||||
|
||||
使用以命令从基于 RHEL (如 Centos、RHEL (Redhat) 和 OEL (Oracle Enterprise Linux)) 的系统上卸载文件中列出的软件包。
|
||||
|
||||
```
|
||||
# cat /tmp/pack1.txt | xargs yum -y remove
|
||||
```
|
||||
|
||||
使用以下 `dnf` 命令在 Fedora 系统上安装文件中列出的软件包。
|
||||
|
||||
```
|
||||
# cat /tmp/pack1.txt | xargs dnf -y install
|
||||
```
|
||||
|
||||
使用以下命令从 Fedora 系统上卸载文件中列出的软件包。
|
||||
|
||||
```
|
||||
# cat /tmp/pack1.txt | xargs dnf -y remove
|
||||
```
|
||||
|
||||
使用以下 `zypper` 命令在 openSUSE 系统上安装文件中列出的软件包。
|
||||
|
||||
|
||||
```
|
||||
# cat /tmp/pack1.txt | xargs zypper -y install
|
||||
```
|
||||
|
||||
使用以下命令从 openSUSE 系统上卸载文件中列出的软件包。
|
||||
|
||||
```
|
||||
# cat /tmp/pack1.txt | xargs zypper -y remove
|
||||
```
|
||||
|
||||
使用以下 `pacman` 命令在基于 Arch Linux (如 Manjaro 和 Antergos) 的系统上安装文件中列出的软件包。
|
||||
|
||||
```
|
||||
# cat /tmp/pack1.txt | xargs pacman -S
|
||||
```
|
||||
|
||||
使用下以命令从基于 Arch Linux (如 Manjaro 和 Antergos) 的系统上卸载文件中列出的软件包。
|
||||
|
||||
```
|
||||
# cat /tmp/pack1.txt | xargs pacman -Rs
|
||||
```
|
||||
|
||||
### 方法三 : 如何使用 For 循环在 Linux 上安装文件中列出的软件包
|
||||
|
||||
我们也可以使用 `for` 循环命令来实现此目的。
|
||||
|
||||
安装批量包可以使用以下一条 `for` 循环的命令。
|
||||
|
||||
```
|
||||
# for pack in `cat /tmp/pack1.txt` ; do apt -y install $i; done
|
||||
```
|
||||
|
||||
要使用 shell 脚本安装批量包,请使用以下 `for` 循环。
|
||||
|
||||
```
|
||||
# vi /opt/scripts/bulk-package-install.sh
|
||||
|
||||
#!/bin/bash
|
||||
for pack in `cat /tmp/pack1.txt`
|
||||
do apt -y remove $pack
|
||||
done
|
||||
```
|
||||
|
||||
为 `bulk-package-install.sh` 设置可执行权限。
|
||||
|
||||
```
|
||||
# chmod + bulk-package-install.sh
|
||||
```
|
||||
|
||||
最后运行这个脚本。
|
||||
|
||||
```
|
||||
# sh bulk-package-install.sh
|
||||
```
|
||||
|
||||
### 方法四:如何使用 While 循环在 Linux 上安装文件中列出的软件包
|
||||
|
||||
我们也可以使用 `while` 循环命令来实现目的。
|
||||
|
||||
安装批量包可以使用以下一条 `while` 循环的命令。
|
||||
|
||||
```
|
||||
# file="/tmp/pack1.txt"; while read -r pack; do apt -y install $pack; done < "$file"
|
||||
```
|
||||
|
||||
要使用 shell 脚本安装批量包,请使用以下 `while` 循环。
|
||||
|
||||
```
|
||||
# vi /opt/scripts/bulk-package-install.sh
|
||||
|
||||
#!/bin/bash
|
||||
file="/tmp/pack1.txt"
|
||||
while read -r pack
|
||||
do apt -y remove $pack
|
||||
done < "$file"
|
||||
```
|
||||
|
||||
为 `bulk-package-install.sh` 设置可执行权限。
|
||||
|
||||
```
|
||||
# chmod + bulk-package-install.sh
|
||||
```
|
||||
|
||||
最后运行这个脚本。
|
||||
|
||||
```
|
||||
# sh bulk-package-install.sh
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/how-to-install-uninstall-listed-packages-from-a-file-in-linux/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[way-ww](https://github.com/way-ww)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://linux.cn/article-10116-1.html
|
||||
[2]: https://www.2daygeek.com/apt-command-examples-manage-packages-debian-ubuntu-systems/
|
||||
[3]: https://www.2daygeek.com/yum-command-examples-manage-packages-rhel-centos-systems/
|
||||
[4]: https://www.2daygeek.com/dnf-command-examples-manage-packages-fedora-system/
|
||||
[5]: https://www.2daygeek.com/zypper-command-examples-manage-packages-opensuse-system/
|
||||
[6]: https://www.2daygeek.com/pacman-command-examples-manage-packages-arch-linux-system/
|
||||
|
@ -0,0 +1,154 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10943-1.html)
|
||||
[#]: subject: (Using Testinfra with Ansible to verify server state)
|
||||
[#]: via: (https://opensource.com/article/19/5/using-testinfra-ansible-verify-server-state)
|
||||
[#]: author: (Clement Verna https://opensource.com/users/cverna/users/paulbischoff/users/dcritch/users/cobiacomm/users/wgarry155/users/kadinroob/users/koreyhilpert)
|
||||
|
||||
使用 Testinfra 和 Ansible 验证服务器状态
|
||||
======
|
||||
|
||||
> Testinfra 是一个功能强大的库,可用于编写测试来验证基础设施的状态。另外它与 Ansible 和 Nagios 相结合,提供了一个用于架构即代码 (IaC) 的简单解决方案。
|
||||
|
||||
![Terminal command prompt on orange background][1]
|
||||
|
||||
根据设计,[Ansible][2] 传递机器的期望状态,以确保 Ansible 剧本或角色的内容部署到目标机器上。但是,如果你需要确保所有基础架构更改都在 Ansible 中,该怎么办?或者想随时验证服务器的状态?
|
||||
|
||||
[Testinfra][3] 是一个基础架构测试框架,它可以轻松编写单元测试来验证服务器的状态。它是一个 Python 库,使用强大的 [pytest][4] 测试引擎。
|
||||
|
||||
### 开始使用 Testinfra
|
||||
|
||||
可以使用 Python 包管理器(`pip`)和 Python 虚拟环境轻松安装 Testinfra。
|
||||
|
||||
```
|
||||
$ python3 -m venv venv
|
||||
$ source venv/bin/activate
|
||||
(venv) $ pip install testinfra
|
||||
```
|
||||
|
||||
Testinfra 也可以通过 Fedora 和 CentOS 的 EPEL 仓库中使用。例如,在 CentOS 7 上,你可以使用以下命令安装它:
|
||||
|
||||
```
|
||||
$ yum install -y epel-release
|
||||
$ yum install -y python-testinfra
|
||||
```
|
||||
|
||||
#### 一个简单的测试脚本
|
||||
|
||||
在 Testinfra 中编写测试很容易。使用你选择的代码编辑器,将以下内容添加到名为 `test_simple.py` 的文件中:
|
||||
|
||||
```
|
||||
import testinfra
|
||||
|
||||
def test_os_release(host):
|
||||
assert host.file("/etc/os-release").contains("Fedora")
|
||||
|
||||
def test_sshd_inactive(host):
|
||||
assert host.service("sshd").is_running is False
|
||||
```
|
||||
|
||||
默认情况下,Testinfra 为测试用例提供了一个 `host` 对象,该对象能访问不同的辅助模块。例如,第一个测试使用 `file` 模块来验证主机上文件的内容,第二个测试用例使用 `service` 模块来检查 systemd 服务的状态。
|
||||
|
||||
要在本机运行这些测试,请执行以下命令:
|
||||
|
||||
```
|
||||
(venv)$ pytest test_simple.py
|
||||
================================ test session starts ================================
|
||||
platform linux -- Python 3.7.3, pytest-4.4.1, py-1.8.0, pluggy-0.9.0
|
||||
rootdir: /home/cverna/Documents/Python/testinfra
|
||||
plugins: testinfra-3.0.0
|
||||
collected 2 items
|
||||
test_simple.py ..
|
||||
|
||||
================================ 2 passed in 0.05 seconds ================================
|
||||
```
|
||||
|
||||
有关 Testinfra API 的完整列表,你可以参考[文档][5]。
|
||||
|
||||
### Testinfra 和 Ansible
|
||||
|
||||
Testinfra 支持的后端之一是 Ansible,这意味着 Testinfra 可以直接使用 Ansible 的清单文件和清单中定义的一组机器来对它们进行测试。
|
||||
|
||||
我们使用以下清单文件作为示例:
|
||||
|
||||
```
|
||||
[web]
|
||||
app-frontend01
|
||||
app-frontend02
|
||||
|
||||
[database]
|
||||
db-backend01
|
||||
```
|
||||
|
||||
我们希望确保我们的 Apache Web 服务器在 `app-frontend01` 和 `app-frontend02` 上运行。让我们在名为 `test_web.py` 的文件中编写测试:
|
||||
|
||||
```
|
||||
def check_httpd_service(host):
|
||||
"""Check that the httpd service is running on the host"""
|
||||
assert host.service("httpd").is_running
|
||||
```
|
||||
|
||||
要使用 Testinfra 和 Ansible 运行此测试,请使用以下命令:
|
||||
|
||||
|
||||
```
|
||||
(venv) $ pip install ansible
|
||||
(venv) $ py.test --hosts=web --ansible-inventory=inventory --connection=ansible test_web.py
|
||||
```
|
||||
|
||||
在调用测试时,我们使用 Ansible 清单文件的 `[web]` 组作为目标计算机,并指定我们要使用 Ansible 作为连接后端。
|
||||
|
||||
#### 使用 Ansible 模块
|
||||
|
||||
Testinfra 还为 Ansible 提供了一个很好的可用于测试的 API。该 Ansible 模块能够在测试中运行 Ansible 动作,并且能够轻松检查动作的状态。
|
||||
|
||||
```
|
||||
def check_ansible_play(host):
|
||||
"""
|
||||
Verify that a package is installed using Ansible
|
||||
package module
|
||||
"""
|
||||
assert not host.ansible("package", "name=httpd state=present")["changed"]
|
||||
```
|
||||
|
||||
默认情况下,Ansible 的[检查模式][6]已启用,这意味着 Ansible 将报告在远程主机上执行动作时会发生的变化。
|
||||
|
||||
### Testinfra 和 Nagios
|
||||
|
||||
现在我们可以轻松地运行测试来验证机器的状态,我们可以使用这些测试来触发监控系统上的警报。这是捕获意外的更改的好方法。
|
||||
|
||||
Testinfra 提供了与 [Nagios][7] 的集成,它是一种流行的监控解决方案。默认情况下,Nagios 使用 [NRPE][8] 插件对远程主机进行检查,但使用 Testinfra 可以直接从 Nagios 主控节点上运行测试。
|
||||
|
||||
要使 Testinfra 输出与 Nagios 兼容,我们必须在触发测试时使用 `--nagios` 标志。我们还使用 `-qq` 这个 pytest 标志来启用 pytest 的静默模式,这样就不会显示所有测试细节。
|
||||
|
||||
```
|
||||
(venv) $ py.test --hosts=web --ansible-inventory=inventory --connection=ansible --nagios -qq line test.py
|
||||
TESTINFRA OK - 1 passed, 0 failed, 0 skipped in 2.55 seconds
|
||||
```
|
||||
|
||||
Testinfra 是一个功能强大的库,可用于编写测试以验证基础架构的状态。 另外与 Ansible 和 Nagios 相结合,提供了一个用于架构即代码 (IaC) 的简单解决方案。 它也是使用 [Molecule][9] 开发 Ansible 角色过程中添加测试的关键组件。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://opensource.com/article/19/5/using-testinfra-ansible-verify-server-state
|
||||
|
||||
作者:[Clement Verna][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://opensource.com/users/cverna/users/paulbischoff/users/dcritch/users/cobiacomm/users/wgarry155/users/kadinroob/users/koreyhilpert
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/terminal_command_linux_desktop_code.jpg?itok=p5sQ6ODE (Terminal command prompt on orange background)
|
||||
[2]: https://www.ansible.com/
|
||||
[3]: https://testinfra.readthedocs.io/en/latest/
|
||||
[4]: https://pytest.org/
|
||||
[5]: https://testinfra.readthedocs.io/en/latest/modules.html#modules
|
||||
[6]: https://docs.ansible.com/ansible/playbooks_checkmode.html
|
||||
[7]: https://www.nagios.org/
|
||||
[8]: https://en.wikipedia.org/wiki/Nagios#NRPE
|
||||
[9]: https://github.com/ansible/molecule
|
471
published/20190520 Getting Started With Docker.md
Normal file
471
published/20190520 Getting Started With Docker.md
Normal file
@ -0,0 +1,471 @@
|
||||
[#]: collector: "lujun9972"
|
||||
[#]: translator: "zhang5788"
|
||||
[#]: reviewer: "wxy"
|
||||
[#]: publisher: "wxy"
|
||||
[#]: url: "https://linux.cn/article-10940-1.html"
|
||||
[#]: subject: "Getting Started With Docker"
|
||||
[#]: via: "https://www.ostechnix.com/getting-started-with-docker/"
|
||||
[#]: author: "sk https://www.ostechnix.com/author/sk/"
|
||||
|
||||
Docker 入门指南
|
||||
======
|
||||
|
||||
![Getting Started With Docker][1]
|
||||
|
||||
在我们的上一个教程中,我们已经了解[如何在 Ubuntu 上安装 Docker][1],和如何在 [CentOS 上安装 Docker][2]。今天,我们将会了解 Docker 的一些基础用法。该教程包含了如何创建一个新的 Docker 容器,如何运行该容器,如何从现有的 Docker 容器中创建自己的 Docker 镜像等 Docker 的一些基础知识、操作。所有步骤均在 Ubuntu 18.04 LTS server 版本下测试通过。
|
||||
|
||||
### 入门指南
|
||||
|
||||
在开始指南之前,不要混淆 Docker 镜像和 Docker 容器这两个概念。在之前的教程中,我就解释过,Docker 镜像是决定 Docker 容器行为的一个文件,Docker 容器则是 Docker 镜像的运行态或停止态。(LCTT 译注:在 macOS 下使用 Docker 终端时,不需要加 `sudo`)
|
||||
|
||||
#### 1、搜索 Docker 镜像
|
||||
|
||||
我们可以从 Docker 仓库中获取镜像,例如 [Docker hub][3],或者自己创建镜像。这里解释一下,Docker hub 是一个云服务器,用来提供给 Docker 的用户们创建、测试,和保存他们的镜像。
|
||||
|
||||
Docker hub 拥有成千上万个 Docker 镜像文件。你可以通过 `docker search`命令在这里搜索任何你想要的镜像。
|
||||
|
||||
例如,搜索一个基于 Ubuntu 的镜像文件,只需要运行:
|
||||
|
||||
```shell
|
||||
$ sudo docker search ubuntu
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
![][5]
|
||||
|
||||
搜索基于 CentOS 的镜像,运行:
|
||||
|
||||
```shell
|
||||
$ sudo docker search centos
|
||||
```
|
||||
|
||||
搜索 AWS 的镜像,运行:
|
||||
|
||||
```shell
|
||||
$ sudo docker search aws
|
||||
```
|
||||
|
||||
搜索 WordPress 的镜像:
|
||||
|
||||
```shell
|
||||
$ sudo docker search wordpress
|
||||
```
|
||||
|
||||
Docker hub 拥有几乎所有种类的镜像,包含操作系统、程序和其他任意的类型,这些你都能在 Docker hub 上找到已经构建完的镜像。如果你在搜索时,无法找到你想要的镜像文件,你也可以自己构建一个,将其发布出去,或者仅供你自己使用。
|
||||
|
||||
#### 2、下载 Docker 镜像
|
||||
|
||||
下载 Ubuntu 的镜像,你需要在终端运行以下命令:
|
||||
|
||||
```shell
|
||||
$ sudo docker pull ubuntu
|
||||
```
|
||||
|
||||
这条命令将会从 Docker hub 下载最近一个版本的 Ubuntu 镜像文件。
|
||||
|
||||
示例输出:
|
||||
|
||||
```
|
||||
Using default tag: latest
|
||||
latest: Pulling from library/ubuntu
|
||||
6abc03819f3e: Pull complete
|
||||
05731e63f211: Pull complete
|
||||
0bd67c50d6be: Pull complete
|
||||
Digest: sha256:f08638ec7ddc90065187e7eabdfac3c96e5ff0f6b2f1762cf31a4f49b53000a5
|
||||
Status: Downloaded newer image for ubuntu:latest
|
||||
```
|
||||
|
||||
![下载 Docker 镜像][6]
|
||||
|
||||
你也可以下载指定版本的 Ubuntu 镜像。运行以下命令:
|
||||
|
||||
```shell
|
||||
$ docker pull ubuntu:18.04
|
||||
```
|
||||
|
||||
Docker 允许在任意的宿主机操作系统下,下载任意的镜像文件,并运行。
|
||||
|
||||
例如,下载 CentOS 镜像:
|
||||
|
||||
```shell
|
||||
$ sudo docker pull centos
|
||||
```
|
||||
|
||||
所有下载的镜像文件,都被保存在 `/var/lib/docker` 文件夹下。(LCTT 译注:不同操作系统存放的文件夹并不是一致的,具体存放位置请在官方查询)
|
||||
|
||||
查看已经下载的镜像列表,可以使用以下命令:
|
||||
|
||||
```shell
|
||||
$ sudo docker images
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
```shell
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
ubuntu latest 7698f282e524 14 hours ago 69.9MB
|
||||
centos latest 9f38484d220f 2 months ago 202MB
|
||||
hello-world latest fce289e99eb9 4 months ago 1.84kB
|
||||
```
|
||||
|
||||
正如你看到的那样,我已经下载了三个镜像文件:`ubuntu`、`centos` 和 `hello-world`。
|
||||
|
||||
现在,让我们继续,来看一下如何运行我们刚刚下载的镜像。
|
||||
|
||||
#### 3、运行 Docker 镜像
|
||||
|
||||
运行一个容器有两种方法。我们可以使用标签或者是镜像 ID。标签指的是特定的镜像快照。镜像 ID 是指镜像的唯一标识。
|
||||
|
||||
正如上面结果中显示,`latest` 是所有镜像的一个标签。`7698f282e524` 是 Ubuntu Docker 镜像的镜像 ID,`9f38484d220f`是 CentOS 镜像的镜像 ID,`fce289e99eb9` 是 hello_world 镜像的 镜像 ID。
|
||||
|
||||
下载完 Docker 镜像之后,你可以通过下面的命令来使用其标签来启动:
|
||||
|
||||
```shell
|
||||
$ sudo docker run -t -i ubuntu:latest /bin/bash
|
||||
```
|
||||
|
||||
在这条语句中:
|
||||
|
||||
* `-t`:在该容器中启动一个新的终端
|
||||
* `-i`:通过容器中的标准输入流建立交互式连接
|
||||
* `ubuntu:latest`:带有标签 `latest` 的 Ubuntu 容器
|
||||
* `/bin/bash`:在新的容器中启动 BASH Shell
|
||||
|
||||
或者,你可以通过镜像 ID 来启动新的容器:
|
||||
|
||||
```shell
|
||||
$ sudo docker run -t -i 7698f282e524 /bin/bash
|
||||
```
|
||||
|
||||
在这条语句里:
|
||||
|
||||
* `7698f282e524` — 镜像 ID
|
||||
|
||||
在启动容器之后,将会自动进入容器的 shell 中(注意看命令行的提示符)。
|
||||
|
||||
![][7]
|
||||
|
||||
*Docker 容器的 Shell*
|
||||
|
||||
如果想要退回到宿主机的终端(在这个例子中,对我来说,就是退回到 18.04 LTS),并且不中断该容器的执行,你可以按下 `CTRL+P`,再按下 `CTRL+Q`。现在,你就安全的返回到了你的宿主机系统中。需要注意的是,Docker 容器仍然在后台运行,我们并没有中断它。
|
||||
|
||||
可以通过下面的命令来查看正在运行的容器:
|
||||
|
||||
```shell
|
||||
$ sudo docker ps
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
```shell
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
32fc32ad0d54 ubuntu:latest "/bin/bash" 7 minutes ago Up 7 minutes modest_jones
|
||||
```
|
||||
|
||||
![][8]
|
||||
|
||||
*列出正在运行的容器*
|
||||
|
||||
可以看到:
|
||||
|
||||
* `32fc32ad0d54` – 容器 ID
|
||||
* `ubuntu:latest` – Docker 镜像
|
||||
|
||||
需要注意的是,容器 ID 和 Docker 的镜像 ID是不同的。
|
||||
|
||||
可以通过以下命令查看所有正在运行和停止运行的容器:
|
||||
|
||||
```shell
|
||||
$ sudo docker ps -a
|
||||
```
|
||||
|
||||
在宿主机中断容器的执行:
|
||||
|
||||
```shell
|
||||
$ sudo docker stop <container-id>
|
||||
```
|
||||
|
||||
例如:
|
||||
|
||||
```shell
|
||||
$ sudo docker stop 32fc32ad0d54
|
||||
```
|
||||
|
||||
如果想要进入正在运行的容器中,你只需要运行:
|
||||
|
||||
```shell
|
||||
$ sudo docker attach 32fc32ad0d54
|
||||
```
|
||||
|
||||
正如你看到的,`32fc32ad0d54` 是一个容器的 ID。当你在容器中想要退出时,只需要在容器内的终端中输入命令:
|
||||
|
||||
```shell
|
||||
# exit
|
||||
```
|
||||
|
||||
你可以使用这个命令查看后台正在运行的容器:
|
||||
|
||||
```shell
|
||||
$ sudo docker ps
|
||||
```
|
||||
|
||||
#### 4、构建自己的 Docker 镜像
|
||||
|
||||
Docker 不仅仅可以下载运行在线的容器,你也可以创建你的自己的容器。
|
||||
|
||||
想要创建自己的 Docker 镜像,你需要先运行一个你已经下载完的容器:
|
||||
|
||||
```shell
|
||||
$ sudo docker run -t -i ubuntu:latest /bin/bash
|
||||
```
|
||||
|
||||
现在,你运行了一个容器,并且进入了该容器。然后,在该容器安装任意一个软件或做任何你想做的事情。
|
||||
|
||||
例如,我们在容器中安装一个 Apache web 服务器。
|
||||
|
||||
当你完成所有的操作,安装完所有的软件之后,你可以执行以下的命令来构建你自己的 Docker 镜像:
|
||||
|
||||
```shell
|
||||
# apt update
|
||||
# apt install apache2
|
||||
```
|
||||
|
||||
同样的,在容器中安装和测试你想要安装的所有软件。
|
||||
|
||||
当你安装完毕之后,返回的宿主机的终端。记住,不要关闭容器。想要返回到宿主机而不中断容器。请按下`CTRL+P`,再按下 `CTRL+Q`。
|
||||
|
||||
从你的宿主机的终端中,运行以下命令如寻找容器的 ID:
|
||||
|
||||
```shell
|
||||
$ sudo docker ps
|
||||
```
|
||||
|
||||
最后,从一个正在运行的容器中创建 Docker 镜像:
|
||||
|
||||
```shell
|
||||
$ sudo docker commit 3d24b3de0bfc ostechnix/ubuntu_apache
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
```shell
|
||||
sha256:ce5aa74a48f1e01ea312165887d30691a59caa0d99a2a4aa5116ae124f02f962
|
||||
```
|
||||
|
||||
在这里:
|
||||
|
||||
* `3d24b3de0bfc` — 指 Ubuntu 容器的 ID。
|
||||
* `ostechnix` — 我们创建的容器的用户名称
|
||||
* `ubuntu_apache` — 我们创建的镜像
|
||||
|
||||
让我们检查一下我们新创建的 Docker 镜像:
|
||||
|
||||
```shell
|
||||
$ sudo docker images
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
```shell
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
ostechnix/ubuntu_apache latest ce5aa74a48f1 About a minute ago 191MB
|
||||
ubuntu latest 7698f282e524 15 hours ago 69.9MB
|
||||
centos latest 9f38484d220f 2 months ago 202MB
|
||||
hello-world latest fce289e99eb9 4 months ago 1.84kB
|
||||
```
|
||||
|
||||
![][9]
|
||||
|
||||
*列出所有的 Docker 镜像*
|
||||
|
||||
正如你看到的,这个新的镜像就是我们刚刚在本地系统上从运行的容器上创建的。
|
||||
|
||||
现在,你可以从这个镜像创建一个新的容器。
|
||||
|
||||
```shell
|
||||
$ sudo docker run -t -i ostechnix/ubuntu_apache /bin/bash
|
||||
```
|
||||
|
||||
#### 5、删除容器
|
||||
|
||||
如果你在 Docker 上的工作已经全部完成,你就可以删除那些你不需要的容器。
|
||||
|
||||
想要删除一个容器,首先,你需要停止该容器。
|
||||
|
||||
我们先来看一下正在运行的容器有哪些
|
||||
|
||||
```shell
|
||||
$ sudo docker ps
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
```shell
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
3d24b3de0bfc ubuntu:latest "/bin/bash" 28 minutes ago Up 28 minutes goofy_easley
|
||||
```
|
||||
|
||||
使用容器 ID 来停止该容器:
|
||||
|
||||
```shell
|
||||
$ sudo docker stop 3d24b3de0bfc
|
||||
```
|
||||
|
||||
现在,就可以删除该容器了。
|
||||
|
||||
```shell
|
||||
$ sudo docker rm 3d24b3de0bfc
|
||||
```
|
||||
|
||||
你就可以按照这样的方法来删除那些你不需要的容器了。
|
||||
|
||||
当需要删除的容器数量很多时,一个一个删除也是很麻烦的,我们可以直接删除所有的已经停止的容器。只需要运行:
|
||||
|
||||
```shell
|
||||
$ sudo docker container prune
|
||||
```
|
||||
|
||||
按下 `Y`,来确认你的操作:
|
||||
|
||||
```sehll
|
||||
WARNING! This will remove all stopped containers.
|
||||
Are you sure you want to continue? [y/N] y
|
||||
Deleted Containers:
|
||||
32fc32ad0d5445f2dfd0d46121251c7b5a2aea06bb22588fb2594ddbe46e6564
|
||||
5ec614e0302061469ece212f0dba303c8fe99889389749e6220fe891997f38d0
|
||||
|
||||
Total reclaimed space: 5B
|
||||
```
|
||||
|
||||
这个命令仅支持最新的 Docker。(LCTT 译注:仅支持 1.25 及以上版本的 Docker)
|
||||
|
||||
#### 6、删除 Docker 镜像
|
||||
|
||||
当你删除了不要的 Docker 容器后,你也可以删除你不需要的 Docker 镜像。
|
||||
|
||||
列出已经下载的镜像:
|
||||
|
||||
```shell
|
||||
$ sudo docker images
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
```shell
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
ostechnix/ubuntu_apache latest ce5aa74a48f1 5 minutes ago 191MB
|
||||
ubuntu latest 7698f282e524 15 hours ago 69.9MB
|
||||
centos latest 9f38484d220f 2 months ago 202MB
|
||||
hello-world latest fce289e99eb9 4 months ago 1.84kB
|
||||
```
|
||||
|
||||
由上面的命令可以知道,在本地的系统中存在三个镜像。
|
||||
|
||||
使用镜像 ID 来删除镜像。
|
||||
|
||||
```shell
|
||||
$ sudo docekr rmi ce5aa74a48f1
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
```shell
|
||||
Untagged: ostechnix/ubuntu_apache:latest
|
||||
Deleted: sha256:ce5aa74a48f1e01ea312165887d30691a59caa0d99a2a4aa5116ae124f02f962
|
||||
Deleted: sha256:d21c926f11a64b811dc75391bbe0191b50b8fe142419f7616b3cee70229f14cd
|
||||
```
|
||||
|
||||
#### 解决问题
|
||||
|
||||
Docker 禁止我们删除一个还在被容器使用的镜像。
|
||||
|
||||
例如,当我试图删除 Docker 镜像 `b72889fa879c` 时,我只能获得一个错误提示:
|
||||
|
||||
```shell
|
||||
Error response from daemon: conflict: unable to delete b72889fa879c (must be forced) - image is being used by stopped container dde4dd285377
|
||||
```
|
||||
|
||||
这是因为这个 Docker 镜像正在被一个容器使用。
|
||||
|
||||
所以,我们来检查一个正在运行的容器:
|
||||
|
||||
```shell
|
||||
$ sudo docker ps
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
![][10]
|
||||
|
||||
注意,现在并没有正在运行的容器!!!
|
||||
|
||||
查看一下所有的容器(包含所有的正在运行和已经停止的容器):
|
||||
|
||||
```shell
|
||||
$ sudo docker pa -a
|
||||
```
|
||||
|
||||
示例输出:
|
||||
|
||||
![][11]
|
||||
|
||||
可以看到,仍然有一些已经停止的容器在使用这些镜像。
|
||||
|
||||
让我们把这些容器删除:
|
||||
|
||||
```shell
|
||||
$ sudo docker rm 12e892156219
|
||||
```
|
||||
|
||||
我们仍然使用容器 ID 来删除这些容器。
|
||||
|
||||
当我们删除了所有使用该镜像的容器之后,我们就可以删除 Docker 的镜像了。
|
||||
|
||||
例如:
|
||||
|
||||
```shell
|
||||
$ sudo docekr rmi b72889fa879c
|
||||
```
|
||||
|
||||
我们再来检查一下本机存在的镜像:
|
||||
|
||||
```shell
|
||||
$ sudo docker images
|
||||
```
|
||||
|
||||
想要知道更多的细节,请参阅本指南末尾给出的官方资源的链接或者在评论区进行留言。
|
||||
|
||||
这就是全部的教程了,希望你可以了解 Docker 的一些基础用法。
|
||||
|
||||
更多的教程马上就会到来,敬请关注。
|
||||
|
||||
---
|
||||
|
||||
via: https://www.ostechnix.com/getting-started-with-docker/
|
||||
|
||||
作者:[sk][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[zhang5788](https://github.com/zhang5788)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/sk/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ostechnix.com/wp-content/uploads/2016/04/docker-basics-720x340.png
|
||||
[2]: http://www.ostechnix.com/install-docker-ubuntu/
|
||||
[3]: https://www.ostechnix.com/install-docker-centos/
|
||||
[4]: https://hub.docker.com/
|
||||
[5]: http://www.ostechnix.com/wp-content/uploads/2016/04/Search-Docker-images.png
|
||||
[6]: http://www.ostechnix.com/wp-content/uploads/2016/04/Download-docker-images.png
|
||||
[7]: http://www.ostechnix.com/wp-content/uploads/2016/04/Docker-containers-shell.png
|
||||
[8]: http://www.ostechnix.com/wp-content/uploads/2016/04/List-running-containers.png
|
||||
[9]: http://www.ostechnix.com/wp-content/uploads/2016/04/List-docker-images.png
|
||||
[10]: http://www.ostechnix.com/wp-content/uploads/2016/04/sk@sk-_005-1-1.jpg
|
||||
[11]: http://www.ostechnix.com/wp-content/uploads/2016/04/sk@sk-_006-1.jpg
|
||||
[12]: https://ostechnix.tradepub.com/free/w_java39/prgm.cgi?a=1
|
||||
[13]: https://ostechnix.tradepub.com/free/w_pacb32/prgm.cgi?a=1
|
||||
[14]: https://ostechnix.tradepub.com/free/w_pacb31/prgm.cgi?a=1
|
||||
[15]: https://ostechnix.tradepub.com/free/w_pacb29/prgm.cgi?a=1
|
||||
[16]: https://ostechnix.tradepub.com/free/w_pacb28/prgm.cgi?a=1
|
@ -0,0 +1,75 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (chen-ni)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10961-1.html)
|
||||
[#]: subject: (When IoT systems fail: The risk of having bad IoT data)
|
||||
[#]: via: (https://www.networkworld.com/article/3396230/when-iot-systems-fail-the-risk-of-having-bad-iot-data.html)
|
||||
[#]: author: (Fredric Paul https://www.networkworld.com/author/Fredric-Paul/)
|
||||
|
||||
当物联网系统出现故障:使用低质量物联网数据的风险
|
||||
======
|
||||
|
||||
> 伴随着物联网设备使用量的增长,这些设备产生的数据可以让消费者节约巨大的开支,也给商家带来新的机遇。但是当故障不可避免地出现的时候,会发生什么呢?
|
||||
|
||||
![Oonal / Getty Images][1]
|
||||
|
||||
不管你看的是什么统计数字,很明显物联网正在走进个人和私人生活的方方面面。这种增长虽然有不少好处,但是也带来了新的风险。一个很重要的问题是,出现问题的时候谁来负责呢?
|
||||
|
||||
也许最大的问题出在基于物联网数据进行的个性化营销以及定价策略上。[保险公司长期以来致力于寻找利用物联网数据的最佳方式][2],我去年写过家庭财产保险公司是如何开始利用物联网传感器减少水灾带来的损失的。一些公司正在研究保险公司竞购消费者的可能性:这种业务基于智能家居数据所揭示的风险的高低。
|
||||
|
||||
但是最大的进步出现在汽车保险领域。许多汽车保险公司已经可以让客户在车辆上安装追踪设备,如果数据证明他们的驾驶习惯良好就可以获取保险折扣。
|
||||
|
||||
- 延伸阅读:[保险公司终于有了一个利用智能家居物联网的好办法][3]
|
||||
|
||||
### UBI 车险的崛起
|
||||
|
||||
UBI(<ruby>基于使用的保险<rt>usage-based insurance</rt></ruby>)车险是一种“按需付费”的业务,可以通过追踪速度、位置,以及其他因素来评估风险并计算车险保费。到 2020 年,预计有 [5000 万美国司机][4]会加入到 UBI 车险的项目中。
|
||||
|
||||
不出所料,保险公司对 UBI 车险青睐有加,因为 UBI 车险可以帮助他们更加精确地计算风险。事实上,[AIG 爱尔兰已经在尝试让国家向 25 岁以下的司机强制推行 UBI 车险][5]。并且,被认定为驾驶习惯良好的司机自然也很乐意节省一笔费用。当然也有反对的声音了,大多数是来自于隐私权倡导者,以及会因此支付更多费用的群体。
|
||||
|
||||
### 出了故障会发生什么?
|
||||
|
||||
但是还有一个更加令人担忧的潜在问题:当物联网设备提供的数据有错误,或者在传输过程中出了问题会发生什么?因为尽管有自动化程序、错误检查等等,还是不可避免地会偶尔发生一些故障。
|
||||
|
||||
不幸的是,这并不是一个理论上某天会给细心的司机不小心多扣几块钱保费的问题。这已经是一个会带来严重后果的现实问题。就像[保险行业仍然没有想清楚谁应该“拥有”面向客户的物联网设备产生的数据][6]一样,我们也不清楚谁将对这些数据所带来的问题负责。
|
||||
|
||||
计算机“故障”据说曾导致赫兹的出租车辆被误报为被盗(虽然在这个例子中这并不是一个严格意义上的物联网问题),并且导致无辜的租车人被逮捕并扣留。结果呢?刑事指控,多年的诉讼官司,以及舆论的指责。非常强烈的舆论指责。
|
||||
|
||||
我们非常容易想象一些类似的情况,比如说一个物联网传感器出了故障,然后报告说某辆车超速了,然而事实上并没有超速。想想为这件事打官司的麻烦吧,或者想想和你的保险公司如何争执不下。
|
||||
|
||||
(当然,这个问题还有另外一面:消费者可能会想办法篡改他们的物联网设备上的数据,以获得更低的费率或者转移事故责任。我们同样也没有可行的办法来应对*这个问题*。)
|
||||
|
||||
### 政府监管是否有必要
|
||||
|
||||
考虑到这些问题的潜在影响,以及所涉及公司对处理这些问题的无动于衷,我们似乎有理由猜想政府干预的必要性。
|
||||
|
||||
这可能是美国众议员 Bob Latta(俄亥俄州,共和党)[重新引入 SMART IOT(物联网现代应用、研究及趋势的现状)法案][7]背后的一个动机。这项由 Latta 和美国众议员 Peter Welch(佛蒙特州,民主党)领导的两党合作物联网工作组提出的[法案][8],于去年秋天通过美国众议院,但被美国参议院驳回了。美国商务部需要研究物联网行业的状况,并在两年后向美国众议院能源与商业部和美国参议院商务委员会报告。
|
||||
|
||||
Latta 在一份声明中表示,“由于预计会有数万亿美元的经济影响,我们需要考虑物联网所带来的的政策,机遇和挑战。SMART IoT 法案会让人们更容易理解美国政府在物联网政策上的做法、可以改进的地方,以及美国联邦政策如何影响尖端技术的研究和发明。”
|
||||
|
||||
这项研究受到了欢迎,但该法案甚至可能不会被通过。即便通过了,物联网在两年的等待时间里也可能会翻天覆地,让美国政府还是无法跟上。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3396230/when-iot-systems-fail-the-risk-of-having-bad-iot-data.html
|
||||
|
||||
作者:[Fredric Paul][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[chen-ni](https://github.com/chen-ni)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Fredric-Paul/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://images.idgesg.net/images/article/2018/08/cloud_connected_smart_cars_by_oonal_gettyimages-692819426_1200x800-100767788-large.jpg
|
||||
[2]: https://www.networkworld.com/article/3264655/most-insurance-carriers-not-ready-to-use-iot-data.html
|
||||
[3]: https://www.networkworld.com/article/3296706/finally-a-smart-way-for-insurers-to-leverage-iot-in-smart-homes.html
|
||||
[4]: https://www.businessinsider.com/iot-is-changing-the-auto-insurance-industry-2015-8
|
||||
[5]: https://www.iotforall.com/iot-data-is-disrupting-the-insurance-industry/
|
||||
[6]: https://www.sas.com/en_us/insights/articles/big-data/5-challenges-for-iot-in-insurance-industry.html
|
||||
[7]: https://www.multichannel.com/news/latta-re-ups-smart-iot-act
|
||||
[8]: https://latta.house.gov/uploadedfiles/smart_iot_116th.pdf
|
||||
[9]: https://www.facebook.com/NetworkWorld/
|
||||
[10]: https://www.linkedin.com/company/network-world
|
@ -0,0 +1,109 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10922-1.html)
|
||||
[#]: subject: (Zettlr – Markdown Editor for Writers and Researchers)
|
||||
[#]: via: (https://itsfoss.com/zettlr-markdown-editor/)
|
||||
[#]: author: (John Paul https://itsfoss.com/author/john/)
|
||||
|
||||
Zettlr:适合写作者和研究人员的 Markdown 编辑器
|
||||
======
|
||||
|
||||
有很多[适用于 Linux 的 Markdown 编辑器][1],并且还在继续增加。问题是,像 [Boostnote][2] 一样,大多数是为编码人员设计的,可能不会受到非技术人员的欢迎。让我们看一个想要替代 Word 和昂贵的文字处理器,适用于非技术人员的 Markdown 编辑器。我们来看看 Zettlr 吧。
|
||||
|
||||
### Zettlr Markdown 编辑器
|
||||
|
||||
![Zettlr Light Mode][3]
|
||||
|
||||
我可能在网站上提到过一两次,我更喜欢用 [Markdown][4] 写下我的所有文档。它易于学习,不会让你受困于专有文档格式。我还在我的[适合作者的开源工具列表][5]中提到了 Markdown 编辑器。
|
||||
|
||||
我用过许多 Markdown 编辑器,但是我一直有兴趣尝试新的。最近,我遇到了 Zettlr,一个开源 Markdown 编辑器。
|
||||
|
||||
[Zettlr][6] 是一位名叫 [Hendrik Erz][7] 的德国社会学家/政治理论家创建的。Hendrik 创建了 Zettlr,因为他对目前的文字处理器感到不满意。他想要可以让他“专注于写作和阅读”的编辑器。
|
||||
|
||||
在发现 Markdown 之后,他在不同的操作系统上尝试了几个 Markdown 编辑器。但它们都没有他想要的东西。[根据 Hendrik 的说法][8],“但我不得不意识到没有为高效组织大量文本而写的编辑器。大多数编辑都是由编码人员编写的,因此可以满足工程师和数学家的需求。没有为我这样的社会科学、历史或政治学的学生的编辑器。“
|
||||
|
||||
所以他决定创造自己的。2017 年 11 月,他开始编写 Zettlr。
|
||||
|
||||
![Zettlr About][9]
|
||||
|
||||
#### Zettlr 功能
|
||||
|
||||
Zettlr 有许多简洁的功能,包括:
|
||||
|
||||
* 从 [Zotero 数据库][10]导入源并在文档中引用它们
|
||||
* 使用可选的行屏蔽,让你无打扰地专注于写作
|
||||
* 支持代码高亮
|
||||
* 使用标签对信息进行排序
|
||||
* 能够为该任务设定写作目标
|
||||
* 查看一段时间的写作统计
|
||||
* 番茄钟计时器
|
||||
* 浅色/深色主题
|
||||
* 使用 [reveal.js][11] 创建演示文稿
|
||||
* 快速预览文档
|
||||
* 可以在一个项目文件夹中搜索 Markdown 文档,并用热图展示文字搜索密度。
|
||||
* 将文件导出为 HTML、PDF、ODT、DOC、reStructuredText、LaTex、TXT、Emacs ORG、[TextBundle][12] 和 Textpack
|
||||
* 将自定义 CSS 添加到你的文档
|
||||
|
||||
当我写这篇文章时,一个对话框弹出来告诉我最近发布了 [1.3.0 beta][14]。此测试版将包括几个新的主题,以及一大堆修复,新功能和改进。
|
||||
|
||||
![Zettlr Night Mode][15]
|
||||
|
||||
#### 安装 Zettlr
|
||||
|
||||
目前,唯一可安装 Zettlr 的 Linux 存储库是 [AUR][16]。如果你的 Linux 发行版不是基于 Arch 的,你可以从网站上下载 macOS、Windows、Debian 和 Fedora 的[安装程序][17]。
|
||||
|
||||
#### 对 Zettlr 的最后一点想法
|
||||
|
||||
注意:为了测试 Zettlr,我用它来写这篇文章。
|
||||
|
||||
Zettlr 有许多我希望我之前选择的编辑器 (ghostwriter) 有的简洁的功能,例如为文档设置字数目标。我也喜欢在不打开文档的情况下预览文档的功能。
|
||||
|
||||
![Zettlr Settings][18]
|
||||
|
||||
我也遇到了几个问题,但这些更多的是因为 Zettlr 与 ghostwriter 的工作方式略有不同。例如,当我尝试从网站复制引用或名称时,它会将内嵌样式粘贴到 Zettlr 中。幸运的是,它有一个“不带样式粘贴”的选项。有几次我在打字时有轻微的延迟。但那可能是因为它是一个 Electron 程序。
|
||||
|
||||
总的来说,我认为 Zettlr 是第一次使用 Markdown 用户的好选择。它有许多 Markdown 编辑器已有的功能,并为那些只使用过文字处理器的用户增加了一些功能。
|
||||
|
||||
正如 Hendrik 在 [Zettlr 网站][8]中所说的那样,“让自己摆脱文字处理器的束缚,看看你的写作过程如何通过身边的技术得到改善!”
|
||||
|
||||
如果你觉得 Zettlr 有用,请考虑支持 [Hendrik][19]。正如他在网站上所说,“它是免费的,因为我不相信激烈竞争、早逝的创业文化。我只是想帮忙。”
|
||||
|
||||
你有没有用过 Zettlr?你最喜欢的 Markdown 编辑器是什么?请在下面的评论中告诉我们。
|
||||
|
||||
如果你觉得这篇文章有趣,请在社交媒体,Hacker News 或 [Reddit][21] 上分享它。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/zettlr-markdown-editor/
|
||||
|
||||
作者:[John Paul][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/john/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://itsfoss.com/best-markdown-editors-linux/
|
||||
[2]: https://itsfoss.com/boostnote-linux-review/
|
||||
[3]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/Zettlr-light-mode.png?fit=800%2C462&ssl=1
|
||||
[4]: https://daringfireball.net/projects/markdown/
|
||||
[5]: https://itsfoss.com/open-source-tools-writers/
|
||||
[6]: https://www.zettlr.com/
|
||||
[7]: https://github.com/nathanlesage
|
||||
[8]: https://www.zettlr.com/about
|
||||
[9]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/zettlr-about.png?fit=800%2C528&ssl=1
|
||||
[10]: https://www.zotero.org/
|
||||
[11]: https://revealjs.com/#/
|
||||
[12]: http://textbundle.org/
|
||||
[13]: https://itsfoss.com/great-little-book-shelf-review/
|
||||
[14]: https://github.com/Zettlr/Zettlr/releases/tag/v1.3.0-beta
|
||||
[15]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/Zettlr-night-mode.png?fit=800%2C469&ssl=1
|
||||
[16]: https://aur.archlinux.org/packages/zettlr-bin/
|
||||
[17]: https://www.zettlr.com/download
|
||||
[18]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/zettlr-settings.png?fit=800%2C353&ssl=1
|
||||
[19]: https://www.zettlr.com/supporters
|
||||
[21]: http://reddit.com/r/linuxusersgroup
|
@ -0,0 +1,62 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (chen-ni)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10934-1.html)
|
||||
[#]: subject: (French IT giant Atos enters the edge-computing business)
|
||||
[#]: via: (https://www.networkworld.com/article/3397139/atos-is-the-latest-to-enter-the-edge-computing-business.html)
|
||||
[#]: author: (Andy Patrizio https://www.networkworld.com/author/Andy-Patrizio/)
|
||||
|
||||
法国 IT 巨头 Atos 进军边缘计算
|
||||
======
|
||||
|
||||
> Atos 另辟蹊径,通过一种只有行李箱大小的设备 BullSequana Edge 进军边缘计算。
|
||||
|
||||
![iStock][1]
|
||||
|
||||
法国 IT 巨头 Atos 是最近才开展边缘计算业务的,他们的产品是一个叫做 BullSequana Edge 的小型设备。和竞争对手们的集装箱大小的设备不同(比如说 Vapor IO 和 Schneider Electronics 的产品),Atos 的边缘设备完全可以被放进衣柜里。
|
||||
|
||||
Atos 表示,他们的这个设备使用人工智能应用提供快速响应,适合需要快速响应的领域比如生产 4.0、自动驾驶汽车、健康管理,以及零售业和机场的安保系统。在这些领域,数据需要在边缘进行实时处理和分析。
|
||||
|
||||
[延伸阅读:[什么是边缘计算?][2] 以及 [边缘网络和物联网如何重新定义数据中心][3]]
|
||||
|
||||
BullSequana Edge 可以作为独立的基础设施单独采购,也可以和 Atos 的边缘软件捆绑采购,并且这个软件还是非常出色的。Atos 表示 BullSequana Edge 主要支持三种使用场景:
|
||||
|
||||
* AI(人工智能):Atos 的边缘计算机视觉软件为监控摄像头提供先进的特征抽取和分析技术,包括人像、人脸、行为等特征。这些分析可以支持系统做出自动化响应。
|
||||
* 大数据:Atos 边缘数据分析系统通过预测性和规范性的解决方案,帮助机构优化商业模型。它使用数据湖的功能,确保数据的可信度和可用性。
|
||||
* 容器:Atos 边缘数据容器(EDC)是一种一体化容器解决方案。它可以作为一个去中心化的 IT 系统在边缘运行,并且可以在没有数据中心的环境下自动运行,而不需要现场操作。
|
||||
|
||||
由于体积小,BullSequana Edge 并不具备很强的处理能力。它装载一个 16 核的 Intel Xeon 中央处理器,可以装备最多两枚英伟达 Tesla T4 图形处理器或者是 FPGA(现场可编程门阵列)。Atos 表示,这就足够让复杂的 AI 模型在边缘进行低延迟的运行了。
|
||||
|
||||
考虑到数据的敏感性,BullSequana Edge 同时装备了一个入侵感应器,用来在遭遇物理入侵的时候禁用机器。
|
||||
|
||||
虽然大多数边缘设备都被安放在信号塔附近,但是考虑到边缘系统可能被安放在任何地方,BullSequana Edge 还支持通过无线电、全球移动通信系统(GSM),或者 Wi-Fi 来进行通信。
|
||||
|
||||
Atos 在美国也许不是一个家喻户晓的名字,但是在欧洲它可以和 IBM 相提并论,并且在过去的十年里已经收购了诸如 Bull SA、施乐 IT 外包以及西门子 IT 等 IT 巨头们。
|
||||
|
||||
关于边缘网络的延伸阅读:
|
||||
|
||||
* [边缘网络和物联网如何重新定义数据中心][3]
|
||||
* [边缘计算的最佳实践][4]
|
||||
* [边缘计算如何提升物联网安全][5]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3397139/atos-is-the-latest-to-enter-the-edge-computing-business.html
|
||||
|
||||
作者:[Andy Patrizio][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[chen-ni](https://github.com/chen-ni)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Andy-Patrizio/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://images.idgesg.net/images/article/2019/01/huawei-18501-edge-gartner-100786331-large.jpg
|
||||
[2]: https://www.networkworld.com/article/3224893/internet-of-things/what-is-edge-computing-and-how-it-s-changing-the-network.html
|
||||
[3]: https://www.networkworld.com/article/3291790/data-center/how-edge-networking-and-iot-will-reshape-data-centers.html
|
||||
[4]: https://www.networkworld.com/article/3331978/lan-wan/edge-computing-best-practices.html
|
||||
[5]: https://www.networkworld.com/article/3331905/internet-of-things/how-edge-computing-can-help-secure-the-iot.html
|
||||
[6]: https://www.facebook.com/NetworkWorld/
|
||||
[7]: https://www.linkedin.com/company/network-world
|
201
published/20190522 Securing telnet connections with stunnel.md
Normal file
201
published/20190522 Securing telnet connections with stunnel.md
Normal file
@ -0,0 +1,201 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10945-1.html)
|
||||
[#]: subject: (Securing telnet connections with stunnel)
|
||||
[#]: via: (https://fedoramagazine.org/securing-telnet-connections-with-stunnel/)
|
||||
[#]: author: (Curt Warfield https://fedoramagazine.org/author/rcurtiswarfield/)
|
||||
|
||||
使用 stunnel 保护 telnet 连接
|
||||
======
|
||||
|
||||
![][1]
|
||||
|
||||
Telnet 是一种客户端-服务端协议,通过 TCP 的 23 端口连接到远程服务器。Telnet 并不加密数据,因此它被认为是不安全的,因为数据是以明文形式发送的,所以密码很容易被嗅探。但是,仍有老旧系统需要使用它。这就是用到 **stunnel** 的地方。
|
||||
|
||||
stunnel 旨在为使用不安全连接协议的程序增加 SSL 加密。本文将以 telnet 为例介绍如何使用它。
|
||||
|
||||
### 服务端安装
|
||||
|
||||
[使用 sudo][2] 安装 stunnel 以及 telnet 的服务端和客户端:
|
||||
|
||||
```
|
||||
sudo dnf -y install stunnel telnet-server telnet
|
||||
```
|
||||
|
||||
添加防火墙规则,在提示时输入你的密码:
|
||||
|
||||
```
|
||||
firewall-cmd --add-service=telnet --perm
|
||||
firewall-cmd --reload
|
||||
```
|
||||
|
||||
接下来,生成 RSA 私钥和 SSL 证书:
|
||||
|
||||
```
|
||||
openssl genrsa 2048 > stunnel.key
|
||||
openssl req -new -key stunnel.key -x509 -days 90 -out stunnel.crt
|
||||
```
|
||||
|
||||
系统将一次提示你输入以下信息。当询问 `Common Name` 时,你必须输入正确的主机名或 IP 地址,但是你可以按回车键跳过其他所有内容。
|
||||
|
||||
```
|
||||
You are about to be asked to enter information that will be
|
||||
incorporated into your certificate request.
|
||||
What you are about to enter is what is called a Distinguished Name or a DN.
|
||||
There are quite a few fields but you can leave some blank
|
||||
For some fields there will be a default value,
|
||||
If you enter '.', the field will be left blank.
|
||||
-----
|
||||
Country Name (2 letter code) [XX]:
|
||||
State or Province Name (full name) []:
|
||||
Locality Name (eg, city) [Default City]:
|
||||
Organization Name (eg, company) [Default Company Ltd]:
|
||||
Organizational Unit Name (eg, section) []:
|
||||
Common Name (eg, your name or your server's hostname) []:
|
||||
Email Address []
|
||||
```
|
||||
|
||||
将 RSA 密钥和 SSL 证书合并到单个 `.pem` 文件中,并将其复制到 SSL 证书目录:
|
||||
|
||||
```
|
||||
cat stunnel.crt stunnel.key > stunnel.pem
|
||||
sudo cp stunnel.pem /etc/pki/tls/certs/
|
||||
```
|
||||
|
||||
现在可以定义服务和用于加密连接的端口了。选择尚未使用的端口。此例使用 450 端口进行隧道传输 telnet。编辑或创建 `/etc/stunnel/telnet.conf`:
|
||||
|
||||
```
|
||||
cert = /etc/pki/tls/certs/stunnel.pem
|
||||
sslVersion = TLSv1
|
||||
chroot = /var/run/stunnel
|
||||
setuid = nobody
|
||||
setgid = nobody
|
||||
pid = /stunnel.pid
|
||||
socket = l:TCP_NODELAY=1
|
||||
socket = r:TCP_NODELAY=1
|
||||
[telnet]
|
||||
accept = 450
|
||||
connect = 23
|
||||
```
|
||||
|
||||
`accept` 选项是服务器将监听传入 telnet 请求的接口。`connect` 选项是 telnet 服务器的内部监听接口。
|
||||
|
||||
接下来,创建一个 systemd 单元文件的副本来覆盖原来的版本:
|
||||
|
||||
```
|
||||
sudo cp /usr/lib/systemd/system/stunnel.service /etc/systemd/system
|
||||
```
|
||||
|
||||
编辑 `/etc/systemd/system/stunnel.service` 来添加两行。这些行在启动时为服务创建 chroot 监狱。
|
||||
|
||||
```
|
||||
[Unit]
|
||||
Description=TLS tunnel for network daemons
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/stunnel
|
||||
Type=forking
|
||||
PrivateTmp=true
|
||||
ExecStartPre=-/usr/bin/mkdir /var/run/stunnel
|
||||
ExecStartPre=/usr/bin/chown -R nobody:nobody /var/run/stunnel
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
接下来,配置 SELinux 以在你刚刚指定的新端口上监听 telnet:
|
||||
|
||||
```
|
||||
sudo semanage port -a -t telnetd_port_t -p tcp 450
|
||||
```
|
||||
|
||||
最后,添加新的防火墙规则:
|
||||
|
||||
```
|
||||
firewall-cmd --add-port=450/tcp --perm
|
||||
firewall-cmd --reload
|
||||
```
|
||||
|
||||
现在你可以启用并启动 telnet 和 stunnel。
|
||||
|
||||
```
|
||||
systemctl enable telnet.socket stunnel@telnet.service --now
|
||||
```
|
||||
|
||||
要注意 `systemctl` 命令是有顺序的。systemd 和 stunnel 包默认提供额外的[模板单元文件][3]。该模板允许你将 stunnel 的多个配置文件放到 `/etc/stunnel` 中,并使用文件名启动该服务。例如,如果你有一个 `foobar.conf` 文件,那么可以使用 `systemctl start stunnel@foobar.service` 启动该 stunnel 实例,而无需自己编写任何单元文件。
|
||||
|
||||
如果需要,可以将此 stunnel 模板服务设置为在启动时启动:
|
||||
|
||||
```
|
||||
systemctl enable stunnel@telnet.service
|
||||
```
|
||||
|
||||
### 客户端安装
|
||||
|
||||
本文的这部分假设你在客户端系统上以普通用户([拥有 sudo 权限][2])身份登录。安装 stunnel 和 telnet 客户端:
|
||||
|
||||
```
|
||||
dnf -y install stunnel telnet
|
||||
```
|
||||
|
||||
将 `stunnel.pem` 从远程服务器复制到客户端的 `/etc/pki/tls/certs` 目录。在此例中,远程 telnet 服务器的 IP 地址为 `192.168.1.143`。
|
||||
|
||||
```
|
||||
sudo scp myuser@192.168.1.143:/etc/pki/tls/certs/stunnel.pem
|
||||
/etc/pki/tls/certs/
|
||||
```
|
||||
|
||||
创建 `/etc/stunnel/telnet.conf`:
|
||||
|
||||
```
|
||||
cert = /etc/pki/tls/certs/stunnel.pem
|
||||
client=yes
|
||||
[telnet]
|
||||
accept=450
|
||||
connect=192.168.1.143:450
|
||||
```
|
||||
|
||||
`accept` 选项是用于 telnet 会话的端口。`connect` 选项是你远程服务器的 IP 地址以及监听的端口。
|
||||
|
||||
接下来,启用并启动 stunnel:
|
||||
|
||||
```
|
||||
systemctl enable stunnel@telnet.service --now
|
||||
```
|
||||
|
||||
测试你的连接。由于有一条已建立的连接,你会 `telnet` 到 `localhost` 而不是远程 telnet 服务器的主机名或者 IP 地址。
|
||||
|
||||
```
|
||||
[user@client ~]$ telnet localhost 450
|
||||
Trying ::1...
|
||||
telnet: connect to address ::1: Connection refused
|
||||
Trying 127.0.0.1...
|
||||
Connected to localhost.
|
||||
Escape character is '^]'.
|
||||
|
||||
Kernel 5.0.9-301.fc30.x86_64 on an x86_64 (0)
|
||||
server login: myuser
|
||||
Password: XXXXXXX
|
||||
Last login: Sun May 5 14:28:22 from localhost
|
||||
[myuser@server ~]$
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://fedoramagazine.org/securing-telnet-connections-with-stunnel/
|
||||
|
||||
作者:[Curt Warfield][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://fedoramagazine.org/author/rcurtiswarfield/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://fedoramagazine.org/wp-content/uploads/2019/05/stunnel-816x345.jpg
|
||||
[2]: https://fedoramagazine.org/howto-use-sudo/
|
||||
[3]: https://fedoramagazine.org/systemd-template-unit-files/
|
119
published/20190525 4 Ways to Run Linux Commands in Windows.md
Normal file
119
published/20190525 4 Ways to Run Linux Commands in Windows.md
Normal file
@ -0,0 +1,119 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10935-1.html)
|
||||
[#]: subject: (4 Ways to Run Linux Commands in Windows)
|
||||
[#]: via: (https://itsfoss.com/run-linux-commands-in-windows/)
|
||||
[#]: author: (Abhishek Prakash https://itsfoss.com/author/abhishek/)
|
||||
|
||||
在 Windows 中运行 Linux 命令的 4 种方法
|
||||
======
|
||||
|
||||
> 想要使用 Linux 命令,但又不想离开 Windows ?以下是在 Windows 中运行 Linux bash 命令的几种方法。
|
||||
|
||||
如果你正在课程中正在学习 shell 脚本,那么需要使用 Linux 命令来练习命令和脚本。
|
||||
|
||||
你的学校实验室可能安装了 Linux,但是你自己没有安装了 [Linux 的笔记本电脑][1],而是像其他人一样的 Windows 计算机。你的作业需要运行 Linux 命令,你或许想知道如何在 Windows 上运行 Bash 命令和脚本。
|
||||
|
||||
你可以[在双启动模式下同时安装 Windows 和 Linux][2]。此方法能让你在启动计算机时选择 Linux 或 Windows。但是,为了运行 Linux 命令而使用单独分区的麻烦可能不适合所有人。
|
||||
|
||||
你也可以[使用在线 Linux 终端][3],但你的作业无法保存。
|
||||
|
||||
好消息是,有几种方法可以在 Windows 中运行 Linux 命令,就像其他常规应用一样。不是很酷吗?
|
||||
|
||||
### 在 Windows 中使用 Linux 命令
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/04/093809hlz2tblfzt7mbwwl.jpg)
|
||||
|
||||
作为一个热心的 Linux 用户和推广者,我希望看到越来越多的人使用“真正的” Linux,但我知道有时候,这不是优先考虑的问题。如果你只是想练习 Linux 来通过考试,可以使用这些方法之一在 Windows 上运行 Bash 命令。
|
||||
|
||||
#### 1、在 Windows 10 上使用 Linux Bash Shell
|
||||
|
||||
你是否知道可以在 Windows 10 中运行 Linux 发行版? [Windows 的 Linux 子系统 (WSL)][5] 能让你在 Windows 中运行 Linux。即将推出的 WSL 版本将在 Windows 内部使用真正 Linux 内核。
|
||||
|
||||
此 WSL 也称为 Bash on Windows,它作为一个常规的 Windows 应用运行,并提供了一个命令行模式的 Linux 发行版。不要害怕命令行模式,因为你的目的是运行 Linux 命令。这就是你所需要的。
|
||||
|
||||
![Ubuntu Linux inside Windows][6]
|
||||
|
||||
你可以在 Windows 应用商店中找到一些流行的 Linux 发行版,如 Ubuntu、Kali Linux、openSUSE 等。你只需像任何其他 Windows 应用一样下载和安装它。安装后,你可以运行所需的所有 Linux 命令。
|
||||
|
||||
![Linux distributions in Windows 10 Store][8]
|
||||
|
||||
请参考教程:[在 Windows 上安装 Linux bash shell][9]。
|
||||
|
||||
#### 2、使用 Git Bash 在 Windows 上运行 Bash 命令
|
||||
|
||||
你可能知道 [Git][10] 是什么。它是由 [Linux 创建者 Linus Torvalds][11] 开发的版本控制系统。
|
||||
|
||||
[Git for Windows][12] 是一组工具,能让你在命令行和图形界面中使用 Git。Git for Windows 中包含的工具之一是 Git Bash。
|
||||
|
||||
Git Bash 为 Git 命令行提供了仿真层。除了 Git 命令,Git Bash 还支持许多 Bash 程序,如 `ssh`、`scp`、`cat`、`find` 等。
|
||||
|
||||
![Git Bash][13]
|
||||
|
||||
换句话说,你可以使用 Git Bash 运行许多常见的 Linux/Bash 命令。
|
||||
|
||||
你可以从其网站免费下载和安装 Git for Windows 工具来在 Windows 中安装 Git Bash。
|
||||
|
||||
- [下载 Git for Windows][12]
|
||||
|
||||
#### 3、使用 Cygwin 在 Windows 中使用 Linux 命令
|
||||
|
||||
如果要在 Windows 中运行 Linux 命令,那么 Cygwin 是一个推荐的工具。Cygwin 创建于 1995 年,旨在提供一个原生运行于 Windows 中的 POSIX 兼容环境。Cygwin 是由 Red Hat 员工和许多其他志愿者维护的自由开源软件。
|
||||
|
||||
二十年来,Windows 用户使用 Cygwin 来运行和练习 Linux/Bash 命令。十多年前,我甚至用 Cygwin 来学习 Linux 命令。
|
||||
|
||||
![Cygwin][14]
|
||||
|
||||
你可以从下面的官方网站下载 Cygwin。我还建议你参考这个 [Cygwin 备忘录][15]来开始使用。
|
||||
|
||||
- [下载 Cygwin][16]
|
||||
|
||||
#### 4、在虚拟机中使用 Linux
|
||||
|
||||
另一种方法是使用虚拟化软件并在其中安装 Linux。这样,你可以在 Windows 中安装 Linux 发行版(带有图形界面)并像常规 Windows 应用一样运行它。
|
||||
|
||||
这种方法要求你的系统有大的内存,至少 4GB ,但如果你有超过 8GB 的内存那么更好。这里的好处是你可以真实地使用桌面 Linux。如果你喜欢这个界面,那么你可能会在以后决定[切换到 Linux][17]。
|
||||
|
||||
![Ubuntu Running in Virtual Machine Inside Windows][18]
|
||||
|
||||
有两种流行的工具可在 Windows 上创建虚拟机,它们是 Oracle VirtualBox 和 VMware Workstation Player。你可以使用两者中的任何一个。就个人而言,我更喜欢 VirtualBox。
|
||||
|
||||
你可以按照[本教程学习如何在 VirtualBox 中安装 Linux][20]。
|
||||
|
||||
### 总结
|
||||
|
||||
运行 Linux 命令的最佳方法是使用 Linux。当选择不安装 Linux 时,这些工具能让你在 Windows 上运行 Linux 命令。都试试看,看哪种适合你。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/run-linux-commands-in-windows/
|
||||
|
||||
作者:[Abhishek Prakash][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/abhishek/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://itsfoss.com/get-linux-laptops/
|
||||
[2]: https://itsfoss.com/guide-install-linux-mint-16-dual-boot-windows/
|
||||
[3]: https://itsfoss.com/online-linux-terminals/
|
||||
[4]: https://i0.wp.com/itsfoss.com/wp-content/uploads/2019/05/run-linux-commands-in-windows.png?resize=800%2C450&ssl=1
|
||||
[5]: https://itsfoss.com/bash-on-windows/
|
||||
[6]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2016/08/install-ubuntu-windows-10-linux-subsystem-10.jpeg?resize=800%2C268&ssl=1
|
||||
[8]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2016/08/install-ubuntu-windows-10-linux-subsystem-4.jpeg?resize=800%2C632&ssl=1
|
||||
[9]: https://itsfoss.com/install-bash-on-windows/
|
||||
[10]: https://itsfoss.com/basic-git-commands-cheat-sheet/
|
||||
[11]: https://itsfoss.com/linus-torvalds-facts/
|
||||
[12]: https://gitforwindows.org/
|
||||
[13]: https://i2.wp.com/itsfoss.com/wp-content/uploads/2019/05/git-bash.png?ssl=1
|
||||
[14]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/cygwin-shell.jpg?ssl=1
|
||||
[15]: http://www.voxforge.org/home/docs/cygwin-cheat-sheet
|
||||
[16]: https://www.cygwin.com/
|
||||
[17]: https://itsfoss.com/reasons-switch-linux-windows-xp/
|
||||
[18]: https://i1.wp.com/itsfoss.com/wp-content/uploads/2019/05/ubuntu-running-in-virtual-machine-inside-windows.jpeg?resize=800%2C450&ssl=1
|
||||
[20]: https://itsfoss.com/install-linux-in-virtualbox/
|
471
published/20190527 20- FFmpeg Commands For Beginners.md
Normal file
471
published/20190527 20- FFmpeg Commands For Beginners.md
Normal file
@ -0,0 +1,471 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (robsean)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10932-1.html)
|
||||
[#]: subject: (20+ FFmpeg Commands For Beginners)
|
||||
[#]: via: (https://www.ostechnix.com/20-ffmpeg-commands-beginners/)
|
||||
[#]: author: (sk https://www.ostechnix.com/author/sk/)
|
||||
|
||||
给初学者的 20 多个 FFmpeg 命令示例
|
||||
======
|
||||
|
||||
![FFmpeg Commands](https://img.linux.net.cn/data/attachment/album/201906/03/011553xu323dzu40pb03bx.jpg)
|
||||
|
||||
在这个指南中,我将用示例来阐明如何使用 FFmpeg 媒体框架来做各种各样的音频、视频转码和转换的操作。我已经为初学者汇集了最常用的 20 多个 FFmpeg 命令,我将不时地添加更多的示例来保持更新这个指南。请给这个指南加书签,以后回来检查更新。让我们开始吧,如果你还没有在你的 Linux 系统中安装 FFmpeg,参考下面的指南。
|
||||
|
||||
* [在 Linux 中安装 FFmpeg][2]
|
||||
|
||||
### 针对初学者的 20 多个 FFmpeg 命令
|
||||
|
||||
FFmpeg 命令的典型语法是:
|
||||
|
||||
```
|
||||
ffmpeg [全局选项] {[输入文件选项] -i 输入_url_地址} ...
|
||||
{[输出文件选项] 输出_url_地址} ...
|
||||
```
|
||||
|
||||
现在我们将查看一些重要的和有用的 FFmpeg 命令。
|
||||
|
||||
#### 1、获取音频/视频文件信息
|
||||
|
||||
为显示你的媒体文件细节,运行:
|
||||
|
||||
```
|
||||
$ ffmpeg -i video.mp4
|
||||
```
|
||||
|
||||
样本输出:
|
||||
|
||||
```
|
||||
ffmpeg version n4.1.3 Copyright (c) 2000-2019 the FFmpeg developers
|
||||
built with gcc 8.2.1 (GCC) 20181127
|
||||
configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3
|
||||
libavutil 56. 22.100 / 56. 22.100
|
||||
libavcodec 58. 35.100 / 58. 35.100
|
||||
libavformat 58. 20.100 / 58. 20.100
|
||||
libavdevice 58. 5.100 / 58. 5.100
|
||||
libavfilter 7. 40.101 / 7. 40.101
|
||||
libswscale 5. 3.100 / 5. 3.100
|
||||
libswresample 3. 3.100 / 3. 3.100
|
||||
libpostproc 55. 3.100 / 55. 3.100
|
||||
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'video.mp4':
|
||||
Metadata:
|
||||
major_brand : isom
|
||||
minor_version : 512
|
||||
compatible_brands: isomiso2avc1mp41
|
||||
encoder : Lavf58.20.100
|
||||
Duration: 00:00:28.79, start: 0.000000, bitrate: 454 kb/s
|
||||
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, smpte170m/bt470bg/smpte170m), 1920x1080 [SAR 1:1 DAR 16:9], 318 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default)
|
||||
Metadata:
|
||||
handler_name : ISO Media file produced by Google Inc. Created on: 04/08/2019.
|
||||
Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default)
|
||||
Metadata:
|
||||
handler_name : ISO Media file produced by Google Inc. Created on: 04/08/2019.
|
||||
At least one output file must be specified
|
||||
```
|
||||
|
||||
如你在上面的输出中看到的,FFmpeg 显示该媒体文件信息,以及 FFmpeg 细节,例如版本、配置细节、版权标记、构建参数和库选项等等。
|
||||
|
||||
如果你不想看 FFmpeg 标语和其它细节,而仅仅想看媒体文件信息,使用 `-hide_banner` 标志,像下面。
|
||||
|
||||
```
|
||||
$ ffmpeg -i video.mp4 -hide_banner
|
||||
```
|
||||
|
||||
样本输出:
|
||||
|
||||
![][3]
|
||||
|
||||
*使用 FFMpeg 查看音频、视频文件信息。*
|
||||
|
||||
看见了吗?现在,它仅显示媒体文件细节。
|
||||
|
||||
|
||||
#### 2、转换视频文件到不同的格式
|
||||
|
||||
FFmpeg 是强有力的音频和视频转换器,因此,它能在不同格式之间转换媒体文件。举个例子,要转换 mp4 文件到 avi 文件,运行:
|
||||
|
||||
```
|
||||
$ ffmpeg -i video.mp4 video.avi
|
||||
```
|
||||
|
||||
类似地,你可以转换媒体文件到你选择的任何格式。
|
||||
|
||||
例如,为转换 YouTube flv 格式视频为 mpeg 格式,运行:
|
||||
|
||||
```
|
||||
$ ffmpeg -i video.flv video.mpeg
|
||||
```
|
||||
|
||||
如果你想维持你的源视频文件的质量,使用 `-qscale 0` 参数:
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.webm -qscale 0 output.mp4
|
||||
```
|
||||
|
||||
为检查 FFmpeg 的支持格式的列表,运行:
|
||||
|
||||
```
|
||||
$ ffmpeg -formats
|
||||
```
|
||||
|
||||
#### 3、转换视频文件到音频文件
|
||||
|
||||
我转换一个视频文件到音频文件,只需具体指明输出格式,像 .mp3,或 .ogg,或其它任意音频格式。
|
||||
|
||||
上面的命令将转换 input.mp4 视频文件到 output.mp3 音频文件。
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -vn output.mp3
|
||||
```
|
||||
|
||||
此外,你也可以对输出文件使用各种各样的音频转换编码选项,像下面演示。
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -vn -ar 44100 -ac 2 -ab 320 -f mp3 output.mp3
|
||||
```
|
||||
|
||||
在这里,
|
||||
|
||||
* `-vn` – 表明我们已经在输出文件中禁用视频录制。
|
||||
* `-ar` – 设置输出文件的音频频率。通常使用的值是22050 Hz、44100 Hz、48000 Hz。
|
||||
* `-ac` – 设置音频通道的数目。
|
||||
* `-ab` – 表明音频比特率。
|
||||
* `-f` – 输出文件格式。在我们的实例中,它是 mp3 格式。
|
||||
|
||||
#### 4、更改视频文件的分辨率
|
||||
|
||||
如果你想设置一个视频文件为指定的分辨率,你可以使用下面的命令:
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -filter:v scale=1280:720 -c:a copy output.mp4
|
||||
```
|
||||
|
||||
或,
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -s 1280x720 -c:a copy output.mp4
|
||||
```
|
||||
|
||||
上面的命令将设置所给定视频文件的分辨率到 1280×720。
|
||||
|
||||
类似地,为转换上面的文件到 640×480 大小,运行:
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -filter:v scale=640:480 -c:a copy output.mp4
|
||||
```
|
||||
|
||||
或者,
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -s 640x480 -c:a copy output.mp4
|
||||
```
|
||||
|
||||
这个技巧将帮助你缩放你的视频文件到较小的显示设备上,例如平板电脑和手机。
|
||||
|
||||
#### 5、压缩视频文件
|
||||
|
||||
减小媒体文件的大小到较小来节省硬件的空间总是一个好主意.
|
||||
|
||||
下面的命令将压缩并减少输出文件的大小。
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -vf scale=1280:-1 -c:v libx264 -preset veryslow -crf 24 output.mp4
|
||||
```
|
||||
|
||||
请注意,如果你尝试减小视频文件的大小,你将损失视频质量。如果 24 太有侵略性,你可以降低 `-crf` 值到或更低值。
|
||||
|
||||
你也可以通过下面的选项来转换编码音频降低比特率,使其有立体声感,从而减小大小。
|
||||
|
||||
```
|
||||
-ac 2 -c:a aac -strict -2 -b:a 128k
|
||||
```
|
||||
|
||||
#### 6、压缩音频文件
|
||||
|
||||
正像压缩视频文件一样,为节省一些磁盘空间,你也可以使用 `-ab` 标志压缩音频文件。
|
||||
|
||||
例如,你有一个 320 kbps 比特率的音频文件。你想通过更改比特率到任意较低的值来压缩它,像下面。
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp3 -ab 128 output.mp3
|
||||
```
|
||||
|
||||
各种各样可用的音频比特率列表是:
|
||||
|
||||
1. 96kbps
|
||||
2. 112kbps
|
||||
3. 128kbps
|
||||
4. 160kbps
|
||||
5. 192kbps
|
||||
6. 256kbps
|
||||
7. 320kbps
|
||||
|
||||
#### 7、从一个视频文件移除音频流
|
||||
|
||||
如果你不想要一个视频文件中的音频,使用 `-an` 标志。
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -an output.mp4
|
||||
```
|
||||
|
||||
在这里,`-an` 表示没有音频录制。
|
||||
|
||||
上面的命令会撤销所有音频相关的标志,因为我们不要来自 input.mp4 的音频。
|
||||
|
||||
#### 8、从一个媒体文件移除视频流
|
||||
|
||||
类似地,如果你不想要视频流,你可以使用 `-vn` 标志从媒体文件中简单地移除它。`-vn` 代表没有视频录制。换句话说,这个命令转换所给定媒体文件为音频文件。
|
||||
|
||||
下面的命令将从所给定媒体文件中移除视频。
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -vn output.mp3
|
||||
```
|
||||
|
||||
你也可以使用 `-ab` 标志来指出输出文件的比特率,如下面的示例所示。
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -vn -ab 320 output.mp3
|
||||
```
|
||||
|
||||
#### 9、从视频中提取图像
|
||||
|
||||
FFmpeg 的另一个有用的特色是我们可以从一个视频文件中轻松地提取图像。如果你想从一个视频文件中创建一个相册,这可能是非常有用的。
|
||||
|
||||
为从一个视频文件中提取图像,使用下面的命令:
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -r 1 -f image2 image-%2d.png
|
||||
```
|
||||
|
||||
在这里,
|
||||
|
||||
* `-r` – 设置帧速度。即,每秒提取帧到图像的数字。默认值是 25。
|
||||
* `-f` – 表示输出格式,即,在我们的实例中是图像。
|
||||
* `image-%2d.png` – 表明我们如何想命名提取的图像。在这个实例中,命名应该像这样image-01.png、image-02.png、image-03.png 等等开始。如果你使用 `%3d`,那么图像的命名像 image-001.png、image-002.png 等等开始。
|
||||
|
||||
#### 10、裁剪视频
|
||||
|
||||
FFMpeg 允许以我们选择的任何范围裁剪一个给定的媒体文件。
|
||||
|
||||
裁剪一个视频文件的语法如下给定:
|
||||
|
||||
```
|
||||
ffmpeg -i input.mp4 -filter:v "crop=w:h:x:y" output.mp4
|
||||
```
|
||||
|
||||
在这里,
|
||||
|
||||
* `input.mp4` – 源视频文件。
|
||||
* `-filter:v` – 表示视频过滤器。
|
||||
* `crop` – 表示裁剪过滤器。
|
||||
* `w` – 我们想自源视频中裁剪的矩形的宽度。
|
||||
* `h` – 矩形的高度。
|
||||
* `x` – 我们想自源视频中裁剪的矩形的 x 坐标 。
|
||||
* `y` – 矩形的 y 坐标。
|
||||
|
||||
比如说你想要一个来自视频的位置 (200,150),且具有 640 像素宽度和 480 像素高度的视频,命令应该是:
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -filter:v "crop=640:480:200:150" output.mp4
|
||||
```
|
||||
|
||||
请注意,剪切视频将影响质量。除非必要,请勿剪切。
|
||||
|
||||
#### 11、转换一个视频的具体的部分
|
||||
|
||||
有时,你可能想仅转换视频文件的一个具体的部分到不同的格式。以示例说明,下面的命令将转换所给定视频input.mp4 文件的开始 10 秒到视频 .avi 格式。
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -t 10 output.avi
|
||||
```
|
||||
|
||||
在这里,我们以秒具体说明时间。此外,以 `hh.mm.ss` 格式具体说明时间也是可以的。
|
||||
|
||||
#### 12、设置视频的屏幕高宽比
|
||||
|
||||
你可以使用 `-aspect` 标志设置一个视频文件的屏幕高宽比,像下面。
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -aspect 16:9 output.mp4
|
||||
```
|
||||
|
||||
通常使用的高宽比是:
|
||||
|
||||
* 16:9
|
||||
* 4:3
|
||||
* 16:10
|
||||
* 5:4
|
||||
* 2:21:1
|
||||
* 2:35:1
|
||||
* 2:39:1
|
||||
|
||||
#### 13、添加海报图像到音频文件
|
||||
|
||||
你可以添加海报图像到你的文件,以便图像将在播放音频文件时显示。这对托管在视频托管主机或共享网站中的音频文件是有用的。
|
||||
|
||||
```
|
||||
$ ffmpeg -loop 1 -i inputimage.jpg -i inputaudio.mp3 -c:v libx264 -c:a aac -strict experimental -b:a 192k -shortest output.mp4
|
||||
```
|
||||
|
||||
#### 14、使用开始和停止时间剪下一段媒体文件
|
||||
|
||||
可以使用开始和停止时间来剪下一段视频为小段剪辑,我们可以使用下面的命令。
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -ss 00:00:50 -codec copy -t 50 output.mp4
|
||||
```
|
||||
|
||||
在这里,
|
||||
|
||||
* `–s` – 表示视频剪辑的开始时间。在我们的示例中,开始时间是第 50 秒。
|
||||
* `-t` – 表示总的持续时间。
|
||||
|
||||
当你想使用开始和结束时间从一个音频或视频文件剪切一部分时,它是非常有用的。
|
||||
|
||||
类似地,我们可以像下面剪下音频。
|
||||
|
||||
```
|
||||
$ ffmpeg -i audio.mp3 -ss 00:01:54 -to 00:06:53 -c copy output.mp3
|
||||
```
|
||||
|
||||
#### 15、切分视频文件为多个部分
|
||||
|
||||
一些网站将仅允许你上传具体指定大小的视频。在这样的情况下,你可以切分大的视频文件到多个较小的部分,像下面。
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -t 00:00:30 -c copy part1.mp4 -ss 00:00:30 -codec copy part2.mp4
|
||||
```
|
||||
|
||||
在这里,
|
||||
|
||||
* `-t 00:00:30` 表示从视频的开始到视频的第 30 秒创建一部分视频。
|
||||
* `-ss 00:00:30` 为视频的下一部分显示开始时间戳。它意味着第 2 部分将从第 30 秒开始,并将持续到原始视频文件的结尾。
|
||||
|
||||
#### 16、接合或合并多个视频部分到一个
|
||||
|
||||
FFmpeg 也可以接合多个视频部分,并创建一个单个视频文件。
|
||||
|
||||
创建包含你想接合文件的准确的路径的 `join.txt`。所有的文件都应该是相同的格式(相同的编码格式)。所有文件的路径应该逐个列出,像下面。
|
||||
|
||||
```
|
||||
file /home/sk/myvideos/part1.mp4
|
||||
file /home/sk/myvideos/part2.mp4
|
||||
file /home/sk/myvideos/part3.mp4
|
||||
file /home/sk/myvideos/part4.mp4
|
||||
```
|
||||
|
||||
现在,接合所有文件,使用命令:
|
||||
|
||||
```
|
||||
$ ffmpeg -f concat -i join.txt -c copy output.mp4
|
||||
```
|
||||
|
||||
如果你得到一些像下面的错误;
|
||||
|
||||
```
|
||||
[concat @ 0x555fed174cc0] Unsafe file name '/path/to/mp4'
|
||||
join.txt: Operation not permitted
|
||||
```
|
||||
|
||||
添加 `-safe 0` :
|
||||
|
||||
```
|
||||
$ ffmpeg -f concat -safe 0 -i join.txt -c copy output.mp4
|
||||
```
|
||||
|
||||
上面的命令将接合 part1.mp4、part2.mp4、part3.mp4 和 part4.mp4 文件到一个称为 output.mp4 的单个文件中。
|
||||
|
||||
#### 17、添加字幕到一个视频文件
|
||||
|
||||
我们可以使用 FFmpeg 来添加字幕到视频文件。为你的视频下载正确的字幕,并如下所示添加它到你的视频。
|
||||
|
||||
```
|
||||
$ fmpeg -i input.mp4 -i subtitle.srt -map 0 -map 1 -c copy -c:v libx264 -crf 23 -preset veryfast output.mp4
|
||||
```
|
||||
|
||||
#### 18、预览或测试视频或音频文件
|
||||
|
||||
你可能希望通过预览来验证或测试输出的文件是否已经被恰当地转码编码。为完成预览,你可以从你的终端播放它,用命令:
|
||||
|
||||
```
|
||||
$ ffplay video.mp4
|
||||
```
|
||||
|
||||
![][7]
|
||||
|
||||
类似地,你可以测试音频文件,像下面所示。
|
||||
|
||||
```
|
||||
$ ffplay audio.mp3
|
||||
```
|
||||
|
||||
![][8]
|
||||
|
||||
#### 19、增加/减少视频播放速度
|
||||
|
||||
FFmpeg 允许你调整视频播放速度。
|
||||
|
||||
为增加视频播放速度,运行:
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -vf "setpts=0.5*PTS" output.mp4
|
||||
```
|
||||
|
||||
该命令将双倍视频的速度。
|
||||
|
||||
为降低你的视频速度,你需要使用一个大于 1 的倍数。为减少播放速度,运行:
|
||||
|
||||
```
|
||||
$ ffmpeg -i input.mp4 -vf "setpts=4.0*PTS" output.mp4
|
||||
```
|
||||
|
||||
#### 20、创建动画的 GIF
|
||||
|
||||
出于各种目的,我们在几乎所有的社交和专业网络上使用 GIF 图像。使用 FFmpeg,我们可以简单地和快速地创建动画的视频文件。下面的指南阐释了如何在类 Unix 系统中使用 FFmpeg 和 ImageMagick 创建一个动画的 GIF 文件。
|
||||
|
||||
* [在 Linux 中如何创建动画的 GIF][9]
|
||||
|
||||
#### 21、从 PDF 文件中创建视频
|
||||
|
||||
我长年累月的收集了很多 PDF 文件,大多数是 Linux 教程,保存在我的平板电脑中。有时我懒得从平板电脑中阅读它们。因此,我决定从 PDF 文件中创建一个视频,在一个大屏幕设备(像一台电视机或一台电脑)中观看它们。如果你想知道如何从一批 PDF 文件中制作一个电影,下面的指南将帮助你。
|
||||
|
||||
* [在 Linux 中如何从 PDF 文件中创建一个视频][10]
|
||||
|
||||
#### 22、获取帮助
|
||||
|
||||
在这个指南中,我已经覆盖大多数常常使用的 FFmpeg 命令。它有很多不同的选项来做各种各样的高级功能。要学习更多用法,请参考手册页。
|
||||
|
||||
```
|
||||
$ man ffmpeg
|
||||
```
|
||||
|
||||
这就是全部了。我希望这个指南将帮助你入门 FFmpeg。如果你发现这个指南有用,请在你的社交和专业网络上分享它。更多好东西将要来。敬请期待!
|
||||
|
||||
谢谢!
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/20-ffmpeg-commands-beginners/
|
||||
|
||||
作者:[sk][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[robsean](https://github.com/robsean)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/sk/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ostechnix.com/wp-content/uploads/2017/05/FFmpeg-Commands-720x340.png
|
||||
[2]: https://www.ostechnix.com/install-ffmpeg-linux/
|
||||
[3]: http://www.ostechnix.com/wp-content/uploads/2017/05/sk@sk_001.png
|
||||
[4]: https://ostechnix.tradepub.com/free/w_make141/prgm.cgi
|
||||
[5]: https://ostechnix.tradepub.com/free/w_make75/prgm.cgi
|
||||
[6]: https://ostechnix.tradepub.com/free/w_make235/prgm.cgi
|
||||
[7]: http://www.ostechnix.com/wp-content/uploads/2017/05/Menu_004.png
|
||||
[8]: http://www.ostechnix.com/wp-content/uploads/2017/05/Menu_005-3.png
|
||||
[9]: https://www.ostechnix.com/create-animated-gif-ubuntu-16-04/
|
||||
[10]: https://www.ostechnix.com/create-video-pdf-files-linux/
|
171
published/20190527 A deeper dive into Linux permissions.md
Normal file
171
published/20190527 A deeper dive into Linux permissions.md
Normal file
@ -0,0 +1,171 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10947-1.html)
|
||||
[#]: subject: (A deeper dive into Linux permissions)
|
||||
[#]: via: (https://www.networkworld.com/article/3397790/a-deeper-dive-into-linux-permissions.html)
|
||||
[#]: author: (Sandra Henry-Stocker https://www.networkworld.com/author/Sandra-Henry_Stocker/)
|
||||
|
||||
更深入地了解 Linux 权限
|
||||
======
|
||||
> 在 Linux 上查看文件权限时,有时你会看到的不仅仅是普通的 r、w、x 和 -。如何更清晰地了解这些字符试图告诉你什么以及这些权限如何工作?
|
||||
|
||||
![Sandra Henry-Stocker](https://img.linux.net.cn/data/attachment/album/201906/07/150718q09wnve6ne6v9063.jpg)
|
||||
|
||||
在 Linux 上查看文件权限时,有时你会看到的不仅仅是普通的 `r`、`w`、`x` 和 `-`。除了在所有者、组和其他中看到 `rwx` 之外,你可能会看到 `s` 或者 `t`,如下例所示:
|
||||
|
||||
```
|
||||
drwxrwsrwt
|
||||
```
|
||||
|
||||
要进一步明确的方法之一是使用 `stat` 命令查看权限。`stat` 的第四行输出以八进制和字符串格式显示文件权限:
|
||||
|
||||
```
|
||||
$ stat /var/mail
|
||||
File: /var/mail
|
||||
Size: 4096 Blocks: 8 IO Block: 4096 directory
|
||||
Device: 801h/2049d Inode: 1048833 Links: 2
|
||||
Access: (3777/drwxrwsrwt) Uid: ( 0/ root) Gid: ( 8/ mail)
|
||||
Access: 2019-05-21 19:23:15.769746004 -0400
|
||||
Modify: 2019-05-21 19:03:48.226656344 -0400
|
||||
Change: 2019-05-21 19:03:48.226656344 -0400
|
||||
Birth: -
|
||||
```
|
||||
|
||||
这个输出提示我们,分配给文件权限的位数超过 9 位。事实上,有 12 位。这些额外的三位提供了一种分配超出通常的读、写和执行权限的方法 - 例如,`3777`(二进制 `011111111111`)表示使用了两个额外的设置。
|
||||
|
||||
该值的第一个 `1` (第二位)表示 SGID(设置 GID),为运行文件而赋予临时权限,或以该关联组的权限来使用目录。
|
||||
|
||||
```
|
||||
011111111111
|
||||
^
|
||||
```
|
||||
|
||||
SGID 将正在使用该文件的用户作为该组成员之一而分配临时权限。
|
||||
|
||||
第二个 `1`(第三位)是“粘连”位。它确保*只有*文件的所有者能够删除或重命名该文件或目录。
|
||||
|
||||
```
|
||||
011111111111
|
||||
^
|
||||
```
|
||||
|
||||
如果权限是 `7777` 而不是 `3777`,我们知道 SUID(设置 UID)字段也已设置。
|
||||
|
||||
```
|
||||
111111111111
|
||||
^
|
||||
```
|
||||
|
||||
SUID 将正在使用该文件的用户作为文件拥有者分配临时权限。
|
||||
|
||||
至于我们上面看到的 `/var/mail` 目录,所有用户都需要访问,因此需要一些特殊值来提供它。
|
||||
|
||||
但现在让我们更进一步。
|
||||
|
||||
特殊权限位的一个常见用法是使用 `passwd` 之类的命令。如果查看 `/usr/bin/passwd` 文件,你会注意到 SUID 位已设置,它允许你更改密码(以及 `/etc/shadow` 文件的内容),即使你是以普通(非特权)用户身份运行,并且对此文件没有读取或写入权限。当然,`passwd` 命令很聪明,不允许你更改其他人的密码,除非你是以 root 身份运行或使用 `sudo`。
|
||||
|
||||
```
|
||||
$ ls -l /usr/bin/passwd
|
||||
-rwsr-xr-x 1 root root 63736 Mar 22 14:32 /usr/bin/passwd
|
||||
$ ls -l /etc/shadow
|
||||
-rw-r----- 1 root shadow 2195 Apr 22 10:46 /etc/shadow
|
||||
```
|
||||
|
||||
现在,让我们看一下使用这些特殊权限可以做些什么。
|
||||
|
||||
### 如何分配特殊文件权限
|
||||
|
||||
与 Linux 命令行中的许多东西一样,你可以有不同的方法设置。 `chmod` 命令允许你以数字方式或使用字符表达式更改权限。
|
||||
|
||||
要以数字方式更改文件权限,你可以使用这样的命令来设置 SUID 和 SGID 位:
|
||||
|
||||
```
|
||||
$ chmod 6775 tryme
|
||||
```
|
||||
|
||||
或者你可以使用这样的命令:
|
||||
|
||||
```
|
||||
$ chmod ug+s tryme <== 用于 SUID 和 SGID 权限
|
||||
```
|
||||
|
||||
如果你要添加特殊权限的文件是脚本,你可能会对它不符合你的期望感到惊讶。这是一个非常简单的例子:
|
||||
|
||||
```
|
||||
$ cat tryme
|
||||
#!/bin/bash
|
||||
|
||||
echo I am $USER
|
||||
```
|
||||
|
||||
即使设置了 SUID 和 SGID 位,并且 root 是文件所有者,运行脚本也不会产生你可能期望的 “I am root”。为什么?因为 Linux 会忽略脚本的 SUID 和 SGID 位。
|
||||
|
||||
```
|
||||
$ ls -l tryme
|
||||
-rwsrwsrwt 1 root root 29 May 26 12:22 tryme
|
||||
$ ./tryme
|
||||
I am jdoe
|
||||
```
|
||||
|
||||
另一方面,如果你对一个编译的程序之类进行类似的尝试,就像下面这个简单的 C 程序一样,你会看到不同的效果。在此示例程序中,我们提示用户输入文件名并创建它,并给文件写入权限。
|
||||
|
||||
```
|
||||
#include <stdlib.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
FILE *fp; /* file pointer*/
|
||||
char fName[20];
|
||||
|
||||
printf("Enter the name of file to be created: ");
|
||||
scanf("%s",fName);
|
||||
|
||||
/* create the file with write permission */
|
||||
fp=fopen(fName,"w");
|
||||
/* check if file was created */
|
||||
if(fp==NULL)
|
||||
{
|
||||
printf("File not created");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
printf("File created successfully\n");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
编译程序并运行该命令以使 root 用户成为所有者并设置所需权限后,你将看到它以预期的 root 权限运行 - 留下新创建的 root 为所有者的文件。当然,你必须具有 `sudo` 权限才能运行一些需要的命令。
|
||||
|
||||
```
|
||||
$ cc -o mkfile mkfile.c <== 编译程序
|
||||
$ sudo chown root:root mkfile <== 更改所有者和组为 “root”
|
||||
$ sudo chmod ug+s mkfile <== 添加 SUID and SGID 权限
|
||||
$ ./mkfile <== 运行程序
|
||||
Enter name of file to be create: empty
|
||||
File created successfully
|
||||
$ ls -l empty
|
||||
-rw-rw-r-- 1 root root 0 May 26 13:15 empty
|
||||
```
|
||||
|
||||
请注意,文件所有者是 root - 如果程序未以 root 权限运行,则不会发生这种情况。
|
||||
|
||||
权限字符串中不常见设置的位置(例如,rw**s**rw**s**rw**t**)可以帮助提醒我们每个位的含义。至少第一个 “s”(SUID) 位于所有者权限区域中,第二个 (SGID) 位于组权限区域中。为什么粘连位是 “t” 而不是 “s” 超出了我的理解。也许创造者想把它称为 “tacky bit”,但由于这个词的不太令人喜欢的第二个定义而改变了他们的想法。无论如何,额外的权限设置为 Linux 和其他 Unix 系统提供了许多额外的功能。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.networkworld.com/article/3397790/a-deeper-dive-into-linux-permissions.html
|
||||
|
||||
作者:[Sandra Henry-Stocker][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.networkworld.com/author/Sandra-Henry_Stocker/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://images.idgesg.net/images/article/2019/05/shs_rwsr-100797564-large.jpg
|
||||
[2]: https://www.facebook.com/NetworkWorld/
|
||||
[3]: https://www.linkedin.com/company/network-world
|
@ -0,0 +1,146 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (geekpi)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10925-1.html)
|
||||
[#]: subject: (Dockly – Manage Docker Containers From Terminal)
|
||||
[#]: via: (https://www.ostechnix.com/dockly-manage-docker-containers-from-terminal/)
|
||||
[#]: author: (sk https://www.ostechnix.com/author/sk/)
|
||||
|
||||
Dockly:从终端管理 Docker 容器
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/01/144422bfwx1e7fqx1ee11x.jpg)
|
||||
|
||||
几天前,我们发布了一篇指南,其中涵盖了[开始使用 Docker][2] 时需要了解的几乎所有细节。在该指南中,我们向你展示了如何详细创建和管理 Docker 容器。还有一些可用于管理 Docker 容器的非官方工具。如果你看过我们以前的文章,你可能会看到两个基于 Web 的工具,[Portainer][3] 和 [PiCluster][4]。它们都使得 Docker 管理任务在 Web 浏览器中变得更加容易和简单。今天,我遇到了另一个名为 Dockly 的 Docker 管理工具。
|
||||
|
||||
与上面的工具不同,Dockly 是一个 TUI(文本界面)程序,用于在类 Unix 系统中从终端管理 Docker 容器和服务。它是使用 NodeJS 编写的自由开源工具。在本简要指南中,我们将了解如何安装 Dockly 以及如何从命令行管理 Docker 容器。
|
||||
|
||||
### 安装 Dockly
|
||||
|
||||
确保已在 Linux 上安装了 NodeJS。如果尚未安装,请参阅以下指南。
|
||||
|
||||
* [如何在 Linux 上安装 NodeJS][5]
|
||||
|
||||
安装 NodeJS 后,运行以下命令安装 Dockly:
|
||||
|
||||
```
|
||||
# npm install -g dockly
|
||||
```
|
||||
|
||||
### 使用 Dockly 在终端管理 Docker 容器
|
||||
|
||||
使用 Dockly 管理 Docker 容器非常简单!你所要做的就是打开终端并运行以下命令:
|
||||
|
||||
```
|
||||
# dockly
|
||||
```
|
||||
|
||||
Dockly 将通过 unix 套接字自动连接到你的本机 docker 守护进程,并在终端中显示正在运行的容器列表,如下所示。
|
||||
|
||||
![][6]
|
||||
|
||||
*使用 Dockly 管理 Docker 容器*
|
||||
|
||||
正如你在上面的截图中看到的,Dockly 在顶部显示了运行容器的以下信息:
|
||||
|
||||
* 容器 ID,
|
||||
* 容器名称,
|
||||
* Docker 镜像,
|
||||
* 命令,
|
||||
* 运行中容器的状态,
|
||||
* 状态。
|
||||
|
||||
在右上角,你将看到容器的 CPU 和内存利用率。使用向上/向下箭头键在容器之间移动。
|
||||
|
||||
在底部,有少量的键盘快捷键来执行各种 Docker 管理任务。以下是目前可用的键盘快捷键列表:
|
||||
|
||||
* `=` - 刷新 Dockly 界面,
|
||||
* `/` - 搜索容器列表视图,
|
||||
* `i` - 显示有关当前所选容器或服务的信息,
|
||||
* `回车` - 显示当前容器或服务的日志,
|
||||
* `v` - 在容器和服务视图之间切换,
|
||||
* `l` - 在选定的容器上启动 `/bin/bash` 会话,
|
||||
* `r` - 重启选定的容器,
|
||||
* `s` - 停止选定的容器,
|
||||
* `h` - 显示帮助窗口,
|
||||
* `q` - 退出 Dockly。
|
||||
|
||||
#### 查看容器的信息
|
||||
|
||||
使用向上/向下箭头选择一个容器,然后按 `i` 以显示所选容器的信息。
|
||||
|
||||
![][7]
|
||||
|
||||
*查看容器的信息*
|
||||
|
||||
#### 重启容器
|
||||
|
||||
如果你想随时重启容器,只需选择它并按 `r` 即可重新启动。
|
||||
|
||||
![][8]
|
||||
|
||||
*重启 Docker 容器*
|
||||
|
||||
#### 停止/删除容器和镜像
|
||||
|
||||
如果不再需要容器,我们可以立即停止和/或删除一个或所有容器。为此,请按 `m` 打开菜单。
|
||||
|
||||
![][9]
|
||||
|
||||
*停止,删除 Docker 容器和镜像*
|
||||
|
||||
在这里,你可以执行以下操作。
|
||||
|
||||
* 停止所有 Docker 容器,
|
||||
* 删除选定的容器,
|
||||
* 删除所有容器,
|
||||
* 删除所有 Docker 镜像等。
|
||||
|
||||
#### 显示 Dockly 帮助部分
|
||||
|
||||
如果你有任何疑问,只需按 `h` 即可打开帮助部分。
|
||||
|
||||
![][10]
|
||||
|
||||
*Dockly 帮助*
|
||||
|
||||
有关更多详细信息,请参考最后给出的官方 GitHub 页面。
|
||||
|
||||
就是这些了。希望这篇文章有用。如果你一直在使用 Docker 容器,请试试 Dockly,看它是否有帮助。
|
||||
|
||||
建议阅读:
|
||||
|
||||
* [如何自动更新正在运行的 Docker 容器][11]
|
||||
* [ctop:一个 Linux 容器的命令行监控工具][12]
|
||||
|
||||
资源:
|
||||
|
||||
* [Dockly 的 GitHub 仓库][13]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/dockly-manage-docker-containers-from-terminal/
|
||||
|
||||
作者:[sk][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.ostechnix.com/author/sk/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.ostechnix.com/wp-content/uploads/2019/05/Dockly-720x340.png
|
||||
[2]: https://www.ostechnix.com/getting-started-with-docker/
|
||||
[3]: https://www.ostechnix.com/portainer-an-easiest-way-to-manage-docker/
|
||||
[4]: https://www.ostechnix.com/picluster-simple-web-based-docker-management-application/
|
||||
[5]: https://www.ostechnix.com/install-node-js-linux/
|
||||
[6]: http://www.ostechnix.com/wp-content/uploads/2019/05/Manage-Docker-Containers-Using-Dockly.png
|
||||
[7]: http://www.ostechnix.com/wp-content/uploads/2019/05/View-containers-information.png
|
||||
[8]: http://www.ostechnix.com/wp-content/uploads/2019/05/Restart-containers.png
|
||||
[9]: http://www.ostechnix.com/wp-content/uploads/2019/05/stop-remove-containers-and-images.png
|
||||
[10]: http://www.ostechnix.com/wp-content/uploads/2019/05/Dockly-Help.png
|
||||
[11]: https://www.ostechnix.com/automatically-update-running-docker-containers/
|
||||
[12]: https://www.ostechnix.com/ctop-commandline-monitoring-tool-linux-containers/
|
||||
[13]: https://github.com/lirantal/dockly
|
@ -0,0 +1,312 @@
|
||||
[#]: collector: (lujun9972)
|
||||
[#]: translator: (jdh8383)
|
||||
[#]: reviewer: (wxy)
|
||||
[#]: publisher: (wxy)
|
||||
[#]: url: (https://linux.cn/article-10938-1.html)
|
||||
[#]: subject: (How To Check Available Security Updates On Red Hat (RHEL) And CentOS System?)
|
||||
[#]: via: (https://www.2daygeek.com/check-list-view-find-available-security-updates-on-redhat-rhel-centos-system/)
|
||||
[#]: author: (Magesh Maruthamuthu https://www.2daygeek.com/author/magesh/)
|
||||
|
||||
如何在 CentOS 或 RHEL 系统上检查可用的安全更新?
|
||||
======
|
||||
|
||||
![](https://img.linux.net.cn/data/attachment/album/201906/05/003907tljfmy4bnn4qj1tp.jpg)
|
||||
|
||||
当你更新系统时,根据你所在公司的安全策略,有时候可能只需要打上与安全相关的补丁。大多数情况下,这应该是出于程序兼容性方面的考量。那该怎样实践呢?有没有办法让 `yum` 只安装安全补丁呢?
|
||||
|
||||
答案是肯定的,可以用 `yum` 包管理器轻松实现。
|
||||
|
||||
在这篇文章中,我们不但会提供所需的信息。而且,我们会介绍一些额外的命令,可以帮你获取指定安全更新的详实信息。
|
||||
|
||||
希望这样可以启发你去了解并修复你列表上的那些漏洞。一旦有安全漏洞被公布,就必须更新受影响的软件,这样可以降低系统中的安全风险。
|
||||
|
||||
对于 RHEL 或 CentOS 6 系统,运行下面的 [Yum 命令][1] 来安装 yum 安全插件。
|
||||
|
||||
```
|
||||
# yum -y install yum-plugin-security
|
||||
```
|
||||
|
||||
在 RHEL 7&8 或是 CentOS 7&8 上面,这个插件已经是 `yum` 的一部分了,不用单独安装。
|
||||
|
||||
要列出全部可用的补丁(包括安全、Bug 修复以及产品改进),但不安装它们:
|
||||
|
||||
```
|
||||
# yum updateinfo list available
|
||||
Loaded plugins: changelog, package_upload, product-id, search-disabled-repos,
|
||||
: subscription-manager, verify, versionlock
|
||||
RHSA-2014:1031 Important/Sec. 389-ds-base-1.3.1.6-26.el7_0.x86_64
|
||||
RHSA-2015:0416 Important/Sec. 389-ds-base-1.3.3.1-13.el7.x86_64
|
||||
RHBA-2015:0626 bugfix 389-ds-base-1.3.3.1-15.el7_1.x86_64
|
||||
RHSA-2015:0895 Important/Sec. 389-ds-base-1.3.3.1-16.el7_1.x86_64
|
||||
RHBA-2015:1554 bugfix 389-ds-base-1.3.3.1-20.el7_1.x86_64
|
||||
RHBA-2015:1960 bugfix 389-ds-base-1.3.3.1-23.el7_1.x86_64
|
||||
RHBA-2015:2351 bugfix 389-ds-base-1.3.4.0-19.el7.x86_64
|
||||
RHBA-2015:2572 bugfix 389-ds-base-1.3.4.0-21.el7_2.x86_64
|
||||
RHSA-2016:0204 Important/Sec. 389-ds-base-1.3.4.0-26.el7_2.x86_64
|
||||
RHBA-2016:0550 bugfix 389-ds-base-1.3.4.0-29.el7_2.x86_64
|
||||
RHBA-2016:1048 bugfix 389-ds-base-1.3.4.0-30.el7_2.x86_64
|
||||
RHBA-2016:1298 bugfix 389-ds-base-1.3.4.0-32.el7_2.x86_64
|
||||
```
|
||||
|
||||
要统计补丁的大约数量,运行下面的命令:
|
||||
|
||||
```
|
||||
# yum updateinfo list available | wc -l
|
||||
11269
|
||||
```
|
||||
|
||||
想列出全部可用的安全补丁但不安装,以下命令用来展示你系统里已安装和待安装的推荐补丁:
|
||||
|
||||
```
|
||||
# yum updateinfo list security all
|
||||
Loaded plugins: changelog, package_upload, product-id, search-disabled-repos,
|
||||
: subscription-manager, verify, versionlock
|
||||
RHSA-2014:1031 Important/Sec. 389-ds-base-1.3.1.6-26.el7_0.x86_64
|
||||
RHSA-2015:0416 Important/Sec. 389-ds-base-1.3.3.1-13.el7.x86_64
|
||||
RHSA-2015:0895 Important/Sec. 389-ds-base-1.3.3.1-16.el7_1.x86_64
|
||||
RHSA-2016:0204 Important/Sec. 389-ds-base-1.3.4.0-26.el7_2.x86_64
|
||||
RHSA-2016:2594 Moderate/Sec. 389-ds-base-1.3.5.10-11.el7.x86_64
|
||||
RHSA-2017:0920 Important/Sec. 389-ds-base-1.3.5.10-20.el7_3.x86_64
|
||||
RHSA-2017:2569 Moderate/Sec. 389-ds-base-1.3.6.1-19.el7_4.x86_64
|
||||
RHSA-2018:0163 Important/Sec. 389-ds-base-1.3.6.1-26.el7_4.x86_64
|
||||
RHSA-2018:0414 Important/Sec. 389-ds-base-1.3.6.1-28.el7_4.x86_64
|
||||
RHSA-2018:1380 Important/Sec. 389-ds-base-1.3.7.5-21.el7_5.x86_64
|
||||
RHSA-2018:2757 Moderate/Sec. 389-ds-base-1.3.7.5-28.el7_5.x86_64
|
||||
RHSA-2018:3127 Moderate/Sec. 389-ds-base-1.3.8.4-15.el7.x86_64
|
||||
RHSA-2014:1031 Important/Sec. 389-ds-base-libs-1.3.1.6-26.el7_0.x86_64
|
||||
```
|
||||
|
||||
要显示所有待安装的安全补丁:
|
||||
|
||||
```
|
||||
# yum updateinfo list security all | grep -v "i"
|
||||
|
||||
RHSA-2014:1031 Important/Sec. 389-ds-base-1.3.1.6-26.el7_0.x86_64
|
||||
RHSA-2015:0416 Important/Sec. 389-ds-base-1.3.3.1-13.el7.x86_64
|
||||
RHSA-2015:0895 Important/Sec. 389-ds-base-1.3.3.1-16.el7_1.x86_64
|
||||
RHSA-2016:0204 Important/Sec. 389-ds-base-1.3.4.0-26.el7_2.x86_64
|
||||
RHSA-2016:2594 Moderate/Sec. 389-ds-base-1.3.5.10-11.el7.x86_64
|
||||
RHSA-2017:0920 Important/Sec. 389-ds-base-1.3.5.10-20.el7_3.x86_64
|
||||
RHSA-2017:2569 Moderate/Sec. 389-ds-base-1.3.6.1-19.el7_4.x86_64
|
||||
RHSA-2018:0163 Important/Sec. 389-ds-base-1.3.6.1-26.el7_4.x86_64
|
||||
RHSA-2018:0414 Important/Sec. 389-ds-base-1.3.6.1-28.el7_4.x86_64
|
||||
RHSA-2018:1380 Important/Sec. 389-ds-base-1.3.7.5-21.el7_5.x86_64
|
||||
RHSA-2018:2757 Moderate/Sec. 389-ds-base-1.3.7.5-28.el7_5.x86_64
|
||||
```
|
||||
|
||||
要统计全部安全补丁的大致数量,运行下面的命令:
|
||||
|
||||
```
|
||||
# yum updateinfo list security all | wc -l
|
||||
3522
|
||||
```
|
||||
|
||||
下面根据已装软件列出可更新的安全补丁。这包括 bugzilla(bug 修复)、CVE(知名漏洞数据库)、安全更新等:
|
||||
|
||||
```
|
||||
# yum updateinfo list security
|
||||
|
||||
或者
|
||||
|
||||
# yum updateinfo list sec
|
||||
|
||||
Loaded plugins: changelog, package_upload, product-id, search-disabled-repos,
|
||||
: subscription-manager, verify, versionlock
|
||||
|
||||
RHSA-2018:3665 Important/Sec. NetworkManager-1:1.12.0-8.el7_6.x86_64
|
||||
RHSA-2018:3665 Important/Sec. NetworkManager-adsl-1:1.12.0-8.el7_6.x86_64
|
||||
RHSA-2018:3665 Important/Sec. NetworkManager-bluetooth-1:1.12.0-8.el7_6.x86_64
|
||||
RHSA-2018:3665 Important/Sec. NetworkManager-config-server-1:1.12.0-8.el7_6.noarch
|
||||
RHSA-2018:3665 Important/Sec. NetworkManager-glib-1:1.12.0-8.el7_6.x86_64
|
||||
RHSA-2018:3665 Important/Sec. NetworkManager-libnm-1:1.12.0-8.el7_6.x86_64
|
||||
RHSA-2018:3665 Important/Sec. NetworkManager-ppp-1:1.12.0-8.el7_6.x86_64
|
||||
RHSA-2018:3665 Important/Sec. NetworkManager-team-1:1.12.0-8.el7_6.x86_64
|
||||
RHSA-2018:3665 Important/Sec. NetworkManager-tui-1:1.12.0-8.el7_6.x86_64
|
||||
RHSA-2018:3665 Important/Sec. NetworkManager-wifi-1:1.12.0-8.el7_6.x86_64
|
||||
RHSA-2018:3665 Important/Sec. NetworkManager-wwan-1:1.12.0-8.el7_6.x86_64
|
||||
```
|
||||
|
||||
显示所有与安全相关的更新,并且返回一个结果来告诉你是否有可用的补丁:
|
||||
|
||||
```
|
||||
# yum --security check-update
|
||||
Loaded plugins: changelog, package_upload, product-id, search-disabled-repos, subscription-manager, verify, versionlock
|
||||
rhel-7-server-rpms | 2.0 kB 00:00:00
|
||||
--> policycoreutils-devel-2.2.5-20.el7.x86_64 from rhel-7-server-rpms excluded (updateinfo)
|
||||
--> smc-raghumalayalam-fonts-6.0-7.el7.noarch from rhel-7-server-rpms excluded (updateinfo)
|
||||
--> amanda-server-3.3.3-17.el7.x86_64 from rhel-7-server-rpms excluded (updateinfo)
|
||||
--> 389-ds-base-libs-1.3.4.0-26.el7_2.x86_64 from rhel-7-server-rpms excluded (updateinfo)
|
||||
--> 1:cups-devel-1.6.3-26.el7.i686 from rhel-7-server-rpms excluded (updateinfo)
|
||||
--> openwsman-client-2.6.3-3.git4391e5c.el7.i686 from rhel-7-server-rpms excluded (updateinfo)
|
||||
--> 1:emacs-24.3-18.el7.x86_64 from rhel-7-server-rpms excluded (updateinfo)
|
||||
--> augeas-libs-1.4.0-2.el7_4.2.i686 from rhel-7-server-rpms excluded (updateinfo)
|
||||
--> samba-winbind-modules-4.2.3-10.el7.i686 from rhel-7-server-rpms excluded (updateinfo)
|
||||
--> tftp-5.2-11.el7.x86_64 from rhel-7-server-rpms excluded (updateinfo)
|
||||
.
|
||||
.
|
||||
35 package(s) needed for security, out of 115 available
|
||||
NetworkManager.x86_64 1:1.12.0-10.el7_6 rhel-7-server-rpms
|
||||
NetworkManager-adsl.x86_64 1:1.12.0-10.el7_6 rhel-7-server-rpms
|
||||
NetworkManager-bluetooth.x86_64 1:1.12.0-10.el7_6 rhel-7-server-rpms
|
||||
NetworkManager-config-server.noarch 1:1.12.0-10.el7_6 rhel-7-server-rpms
|
||||
NetworkManager-glib.x86_64 1:1.12.0-10.el7_6 rhel-7-server-rpms
|
||||
NetworkManager-libnm.x86_64 1:1.12.0-10.el7_6 rhel-7-server-rpms
|
||||
NetworkManager-ppp.x86_64 1:1.12.0-10.el7_6 rhel-7-server-rpms
|
||||
```
|
||||
|
||||
列出所有可用的安全补丁,并且显示其详细信息:
|
||||
|
||||
```
|
||||
# yum info-sec
|
||||
.
|
||||
.
|
||||
===============================================================================
|
||||
tzdata bug fix and enhancement update
|
||||
===============================================================================
|
||||
Update ID : RHBA-2019:0689
|
||||
Release : 0
|
||||
Type : bugfix
|
||||
Status : final
|
||||
Issued : 2019-03-28 19:27:44 UTC
|
||||
Description : The tzdata packages contain data files with rules for various
|
||||
: time zones.
|
||||
:
|
||||
: The tzdata packages have been updated to version
|
||||
: 2019a, which addresses recent time zone changes.
|
||||
: Notably:
|
||||
:
|
||||
: * The Asia/Hebron and Asia/Gaza zones will start
|
||||
: DST on 2019-03-30, rather than 2019-03-23 as
|
||||
: previously predicted.
|
||||
: * Metlakatla rejoined Alaska time on 2019-01-20,
|
||||
: ending its observances of Pacific standard time.
|
||||
:
|
||||
: (BZ#1692616, BZ#1692615, BZ#1692816)
|
||||
:
|
||||
: Users of tzdata are advised to upgrade to these
|
||||
: updated packages.
|
||||
Severity : None
|
||||
```
|
||||
|
||||
如果你想要知道某个更新的具体内容,可以运行下面这个命令:
|
||||
|
||||
```
|
||||
# yum updateinfo RHSA-2019:0163
|
||||
|
||||
Loaded plugins: changelog, package_upload, product-id, search-disabled-repos, subscription-manager, verify, versionlock
|
||||
rhel-7-server-rpms | 2.0 kB 00:00:00
|
||||
===============================================================================
|
||||
Important: kernel security, bug fix, and enhancement update
|
||||
===============================================================================
|
||||
Update ID : RHSA-2019:0163
|
||||
Release : 0
|
||||
Type : security
|
||||
Status : final
|
||||
Issued : 2019-01-29 15:21:23 UTC
|
||||
Updated : 2019-01-29 15:23:47 UTC Bugs : 1641548 - CVE-2018-18397 kernel: userfaultfd bypasses tmpfs file permissions
|
||||
: 1641878 - CVE-2018-18559 kernel: Use-after-free due to race condition in AF_PACKET implementation
|
||||
CVEs : CVE-2018-18397
|
||||
: CVE-2018-18559
|
||||
Description : The kernel packages contain the Linux kernel, the core of any
|
||||
: Linux operating system.
|
||||
:
|
||||
: Security Fix(es):
|
||||
:
|
||||
: * kernel: Use-after-free due to race condition in
|
||||
: AF_PACKET implementation (CVE-2018-18559)
|
||||
:
|
||||
: * kernel: userfaultfd bypasses tmpfs file
|
||||
: permissions (CVE-2018-18397)
|
||||
:
|
||||
: For more details about the security issue(s),
|
||||
: including the impact, a CVSS score, and other
|
||||
: related information, refer to the CVE page(s)
|
||||
: listed in the References section.
|
||||
:
|
||||
: Bug Fix(es):
|
||||
:
|
||||
: These updated kernel packages include also
|
||||
: numerous bug fixes and enhancements. Space
|
||||
: precludes documenting all of the bug fixes in this
|
||||
: advisory. See the descriptions in the related
|
||||
: Knowledge Article:
|
||||
: https://access.redhat.com/articles/3827321
|
||||
Severity : Important
|
||||
updateinfo info done
|
||||
```
|
||||
|
||||
跟之前类似,你可以只查询那些通过 CVE 释出的系统漏洞:
|
||||
|
||||
```
|
||||
# yum updateinfo list cves
|
||||
|
||||
Loaded plugins: changelog, package_upload, product-id, search-disabled-repos,
|
||||
: subscription-manager, verify, versionlock
|
||||
CVE-2018-15688 Important/Sec. NetworkManager-1:1.12.0-8.el7_6.x86_64
|
||||
CVE-2018-15688 Important/Sec. NetworkManager-adsl-1:1.12.0-8.el7_6.x86_64
|
||||
CVE-2018-15688 Important/Sec. NetworkManager-bluetooth-1:1.12.0-8.el7_6.x86_64
|
||||
CVE-2018-15688 Important/Sec. NetworkManager-config-server-1:1.12.0-8.el7_6.noarch
|
||||
CVE-2018-15688 Important/Sec. NetworkManager-glib-1:1.12.0-8.el7_6.x86_64
|
||||
CVE-2018-15688 Important/Sec. NetworkManager-libnm-1:1.12.0-8.el7_6.x86_64
|
||||
CVE-2018-15688 Important/Sec. NetworkManager-ppp-1:1.12.0-8.el7_6.x86_64
|
||||
CVE-2018-15688 Important/Sec. NetworkManager-team-1:1.12.0-8.el7_6.x86_64
|
||||
```
|
||||
|
||||
你也可以查看那些跟 bug 修复相关的更新,运行下面的命令:
|
||||
|
||||
```
|
||||
# yum updateinfo list bugfix | less
|
||||
|
||||
Loaded plugins: changelog, package_upload, product-id, search-disabled-repos,
|
||||
: subscription-manager, verify, versionlock
|
||||
RHBA-2018:3349 bugfix NetworkManager-1:1.12.0-7.el7_6.x86_64
|
||||
RHBA-2019:0519 bugfix NetworkManager-1:1.12.0-10.el7_6.x86_64
|
||||
RHBA-2018:3349 bugfix NetworkManager-adsl-1:1.12.0-7.el7_6.x86_64
|
||||
RHBA-2019:0519 bugfix NetworkManager-adsl-1:1.12.0-10.el7_6.x86_64
|
||||
RHBA-2018:3349 bugfix NetworkManager-bluetooth-1:1.12.0-7.el7_6.x86_64
|
||||
RHBA-2019:0519 bugfix NetworkManager-bluetooth-1:1.12.0-10.el7_6.x86_64
|
||||
RHBA-2018:3349 bugfix NetworkManager-config-server-1:1.12.0-7.el7_6.noarch
|
||||
RHBA-2019:0519 bugfix NetworkManager-config-server-1:1.12.0-10.el7_6.noarch
|
||||
```
|
||||
|
||||
要想得到待安装更新的摘要信息,运行这个:
|
||||
|
||||
```
|
||||
# yum updateinfo summary
|
||||
Loaded plugins: changelog, package_upload, product-id, search-disabled-repos, subscription-manager, verify, versionlock
|
||||
rhel-7-server-rpms | 2.0 kB 00:00:00
|
||||
Updates Information Summary: updates
|
||||
13 Security notice(s)
|
||||
9 Important Security notice(s)
|
||||
3 Moderate Security notice(s)
|
||||
1 Low Security notice(s)
|
||||
35 Bugfix notice(s)
|
||||
1 Enhancement notice(s)
|
||||
updateinfo summary done
|
||||
```
|
||||
|
||||
如果只想打印出低级别的安全更新,运行下面这个命令。类似的,你也可以只查询重要级别和中等级别的安全更新。
|
||||
|
||||
```
|
||||
# yum updateinfo list sec | grep -i "Low"
|
||||
|
||||
RHSA-2019:0201 Low/Sec. libgudev1-219-62.el7_6.3.x86_64
|
||||
RHSA-2019:0201 Low/Sec. systemd-219-62.el7_6.3.x86_64
|
||||
RHSA-2019:0201 Low/Sec. systemd-libs-219-62.el7_6.3.x86_64
|
||||
RHSA-2019:0201 Low/Sec. systemd-sysv-219-62.el7_6.3.x86_64
|
||||
```
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.2daygeek.com/check-list-view-find-available-security-updates-on-redhat-rhel-centos-system/
|
||||
|
||||
作者:[Magesh Maruthamuthu][a]
|
||||
选题:[lujun9972][b]
|
||||
译者:[jdh8383](https://github.com/jdh8383)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://www.2daygeek.com/author/magesh/
|
||||
[b]: https://github.com/lujun9972
|
||||
[1]: https://www.2daygeek.com/yum-command-examples-manage-packages-rhel-centos-systems/
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user