使用 Python 的 urllib.parse 库解析 URL

Summary: Python 中的 urllib.parse 模块提供了很多解析和组建 URL 的函数。

![](/data/attachment/album/201802/22/235511q1pbibaabi5cx6xf.jpg)

Python 中的 `urllib.parse` 模块提供了很多解析和组建 URL 的函数。

### 解析url

`urlparse()` 函数可以将 URL 解析成 `ParseResult` 对象。对象中包含了六个元素，分别为：

* 协议（scheme）
* 域名（netloc）
* 路径（path）
* 路径参数（params）
* 查询参数（query）
* 片段（fragment）

```
from urllib.parse import urlparse

url='http://user:pwd@domain:80/path;params?query=queryarg#fragment'

parsed_result=urlparse(url)

print('parsed_result 包含了',len(parsed_result),'个元素')
print(parsed_result)
```

结果为:

```
parsed_result 包含了 6 个元素
ParseResult(scheme='http', netloc='user:pwd@domain:80', path='/path', params='params', query='query=queryarg', fragment='fragment')

```

`ParseResult` 继承于 `namedtuple`，因此可以同时通过索引和命名属性来获取 URL 中各部分的值。

为了方便起见， `ParseResult` 还提供了 `username`、 `password`、 `hostname`、 `port` 对 `netloc` 进一步进行拆分。

```
print('scheme  :', parsed_result.scheme)
print('netloc  :', parsed_result.netloc)
print('path    :', parsed_result.path)
print('params  :', parsed_result.params)
print('query   :', parsed_result.query)
print('fragment:', parsed_result.fragment)
print('username:', parsed_result.username)
print('password:', parsed_result.password)
print('hostname:', parsed_result.hostname)
print('port    :', parsed_result.port)
```

结果为：

```
scheme  : http
netloc  : user:pwd@domain:80
path    : /path
params  : params
query   : query=queryarg
fragment: fragment
username: user
password: pwd
hostname: domain
port    : 80

```

除了 `urlparse()` 之外，还有一个类似的 `urlsplit()` 函数也能对 URL 进行拆分，所不同的是， `urlsplit()` 并不会把 `路径参数(params)` 从 `路径(path)` 中分离出来。

当 URL 中路径部分包含多个参数时，使用 `urlparse()` 解析是有问题的：

```
url='http://user:pwd@domain:80/path1;params1/path2;params2?query=queryarg#fragment'

parsed_result=urlparse(url)

print(parsed_result)
print('parsed.path    :', parsed_result.path)
print('parsed.params  :', parsed_result.params)
```

结果为：

```
ParseResult(scheme='http', netloc='user:pwd@domain:80', path='/path1;params1/path2', params='params2', query='query=queryarg', fragment='fragment')
parsed.path    : /path1;params1/path2
parsed.params  : params2

```

这时可以使用 `urlsplit()` 来解析：

```
from urllib.parse import urlsplit
split_result=urlsplit(url)

print(split_result)
print('split.path    :', split_result.path)
# SplitResult 没有 params 属性
```

结果为：

```
SplitResult(scheme='http', netloc='user:pwd@domain:80', path='/path1;params1/path2;params2', query='query=queryarg', fragment='fragment')
split.path    : /path1;params1/path2;params2

```

若只是要将 URL 后的 `fragment` 标识拆分出来，可以使用 `urldefrag()` 函数：

```
from urllib.parse import urldefrag

url = 'http://user:pwd@domain:80/path1;params1/path2;params2?query=queryarg#fragment'

d = urldefrag(url)
print(d)
print('url     :', d.url)
print('fragment:', d.fragment)
```

结果为：

```
DefragResult(url='http://user:pwd@domain:80/path1;params1/path2;params2?query=queryarg', fragment='fragment')
url     : http://user:pwd@domain:80/path1;params1/path2;params2?query=queryarg
fragment: fragment

```

### 组建URL

`ParsedResult` 对象和 `SplitResult` 对象都有一个 `geturl()` 方法，可以返回一个完整的 URL 字符串。

```
print(parsed_result.geturl())
print(split_result.geturl())
```

结果为：

```
http://user:pwd@domain:80/path1;params1/path2;params2?query=queryarg#fragment
http://user:pwd@domain:80/path1;params1/path2;params2?query=queryarg#fragment

```

但是 `geturl()` 只在 `ParsedResult` 和 `SplitResult` 对象中有，若想将一个普通的元组组成 URL，则需要使用 `urlunparse()` 函数：

```
from urllib.parse import urlunparse
url_compos = ('http', 'user:pwd@domain:80', '/path1;params1/path2', 'params2', 'query=queryarg', 'fragment')
print(urlunparse(url_compos))
```

结果为：

```
http://user:pwd@domain:80/path1;params1/path2;params2?query=queryarg#fragment

```

### 相对路径转换绝对路径

除此之外，`urllib.parse` 还提供了一个 `urljoin()` 函数，来将相对路径转换成绝对路径的 URL。

```
from urllib.parse import urljoin

print(urljoin('http://www.example.com/path/file.html', 'anotherfile.html'))
print(urljoin('http://www.example.com/path/', 'anotherfile.html'))
print(urljoin('http://www.example.com/path/file.html', '../anotherfile.html'))
print(urljoin('http://www.example.com/path/file.html', '/anotherfile.html'))
```

结果为：

```
http://www.example.com/path/anotherfile.html
http://www.example.com/path/anotherfile.html
http://www.example.com/anotherfile.html
http://www.example.com/anotherfile.html

```

### 查询参数的构造和解析

使用 `urlencode()` 函数可以将一个 dict 转换成合法的查询参数：

```
from urllib.parse import urlencode

query_args = {
    'name': 'dark sun',
    'country': '中国'
}

query_args = urlencode(query_args)
print(query_args)
```

结果为：

```
name=dark+sun&country=%E4%B8%AD%E5%9B%BD

```

可以看到特殊字符也被正确地转义了。

相对的，可以使用 `parse_qs()` 来将查询参数解析成 dict。

```
from urllib.parse import parse_qs
print(parse_qs(query_args))
```

结果为：

```
{'name': ['dark sun'], 'country': ['中国']}

```

如果只是希望对特殊字符进行转义，那么可以使用 `quote` 或 `quote_plus` 函数，其中 `quote_plus` 比 `quote` 更激进一些，会把 `:`、`/` 一类的符号也给转义了。

```
from urllib.parse import quote, quote_plus, urlencode

url = 'http://localhost:1080/~hello!/'
print('urlencode :', urlencode({'url': url}))
print('quote     :', quote(url))
print('quote_plus:', quote_plus(url))
```

结果为：

```
urlencode : url=http%3A%2F%2Flocalhost%3A1080%2F%7Ehello%21%2F
quote     : http%3A//localhost%3A1080/%7Ehello%21/
quote_plus: http%3A%2F%2Flocalhost%3A1080%2F%7Ehello%21%2F

```

可以看到 `urlencode` 中应该是调用 `quote_plus` 来进行转义的。

逆向操作则使用 `unquote` 或 `unquote_plus` 函数：

```
from urllib.parse import unquote, unquote_plus

encoded_url = 'http%3A%2F%2Flocalhost%3A1080%2F%7Ehello%21%2F'
print(unquote(encoded_url))
print(unquote_plus(encoded_url))
```

结果为：

```
http://localhost:1080/~hello!/
http://localhost:1080/~hello!/

```

你会发现 `unquote` 函数居然能正确地将 `quote_plus` 的结果转换回来。


每个系统管理员都要知道的 30 个 Linux 系统监控工具

![](/data/attachment/album/201802/23/003604wd2215mdgd5i1g2y.jpg)

您需要监控 Linux 服务器的性能吗？试试用这些内置命令和附加工具吧！大多数 Linux 发行版都附带了大量的监控工具。这些工具提供了获取系统活动的相关指标。您可以使用这些工具来查找性能问题的可能原因。本文提到的是一些基本的命令，用于系统分析和服务器调试等，例如：

1. 找出系统瓶颈
2. 磁盘（存储）瓶颈
3. CPU 和内存瓶颈
4. 网络瓶颈

### 1. top - 进程活动监控命令

`top` 命令会显示 Linux 的进程。它提供了一个运行中系统的实时动态视图，即实际的进程活动。默认情况下，它显示在服务器上运行的 CPU 占用率最高的任务，并且每五秒更新一次。

![](/data/attachment/album/201802/23/003617yrapgeguzhzegrjm.jpg)

*图 01：Linux top 命令*

#### top 的常用快捷键

常用快捷键列表：

| 快捷键 | 用法 |
| --- | --- |
| `t` | 是否显示汇总信息 |
| `m` | 是否显示内存信息 |
| `A` | 根据各种系统资源的利用率对进程进行排序，有助于快速识别系统中性能不佳的任务。 |
| `f` | 进入 `top` 的交互式配置屏幕，用于根据特定的需求而设置 `top` 的显示。 |
| `o` | 交互式地调整 `top` 每一列的顺序。 |
| `r` | 调整优先级（`renice`） |
| `k` | 杀掉进程（`kill`） |
| `z` | 切换彩色或黑白模式 |

