mirror of
https://github.com/LCTT/TranslateProject.git
synced 2024-12-26 21:30:55 +08:00
Merge remote-tracking branch 'LCTT/master'
This commit is contained in:
commit
e15bbd50f5
@ -1,14 +1,16 @@
|
||||
Cron 任务入门指南
|
||||
======
|
||||
|
||||
![](https://www.ostechnix.com/wp-content/uploads/2018/05/cron-任务s1-720x340.jpg)
|
||||
** Cron **是您可以在任何类 Unix 操作系统中找到的最有用的实用程序之一。它用于安排命令在特定时间执行。这些预定的命令或任务被称为 “Cron 任务”。Cron 通常用于运行计划备份,监视磁盘空间,定期删除不再需要的文件(例如日志文件),运行系统维护任务等等。在本简要指南中,我们将看到 Linux 中 Cron 任务 的基本用法。
|
||||
![](https://www.ostechnix.com/wp-content/uploads/2018/05/cron-jobs1-720x340.jpg)
|
||||
|
||||
Cron 任务入门指南
|
||||
**Cron** 是您可以在任何类 Unix 操作系统中找到的最有用的实用程序之一。它用于安排命令在特定时间执行。这些预定的命令或任务被称为 “Cron 任务”。Cron 通常用于运行计划备份、监视磁盘空间、定期删除不再需要的文件(例如日志文件)、运行系统维护任务等等。在本简要指南中,我们将看到 Linux 中 Cron 任务的基本用法。
|
||||
|
||||
### Cron 任务入门指南
|
||||
|
||||
cron 任务的典型格式是:
|
||||
|
||||
```
|
||||
Minute(0-59) Hour(0-24) Day_of_month(1-31) Month(1-12) Day_of_week(0-6) Command_to_execute
|
||||
分钟(0-59) 小时(0-24) 日(1-31) 月(1-12) 星期(0-6) 要执行的命令
|
||||
```
|
||||
|
||||
只需记住 cron 任务的格式或打印下面的插图并将其放在你桌面上即可。
|
||||
@ -17,18 +19,20 @@ Minute(0-59) Hour(0-24) Day_of_month(1-31) Month(1-12) Day_of_week(0-6) Command_
|
||||
|
||||
在上图中,星号表示特定的时间块。
|
||||
|
||||
要显示当前登录用户的 ** crontab ** 文件的内容:
|
||||
要显示当前登录用户的 crontab 文件的内容:
|
||||
|
||||
```
|
||||
$ crontab -l
|
||||
|
||||
```
|
||||
|
||||
要编辑当前用户的 cron 任务,请执行以下操作:
|
||||
|
||||
```
|
||||
$ crontab -e
|
||||
```
|
||||
|
||||
如果这是第一次编辑此文件,你将需要使用编辑器来编辑此文件。
|
||||
如果这是第一次编辑此文件,会询问你使用哪个编辑器来编辑此文件。
|
||||
|
||||
```
|
||||
no crontab for sk - using an empty one
|
||||
|
||||
@ -39,168 +43,168 @@ Select an editor. To change later, run 'select-editor'.
|
||||
4. /bin/ed
|
||||
|
||||
Choose 1-4 [1]:
|
||||
|
||||
```
|
||||
|
||||
选择适合你的编辑器。这里是一个示例crontab文件的样子。
|
||||
选择适合你的编辑器。这里是一个示例 crontab 文件的样子。
|
||||
|
||||
![][3]
|
||||
|
||||
在这个文件中,你需要添加你的 cron 任务s。
|
||||
在这个文件中,你需要添加你的 cron 任务。
|
||||
|
||||
要编辑其他用户的 crontab,例如 ostechnix,请执行:
|
||||
|
||||
要编辑其他用户的crontab,例如 ostechnix,请执行:
|
||||
```
|
||||
$ crontab -u ostechnix -e
|
||||
```
|
||||
|
||||
让我们看看一些例子。
|
||||
|
||||
要 **每分钟** 执行一次 cron 任务 , 需使用如下格式.
|
||||
要 **每分钟** 执行一次 cron 任务,需使用如下格式。
|
||||
|
||||
```
|
||||
* * * * * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
要每5分钟运行一次cron 任务,请在crontab文件中添加以下内容。
|
||||
要每 5 分钟运行一次 cron 任务,请在 crontab 文件中添加以下内容。
|
||||
|
||||
```
|
||||
*/5 * * * * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
要在每 1/4 个小时(每15分钟)运行一次 cron 任务,请添加以下内容:
|
||||
要在每 1/4 个小时(每 15 分钟)运行一次 cron 任务,请添加以下内容:
|
||||
|
||||
```
|
||||
*/15 * * * * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
要每小时的第30分钟运行一次 cron 任务,请运行:
|
||||
要每小时的第 30 分钟运行一次 cron 任务,请运行:
|
||||
|
||||
```
|
||||
30 * * * * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
您还可以使用逗号定义多个时间间隔。例如,以下 cron 任务 每小时运行三次,分别在第 0, 5 和 10 分钟运行:
|
||||
您还可以使用逗号定义多个时间间隔。例如,以下 cron 任务每小时运行三次,分别在第 0、 5 和 10 分钟运行:
|
||||
|
||||
```
|
||||
0,5,10 * * * * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
每半小时运行一次 cron 任务:
|
||||
|
||||
```
|
||||
*/30 * * * * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
每小时运行一次:
|
||||
|
||||
```
|
||||
0 * * * * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
每2小时运行一次:
|
||||
每 2 小时运行一次:
|
||||
|
||||
```
|
||||
0 */2 * * * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
每天运行一项(在00:00运行):
|
||||
每天运行一项(在 00:00 运行):
|
||||
|
||||
```
|
||||
0 0 * * * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
每天凌晨3点运行:
|
||||
每天凌晨 3 点运行:
|
||||
|
||||
```
|
||||
0 3 * * * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
每周日运行:
|
||||
|
||||
```
|
||||
0 0 * * SUN <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
或使用,
|
||||
|
||||
```
|
||||
0 0 * * 0 <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
它将在每周日的午夜 00:00 运行。
|
||||
|
||||
星期一至星期五每天运行一次,亦即每个工作日运行一次:
|
||||
|
||||
```
|
||||
0 0 * * 1-5 <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
这项工作将于00:00开始。
|
||||
这项工作将于 00:00 开始。
|
||||
|
||||
每个月运行一次:
|
||||
|
||||
```
|
||||
0 0 1 * * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
于每月第1天的16:15运行:
|
||||
于每月第 1 天的 16:15 运行:
|
||||
|
||||
```
|
||||
15 16 1 * * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
每季度运行一次,亦即每隔3个月的第1天运行:
|
||||
每季度运行一次,亦即每隔 3 个月的第 1 天运行:
|
||||
|
||||
```
|
||||
0 0 1 */3 * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
在特定月份的特定时间运行:
|
||||
|
||||
```
|
||||
5 0 * 4 * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
每个四月的 00:05 运行。
|
||||
|
||||
每6个月运行:
|
||||
每 6 个月运行:
|
||||
|
||||
```
|
||||
0 0 1 */6 * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
这个定时任务将在每六个月的第一天的 00:00 运行。
|
||||
|
||||
每年运行:
|
||||
|
||||
```
|
||||
0 0 1 1 * <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
这项 cron 任务将于 1 月份的第一天的 00:00 运行。
|
||||
|
||||
我们也可以使用以下字符串来定义任务。
|
||||
|
||||
@reboot 在每次启动时运行一次。 @yearly 每年运行一次。 @annually(和 @yearly 一样)。 @monthly 每月运行一次。 @weekly 每周运行一次。 @daily 每天运行一次。 @midnight (和 @daily 一样)。 @hourly 每小时运行一次。
|
||||
`@reboot` 在每次启动时运行一次。 `@yearly` 每年运行一次。 `@annually`(和 `@yearly` 一样)。 `@monthly` 每月运行一次。 `@weekly` 每周运行一次。 `@daily` 每天运行一次。 `@midnight` (和 `@daily` 一样)。 `@hourly` 每小时运行一次。
|
||||
|
||||
例如,要在每次重新启动服务器时运行任务,请将此行添加到您的 crontab 文件中。
|
||||
|
||||
```
|
||||
@reboot <command-to-execute>
|
||||
|
||||
```
|
||||
|
||||
要删除当前用户的所有 cron 任务:
|
||||
|
||||
```
|
||||
$ crontab -r
|
||||
|
||||
```
|
||||
|
||||
还有一个名为 [** crontab.guru **] [4] 的专业网站,用于学习 cron 任务示例。这个网站提供了很多 cron 任务的例子。
|
||||
还有一个名为 [crontab.guru] [4] 的专业网站,用于学习 cron 任务示例。这个网站提供了很多 cron 任务的例子。
|
||||
|
||||
有关更多详细信息,请查看手册页。
|
||||
|
||||
```
|
||||
$ man crontab
|
||||
|
||||
```
|
||||
|
||||
那么,就是这样。到此为止,您应该对 cron 任务以及如何在世使用它们有了一个基本的了解。后续还会介绍更多的优秀工具。敬请关注!!
|
||||
@ -211,17 +215,17 @@ $ man crontab
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://www.ostechnix.com/a-beginners-guide-to-cron-任务s/
|
||||
via: https://www.ostechnix.com/a-beginners-guide-to-cron-jobs/
|
||||
|
||||
作者:[SK][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[译者KevinSJ](https://github.com/KevinSJ)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
译者:[KevinSJ](https://github.com/KevinSJ)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:https://www.ostechnix.com/author/sk/
|
||||
[1]:data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
|
||||
[2]:http://www.ostechnix.com/wp-content/uploads/2018/05/cron-任务-format-1.png
|
||||
[3]:http://www.ostechnix.com/wp-content/uploads/2018/05/cron-任务s-1.png
|
||||
[2]:http://www.ostechnix.com/wp-content/uploads/2018/05/cron-job-format-1.png
|
||||
[3]:http://www.ostechnix.com/wp-content/uploads/2018/05/cron-jobs-1.png
|
||||
[4]:https://crontab.guru/
|
@ -0,0 +1,108 @@
|
||||
在 Ubuntu Snap 应用商店上发现的加密货币 ‘恶意软件’ 是怎么回事?
|
||||
======
|
||||
|
||||
最近,有发现称一些 Ubuntu Snap 应用商店上的应用包含加密货币挖矿程序。Canonical 公司迅速下架了这些违规的应用,但是留下了几个有待回答的问题。
|
||||
|
||||
### 在 Snap 应用商店上发现了加密矿工
|
||||
|
||||
![Crypto Miner Malware on Ubuntu Snap Store][1]
|
||||
|
||||
5 月 11 号,一位名叫 [tarwirdur][2] 的用户在 [snapcraft.io repository][3] 开了一个新的工单 ,他提到一个由 Nicolas Tomb 开发,叫做 2048buntu 的 snap 应用包含加密货币矿工。tarwirdur 询问,他怎样才能出于安全的原因而“投诉该应用” 。tarwirdur 后来发表说其它由 Nicolas Tomb 开发的 snap 应用也都包含加密货币矿工。
|
||||
|
||||
看起来该 snap 应用使用了 systemd 在系统启动时自动地运行代码,并在用户不知情的情况下在后台运行。
|
||||
|
||||
> 对那些不熟悉相关术语的人来说,<ruby>加密货币<rt>cryptocurrency</rt></ruby><ruby>矿工<rt>miner</rt></ruby>是一段使用计算机的主处理器或者图形处理器来“挖掘”数字货币的程序。“挖矿”通常涉及到解决一个数学等式。在这种情况下,如果你在运行 2048buntu 游戏,这个游戏将会额外使用处理器的计算能力去进行加密货币的挖掘。
|
||||
|
||||
Snapcraft 团队迅速地下架了所有由该违规者开发的应用来做出回应。他们同时也开展了调查。
|
||||
|
||||
### 隐匿者发声
|
||||
|
||||
5 月 13 号,一位同名为 Nicolas Tomb 的 Disqus 用户在 OMGUbuntu 的新闻报道上发表了[评论][4],他在评论中称自己向 snap 应用中添加了加密货币矿工,从而获取收益。他为他的行为道歉,同时承诺将所有挖矿所得的收益送给 Ubuntu 基金会。
|
||||
|
||||
我们不能确认这个评论就是由 Nicolas Tomb 发表的,因为这个 Disqus 账户最近才被创建,也只有一条评论与之相关联。现在,我们假设他是。
|
||||
|
||||
### Canonical 公司发表了声明
|
||||
|
||||
5 月 15 号,Canonical 公司在这种情况下发表了一份声明。标题为 [“在 Snap 应用商店中的信任与安全”][5],声明开头重申了当下的情况。他们也补充道[重新发布的 snap 应用中已经被删除了加密货币挖矿程序][6]。
|
||||
|
||||
Canonical 公司随后尝试调查 Nicolas Tomb 的动机。他们指出,他告诉他们说自己这样做是为了通过应用赚钱(如上所诉),而当面对质疑时就停止了。他们也指出“挖掘加密货币本身并非不合法和不道德的”。然而,他们仍对他没有在 snap 应用描述中披露加密矿工这件事表示了不满意。
|
||||
|
||||
随后 Canonical 公司将话题转移到审核软件上。根据这份申明,Snap 应用商店将会采用一种类似 iOS、Android、Windows 的质量控制系统,这个系统将有“自动化的检查点,安装包必须在被接受前通过检查,同时在特殊问题被标记时会进行人工审核”。
|
||||
|
||||
然后,Canonical 公司声称“对大规模的软件仓库来说,只接受每个单独文件都被仔细审核过的软件是不可能的”。因此,他们需要信任软件源而不是内容。毕竟,软件源是现在 Ubuntu 软件仓库系统的基础。
|
||||
|
||||
Canonical 公司紧接着谈到了 Snap 应用的未来。他们承认现在的系统是不完美的。他们也在不断地进行改善。他们“在开发非常有趣的安全功能,这些功能将会在改善系统安全性同时提升人们在服务器或桌面上进行软件开发的体验”。
|
||||
|
||||
其中一个他们正在开发的功能是查看一个软件发布者是否已通过验证。其他的改进包括:“将所有 AppArmor 内核补丁递交到上游”和其它底层修复。
|
||||
|
||||
### 一些关于“Snap 应用商店中的恶意软件”的想法
|
||||
|
||||
基于我所了解的所有内容,我产生了一些想法和问题。
|
||||
|
||||
#### 这种挖矿软件运行多久了?
|
||||
|
||||
首先,这些挖矿软件存在于 Snap 应用商店多久了?因为它们已经被下架了,我们没有这样的数据。我可以通过 Google 快照抓取一些 2048buntu 页面的图片,但这没有提供任何可用的信息。根据该软件运行时间,多少系统安装过,挖掘出了什么加密货币,我们能否知道违规者获取的是一点钱还是一笔钱。一个更长远的问题是:Canonical 公司将来有能力捕捉到这样的违规情况吗?
|
||||
|
||||
#### 这真的是一个恶意软件吗?
|
||||
|
||||
许多新闻网站将之报道为恶意软件感染。我想我甚至可以看到这个事件被称为 Linux 的第一个恶意软件。我不确定这个术语是否精确。Dictionary.com 这样定义 [恶意软件][7]:“意图损害计算机、移动设备、计算机系统或者计算机网络,或者对其运作进行部分控制的软件”。
|
||||
|
||||
这个有问题的 snap 应用并没有损害或者控制涉及到的计算机。它同样没有感染其他计算机。它也不能这样做,因为所有的 snap 应用位于沙盒之中。它们最多利用了处理器的计算能力,就是这样。所以,我不会称之为恶意软件。
|
||||
|
||||
#### 无孔不入
|
||||
|
||||
Nicolas Tomb 使用的一个辩解是在他上传应用的时候 Snap 应用商店没有任何反对加密货币挖矿的规则。(我敢向你打赌他们正在纠正这个错误。)他们之所以没有这样的规则,原因很简单,之前没有人做过这种事。如果 Tomb 想正确地做事,他应该提前询问是否允许这种行为。而事实是他似乎没有指出他知道 Canonical 公司可能会拒绝的事实。至少,Canonical 公司会告诉他将这些写在软件的描述中。
|
||||
|
||||
#### 一看就不对劲
|
||||
|
||||
![][8]
|
||||
|
||||
如我之前说的,我从 Google 快照获取了一个 2048buntu 的页面截图。仅仅看它就会感觉到一些危险的信号。首先,截图中几乎没有真实的描述。它是这样描述的“类似 2048 的游戏。这个游戏用 ubuntu 主题克隆了流行的游戏 2048。”哇,这将会引来容易上当受骗的人。当我读到类似空洞的描述时,我会多考虑下。
|
||||
|
||||
我注意到的另一件事是软件的大小。2048buntu 的 1.0 版本大小将近 140 MB。一个简单的游戏怎么需要这么多的空间?有用 Javascript 写的浏览器版本大概只用了不到它的四分之一。其他 snap 应用商店的 2048 游戏的大小没有一个达到了这个软件的一半。
|
||||
|
||||
然后,它有个许可证。这是一个使用了 Ubuntu 主题的流行游戏的克隆。它怎么能被认为是专有软件?我确信,其他合法的开发者会因为该内容而使用了 FOSS (自由开源软件)许可证来上传它。
|
||||
|
||||
单是这些因素就使得这个 snap 应用很特殊,应该进行审核。
|
||||
|
||||
#### 谁是 Nicolas Tomb?
|
||||
|
||||
当第一次了解到这些之后,我决定看看我能否找出造成这些混乱的人。当我搜索 Nicolas Tomb 的时候,我什么都没找到。所有我找到的只是一大堆关于加密货币挖矿 snap 应用的新闻和文章,以及去 tomb of St. Nicolas 旅游的信息。在 Twiter 和 Github 上都没有 Nicolas Tomb 的标志。看起来似乎是为了上传这些 snap 应用才创建的名称。
|
||||
|
||||
这同样引出了 Canonical 公司发表的申明中的一点,关于验证发布者。上一次我查看的时候,相当多的 snap 应用不是由应用的维护者发布的。这让我感到担忧。我更乐意相信由 Mozilla 基金会发布的 firefox 的 snap 应用,而不是 Leonard Borsch。如果对应用维护者来说关注应用的 snap 版本太耗费精力,应该有办法让维护者在他们软件的 snap 版本上贴上批准的标签。就像是 Firefox 的 snap 版本由 Fredrick 发布,经 Mozilla 基金会批准。这样才能让用户对下载的内容更放心。
|
||||
|
||||
#### 无疑 Snap 应用商店还有改善的空间
|
||||
|
||||
在我看来,Snap 应用商店团队应该实现的第一个特性是报告可疑应用的方式。tarwirdur 必须找到该网站的 Github 页面才行。而大多数用户不会想到这一点。如果 Snap 应用商店不能审核每一行代码,那么使用户能够报告问题是退而求其次的办法。即使评分系统也是一个不差的补充。我确信一定有部分人因为 2048buntu 使用了太多系统资源而给它很低的评分。
|
||||
|
||||
### 结论
|
||||
|
||||
从我所知道的情况来说,我认为这是某个人创建了一些简单的应用,在每个应用中嵌入了加密货币矿工,之后将这些应用上传到 Snap 应用商店,想着捞一笔钱。一旦他们被抓了,他们就声称这仅仅为了通过应用程序获利。如果真的是这样,他们应该在 snap 应用的描述中提到才对。隐藏加密矿工并不是什么[新鲜事][9]。他们通常是一种盗取计算能力的方法。
|
||||
|
||||
我希望 Canonical 公司已经具备了解决这个问题的功能,盼望这些功能能很快出来。
|
||||
|
||||
你对 Snap 应用商店的“恶意软件风波”有什么看法?你将如何改善这种情况?请在下面的评论中告诉我们。
|
||||
|
||||
如果你觉得这篇文章有趣,请花费一点时间将它分享到社交媒体上。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/snapstore-cryptocurrency-saga/
|
||||
|
||||
作者:[John Paul][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[paperzhang](https://github.com/paperzhang)
|
||||
校对:[wxy](https://github.com/wxy)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/john/
|
||||
[1]:https://4bds6hergc-flywheel.netdna-ssl.com/wp-content/uploads/2018/05/ubuntu-snap-malware-800x450.jpeg
|
||||
[2]:https://github.com/tarwirdur
|
||||
[3]:https://github.com/canonical-websites/snapcraft.io/issues/651
|
||||
[4]:https://disqus.com/home/discussion/omgubuntu/malware_found_on_the_ubuntu_snap_store/#comment-3899153046
|
||||
[5]:https://blog.ubuntu.com/2018/05/15/trust-and-security-in-the-snap-store
|
||||
[6]:https://forum.snapcraft.io/t/action-against-snap-store-malware/5417/8
|
||||
[7]:http://www.dictionary.com/browse/malware?s=t
|
||||
[8]:https://4bds6hergc-flywheel.netdna-ssl.com/wp-content/uploads/2018/05/2048buntu.png
|
||||
[9]:https://krebsonsecurity.com/2018/03/who-and-what-is-coinhive/
|
@ -1,3 +1,5 @@
|
||||
translating---geekpi
|
||||
|
||||
/dev/[u]random: entropy explained
|
||||
======
|
||||
### Entropy
|
||||
|
@ -1,4 +1,4 @@
|
||||
translating by kennethXia
|
||||
translating by
|
||||
|
||||
Getting started with Jenkins Pipelines
|
||||
======
|
||||
|
@ -1,430 +0,0 @@
|
||||
Translating by qhwdw
|
||||
Some Common Concurrent Programming Mistakes
|
||||
============================================================
|
||||
|
||||
Go is a language supporting built-in concurrent programming. By using the `go` keyword to create goroutines (light weight threads) and by [using][8] [channels][9] and [other concurrency][10] [synchronization techniques][11] provided in Go, concurrent programming becomes easy, flexible and enjoyable.
|
||||
|
||||
One the other hand, Go doesn't prevent Go programmers from making some concurrent programming mistakes which are caused by either carelessnesses or lacking of experiences. The remaining of the current article will show some common mistakes in Go concurrent programming, to help Go programmers avoid making such mistakes.
|
||||
|
||||
### No Synchronizations When Synchronizations Are Needed
|
||||
|
||||
Code lines may be [not executed by the appearance orders][2].
|
||||
|
||||
There are two mistakes in the following program.
|
||||
|
||||
* First, the read of `b` in the main goroutine and the write of `b` in the new goroutine might cause data races.
|
||||
|
||||
* Second, the condition `b == true` can't ensure that `a != nil` in the main goroutine. Compilers and CPUs may make optimizations by [reordering instructions][1] in the new goroutine, so the assignment of `b`may happen before the assignment of `a` at run time, which makes that slice `a` is still `nil` when the elements of `a` are modified in the main goroutine.
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var a []int // nil
|
||||
var b bool // false
|
||||
|
||||
// a new goroutine
|
||||
go func () {
|
||||
a = make([]int, 3)
|
||||
b = true // write b
|
||||
}()
|
||||
|
||||
for !b { // read b
|
||||
time.Sleep(time.Second)
|
||||
runtime.Gosched()
|
||||
}
|
||||
a[0], a[1], a[2] = 0, 1, 2 // might panic
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The above program may run well on one computer, but may panic on another one. Or it may run well for _N_ times, but may panic at the _(N+1)_ th time.
|
||||
|
||||
We should use channels or the synchronization techniques provided in the `sync` standard package to ensure the memory orders. For example,
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var a []int = nil
|
||||
c := make(chan struct{})
|
||||
|
||||
// a new goroutine
|
||||
go func () {
|
||||
a = make([]int, 3)
|
||||
c <- struct{}{}
|
||||
}()
|
||||
|
||||
<-c
|
||||
a[0], a[1], a[2] = 0, 1, 2
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Use `time.Sleep` Calls To Do Synchronizations
|
||||
|
||||
Let's view a simple example.
|
||||
|
||||
```
|
||||
ppackage main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var x = 123
|
||||
|
||||
go func() {
|
||||
x = 789 // write x
|
||||
}()
|
||||
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println(x) // read x
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
We expect the program to print `789`. If we run it, it really prints `789`, almost always. But is it a program with good syncrhonization? No! The reason is Go runtime doesn't guarantee the write of `x` happens before the read of `x` for sure. Under certain conditions, such as most CPU resources are cunsumed by other programs running on same OS, the write of `x` might happen after the read of `x`. This is why we should never use `time.Sleep` calls to do syncrhonizations in formal projects.
|
||||
|
||||
Let's view another example.
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var x = 0
|
||||
|
||||
func main() {
|
||||
var num = 123
|
||||
var p = &num
|
||||
|
||||
c := make(chan int)
|
||||
|
||||
go func() {
|
||||
c <- *p + x
|
||||
}()
|
||||
|
||||
time.Sleep(time.Second)
|
||||
num = 789
|
||||
fmt.Println(<-c)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
What do you expect the program will output? `123`, or `789`? In fact, the output is compiler dependent. For the standard Go compiler 1.10, it is very possible the program will output `123`. But in theory, it might output `789`, or another random number.
|
||||
|
||||
Now, let's change `c <- *p + x` to `c <- *p` and run the program again. You will find the output becomes to `789` (for the he standard Go compiler 1.10). Again, the output is compiler dependent.
|
||||
|
||||
Yes, there are data races in the above program. The expression `*p` might be evaluated before, after, or when the assignment `num = 789` is processed. The `time.Sleep` call can't guarantee the evaluation of `*p`happens before the assignment is processed.
|
||||
|
||||
For this specified example, we should store the value to be sent in a temporary value before creating the new goroutine and send the temporary value instead in the new goroutine to remove the data races.
|
||||
|
||||
```
|
||||
...
|
||||
tmp := *p + x
|
||||
go func() {
|
||||
c <- tmp
|
||||
}()
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
### Leave Goroutines Hanging
|
||||
|
||||
Hanging goroutines are the goroutines staying in blocking state for ever. There are many reasons leading goroutines into hanging. For example,
|
||||
|
||||
* a goroutine tries to receive a value from a nil channel or from a channel which no more other goroutines will send values to.
|
||||
|
||||
* a goroutine tries to send a value to nil channel or to a channel which no more other goroutines will receive values from.
|
||||
|
||||
* a goroutine is dead locked by itself.
|
||||
|
||||
* a group of goroutines are dead locked by each other.
|
||||
|
||||
* a goroutine is blocked when executing a `select` code block without `default` branch, and all the channel operations following the `case` keywords in the `select` code block keep blocking for ever.
|
||||
|
||||
Except sometimes we deliberately let the main goroutine in a program hanging to avoid the program exiting, most other hanging goroutine cases are unexpected. It is hard for Go runtime to judge whether or not a goroutine in blocking state is hanging or stays in blocking state temporarily. So Go runtime will never release the resources consumed by a hanging goroutine.
|
||||
|
||||
In the [first-response-wins][12] channel use case, if the capacity of the channel which is used a future is not large enough, some slower response goroutines will hang when trying to send a result to the future channel. For example, if the following function is called, there will be 4 goroutines stay in blocking state for ever.
|
||||
|
||||
```
|
||||
func request() int {
|
||||
c := make(chan int)
|
||||
for i := 0; i < 5; i++ {
|
||||
i := i
|
||||
go func() {
|
||||
c <- i // 4 goroutines will hang here.
|
||||
}()
|
||||
}
|
||||
return <-c
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
To avoid the four goroutines hanging, the capacity of channel `c` must be at least `4`.
|
||||
|
||||
In [the second way to implement the first-response-wins][13] channel use case, if the channel which is used as a future is an unbufferd channel, it is possible that the channel reveiver will never get a response and hang. For example, if the following function is called in a goroutine, the goroutine might hang. The reason is, if the five try-send operations all happen before the receive operation `<-c` is ready, then all the five try-send operations will fail to send values so that the caller goroutine will never receive a value.
|
||||
|
||||
```
|
||||
func request() int {
|
||||
c := make(chan int)
|
||||
for i := 0; i < 5; i++ {
|
||||
i := i
|
||||
go func() {
|
||||
select {
|
||||
case c <- i:
|
||||
default:
|
||||
}
|
||||
}()
|
||||
}
|
||||
return <-c
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Changing the channel `c` as a buffered channel will guarantee at least one of the five try-send operations succeed so that the caller goroutine will never hang in the above function.
|
||||
|
||||
### Copy Values Of The Types In The `sync` Standard Package
|
||||
|
||||
In practice, values of the types in the `sync` standard package shouldn't be copied. We should only copy pointers of such values.
|
||||
|
||||
The following is bad concurrent programming example. In this example, when the `Counter.Value` method is called, a `Counter` receiver value will be copied. As a field of the receiver value, the respective `Mutex` field of the `Counter` receiver value will also be copied. The copy is not synchronized, so the copied `Mutex` value might be corrupt. Even if it is not corrupt, what it protects is the accessment of the copied `Counter` receiver value, which is meaningless generally.
|
||||
|
||||
```
|
||||
import "sync"
|
||||
|
||||
type Counter struct {
|
||||
sync.Mutex
|
||||
n int64
|
||||
}
|
||||
|
||||
// This method is okay.
|
||||
func (c *Counter) Increase(d int64) (r int64) {
|
||||
c.Lock()
|
||||
c.n += d
|
||||
r = c.n
|
||||
c.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// The method is bad. When it is called, a Counter
|
||||
// receiver value will be copied.
|
||||
func (c Counter) Value() (r int64) {
|
||||
c.Lock()
|
||||
r = c.n
|
||||
c.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
We should change the reveiver type of the `Value` method to the poiner type `*Counter` to avoid copying `Mutex` values.
|
||||
|
||||
The `go vet` command provided in the official Go SDK will report potential bad value copies.
|
||||
|
||||
### Call Methods Of `sync.WaitGroup` At Wrong Places
|
||||
|
||||
Each `sync.WaitGroup` value maintains a counter internally, The initial value of the counter is zero. If the counter of a `WaitGroup` value is zero, a call to the `Wait` method of the `WaitGroup` value will not block, otherwise, the call blocks until the counter value becomes zero.
|
||||
|
||||
To make the uses of `WaitGroup` value meaningful, when the counter of a `WaitGroup` value is zero, a call to the `Add` method of the `WaitGroup` value must happen before the corresponding call to the `Wait` method of the `WaitGroup` value.
|
||||
|
||||
For example, in the following program, the `Add` method is called at an improper place, which makes that the final printed number is not always `100`. In fact, the final printed number of the program may be an arbitrary number in the range `[0, 100)`. The reason is none of the `Add` method calls are guaranteed to happen before the `Wait` method call.
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var wg sync.WaitGroup
|
||||
var x int32 = 0
|
||||
for i := 0; i < 100; i++ {
|
||||
go func() {
|
||||
wg.Add(1)
|
||||
atomic.AddInt32(&x, 1)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
fmt.Println("To wait ...")
|
||||
wg.Wait()
|
||||
fmt.Println(atomic.LoadInt32(&x))
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
To make the program behave as expected, we should move the `Add` method calls out of the new goroutines created in the `for` loop, as the following code shown.
|
||||
|
||||
```
|
||||
...
|
||||
for i := 0; i < 100; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
atomic.AddInt32(&x, 1)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
### Use Channels As Futures Improperly
|
||||
|
||||
From the article [channel use cases][14], we know that some functions will return [channels as futures][15]. Assume `fa` and `fb` are two such functions, then the following call uses future arguments improperly.
|
||||
|
||||
```
|
||||
doSomethingWithFutureArguments(<-fa(), <-fb())
|
||||
|
||||
```
|
||||
|
||||
In the above code line, the two channel receive operations are processed in sequentially, instead of concurrently. We should modify it as the following to process them concurrently.
|
||||
|
||||
```
|
||||
ca, cb := fa(), fb()
|
||||
doSomethingWithFutureArguments(<-c1, <-c2)
|
||||
|
||||
```
|
||||
|
||||
### Close Channels Not From The Last Active Sender Goroutine
|
||||
|
||||
A common mistake made by Go programmers is closing a channel when there are still some other goroutines will potentially send values to the channel later. When such a potential send (to the closed channel) really happens, a panic will occur.
|
||||
|
||||
This mistake was ever made in some famous Go projects, such as [this bug][3] and [this bug][4] in the kubernetes project.
|
||||
|
||||
Please read [this article][5] for explanations on how to safely and gracefully close channels.
|
||||
|
||||
### Do 64-bit Atomic Operations On Values Which Are Not Guaranteed To Be 64-bit Aligned
|
||||
|
||||
Up to now (Go 1.10), for the standard Go compiler, the address of the value involved in a 64-bit atomic operation is required to be 64-bit aligned. Failure to do so may make the current goroutine panic. For the standard Go compiler, such failure can only happen on 32-bit architectures. Please read [memory layouts][6] to get how to guarantee the addresses of 64-bit word 64-bit aligned on 32-bit OSes.
|
||||
|
||||
### Not Pay Attention To Too Many Resources Are Consumed By Calls To The `time.After` Function
|
||||
|
||||
The `After` function in the `time` standard package returns [a channel for delay notification][7]. The function is convenient, however each of its calls will create a new value of the `time.Timer` type. The new created `Timer` value will keep alive in the duration specified by the passed argument to the `After` function. If the function is called many times in the duration, there will be many `Timer` values alive and consuming much memory and computation.
|
||||
|
||||
For example, if the following `longRunning` function is called and there are millions of messages coming in one minute, then there will be millions of `Timer` values alive in a certain period, even if most of these `Timer`values have already become useless.
|
||||
|
||||
```
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The function will return if a message arrival interval
|
||||
// is larger than one minute.
|
||||
func longRunning(messages <-chan string) {
|
||||
for {
|
||||
select {
|
||||
case <-time.After(time.Minute):
|
||||
return
|
||||
case msg := <-messages:
|
||||
fmt.Println(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
To avoid too many `Timer` values being created in the above code, we should use a single `Timer` value to do the same job.
|
||||
|
||||
```
|
||||
func longRunning(messages <-chan string) {
|
||||
timer := time.NewTimer(time.Minute)
|
||||
defer timer.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
return
|
||||
case msg := <-messages:
|
||||
fmt.Println(msg)
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
}
|
||||
|
||||
// The above "if" block can also be put here.
|
||||
|
||||
timer.Reset(time.Minute)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Use `time.Timer` Values Incorrectly
|
||||
|
||||
An idiomatic use example of a `time.Timer` value has been shown in the last section. One detail which should be noted is that the `Reset` method should always be invoked on stopped or expired `time.Timer`values.
|
||||
|
||||
At the end of the first `case` branch of the `select` block, the `time.Timer` value has expired, so we don't need to stop it. But we must stop the timer in the second branch. If the `if` code block in the second branch is missing, it is possible that a send (by the Go runtime) to the channel `timer.C` races with the `Reset`method call, and it is possible that the `longRunning` function returns earlier than expected, for the `Reset`method will only reset the internal timer to zero, it will not clear (drain) the value which has been sent to the `timer.C` channel.
|
||||
|
||||
For example, the following program is very possible to exit in about one second, instead of ten seconds. and more importantly, the program is not data race free.
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
timer := time.NewTimer(time.Second/2)
|
||||
select {
|
||||
case <-timer.C:
|
||||
default:
|
||||
time.Sleep(time.Second) // go here
|
||||
}
|
||||
timer.Reset(time.Second * 10)
|
||||
<-timer.C
|
||||
fmt.Println(time.Since(start)) // 1.000188181s
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
A `time.Timer` value can be leaved in non-stopping status when it is not used any more, but it is recommended to stop it in the end.
|
||||
|
||||
It is bug prone and not recommended to use a `time.Timer` value concurrently in multiple goroutines.
|
||||
|
||||
We should not rely on the return value of a `Reset` method call. The return result of the `Reset` method exists just for compatibility purpose.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://go101.org/article/concurrent-common-mistakes.html
|
||||
|
||||
作者:[go101.org ][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:go101.org
|
||||
[1]:https://go101.org/article/memory-model.html
|
||||
[2]:https://go101.org/article/memory-model.html
|
||||
[3]:https://github.com/kubernetes/kubernetes/pull/45291/files?diff=split
|
||||
[4]:https://github.com/kubernetes/kubernetes/pull/39479/files?diff=split
|
||||
[5]:https://go101.org/article/channel-closing.html
|
||||
[6]:https://go101.org/article/memory-layout.html
|
||||
[7]:https://go101.org/article/channel-use-cases.html#timer
|
||||
[8]:https://go101.org/article/channel-use-cases.html
|
||||
[9]:https://go101.org/article/channel.html
|
||||
[10]:https://go101.org/article/concurrent-atomic-operation.html
|
||||
[11]:https://go101.org/article/concurrent-synchronization-more.html
|
||||
[12]:https://go101.org/article/channel-use-cases.html#first-response-wins
|
||||
[13]:https://go101.org/article/channel-use-cases.html#first-response-wins-2
|
||||
[14]:https://go101.org/article/channel-use-cases.html
|
||||
[15]:https://go101.org/article/channel-use-cases.html#future-promise
|
@ -1,3 +1,5 @@
|
||||
translating---geekpi
|
||||
|
||||
Audacity quick tip: quickly remove background noise
|
||||
======
|
||||
|
||||
|
@ -1,82 +0,0 @@
|
||||
translating---geekpi
|
||||
|
||||
Starting user software in X
|
||||
======
|
||||
|
||||
There are currently many ways of starting software when a user session starts.
|
||||
|
||||
This is an attempt to collect a list of pointers to piece the big picture together. It's partial and some parts might be imprecise or incorrect, but it's a start, and I'm happy to keep it updated if I receive corrections.
|
||||
|
||||
### x11-common
|
||||
|
||||
`man xsession`
|
||||
|
||||
* Started by the display manager for example, `/usr/share/lightdm/lightdm.conf.d/01_debian.conf` or `/etc/gdm3/Xsession`
|
||||
* Debian specific
|
||||
* Runs scripts in `/etc/X11/Xsession.d/`
|
||||
* `/etc/X11/Xsession.d/40x11-common_xsessionrc` sources `~/.xsessionrc` which can do little more than set env vars, because it is run at the beginning of X session startup
|
||||
* At the end, it starts the session manager (`gnome-session`, `xfce4-session`, and so on)
|
||||
|
||||
|
||||
|
||||
### systemd --user
|
||||
|
||||
* <https://wiki.archlinux.org/index.php/Systemd/User>
|
||||
* Started by `pam_systemd`, so it might not have a DISPLAY variable set in the environment yet
|
||||
* Manages units in:
|
||||
* `/usr/lib/systemd/user/` where units provided by installed packages belong.
|
||||
* `~/.local/share/systemd/user/` where units of packages that have been installed in the home directory belong.
|
||||
* `/etc/systemd/user/` where system-wide user units are placed by the system administrator.
|
||||
* `~/.config/systemd/user/` where the users put their own units.
|
||||
* A trick to start a systemd user unit when the X session has been set up and the DISPLAY variable is available, is to call `systemctl start` from a `.desktop` autostart file.
|
||||
|
||||
|
||||
|
||||
### dbus activation
|
||||
|
||||
* <https://dbus.freedesktop.org/doc/system-activation.txt>
|
||||
* A user process making a dbus request can trigger starting a server program
|
||||
* For systems debugging, is there a way of monitoring what services are getting dbus activated?
|
||||
|
||||
|
||||
|
||||
### X session manager
|
||||
|
||||
* <https://en.wikipedia.org/wiki/X_session_manager>
|
||||
* Run by `x11-common`'s `Xsession.d`
|
||||
* Runs freedesktop autostart .desktop files
|
||||
* Runs Desktop Environment specific software
|
||||
|
||||
|
||||
|
||||
### xdg autostart
|
||||
|
||||
* <https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html>
|
||||
* Run by the session manager
|
||||
* If `/etc/xdg/autostart/foo.desktop` and `~/.config/autostart/foo.desktop` exist then only the file `~/.config/autostart/foo.desktop` will be used because `~/.config/autostart/` is more important than `/etc/xdg/autostart/`
|
||||
* Is there an ordering or is it all in parallel?
|
||||
|
||||
|
||||
|
||||
### Other startup notes
|
||||
|
||||
#### ~/.Xauthority
|
||||
|
||||
To connect to an X server, a client needs to send a token from `~/.Xauthority`, which proves that they can read the user's provate data.
|
||||
|
||||
`~/.Xauthority` contains a token generated by display manager and communicated to X at startup.
|
||||
|
||||
To view its contents, use `xauth -i -f ~/.Xauthority list`
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: http://www.enricozini.org/blog/2018/debian/starting-user-software/
|
||||
|
||||
作者:[Enrico Zini][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:http://www.enricozini.org/
|
@ -1,3 +1,4 @@
|
||||
translating by wyxplus
|
||||
How to Run Your Own Git Server
|
||||
======
|
||||
**Learn how to set up your own Git server in this tutorial from our archives.**
|
||||
|
@ -1,58 +1,57 @@
|
||||
Translating by qhwdw
|
||||
How To Register The Oracle Linux System With The Unbreakable Linux Network (ULN)
|
||||
Oracle Linux 系统如何去注册使用坚不可摧 Linux 网络(ULN)
|
||||
======
|
||||
Most of us knows about RHEL subscription but only few of them knows about Oracle subscription and its details.
|
||||
大多数人都知道 RHEL 的订阅 ,但是知道 Oracle 订阅及细节的人却很少。
|
||||
|
||||
Even i don’t know the information about this and i recently came to know about this and wants to share this with others so, i’m going to write a article which will guide you to register the Oracle Linux system with the Unbreakable Linux Network (ULN).
|
||||
甚至我也不知道关于它的信息,我是最近才了解了有关它的信息,想将这些内容共享给其他人。因此写了这篇文章,它将指导你去注册 Oracle Linux 系统去使用坚不可摧 Linux 网络(ULN) 。
|
||||
|
||||
This allows the register systems to get software update and other patches ASAP.
|
||||
这将允许你去注册系统以获得软件更新和其它的 ASAP 补丁。
|
||||
|
||||
### What Is Unbreakable Linux Network
|
||||
### 什么是坚不可摧 Linux 网络
|
||||
|
||||
ULN stands for Unbreakable Linux Network which is owned by Oracle. If you have a active subscription to Oracle OS Support, you can register your system with Unbreakable Linux Network (ULN).
|
||||
ULN 代表坚不可摧 Linux 网络,它是由 Oracle 所拥有的。如果你去 Oracle OS 支持中去激活这个订阅,你就可以注册你的系统去使用坚不可摧 Linux 网络(ULN)。
|
||||
|
||||
ULN offers software patches, updates, and fixes for Oracle Linux and Oracle VM, as well as information on yum, Ksplice, and support policies. You can also download useful packages that are not included in the original distribution.
|
||||
ULN 为 Oracle Linux 和 Oracle VM 提供软件补丁、更新、以及修复,此外还有在 yum、Ksplice、以及支持策略上的信息。你也可以通过它来下载原始发行版中没有包含的有用的安装包。
|
||||
|
||||
The ULN Alert Notification Tool periodically checks with ULN and alerts you when updates are available.
|
||||
ULN 的告警提示工具周期性地使用 ULN 去检查,当有更新的时候它给你发送警报信息。
|
||||
|
||||
If you want to use ULN repository with yum to manage your systems, make sure your system should registered with ULN and subscribe each system to one or more ULN channels. When you register a system with ULN, automatically it will choose latest version of channel based on the system architecture and OS.
|
||||
如果你想在 yum 上使用 ULN 仓库去管理你的系统,需要确保你的系统已经注册到 ULN 上,并且订阅了一个或多个 ULN 频道。当你注册一个系统使用 ULN,它将基于你的系统架构和操作系统去自动选择频道中最新的版本。
|
||||
|
||||
### How To Register As A ULN User
|
||||
### 如何注册为一个 ULN 用户
|
||||
|
||||
To register as a ULN user, make sure you have a valid customer support identifier (CSI) for Oracle Linux support or Oracle VM support.
|
||||
去注册为一个 ULN 用户,需要你有一个 Oracle Linux 支持或者 Oracle VM 支持的有效客户支持代码(CSI)。
|
||||
|
||||
Follow the below steps to register as a ULN user.
|
||||
请按以下步骤去注册为一个 ULN 用户。
|
||||
|
||||
Visit @ [linux.oracle.com][1]
|
||||
请访问 [linux.oracle.com][1]
|
||||
![][3]
|
||||
|
||||
If you already have an SSO account, click Sign On.
|
||||
如果你已经有一个 SSO 帐户,请点击 “Sign On”。
|
||||
![][4]
|
||||
|
||||
If you don’t have a account, click Create New Single Signon Account and follow the onscreen instructions to create one.
|
||||
如果你没有帐户,点击 “Create New Single Signon Account” 然后按屏幕上的要求去创建一个帐户。
|
||||
![][5]
|
||||
|
||||
Verify your email address to complete your account setup.
|
||||
验证你的电子邮件地址以完成帐户设置。
|
||||
|
||||
Log in using your SSO user name and password. On the Create New ULN User page, enter your CSI and click Create New User.
|
||||
使用你的 SSO 帐户的用户名和密码去登入。在 “Create New ULN User” 页面上,输入你的 CSI 然后点击 “Create New User”。
|
||||
![][6]
|
||||
|
||||
**Note :**
|
||||
**注意:**
|
||||
|
||||
* If no administrator is currently assigned to manage the CSI, you are prompted to click Confirm to become the CSI administrator.
|
||||
* If your user name already exists on the system, you are prompted to proceed to ULN by clicking the link Unbreakable Linux Network.
|
||||
* 如果当前没有分配管理员去管理 CSI,将会提示你去点击确认让你成为 CSI 管理员。
|
||||
* 如果你的用户名已经在系统上存在,你将被提示通过点击坚不可摧 Linux 网络的链接去操作 ULN。
|
||||
|
||||
|
||||
|
||||
### How To Register The Oracle Linux 6/7 System With ULN
|
||||
### 如何注册 Oracle Linux 6/7 系统使用 ULN
|
||||
|
||||
Just run the below command and follow the instruction to register the system.
|
||||
只需要运行下列的命令,并按随后的指令提示去注册系统。
|
||||
```
|
||||
# uln_register
|
||||
|
||||
```
|
||||
|
||||
Make sure your system is having active internet connection. Also keep ready your Oracle Single Sign-On Login & password (SSO) details then hit `Next`.
|
||||
确保你的系统有一个活动的因特网连接。同时准备好你的 Oracle 单点登陆帐户(SSO)的用户名和密码,然后点击 `Next`。
|
||||
```
|
||||
Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
@ -78,7 +77,7 @@ Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
```
|
||||
|
||||
Input your login information for Unbreakable Linux Network, then hit `Next`.
|
||||
输入你的 ULN 登陆信息,然后点击 `Next`。
|
||||
```
|
||||
Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
@ -101,7 +100,7 @@ Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
```
|
||||
|
||||
Register a System Profile – Hardware information, then hit `Next`.
|
||||
注册一个系统概要 – 硬件信息,然后点击 `Next`。
|
||||
```
|
||||
Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
@ -130,7 +129,7 @@ Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
```
|
||||
|
||||
Register a System Profile – Packages configuration, then hit `Next`.
|
||||
注册一个系统概要 – 包配置,然后点击 `Next`。
|
||||
```
|
||||
Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
@ -168,7 +167,7 @@ Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
```
|
||||
|
||||
Press “Next” to send this System Profile to Unbreakable Linux Network.
|
||||
按下 “Next” 去发送系统概要到 ULN。
|
||||
```
|
||||
Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
@ -188,7 +187,7 @@ Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
```
|
||||
|
||||
Sending Profile to Unbreakable Linux Network is under processing.
|
||||
发送概要到 ULN 是如下的一个过程。
|
||||
```
|
||||
Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
@ -202,7 +201,7 @@ Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
```
|
||||
|
||||
ULN registration has been done and review system subscription details. If everything is fine, then hit `ok`.
|
||||
ULN 注册做完后,重新回顾系统订阅的详细情况。如果一切正确,然后点击 `ok`。
|
||||
```
|
||||
Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
@ -237,7 +236,7 @@ Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
```
|
||||
|
||||
Finally hit `Finish` to complete the registration.
|
||||
最后点击 `Finish` 完成注册。
|
||||
```
|
||||
Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
@ -256,7 +255,7 @@ Copyright © 2006--2010 Red Hat, Inc. All rights reserved.
|
||||
|
||||
```
|
||||
|
||||
ULN registration has been done successfully, in order to get repository from ULN run the following command.
|
||||
ULN 注册已经成功,为了从 ULN 中得到仓库,运行如下的命令。
|
||||
```
|
||||
# yum repolist
|
||||
Loaded plugins: aliases, changelog, presto, refresh-packagekit, rhnplugin, security, tmprepo, ulninfo, verify, versionlock
|
||||
@ -271,13 +270,13 @@ repolist: 40,966
|
||||
|
||||
```
|
||||
|
||||
Also, you can check the same in ULN website. Go to the `System` tab to view the list of registered systems.
|
||||
另外,你也可以在 ULN 网站上查看到相同的信息。转到 `System` 标签页去查看已注册的系统列表。
|
||||
![][7]
|
||||
|
||||
To view list of the enabled repositories. Go to the `System` tab then hit the corresponding system. Also, you can able to see the available updates for the system for errata.
|
||||
去查看已经启用的仓库列表。转到 `System` 标签页,然后点击相应的系统。另外,你也能够看到系统勘误及可用更新。
|
||||
![][8]
|
||||
|
||||
To manage subscription channel. Go to the `System` tab, then hit appropriate `system name` and finally hit `Manage Subscriptions`.
|
||||
去管理订阅的频道。转到 `System` 标签页,然后点击有关的 `system name`,最后点击 `Manage Subscriptions`。
|
||||
![][9]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -285,7 +284,7 @@ To manage subscription channel. Go to the `System` tab, then hit appropriate `sy
|
||||
via: https://www.2daygeek.com/how-to-register-the-oracle-linux-system-with-the-unbreakable-linux-network-uln/
|
||||
|
||||
作者:[Vinoth Kumar][a]
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
译者:[qhwdw](https://github.com/qhwdw)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
|
@ -0,0 +1,429 @@
|
||||
一些常见的并发编程错误
|
||||
============================================================
|
||||
|
||||
Go 是一个内置支持并发编程的语言。借助使用 `go` 关键字去创建 goroutines(轻量级线程)和在 Go 中提供的 [使用][8] [信道][9] 和 [其它的并发][10] [同步方法][11],使得并发编程变得很容易、很灵活和很有趣。
|
||||
|
||||
另一方面,Go 并不会阻止一些因 Go 程序员粗心大意或者缺乏经验而造成的并发编程错误。在本文的下面部分将展示一些在 Go 编程中常见的并发编程错误,以帮助 Go 程序员们避免再犯类似的错误。
|
||||
|
||||
### 需要同步的时候没有同步
|
||||
|
||||
代码行或许 [没有按出现的顺序运行][2]。
|
||||
|
||||
在下面的程序中有两个错误。
|
||||
|
||||
* 第一,在 main goroutine 中读取 `b` 和在新的 goroutine 中写入 `b` 可能导致数据争用。
|
||||
|
||||
* 第二,条件 `b == true` 并不能保证在 main goroutine 中的 `a != nil`。在新的 goroutine 中编译器和 CPU 可能会通过 [重排序指令][1] 进行优化,因此,在运行时 `b` 赋值可能发生在 `a` 赋值之前,在 main goroutine 中当 `a` 被修改后,它将会让部分 `a` 一直保持为 `nil`。
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var a []int // nil
|
||||
var b bool // false
|
||||
|
||||
// a new goroutine
|
||||
go func () {
|
||||
a = make([]int, 3)
|
||||
b = true // write b
|
||||
}()
|
||||
|
||||
for !b { // read b
|
||||
time.Sleep(time.Second)
|
||||
runtime.Gosched()
|
||||
}
|
||||
a[0], a[1], a[2] = 0, 1, 2 // might panic
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
上面的程序或者在一台计算机上运行的很好,但是在另一台上可能会引发异常。或者它可能运行了 _N_ 次都很好,但是可能在第 _(N+1)_ 次引发了异常。
|
||||
|
||||
我们将使用 `sync` 标准包中提供的信道或者同步方法去确保内存中的顺序。例如,
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var a []int = nil
|
||||
c := make(chan struct{})
|
||||
|
||||
// a new goroutine
|
||||
go func () {
|
||||
a = make([]int, 3)
|
||||
c <- struct{}{}
|
||||
}()
|
||||
|
||||
<-c
|
||||
a[0], a[1], a[2] = 0, 1, 2
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 使用 `time.Sleep` 调用去做同步
|
||||
|
||||
我们先来看一个简单的例子。
|
||||
|
||||
```
|
||||
ppackage main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var x = 123
|
||||
|
||||
go func() {
|
||||
x = 789 // write x
|
||||
}()
|
||||
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println(x) // read x
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
我们预期程序将打印出 `789`。如果我们运行它,通常情况下,它确定打印的是 `789`。但是,这个程序使用的同步方式好吗?No!原因是 Go 运行时并不保证 `x` 的写入一定会发生在 `x` 的读取之前。在某些条件下,比如在同一个操作系统上,大部分 CPU 资源被其它运行的程序所占用的情况下,写入 `x` 可能就会发生在读取 `x` 之后。这就是为什么我们在正式的项目中,从来不使用 `time.Sleep` 调用去实现同步的原因。
|
||||
|
||||
我们来看一下另外一个示例。
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var x = 0
|
||||
|
||||
func main() {
|
||||
var num = 123
|
||||
var p = &num
|
||||
|
||||
c := make(chan int)
|
||||
|
||||
go func() {
|
||||
c <- *p + x
|
||||
}()
|
||||
|
||||
time.Sleep(time.Second)
|
||||
num = 789
|
||||
fmt.Println(<-c)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
你认为程序的预期输出是什么?`123` 还是 `789`?事实上它的输出与编译器有关。对于标准的 Go 编译器 1.10 来说,这个程序很有可能输出是 `123`。但是在理论上,它可能输出的是 `789`,或者其它的随机数。
|
||||
|
||||
现在,我们来改变 `c <- *p + x` 为 `c <- *p`,然后再次运行这个程序。你将会发现输出变成了 `789` (使用标准的 Go 编译器 1.10)。这再次说明它的输出是与编译器相关的。
|
||||
|
||||
是的,在上面的程序中存在数据争用。表达式 `*p` 可能会被先计算、后计算、或者在处理赋值语句 `num = 789` 时计算。`time.Sleep` 调用并不能保证 `*p` 发生在赋值语句处理之前进行。
|
||||
|
||||
对于这个特定的示例,我们将在新的 goroutine 创建之前,将值保存到一个临时值中,然后在新的 goroutine 中使用临时值去消除数据争用。
|
||||
|
||||
```
|
||||
...
|
||||
tmp := *p + x
|
||||
go func() {
|
||||
c <- tmp
|
||||
}()
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
### 使 Goroutines 挂起
|
||||
|
||||
挂起 goroutines 是指让 goroutines 一直处于阻塞状态。导致 goroutines 被挂起的原因很多。比如,
|
||||
|
||||
* 一个 goroutine 尝试从一个 nil 信道中或者从一个没有其它 goroutines 给它发送值的信道中检索数据。
|
||||
|
||||
* 一个 goroutine 尝试去发送一个值到 nil 信道,或者发送到一个没有其它的 goroutines 接收值的信道中。
|
||||
|
||||
* 一个 goroutine 被它自己死锁。
|
||||
|
||||
* 一组 goroutines 彼此死锁。
|
||||
|
||||
* 当运行一个没有 `default` 分支的 `select` 代码块时,一个 goroutine 被阻塞,以及在 `select` 代码块中 `case` 关键字后的所有信道操作保持阻塞状态。
|
||||
|
||||
除了有时我们为了避免程序退出,特意让一个程序中的 main goroutine 保持挂起之外,大多数其它的 goroutine 挂起都是意外情况。Go 运行时很难判断一个 goroutine 到底是处于挂起状态还是临时阻塞。因此,Go 运行时并不会去释放一个挂起的 goroutine 所占用的资源。
|
||||
|
||||
在 [谁先响应谁获胜][12] 的信道使用案例中,如果使用的 Future 信道容量不够大,当尝试向 Future 信道发送结果时,一些响应较慢的信道将被挂起。比如,如果调用下面的函数,将有 4 个 goroutine 处于永远阻塞状态。
|
||||
|
||||
```
|
||||
func request() int {
|
||||
c := make(chan int)
|
||||
for i := 0; i < 5; i++ {
|
||||
i := i
|
||||
go func() {
|
||||
c <- i // 4 goroutines will hang here.
|
||||
}()
|
||||
}
|
||||
return <-c
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
为避免这 4 个 goroutines 一直处于挂起状态, `c` 信道的容量必须至少是 `4`。
|
||||
|
||||
在 [实现谁先响应谁获胜的第二种方法][13] 的信道使用案例中,如果将 future 信道用做非缓冲信道,那么有可能这个信息将永远也不会有响应并挂起。例如,如果在一个 goroutine 中调用下面的函数,goroutine 可能会挂起。原因是,如果接收操作 `<-c` 准备就绪之前,五个发送操作全部尝试发送,那么所有的尝试发送的操作将全部失败,因此那个调用者 goroutine 将永远也不会接收到值。
|
||||
|
||||
```
|
||||
func request() int {
|
||||
c := make(chan int)
|
||||
for i := 0; i < 5; i++ {
|
||||
i := i
|
||||
go func() {
|
||||
select {
|
||||
case c <- i:
|
||||
default:
|
||||
}
|
||||
}()
|
||||
}
|
||||
return <-c
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
将信道 `c` 变成缓冲信道将保证五个发送操作中的至少一个操作会发送成功,这样,上面函数中的那个调用者 goroutine 将不会被挂起。
|
||||
|
||||
### 在 `sync` 标准包中拷贝类型值
|
||||
|
||||
在实践中,`sync` 标准包中的类型值不会被拷贝。我们应该只拷贝这个值的指针。
|
||||
|
||||
下面是一个错误的并发编程示例。在这个示例中,当调用 `Counter.Value` 方法时,将拷贝一个 `Counter` 接收值。作为接收值的一个字段,`Counter` 接收值的各个 `Mutex` 字段也会被拷贝。拷贝不是同步发生的,因此,拷贝的 `Mutex` 值可能会出错。即便是没有错误,拷贝的 `Counter` 接收值的访问保护也是没有意义的。
|
||||
|
||||
```
|
||||
import "sync"
|
||||
|
||||
type Counter struct {
|
||||
sync.Mutex
|
||||
n int64
|
||||
}
|
||||
|
||||
// This method is okay.
|
||||
func (c *Counter) Increase(d int64) (r int64) {
|
||||
c.Lock()
|
||||
c.n += d
|
||||
r = c.n
|
||||
c.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// The method is bad. When it is called, a Counter
|
||||
// receiver value will be copied.
|
||||
func (c Counter) Value() (r int64) {
|
||||
c.Lock()
|
||||
r = c.n
|
||||
c.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
我们只需要改变 `Value` 接收类型方法为指针类型 `*Counter`,就可以避免拷贝 `Mutex` 值。
|
||||
|
||||
在官方的 Go SDK 中提供的 `go vet` 命令将会报告潜在的错误值拷贝。
|
||||
|
||||
### 在错误的地方调用 `sync.WaitGroup` 的方法
|
||||
|
||||
每个 `sync.WaitGroup` 值维护一个内部计数器,这个计数器的初始值为 0。如果一个 `WaitGroup` 计数器的值也是 0,调用 `WaitGroup` 值的 `Wait` 方法不会被阻塞,否则,在计数器值为 0 之前,这个调用会一直被阻塞。
|
||||
|
||||
为了让 `WaitGroup` 值的使用有意义,当一个 `WaitGroup` 计数器值为 0 时,必须在相应的 `WaitGroup` 值的 `Wait` 方法调用之前,去调用 `WaitGroup` 值的 `Add` 方法。
|
||||
|
||||
例如,下面的程序中,在不正确位置调用了 `Add` 方法,这将使最后打印出的数字不总是 `100`。事实上,这个程序最后打印的数字可能是在 `[0, 100)` 范围内的一个随意数字。原因就是 `Add` 方法的调用并不保证一定会发生在 `Wait` 方法调用之前。
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var wg sync.WaitGroup
|
||||
var x int32 = 0
|
||||
for i := 0; i < 100; i++ {
|
||||
go func() {
|
||||
wg.Add(1)
|
||||
atomic.AddInt32(&x, 1)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
fmt.Println("To wait ...")
|
||||
wg.Wait()
|
||||
fmt.Println(atomic.LoadInt32(&x))
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
为让程序的表现符合预期,在 `for` 循环中,我们将把 `Add` 方法的调用移动到创建的新 goroutines 的范围之外,修改后的代码如下。
|
||||
|
||||
```
|
||||
...
|
||||
for i := 0; i < 100; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
atomic.AddInt32(&x, 1)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
### 不正确使用 Futures 信道
|
||||
|
||||
在 [信道使用案例][14] 的文章中,我们知道一些函数将返回 [futures 信道][15]。假设 `fa` 和 `fb` 就是这样的两个函数,那么下面的调用就使用了不正确的 future 参数。
|
||||
|
||||
```
|
||||
doSomethingWithFutureArguments(<-fa(), <-fb())
|
||||
|
||||
```
|
||||
|
||||
在上面的代码行中,两个信道接收操作是顺序进行的,而不是并发的。我们做如下修改使它变成并发操作。
|
||||
|
||||
```
|
||||
ca, cb := fa(), fb()
|
||||
doSomethingWithFutureArguments(<-c1, <-c2)
|
||||
|
||||
```
|
||||
|
||||
### 没有等 Goroutine 的最后的活动的发送结束就关闭信道
|
||||
|
||||
Go 程序员经常犯的一个错误是,还有一些其它的 goroutine 可能会发送值到以前的信道时,这个信道就已经被关闭了。当这样的发送(发送到一个已经关闭的信道)真实发生时,将引发一个异常。
|
||||
|
||||
这种错误在一些以往的著名 Go 项目中也有发生,比如在 Kubernetes 项目中的 [这个 bug][3] 和 [这个 bug][4]。
|
||||
|
||||
如何安全和优雅地关闭信道,请阅读 [这篇文章][5]。
|
||||
|
||||
### 在值上做 64 位原子操作时没有保证值地址 64 位对齐
|
||||
|
||||
到目前为止(Go 1.10),在标准的 Go 编译器中,在一个 64 位原子操作中涉及到的值的地址要求必须是 64 位对齐的。如果没有对齐则导致当前的 goroutine 异常。对于标准的 Go 编译器来说,这种失败仅发生在 32 位的架构上。请阅读 [内存布局][6] 去了解如何在一个 32 位操作系统上保证 64 位对齐。
|
||||
|
||||
### 没有注意到大量的资源被 `time.After` 函数调用占用
|
||||
|
||||
在 `time` 标准包中的 `After` 函数返回 [一个延迟通知的信道][7]。这个函数在某些情况下用起来很便捷,但是,每次调用它将创建一个 `time.Timer` 类型的新值。这个新创建的 `Timer` 值在通过传递参数到 `After` 函数指定期间保持激活状态,如果在这个期间过多的调用了该函数,可能会有太多的 `Timer` 值保持激活,这将占用大量的内存和计算资源。
|
||||
|
||||
例如,如果调用了下列的 `longRunning` 函数,将在一分钟内产生大量的消息,然后在某些周期内将有大量的 `Timer` 值保持激活,即便是大量的这些 `Timer` 值已经没用了也是如此。
|
||||
|
||||
```
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The function will return if a message arrival interval
|
||||
// is larger than one minute.
|
||||
func longRunning(messages <-chan string) {
|
||||
for {
|
||||
select {
|
||||
case <-time.After(time.Minute):
|
||||
return
|
||||
case msg := <-messages:
|
||||
fmt.Println(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
为避免在上述代码中创建过多的 `Timer` 值,我们将使用一个单一的 `Timer` 值去完成同样的任务。
|
||||
|
||||
```
|
||||
func longRunning(messages <-chan string) {
|
||||
timer := time.NewTimer(time.Minute)
|
||||
defer timer.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
return
|
||||
case msg := <-messages:
|
||||
fmt.Println(msg)
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
}
|
||||
|
||||
// The above "if" block can also be put here.
|
||||
|
||||
timer.Reset(time.Minute)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 不正确地使用 `time.Timer` 值
|
||||
|
||||
在最后,我们将展示一个符合语言使用习惯的 `time.Timer` 值的使用示例。需要注意的一个细节是,那个 `Reset` 方法总是在停止或者 `time.Timer` 值释放时被使用。
|
||||
|
||||
在 `select` 块的第一个 `case` 分支的结束部分,`time.Timer` 值被释放,因此,我们不需要去停止它。但是必须在第二个分支中停止定时器。如果在第二个分支中 `if` 代码块缺失,它可能至少在 `Reset` 方法调用时,会(通过 Go 运行时)发送到 `timer.C` 信道,并且那个 `longRunning` 函数可能会早于预期返回,对于 `Reset` 方法来说,它可能仅仅是重置内部定时器为 0,它将不会清理(耗尽)那个发送到 `timer.C` 信道的值。
|
||||
|
||||
例如,下面的程序很有可能在一秒内而不是十秒时退出。并且更重要的是,这个程序并不是 DRF 的(译者注:data race free,多线程程序的一种同步程度)。
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
timer := time.NewTimer(time.Second/2)
|
||||
select {
|
||||
case <-timer.C:
|
||||
default:
|
||||
time.Sleep(time.Second) // go here
|
||||
}
|
||||
timer.Reset(time.Second * 10)
|
||||
<-timer.C
|
||||
fmt.Println(time.Since(start)) // 1.000188181s
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
当 `time.Timer` 的值不再被其它任何一个东西使用时,它的值可能被停留在一种非停止状态,但是,建议在结束时停止它。
|
||||
|
||||
在多个 goroutines 中如果不按建议使用 `time.Timer` 值并发,可能会有 bug 隐患。
|
||||
|
||||
我们不应该依赖一个 `Reset` 方法调用的返回值。`Reset` 方法返回值的存在仅仅是为了兼容性目的。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://go101.org/article/concurrent-common-mistakes.html
|
||||
|
||||
作者:[go101.org ][a]
|
||||
译者:[qhwdw](https://github.com/qhwdw)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:go101.org
|
||||
[1]:https://go101.org/article/memory-model.html
|
||||
[2]:https://go101.org/article/memory-model.html
|
||||
[3]:https://github.com/kubernetes/kubernetes/pull/45291/files?diff=split
|
||||
[4]:https://github.com/kubernetes/kubernetes/pull/39479/files?diff=split
|
||||
[5]:https://go101.org/article/channel-closing.html
|
||||
[6]:https://go101.org/article/memory-layout.html
|
||||
[7]:https://go101.org/article/channel-use-cases.html#timer
|
||||
[8]:https://go101.org/article/channel-use-cases.html
|
||||
[9]:https://go101.org/article/channel.html
|
||||
[10]:https://go101.org/article/concurrent-atomic-operation.html
|
||||
[11]:https://go101.org/article/concurrent-synchronization-more.html
|
||||
[12]:https://go101.org/article/channel-use-cases.html#first-response-wins
|
||||
[13]:https://go101.org/article/channel-use-cases.html#first-response-wins-2
|
||||
[14]:https://go101.org/article/channel-use-cases.html
|
||||
[15]:https://go101.org/article/channel-use-cases.html#future-promise
|
@ -1,107 +0,0 @@
|
||||
关于在 Ubuntu Snap 应用商店上发现的加密货币 ‘恶意软件’ 需要了解的内容
|
||||
======
|
||||
最近,有发现称一些 Ubuntu Snaps 应用商店上的应用包含加密货币挖矿程序。Canonical 公司迅速下架了这些违规的应用,但是留下了几个有待回答的问题。
|
||||
|
||||
### 在 Snap 应用商店上发现了加密矿工
|
||||
|
||||
![Crypto Miner Malware on Ubuntu Snap Store][1]
|
||||
|
||||
5月11号,一位名叫 [tarwirdur][2] 的用户在 [snapcraft.io repository][3] 开了一个新的 issue ,他提到一个由 Nicolas Tomb 开发,叫做 2048buntu 的 snap 应用包含加密货币矿工。tarwirdur 询问他怎样才能因为安全原因“抱怨应用” 。tarwirdur 后来发表说其他由 Nicolas Tomb 开发的 snap 应用都包含加密货币矿工。
|
||||
|
||||
看起来 snap 应用使用了 systemd 在系统启动时自动地运行代码,并在用户不知情的情况下在后台运行。
|
||||
|
||||
{对那些不熟悉相关术语的人来说,加密货币矿工是一段占用计算机主处理器或者图形处理器来“挖掘”数字货币的程序。“挖矿”通常涉及到解决一个数学等式。在这种情况下,如果你在运行 2048buntu 游戏,这个游戏将会使用处理器额外的计算能力去进行加密货币的挖掘。}
|
||||
|
||||
Snapcraft 团队迅速地下架了所有由违规者开发的应用来做出回应。他们同时也开展了调查。
|
||||
|
||||
### 隐匿者发声
|
||||
|
||||
5月13号,一位同名为 Nicolas Tomb 的 Disqus 用户在 OMGUbuntu 的新闻报道上发表了[评论][4],他在评论中称自己向 snap 应用中添加了加密货币矿工,从而获取收益。他为他的行为道歉,同时承诺将所有挖矿所得的收益送给 Ubuntu 基金会。
|
||||
|
||||
我们不能确认这个评论就是由 Nicolas Tomb 发表的,因为这个 Disqus 账户最近才被创建,也只有一条评论与之相关联。现在,我们假设是的。
|
||||
|
||||
### Canonical 公司发表了声明
|
||||
|
||||
5月15号,Canonical 公司在这种情况下发表了一份声明。标题为 [“Trust and security in the Snap Store”][5],声明开头重申了当下的情况。他们也补充道[重新发布的 snap 应用中加密货币挖矿程序已经被删除了][6]。
|
||||
|
||||
Canonical 公司随后尝试检测 Nicolas Tomb 的动机。他们指出到他称自己这样做是为了通过应用赚钱(如上所诉),而在真的面对赚钱时就停止了。他们也指出“挖掘加密货币本身是不合法也是不道德的”。然而,他们对实际情况仍旧是不满意的,因为 Nicolas Tomb 没有在 snap 应用的描述中透露加密货币矿工。
|
||||
|
||||
随后 Canonical 公司将主题转移到审核软件上。根据这份申明,Snap 应用商店将会采用一种类似 iOS,Android,Windows 的质量控制系统,这个系统将“自动化检查点,安装包必须在被接受前通过检查,同时在特殊问题被标记时会进行人工审核”。
|
||||
|
||||
然而,Canonical 公司声称“对巨大而弹性的软件仓库来说,只接受每个单独文件都被仔细审核的软件是不可能的”。因此,他们需要相信软件源而不是基于源开发的应用。毕竟,软件源是现在 Ubuntu 软件仓库系统的基础。
|
||||
|
||||
Canonical 公司紧接着谈到了 snap 应用的未来。他们承认现在的系统是不完美的。他们也在不断工作进行改善。他们“在目前的工作中有非常有趣的安全功能,这些功能将会改善系统安全性同时提升人们在服务器或桌面上进行软件开发的体验”。
|
||||
|
||||
其中一个他们正在开发的功能是查看一个软件发布者是否已通过验证。Other improvements include: “upstreaming of all the AppArmor kernel patches” and other under-the-hood fixes.(不确定,under-the-hood 指实现是不透明的,[quora回答](https://www.quora.com/What-does-under-the-hood-mean-in-programming) ,其他的改善包括:“所有 AppArmor 内核补丁的上游”和其他黑盒服务都被修复了。)
|
||||
|
||||
### 一些关于'Snap 应用商店恶意软件'的想法
|
||||
|
||||
基于我读过的所有内容,我产生了了一些想法和问题。
|
||||
|
||||
#### 这种挖矿软件运行多久了?
|
||||
|
||||
首先,这些挖矿软件存在于 Snap 应用商店多久了?因为它们已经被下架了,我们没有这样的数据。我可以通过 Google 快照抓取一些 2048buntu 页面的图片,但这没有提供任何可用的信息。根据软件运行时间,多少系统安装过,什么加密货币被挖掘出了,我们可以谈谈违规者获取的一点钱或一笔钱。一个更长远的问题是:Canonical 公司将来有能力捕捉到这样的违规情况吗?
|
||||
|
||||
#### 这真的是一个恶意软件吗?
|
||||
|
||||
许多新闻网站将之报道为恶意软件感染。我想我甚至可以看到这个事件被称为 Linux 的第一个恶意软件。我不确定这个术语是否精确。Dictionary.com 这样定义 [恶意软件][7]:“意图损害计算机、移动设备、计算机系统或者计算机网络,或者对其运作进行部分控制的软件”。
|
||||
|
||||
有问题的 snap 应用并没有损害或者控制涉及到的计算机。它同样没有感染其他计算机。它也不能这样做,因为所有的 snap 应用位于沙盒之中。它们最多利用了处理器的计算能力,就是这样。所以,我不会称之为恶意软件。
|
||||
|
||||
#### Nothing Like a Loophole(无孔不入?)
|
||||
|
||||
Nicolas Tomb 使用的一个辩解是在他上传应用的时候 Snap 应用商店没有任何反对加密货币挖矿的规则。{我敢向你打赌他们正在纠正这个错误。}他们之所以没有这样的规则,原因很简单,之前没有人做过这种事。如果 Tomb 想正确地做事,他应该提前询问是否允许这种行为。而事实是他似乎没有指出他知道 Canonical 公司可能会拒绝的事实。至少,Canonical 公司会告诉他将这些写在软件的描述中。
|
||||
|
||||
![][8]
|
||||
|
||||
#### Something Looks Hinkey(不会翻译)
|
||||
|
||||
如我之前说的,我从 Google 快照获取了一个 2048buntu 的页面截图。仅仅看它就会感觉到一些危险的信号。首先,截图中几乎没有真实的描述。它是这样描述的“类似2048的游戏。这个游戏用 ubuntu 主题克隆了流行的游戏 — 2048。”哇,{这将会引来容易上当受骗的人。}当我读到类似空洞的描述时,我会多考虑下。
|
||||
|
||||
我注意到的另一件事是软件的大小。2048buntu 的 1.0 版本大小将近 140 MB。一个简单的游戏怎么需要这么多的空间?有用 Javascript 写的浏览器版本大概只用了不到它的四分之一。其他 snap 应用商店的 2048 游戏的大小没有一个达到了这个软件的一半。
|
||||
|
||||
然后,你有许可证。这是一个使用了 Ubuntu 主题的流行游戏的克隆。它如何被认为是专有的?我确信,其他合法的开发者会因为内容而使用了 FOSS (自由开源软件)许可证来上传它。
|
||||
|
||||
单是这些因素就使得这个 snap 应用很特殊,并呼吁进行审核。
|
||||
|
||||
#### 谁是 Nicolas Tomb?
|
||||
|
||||
当第一次读到这些之后,我决定看看我能否找出造成这一团混乱的人。当我搜索 Nicolas Tomb 的时候,我什么都没找到,zip,nada,zilch(感觉是错误,不太明白这几个单词在这里的意思)。所有我找到的只是一大堆关于加密货币挖矿 snap 应用的新闻和文章,以及去 tomb of St. Nicolas 旅游的信息。在 Twiter 和 Github 上都没有 Nicolas Tomb 的标志。看起来似乎是为了上传这些 snap 应用才创建的名称。
|
||||
|
||||
这同样引出了 Canonical 公司发表的申明中的一点,关于验证发布者。上一次我查看的时候,相当多的 snap 应用不是由应用的维护者发布的。这让我感到担忧。我更乐意相信 firefox 的 snap 应用是由 Mozilla 基金会发布的,而不是 Leonard Borsch。如果对应用维护者来说关注应用的 snap 版本太耗费精力,应该有办法让维护者在他们软件的 snap 版本上贴上批准的标签。就像是 Firefox 的 snap 版本由 Fredrick 发布,经 Mozilla 基金会批准。只是为了让用户对下载的内容更放心。
|
||||
|
||||
#### Snap 应用商店无疑有改善的空间
|
||||
|
||||
在我看来,Snap 应用商店团队应该实现的第一个特性是报告可疑应用的方式。tarwirdur 必须找到该网站的 Github 页面。大多数用户不会想到这一点。如果 Snap 应用商店不能审核每一行代码,使用户能够报告问题是下一个最好的事情。即使评分系统也不会是一个坏的补充。我确信一定有部分人因为 2048buntu 使用了太多系统资源而给它很低的评分。
|
||||
|
||||
#### 结论
|
||||
|
||||
从我所见过的来说,我认为某个人开发了一些简单的应用,在每个应用中嵌入了加密货币矿工,之后将这些应用上传到 Snap 应用商店,想着捞一笔钱。一旦他们被抓了,他们就声称这仅仅为了通过应用程序获利。如果这是真的,他们应该已经在 snap 应用的描述中提到了。隐藏加密矿工并不是什么[新鲜事][9]。他们通常是一种盗取计算能力的方法。
|
||||
|
||||
我希望 Canonical 公司已经具备了解决这个问题的功能,盼望这些功能能很快出来。
|
||||
|
||||
你对 Snap 应用商店的‘恶意软件风波’有什么看法?你将如何改善这种情况?请在下面的评论中告诉我们。
|
||||
|
||||
如果你觉得这篇文章有趣,请花费一点时间将它分享到社交媒体上。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: https://itsfoss.com/snapstore-cryptocurrency-saga/
|
||||
|
||||
作者:[John Paul][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[paperzhang](https://github.com/paperzhang)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]: https://itsfoss.com/author/john/
|
||||
[1]:https://4bds6hergc-flywheel.netdna-ssl.com/wp-content/uploads/2018/05/ubuntu-snap-malware-800x450.jpeg
|
||||
[2]:https://github.com/tarwirdur
|
||||
[3]:https://github.com/canonical-websites/snapcraft.io/issues/651
|
||||
[4]:https://disqus.com/home/discussion/omgubuntu/malware_found_on_the_ubuntu_snap_store/#comment-3899153046
|
||||
[5]:https://blog.ubuntu.com/2018/05/15/trust-and-security-in-the-snap-store
|
||||
[6]:https://forum.snapcraft.io/t/action-against-snap-store-malware/5417/8
|
||||
[7]:http://www.dictionary.com/browse/malware?s=t
|
||||
[8]:https://4bds6hergc-flywheel.netdna-ssl.com/wp-content/uploads/2018/05/2048buntu.png
|
||||
[9]:https://krebsonsecurity.com/2018/03/who-and-what-is-coinhive/
|
@ -1,15 +1,17 @@
|
||||
translating by wyxplus
|
||||
How to Install and Configure KVM on Ubuntu 18.04 LTS Server
|
||||
如何在 Ubuntu 18.04 服务器上安装和配置 KVM
|
||||
|
||||
======
|
||||
**KVM** (Kernel-based Virtual Machine) is an open source full virtualization solution for Linux like systems, KVM provides virtualization functionality using the virtualization extensions like **Intel VT** or **AMD-V**. Whenever we install KVM on any linux box then it turns it into the hyervisor by loading the kernel modules like **kvm-intel.ko** ( for intel based machines) and **kvm-amd.ko** ( for amd based machines).
|
||||
|
||||
KVM allows us to install and run multiple virtual machines (Windows & Linux). We can create and manage KVM based virtual machines either via **virt-manager** graphical user interface or **virt-install** & **virsh** cli commands.
|
||||
**KVM**(基于内核的虚拟机)是一款为类 Linux 系统提供的开源全虚拟化解决方案,KVM 使用虚拟化扩展(如 **Intel VT** 或 **AMD-V**)提供虚拟化功能。无论何时我们在任何 Linux 机器上安装 KVM,都会通过加载诸如 kvm-intel.ko(基于 Intel 的机器)和 kvm-amd.ko(基于 amd 的机器)的内核模块,使其成为<ruby>管理程序<rt>hyervisor</rt></ruby>(LCTT 译者注:一种监控和管理虚拟机运行的核心软件层)。
|
||||
|
||||
In this article we will discuss how to install and configure **KVM hypervisor** on Ubuntu 18.04 LTS server. I am assuming you have already installed Ubuntu 18.04 LTS server on your system. Login to your server and perform the following steps.
|
||||
KVM 允许我们安装和运行多个虚拟机(Windows 和 Linux)。我们可以通过 **virt-manager** 的图形用户界面或使用 **virt-install** 和 **virsh** 命令在命令行界面来创建和管理基于 KVM 的虚拟机。
|
||||
|
||||
### Step:1 Verify Whether your system support hardware virtualization
|
||||
在本文中,我们将讨论如何在Ubuntu 18.04 LTS 服务器上安装和配置 **KVM 管理程序**。我假设你已经在你的服务器上安装了 Ubuntu 18.04 LTS 。接下来登录到您的服务器执行以下步骤。
|
||||
|
||||
### 第一步:确认您的硬件是否支持虚拟化
|
||||
|
||||
执行 egrep 命令以验证您的服务器的硬件是否支持虚拟化,
|
||||
|
||||
Execute below egrep command to verify whether your system supports hardware virtualization or not,
|
||||
```
|
||||
linuxtechi@kvm-ubuntu18-04:~$ egrep -c '(vmx|svm)' /proc/cpuinfo
|
||||
1
|
||||
@ -17,15 +19,17 @@ linuxtechi@kvm-ubuntu18-04:~$
|
||||
|
||||
```
|
||||
|
||||
If the output is greater than 0 then it means your system supports Virtualization else reboot your system, then go to BIOS settings and enable VT technology.
|
||||
如果输出结果大于 0,就意味着您的硬件支持虚拟化。重启,进入 BIOS 设置中启用 VT 技术。
|
||||
|
||||
现在使用下面的命令安装“ **kvm-ok** ”实用程序,该程序用于确定您的服务器是否能够运行硬件加速的 KVM 虚拟机
|
||||
|
||||
Now Install “ **kvm-ok** ” utility using below command, it is used to determine if your server is capable of running hardware accelerated KVM virtual machines
|
||||
```
|
||||
linuxtechi@kvm-ubuntu18-04:~$ sudo apt install cpu-checker
|
||||
|
||||
```
|
||||
|
||||
Run kvm-ok command and verify the output,
|
||||
运行 kvm-ok 命令确认输出结果,
|
||||
|
||||
```
|
||||
linuxtechi@kvm-ubuntu18-04:~$ sudo kvm-ok
|
||||
INFO: /dev/kvm exists
|
||||
@ -34,41 +38,44 @@ linuxtechi@kvm-ubuntu18-04:~$
|
||||
|
||||
```
|
||||
|
||||
### Step:2 Install KVM and its required packages
|
||||
### 第二步:安装 KVM 及其依赖包
|
||||
|
||||
Run the below apt commands to install KVM and its dependencies
|
||||
|
||||
运行下面的 apt 命令安装 KVM 及其依赖项
|
||||
```
|
||||
linuxtechi@kvm-ubuntu18-04:~$ sudo apt update
|
||||
linuxtechi@kvm-ubuntu18-04:~$ sudo apt install qemu qemu-kvm libvirt-bin bridge-utils virt-manager
|
||||
|
||||
```
|
||||
|
||||
Once the above packages are installed successfully, then your local user (In my case linuxtechi) will be added to the group libvirtd automatically.
|
||||
只要上图相应的软件包安装成功,那么你的本地用户(对于我来说是 linuxtechi)将被自动添加到 libvirtd 群组。
|
||||
|
||||
### Step:3 Start & enable libvirtd service
|
||||
### 第三步:启动并启用 libvirtd 服务
|
||||
|
||||
我们在 Ubuntu 18.04 服务器上安装 qemu 和 libvirtd 软件包之后,它就会自动启动并启用 libvirtd 服务,如果 libvirtd 服务没有开启,则运行以下命令开启,
|
||||
|
||||
Whenever we install qemu & libvirtd packages in Ubuntu 18.04 Server then it will automatically start and enable libvirtd service, In case libvirtd service is not started and enabled then run beneath commands,
|
||||
```
|
||||
linuxtechi@kvm-ubuntu18-04:~$ sudo service libvirtd start
|
||||
linuxtechi@kvm-ubuntu18-04:~$ sudo update-rc.d libvirtd enable
|
||||
|
||||
```
|
||||
|
||||
Now verify the status of libvirtd service using below command,
|
||||
现在使用下面的命令确认 libvirtd 服务的状态,
|
||||
|
||||
```
|
||||
linuxtechi@kvm-ubuntu18-04:~$ service libvirtd status
|
||||
|
||||
```
|
||||
|
||||
Output would be something like below:
|
||||
输出结果如下所示:
|
||||
|
||||
[![libvirtd-command-ubuntu18-04][1]![libvirtd-command-ubuntu18-04][2]][3]
|
||||
|
||||
### Step:4 Configure Network Bridge for KVM virtual Machines
|
||||
### 第四步:为 KVM 虚拟机配置桥接网络
|
||||
|
||||
Network bridge is required to access the KVM based virtual machines outside the KVM hypervisor or host. In Ubuntu 18.04, network is managed by netplan utility, whenever we freshly installed Ubuntu 18.04 server then a file with name “ **/etc/netplan/50-cloud-init.yaml** ” is created automatically, to configure static IP and bridge, netplan utility will refer this file.
|
||||
只有通过桥接网络,KVM 虚拟机才能访问外部的 KVM 管理程序或主机。在Ubuntu 18.04中,网络由 netplan 实用程序管理,每当我们新安装一个 Ubuntu 18.04 系统时,会自动创建一个名称为 “**/etc/netplan/50-cloud-init.yaml**” 文件,其配置了静态 IP 和桥接网络,netplan 实用工具将引用这个文件。
|
||||
|
||||
截至目前,我已经在此文件配置了静态 IP,文件的具体内容如下:
|
||||
|
||||
As of now I have already configured the static IP via this file and content of this file is below:
|
||||
```
|
||||
network:
|
||||
ethernets:
|
||||
@ -83,7 +90,8 @@ network:
|
||||
|
||||
```
|
||||
|
||||
Let’s add the network bridge definition in this file,
|
||||
我们在这个文件中添加桥接网络的配置信息,
|
||||
|
||||
```
|
||||
linuxtechi@kvm-ubuntu18-04:~$ sudo vi /etc/netplan/50-cloud-init.yaml
|
||||
|
||||
@ -105,20 +113,23 @@ network:
|
||||
|
||||
```
|
||||
|
||||
As you can see we have removed the IP address from interface(ens33) and add the same IP to the bridge ‘ **br0** ‘ and also added interface (ens33) to the bridge br0. Apply these changes using below netplan command,
|
||||
正如你所看到的,我们已经从接口(ens33)中删除了 IP 地址,并将该 IP 添加到 '**br0**' 中,并且还将接口(ens33)添加到 br0。使用下面的 netplan 命令使更改生效,
|
||||
|
||||
```
|
||||
linuxtechi@kvm-ubuntu18-04:~$ sudo netplan apply
|
||||
linuxtechi@kvm-ubuntu18-04:~$
|
||||
|
||||
```
|
||||
|
||||
If you want to see the debug logs then use the below command,
|
||||
如果您想查看 debug 日志请使用以下命令,
|
||||
|
||||
```
|
||||
linuxtechi@kvm-ubuntu18-04:~$ sudo netplan --debug apply
|
||||
|
||||
```
|
||||
|
||||
Now Verify the bridge status using following methods:
|
||||
现在使用以下方法确认网络桥接状态:
|
||||
|
||||
```
|
||||
linuxtechi@kvm-ubuntu18-04:~$ sudo networkctl status -a
|
||||
|
||||
@ -132,18 +143,18 @@ linuxtechi@kvm-ubuntu18-04:~$ ifconfig
|
||||
|
||||
[![ifconfig-command-output-ubuntu18-04][1]![ifconfig-command-output-ubuntu18-04][5]][5]
|
||||
|
||||
### Start:5 Creating Virtual machine (virt-manager or virt-install command )
|
||||
### 第五步:创建虚拟机(使用 virt-manager 或 virt-install 命令)
|
||||
|
||||
There are two ways to create virtual machine:
|
||||
有两种方式创建虚拟机:
|
||||
|
||||
* virt-manager (GUI utility)
|
||||
* virt-install command (cli utility)
|
||||
* virt-manager(图形化工具)
|
||||
* virt-install(命令行工具)
|
||||
|
||||
|
||||
**使用 virt-manager 创建虚拟机:**
|
||||
|
||||
**Creating Virtual machine using virt-manager:**
|
||||
通过执行下面的命令启动 virt-manager,
|
||||
|
||||
Start the virt-manager by executing the beneath command,
|
||||
```
|
||||
linuxtechi@kvm-ubuntu18-04:~$ sudo virt-manager
|
||||
|
||||
@ -151,39 +162,42 @@ linuxtechi@kvm-ubuntu18-04:~$ sudo virt-manager
|
||||
|
||||
[![Start-Virt-Manager-Ubuntu18-04][1]![Start-Virt-Manager-Ubuntu18-04][6]][6]
|
||||
|
||||
Create a new virtual machine
|
||||
创建一个新的虚拟机
|
||||
|
||||
[![ISO-file-Virt-Manager][1]![ISO-file-Virt-Manager][7]][7]
|
||||
|
||||
Click on forward and select the ISO file, in my case I am using RHEL 7.3 iso file.
|
||||
点击下一步然后选择 ISO 镜像文件,我使用的是 RHEL 7.3 iso 镜像。
|
||||
|
||||
[![Select-ISO-file-virt-manager-Ubuntu18-04-Server][1]![Select-ISO-file-virt-manager-Ubuntu18-04-Server][8]][8]
|
||||
|
||||
Click on Forward
|
||||
点击下一步
|
||||
|
||||
In the next couple of windows, you will be prompted to specify the RAM, CPU and disk for the VM.
|
||||
在接下来的几个窗口中,系统会提示要求您为 VM 分配内存,处理器数量和磁盘空间。
|
||||
|
||||
Now Specify the Name of the Virtual Machine and network,
|
||||
并指定虚拟机名字和桥接网络名,
|
||||
|
||||
[![VM-Name-Network-Virt-Manager-Ubuntu18-04][1]![VM-Name-Network-Virt-Manager-Ubuntu18-04][9]][9]
|
||||
|
||||
Click on Finish
|
||||
点击结束
|
||||
|
||||
[![RHEL7-3-Installation-Virt-Manager][1]![RHEL7-3-Installation-Virt-Manager][10]][10]
|
||||
|
||||
Now follow the screen instruction and complete the installation,
|
||||
接下来只需要按照屏幕指示安装系统,
|
||||
|
||||
**Creating Virtual machine from CLI using virt-install command,**
|
||||
**使用virt-install命令从命令行界面创建虚拟机,**
|
||||
|
||||
使用下面的 virt-install 命令从终端创建一个虚拟机,它将在命令行界面中开始安装,并根据您对虚拟机的名字,说明,ISO 文件位置和桥接配置的设置创建虚拟机。
|
||||
|
||||
Use the below virt-install command to create a VM from terminal, it will start the installation in CLI, replace the name of the VM, description, location of ISO file and network bridge as per your setup.
|
||||
```
|
||||
linuxtechi@kvm-ubuntu18-04:~$ sudo virt-install -n DB-Server --description "Test VM for Database" --os-type=Linux --os-variant=rhel7 --ram=1096 --vcpus=1 --disk path=/var/lib/libvirt/images/dbserver.img,bus=virtio,size=10 --network bridge:br0 --graphics none --location /home/linuxtechi/rhel-server-7.3-x86_64-dvd.iso --extra-args console=ttyS0
|
||||
|
||||
```
|
||||
|
||||
That’s conclude the article, I hope this article help you to install KVM on your Ubuntu 18.04 Server. Apart from this, KVM is the default hypervisor for Openstack.
|
||||
本文到此为止,我希望这篇文章能帮助你能够在 Ubuntu 18.04 服务器上成功安装 KVM。 除此之外,KVM 也是 Openstack 默认的管理程序。
|
||||
|
||||
|
||||
阅读更多:“[**如何使用 virsh 命令创建,还原和删除 KVM 虚拟机快照**][11]”
|
||||
|
||||
Read More On : “[ **How to Create, Revert and Delete KVM Virtual machine (domain) snapshot with virsh command**][11]“
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
@ -191,7 +205,7 @@ via: https://www.linuxtechi.com/install-configure-kvm-ubuntu-18-04-server/
|
||||
|
||||
作者:[Pradeep Kumar][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[译者ID](https://github.com/译者ID)
|
||||
译者:[wyxplus](https://github.com/wyxplus)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
80
translated/tech/20180521 Starting user software in X.md
Normal file
80
translated/tech/20180521 Starting user software in X.md
Normal file
@ -0,0 +1,80 @@
|
||||
在 X 中启动用户软件
|
||||
======
|
||||
|
||||
目前有很多方法可以在开启用户会话时启动软件。
|
||||
|
||||
这是一篇试图将所有方法聚集在一起的文章。某些部分可能不精确或不正确,但这是一个开始,如果我收到更正,我很乐意保持更新。
|
||||
|
||||
### x11-common
|
||||
|
||||
`man xsession`
|
||||
|
||||
* 由显示管理器启动,如 `/usr/share/lightdm/lightdm.conf.d/01_debian.conf` 或 `/etc/gdm3/Xsession`。
|
||||
* Debian 特定。
|
||||
* 在 `/etc/X11/Xsession.d/` 中运行脚本。
|
||||
* `/etc/X11/Xsession.d/40x11-common_xsessionrc` 引用 `~/.xsessionrc` ,它只比设置环境变量能做的多一点,因为它在 X 会话启动时运行。
|
||||
* 最后,它启动会话管理器(`gnome-session`、`xfce4-session` 等等)。
|
||||
|
||||
|
||||
|
||||
### systemd --user
|
||||
|
||||
* <https://wiki.archlinux.org/index.php/Systemd/User>
|
||||
* 由 `pam_systemd` 启动,所以它可能在环境变量中没有 DISPLAY 变量设置。
|
||||
* 管理单元:
|
||||
* `/usr/lib/systemd/user/` 由已安装的软件包提供的单元。
|
||||
* `~/.local/share/systemd/user/` 由安装在家目录的软件包提供的单元。
|
||||
* `/etc/systemd/user/` 由系统管理员提供的系统范围的用户的单元。
|
||||
* `~/.config/systemd/user/` ,用户自己放置的单元。
|
||||
* 设置 X 会话并使用 DISPLAY 变量时,启动 systemd 用户单元的技巧是从 `.desktop` 自启动文件调用 `systemctl start`。
|
||||
|
||||
|
||||
|
||||
### dbus activation
|
||||
|
||||
* <https://dbus.freedesktop.org/doc/system-activation.txt>
|
||||
* 进行 dbus 请求的用户进程可以触发启动服务器程序。
|
||||
* 对于系统调试,有没有一种方法可以监控哪些服务正在启动 dbus?
|
||||
|
||||
|
||||
|
||||
### X 会话管理器
|
||||
|
||||
* <https://en.wikipedia.org/wiki/X_session_manager>
|
||||
* 由 `x11-common` 的 `Xsession.d` 运行。
|
||||
* 运行 freedesktop autostart .desktop 文件。
|
||||
* 运行桌面环境特定的软件。
|
||||
|
||||
|
||||
|
||||
### xdg autostart
|
||||
|
||||
* <https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html>
|
||||
* 由会话管理器运行
|
||||
* 如果存在 `/etc/xdg/autostart/foo.desktop` 和 `~/.config/autostart/foo.desktop` ,那么只会使用 `~/.config/autostart/foo.desktop`,因为 `~/.config/autostart/` 比 `/etc/xdg/autostart/` 更重要。
|
||||
* 是否有顺序或者并行?
|
||||
|
||||
|
||||
|
||||
### 其他启动笔记
|
||||
|
||||
#### ~/.Xauthority
|
||||
|
||||
要连接到 X 服务器,客户端需要从 `~/.Xauthority` 发送一个令牌,这证明他们可以读取用户的隐私数据。
|
||||
|
||||
`~/.Xauthority` 包含显示管理器生成的一个令牌,并在启动时传递给 X。
|
||||
|
||||
要查看它的内容,请使用 `xauth -i -f ~/.Xauthority list`。
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
via: http://www.enricozini.org/blog/2018/debian/starting-user-software/
|
||||
|
||||
作者:[Enrico Zini][a]
|
||||
选题:[lujun9972](https://github.com/lujun9972)
|
||||
译者:[geekpi](https://github.com/geekpi)
|
||||
校对:[校对者ID](https://github.com/校对者ID)
|
||||
|
||||
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
|
||||
|
||||
[a]:http://www.enricozini.org/
|
Loading…
Reference in New Issue
Block a user