相关链接：[Linux 如何查看 CPU 利用率？](https://www.cyberciti.biz/tips/how-do-i-find-out-linux-cpu-utilization.html)

### 2. vmstat - 虚拟内存统计

`vmstat` 命令报告有关进程、内存、分页、块 IO、中断和 CPU 活动等信息。

```
# vmstat 3

```

输出示例：

```
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
 r b swpd free buff cache si so bi bo in cs us sy id wa st
 0 0 0 2540988 522188 5130400 0 0 2 32 4 2 4 1 96 0 0
 1 0 0 2540988 522188 5130400 0 0 0 720 1199 665 1 0 99 0 0
 0 0 0 2540956 522188 5130400 0 0 0 0 1151 1569 4 1 95 0 0
 0 0 0 2540956 522188 5130500 0 0 0 6 1117 439 1 0 99 0 0
 0 0 0 2540940 522188 5130512 0 0 0 536 1189 932 1 0 98 0 0
 0 0 0 2538444 522188 5130588 0 0 0 0 1187 1417 4 1 96 0 0
 0 0 0 2490060 522188 5130640 0 0 0 18 1253 1123 5 1 94 0 0

```

#### 显示 Slab 缓存的利用率

```
# vmstat -m

```

#### 获取有关活动和非活动内存页面的信息

```
# vmstat -a

```

相关链接：[如何查看 Linux 的资源利用率从而找到系统瓶颈？](https://www.cyberciti.biz/tips/linux-resource-utilization-to-detect-system-bottlenecks.html)

### 3. w - 找出登录的用户以及他们在做什么

[w 命令](https://www.cyberciti.biz/faq/unix-linux-w-command-examples-syntax-usage-2/ "See Linux/Unix w command examples for more info") 显示了当前登录在该系统上的用户及其进程。

```
# w username
# w vivek

```

输出示例：

```
 17:58:47 up 5 days, 20:28, 2 users, load average: 0.36, 0.26, 0.24
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 10.1.3.145 14:55 5.00s 0.04s 0.02s vim /etc/resolv.conf
root pts/1 10.1.3.145 17:43 0.00s 0.03s 0.00s w

```

### 4. uptime - Linux 系统运行了多久

`uptime` 命令可以用来查看服务器运行了多长时间：当前时间、已运行的时间、当前登录的用户连接数，以及过去 1 分钟、5 分钟和 15 分钟的系统负载平均值。

```
# uptime

```

输出示例：

```
 18:02:41 up 41 days, 23:42, 1 user, load average: 0.00, 0.00, 0.00

```

`1` 可以被认为是最佳负载值。不同的系统会有不同的负载：对于单核 CPU 系统来说，`1` 到 `3` 的负载值是可以接受的；而对于 SMP（对称多处理）系统来说，负载可以是 `6` 到 `10`。

### 5. ps - 显示系统进程

`ps` 命令显示当前运行的进程。要显示所有的进程，请使用 `-A` 或 `-e` 选项：

```
# ps -A

```

输出示例：

```
 PID TTY TIME CMD
 1 ? 00:00:02 init
 2 ? 00:00:02 migration/0
 3 ? 00:00:01 ksoftirqd/0
 4 ? 00:00:00 watchdog/0
 5 ? 00:00:00 migration/1
 6 ? 00:00:15 ksoftirqd/1
....
.....
 4881 ? 00:53:28 java
 4885 tty1 00:00:00 mingetty
 4886 tty2 00:00:00 mingetty
 4887 tty3 00:00:00 mingetty
 4888 tty4 00:00:00 mingetty
 4891 tty5 00:00:00 mingetty
 4892 tty6 00:00:00 mingetty
 4893 ttyS1 00:00:00 agetty
12853 ? 00:00:00 cifsoplockd
12854 ? 00:00:00 cifsdnotifyd
14231 ? 00:10:34 lighttpd
14232 ? 00:00:00 php-cgi
54981 pts/0 00:00:00 vim
55465 ? 00:00:00 php-cgi
55546 ? 00:00:00 bind9-snmp-stat
55704 pts/1 00:00:00 ps

```

`ps` 与 `top` 类似，但它提供了更多的信息。

#### 显示长输出格式

```
# ps -Al

```

显示完整输出格式（它将显示传递给进程的命令行参数）：

```
# ps -AlF

```

#### 显示线程（轻量级进程（LWP）和线程的数量（NLWP））

```
# ps -AlFH

```

#### 在进程后显示线程

```
# ps -AlLm

```

#### 显示系统上所有的进程

```
# ps ax
# ps axu

```

#### 显示进程树

```
# ps -ejH
# ps axjf
# pstree

```

#### 显示进程的安全信息

```
# ps -eo euser,ruser,suser,fuser,f,comm,label
# ps axZ
# ps -eM

```

#### 显示指定用户（如 vivek）运行的进程

```
# ps -U vivek -u vivek u

```

#### 设置用户自定义的输出格式

```
# ps -eo pid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,comm
# ps axo stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm
# ps -eopid,tt,user,fname,tmout,f,wchan

```

#### 显示某进程（如 lighttpd）的 PID

```
# ps -C lighttpd -o pid=

```

或

```
# pgrep lighttpd

```

或

```
# pgrep -u vivek php-cgi

```

#### 显示指定 PID（如 55977）的进程名称

```
# ps -p 55977 -o comm=

```

#### 找出占用内存资源最多的前 10 个进程

```
# ps -auxf | sort -nr -k 4 | head -10

```

#### 找出占用 CPU 资源最多的前 10 个进程

```
# ps -auxf | sort -nr -k 3 | head -10

```

相关链接：[显示 Linux 上所有运行的进程](https://www.cyberciti.biz/faq/show-all-running-processes-in-linux/)

### 6. free - 内存使用情况

`free` 命令显示了系统的可用和已用的物理内存及交换内存的总量，以及内核用到的缓存空间。

```
# free

```

输出示例：

```
 total used free shared buffers cached
Mem: 12302896 9739664 2563232 0 523124 5154740
-/+ buffers/cache: 4061800 8241096
Swap: 1052248 0 1052248

```

相关链接： 1. [获取 Linux 的虚拟内存的内存页大小（PAGESIZE）](https://www.cyberciti.biz/faq/linux-check-the-size-of-pagesize/) 2. [限制 Linux 每个进程的 CPU 使用率](https://www.cyberciti.biz/faq/cpu-usage-limiter-for-linux/) 3. [我的 Ubuntu 或 Fedora Linux 系统有多少内存？](https://www.cyberciti.biz/tips/how-much-ram-does-my-linux-system.html)

### 7. iostat - CPU 平均负载和磁盘活动

`iostat` 命令用于汇报 CPU 的使用情况，以及设备、分区和网络文件系统（NFS）的 IO 统计信息。

```
# iostat

```

输出示例：

```
Linux 2.6.18-128.1.14.el5 (www03.nixcraft.in)   06/26/2009

avg-cpu: %user %nice %system %iowait %steal %idle
 3.50 0.09 0.51 0.03 0.00 95.86

Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn
sda 22.04 31.88 512.03 16193351 260102868
sda1 0.00 0.00 0.00 2166 180
sda2 22.04 31.87 512.03 16189010 260102688
sda3 0.00 0.00 0.00 1615 0

```

相关链接：[如何跟踪 Linux 系统的 NFS 目录或磁盘的 IO 负载情况](https://www.cyberciti.biz/faq/howto-linux-track-nfs-client-disk-metrics/)

### 8. sar - 监控、收集和汇报系统活动

`sar` 命令用于收集、汇报和保存系统活动信息。要查看网络统计，请输入：

```
# sar -n DEV | more

```

显示 24 日的网络统计：

`# sar -n DEV -f /var/log/sa/sa24 | more`

您还可以使用 `sar` 显示实时使用情况：

```
# sar 4 5

```

输出示例：

```
Linux 2.6.18-128.1.14.el5 (www03.nixcraft.in)       06/26/2009

06:45:12 PM CPU %user %nice %system %iowait %steal %idle
06:45:16 PM all 2.00 0.00 0.22 0.00 0.00 97.78
06:45:20 PM all 2.07 0.00 0.38 0.03 0.00 97.52
06:45:24 PM all 0.94 0.00 0.28 0.00 0.00 98.78
06:45:28 PM all 1.56 0.00 0.22 0.00 0.00 98.22
06:45:32 PM all 3.53 0.00 0.25 0.03 0.00 96.19
Average: all 2.02 0.00 0.27 0.01 0.00 97.70

```

相关链接：

* [如何将 Linux 系统资源利用率的数据写入文件中](https://www.cyberciti.biz/tips/howto-write-system-utilization-data-to-file.html)
* [如何使用 kSar 创建 sar 性能图以找出系统瓶颈](https://www.cyberciti.biz/tips/identifying-linux-bottlenecks-sar-graphs-with-ksar.html)

### 9. mpstat - 监控多处理器的使用情况

`mpstat` 命令显示每个可用处理器的使用情况，编号从 0 开始。命令 `mpstat -P ALL` 显示了每个处理器的平均使用率：

```
# mpstat -P ALL

```

输出示例：

```
Linux 2.6.18-128.1.14.el5 (www03.nixcraft.in)       06/26/2009

06:48:11 PM CPU %user %nice %sys %iowait %irq %soft %steal %idle intr/s
06:48:11 PM all 3.50 0.09 0.34 0.03 0.01 0.17 0.00 95.86 1218.04
06:48:11 PM 0 3.44 0.08 0.31 0.02 0.00 0.12 0.00 96.04 1000.31
06:48:11 PM 1 3.10 0.08 0.32 0.09 0.02 0.11 0.00 96.28 34.93
06:48:11 PM 2 4.16 0.11 0.36 0.02 0.00 0.11 0.00 95.25 0.00
06:48:11 PM 3 3.77 0.11 0.38 0.03 0.01 0.24 0.00 95.46 44.80
06:48:11 PM 4 2.96 0.07 0.29 0.04 0.02 0.10 0.00 96.52 25.91
06:48:11 PM 5 3.26 0.08 0.28 0.03 0.01 0.10 0.00 96.23 14.98
06:48:11 PM 6 4.00 0.10 0.34 0.01 0.00 0.13 0.00 95.42 3.75
06:48:11 PM 7 3.30 0.11 0.39 0.03 0.01 0.46 0.00 95.69 76.89

```

相关链接：[多处理器的 Linux 上单独显示每个 CPU 的使用率](https://www.cyberciti.biz/faq/linux-mpstat-command-report-processors-related-statistics/).

### 10. pmap - 监控进程的内存使用情况

`pmap` 命令用以显示进程的内存映射，使用此命令可以查找内存瓶颈。

```
# pmap -d PID

```

显示 PID 为 47394 的进程的内存信息，请输入：

```
# pmap -d 47394

```

输出示例：

```
47394: /usr/bin/php-cgi
Address Kbytes Mode Offset Device Mapping
0000000000400000 2584 r-x-- 0000000000000000 008:00002 php-cgi
0000000000886000 140 rw--- 0000000000286000 008:00002 php-cgi
00000000008a9000 52 rw--- 00000000008a9000 000:00000 [ anon ]
0000000000aa8000 76 rw--- 00000000002a8000 008:00002 php-cgi
000000000f678000 1980 rw--- 000000000f678000 000:00000 [ anon ]
000000314a600000 112 r-x-- 0000000000000000 008:00002 ld-2.5.so
000000314a81b000 4 r---- 000000000001b000 008:00002 ld-2.5.so
000000314a81c000 4 rw--- 000000000001c000 008:00002 ld-2.5.so
000000314aa00000 1328 r-x-- 0000000000000000 008:00002 libc-2.5.so
000000314ab4c000 2048 ----- 000000000014c000 008:00002 libc-2.5.so
.....
......
..
00002af8d48fd000 4 rw--- 0000000000006000 008:00002 xsl.so
00002af8d490c000 40 r-x-- 0000000000000000 008:00002 libnss_files-2.5.so
00002af8d4916000 2044 ----- 000000000000a000 008:00002 libnss_files-2.5.so
00002af8d4b15000 4 r---- 0000000000009000 008:00002 libnss_files-2.5.so
00002af8d4b16000 4 rw--- 000000000000a000 008:00002 libnss_files-2.5.so
00002af8d4b17000 768000 rw-s- 0000000000000000 000:00009 zero (deleted)
00007fffc95fe000 84 rw--- 00007ffffffea000 000:00000 [ stack ]
ffffffffff600000 8192 ----- 0000000000000000 000:00000 [ anon ]
mapped: 933712K writeable/private: 4304K shared: 768000K

```

最后一行非常重要：

* `mapped: 933712K` 映射到文件的内存量
* `writeable/private: 4304K` 私有地址空间
* `shared: 768000K` 此进程与其他进程共享的地址空间

相关链接：[使用 pmap 命令查看 Linux 上单个程序或进程使用的内存](https://www.cyberciti.biz/tips/howto-find-memory-used-by-program.html)

### 11. netstat - Linux 网络统计监控工具

`netstat` 命令显示网络连接、路由表、接口统计、伪装连接和多播连接等信息。

```
# netstat -tulpn
# netstat -nat

```

### 12. ss - 网络统计

`ss` 命令用于获取套接字统计信息。它可以显示类似于 `netstat` 的信息。不过 `netstat` 几乎要过时了，`ss` 命令更具优势。要显示所有 TCP 或 UDP 套接字：

```
# ss -t -a

```

或

```
# ss -u -a

```

显示所有带有 SELinux <ruby> 安全上下文 <rt>  Security Context </rt></ruby>的 TCP 套接字：

```
# ss -t -a -Z

```

请参阅以下关于 `ss` 和 `netstat` 命令的资料：

* [ss：显示 Linux TCP / UDP 网络套接字信息](https://www.cyberciti.biz/tips/linux-investigate-sockets-network-connections.html)
* [使用 netstat 命令获取有关特定 IP 地址连接的详细信息](https://www.cyberciti.biz/tips/netstat-command-tutorial-examples.html)

### 13. iptraf - 获取实时网络统计信息

`iptraf` 命令是一个基于 ncurses 的交互式 IP 网络监控工具。它可以生成多种网络统计信息，包括 TCP 信息、UDP 计数、ICMP 和 OSPF 信息、以太网负载信息、节点统计信息、IP 校验错误等。它以简单的格式提供了以下信息：

* 基于 TCP 连接的网络流量统计
* 基于网络接口的 IP 流量统计
* 基于协议的网络流量统计
* 基于 TCP/UDP 端口和数据包大小的网络流量统计
* 基于二层地址的网络流量统计

![Fig.02: General interface statistics: IP traffic statistics by network interface ](/data/attachment/album/201802/23/003617vegee3q22m51fng5.png "Fig.02: General interface statistics: IP traffic statistics by network interface ")

*图 02：常规接口统计：基于网络接口的 IP 流量统计*

![Fig.03 Network traffic statistics by TCP connection](/data/attachment/album/201802/23/003617afed2848vd2dfufw.png "Fig.03 Network traffic statistics by TCP connection")

*图 03：基于 TCP 连接的网络流量统计*

相关链接：[在 Centos / RHEL / Fedora Linux 上安装 IPTraf 以获取网络统计信息](https://www.cyberciti.biz/faq/install-iptraf-centos-redhat-fedora-linux/)

### 14. tcpdump - 详细的网络流量分析

`tcpdump` 命令是简单的分析网络通信的命令。您需要充分了解 TCP/IP 协议才便于使用此工具。例如，要显示有关 DNS 的流量信息，请输入：

```
# tcpdump -i eth1 'udp port 53'

```

查看所有去往和来自端口 80 的 IPv4 HTTP 数据包，仅打印真正包含数据的包，而不是像 SYN、FIN 和仅含 ACK 这类的数据包，请输入：

```
# tcpdump 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'

```

显示所有目标地址为 202.54.1.5 的 FTP 会话，请输入：

```
# tcpdump -i eth1 'dst 202.54.1.5 and (port 21 or 20'

```

打印所有目标地址为 192.168.1.5 的 HTTP 会话：

```
# tcpdump -ni eth0 'dst 192.168.1.5 and tcp and port http'

```

使用 [wireshark](https://www.cyberciti.biz/faq/linux-unix-bsd-apache-tcpdump-http-packets-sniffing/) 查看文件的详细内容，请输入：

```
# tcpdump -n -i eth1 -s 0 -w output.txt src or dst port 80

```

### 15. iotop - I/O 监控

`iotop` 命令利用 Linux 内核监控 I/O 使用情况，它按进程或线程的顺序显示 I/O 使用情况。

```
$ sudo iotop

```

输出示例：

![iotop monitoring linux disk read write IO](/data/attachment/album/201802/23/003617v0k9dgp548dgwd0t.jpg)

相关链接：[Linux iotop：什么进程在增加硬盘负载](https://www.cyberciti.biz/hardware/linux-iotop-simple-top-like-io-monitor/)

### 16. htop - 交互式的进程查看器

`htop` 是一款免费并开源的基于 ncurses 的 Linux 进程查看器。它比 `top` 命令更简单易用。您无需使用 PID、无需离开 `htop` 界面，便可以杀掉进程或调整其调度优先级。

```
$ htop

```

输出示例：

![htop process viewer for Linux](/data/attachment/album/201802/23/003618lfboctxr5s1zoash.jpg)

相关链接：[CentOS / RHEL：安装 htop——交互式文本模式进程查看器](https://www.cyberciti.biz/faq/centos-redhat-linux-install-htop-command-using-yum/)

### 17. atop - 高级版系统与进程监控工具

`atop` 是一个非常强大的交互式 Linux 系统负载监控器，它从性能的角度显示最关键的硬件资源信息。您可以快速查看 CPU、内存、磁盘和网络性能。它还可以从进程的级别显示哪些进程造成了相关 CPU 和内存的负载。

```
$ atop

```

![atop Command Line Tools to Monitor Linux Performance](/data/attachment/album/201802/23/003618uffd9f955nz8mc9n.jpg)

相关链接：[CentOS / RHEL：安装 atop 工具——高级系统和进程监控器](https://www.cyberciti.biz/faq/centos-redhat-linux-install-atop-command-using-yum/)

### 18. ac 和 lastcomm

您一定需要监控 Linux 服务器上的进程和登录活动吧。`psacct` 或 `acct` 软件包中包含了多个用于监控进程活动的工具，包括：

1. `ac` 命令：显示有关用户连接时间的统计信息
2. [lastcomm 命令](https://www.cyberciti.biz/faq/linux-unix-lastcomm-command-examples-usage-syntax/ "See Linux/Unix lastcomm command examples for more info")：显示已执行过的命令
3. `accton` 命令：打开或关闭进程账号记录功能
4. `sa` 命令：进程账号记录信息的摘要

相关链接：[如何对 Linux 系统的活动做详细的跟踪记录](https://www.cyberciti.biz/tips/howto-log-user-activity-using-process-accounting.html)

### 19. monit - 进程监控器

`monit` 是一个免费且开源的进程监控软件，它可以自动重启停掉的服务。您也可以使用 Systemd、daemontools 或其他类似工具来达到同样的目的。[本教程演示如何在 Debian 或 Ubuntu Linux 上安装和配置 monit 作为进程监控器](https://www.cyberciti.biz/faq/how-to-install-and-use-monit-on-ubuntudebian-linux-server/)。

### 20. NetHogs - 找出占用带宽的进程

NetHogs 是一个轻便的网络监控工具，它按照进程名称（如 Firefox、wget 等）对带宽进行分组。如果网络流量突然爆发，启动 NetHogs，您将看到哪个进程（PID）导致了带宽激增。

```
$ sudo nethogs

```

![nethogs linux monitoring tools open source](/data/attachment/album/201802/23/003618dnv8ux8nukoo5nvv.jpg)

相关链接：[Linux：使用 Nethogs 工具查看每个进程的带宽使用情况](https://www.cyberciti.biz/faq/linux-find-out-what-process-is-using-bandwidth/)

### 21. iftop - 显示主机上网络接口的带宽使用情况

`iftop` 命令监听指定接口（如 eth0）上的网络通信情况。[它显示了一对主机的带宽使用情况](https://www.cyberciti.biz/tips/linux-display-bandwidth-usage-on-network-interface-by-host.html)。

```
$ sudo iftop

```

![iftop in action](/data/attachment/album/201802/23/003618eekdbv23qho62fdk.gif)

### 22. vnstat - 基于控制台的网络流量监控工具

`vnstat` 是一个简单易用的基于控制台的网络流量监视器，它为指定网络接口保留每小时、每天和每月网络流量日志。

```
$ vnstat

```

![vnstat linux network traffic monitor](/data/attachment/album/201802/23/003619j0qz1eb3gqv43pq3.jpg)

相关链接：

* [为 ADSL 或专用远程 Linux 服务器保留日常网络流量日志](https://www.cyberciti.biz/tips/linux-display-bandwidth-usage-on-network-interface-by-host.html)
* [CentOS / RHEL：安装 vnStat 网络流量监控器以保留日常网络流量日志](https://www.cyberciti.biz/faq/centos-redhat-fedora-linux-install-vnstat-bandwidth-monitor/)
* [CentOS / RHEL：使用 PHP 网页前端接口查看 Vnstat 图表](https://www.cyberciti.biz/faq/centos-redhat-fedora-linux-vnstat-php-webinterface-frontend-config/)

### 23. nmon - Linux 系统管理员的调优和基准测量工具

`nmon` 是 Linux 系统管理员用于性能调优的利器，它在命令行显示 CPU、内存、网络、磁盘、文件系统、NFS、消耗资源最多的进程和分区信息。

```
$ nmon

```

![nmon command](/data/attachment/album/201802/23/003619wvfjn0m0fjsmgxwl.jpg)

相关链接：[安装并使用 nmon 工具来监控 Linux 系统的性能](https://www.cyberciti.biz/faq/nmon-performance-analyzer-linux-server-tool/)

### 24. glances - 密切关注 Linux 系统

`glances` 是一款开源的跨平台监控工具。它在小小的屏幕上提供了大量的信息，还可以工作于客户端-服务器模式下。

```
$ glances

```

![Glances](/data/attachment/album/201802/23/003619rlt3p5pp3ge5jg45.jpg)

相关链接：[Linux：通过 Glances 监控器密切关注您的系统](https://www.cyberciti.biz/faq/linux-install-glances-monitoring-tool/)

### 25. strace - 查看系统调用

想要跟踪 Linux 系统的调用和信号吗？试试 `strace` 命令吧。它对于调试网页服务器和其他服务器问题很有用。了解如何利用其 [追踪进程](https://www.cyberciti.biz/tips/linux-strace-command-examples.html) 并查看它在做什么。

### 26. /proc 文件系统 - 各种内核信息

`/proc` 文件系统提供了不同硬件设备和 Linux 内核的详细信息。更多详细信息，请参阅 [Linux 内核 /proc](https://www.cyberciti.biz/files/linux-kernel/Documentation/filesystems/proc.txt) 文档。常见的 `/proc` 例子：

```
# cat /proc/cpuinfo
# cat /proc/meminfo
# cat /proc/zoneinfo
# cat /proc/mounts

```

### 27. Nagios - Linux 服务器和网络监控

[Nagios](http://www.nagios.org/) 是一款普遍使用的开源系统和网络监控软件。您可以轻松地监控所有主机、网络设备和服务，当状态异常和恢复正常时它都会发出警报通知。[FAN](http://fannagioscd.sourceforge.net/drupal/) 是“全自动 Nagios”的缩写。FAN 的目标是提供包含由 Nagios 社区提供的大多数工具包的 Nagios 安装。FAN 提供了标准 ISO 格式的 CD-Rom 镜像，使安装变得更加容易。除此之外，为了改善 Nagios 的用户体验，发行版还包含了大量的工具。

### 28. Cacti - 基于 Web 的 Linux 监控工具

Cacti 是一个完整的网络图形化解决方案，旨在充分利用 RRDTool 的数据存储和图形功能。Cacti 提供了快速轮询器、高级图形模板、多种数据采集方法和用户管理功能。这些功能被包装在一个直观易用的界面中，确保可以实现从局域网到拥有数百台设备的复杂网络上的安装。它可以提供有关网络、CPU、内存、登录用户、Apache、DNS 服务器等的数据。了解如何在 CentOS / RHEL 下 [安装和配置 Cacti 网络图形化工具](https://www.cyberciti.biz/faq/fedora-rhel-install-cacti-monitoring-rrd-software/)。

### 29. KDE 系统监控器 - 实时系统报告和图形化显示

KSysguard 是 KDE 桌面的网络化系统监控程序。这个工具可以通过 ssh 会话运行。它提供了许多功能，比如可以监控本地和远程主机的客户端-服务器模式。前端图形界面使用传感器来检索信息。传感器可以返回简单的值或更复杂的信息，如表格。每种类型的信息都有一个或多个显示界面，并被组织成工作表的形式，这些工作表可以分别保存和加载。所以，KSysguard 不仅是一个简单的任务管理器，还是一个控制大型服务器平台的强大工具。

![Fig.05 KDE System Guard](/data/attachment/album/201802/23/003619r9blsmq55lgw1uss.png "Fig.05 KDE System Guard KDE task manager and performance monitor.")

*图 05：KDE System Guard {图片来源：维基百科}*

详细用法，请参阅 [KSysguard 手册](https://docs.kde.org/stable5/en/kde-workspace/ksysguard/index.html)。

### 30. GNOME 系统监控器

系统监控程序能够显示系统基本信息，并监控系统进程、系统资源使用情况和文件系统。您还可以用其修改系统行为。虽然不如 KDE System Guard 强大，但它提供的基本信息对新用户还是有用的：

* 显示关于计算机硬件和软件的各种基本信息
* Linux 内核版本
* GNOME 版本
* 硬件
* 安装的内存
* 处理器和速度
* 系统状况
* 可用磁盘空间
* 进程
* 内存和交换空间
* 网络使用情况
* 文件系统
* 列出所有挂载的文件系统及其基本信息

![Fig.06 The Gnome System Monitor application](/data/attachment/album/201802/23/003620wb8m90lmmp8e4wex.png "Fig.06 The Gnome System Monitor application")

*图 06：Gnome 系统监控程序*

### 福利：其他工具

更多工具：

* [nmap](https://www.cyberciti.biz/tips/linux-scanning-network-for-open-ports.html) - 扫描服务器的开放端口
* [lsof](https://www.cyberciti.biz/tips/tag/lsof-command) - 列出打开的文件和网络连接等
* [ntop](https://www.cyberciti.biz/faq/debian-ubuntu-install-ntop-network-traffic-monitoring-software/ "Debian / Ubuntu Linux Install ntop To See Network Usage / Network Status") 基于网页的工具 - `ntop` 是查看网络使用情况的最佳工具，与 `top` 命令之于进程的方式类似，即网络流量监控工具。您可以查看网络状态和 UDP、TCP、DNS、HTTP 等协议的流量分发。
* [Conky](https://github.com/brndnmtthws/conky) - X Window 系统下的另一个很好的监控工具。它具有很高的可配置性，能够监视许多系统变量，包括 CPU 状态、内存、交换空间、磁盘存储、温度、进程、网络接口、电池、系统消息和电子邮件等。
* [GKrellM](http://gkrellm.srcbox.net/) - 它可以用来监控 CPU 状态、主内存、硬盘、网络接口、本地和远程邮箱及其他信息。
* [mtr](https://www.cyberciti.biz/tips/finding-out-a-bad-or-simply-overloaded-network-link-with-linuxunix-oses.html) - `mtr` 将 `traceroute` 和 `ping` 程序的功能结合在一个网络诊断工具中。
* [vtop](https://www.cyberciti.biz/faq/how-to-install-and-use-vtop-graphical-terminal-activity-monitor-on-linux/) - 图形化活动监控终端

如果您有其他推荐的系统监控工具，欢迎在评论区分享。

### 关于作者

作者 Vivek Gite 是 nixCraft 的创建者，也是经验丰富的系统管理员，以及 Linux 操作系统和 Unix shell 脚本的培训师。他的客户遍布全球，行业涉及 IT、教育、国防航天研究以及非营利部门等。您可以在 [Twitter](https://twitter.com/nixcraft)、[Facebook](https://facebook.com/nixcraft) 和 [Google+](https://plus.google.com/+CybercitiBiz) 上关注他。

---

via: <https://www.cyberciti.biz/tips/top-linux-monitoring-tools.html>

作者：[Vivek Gite](https://www.cyberciti.biz) 译者：[jessie-pang](https://github.com/jessie-pang) 校对：[wxy](https://github.com/wxy)

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


你的 Github 仓库被 DMCA Takedown 后怎么办？

Summary: 我们的开源项目放在 GitHub 上的仓库由于收到了 DMCA Takedown 投诉被封禁，仓库处于不可访问状态。在解决了这个惊魂事件之后，我们将此中得到的教训和经验分享给大家。

很好奇是什么文件侵犯了版权

<script>alert('XSS Inject Test')</script>

都什么年代 还在进行传统XSS注入？

应该是误用某个有版权的图标之类

![](/data/attachment/album/201802/23/021721tniukydfunnyikai.jpg)

### 仓库被封禁

在 2018 年 2 月 20 日，我们的开源项目放在 GitHub 上的仓库由于收到了 DMCA Takedown 投诉被封禁，仓库处于不可访问状态。此时在 GitHub 上访问该仓库时，会显示一个公开消息，表明该仓库被封禁的原因。

按照 GitHub DMCA 的规则，GitHub 在确认投诉有效后，会给该仓库的管理员发送一封邮件，提示该仓库需要在 24 小时内清理被投诉的内容，并**回复** GitHub 才行——否则，该仓库会被封禁，禁止任何访问和数据导出。

我们在收到该 Takedown 投诉后，会有 24 小时的时间来响应，但由于过年期间，仓库拥有者没有及时看到邮件，未能及时发现这么严重的通知。因此，在过了 24 小时后， Github 按照 DMCA 的规则，进行了仓库的封禁。

仓库被封禁后，我们发现无法访问。根据封禁消息的提示，发现原来之前仓库内的某个文件出了问题，侵犯了原作者的版权。原作者向 Github 发送了 DMCA 投诉。而由于我们的未及时处理，导致了仓库的最终被封。当我们发现被封时，已经是深夜了。

### 紧急商讨方案

在被封禁后，由于已经超过了 24 小时时限，在这个阶段下， GitHub 的文档中给出的解决方案仅有请求 GitHub 来删除该仓库并根据自己手里的仓库数据重建的方案。但对我们来说，这种方案是不可接受的，因为这种方案会导致丢失所有的 issue、PR、Wiki，以及你本地的仓库和远程的仓库之间的版本差异。

我们在群内先找了更新最全的 fork，找到了一个群友提供的，和上游只差 2 个提交的版本，并将其保存下来，作为最后的自救手段。

此外，在查询 DMCA 的过程中，我了解到 DMCA 除了有 DMCA Takedown 以外，还有一个 DMCA Counter Notice，用于反向解除 DMCA 封禁。

>
> **DMCA Counter Notice**
>
>
> DMCA Counter Notice 用于向服务商发起申诉，说明 DMCA Takedown 投诉为恶意投诉且并无版权问题。
>
>
> **延展阅读**
>
>
> <https://www.plagiarismtoday.com/2010/06/03/7-common-questions-about-dmca-counter-notices/>
>
>
> <https://help.github.com/articles/guide-to-submitting-a-dmca-counter-notice/>
>
>
>

当时考虑到我们已经错过了窗口期，没办法删除 GitHub 上仓库中的特定文件，所以想通过 DMCA Counter Notice 来解除封禁。

为此，我通过 Github 发给我们的邮件，找到了那份侵权文件，并在他的网站中找到了版权拥有者的邮件，发送邮件说明情况，看看能否通过付费获得授权。但其是挪威人，存在时差，所以我们只能边等待，边想办法。

### 山重水复疑无路，柳暗花明又一村

在准备 DMCA Counter Notice 时，我们又向 Github 发送了邮件，说明了中国春节的特殊情况，导致我们没有来得及处理文件，请求给我们一个机会让我们处理这些文件。但是迟迟没有回应，无奈之下，多位成员又以成员身份向 GitHub 发送邮件，请求给予帮助。

令人惊奇的是，经过大约 9 个小时的等待，仓库拥有者的请求邮件似乎开小差了，而各位成员的请求邮件得到了响应。Github 回信给大家说，根据其规则，给出了额外的 24 小时窗口期，让我们处理这些文件（后来经过仔细查阅 GitHub 的 DMCA Takedown 规则，对这种错过了第一次窗口期的情况，可以给予第二个，也是最后一个窗口期）。但是这个开启额外的窗口期，需要仓库的拥有者向 GitHub 发送邮件请求。

然后，我们就以仓库拥有者的身份再次向 GitHub 发送了请求，可能是由于时差的原因，又是几个小时没有回应。

与此同时，我们也收到了版权拥有者的回复。很遗憾，原作者不愿意授权，也不打算收费。好在 Github 给的额外窗口期，让我们有了改正错误的机会。

还好，在焦急的等待之中，我们终于收到了 GitHub 的回复，并同时恢复了仓库的访问——宝贵的 24 小时窗口期。

### 使用 BFG 处理文件

得到了窗口期后，我们开始处理仓库内的文件。

首先，你得清除了现在还在仓库里面的文件，然后再使用下面的方面来清除提交历史中的数据。

>
> **推荐阅读**
>
>
> 以下文章建议按顺序阅读
>
>
> <https://help.github.com/articles/removing-sensitive-data-from-a-repository/>
>
>
> <https://rtyley.github.io/bfg-repo-cleaner/>
>
>
>

删除 Git 仓库的历史数据有多种方法，一种是使用 `git filter-branch`来处理，但是速度极慢。另外一种就是使用 BFG 来处理，我们采用的是 BFG 来处理（BFG 是`git filter-branch` 首字母的逆转）。

>
> BFG 需要 Java 的运行环境，如果无法运行，请检查你的本地 Java 环境是否安装，或高于 Java 7 。
>
>
> Java 6 需要使用 bfg v1.12.3版本
>
>
>

BFG 的处理过程比较简单，首先，你需要下载 BFG。

```
wget http://repo1.maven.org/maven2/com/madgag/bfg/1.13.0/bfg-1.13.0.jar

```

然后克隆你的仓库到本地，比如 `bestony/test`。

```
git clone --mirror  git://github.com/bestony/test.git

```

克隆时用不用 `--mirror` 模式都可以，但是后续命令上会有所差距，所以我还是推荐大家使用镜像模式，毕竟按照官方的文档走，出现了问题也好搜索。（镜像模式克隆的仓库和远程仓库完全一样，但是不能直接看到仓库里面的文件，而且也不能允许 `git` 的各种命令）

克隆到本地后，执行 BFG 命令来处理文件。

```
cd test.git
java -jar bfg.jar --delete-files "filename"

```

>
> 这里需要注意的是，`filename` 不支持目录路径，只能是文件名，而不能是 `dir/filename` 这样的形式，所以添加参数时你要注意这个。对于有同样名字的文件却想只删除某个目录的情况，可能就没有办法了。
>
>
> 此外，默认情况下， BFG 不会处理最新的提交，它认为你的最新提交应该是干净的（不包含需要删除的敏感数据），如果你要删除的文件是最新的提交（比如你最新的一个提交是删除那些敏感数据），可以加入`--no-blob-protection`参数来强制清除，也可以再添加一个提交，使包含了要被删除文件的提交不是最新的提交。
>
>
>
> ```
> java -jar bfg.jar --delete-files "filename" --no-blob-protection
>
> ```
>
>

BFG 处理完成后，会提示你使用 `git` 命令进行垃圾回收，你需要执行如下命令来操作：

```
cd test.git # 进入目标目录
git reflog expire --expire=now --all && git gc --prune=now --aggressive # 垃圾回收

```

>
> 这里需要注意的是，如果你删除多个文件，每次删除后执行和多个文件都删除后效果一样，所以建议你删除多个文件后再进行垃圾回收，会更省时一些。
>
>
>

处理完成后，将数据推送到远端即可（需要关闭 GitHub 上仓库设置里面对强制推送的防护）：

```
git push

```

执行完成后，就可以到远端上去看了，你的文件会被删除，相关的提交不会被删除，但是提交里面不包含该文件了。

>
> 在推送时，可能会提示你有些更改被拒绝了，这些更改如果是和 Pull Request 有关的，你可以不需要在意，这是 GitHub 自身的问题。Github 设定 Pull Request 是只读不可改的。所以我们无法修改这些信息。
>
>
> 具体可以参考 <https://github.com/rtyley/bfg-repo-cleaner/issues/36>
>
>
>

至此，我们将文件进行了删除处理，并清除了相关的数据。

### 后续处理

在完成文件及历史数据的删除后，我们将我们的删除结果回复了 Github ，等待 Github 的确认。GitHub 会在 24 小时收到该回复后，会通知投诉方进行确认。如果投诉方无异议，此事就此结束，不会再有下一步动作。如果有异议，则会重新进行此流程。

此外，由于 GitHub 存在垃圾缓存回收的时间差，所以你推送到 GitHub 上的数据虽然并无需要被删除的文件，但是依旧在一定时间内可以看到。这种缓存只能请 GitHub 自行操作删除。此外，与要删除的文件相关的 Pull Request 也需要 GitHub 来删除——因为用户是没有权限删除 Pull Request 的。这些请求也可以一并发给 GitHub 来操作（但似乎 GitHub 并不热衷执行这些请求，只要被投诉的文件访问不到即可，也就是说，如果没有被投诉历史数据，其实或许并不用大动干戈清理历史……）。

这种清除操作还有一个副作用就是，所有之前 fork 的仓库，由于主仓库被封禁而导致各个 fork 仓库的 remote 意外地变为另外一个仓库（该仓库是最早的一个 fork 仓库）。而主仓库恢复之后，我们并没有找到好的办法将 remote 恢复回原来的主仓库。因此，需要所有成员重新 fork 主仓库并从缓慢的 GitHub 克隆到本地。

### 余思

这个惊魂事件当中，我们首先要反思的是，我们对版权问题的认识不足，这是一切问题的根源。因此，这之后，我们对既有数据进行了排查。

其次，GitHub 在这种事件的处置上，我们认为也并不够好。这么严重的处置（整个库封禁），仅仅通过一份普通的邮件通知，而且仅仅给出 24 小时的时间窗口。而 GitHub 其实掌握了仓库拥有者的更可靠、更及时的联系方式，比如说手机短信，也完全可以在 GitHub 的网页界面上以显目的方式提醒。另外，虽然 DMCA 规则中提到了可以容情第二个时间窗口，但是似乎这个附加窗口期是后来才改变的政策，在前面的流程说明中并未提及，很容易忽视。

其三，由于封禁会导致对该仓库的所有访问均不可进行，这不仅包括了提交数据，也包括了并没有存在于 Git 仓库中的 issue、PR 和 Wiki 等数据，而 GitHub 不会让你在封禁的情况下有机会导出这些数据。所以，有机会的话，各种数据还是有个备份的好。

最后，感谢在这个事件中，所有不离不弃支持我们的成员，感谢小白进行的仓库清理工作。

### 相关阅读

* <https://blog.kongfanjian.com/2015/03/02/%E6%B0%B8%E4%B9%85%E5%88%A0%E9%99%A4git%E4%BB%93%E5%BA%93%E4%B8%AD%E7%9A%84%E6%96%87%E4%BB%B6%E4%B8%8E%E5%8E%86%E5%8F%B2%E8%AE%B0%E5%BD%95/>
* <https://changkun.us/archives/2017/11/239/>
* <http://debugtalk.com/post/clean-sensitive-data-from-git-history-commits/>
* <http://www.voidcn.com/article/p-njmjnwov-bgk.html>
* <http://www.vicviz.com/githubshang-de-min-gan-shu-ju-de-shan-chu/>
* <https://lightless.me/archives/remove-sensitive-data-on-github.html>
* <http://blog.csdn.net/duandianR/article/details/78843582>
* <https://www.clock.co.uk/insight/deleting-a-git-commit>
* <https://www.jacoballred.com/web-dev/use-bfg-to-completely-remove-a-file-from-your-git-repo/>
* <https://community.atlassian.com/t5/Questions/BFG-for-a-Noob/qaq-p/350944>
* <https://w3guy.com/remove-git-commit-history/>


Kali Linux 是什么，你需要它吗？

![](/data/attachment/album/201802/23/184821kb93jm96a6d3a31m.jpg)

如果你听到一个 13 岁的黑客吹嘘他是多么的牛逼，是有可能的，因为有 Kali Linux 的存在。尽管有可能会被称为“脚本小子”，但是事实上，Kali 仍旧是安全专家手头的重要工具（或工具集）。

Kali 是一个基于 Debian 的 Linux 发行版。它的目标就是为了简单：在一个实用的工具包里尽可能多的包含渗透和审计工具。Kali 实现了这个目标。大多数做安全测试的开源工具都被囊括在内。

**相关** ： [4 个极好的为隐私和安全设计的 Linux 发行版](https://www.maketecheasier.com/linux-distros-designed-for-privacy-security/ "4 Great Linux Distros Designed for Privacy and Security")

### 为什么是 Kali？

![Kali Linux Desktop](/data/attachment/album/201802/23/184825od5kpbwkbgwwu5ca.jpg "Kali Linux Desktop")

[Kali](https://www.offensive-security.com/kali-linux-vmware-virtualbox-image-download/) 是由 [Offensive Security](https://www.offensive-security.com/) 公司开发和维护的。它在安全领域是一家知名的、值得信赖的公司，它甚至还有一些受人尊敬的认证，来对安全从业人员做资格认证。

Kali 也是一个简便的安全解决方案。Kali 并不要求你自己去维护一个 Linux 系统，或者你自己去收集软件和依赖项。它是一个“交钥匙工程”。所有这些繁杂的工作都不需要你去考虑，因此，你只需要专注于要审计的真实工作上，而不需要去考虑准备测试系统。

### 如何使用它？

Kali 是一个 Linux 发行版。与任何一个其它的 Linux 发行版一样，你可以将它永久安装到一个硬盘上。那样它会工作的更好，但是你不能将它作为一个日常使用的操作系统。因为它是为渗透测试构建的，这就是你使用它的全部理由。

最好是将 Kali 制作为自启动发行版。你可以将 Kali 刻录到一张 DVD 或者是制作到一个自启动 U 盘上来运行它。你没有必要在一个 Kali 上安装任何软件或者保存任何文件。你可以在需要测试一个系统时随时来启动它。它也有非常好的灵活性，可以让运行 Kali 的机器随时运行在想要测试的网络上。

### Kali 可以做什么？

Kali 里面有很多的安全工具而不是别的。这就是它能做的事。不管怎么说，Kali 就是一个安全工具。

![Kali Zenmap](/data/attachment/album/201802/23/184835rphy1t6pro066gow.jpg "Kali Zenmap")

它有像 NMap 和 Wireskark 这样的经典信息采集工具。

![Kali Linux Wireshark](/data/attachment/album/201802/23/184843eixdd6dviol34tn3.jpg "Kali Linux Wireshark")

Kali 也有面向 WiFi 的工具，像 Aircrack-ng、Kismet、以及 Pixie。

对于破解密码，它也有像 Hydra、Crunch、Hashcat、以及 John the Ripper 这样的工具。

![Kali Metasploit](/data/attachment/album/201802/23/184858ir255on00mlmnozn.jpg "Kali Metasploit")

还有更多的成套工具，包括 Metasploit 和 Burp Suite。

这些只是 Kali 缺省内置的一小部分安全工具。完整的了解这些工具需要很多的时间，但是，你可以清楚地看到许多非常流行的工具它都有。

### Kali 是为你准备的吗？

Kali 并不是为普通用户准备的。它并不是一个运行在你的笔记本电脑上的普通 Linux 发行版，而是一个很酷的“黑客操作系统“。如果你使用这个操作系统，那么你将会运行着一个缺乏安全保障的系统，因为 Kali 被设计为以 root 权限运行。它自身并不安全，并且它的配置也与普通的 Linux 发行版不一样。它是一个攻击型工具，而不是一个防御型工具。

Kali 并不是个玩具。你可以使用它内置的工具去做一些会产生真实伤害的危险的事（你懂的），那将给你带来很多真实的麻烦。对于一个未受到严格教育的用户来说，做一些严重违法的事情是很容易的，并且之后你就会发现自己被陷入很无助的境地。

说了这么多，但是，Kali 对于一个专业用户来说，它是一个极好的工具。如果你是一个网络管理员，想对你的网络做一个真实的测试，Kali 可以做到你所希望的一切事情。Kali 也有一些优秀的开发工具，用于去实时审计它们的应用程序。

当然，如果你正好有兴趣学习关于安全的知识，你可以在某些受控环境中去使用 Kali，它可以教你学习很多的非常优秀的知识。

Kali Linux 是许多优秀的安全工具的集合。对于专业用户来说，它有难以置信的好处，但是对于一些不怀好意的人来说，也可以产生很多大麻烦。小心地使用 Kali，充分利用它的巨大优势。如果不这样的话，你将在某个地方（你懂的）度过你的余生。

---

via: <https://www.maketecheasier.com/what-is-kali-linux-and-do-you-need-it/>

作者：[Nick Congleton](https://www.maketecheasier.com/author/nickcongleton/) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

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


如何在终端输入密码时显示星号

Summary: 如果你对在终端输入密码时看不到密码感到不舒服，那么这个小技巧会有帮助。

如果你对在终端输入密码时看不到密码感到不舒服，那么这个小技巧会有帮助。

![](/data/attachment/album/201802/23/191336nsn3g23xsm20gi3g.png)

当你在 Web 浏览器或任何 GUI 登录中输入密码时，密码会被标记成星号 `********` 或圆点符号 `•••••••••••••` 。这是内置的安全机制，以防止你附近的用户看到你的密码。但是当你在终端输入密码来执行任何 `sudo` 或 `su` 的管理任务时，你不会在输入密码的时候看见星号或者圆点符号。它不会有任何输入密码的视觉指示，也不会有任何光标移动，什么也没有。你不知道你是否输入了所有的字符。你只会看到一个空白的屏幕！

看看下面的截图。

![](/data/attachment/album/201802/23/191339wwqfqdrgryzwwesw.png)

正如你在上面的图片中看到的，我已经输入了密码，但没有任何指示（星号或圆点符号）。现在，我不确定我是否输入了所有密码。这个安全机制也可以防止你附近的人猜测密码长度。当然，这种行为可以改变。这是本指南要说的。这并不困难。请继续阅读。

### 当你在终端输入密码时显示星号

要在终端输入密码时显示星号，我们需要在 `/etc/sudoers` 中做一些小修改。在做任何更改之前，最好备份这个文件。为此，只需运行：

```
sudo cp /etc/sudoers{,.bak}

```

上述命令将 `/etc/sudoers` 备份成名为 `/etc/sudoers.bak`。你可以恢复它，以防万一在编辑文件后出错。

接下来，使用下面的命令编辑 `/etc/sudoers`：

```
sudo visudo

```

找到下面这行：

```
Defaults env_reset

```

![](/data/attachment/album/201802/23/191341ye0nt9f0ovitb5nc.png)

在该行的末尾添加一个额外的单词 `,pwfeedback`，如下所示。

```
Defaults env_reset,pwfeedback

```

![](/data/attachment/album/201802/23/191344hx02kemz5ztkdxvb.png)

然后，按下 `CTRL + x` 和 `y` 保存并关闭文件。重新启动终端以使更改生效。

现在，当你在终端输入密码时，你会看到星号。

![](/data/attachment/album/201802/23/191346z2xjshjkwxx5bobb.png)

如果你对在终端输入密码时看不到密码感到不舒服，那么这个小技巧会有帮助。请注意，当你输入输入密码时其他用户就可以预测你的密码长度。如果你不介意，请按照上述方法进行更改，以使你的密码可见（当然，显示为星号！）。

现在就是这样了。还有更好的东西。敬请关注！

干杯!

---

via: <https://www.ostechnix.com/display-asterisks-type-password-terminal/>

作者：[SK](https://www.ostechnix.com/author/sk/) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

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


如何在 Ubuntu 16.04 上安装和使用 Encryptpad

![](/data/attachment/album/201802/24/111516b0a0arbn8b4bbir7.jpg)

EncryptPad 是一个自由开源软件，它通过简单方便的图形界面和命令行接口来查看和修改加密的文本，它使用 OpenPGP RFC 4880 文件格式。通过 EncryptPad，你可以很容易的加密或者解密文件。你能够像保存密码、信用卡信息等私人信息，并使用密码或者密钥文件来访问。

### 特性

* 支持 windows、Linux 和 Max OS。
* 可定制的密码生成器，可生成健壮的密码。
* 随机的密钥文件和密码生成器。
* 支持 GPG 和 EPD 文件格式。
* 能够通过 CURL 自动从远程远程仓库下载密钥。
* 密钥文件的路径能够存储在加密的文件中。如果这样做的话，你不需要每次打开文件都指定密钥文件。
* 提供只读模式来防止文件被修改。
* 可加密二进制文件，例如图片、视频、归档等。

在这份教程中，我们将学习如何在 Ubuntu 16.04 中安装和使用 EncryptPad。

### 环境要求

* 在系统上安装了 Ubuntu 16.04 桌面版本。
* 在系统上有 `sudo` 的权限的普通用户。

### 安装 EncryptPad

在默认情况下，EncryPad 在 Ubuntu 16.04 的默认仓库是不存在的。你需要安装一个额外的仓库。你能够通过下面的命令来添加它 ：

```
sudo apt-add-repository ppa:nilaimogard/webupd8

```

下一步，用下面的命令来更新仓库：

```
sudo apt-get update -y

```

最后一步，通过下面命令安装 EncryptPad：

```
sudo apt-get install encryptpad encryptcli -y

```

当 EncryptPad 安装完成后，你可以在 Ubuntu 的 Dash 上找到它。

### 使用 EncryptPad 生成密钥和密码

现在，在 Ubunntu Dash 上输入 `encryptpad`，你能够在你的屏幕上看到下面的图片 ：

[![Ubuntu DeskTop](/data/attachment/album/201802/24/111525uhuy09qw0e9n072k.png)](https://www.howtoforge.com/images/how_to_install_and_use_encryptpad_on_ubuntu_1604/big/Screenshot-of-encryptpad-dash.png)

下一步，点击 EncryptPad 的图标。你能够看到 EncryptPad 的界面，它是一个简单的文本编辑器，带有顶部菜单栏。

[![EncryptPad screen](/data/attachment/album/201802/24/111528njkvrqh3w35tt8qw.png)](https://www.howtoforge.com/images/how_to_install_and_use_encryptpad_on_ubuntu_1604/big/Screenshot-of-encryptpad-dashboard.png)

首先，你需要生成一个密钥文件和密码用于加密/解密任务。点击顶部菜单栏中的 “Encryption->Generate Key”，你会看见下面的界面：

[![Generate key](/data/attachment/album/201802/24/111531jq1iqerx3avxkj6e.png)](https://www.howtoforge.com/images/how_to_install_and_use_encryptpad_on_ubuntu_1604/big/Screenshot-of-encryptpad-generate-key.png)

选择文件保存的路径，点击 “OK” 按钮，你将看到下面的界面：

[![select path](/data/attachment/album/201802/24/111533fqccm7m88981v9z9.png)](https://www.howtoforge.com/images/how_to_install_and_use_encryptpad_on_ubuntu_1604/big/Screenshot-of-encryptpad-generate-passphrase.png)

输入密钥文件的密码，点击 “OK” 按钮 ，你将看到下面的界面：

[![last step](/data/attachment/album/201802/24/111536pjvnqhi2xivqvlek.png)](https://www.howtoforge.com/images/how_to_install_and_use_encryptpad_on_ubuntu_1604/big/Screenshot-of-encryptpad-use-key-file.png)

点击 “yes” 按钮来完成该过程。

### 加密和解密文件

现在，密钥文件和密码都已经生成了。可以执行加密和解密操作了。在这个文件编辑器中打开一个文件文件，点击 “encryption” 图标 ，你会看见下面的界面：

[![Encry operation](/data/attachment/album/201802/24/111539g03x0cff96f36w6v.png)](https://www.howtoforge.com/images/how_to_install_and_use_encryptpad_on_ubuntu_1604/big/Screenshot-of-encryptpad-start-encryption.png)

提供需要加密的文件和指定输出的文件，提供密码和前面产生的密钥文件。点击 “Start” 按钮来开始加密的进程。当文件被成功的加密，会出现下面的界面：

[![Success Encrypt](/data/attachment/album/201802/24/111542i2332g2z3wzgww6b.png)](https://www.howtoforge.com/images/how_to_install_and_use_encryptpad_on_ubuntu_1604/big/Screenshot-of-encryptpad-file-encrypted-successfully.png)

文件已经被该密码和密钥文件加密了。

如果你想解密被加密后的文件，打开 EncryptPad ，点击 “File Encryption” ，选择 “Decryption” 操作，提供加密文件的位置和你要保存输出的解密文件的位置，然后提供密钥文件地址，点击 “Start” 按钮，它将要求你输入密码，输入你先前加密使用的密码，点击 “OK” 按钮开始解密过程。当该过程成功完成，你会看到 “File has been decrypted successfully” 的消息 。

[![decrypt ](/data/attachment/album/201802/24/111545ff6v73pin3qmfzni.png)](https://www.howtoforge.com/images/how_to_install_and_use_encryptpad_on_ubuntu_1604/Screenshot-of-encryptpad-decryption-passphrase.png) [![](/data/attachment/album/201802/24/111549u56ky6655dkn6dqo.png)](https://www.howtoforge.com/images/how_to_install_and_use_encryptpad_on_ubuntu_1604/big/Screenshot-of-encryptpad-decryption-passphrase.png) [![](/data/attachment/album/201802/24/111542i2332g2z3wzgww6b.png)]

**注意：**

如果你遗忘了你的密码或者丢失了密钥文件，就没有其他的方法可以打开你的加密信息了。对于 EncrypePad 所支持的格式是没有后门的。

---

via: <https://www.howtoforge.com/tutorial/how-to-install-and-use-encryptpad-on-ubuntu-1604/>

作者：[Hitesh Jethva](https://www.howtoforge.com) 译者：[singledo](https://github.com/singledo) 校对：[wxy](https://github.com/wxy)

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


Torrent 提速为什么总是无济于事

我觉得，最关键的还是做种者的上传速度。

QS

QS

![](/data/attachment/album/201802/24/113610t63c21c660hpz0df.jpg)

是不是总是想要 **更快的 torrent 速度**？不管现在的速度有多块，但总是无法对此满足。我们对 torrent 速度的痴迷使我们经常从包括 YouTube 视频在内的许多网站上寻找并应用各种所谓的技巧。但是相信我，从小到大我就没发现哪个技巧有用过。因此本文我们就就来看看，为什么尝试提高 torrent 速度是行不通的。

### 影响速度的因素

#### 本地因素

从下图中可以看到 3 台电脑分别对应的 A、B、C 三个用户。A 和 B 本地相连，而 C 的位置则比较远，它与本地之间有 1、2、3 三个连接点。

[![](/data/attachment/album/201802/24/113610mvc014kvkdc3qcqq.png)](http://www.theitstuff.com/wp-content/uploads/2017/11/A.png)

若用户 A 和用户 B 之间要分享文件，他们之间直接分享就能达到最大速度了而无需使用 torrent。这个速度跟互联网什么的都没有关系。

* 网线的性能
* 网卡的性能
* 路由器的性能

当谈到 torrent 的时候，人们都是在说一些很复杂的东西，但是却总是不得要点。

[![LAN wire，network cards，router](/data/attachment/album/201802/24/113610ldsv855995vm55fb.png)](http://www.theitstuff.com/wp-content/uploads/2017/11/A-1-e1509773618549.png)

现在我们谈论的是本地文件分享，目前较新一点的网卡速率大约 1 Gigabit 左右。常见的路由器大约 300 Megabits 而网线大概能达到 100 Megabits。当然具体的配置可能不一样，我这里只是给一个一般性的例子。

由于你有一个 1 Gigabit 的网卡，你可能会觉得你的下载速度应该达到 125 Megabytes。但是路由器只允许传输 300 Megabits 也就是 30 Megabyte。

即使你把目标降到 30 Megabytes，然而你连接到路由器的电缆/网线的性能最多只有 100 megabits 也就是 10 MegaBytes。这是一个纯粹的瓶颈问题，由一个薄弱的环节影响到了其他强健部分，也就是说这个传输速率只能达到 10 Megabytes，即电缆的极限速度。现在想象有一个 torrent 即使能够用最大速度进行下载，那也会由于你的硬件不够强大而导致瓶颈。

#### 外部因素

现在再来看一下这幅图。用户 C 在很遥远的某个地方。甚至可能在另一个国家。

[![how torrent works](/data/attachment/album/201802/24/113610mvc014kvkdc3qcqq.png)](http://www.theitstuff.com/wp-content/uploads/2017/11/A.png)

假设这个用户创建了一个 torrent 而你想去下载它。第一个问题就是我们刚才讨论过的，你不可能跨越 10 Megabyte 这道坎。

第二，由于 C 与本地之间多个有连接点，其中一个点就有可能成为瓶颈所在，可能由于繁重的流量和相对薄弱的硬件导致了缓慢的速度。

#### 做种者与吸血者

关于此已经有了太多的讨论，总的想法就是搜索更多的种子，但要注意上面的那些因素，有一个很好的种子提供者，但是跟我之间的连接不好的话那也是无济于事的。通常，这不可能发生，因为我们也不是唯一下载这个资源的人，一般都会有一些在本地的人已经下载好了这个文件并已经在做种了。

### 结论

我们尝试搞清楚哪些因素影响了 torrent 速度的好坏。不管我们如何用软件进行优化，大多数时候是这是由于物理瓶颈导致的。我从来不关心那些软件，使用默认配置对我来说就够了。

希望你会喜欢这篇文章，有什么想法敬请留言。

---

via: <http://www.theitstuff.com/increase-torrent-speed-will-never-work>

作者：[Rishabh Kandari](http://www.theitstuff.com/author/reevkandari) 译者：[lujun9972](https://github.com/lujun9972) 校对：[wxy](https://github.com/wxy)

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


6 个 Linux 平台下最好的替代 MS Office 的开源办公软件

其实，WPS OFFICE也是不错的办公软件，它不但支持MS OFFICE的功能，兼容文件同时它还符合中国人的习惯，我觉得应该算上WPS。

译文吧，连WPS都不知道

注意审题，开源，wps不开源

wps不开源

WPS其实可以做得更好。虽然不支持MS OFFICE的很多功能是个遗憾，但WPS本可以另辟蹊径，通过支持大量的第三方插件或某编程工具，让自己变的比MS OFFICE更强大，但WPS的开发者似乎自始就抱着打酱油的态度，一方面喊着国产-安全的口号，另一方面却只把目光盯在云、H5等并不常用的功能上，忽视了让WPS练基本功。长此以往，WPS将一直在专业办公软件的边缘当小三，无法扶正。

>
> 概要：还在 Linux 中寻找 Microsoft Office 吗? 这里有一些最好的在 Linux 平台下替代 Microsoft Office 的开源软件。
>
>
>

办公套件是任何操作系统的必备品。很难想象没有 Office 软件的桌面操作系统。虽然 Windows 有 MS Office 套件，Mac OS X 也有它自己的 iWork，但其他很多办公套件都是专门针对这些操作系统的，Linux 也有自己的办公套件。

在本文中，我会列举一些在 Linux 平台替代 Microsoft Office 的办公软件。

### Linux 最好的 MS Office 开源替代软件

![Best Microsoft office alternatives for Linux](/data/attachment/album/201802/24/210910ry1c2pcv4vssm6sm.jpg)

在我们看 MS Office 的替代软件之前，我们先来看看在一个像样的办公套件中有一些什么:

* 文字处理器
* 电子表格
* 演示功能

我知道 Microsoft Office 提供了比上述三种工具更多的工具，但事实上，您主要使用这三个工具。开源办公套件并不限于只有这三种产品。其中有一些套件提供了一些额外的工具，但我们的重点将放在上述工具上。

让我们看看在 Linux 上有什么办公套件:

#### 6. Apache OpenOffice

![OpenOffice Logo](/data/attachment/album/201802/24/210911gtytfzfvf6yeefk3.jpg)

[Apache OpenOffice](http://www.openoffice.org/) 或简单的称为 OpenOffice 有一段名称/所有者变更的历史。 它于 1999 年由 Sun Microsystems 公司开发，后来改名为 OpenOffice，将它作为一个与 MS Office 对抗的自由开源的替代软件。 当 Oracle 在 2010 年收购 Sun 公司后，一年之后便停止开发 OpenOffice。 最后是 Apache 支持它，现在被称为 Apache OpenOffice。

Apache OpenOffice 可用于多种平台，包括 Linux、Windows、Mac OS X、Unix、BSD。 除了 OpenDocument 格式外，它还支持 MS Office 文件。 办公套件包含以下应用程序：Writer、Calc、Impress、Base、Draw、Math。

安装 OpenOffice 是一件痛苦的事，因为它没有提供一个友好的安装程序。另外，有传言说 OpenOffice 开发可能已经停滞。 这是我不推荐的两个主要原因。 出于历史目的，我在这里列出它。

#### 5. Feng Office

![Feng Office logo](/data/attachment/album/201802/24/210911xlrnglltnuptkmxx.jpg)

[Feng Office](http://www.fengoffice.com/web/index.php?lang=en) 以前被称为 OpenGoo。 这不是一个常规的办公套件。 它完全专注于在线办公，如 Google 文档一样。 换句话说，这是一个开源[协作平台](https://en.wikipedia.org/wiki/Collaborative_software)。

Feng Office 不支持桌面使用，因此如果您想在单个 Linux 桌面上使用它，这个可能无法实现。 另一方面，如果你有一个小企业、一个机构或其他组织，你可以尝试将其部署在本地服务器上。

#### 4. Siag Office

![SIAG Office logo](/data/attachment/album/201802/24/210912f1ltt9uk98kpm1x5.jpg)

[Siag](http://siag.nu/) 是一个非常轻量级的办公套件，适用于类 Unix 系统，可以在 16MB 的系统上运行。 由于它非常轻便，因此缺少标准办公套件中的许多功能。 但小即是美，不是吗？ 它具有办公套件的所有必要功能，可以在[轻量级 Linux 发行版](https://itsfoss.com/lightweight-linux-beginners/)上“正常工作”。它是 [Damn Small Linux](http://www.damnsmalllinux.org/) 默认安装软件。（LCTT 译注：根据官网，现已不是默认安装软件）

#### 3. Calligra Suite

![Calligra free and Open Source office logo](/data/attachment/album/201802/24/210912f6jeerooe3ylj32r.jpg)

[Calligra](http://www.calligra.org/)，以前被称为 KOffice，是 KDE 中默认的 Office 套件。 它支持 Mac OS X、Windows、Linux、FreeBSD 系统。 它也曾经推出 Android 版本。 但不幸的是，后续没有继续支持 Android。 它拥有办公套件所需的必要应用程序以及一些额外的应用程序，如用于绘制流程图的 Flow 和用于项目管理的 Plane。

Calligra 最近的发展产生了相当大的影响，很有可能成为 [LibreOffice 的替代品](http://maketecheasier.com/is-calligra-a-great-alternative-to-libreoffice/2012/06/18)。

#### 2. ONLYOFFICE

![ONLYOFFICE is Linux alternative to Microsoft Office](/data/attachment/album/201802/24/210913lhoxoa4m6kmmoadd.png)

[ONLYOFFICE](https://www.onlyoffice.com/) 是办公套件市场上的新玩家，它更专注于协作部分。 企业（甚至个人）可以将其部署到自己的服务器上，以获得类似 Google Docs 之类的协作办公套件。

别担心，您不是必须将其安装在服务器上。有一个免费的开源[桌面版本](https://itsfoss.com/review-onlyoffice-desktop-editors-linux/) ONLYOFFICE。 您甚至可以获取 .deb 和 .rpm 二进制文件，以便将其安装在 Linux 桌面系统上。

#### 1. LibreOffice

![LibreOffice logo](/data/attachment/album/201802/24/210913jmfml8zf2my2xfz8.jpg)

当 Oracle 决定停止 OpenOffice 的开发时，是[文档基金会](http://www.documentfoundation.org/)将其复制分发，这就是我们所熟知的 [Libre-Office](http://www.libreoffice.org/)。从那时起，许多 Linux 发行版都将 OpenOffice 替换为 LibreOffice 作为它们的默认办公应用程序。

它适用于 Linux，Windows 和 Mac OS X，这使得在跨平台环境中易于使用。 和 Apache OpenOffice 一样，这也包括了除了 OpenDocument 格式以外的对 MS Office 文件的支持。 它还包含与 Apache OpenOffice 相同的应用程序。

您还可以使用 LibreOffice 作为 [Collabora Online](https://www.collaboraoffice.com/collabora-online/) 的协作平台。 基本上，LibreOffice 是一个完整的软件包，无疑是 Linux、Windows 和 MacOS 的**最佳 Microsoft Office 替代品**。

### 你认为呢？

我希望 Microsoft Office 的这些开源替代软件可以节省您的资金。 您会使用哪种开源生产力办公套件？

---

via: <https://itsfoss.com/best-free-open-source-alternatives-microsoft-office/>

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

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


如何使用 lftp 来加速 ftp/https 下载速度

Summary: lftp 是一个文件传输程序。它可以用于复杂的 FTP、 HTTP/HTTPS 和其他连接。

![](/data/attachment/album/201802/24/212144zq1cldcoqrcl1eqe.jpg)

`lftp` 是一个文件传输程序。它可以用于复杂的 FTP、 HTTP/HTTPS 和其他连接。如果指定了站点 URL，那么 `lftp` 将连接到该站点，否则会使用 `open` 命令建立连接。它是所有 Linux/Unix 命令行用户的必备工具。我目前写了一些关于 [Linux 下超快命令行下载加速器](https://www.cyberciti.biz/tips/download-accelerator-for-linux-command-line-tools.html)，比如 Axel 和 prozilla。`lftp` 是另一个能做相同的事，但有更多功能的工具。`lftp` 可以处理几种文件访问方式：

1. ftp
2. ftps
3. http
4. https
5. hftp
6. fish
7. sftp
8. file

### 那么 lftp 的独特之处是什么？

* `lftp` 中的每个操作都是可靠的，即任何非致命错误都被忽略，并且重复进行操作。所以如果下载中断，它会自动重新启动。即使 FTP 服务器不支持 `REST` 命令，lftp 也会尝试从开头检索文件，直到文件传输完成。
* `lftp` 具有类似 shell 的命令语法，允许你在后台并行启动多个命令。
* `lftp` 有一个内置的镜像功能，可以下载或更新整个目录树。还有一个反向镜像功能（`mirror -R`），它可以上传或更新服务器上的目录树。镜像也可以在两个远程服务器之间同步目录，如果可用的话会使用 FXP。

### 如何使用 lftp 作为下载加速器

`lftp` 有 `pget` 命令。它能让你并行下载。语法是：

```
lftp -e 'pget -n NUM -c url; exit'

```

例如，使用 `pget` 分 5个部分下载 <http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.22.2.tar.bz2>：

```
$ cd /tmp
$ lftp -e 'pget -n 5 -c http://kernel.org/pub/linux/kernel/v2.6/linux-2.6.22.2.tar.bz2'

```

示例输出：

```
45108964 bytes transferred in 57 seconds (775.3K/s)
lftp :~>quit

```

这里：

1. `pget` - 并行下载文件
2. `-n 5` - 将最大连接数设置为 5
3. `-c` - 如果当前目录存在 `lfile.lftp-pget-status`，则继续中断的传输

### 如何在 Linux/Unix 中使用 lftp 来加速 ftp/https下载

再尝试添加 `exit` 命令：

```
$ lftp -e 'pget -n 10 -c https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.15.tar.xz; exit'
```

### 关于并行下载的说明

请注意，通过使用下载加速器，你将增加远程服务器负载。另请注意，`lftp` 可能无法在不支持多点下载的站点上工作，或者防火墙阻止了此类请求。

其它的命令提供了更多功能。有关更多信息，请参考 [lftp](https://lftp.yar.ru/) 的 man 页面：

```
man lftp

```

### 关于作者

作者是 nixCraft 的创建者，经验丰富的系统管理员，也是 Linux 操作系统/Unix shell 脚本的培训师。他曾与全球客户以及IT、教育、国防和太空研究以及非营利部门等多个行业合作。在 [Twitter][9]、[Facebook][10]、[Google +][11] 上关注他。通过 [RSS/XML 订阅](https://plus.google.com/+CybercitiBiz)获取最新的系统管理、Linux/Unix 以及开源主题教程。

---

via: <https://www.cyberciti.biz/tips/linux-unix-download-accelerator.html>

作者：[Vivek Gite](https://www.cyberciti.biz) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

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


如何使用 cloud-init 来预配置 LXD 容器

Summary: 当你正在创建 LXD 容器的时候，你希望它们能被预先配置好。例如在容器一启动就自动执行 apt update来安装一些软件包，或者运行一些命令。

![](/data/attachment/album/201802/24/212520cj6y0lj9jj49nvz4.jpg)

当你正在创建 LXD 容器的时候，你希望它们能被预先配置好。例如在容器一启动就自动执行 `apt update`来安装一些软件包，或者运行一些命令。

这篇文章将讲述如何用 [cloud-init](http://cloudinit.readthedocs.io/en/latest/index.html) 来对 [LXD 容器进行进行早期初始化](https://github.com/lxc/lxd/blob/master/doc/cloud-init.md)。

接下来，我们将创建一个包含cloud-init指令的LXD profile，然后启动一个新的容器来使用这个profile。

### 如何创建一个新的 LXD profile

查看已经存在的 profile：

```
$ lxc profile list
+---------|---------+
| NAME    | USED BY |
+---------|---------+
| default | 11      |
+---------|---------+

```

我们把名叫 `default` 的 profile 复制一份，然后在其内添加新的指令：

```
$ lxc profile copy default devprofile

$ lxc profile list
+------------|---------+
| NAME       | USED BY |
+------------|---------+
| default    | 11      |
+------------|---------+
| devprofile | 0       |
+------------|---------+

```

我们就得到了一个新的 profile： `devprofile`。下面是它的详情：

```
$ lxc profile show devprofile
config:
 environment.TZ: ""
description: Default LXD profile
devices:
 eth0:
 nictype: bridged
 parent: lxdbr0
 type: nic
 root:
 path: /
 pool: default
 type: disk
name: devprofile
used_by: []

```

注意这几个部分： `config:` 、 `description:` 、 `devices:` 、 `name:` 和 `used_by:`，当你修改这些内容的时候注意不要搞错缩进。（LCTT 译注：因为这些内容是 YAML 格式的，缩进是语法的一部分）

### 如何把 cloud-init 添加到 LXD profile 里

[cloud-init](http://cloudinit.readthedocs.io/en/latest/index.html) 可以添加到 LXD profile 的 `config` 里。当这些指令将被传递给容器后，会在容器第一次启动的时候执行。

下面是用在示例中的指令：

```
 package_upgrade: true
 packages:
 - build-essential
 locale: es_ES.UTF-8
 timezone: Europe/Madrid
 runcmd:
 - [touch, /tmp/simos_was_here]

```

`package_upgrade: true` 是指当容器第一次被启动时，我们想要 `cloud-init` 运行 `sudo apt upgrade`。`packages:` 列出了我们想要自动安装的软件。然后我们设置了 `locale` 和 `timezone`。在 Ubuntu 容器的镜像里，root 用户默认的 `locale` 是 `C.UTF-8`，而 `ubuntu` 用户则是 `en_US.UTF-8`。此外，我们把时区设置为 `Etc/UTC`。最后，我们展示了[如何使用 runcmd 来运行一个 Unix 命令](http://cloudinit.readthedocs.io/en/latest/topics/modules.html#runcmd)。

我们需要关注如何将 `cloud-init` 指令插入 LXD profile。

我首选的方法是：

```
$ lxc profile edit devprofile

```

它会打开一个文本编辑器，以便你将指令粘贴进去。[结果应该是这样的](https://paste.ubuntu.com/26313399/)：

```
$ lxc profile show devprofile
config:
  environment.TZ: ""
  user.user-data: |
    #cloud-config
    package_upgrade: true
    packages:
      - build-essential
    locale: es_ES.UTF-8
    timezone: Europe/Madrid
    runcmd:
      - [touch, /tmp/simos_was_here]
description: Default LXD profile
devices:
  eth0:
    nictype: bridged
    parent: lxdbr0
    type: nic
  root:
    path: /
    pool: default
    type: disk
name: devprofile
used_by: []

```

### 如何使用 LXD profile 启动一个容器

使用 profile `devprofile` 来启动一个新容器：

```
$ lxc launch --profile devprofile ubuntu:x mydev

```

然后访问该容器来查看我们的指令是否生效：

```
$ lxc exec mydev bash
root@mydev:~# ps ax
 PID TTY STAT TIME COMMAND
 1 ? Ss 0:00 /sbin/init
 ...
 427 ? Ss 0:00 /usr/bin/python3 /usr/bin/cloud-init modules --mode=f
 430 ? S 0:00 /bin/sh -c tee -a /var/log/cloud-init-output.log
 431 ? S 0:00 tee -a /var/log/cloud-init-output.log
 432 ? S 0:00 /usr/bin/apt-get --option=Dpkg::Options::=--force-con
 437 ? S 0:00 /usr/lib/apt/methods/http
 438 ? S 0:00 /usr/lib/apt/methods/http
 440 ? S 0:00 /usr/lib/apt/methods/gpgv
 570 ? Ss 0:00 bash
 624 ? S 0:00 /usr/lib/apt/methods/store
 625 ? R+ 0:00 ps ax
root@mydev:~#

```

如果我们连接得够快，通过 `ps ax` 将能够看到系统正在更新软件。我们可以从 `/var/log/cloud-init-output.log` 看到完整的日志：

```
Generating locales (this might take a while)...
 es_ES.UTF-8... done
Generation complete.

```

以上可以看出 `locale` 已经被更改了。root 用户还是保持默认的 `C.UTF-8`，只有非 root 用户 `ubuntu` 使用了新的`locale` 设置。

```
Hit:1 http://archive.ubuntu.com/ubuntu xenial InRelease
Get:2 http://archive.ubuntu.com/ubuntu xenial-updates InRelease [102 kB]
Get:3 http://security.ubuntu.com/ubuntu xenial-security InRelease [102 kB]

```

以上是安装软件包之前执行的 `apt update`。

```
The following packages will be upgraded:
 libdrm2 libseccomp2 squashfs-tools unattended-upgrades
4 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 211 kB of archives.

```

以上是在执行 `package_upgrade: true` 和安装软件包。

```
The following NEW packages will be installed:
 binutils build-essential cpp cpp-5 dpkg-dev fakeroot g++ g++-5 gcc gcc-5
 libalgorithm-diff-perl libalgorithm-diff-xs-perl libalgorithm-merge-perl

```

以上是我们安装 `build-essential` 软件包的指令。

`runcmd` 执行的结果如何？

```
root@mydev:~# ls -l /tmp/
total 1
-rw-r--r-- 1 root root 0 Jan 3 15:23 simos_was_here
root@mydev:~#

```

可见它已经生效了！

### 结论

当我们启动 LXD 容器的时候，我们常常需要默认启用一些配置，并且希望能够避免重复工作。通常解决这个问题的方法是创建 LXD profile，然后把需要的配置添加进去。最后，当我们启动新的容器时，只需要应用该 LXD profile 即可。

---

via: <https://blog.simos.info/how-to-preconfigure-lxd-containers-with-cloud-init/>

作者：[Simos Xenitellis](https://blog.simos.info/author/simos/) 译者：[kaneg](https://github.com/kaneg) 校对：[wxy](https://github.com/wxy)

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


Python 中最快解压 zip 文件的方法

Summary: 一步一步地做这个任务的这个过程感觉挺好的。

一步一步地做这个任务的这个过程感觉挺好的。

![](/data/attachment/album/201802/24/213511kqgmemzlqmcbw15n.jpg)

假设现在的上下文（LCTT 译注：context，计算机术语，此处意为业务情景）是这样的：一个 zip 文件被上传到一个[Web 服务](https://symbols.mozilla.org)中，然后 Python 需要解压这个 zip 文件然后分析和处理其中的每个文件。这个特殊的应用查看每个文件各自的名称和大小，并和已经上传到 AWS S3 上的文件进行比较，如果文件（和 AWS S3 上的相比）有所不同或者文件本身更新，那么就将它上传到 AWS S3。

[![Uploads today](/data/attachment/album/201802/24/213540t8v50jgvvtt8vqgg.png)](https://cdn-2916.kxcdn.com/cache/e6/dc/e6dc20acd37d94239edbbc0727721e4a.png)

挑战在于这些 zip 文件太大了。它们的平均大小是 560MB 但是其中一些大于 1GB。这些文件中大多数是文本文件，但是其中同样也有一些巨大的二进制文件。不同寻常的是，每个 zip 文件包含 100 个文件但是其中 1-3 个文件却占据了多达 95% 的 zip 文件大小。

最开始我尝试在内存中解压文件，并且每次只处理一个文件。在各种内存爆炸和 EC2 耗尽内存的情况下，这个方法壮烈失败了。我觉得这个原因是这样的。最开始你有 1GB 文件在内存中，然后你现在解压每个文件，在内存中大约就要占用 2-3GB。所以，在很多次测试之后，解决方案是将这些 zip 文件复制到磁盘上（在临时目录 `/tmp` 中），然后遍历这些文件。这次情况好多了但是我仍然注意到了整个解压过程花费了巨量的时间。**是否可能有方法优化呢？**

### 原始函数

首先是下面这些模拟对 zip 文件中文件实际操作的普通函数：

```
def _count_file(fn):
 with open(fn, 'rb') as f:
   return _count_file_object(f)

def _count_file_object(f):
 # Note that this iterates on 'f'.
 # You *could* do 'return len(f.read())'
 # which would be faster but potentially memory
 # inefficient and unrealistic in terms of this
 # benchmark experiment.
 total = 0
 for line in f:
   total += len(line)
 return total

```

这里是可能最简单的另一个函数：

```
def f1(fn, dest):
 with open(fn, 'rb') as f:
   zf = zipfile.ZipFile(f)
   zf.extractall(dest)

 total = 0
 for root, dirs, files in os.walk(dest):
   for file_ in files:
     fn = os.path.join(root, file_)
     total += _count_file(fn)
 return total

```

如果我更仔细地分析一下，我将会发现这个函数花费时间 40% 运行 `extractall`，60% 的时间在遍历各个文件并读取其长度。

### 第一步尝试

我的第一步尝试是使用线程。先创建一个 `zipfile.ZipFile` 的实例，展开其中的每个文件名，然后为每一个文件开始一个线程。每个线程都给它一个函数来做“实质工作”（在这个基准测试中，就是遍历每个文件然后获取它的名称）。实际业务中的函数进行的工作是复杂的 S3、Redis 和 PostgreSQL 操作，但是在我的基准测试中我只需要制作一个可以找出文件长度的函数就好了。线程池函数：

```
def f2(fn, dest):

    def unzip_member(zf, member, dest):
        zf.extract(member, dest)
        fn = os.path.join(dest, member.filename)
        return _count_file(fn)

    with open(fn, 'rb') as f:
        zf = zipfile.ZipFile(f)
        futures = []
        with concurrent.futures.ThreadPoolExecutor() as executor:
            for member in zf.infolist():
                futures.append(
                    executor.submit(
                        unzip_member,
                        zf,
                        member,
                        dest,
                    )
                )
            total = 0
            for future in concurrent.futures.as_completed(futures):
                total += future.result()
    return total

```

**结果：加速 ~10%**

### 第二步尝试

所以可能是 GIL（LCTT 译注：Global Interpreter Lock，一种全局锁，CPython 中的一个概念）阻碍了我。最自然的想法是尝试使用多线程在多个 CPU 上分配工作。但是这样做有缺点，那就是你不能传递一个非可 pickle 序列化的对象（LCTT 译注：意为只有可 pickle 序列化的对象可以被传递），所以你只能发送文件名到之后的函数中：

```
def unzip_member_f3(zip_filepath, filename, dest):
    with open(zip_filepath, 'rb') as f:
        zf = zipfile.ZipFile(f)
        zf.extract(filename, dest)
    fn = os.path.join(dest, filename)
    return _count_file(fn)

def f3(fn, dest):
    with open(fn, 'rb') as f:
        zf = zipfile.ZipFile(f)
        futures = []
        with concurrent.futures.ProcessPoolExecutor() as executor:
            for member in zf.infolist():
                futures.append(
                    executor.submit(
                        unzip_member_f3,
                        fn,
                        member.filename,
                        dest,
                    )
                )
            total = 0
            for future in concurrent.futures.as_completed(futures):
                total += future.result()
    return total

```

**结果: 加速 ~300%**

### 这是作弊

使用处理器池的问题是这样需要存储在磁盘上的原始 `.zip` 文件。所以为了在我的 web 服务器上使用这个解决方案，我首先得要将内存中的 zip 文件保存到磁盘，然后调用这个函数。这样做的代价我不是很清楚但是应该不低。

好吧，再翻翻看又没有损失。可能，解压过程加速到足以弥补这样做的损失了吧。

但是一定记住！这个优化取决于使用所有可用的 CPU。如果一些其它的 CPU 需要执行在 `gunicorn` 中的其它事务呢？这时，这些其它进程必须等待，直到有 CPU 可用。由于在这个服务器上有其他的事务正在进行，我不是很确定我想要在进程中接管所有其他 CPU。

### 结论

一步一步地做这个任务的这个过程感觉挺好的。你被限制在一个 CPU 上但是表现仍然特别好。同样地，一定要看看在`f1` 和 `f2` 两段代码之间的不同之处！利用 `concurrent.futures` 池类你可以获取到允许使用的 CPU 的个数，但是这样做同样给人感觉不是很好。如果你在虚拟环境中获取的个数是错的呢？或者可用的个数太低以致无法从负载分配获取好处并且现在你仅仅是为了移动负载而支付营运开支呢？

我将会继续使用 `zipfile.ZipFile(file_buffer).extractall(temp_dir)`。这个工作这样做已经足够好了。

### 想试试手吗？

我使用一个 `c5.4xlarge` EC2 服务器来进行我的基准测试。文件可以从此处下载：

```
wget https://www.peterbe.com/unzip-in-parallel/hack.unzip-in-parallel.py
wget https://www.peterbe.com/unzip-in-parallel/symbols-2017-11-27T14_15_30.zip

```

这里的 `.zip` 文件有 34MB。和在服务器上的相比已经小了很多。

`hack.unzip-in-parallel.py` 文件里是一团糟。它包含了大量可怕的修正和丑陋的代码，但是这只是一个开始。

---

via: <https://www.peterbe.com/plog/fastest-way-to-unzip-a-zip-file-in-python>

作者：[Peterbe](https://www.peterbe.com/) 译者：[Leemeans](https://github.com/leemeans) 校对：[wxy](https://github.com/wxy)

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


通过 ncurses 在终端创建一个冒险游戏

Summary: 怎样使用 curses 函数读取键盘并操作屏幕。

怎样使用 curses 函数读取键盘并操作屏幕。

![](/data/attachment/album/201802/25/100828w9xu9oaiyptt8oah.jpg)

>
> 怎样使用 curses 函数读取键盘并操作屏幕。
>
>
>

我[之前的文章](/article-9348-1.html)介绍了 ncurses 库，并提供了一个简单的程序展示了一些将文本放到屏幕上的 curses 函数。在接下来的文章中，我将介绍如何使用其它的 curses 函数。

### 探险

当我逐渐长大，家里有了一台苹果 II 电脑。我和我兄弟正是在这台电脑上自学了如何用 AppleSoft BASIC 写程序。我在写了一些数学智力游戏之后，继续创造游戏。作为 80 年代的人，我已经是龙与地下城桌游的粉丝，在游戏中角色扮演一个追求打败怪物并在陌生土地上抢掠的战士或者男巫，所以我创建一个基本的冒险游戏也在情理之中。

AppleSoft BASIC 支持一种简洁的特性：在标准分辨率图形模式（GR 模式）下，你可以检测屏幕上特定点的颜色。这为创建一个冒险游戏提供了捷径。比起创建并更新周期性传送到屏幕的内存地图，我现在可以依赖 GR 模式为我维护地图，我的程序还可以在玩家的角色（LCTT 译注：此处 character 双关一个代表玩家的角色，同时也是一个字符）在屏幕四处移动的时候查询屏幕。通过这种方式，我让电脑完成了大部分艰难的工作。因此，我的自顶向下的冒险游戏使用了块状的 GR 模式图形来展示我的游戏地图。

我的冒险游戏使用了一张简单的地图，上面有一大片绿地伴着山脉从中间蔓延向下和一个在左上方的大湖。我要粗略地为桌游战役绘制这个地图，其中包含一个允许玩家穿过到远处的狭窄通道。

![](/data/attachment/album/201802/24/215504fa44evgot8t66fye.jpg)

*图 1. 一个有湖和山的简单桌游地图*

你可以用 curses 绘制这个地图，并用字符代表草地、山脉和水。接下来，我描述怎样使用 curses 那样做，以及如何在 Linux 终端创建和进行类似的一个冒险游戏。

### 构建程序

在我的上一篇文章，我提到了大多数 curses 程序以相同的一组指令获取终端类型和设置 curses 环境：

```
initscr();
cbreak();
noecho();

```

在这个程序，我添加了另外的语句：

```
keypad(stdscr, TRUE);

```

这里的 `TRUE` 标志允许 curses 从用户终端读取小键盘和功能键。如果你想要在你的程序中使用上下左右方向键，你需要使用这里的 `keypad(stdscr, TRUE)`。

这样做了之后，你现在可以开始在终端屏幕上绘图了。curses 函数包括了一系列在屏幕上绘制文本的方法。在我之前的文章中，我展示了 `addch()` 和 `addstr()` 函数以及在添加文本之前先移动到指定屏幕位置的对应函数 `mvaddch()` 和 `mvaddstr()`。为了在终端上创建这个冒险游戏的地图，你可以使用另外一组函数：`vline()` 和 `hline()`，以及它们对应的函数 `mvvline()` 和 `mvhline()`。这些 mv 函数接受屏幕坐标、一个要绘制的字符和要重复此字符的次数的参数。例如，`mvhline(1, 2, '-', 20)` 将会绘制一条开始于第一行第二列并由 20 个横线组成的线段。

为了以编程方式绘制地图到终端屏幕上，让我们先定义这个 `draw_map()` 函数：

```
#define GRASS     ' '
#define EMPTY     '.'
#define WATER     '~'
#define MOUNTAIN  '^'
#define PLAYER    '*'

void draw_map(void)
{
    int y, x;

    /* 绘制探索地图 */

    /* 背景 */

    for (y = 0; y < LINES; y++) {
        mvhline(y, 0, GRASS, COLS);
    }

    /* 山和山道 */

    for (x = COLS / 2; x < COLS * 3 / 4; x++) {
        mvvline(0, x, MOUNTAIN, LINES);
    }

    mvhline(LINES / 4, 0, GRASS, COLS);

    /* 湖 */

    for (y = 1; y < LINES / 2; y++) {
        mvhline(y, 1, WATER, COLS / 3);
    }
}

```

在绘制这副地图时，记住填充大块字符到屏幕所使用的 `mvvline()` 和 `mvhline()` 函数。我绘制从 0 列开始的字符水平线（`mvhline`）以创建草地区域，直到占满整个屏幕的高度和宽度。我绘制从 0 行开始的多条垂直线（`mvvline`）在此上添加了山脉，绘制单行水平线添加了一条山道（`mvhline`）。并且，我通过绘制一系列短水平线（`mvhline`）创建了湖。这种绘制重叠方块的方式看起来似乎并没有效率，但是记住在我们调用 `refresh()` 函数之前 curses 并不会真正更新屏幕。

绘制完地图，创建游戏就还剩下进入循环让程序等待用户按下上下左右方向键中的一个然后让玩家图标正确移动了。如果玩家想要移动的地方是空的，就应该允许玩家到那里。

你可以把 curses 当做捷径使用。比起在程序中实例化一个版本的地图并复制到屏幕这么复杂，你可以让屏幕为你跟踪所有东西。`inch()` 函数和相关联的 `mvinch()` 函数允许你探测屏幕的内容。这让你可以查询 curses 以了解玩家想要移动到的位置是否被水填满或者被山阻挡。这样做你需要一个之后会用到的一个帮助函数：

```
int is_move_okay(int y, int x)
{
    int testch;

    /* 如果要进入的位置可以进入，返回 true */

    testch = mvinch(y, x);
    return ((testch == GRASS) || (testch == EMPTY));
}

```

如你所见，这个函数探测行 `x`、列 `y` 并在空间未被占据的时候返回 `true`，否则返回 `false`。

这样我们写移动循环就很容易了：从键盘获取一个键值然后根据是上下左右键移动用户字符。这里是一个这种循环的简单版本：

```

    do {
        ch = getch();

        /* 测试输入的值并获取方向 */

        switch (ch) {
        case KEY_UP:
            if ((y > 0) && is_move_okay(y - 1, x)) {
                y = y - 1;
            }
            break;
        case KEY_DOWN:
            if ((y < LINES - 1) && is_move_okay(y + 1, x)) {
                y = y + 1;
            }
            break;
        case KEY_LEFT:
            if ((x > 0) && is_move_okay(y, x - 1)) {
                x = x - 1;
            }
            break;
        case KEY_RIGHT
            if ((x < COLS - 1) && is_move_okay(y, x + 1)) {
                x = x + 1;
            }
            break;
        }
    }
    while (1);

```

为了在游戏中使用这个循环，你需要在循环里添加一些代码来启用其它的键（例如传统的移动键 WASD），以提供让用户退出游戏和在屏幕上四处移动的方法。这里是完整的程序：

```
/* quest.c */

#include
#include

#define GRASS     ' '
#define EMPTY     '.'
#define WATER     '~'
#define MOUNTAIN  '^'
#define PLAYER    '*'

int is_move_okay(int y, int x);
void draw_map(void);

int main(void)
{
    int y, x;
    int ch;

    /* 初始化curses */

    initscr();
    keypad(stdscr, TRUE);
    cbreak();
    noecho();

    clear();

    /* 初始化探索地图 */

    draw_map();

    /* 在左下角初始化玩家 */

    y = LINES - 1;
    x = 0;

    do {
    /* 默认获得一个闪烁的光标--表示玩家字符 */

    mvaddch(y, x, PLAYER);
    move(y, x);
    refresh();

    ch = getch();

    /* 测试输入的键并获取方向 */

    switch (ch) {
    case KEY_UP:
    case 'w':
    case 'W':
        if ((y > 0) && is_move_okay(y - 1, x)) {
        mvaddch(y, x, EMPTY);
        y = y - 1;
        }
        break;
    case KEY_DOWN:
    case 's':
    case 'S':
        if ((y < LINES - 1) && is_move_okay(y + 1, x)) {
        mvaddch(y, x, EMPTY);
        y = y + 1;
        }
        break;
    case KEY_LEFT:
    case 'a':
    case 'A':
        if ((x > 0) && is_move_okay(y, x - 1)) {
        mvaddch(y, x, EMPTY);
        x = x - 1;
        }
        break;
    case KEY_RIGHT:
    case 'd':
    case 'D':
        if ((x < COLS - 1) && is_move_okay(y, x + 1)) {
        mvaddch(y, x, EMPTY);
        x = x + 1;
        }
        break;
    }
    }
    while ((ch != 'q') && (ch != 'Q'));

    endwin();

    exit(0);
}

int is_move_okay(int y, int x)
{
    int testch;

    /* 当空间可以进入时返回true */

    testch = mvinch(y, x);
    return ((testch == GRASS) || (testch == EMPTY));
}

void draw_map(void)
{
    int y, x;

    /* 绘制探索地图 */

    /* 背景 */

    for (y = 0; y < LINES; y++) {
    mvhline(y, 0, GRASS, COLS);
    }

    /* 山脉和山道 */

    for (x = COLS / 2; x < COLS * 3 / 4; x++) {
    mvvline(0, x, MOUNTAIN, LINES);
    }

    mvhline(LINES / 4, 0, GRASS, COLS);

    /* 湖 */

    for (y = 1; y < LINES / 2; y++) {
    mvhline(y, 1, WATER, COLS / 3);
    }
}

```

在完整的程序清单中，你可以看见使用 curses 函数创建游戏的完整布置：

1. 初始化 curses 环境。
2. 绘制地图。
3. 初始化玩家坐标（左下角）
4. 循环：
	* 绘制玩家的角色。
	* 从键盘获取键值。
	* 对应地上下左右调整玩家坐标。
	* 重复。
5. 完成时关闭curses环境并退出。

### 开始玩

当你运行游戏时，玩家的字符在左下角初始化。当玩家在游戏区域四处移动的时候，程序创建了“一串”点。这样可以展示玩家经过了的点，让玩家避免经过不必要的路径。

![](/data/attachment/album/201802/25/101020cy7yjhz2t7jptcwl.png)

*图 2. 初始化在左下角的玩家*

![](/data/attachment/album/201802/25/101058bcgg5cydhksspdip.png)

*图 3. 玩家可以在游戏区域四处移动，例如湖周围和山的通道*

为了创建上面这样的完整冒险游戏，你可能需要在他/她的角色在游戏区域四处移动的时候随机创建不同的怪物。你也可以创建玩家可以发现在打败敌人后可以掠夺的特殊道具，这些道具应能提高玩家的能力。

但是作为起点，这是一个展示如何使用 curses 函数读取键盘和操纵屏幕的好程序。

### 下一步

这是一个如何使用 curses 函数更新和读取屏幕和键盘的简单例子。按照你的程序需要做什么，curses 可以做得更多。在下一篇文章中，我计划展示如何更新这个简单程序以使用颜色。同时，如果你想要学习更多 curses，我鼓励你去读位于 Linux 文档计划的 Pradeep Padala 写的[如何使用 NCURSES 编程](http://tldp.org/HOWTO/NCURSES-Programming-HOWTO)。

---

via: <http://www.linuxjournal.com/content/creating-adventure-game-terminal-ncurses>

作者：[Jim Hall](http://www.linuxjournal.com/users/jim-hall) 译者：[Leemeans](https://github.com/leemeans) 校对：[wxy](https://github.com/wxy)

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


为初学者介绍 Linux whereis 命令（5个例子）

Summary: 有时，在使用命令行的时候，我们需要快速找到某一个命令的二进制文件所在位置。这种情况下可以选择 find 命令，但使用它会耗费时间，可能也会出现意料之外的情况。有一个专门为这种情况设计的命令：whereis。

![](/data/attachment/album/201802/25/132156q2n3h3be03tn33ax.jpg)

有时，在使用命令行的时候，我们需要快速找到某一个命令的二进制文件所在位置。这种情况下可以选择 [find](https://www.howtoforge.com/tutorial/linux-find-command/) 命令，但使用它会耗费时间，可能也会出现意料之外的情况。有一个专门为这种情况设计的命令：`whereis`。

在这篇文章里，我们会通过一些便于理解的例子来解释这一命令的基础内容。但在这之前，值得说明的一点是，下面出现的所有例子都在 Ubuntu 16.04 LTS 下测试过。

### Linux whereis 命令

`whereis` 命令可以帮助用户寻找某一命令的二进制文件，源码以及帮助页面。下面是它的格式：

```
whereis [options] [-BMS directory... -f] name...

```

这是这一命令的 man 页面给出的解释：

>
> `whereis` 可以查找指定命令的二进制文件、源文件和帮助文件。 被找到的文件在显示时，会去掉主路径名，然后再去掉文件的（单个）尾部扩展名 (如: `.c`)，来源于源代码控制的 `s.` 前缀也会被去掉。接下来，`whereis` 会尝试在标准的 Linux 位置里寻找具体程序，也会在由 `$PATH` 和 `$MANPATH` 指定的路径中寻找。
>
>
>

下面这些以 Q&A 形式出现的例子，可以给你一个关于如何使用 whereis 命令的直观感受。

### Q1. 如何用 whereis 命令寻找二进制文件所在位置?

假设你想找，比如说，`whereis` 命令自己所在位置。下面是你具体的操作：

```
whereis whereis

```

[![How to find location of binary file using whereis](/data/attachment/album/201802/25/132209hea1ryrzpw11faga.png)](https://www.howtoforge.com/images/command-tutorial/big/whereis-basic-usage.png)

需要注意的是，输出的第一个路径才是你想要的结果。使用 `whereis` 命令，同时也会显示帮助页面和源码所在路径。（如果能找到的情况下会显示，但是在这一例中没有找到）所以你在输出中看见的第二个路径就是帮助页面文件所在位置。

### Q2. 怎么在搜索时规定只搜索二进制文件、帮助页面，还是源代码呢？

如果你想只搜索，假设说，二进制文件，你可以使用 `-b` 这一命令行选项。例如：

```
whereis -b cp

```

[![How to specifically search for binaries, manuals, or source code](/data/attachment/album/201802/25/132210x16gujmn1s8n0nzn.png)](https://www.howtoforge.com/images/command-tutorial/big/whereis-b-option.png)

类似的， `-m` 和 `-s` 这两个 选项分别对应 帮助页面和源码。

### Q3.如何限制 whereis 命令搜索位置？

默认情况下，`whereis` 是从由匹配符所定义的硬编码路径来寻找文件的。但如果你想的话，你可以用命令行选项来限制搜索。例如，如果你只想在 `/usr/bin` 寻找二进制文件，你可以用 `-B` 这一选项来实现。

```
whereis -B /usr/bin/ -f cp

```

注意：使用这种方式时可以给出多个路径。使用 `-f` 这一选项来明确分隔目录列表和要搜索的文件名。

类似的，如果你想只搜索帮助文件或源码，你可以对应使用 `-M` 和 `-S` 这两个选项。

### Q4. 如何查看 whereis 的搜索路径?

与此相对应的也有一个选项。只要在 `whereis` 后加上 `-l`。

```
whereis -l

```

这是例子的部分输出结果：

[![How to see paths that whereis uses for search](/data/attachment/album/201802/25/132210kz0392d1fl1ndzgf.png)](https://www.howtoforge.com/images/command-tutorial/big/whereis-l.png)

### Q5. 如何找到一个有异常条目的命令?

对于 `whereis` 命令来说，如果一个命令对每个显式的请求类型都不止一项，则该命令被视为异常。例如，没有可用文档的命令，或者对应文档分散在各处的命令都可以算作异常命令。 当使用 `-u` 这一选项，`whereis` 就会显示那些有异常条目的命令。

例如，下面这一例子就显示，在当前目录中，没有对应文档或有多个文档的命令。

```
whereis -m -u *

```

### 总结

我觉得，`whereis` 不是那种你需要经常使用的命令行工具。但在遇到某些特殊情况时，它绝对会让你的生活变得轻松。我们已经涉及了这一工具提供的一些重要命令行选项，所以要注意练习。想了解更多信息，直接去看它的 [man](https://linux.die.net/man/1/whereis) 页面吧。

---

via: <https://www.howtoforge.com/linux-whereis-command/>

作者：[Himanshu Arora](https://www.howtoforge.com) 译者：[wenwensnow](https://github.com/wenwensnow) 校对：[wxy](https://github.com/wxy)

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


Opensource.com 的 2017 年最佳开源教程

2017 年，Opensource.com 发布了一系列用于帮助从初学者到专家的教程。让我们看看哪些最好。

![](/data/attachment/album/201802/26/122704kdfp9g9coq9fp227.png)

精心编写的教程对于任何软件的官方文档来说都是一个很好的补充。如果官方文件写得不好，不完整或根本没有，那么这些教程也可以是个有效的替代品。

2017 年，Opensource.com 发布一些有关各种主题的优秀教程。这些教程不只是针对专家们的，它们是针对各种技能水平和经验的用户的。

让我们来看看其中最好的教程。

### 关于代码

对许多人来说，他们第一次涉足开源是为一个项目或另一个项目贡献代码。你在哪里学习编码或编程的？以下两篇文章是很好的起点。

严格来说，VM Brasseur 的[如何开始学习编程](/article-8694-1.html)是新手程序员的一个很好的起点，而不是一个教程。它不仅指出了一些有助于你开始学习的优秀资源，而且还提供了了解你的学习方式和如何选择语言的重要建议。

如果您已经在一个 [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) 或文本编辑器中敲击了几个小时，那么您可能需要学习更多关于编码的不同方法。Fraser Tweedale 的[函数式编程简介](/article-8869-1.html)很好地介绍了可以应用到许多广泛使用的编程语言的范式。

### 踏足 Linux

Linux 是开源的典范。它运行了大量的 Web 站点，为世界顶级的超级计算机提供了动力。它让任何人都可以替代台式机上的专有操作系统。

如果你有兴趣深入 Linux，这里有三个教程供你参考。

Jason Baker 告诉你[设置 Linux $PATH 变量](https://opensource.com/article/17/6/set-path-linux)。他引导你掌握这一“任何 Linux 初学者的重要技巧”，使您能够告知系统包含了程序和脚本的目录。

感谢 David Both 的[建立一个 DNS 域名服务器](https://opensource.com/article/17/4/build-your-own-name-server)指南。他详细地记录了如何设置和运行服务器，包括要编辑的配置文件以及如何编辑它们。

想在你的电脑上更复古一点吗？Jim Hall 告诉你如何使用 [FreeDOS](http://www.freedos.org/)和 [qemu](https://www.qemu.org) [在 Linux 下运行 DOS 程序](/article-9014-1.html)。Hall 的文章着重于运行 DOS 生产力工具，但并不全是严肃的——他也谈到了运行他最喜欢的 DOS 游戏。

### 3 片（篇）树莓派

廉价的单板计算机使硬件再次变得有趣，这并不是秘密。不仅如此，它们使更多的人更容易接近，无论他们的年龄或技术水平如何。

其中，[树莓派](https://en.wikipedia.org/wiki/Raspberry_Pi)可能是最广泛使用的单板计算机。Ben Nuttall 带我们一起[在树莓派上安装和设置 Postgres 数据库](/article-9056-1.html)。这样，你可以在任何你想要的项目中使用它。

如果你的品味包括文学和技术，你可能会对 Don Watkins 的[如何将树莓派变成电子书服务器](/article-8684-1.html)感兴趣。稍微付出一点努力和一份 [Calibre 电子书管理软件](https://calibre-ebook.com/)副本，你就可以得到你最喜欢的电子书，无论你在哪里。

树莓派并不是其中唯一有特点的。还有 [Orange Pi Pc Plus](http://www.orangepi.org/)，这是一种开源的单板机。David Egts 告诉你[如何开始使用这个可编程的迷你电脑](/article-8308-1.html)。

### 日常的计算机使用

开源并不仅针对技术专家，更多的普通人用它来做日常工作，而且更加效率。这里有三篇文章，可以使我们这些笨手笨脚的人（你可能不是）做任何事情变得优雅。

当你想到微博客的时候，你可能会想到 Twitter。但是 Twitter 的问题很多。[Mastodon](https://joinmastodon.org/) 是 Twitter 的开放的替代方案，它在 2016 年首次亮相。从此， Mastodon 就获得相当大的用户基数。Seth Kenlon 说明[如何加入和使用 Mastodon](https://opensource.com/article/17/4/guide-to-mastodon)，甚至告诉你如何在 Mastodon 和 Twitter 间交替使用。

你需要一点帮助来维持开支吗？你所需要的只是一个电子表格和正确的模板。我关于[要控制你的财政状况](/article-8831-1.html)的文章，向你展示了如何用 [LibreOffice Calc](https://www.libreoffice.org/discover/calc/) （或任何其他电子表格编辑器）创建一个简单而有吸引力的财务跟踪。

ImageMagick 是强大的图形处理工具。但是，很多人不经常使用。这意味着他们在最需要它们时忘记了命令。如果你也是这样，Greg Pittman 的 [ImageMagick 入门教程](/article-8851-1.html)能在你需要一些帮助时候能派上用场。

你有最喜欢的 2017 Opensource.com 发布的教程吗？请随意留言与社区分享。

---

via: <https://opensource.com/article/18/1/best-tutorials>

作者：[Scott Nesbitt](https://opensource.com/users/scottnesbitt) 译者：[zjon](https://github.com/zjon) 校对：[wxy](https://github.com/wxy)

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


五个值得现在安装的火狐插件

>
> 合适的插件能大大增强你浏览器的功能，但仔细挑选插件很重要。本文有五个值得一看的插件。
>
>
>

![](/data/attachment/album/201802/26/232926k17f4y9owc1h5hwo.jpg)

对于很多用户来说，网页浏览器已经成为电脑使用体验的重要环节。现代浏览器已经发展成强大、可拓展的平台。作为平台的一部分，*插件*能添加或修改浏览器的功能。火狐插件的构建使用了 WebExtensions API ，这是一个跨浏览器的开发系统。

你应该安装哪一个插件？一般而言，这个问题的答案取决于你如何使用你的浏览器、你对于隐私的看法、你信任插件开发者多少以及其他个人喜好。

首先，我想指出浏览器插件通常需要读取和（或者）修改你浏览的网页上的每项内容。你应该*非常*仔细地考虑这件事的后果。如果一个插件有修改所有你访问过的网页的权限，那么它可能记录你的按键、拦截信用卡信息、在线跟踪你、插入广告，以及其他各种各样邪恶的行为。

并不是每个插件都偷偷摸摸地做这些事，但是在你安装任何插件之前，你要慎重考虑下插件安装来源、涉及的权限、你的风险数据和其他因素。记住，你可以从个人数据的角度来管理一个插件如何影响你的攻击面（ LCTT 译注：攻击面是指入侵者能尝试获取或提取数据的途径总和）——例如使用特定的配置、不使用插件来完成例如网上银行的操作。

考虑到这一点，这里有你或许想要考虑的五个火狐插件

### uBlock Origin

![ublock origin ad blocker screenshot](/data/attachment/album/201802/26/232927ek22mku29mwj5niq.png)

*ublock Origin 可以拦截广告和恶意网页，还允许用户定义自己的内容过滤器。*

[uBlock Origin](https://addons.mozilla.org/en-US/firefox/addon/ublock-origin/) 是一款快速、内存占用低、适用范围广的拦截器，它不仅能屏蔽广告，还能让你执行你自己定制的内容过滤。uBlock Origin 默认使用多份预定义好的过滤名单来拦截广告、跟踪器和恶意网页。它允许你任意地添加列表和规则，或者锁定在一个默认拒绝的模式。除了强大之外，这个插件已被证明是效率高、性能好。

### Privacy Badger

![privacy badger ad blocker](/data/attachment/album/201802/26/232929vswvbxfueb2wblvw.png)

*Privacy Badger 运用了算法来无缝地屏蔽侵犯用户准则的广告和跟踪器。*

正如它名字所表明，[Privacy Badger](https://www.eff.org/privacybadger) 是一款专注于隐私的插件，它屏蔽广告和第三方跟踪器。EFF （LCTT 译注：EFF 全称是<ruby> 电子前哨基金会 <rt>  Electronic Frontier Foundation </rt></ruby>，旨在宣传互联网版权和监督执法机构）说：“我们想要推荐一款能自动分析并屏蔽任何侵犯用户准则的跟踪器和广告，而 Privacy Badger 诞生于此目的；它不用任何设置、知识或者用户的配置，就能运行得很好；它是由一个明显为用户服务而不是为广告主服务的组织出品；它使用算法来确定正在跟踪什么，而没有跟踪什么。”

为什么 Privacy Badger 出现在这列表上的原因跟 uBlock Origin 如此相似？其中一个原因是 Privacy Badger 从根本上跟 uBlock Origin 的工作不同。另一个原因是纵深防御的做法是个可以遵循的合理策略。

### LastPass

![lastpass password manager screenshot](/data/attachment/album/201802/26/232930rtzuddrh4racttta.jpg)

*LastPass 是一款用户友好的密码管理插件，支持双因子认证。*

这个插件对于很多人来说是个有争议的补充。你是否应该使用密码管理器——如果你用了，你是否应该选择一个浏览器插件——这都是个热议的话题，而答案取决于你的风险资料。我想说大部分不关心的电脑用户应该用一个，因为这比起常见的选择：每一处使用相同的弱密码，都好太多了。

[LastPass](https://addons.mozilla.org/en-US/firefox/addon/lastpass-password-manager/) 对于用户很友好，支持双因子认证，相当安全。这家公司过去出过点安全事故，但是都处理得当，而且资金充足。记住使用密码管理器不是非此即彼的命题。很多用户选择使用密码管理器管理绝大部分密码，但是保持了一点复杂性，为例如银行这样重要的网页采用了精心设计的密码和多因子认证。

### Xmarks Sync

[Xmarks Sync](https://addons.mozilla.org/en-US/firefox/addon/xmarks-sync/) 是一款方便的插件，能跨实例同步你的书签、打开的标签页、配置项和浏览器历史。如果你有多台机器，想要在桌面设备和移动设备之间同步、或者在同一台设备使用不同的浏览器，那来看看 Xmarks Sync 。（注意这款插件最近被 LastPass 收购）

### Awesome Screenshot Plus

[Awesome Screenshot Plus](https://addons.mozilla.org/en-US/firefox/addon/screenshot-capture-annotate/) 允许你很容易捕获任意网页的全部或部分区域，也能添加注释、评论、使敏感信息模糊等。你还能用一个可选的在线服务来分享图片。我发现这工具在网页调试时截图、讨论设计和分享信息上很棒。这是一款比你预期中发现自己使用得多的工具。

![Awesome Screenshot Plus screenshot](/data/attachment/album/201802/26/232930ad00usu2u22tbr8r.png)

*Awesome Screenshot Plus 允许你容易地截下任何网页的部分或全部内容。*

我发现这五款插件有用，我把它们推荐给其他人。这就是说，还有很多浏览器插件。我很感兴趣社区用户们正在使用哪些插件，请在评论中让我知道。

---

via: <https://opensource.com/article/18/1/top-5-firefox-extensions>

作者：[Jeremy Garcia](https://opensource.com/users/jeremy-garcia) 译者：[ypingcn](https://github.com/ypingcn) 校对：[wxy](https://github.com/wxy)

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


Intel 设计缺陷背后的原因是什么？

Summary: 我们知道有问题，但是并不知道问题的详细情况。

我们知道有问题，但是并不知道问题的详细情况。

>
> 我们知道有问题，但是并不知道问题的详细情况。
>
>
>

![](/data/attachment/album/201802/26/235324a324tj1htn23dpw2.jpg)

（本文发表于 1 月份）最近 Windows 和 Linux 都发送了重大安全更新，为防范这个尚未完全公开的问题，在最坏的情况下，它可能会导致性能下降多达一半。

在过去的几周，Linux 内核陆续打了几个补丁。Microsoft [自 11 月份开始也内部测试了 Windows 更新](https://twitter.com/aionescu/status/930412525111296000)，并且它预计在下周二的例行补丁中将这个改进推送到主流 Windows 构建版中。Microsoft 的 Azure 也在下周的维护窗口中做好了安排，而 Amazon 的 AWS 也安排在周五对相关的设施进行维护。

自从 Linux 第一个补丁 （参见 [KPTI：内核页表隔离的当前的发展](/article-9201-1.html)） 明确描绘了出现的错误以后。虽然 Linux 和 Windows 基于不同的考虑，对此持有不同的看法，但是这两个操作系统 —— 当然还有其它的 x86 操作系统，比如 FreeBSD 和 [macOS](https://twitter.com/aionescu/status/948609809540046849) — 对系统内存的处理采用了相同的方式，因为对于操作系统在这一部分特性是与底层的处理器高度耦合的。

### 保持地址跟踪

在一个系统中的每个内存字节都是隐性编码的，这些编码数字是每个字节的地址。早期的操作系统使用物理内存地址，但是，物理内存地址由于各种原因，它并不很合适。例如，在地址中经常会有空隙，并且（尤其是 32 位的系统上）物理地址很难操作，需要 36 位数字，甚至更多。

因此，现在操作系统完全依赖一个叫虚拟内存的概念。虚拟内存系统允许程序和内核一起在一个简单、清晰、统一的环境中各自去操作。而不是使用空隙和其它奇怪的东西的物理内存，每个程序和内核自身都使用虚拟地址去访问内存。这些虚拟地址是连续的 —— 不用担心有空隙 —— 并且合适的大小也更便于操作。32 位的程序仅可以看到 32 位的地址，而不用管物理地址是 36 位还是更多位。

虽然虚拟地址对每个软件几乎是透明的，但是，处理器最终还是需要知道虚拟地址引用的物理地址是哪个。因此，有一个虚拟地址到物理地址的映射，它保存在一个被称为页面表的数据结构中。操作系统构建页面表，使用一个由处理器决定的布局，并且处理器和操作系统在虚拟地址和物理地址之间进行转换时就需要用到页面表。

这个映射过程是非常重要的，它也是现代操作系统和处理器的重要基础，处理器有专用的缓存 — Translation Lookaside Buffer（简称 TLB）—— 它保存了一定数量的虚拟地址到物理地址的映射，这样就不需要每次都使用全部页面。

虚拟内存的使用为我们提供了很多除了简单寻址之外的有用的特性。其中最主要的是，每个程序都有了自己独立的一组虚拟地址，有了它自己的一组虚拟地址到物理地址的映射。这就是用于提供“内存保护”的关键技术，一个程序不能破坏或者篡改其它程序使用的内存，因为其它程序的内存并不在它的地址映射范围之内。

由于每个进程使用一个单独的映射，因此每个程序也就有了一个额外的页面表，这就使得 TLB 缓存很拥挤。TLB 并不大 —— 一般情况下总共可以容纳几百个映射 —— 而系统使用的页面表越多，TLB 能够包含的任何特定的虚拟地址到物理地址的映射就越少。

### 一半一半

为了更好地使用 TLB，每个主流的操作系统都将虚拟地址范围一分为二。一半用于程序；另一半用于内核。当进程切换时，仅有一半的页面表条目发生变化 —— 仅属于程序的那一半。内核的那一半是每个程序公用的（因为只有一个内核）并且因此它可以为每个进程使用相同的页面表映射。这对 TLB 的帮助非常大；虽然它仍然会丢弃属于进程的那一半内存地址映射；但是它还保持着另一半属于内核的映射。

这种设计并不是一成不变的。在 Linux 上做了一项工作，使它可以为一个 32 位的进程提供整个地址范围，而不用在内核页面表和每个进程之间共享。虽然这样为程序提供了更多的地址空间，但这是以牺牲性能为代价的，因为每次内核代码需要运行时，TLB 重新加载内核的页面表条目。因此，这种方法并没有广泛应用到 x86 的系统上。

在内核和每个程序之间分割虚拟地址的这种做法的一个负面影响是，内存保护被削弱了。如果内核有它自己的一组页面表和虚拟地址，它将在不同的程序之间提供相同的保护；内核内存将是简单的不可见。但是使用地址分割之后，用户程序和内核使用了相同的地址范围，并且从原理上来说，一个用户程序有可能去读写内核内存。

为避免这种明显不好的情况，处理器和虚拟地址系统有一个 “Ring” 或者 “模式”的概念。x86 处理器有许多 Ring，但是对于这个问题，仅有两个是相关的：“user” （Ring 3）和 “supervisor”（ring 0）。当运行普通的用户程序时，处理器将置为用户模式 （Ring 3）。当运行内核代码时，处理器将处于 Ring 0 —— supervisor 模式，也称为内核模式。

这些 Ring 也用于从用户程序中保护内核内存。页面表并不仅仅有虚拟地址到物理地址的映射；它也包含关于这些地址的元数据，包含哪个 Ring 可能访问哪个地址的信息。内核页面表条目被标记为仅有 Ring 0 可以访问；程序的条目被标记为任何 Ring 都可以访问。如果一个处于 Ring 3 中的进程去尝试访问标记为 Ring 0 的内存，处理器将阻止这个访问并生成一个意外错误信息。运行在 Ring 3 中的用户程序不能得到内核以及运行在 Ring 0 内存中的任何东西。

至少理论上是这样的。大量的补丁和更新表明，这个地方已经被突破了。这就是最大的谜团所在。

### Ring 间迁移

这就是我们所知道的。每个现代处理器都执行一定数量的推测运行。例如，给一些指令，让两个数加起来，然后将结果保存在内存中，在查明内存中的目标是否可访问和可写入之前，一个处理器可能已经推测性地做了加法。在一些常见案例中，在地址可写入的地方，处理器节省了一些时间，因为它以并行方式计算出内存中的目标是什么。如果它发现目标位置不可写入 —— 例如，一个程序尝试去写入到一个没有映射的地址或压根就不存在的物理位置 —— 然后它将产生一个意外错误，而推测运行就白做了。

Intel 处理器，尤其是（[虽然不是 AMD 的](https://lkml.org/lkml/2017/12/27/2)）允许对 Ring 3 代码进行推测运行并写入到 Ring 0 内存中的处理器上。处理器并不完全阻止这种写入，但是推测运行轻微扰乱了处理器状态，因为，为了查明目标位置是否可写入，某些数据已经被加载到缓存和 TLB 中。这又意味着一些操作可能快几个周期，或者慢几个周期，这取决于它们所需要的数据是否仍然在缓存中。除此之外，Intel 的处理器还有一些特殊的功能，比如，在 Skylake 处理器上引入的软件保护扩展（SGX）指令，它改变了一点点访问内存的方式。同样的，处理器仍然是保护 Ring 0 的内存不被来自 Ring 3 的程序所访问，但是同样的，它的缓存和其它内部状态已经发生了变化，产生了可测量的差异。

我们至今仍然并不知道具体的情况，到底有多少内核的内存信息泄露给了用户程序，或者信息泄露的情况有多容易发生。以及有哪些 Intel 处理器会受到影响？也或者并不完全清楚，但是，有迹象表明每个 Intel 芯片都使用了推测运行（是自 1995 年 Pentium Pro 以来的所有主流处理器吗？），它们都可能会因此而泄露信息。

这个问题第一次被披露是由来自 [奥地利的 Graz Technical University](https://gruss.cc/files/kaiser.pdf) 的研究者。他们披露的信息表明这个问题已经足够破坏内核模式地址空间布局随机化（内核 ASLR，或称 KASLR）。ASLR 是防范 [缓冲区溢出](https://arstechnica.com/information-technology/2015/08/how-security-flaws-work-the-buffer-overflow/) 漏洞利用的最后一道防线。启用 ASLR 之后，程序和它们的数据被置于随机的内存地址中，它将使一些安全漏洞利用更加困难。KASLR 将这种随机化应用到内核中，这样就使内核的数据（包括页面表）和代码也随机化分布。

Graz 的研究者开发了 [KAISER](https://github.com/IAIK/KAISER)，一组防范这个问题的 Linux 内核补丁。

如果这个问题正好使 ASLR 的随机化被破坏了，这或许将成为一个巨大的灾难。ASLR 是一个非常强大的保护措施，但是它并不是完美的，这意味着对于黑客来说将是一个很大的障碍，一个无法逾越的障碍。整个行业对此的反应是 —— Windows 和 Linux 都有一个非常重要的变化，秘密开发 —— 这表明不仅是 ASLR 被破坏了，而且从内核泄露出信息的更普遍的技术被开发出来了。确实是这样的，研究者已经 [在 Twitter 上发布信息](https://twitter.com/brainsmoke/status/948561799875502080)，他们已经可以随意泄露和读取内核数据了。另一种可能是，漏洞可能被用于从虚拟机中“越狱”，并可能会危及 hypervisor。

Windows 和 Linux 选择的解决方案是非常相似的，将 KAISER 分为两个区域：内核页面表的条目不再是由每个进程共享。在 Linux 中，这被称为内核页面表隔离（KPTI）。

应用补丁后，内存地址仍然被一分为二：这样使内核的那一半几乎是空的。当然它并不是非常的空，因为一些内核片断需要永久映射，不论进程是运行在 Ring 3 还是 Ring 0 中，它都几乎是空的。这意味着如果恶意用户程序尝试去探测内核内存以及泄露信息，它将会失败 —— 因为那里几乎没有信息。而真正的内核页面中只有当内核自身运行的时刻它才能被用到。

这样做就破坏了最初将地址空间分割的理由。现在，每次切换到用户程序时，TLB 需要实时去清除与内核页面表相关的所有条目，这样就失去了启用分割带来的性能提升。

影响的具体大小取决于工作负载。每当一个程序被调入到内核 —— 从磁盘读入、发送数据到网络、打开一个文件等等 —— 这种调用的成本可能会增加一点点，因为它强制 TLB 清除了缓存并实时加载内核页面表。不使用内核的程序可能会观测到 2 - 3 个百分点的性能影响 —— 这里仍然有一些开销，因为内核仍然是偶尔会运行去处理一些事情，比如多任务等等。

但是大量调用进入到内核的工作负载将观测到很大的性能损失。在一个基准测试中，一个除了调入到内核之外什么都不做的程序，观察到 [它的性能下降大约为 50%](https://twitter.com/grsecurity/status/947257569906757638)；换句话说就是，打补丁后每次对内核的调用的时间要比不打补丁调用内核的时间增加一倍。基准测试使用的 Linux 的网络回环（loopback）也观测到一个很大的影响，比如，在 Postgres 的基准测试中大约是 [17%](https://www.postgresql.org/message-id/20180102222354.qikjmf7dvnjgbkxe@alap3.anarazel.de)。真实的数据库负载使用了实时网络可能观测到的影响要低一些，因为使用实时网络时，内核调用的开销基本是使用真实网络的开销。

虽然对 Intel 系统的影响是众所周知的，但是它们可能并不是唯一受影响的。其它的一些平台，比如 SPARC 和 IBM 的 S390，是不受这个问题影响的，因为它们的处理器的内存管理并不需要分割地址空间和共享内核页面表；在这些平台上的操作系统一直就是将它们的内核页面表从用户模式中隔离出来的。但是其它的，比如 ARM，可能就没有这么幸运了；[适用于 ARM Linux 的类似补丁](https://lwn.net/Articles/740393/) 正在开发中。

---

[PETER BRIGHT](https://arstechnica.com/author/peter-bright) 是 Ars 的一位技术编辑。他涉及微软、编程及软件开发、Web 技术和浏览器、以及安全方面。它居住在纽约的布鲁克林。

---

via: <https://arstechnica.com/gadgets/2018/01/whats-behind-the-intel-design-flaw-forcing-numerous-patches/>

作者：[PETER BRIGHT](https://arstechnica.com/author/peter-bright/) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

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


Torrents（种子）：你需要知道的一切事情

Summary: ，在 Torrents 中涉及到各种概念。因此，通过这篇文章我们从技术的角度来了解 torrenting 是什么。

Torrents（种子）：你需要知道的一切事情
=======================

![](/data/attachment/album/201802/27/000941ymcm66mwceweetpe.jpg)

**Torrents（种子）** — 每次听到这个词时，在我的脑海里想到的唯一的事情就是免费的电影、游戏、和被破解的软件。但是我们并不知道它们是如何工作的，在“种子”中涉及到各种概念。因此，通过这篇文章我们从技术的角度来了解**种子下载**是什么。

### “种子”是什么？

“种子”是一个到因特网上文件位置的链接。它们不是一个文件，它们仅仅是动态指向到你想去下载的原始文件上。

例如：如果你点击 [Google Chrome](https://www.google.com/chrome/)，你可以从谷歌的服务器上下载 Google Chrome 浏览器。

如果你明天、或者下周、或者下个月再去点击那个链接，这个文件仍然可以从谷歌服务器上去下载。

但是当我们使用“种子”下载时，它并没有固定的服务器。文件是从以前使用“种子”下载的其它人的个人电脑上下载的。

### Torrents 是如何工作的？

[![Peer to peer network](/data/attachment/album/201802/27/000942occ10c59nnicgsnq.png)](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/torrent_orig.png)

假设 ‘A’ 上有一些视频，它希望以“种子”方式去下载。因此，他创建了一个“种子”，并将这个链接发送给 ‘B’，这个链接包含了那个视频在因特网上的准确 IP 地址的信息。因此，当 ‘B’ 开始下载那个文件的时候，‘B’ 连接到 ‘A’ 的计算机。在 ‘B’ 下载完成这个视频之后，‘B’ 将开始做为种子，也就是 ‘B’ 将允许其它的 ‘C’ 或者 ‘D’ 从 ‘B’ 的计算机上下载它。

因此每个人先下载文件然后会上传，下载的人越多，下载的速度也越快。并且在任何情况下，如果想停止上传，也没有问题，随时可以。这样做并不会成为什么问题，除非很多的人下载而上传的人很少。

### 播种者和索取者

[![kickass torrents website](/data/attachment/album/201802/27/000942e3tdln7s99sqedpq.png)](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/seeders_orig.png)

下载完成特定文件的用户将会即刻做为上传者，因此，可以被新用户下载的已下载者称为播种者。

而一些还没有完成特定文件的下载者，并且还正在下载的用户称为索取者。

### 块

[![qbittorrent software for linux](/data/attachment/album/201802/27/000943vs2kvlp4v4spp425.png)](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/peers_orig.png)

所有的“种子”文件都独立分割成固定大小的数据包，因此，它们可以非线性顺序和随机顺序下载。每个块都有唯一的标识，因此，一旦所有的块下载完成之后，它们会被拼接出原始文件。

正是因为这种机制，如果你正在从某人处下载一个文件，假如这个时候因某些原因他停止了上传，你可以继续从其它的播种者处继续下载，而不需要从头开始重新下载。

### 对端

对端是指当前连接到你的索取者。一个正在上传的索取者，不论它下载了多少块，它就是一个对端。

**例如：**

一个已经下载了文件的前 50 个块的用户就是一个索取者，但是，他又同时上传这些文件，而你只有前 10 个块，因此，你可以从他那里下载最多 50 个块。这时候他就成了你的对端。

### 最佳实践

当你下载一个“种子”时，总是选择最大的播种者。这就是最佳经验。

这里并没有最小的标准，但是只要确保你选择的是最大的那一个播种者就可以了。

### “种子”相关的法律

[![piracy is illegal](/data/attachment/album/201802/27/000945sxgpmvn4nx0z4v4z.png)](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/torrent-laws_orig.png)

“种子”相关的法律和其它的法律并没有什么区别，对受版权保护的其它任何东西一样，侵权行为会受到法律的制裁。大多数的政府都拦截“种子”站点和协议，但是“种子”下载本身并不是有害的东西。

“种子”对快速分享文件是非常有用的，并且它们被用来共享开源社区的软件，因为它们能节约大量的服务器资源。但是，许多人却因为盗版而使用它们。

### 结束语

Torrenting 是降低服务器上负载的一个非常完美的技术。“种子”下载可以使我们将下载速度提升到网卡的极限，这是非常好的。但是，在这种非中心化的服务器上，盗版成为一种必然发生的事。限制我们分享的内容，从不去下载盗版的东西，这是我们的道德责任。

请在下面的评论中分享你使用“种子”的心得，分享你喜欢的、法律许可下载的“种子”网站。

---

via: <http://www.linuxandubuntu.com/home/torrents-everything-you-need-to-know>

作者：[LINUXANDUBUNTU](http://www.linuxandubuntu.com) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

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


关于处理器你所需要知道的一切

[![](/data/attachment/album/201802/27/064657x0oagcol2fs101fc.jpg)](http://www.theitstuff.com/wp-content/uploads/2017/10/processors-all-you-need-to-know.jpg)

我们的手机、主机以及笔记本电脑这样的数字设备已经变得如此成熟，以至于它们进化成为我们的一部分，而不只是一种设备。

在应用和软件的帮助下，处理器执行许多任务。我们是否曾经想过是什么给了这些软件这样的能力？它们是如何执行它们的逻辑的？它们的大脑在哪？

我们知道 CPU （或称处理器）是那些需要处理数据和执行逻辑任务的设备的大脑。

[![cpu image](/data/attachment/album/201802/27/064657bqsgsoyunwffvm1f.jpg)](http://www.theitstuff.com/wp-content/uploads/2017/10/download.jpg)

在处理器的深处有那些不一样的概念呢？它们是如何演化的？一些处理器是如何做到比其它处理器更快的？让我们来看看关于处理器的主要术语，以及它们是如何影响处速度的。

### 架构

处理器有不同的架构，你一定遇到过不同类型的程序说它们是 64 位或 32 位的，这其中的意思就是程序支持特定的处理器架构。

如果一颗处理器是 32 位的架构，这意味着这颗处理器能够在一个处理周期内处理一个 32 位的数据。

同理可得，64 位的处理器能够在一个周期内处理一个 64 位的数据。

同时，你可以使用的内存大小决定于处理器的架构，你可以使用的内存总量为 2 的处理器架构的幂次方（如：`2^64`）。

16 位架构的处理器，仅仅有 64 kb 的内存使用。32 位架构的处理器，最大可使用的 RAM 是 4 GB，64 位架构的处理器的可用内存是 16 EB。

### 核心

在电脑上，核心是基本的处理单元。核心接收指令并且执行它。越多的核心带来越快的速度。把核心比作工厂里的工人，越多的工人使工作能够越快的完成。另一方面，工人越多，你所付出的薪水也就越多，工厂也会越拥挤；相对于核心来说，越多的核心消耗更多的能量，比核心少的 CPU 更容易发热。

### 时钟速度

[![CPU CLOCK SPEED](/data/attachment/album/201802/27/064658xgartiavgkkainm8.jpg)](http://www.theitstuff.com/wp-content/uploads/2017/10/download-1.jpg)

GHz 是 GigaHertz 的简写，Giga 意思是 10 亿次，Hertz （赫兹）意思是一秒有几个周期，2 GHz 的处理器意味着处理器一秒能够执行 20 亿个周期 。

它也以“频率”或者“时钟速度”而熟知。这项数值越高，CPU 的性能越好。

### CPU 缓存

CPU 缓存是处理器内部的一块小的存储单元，用来存储一些内存。不管如何，我们需要执行一些任务时，数据需要从内存传递到 CPU，CPU 的工作速度远快于内存，CPU 在大多数时间是在等待从内存传递过来的数据，而此时 CPU 是处于空闲状态的。为了解决这个问题，内存持续的向 CPU 缓存发送数据。

一般的处理器会有 2 ~ 3 Mb 的 CPU 缓存。高端的处理器会有 6 Mb 的 CPU 缓存，越大的缓存，意味着处理器更好。

### 印刷工艺

晶体管的大小就是处理器平板印刷的大小，尺寸通常是纳米，更小的尺寸意味者更紧凑。这可以让你有更多的核心，更小的面积，更小的能量消耗。

最新的 Intel 处理器有 14 nm 的印刷工艺。

### 热功耗设计（TDP）

代表着平均功耗，单位是瓦特，是在全核心激活以基础频率来处理 Intel 定义的高复杂度的负载时，处理器所散失的功耗。

所以，越低的热功耗设计对你越好。一个低的热功耗设计不仅可以更好的利用能量，而且产生更少的热量。

[![battery](/data/attachment/album/201802/27/064658bd0pc9dh3bxxxdlr.jpg)](http://www.theitstuff.com/wp-content/uploads/2017/10/download-2.jpg)

桌面版的处理器通常消耗更多的能量，热功耗消耗的能量能在 40% 以上，相对应的移动版本只有不到桌面版本的 1/3。

### 内存支持

我们已经提到了处理器的架构是如何影响到我们能够使用的内存总量，但这只是理论上而已。在实际的应用中，我们所能够使用的内存的总量对于处理器的规格来说是足够的，它通常是由处理器规格详细规定的。

[![RAM](/data/attachment/album/201802/27/064658ca47hh1vs9p8z7q6.jpg)](http://www.theitstuff.com/wp-content/uploads/2017/10/images.jpg)

它也指出了内存所支持的 DDR 的版本号。

### 超频

前面我们讲过时钟频率，超频是程序强迫 CPU 执行更多的周期。游戏玩家经常会使他们的处理器超频，以此来获得更好的性能。这样确实会增加速度，但也会增加消耗的能量，产生更多的热量。

一些高端的处理器允许超频，如果我们想让一个不支持超频的处理器超频，我们需要在主板上安装一个新的 BIOS 。 这样通常会成功，但这种情况是不安全的，也是不建议的。

### 超线程（HT）

如果不能添加核心以满足特定的处理需要，那么超线程是建立一个虚拟核心的方式。

如果一个双核处理器有超线程，那么这个双核处理器就有两个物理核心和两个虚拟核心，在技术上讲，一个双核处理器拥有四个核心。

### 结论

处理器有许多相关的数据，这些对数字设备来说是最重要的部分。我们在选择设备时，我们应该在脑海中仔细的检查处理器在上面提到的数据。

时钟速度、核心数、CPU 缓存，以及架构是最重要的数据。印刷尺寸以及热功耗设计重要性差一些 。

仍然有疑惑？ 欢迎评论，我会尽快回复的。

---

via: <http://www.theitstuff.com/processors-everything-need-know>

作者：[Rishabh Kandari](http://www.theitstuff.com/author/reevkandari) 译者：[singledo](https://github.com/singledo) 校对：[wxy](https://github.com/wxy)

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


学习用工具来驾驭 Git 历史

Summary: 现在，我们来深入了解怎么去使用 git 的历史以及如何很轻松地去浏览它的历史。

现在，我们来深入了解怎么去使用 git 的历史以及如何很轻松地去浏览它的历史。

![](/data/attachment/album/201802/27/170304t4tk9oogkronnint.jpg)

在你的日常工作中，不可能每天都从头开始去开发一个新的应用程序。而真实的情况是，在日常工作中，我们大多数时候所面对的都是遗留下来的一个代码库，去修改一些特性的内容或者现存的一些代码行，这是我们在日常工作中很重要的一部分。而这也就是分布式版本控制系统 `git` 的价值所在。现在，我们来深入了解怎么去使用 `git` 的历史以及如何很轻松地去浏览它的历史。

### Git 历史

首先和最重要的事是，什么是 `git` 历史？正如其名字一样，它是一个 `git` 仓库的提交历史。它包含一堆提交信息，其中有它们的作者的名字、该提交的哈希值以及提交日期。查看一个 `git` 仓库历史的方法很简单，就是一个 `git log` 命令。

>
> *旁注：为便于本文的演示，我们使用 Ruby on Rails 的仓库的 `master` 分支。之所以选择它的理由是因为，Rails 有良好的 `git` 历史，漂亮的提交信息、引用以及对每个变更的解释。如果考虑到代码库的大小、维护者的年龄和数量，Rails 肯定是我见过的最好的仓库。当然了，我并不是说其它的 `git` 仓库做的不好，它只是我见过的比较好的一个仓库。*
>
>
>

那么，回到 Rails 仓库。如果你在 Ralis 仓库上运行 `git log`。你将看到如下所示的输出：

```
commit 66ebbc4952f6cfb37d719f63036441ef98149418
Author: Arthur Neves <foo@bar.com>
Date:   Fri Jun 3 17:17:38 2016 -0400

    Dont re-define class SQLite3Adapter on test

    We were declaring  in a few tests, which depending of    the order load will cause an error, as the super class could change.

    see https://github.com/rails/rails/commit/ac1c4e141b20c1067af2c2703db6e1b463b985da#commitcomment-17731383

commit 755f6bf3d3d568bc0af2c636be2f6df16c651eb1
Merge: 4e85538 f7b850e
Author: Eileen M. Uchitelle <foo@bar.com>
Date:   Fri Jun 3 10:21:49 2016 -0400

    Merge pull request #25263 from abhishekjain16/doc_accessor_thread

    [skip ci] Fix grammar

commit f7b850ec9f6036802339e965c8ce74494f731b4a
Author: Abhishek Jain <foo@bar.com>
Date:   Fri Jun 3 16:49:21 2016 +0530

    [skip ci] Fix grammar

commit 4e85538dddf47877cacc65cea6c050e349af0405
Merge: 082a515 cf2158c
Author: Vijay Dev <foo@bar.com>
Date:   Fri Jun 3 14:00:47 2016 +0000

    Merge branch 'master' of github.com:rails/docrails

    Conflicts:
        guides/source/action_cable_overview.md

commit 082a5158251c6578714132e5c4f71bd39f462d71
Merge: 4bd11d4 3bd30d9
Author: Yves Senn <foo@bar.com>
Date:   Fri Jun 3 11:30:19 2016 +0200

    Merge pull request #25243 from sukesan1984/add_i18n_validation_test

    Add i18n_validation_test

commit 4bd11d46de892676830bca51d3040f29200abbfa
Merge: 99d8d45 e98caf8
Author: Arthur Nogueira Neves <foo@bar.com>
Date:   Thu Jun 2 22:55:52 2016 -0400

    Merge pull request #25258 from alexcameron89/master

    [skip ci] Make header bullets consistent in engines.md

commit e98caf81fef54746126d31076c6d346c48ae8e1b
Author: Alex Kitchens <foo@bar.com>
Date:   Thu Jun 2 21:26:53 2016 -0500

    [skip ci] Make header bullets consistent in engines.md

```

正如你所见，`git log` 展示了提交的哈希、作者及其 email 以及该提交创建的日期。当然，`git` 输出的可定制性很强大，它允许你去定制 `git log` 命令的输出格式。比如说，我们只想看提交信息的第一行，我们可以运行 `git log --oneline`，它将输出一个更紧凑的日志：

```
66ebbc4 Dont re-define class SQLite3Adapter on test
755f6bf Merge pull request #25263 from abhishekjain16/doc_accessor_thread
f7b850e [skip ci] Fix grammar4e85538 Merge branch 'master' of github.com:rails/docrails
082a515 Merge pull request #25243 from sukesan1984/add_i18n_validation_test
4bd11d4 Merge pull request #25258 from alexcameron89/master
e98caf8 [skip ci] Make header bullets consistent in engines.md
99d8d45 Merge pull request #25254 from kamipo/fix_debug_helper_test
818397c Merge pull request #25240 from matthewd/reloadable-channels
2c5a8ba Don't blank pad day of the month when formatting dates
14ff8e7 Fix debug helper test

```

如果你想看 `git log` 的全部选项，我建议你去查阅 `git log` 的 man 页面，你可以在一个终端中输入 `man git-log` 或者 `git help log` 来获得。

>
> *小提示：如果你觉得 `git log` 看起来太恐怖或者过于复杂，或者你觉得看它太无聊了，我建议你去寻找一些 `git` 的 GUI 或命令行工具。在之前，我使用过 [GitX](http://gitx.frim.nl/) ，我觉得它很不错，但是，由于我看命令行更“亲切”一些，在我尝试了 [tig](https://github.com/jonas/tig) 之后，就再也没有去用过它。*
>
>
>

### 寻找尼莫

现在，我们已经知道了关于 `git log` 命令的一些很基础的知识之后，我们来看一下，在我们的日常工作中如何使用它更加高效地浏览历史。

假如，我们怀疑在 `String#classify` 方法中有一个预期之外的行为，我们希望能够找出原因，并且定位出实现它的代码行。

为达到上述目的，你可以使用的第一个命令是 `git grep`，通过它可以找到这个方法定义在什么地方。简单来说，这个命令输出了匹配特定模式的那些行。现在，我们来找出定义它的方法，它非常简单 —— 我们对 `def classify` 运行 grep，然后看到的输出如下：

```
➜  git grep 'def classify'

activesupport/lib/active_support/core_ext/string/inflections.rb:    def classifyactivesupport/lib/active_support/inflector/methods.rb:    def classify(table_name)tools/profile:    def classify

```

现在，虽然我们已经看到这个方法是在哪里创建的，但是，并不能够确定它是哪一行。如果，我们在 `git grep` 命令上增加 `-n` 标志，`git` 将提供匹配的行号：

```
➜  git grep -n 'def classify'

activesupport/lib/active_support/core_ext/string/inflections.rb:205:  def classifyactivesupport/lib/active_support/inflector/methods.rb:186:    def classify(table_name)tools/profile:112:    def classify

```

更好看了，是吧？考虑到上下文，我们可以很轻松地找到，这个方法在 `activesupport/lib/active_support/core_ext/string/inflections.rb` 的第 205 行的 `classify` 方法，它看起来像这样，是不是很容易？

```
# Creates a class name from a plural table name like Rails does for table names to models.
# Note that this returns a string and not a class. (To convert to an actual class
# follow +classify+ with +constantize+.)
#
#   'ham_and_eggs'.classify # => "HamAndEgg"
#   'posts'.classify        # => "Post"
    def classify
        ActiveSupport::Inflector.classify(self)
    end

```

尽管我们找到的这个方法是在 `String` 上的一个常见的调用，它调用了 `ActiveSupport::Inflector` 上的另一个同名的方法。根据之前的 `git grep` 的结果，我们可以很轻松地发现结果的第二行， `activesupport/lib/active_support/inflector/methods.rb` 在 186 行上。我们正在寻找的方法是这样的：

```
# Creates a class name from a plural table name like Rails does for table
# names to models. Note that this returns a string and not a Class (To
# convert to an actual class follow +classify+ with constantize).
#
#   classify('ham_and_eggs') # => "HamAndEgg"
#   classify('posts')        # => "Post"
#
# Singular names are not handled correctly:
#
#   classify('calculus')     # => "Calculus"
def classify(table_name)
    # strip out any leading schema name
    camelize(singularize(table_name.to_s.sub(/.*\./, ''.freeze)))
end

```

酷！考虑到 Rails 仓库的大小，我们借助 `git grep` 找到它，用时都没有超越 30 秒。

### 那么，最后的变更是什么？

现在，我们已经找到了所要找的方法，现在，我们需要搞清楚这个文件所经历的变更。由于我们已经知道了正确的文件名和行数，我们可以使用 `git blame`。这个命令展示了一个文件中每一行的最后修订者和修订的内容。我们来看一下这个文件最后的修订都做了什么：

```
git blame activesupport/lib/active_support/inflector/methods.rb

```

虽然我们得到了这个文件每一行的最后的变更，但是，我们更感兴趣的是对特定方法（176 到 189 行）的最后变更。让我们在 `git blame` 命令上增加一个选项，让它只显示那些行的变化。此外，我们将在命令上增加一个 `-s` （忽略）选项，去跳过那一行变更时的作者名字和修订（提交）的时间戳：

```
git blame -L 176,189 -s activesupport/lib/active_support/inflector/methods.rb

9fe8e19a 176)   #Creates a class name from a plural table name like Rails does for table
5ea3f284 177)   # names to models. Note that this returns a string and not a Class (To
9fe8e19a 178)   # convert to an actual class follow +classify+ with #constantize).
51cd6bb8 179)   #
6d077205 180)   #   classify('ham_and_eggs') # => "HamAndEgg"
9fe8e19a 181)   #   classify('posts')        # => "Post"
51cd6bb8 182)   #
51cd6bb8 183)   # Singular names are not handled correctly:
5ea3f284 184)   #
66d6e7be 185)   #   classify('calculus')     # => "Calculus"
51cd6bb8 186)   def classify(table_name)
51cd6bb8 187)     # strip out any leading schema name
5bb1d4d2 188)     camelize(singularize(table_name.to_s.sub(/.*\./, ''.freeze)))
51cd6bb8 189)     end

```

现在，`git blame` 命令的输出展示了指定行的全部内容以及它们各自的修订。让我们来看一下指定的修订，换句话说就是，每个变更都修订了什么，我们可以使用 `git show` 命令。当指定一个修订哈希（像 `66d6e7be`）作为一个参数时，它将展示这个修订的全部内容。包括作者名字、时间戳以及完整的修订内容。我们来看一下 188 行最后的修订都做了什么？

```
git show 5bb1d4d2

```

你亲自做实验了吗？如果没有做，我直接告诉你结果，这个令人惊叹的 [提交](https://github.com/rails/rails/commit/5bb1d4d288d019e276335465d0389fd2f5246bfd) 是由 [Schneems](https://twitter.com/schneems) 完成的，他通过使用 frozen 字符串做了一个非常有趣的性能优化，这在我们当前的场景中是非常有意义的。但是，由于我们在这个假设的调试会话中，这样做并不能告诉我们当前问题所在。因此，我们怎么样才能够通过研究来发现，我们选定的方法经过了哪些变更？

### 搜索日志

现在，我们回到 `git` 日志，现在的问题是，怎么能够看到 `classify` 方法经历了哪些修订？

`git log` 命令非常强大，因此它提供了非常多的列表选项。我们尝试使用 `-p` 选项去看一下保存了这个文件的 `git` 日志内容，这个选项的意思是在 `git` 日志中显示这个文件的完整补丁:

```
git log -p activesupport/lib/active_support/inflector/methods.rb

```

这将给我们展示一个很长的修订列表，显示了对这个文件的每个修订。但是，正如下面所显示的，我们感兴趣的是对指定行的修订。对命令做一个小的修改，只显示我们希望的内容：

```
git log -L 176,189:activesupport/lib/active_support/inflector/methods.rb

```

`git log` 命令接受 `-L` 选项，它用一个行的范围和文件名做为参数。它的格式可能有点奇怪，格式解释如下：

```
git log -L <start-line>,<end-line>:<path-to-file>

```

当我们运行这个命令之后，我们可以看到对这些行的一个修订列表，它将带我们找到创建这个方法的第一个修订：

```
commit 51xd6bb829c418c5fbf75de1dfbb177233b1b154
Author: Foo Bar <foo@bar.com>
Date:   Tue Jun 7 19:05:09 2011 -0700

    Refactor

diff--git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb
--- a/activesupport/lib/active_support/inflector/methods.rb
+++ b/activesupport/lib/active_support/inflector/methods.rb
@@ -58,0 +135,14 @@
+    # Create a class name from a plural table name like Rails does for table names to models.
+    # Note that this returns a string and not a Class. (To convert to an actual class
+    # follow +classify+ with +constantize+.)
+    #
+    # Examples:
+    #   "egg_and_hams".classify # => "EggAndHam"
+    #   "posts".classify        # => "Post"
+    #
+    # Singular names are not handled correctly:
+    #   "business".classify     # => "Busines"
+    def classify(table_name)
+      # strip out any leading schema name
+      camelize(singularize(table_name.to_s.sub(/.*\./, '')))
+    end

```

现在，我们再来看一下 —— 它是在 2011 年提交的。`git` 可以让我们重回到这个时间。这是一个很好的例子，它充分说明了足够的提交信息对于重新了解当时的上下文环境是多么的重要，因为从这个提交信息中，我们并不能获得足够的信息来重新理解当时的创建这个方法的上下文环境，但是，话说回来，你**不应该**对此感到恼怒，因为，你看到的这些项目，它们的作者都是无偿提供他们的工作时间和精力来做开源工作的。（向开源项目贡献者致敬！）

回到我们的正题，我们并不能确认 `classify` 方法最初实现是怎么回事，考虑到这个第一次的提交只是一个重构。现在，如果你认为，“或许、有可能、这个方法不在 176 行到 189 行的范围之内，那么就你应该在这个文件中扩大搜索范围”，这样想是对的。我们看到在它的修订提交的信息中提到了“重构”这个词，它意味着这个方法可能在那个文件中是真实存在的，而且是在重构之后它才存在于那个行的范围内。

但是，我们如何去确认这一点呢？不管你信不信，`git` 可以再次帮助你。`git log` 命令有一个 `-S` 选项，它可以传递一个特定的字符串作为参数，然后去查找代码变更（添加或者删除）。也就是说，如果我们执行 `git log -S classify` 这样的命令，我们可以看到所有包含 `classify` 字符串的变更行的提交。

如果你在 Ralis 仓库上运行上述命令，首先你会发现这个命令运行有点慢。但是，你应该会发现 `git` 实际上解析了在那个仓库中的所有修订来匹配这个字符串，其实它的运行速度是非常快的。在你的指尖下 `git` 再次展示了它的强大之处。因此，如果去找关于 `classify` 方法的第一个修订，我们可以运行如下的命令：

```
git log -S 'def classify'

```

它将返回所有这个方法的引用和修改的地方。如果你一直往下看，你将看到日志中它的最后的提交：

```
commit db045dbbf60b53dbe013ef25554fd013baf88134
Author: David Heinemeier Hansson <foo@bar.com>
Date:   Wed Nov 24 01:04:44 2004 +0000
    Initial
    git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@4 5ecf4fe2-1ee6-0310-87b1-e25e094e27de

```

很酷！是吧？它初次被提交到 Rails，是由 DHH 在一个 `svn` 仓库上做的！这意味着 `classify` 大概在一开始就被提交到了 Rails 仓库。现在，我们去看一下这个提交的所有变更信息，我们运行如下的命令：

```
git show db045dbbf60b53dbe013ef25554fd013baf88134

```

非常好！我们终于找到它的根源了。现在，我们使用 `git log -S 'def classify'` 的输出，结合 `git log -L` 命令来跟踪这个方法都发生了哪些变更。

### 下次见

当然，我们并没有真的去修改任何 bug，因为我们只是去尝试使用一些 `git` 命令，来演示如何查看 `classify` 方法的演变历史。但是不管怎样，`git` 是一个非常强大的工具，我们必须学好它、用好它。我希望这篇文章可以帮助你掌握更多的关于如何使用 `git` 的知识。

你喜欢这些内容吗？

---

作者简介：

后端工程师，对 Ruby、Go、微服务、构建弹性架构来解决大规模部署带来的挑战很感兴趣。我在阿姆斯特丹的 Rails Girls 担任顾问，维护着一个小而精的列表，并且经常为开源做贡献。

那个列表是我写的关于软件开发、编程语言以及任何我感兴趣的东西。

---

via: <https://ieftimov.com/learn-your-tools-navigating-git-history>

作者：[Ilija Eftimov](https://ieftimov.com/) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

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