diff --git a/README.md b/README.md
index bbc3753a39..0e2ee3dc87 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ LCTT 的组成
 * 2013/09/16 公开发布了翻译组成立消息后,又有新的成员申请加入了。并从此建立见习成员制度。
 * 2013/09/24 鉴于大家使用 GitHub 的水平不一,容易导致主仓库的一些错误,因此换成了常规的 fork+PR 的模式来进行翻译流程。
 * 2013/10/11 根据对 LCTT 的贡献,划分了 Core Translators 组,最先的加入成员是 vito-L 和 tinyeyeser。
-* 2013/10/12 取消对 LINUX.CN 注册用户的依赖,在 QQ 群内、文章内都采用 GitHub 的注册 ID。
+* 2013/10/12 取消对 LINUX.CN 注册用户的关联,在 QQ 群内、文章内都采用 GitHub 的注册 ID。
 * 2013/10/18 正式启动 man 翻译计划。
 * 2013/11/10 举行第一次北京线下聚会。
 * 2014/01/02 增加了 Core Translators 成员: geekpi。
@@ -52,7 +52,10 @@ LCTT 的组成
 * 2015/04/19 发起 LFS-BOOK-7.7-systemd 项目。
 * 2015/06/09 提升 ictlyh 和 dongfengweixiao 为 Core Translators 成员。
 * 2015/11/10 提升 strugglingyouth、FSSlc、Vic020、alim0x 为 Core Translators 成员。
+* 2016/02/18 由于选题 DeadFire 重病,任命 oska874 接手选题工作。
+* 2016/02/29 选题 DeadFire 病逝。
 * 2016/05/09 提升 PurlingNayuki 为校对。
+* 2016/09/10 LCTT 三周年。
 
 活跃成员
 -------------------------------
@@ -61,25 +64,26 @@ LCTT 的组成
 - Leader @wxy,
 - Source @oska874,
 - Proofreader @PurlingNayuki,
+- Proofreader @carolinewuyan,
 - CORE @geekpi,
 - CORE @GOLinux,
 - CORE @ictlyh,
-- CORE @carolinewuyan,
 - CORE @strugglingyouth,
 - CORE @FSSlc
 - CORE @zpl1025,
 - CORE @runningwater,
 - CORE @bazz2,
 - CORE @Vic020,
-- CORE @dongfengweixiao,
 - CORE @alim0x,
+- CORE @tinyeyeser,
+- CORE @Locez,
 - Senior @DeadFire,
 - Senior @reinoir222,
-- Senior @tinyeyeser,
 - Senior @vito-L,
 - Senior @jasminepeng,
 - Senior @willqian,
 - Senior @vizv,
+- Senior @dongfengweixiao,
 - ZTinoZ,
 - martin2011qi,
 - theo-l,
@@ -87,94 +91,81 @@ LCTT 的组成
 - wi-cuckoo,
 - disylee,
 - haimingfg,
-- KayGuoWhu,
 - wwy-hust,
 - felixonmars,
+- KayGuoWhu,
+- mr-ping,
 - su-kaiyao,
-- GHLandy,
-- ivo-wang,
-- cvsher,
+- StdioA,
 - wyangsun,
+- ivo-wang,
+- GHLandy,
+- cvsher,
 - DongShuaike,
 - flsf,
 - SPccman,
 - Stevearzh
-- mr-ping,
 - Linchenguang,
 - Linux-pdz,
 - 2q1w2007,
 - H-mudcup,
-- cposture,
+- MikeCoder,
+- NearTan,
+- goreliu,
 - xiqingongzi,
 - goreliu,
-- NearTan,
 - TxmszLou,
 - ZhouJ-sh,
 - wangjiezhe,
 - icybreaker,
+- vim-kakali,
 - shipsw,
+- Moelf,
+- name1e5s,
 - johnhoow,
 - soooogreen,
+- kokialoves,
 - linuhap,
+- GitFuture,
+- ChrisLeeGit,
 - blueabysm,
 - boredivan,
-- name1e5s,
-- StdioA,
 - yechunxiao19,
-- l3b2w1,
 - XLCYun,
 - KevinSJ,
+- zky001,
+- l3b2w1,
 - tenght,
 - coloka,
 - luoyutiantang,
 - sonofelice,
 - jiajia9linuxer,
 - scusjs,
-- tnuoccalanosrep,
 - woodboow,
 - 1w2b3l,
 - JonathanKang,
+- bestony,
 - crowner,
 - dingdongnigetou,
 - mtunique,
-- CNprober,
+- Rekii008,
 - hyaocuk,
 - szrlee,
-- KnightJoker,
 - Xuanwo,
 - nd0104,
-- Moelf,
 - xiaoyu33,
-- guodongxiaren,
 - ynmlml,
-- vim-kakali,
+- Flowsnow,
 - ggaaooppeenngg,
-- Ricky-Gong,
-- zky001,
-- lfzark,
-- 213edu,
-- bestony,
 - mudongliang,
+- Tanete,
+- lfzark,
 - liuaiping,
-- Timeszoro,
 - rogetfan,
 - JeffDing,
 - Yuking-net,
 
-
-(按增加行数排名前百)
-
-LFS 项目活跃成员有:
-
-- @ictlyh
-- @dongfengweixiao
-- @wxy
-- @H-mudcup
-- @zpl1025
-- @KevinSJ
-- @Yuking-net
-
-(更新于2016/06/20)
+(按增加行数排名前百,更新于2016/09/10)
 
 谢谢大家的支持!
 
diff --git a/lctt2016.md b/lctt2016.md
new file mode 100644
index 0000000000..3481a9d40b
--- /dev/null
+++ b/lctt2016.md
@@ -0,0 +1,42 @@
+LCTT 2016:LCTT 成立三周年了!
+===========================
+
+不知不觉,LCTT 已经成立三年了,对于我这样已经迈过四张的人来说,愈发的感觉时间过得真快。
+
+这三年来,我们 LCTT 经历了很多事情,有些事情想起来仍恍如昨日。
+
+三年前的这一天,我的一个偶发的想法促使我在 Linux 中国的 QQ 群里面发了一则消息,具体的消息内容已经不可考了,大意是鉴于英文 man 手册的中文翻译太差,想组织一些人来重新翻译。不料发出去之后很快赢得一些有热情、有理想的小伙伴们的响应。于是我匆匆建了一个群,拉了一些人进来,甚至当时连翻译组的名称都没有确定。LCTT (Linux.Cn Translation Team)这个名称都是后来逐步定下来的。
+
+关于 LCTT 的早期发展情况,可以参考 LCTT 2014 年周年[总结](http://linux.cn/article-3784-1.html)。
+
+虽然说“翻译 man 手册”这个最初建群的目标因为种种原因搁浅而一直未能重启,但是,这三年来,我们组织了 [213 位志愿者](https://github.com/LCTT/TranslateProject/graphs/contributors)翻译了 2155 篇文章,接受了 [4263 个 PR](https://github.com/LCTT/TranslateProject/pulls?q=is%3Apr+is%3Aclosed),得到了 476 个星。
+
+这三年来,我们经历了 man 项目的流产、 LFS 手册的翻译发布、选题 DeadFire 的离去。得益于 Linux 中国的网站和微博,乃至微信的兴起后的传播,志愿者们的译文传播很广,切实的为国内的开源社区做出了贡献(当然,与此同时,Linux 中国社区也随之更加壮大)。
+
+这些年间,LCTT 来了很多人,也有人慢慢淡出,这里面涌现了不少做出了卓越贡献的人,比如:
+
+- geekpi,作为整个 LCTT 项目中翻译量最大贡献者,却鲜少在群内说话,偶尔露面,被戏称为“鸡排兄”。
+- GOLinux,紧追“鸡排兄”的第二位强人,嗯,群内大部分人的昵称都是他起的,包括楼上。
+- tinyeyeser,“小眼儿”以翻译风趣幽默著称,是 LCTT 早期初创成员之一。
+- Vito-L,早期成员,LCTT 的多数 Wiki 出自于其手。
+- DeadFire,创始成员,从最开始到其离世,一直负责 LCTT 的所有选题工作。
+- oska874,在接过选题工作的重任后,全面主持 LCTT 的工作。
+- carolinewuyan,承担了相当多的校对工作。
+- alim0x,独立完成了 Android 编年史系列的翻译(多达 26 篇,现在还没发布完)等等。
+
+其它还有 ictlyh、strugglingyouth、FSSlc、zpl1025、runningwater、bazz2、Vic020、dongfengweixiao、jasminepeng、willqian、vizv、ZTinoZ、martin2011qi、felixonmars、su-kaiyao、GHLandy、flsf、H-mudcup、StdioA、crowner、vim-kakali 等等,以及还有很多这里没有提到名字的人,都对 LCTT 做出不可磨灭的贡献。
+
+具体的贡献排行榜,可以看[这里](https://github.com/LCTT/TranslateProject/graphs/contributors)。
+
+每年写总结时,我都需要和 gource 以及 ffmpeg 搏斗半天,今年,我又用 gource 重新制作了一份 LCTT 的 GitHub 版本仓库的变迁视频,以飨众人。
+
+本来想写很多,或许 LCTT 和 Linux 中国已经成了我的生活的一部分,竟然不知道该写点什么了,那就此搁笔罢。
+
+另外,为 LCTT 的诸位兄弟姐妹们献上我及管理团队的祝福,也欢迎更多的志愿者加入 LCTT ,传送门在此:
+
+- 项目网站:https://lctt.github.io/ ,请先访问此处了解情况。
+- “Linux中国”开源社区:https://linux.cn/ ,所有翻译的文章都在这里以及它的同名微博、微信发布。
+
+LCTT 组长 wxy
+
+2016/9/10
\ No newline at end of file
diff --git a/sources/tech/20160218 How to Set Nginx as Reverse Proxy on Centos7 CPanel.md b/published/20160218 How to Set Nginx as Reverse Proxy on Centos7 CPanel.md
similarity index 63%
rename from sources/tech/20160218 How to Set Nginx as Reverse Proxy on Centos7 CPanel.md
rename to published/20160218 How to Set Nginx as Reverse Proxy on Centos7 CPanel.md
index 7e0c87cd80..ac17a5eca3 100644
--- a/sources/tech/20160218 How to Set Nginx as Reverse Proxy on Centos7 CPanel.md	
+++ b/published/20160218 How to Set Nginx as Reverse Proxy on Centos7 CPanel.md	
@@ -1,21 +1,15 @@
-
-
-【flankershen翻译中】
-
-
-
-How to Set Nginx as Reverse Proxy on Centos7 CPanel
+如何在 CentOS 7 用 cPanel 配置 Nginx 反向代理
 ================================================================================
 
-Nginx is one of the fastest and most powerful web-server. It is known for its high performance and low resource utilization. It can be installed as both a standalone and a Reverse Proxy Web-server. In this article, I'm discussing about the installation of Nginx as a reverse proxy along with Apache on a CPanel server with latest CentOS 7 installed.
+Nginx 是最快和最强大的 Web 服务器之一,以其高性能和低资源占用率而闻名。它既可以被安装为一个独立的 Web 服务器,也可以安装成反向代理 Web 服务器。在这篇文章,我将讨论在安装了 cPanel 管理系统的 Centos 7 服务器上安装 Nginx 作为 Apache 的反向代理服务器。
 
-Nginx as a reverse proxy will work as a frontend webserver serving static contents along with Apache serving the dynamic files in backend. This setup will boost up the overall server performance.
+Nginx 作为前端服务器用反向代理为静态文件提供服务,Apache 作为后端为动态文件提供服务。这个设置将整体提高服务器的性能。
 
-Let's walk through the installation steps for Nginx as reverse proxy in CentOS7 x86_64 bit server with cPanel 11.52 installed.
+让我们过一遍在已经安装好 cPanel 11.52 的 CentOS 7 x86_64 服务器上配置 Nginx 作为反向代理的安装过程。
 
-First of all, we need to install the EPEL repo to start-up with the process.
+首先,我们需要安装 EPEL 库来启动这个进程
 
-### Step 1: Install the EPEL repo. ###
+### 第一步: 安装 EPEL 库###
 
     root@server1 [/usr]# yum -y install epel-release
     Loaded plugins: fastestmirror, tsflags, universal-hooks
@@ -31,13 +25,13 @@ First of all, we need to install the EPEL repo to start-up with the process.
     
     Dependencies Resolved
     
-    ===============================================================================================================================================
-    Package Arch Version Repository Size
-    ===============================================================================================================================================
+	========================================================================================
+        Package Arch Version Repository Size
+	========================================================================================
     Installing:
     epel-release noarch 7-5 extras 14 k
 
-### Step 2: After installing the repo, we can start with the installation of the nDeploy RPM repo for CentOS to install our required nDeploy Webstack and Nginx plugin. ###
+### 第二步: 可以安装 nDeploy 的 CentOS RPM 库来安装我们所需的 nDeploy Web 类软件和 Nginx 插件 ###
 
     root@server1 [/usr]# yum -y install http://rpm.piserve.com/nDeploy-release-centos-1.0-1.noarch.rpm
     Loaded plugins: fastestmirror, tsflags, universal-hooks
@@ -51,13 +45,13 @@ First of all, we need to install the EPEL repo to start-up with the process.
     
     Dependencies Resolved
     
-    ===============================================================================================================================================
+    ========================================================================================
     Package Arch Version Repository Size
-    ===============================================================================================================================================
+    ========================================================================================
     Installing:
     nDeploy-release-centos noarch 1.0-1 /nDeploy-release-centos-1.0-1.noarch 110
 
-### Step 3: Install the nDeploy and Nginx nDeploy plugins. ###
+### 第三步:安装 nDeploy 和 Nginx nDeploy 插件 ###
 
     root@server1 [/usr]# yum --enablerepo=ndeploy install nginx-nDeploy nDeploy
     Loaded plugins: fastestmirror, tsflags, universal-hooks
@@ -70,9 +64,9 @@ First of all, we need to install the EPEL repo to start-up with the process.
     
     Dependencies Resolved
     
-    ===============================================================================================================================================
+	========================================================================================
     Package Arch Version Repository Size
-    ===============================================================================================================================================
+	========================================================================================
     Installing:
     nDeploy noarch 2.0-11.el7 ndeploy 80 k
     nginx-nDeploy x86_64 1.8.0-34.el7 ndeploy 36 M
@@ -84,12 +78,12 @@ First of all, we need to install the EPEL repo to start-up with the process.
     python-lxml x86_64 3.2.1-4.el7 base 758 k
     
     Transaction Summary
-    ===============================================================================================================================================
+    ========================================================================================
     Install 2 Packages (+5 Dependent packages)
 
-With these steps, we've completed with the installation of Nginx plugin in our server. Now we need to configure Nginx as reverse proxy and create the virtualhost for the existing cPanel user accounts. For that we can run the following script.
+通过以上这些步骤,我们完成了在我们的服务器上 Nginx 插件的安装。现在我们可以配置 Nginx 作为反向代理和为已有的 cPanel 用户账户创建虚拟主机,为此我们可以运行如下脚本。
 
-### Step 4: To enable Nginx as a front end Web Server and create the default configuration files. ###
+### 第四步:启动 Nginx 作为默认的前端 Web 服务器,并创建默认的配置文件###
 
     root@server1 [/usr]# /opt/nDeploy/scripts/cpanel-nDeploy-setup.sh enable
     Modifying apache http and https port in cpanel
@@ -101,9 +95,9 @@ With these steps, we've completed with the installation of Nginx plugin in our s
     ConfGen:: saheetha
     ConfGen:: satest
 
-As you can see these script will modify the Apache port from 80 to another port to make Nginx run as a front end web server and create the virtual host configuration files for the existing cPanel accounts. Once it is done, confirm the status of both Apache and Nginx.
+你可以看到这个脚本将修改 Apache 的端口从 80 到另一个端口来让 Nginx 作为前端 Web 服务器,并为现有的 cPanel 用户创建虚拟主机配置文件。一旦完成,确认 Apache 和 Nginx 的状态。
 
-### Apache Status: ###
+#### Apache 状态:####
 
     root@server1 [/var/run/httpd]# systemctl status httpd
     ● httpd.service - Apache Web Server
@@ -118,7 +112,7 @@ As you can see these script will modify the Apache port from 80 to another port
     Jan 18 06:34:23 server1.centos7-test.com apachectl[25606]: httpd (pid 24760) already running
     Jan 18 06:34:23 server1.centos7-test.com systemd[1]: Started Apache Web Server.
 
-### Nginx Status: ###
+#### Nginx 状态:####
 
     root@server1 [~]# systemctl status nginx
     ● nginx.service - nginx-nDeploy - high performance web server
@@ -137,7 +131,7 @@ As you can see these script will modify the Apache port from 80 to another port
     Jan 17 17:18:29 server1.centos7-test.com nginx[3804]: nginx: configuration file /etc/nginx/nginx.conf test is successful
     Jan 17 17:18:29 server1.centos7-test.com systemd[1]: Started nginx-nDeploy - high performance web server.
 
-Nginx act as a frontend webserver running on port 80 and Apache configuration is modified to listen on http port 9999 and https port 4430. Please see their status below:
+Nginx 作为前端服务器运行在 80 端口,Apache 配置被更改为监听 http 端口 9999 和 https 端口 4430。请看他们的情况:
 
     root@server1 [/usr/local/src]# netstat -plan | grep httpd
     tcp 0 0 0.0.0.0:4430 0.0.0.0:* LISTEN 17270/httpd
@@ -151,13 +145,13 @@ Nginx act as a frontend webserver running on port 80 and Apache configuration is
     tcp 0 0 127.0.0.1:80 0.0.0.0:* LISTEN 17802/nginx: master
     tcp 0 0 45.79.183.73:80 0.0.0.0:* LISTEN 17802/nginx: master
 
-The virtualhost entries created for the existing users as located in the folder "**/etc/nginx/sites-enabled**". This file path is included in the Nginx main configuration file.
+为已有用户创建的虚拟主机的配置文件在 "**/etc/nginx/sites-enabled**"。 这个文件路径包含了 Nginx 主要配置文件。
 
     root@server1 [/etc/nginx/sites-enabled]# ll | grep .conf
     -rw-r--r-- 1 root root 311 Jan 17 09:02 saheetha.com.conf
     -rw-r--r-- 1 root root 336 Jan 17 09:02 saheethastest.com.conf
 
-### Sample Vhost for a domain: ###
+#### 一个域名的示例虚拟主机:###
 
     server {
 
@@ -173,7 +167,7 @@ The virtualhost entries created for the existing users as located in the folder
     
     }
 
-We can confirm the working of the web server status by calling a website in the browser. Please see the web server information on my server after the installation.
+我们可以启动浏览器查看网站来确定 Web 服务器的工作状态。安装后,请阅读服务器上的 web 服务信息。
 
     root@server1 [/home]# ip a | grep -i eth0
     3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
@@ -183,24 +177,24 @@ We can confirm the working of the web server status by calling a website in the
 
 ![webserver-status](http://blog.linoxide.com/wp-content/uploads/2016/01/webserver.png)
 
-Nginx will create the virtual host automatically for any newly created accounts in cPanel. With these simple steps we can configure Nginx as reverse proxy on a CentOS 7/CPanel server.
+Nginx 将会为任何最新在 cPanel 中创建的账户创建虚拟主机。通过这些简单的的步骤,我们能够在一台 CentOS 7 / cPanel 的服务器上配置 Nginx 作为反向代理。
 
-### Advantages of Nginx as Reverse Proxy: ###
+### Nginx 作为反向代理的优势###
 
- 1. Easy to install and configure
- 2. Performance and efficiency
- 3. Prevent DDOS attacks
- 4. Allows .htaccess PHP rewrite rules
+1. 便于安装和配置。
+2. 效率高、性能好。
+3. 防止 Ddos 攻击。
+4. 支持使用 .htaccess 作为 PHP 的重写规则。
 
-I hope this article is useful for you guys. Thank you for referring to this. I would appreciate your valuable comments and suggestions on this for further improvements.
+我希望这篇文章对你们有用。感谢你看它。我非常高兴收到你的宝贵意见和建议,并进一步改善。
 
 --------------------------------------------------------------------------------
 
 via: http://linoxide.com/linux-how-to/set-nginx-reverse-proxy-centos-7-cpanel/
 
 作者:[Saheetha Shameer][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
+译者:[bestony](https://github.com/bestony)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
 
diff --git a/published/20160505 Confessions of a cross-platform developer.md b/published/20160505 Confessions of a cross-platform developer.md
new file mode 100644
index 0000000000..72716e9eb8
--- /dev/null
+++ b/published/20160505 Confessions of a cross-platform developer.md	
@@ -0,0 +1,73 @@
+一位跨平台开发者的自白
+=============================================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/business_clouds.png?itok=cucHuJnU)
+
+[Andreia Gaita][1] 在 OSCON 开源大会上发表了一个题为[跨平台开发者的自白][2]的演讲。她长期从事于开源工作,并且为 [Mono][3] 工程(LCTT 译注:一个致力于开创 .NET 在 Linux 上使用的开源工程)做着贡献,主要以 C#/C++ 开发。Andreia 任职于 GitHub,她的工作是专注构建 Visual Studio 的 GitHub 扩展管理器。
+
+我在她发表演讲前就迫不及待的想要问她一些关于跨平台开发的事,问问她作为一名跨平台开发者在这 16 年之中学习到了什么。
+
+![](https://opensource.com/sites/default/files/images/life/Interview%20banner%20Q%26A.png)
+
+**在你开发跨平台代码中,你使用过的最简单的和最难的代码语言是什么?**
+
+我很少讨论某种语言的好坏,更多是讨论是那些语言有哪些库和工具。语言的编译器、解释器以及构建系统决定了用它们做跨平台开发的难易程度(或者它们是否可能做跨平台开发),可用的 UI 库和对本地系统的访问能力决定了与该操作系统集成的紧密程度。按照我的观点,我认为 C# 最适合完成跨平台开发工作。这种语言自身包括了允许快速的本地调用和精确的内存映射的功能,如果你希望你的代码能够与系统和本地函数库进行交互就需要这些功能。而当我需要非常特殊的系统功能时,我就会切换到 C 或者 C++。
+
+**你使用的跨平台开发工具或者抽象层有哪些?**
+
+我的大部分跨平台工作都是为其它需要开发跨平台应用的人开发工具、库和绑定(binding),一般是用 MONO/C# 和 C/C++。在抽象的层面我用的不多,更多是在 glib 库和友元(friends)方面。大多数时候,我用 Mono 去完成各种跨平台应用的,包括 UI,或者偶然在游戏开发中用到 Unity3D 的部分。我经常使用 Electron(LCTT 译注:Atom 编辑器的兄弟项目,可以用 Electron 开发桌面应用)。
+
+**你接触过哪些构建系统?它们之间的区别是由于语言还是平台的不同?**
+
+我试着选择适合我使用的语言的构建系统。那样,就会很少遇到让我头疼的问题(希望如此)。它需要支持平台和体系结构间的选择、构建输出结果位置可以智能些(多个并行构建),以及易配置性等。大多数时候,我的项目会结合使用 C/C++ 和 C#,我要从同一源代码同时构建不同的配置环境(调试、发布、Windows、OSX、Linux、Android、iOS 等等),这通常需要为每个构建的输出结果选择带有不同参数的不同编译器。构建系统可以帮我做到这一切而不用让我(太)费心。我时常尝试着用不同的构建系统,看看有些什么新的变化,但是最终,我还是回到了使用 makefile 的情况,并结合使用 shell 和批处理脚本或 Perl 脚本来完成工作(因为如果我希望用户来构建我的软件,我还是最好选择一种到处都可以用的命令行脚本语言)。
+
+**你怎样平衡在这种使用统一的用户界面下提供原生的外观和体验的需求呢?** 
+
+跨平台的用户界面的实现很困难。在过去几年中我已经使用了一些跨平台 GUI,并且我认为这些事情上并没有最优解。基本上有两种选择。你可以选择一个跨平台工具去做一个并不是完全适合你所有支持的平台的 UI,但是代码库比较小,维护成本比较低。或者你可以选择去开发针对平台的 UI,那样看起来更原生,集成的也更好,但是需要更大的代码库和更高的维护成本。这种决定完全取决于 APP 的类型、它有多少功能、你有多少资源,以及你要把它运行在多少平台上?
+
+最后,我认为用户比较接受这种“一个 UI 打通关”了,就比如 Electron 框架。我有个 Chromium+C+C# 的框架侧项目,有一天我希望可以用 C# 构建 Electron 型的 app,这样的话我就可以做到两全其美了。
+ 
+**构建/打包系统的依赖性对你有影响吗 ?**
+
+我依赖的使用方面很保守,我被崩溃的 ABI(LCTT 译注:应用程序二进制接口)、冲突的符号、以及丢失的包等问题困扰了太多次。我决定我要针对的操作系统版本,并选择最低的公有部分来使问题最小化。通常这就意味着有五种不同的 Xcode 和 OSX 框架库,要在同样的机器上相应安装五种不同的 Visual Studio 版本,多种 clang(LCTT 译注:C语言、C++、Object-C、C++ 语言的轻量级编译器)和 gcc 版本,一系列的运行着各种发行版的虚拟机。如果我不能确定我要使用的操作系统的包的状态,我有时就会静态连接库,有时会子模块化依赖以确保它们一直可用。大多时候,我会避免这些很棘手的问题,除非我非常需要使用他们。 
+  
+**你使用持续集成(CI)、代码审查以及相关的工具吗?**
+
+基本每天都用。这是保持高效的唯一方式。我在一个项目中做的第一件事情是配置跨平台构建脚本,保证每件事尽可能自动化完成。当你面向多平台开发的时候,持续集成是至关重要的。没有人能在一个机器上构建各种平台的不同组合,并且一旦你的构建过程没有包含所有的平台,你就不会注意到你搞砸的事情。在一个共享式的多平台代码库中,不同的人拥有不同的平台和功能,所以保证质量的唯一的方法是跨团队代码审查结合持续集成和其他分析工具。这不同于其他的软件项目,如果不使用相关的工具就会面临失败。
+
+**你依赖于自动构建测试,或者倾向于在每个平台上构建并且进行本地测试吗?**
+
+对于不包括 UI 的工具和库,我通常使用自动构建测试。如果有 UI,两种方法我都会用到——针对已有的 GUI 工具的可靠的、可脚本化的  UI 自动化少到几乎没有,所以我要么我去针对我要跨我所支持的平台创建 UI 自动化工具,要么手动进行测试。如果一个项目使用了定制的 UI 库(比如说一个类似 Unity3D 的 OpenGL UI),开发可编程的自动化工具并自动化大多数工作就相当容易。不过,没有什么东西会像人一样双击就测试出问题。
+
+**如果你要做跨平台开发,你喜欢用跨编辑器的构建系统,比如在 Windows 上使用 Visual Studio,在 Linux 上使用 Qt Creator,在 Mac 上使用 XCode 吗?还是你更趋向于使用 Eclipse 这样的可以在所有平台上使用的单一平台?**
+
+我喜欢使用跨编辑器的构建系统。我更喜欢在不同的IDE上保存项目文件(这样可以使增加 IDE 变得更容易),通过使用构建脚本让 IDE 在它们支持的平台上去构建。对于一个开发者来说编辑器是最重要的工具,学习它们是需要花费时间和精力的,而它们是不可相互替代的。我有我自己喜欢的编辑器和工具,每个人也可以使用他们最喜爱的工具。
+
+**在跨平台开发的时候,你更喜欢使用什么样的编辑器、开发环境和 IDE 呢?**
+
+跨平台开发者被限制在只能选择可以在多数平台上工作的所共有的不多选择之一。我爱用 Visual Studio,但是我不能依赖它完成除 Windows 平台之外的工作(你可能不想让 Windows 成为你的主要的交叉编译平台),所以我不会使用它作为我的主要 IDE。即使可以,跨平台开发者的核心技能也是尽可能的了解和使用大量的平台。这就意味着必须很熟悉它们——使用该平台上的编辑器和库,了解这种操作系统及其适用场景、运行方式以及它的局限性等。做这些事情就需要头脑清醒(我的捷径是加强记忆),我必须依赖跨平台的编辑器。所以我使用 Emacs 和 Sublime。
+
+**你之前和现在最喜欢的跨平台项目是什么?**
+
+我一直很喜欢 Mono,并且得心应手,其它的项目大部分都是以某种方式围绕着它进行的。Gluezilla 是我在多年前开发的一个 Mozilla 绑定(binding),可以把 C# 开发的应用嵌入到 Web 浏览器页面中,并且看起来很有特色。我开发过一个 Winform 应用,它是在 linux 上开发的,它可以在 Windows 上运行在一个 Mozilla 浏览器页面里嵌入的 GTK 视图中。CppSharp 项目(以前叫做 Cxxi,更早时叫做 CppInterop)是一个我开始为 C++ 库生成 C# 绑定(binding)的项目,这样就可以在 C# 中调用、创建实例、子类化 C++ 类。这样,它在运行的时候就能够检测到所使用的平台,以及用来创建本地运行库的是什么编译器,并为它生成正确的 C# 绑定(binding)。这多么有趣啊。
+
+**你怎样看跨平台开发的未来趋势呢?**
+
+我们构建本地应用程序的方式已经改变了,我感觉在各种桌面操作系统的明显差异在慢慢变得模糊;所以构建跨平台的应用程序将会更加容易,而且对系统的集成也不需要完全本地化。不好的是,这可能意味着应用程序易用性更糟,并且在发挥操作系统特性方面所能做的更少。库、工具以及运行环境的跨平台开发是一种我们知道怎样做的更好,但是跨平台应用程序的开发仍然需要我们的努力。
+
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/business/16/5/oscon-interview-andreia-gaita
+
+作者:[Marcus D. Hanwell][a]
+译者:[vim-kakali](https://github.com/vim-kakali)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/mhanwell
+[1]: https://twitter.com/sh4na
+[2]: http://conferences.oreilly.com/oscon/open-source-us/public/schedule/detail/48702
+[3]: http://www.mono-project.com/
+
diff --git a/published/20160509 Android vs. iPhone Pros and Cons.md b/published/20160509 Android vs. iPhone Pros and Cons.md
new file mode 100644
index 0000000000..76993e842c
--- /dev/null
+++ b/published/20160509 Android vs. iPhone Pros and Cons.md	
@@ -0,0 +1,89 @@
+对比 Android 和 iPhone 的优缺点
+===================================
+
+>当我们比较 Android 与 iPhone 的时候,很显然 Android 具有一定的优势,而 iPhone 则在一些关键方面更好。但是,究竟哪个比较好呢?
+
+对 Android 与 iPhone 比较是个个人的问题。
+
+就好比我来说,我两个都用。我深知这两个平台的优缺点。所以,我决定分享我关于这两个移动平台的观点。另外,然后谈谈我对新的 Ubuntu 移动平台的印象和它的优势。
+
+### iPhone 的优点 
+
+虽然这些天我是个十足的 Android 用户,但我必须承认 iPhone 在某些方面做的是不错。首先,苹果公司在他们的设备更新方面有更好的成绩。这对于运行着 iOS 的旧设备来说尤其是这样。反观 Android ,如果不是谷歌亲生的 Nexus,它最好也不过是一个更高端的运营商支持的手机,你将发现它们的更新少的可怜或者根本没有。
+
+其中 iPhone 做得很好的另一个领域是应用程序的可用性。展开来说,iPhone 应用程序几乎总是有一个简洁的外观。这并不是说 Android 应用程序就是丑陋的,而是,它们可能只是没有像 iOS 上一样的保持不变的操控习惯和一以贯之的用户体验。两个典型的例子, [Dark Sky][1] (天气)和 [Facebook Paper][2] 很好表现了 iOS 独有的布局。
+
+再有就是备份过程。 Android 可以备份,默认情况下是备份到谷歌。但是对应用数据起不了太大作用。对比 iPhone ,iCloud 基本上可以对你的 iOS 设备进行了完整备份。
+
+### iPhone 令我失望的地方 
+
+对 iPhone 来说,最无可争辩的问题是它的硬件限制要比软件限制更大,换句话来说,就是存储容量问题。
+
+你看,对于大多数 Android 手机,我可以买一个容量较小的手机,然后以后可以添加 SD 卡。这意味着两件事:第一,我可以使用 SD 卡来存储大量的媒体文件。其次,我甚至可以用 SD 卡来存储“一些”我的应用程序。而苹果完全不能这么做。
+
+另一个 iPhone 让我失望的地方是它提供的选择很少。备份您的设备?希望你喜欢 iTunes 或 iCloud 吧。但对一些像我一样用 Linux 的人,那就意味着,我唯一的选择便是使用 iCloud。
+
+要公平的说,如果你愿意越狱,你的 iPhone 还有一些其他解决方案的,但这并不是这篇文章所讲的。 Android 的 解锁 root 也一样。本文章针对的是两个平台的原生设置。
+
+最后,让我们不要忘记这件看起来很小的事—— [iTunes 会删掉用户的音乐][3],因为它认为和苹果音乐的内容重复了……或者因为一些其它的类似规定。这不是 iPhone 特有的情况?我不同意,因为那些音乐最终就是在 iPhone 上没有了。我能十分肯定地这么说,是因为不管在哪里我都不会说这种谎话。
+
+![](http://www.datamation.com/imagesvr_ce/5552/mobile-abstract-icon-200x150.jpg)
+
+*Android 和 iPhone 的对决取决于什么功能对你来说最重要。*
+
+### Android 的优点 
+
+Android 给我最大的好处就是 iPhone 所提供不了的:选择。这包括对应用程序、设备以及手机是整体如何工作的选择。
+
+我爱桌面小工具!对于 iPhone 用户来说,它们也许看上去很蠢。但我可以告诉你,它们可以让我不用打开应用程序就可以看到所需的数据,而无需额外的麻烦。另一个类似的功能,我喜欢安装定制的桌面界面,而不是我的手机默认的那个!
+
+最后,我可以通过像 [Airdroid][4] 和 [Tasker][5] 这样的工具给我的智能手机添加计算机级的完整功能。AirDroid 可以让我把我的 Android 手机当成带有一个文件管理和通信功能的计算机——这可以让我可以轻而易举的使用鼠标和键盘。Tasker 更厉害,我可以用它让我手机根据环境变得可联系或不可联系,当我设置好了之后,当我到会议室之后我的手机就会自己进入会议模式,甚至变成省电模式。我还可以设置它当我到达特定的目的地时自动启动某个应用程序。
+
+### Android 让我失望的地方
+
+Android 备份选项仅限于特定的用户数据,而不是手机的完整克隆。如果不解锁 root,要么你只能听之任之,要么你必须用 Android SDK 来解决。期望普通用户会解锁 root 或运行 SDK 来完成备份(我的意思是一切都备份)显然是一个笑话。
+
+是的,谷歌的备份服务会备份谷歌应用程序的数据、以及其他相关的自定义设置。但它是远不及我们所看到的苹果服务一样完整。为了完成类似于苹果那样的功能,我发现你就要么必须解锁 root ,要么将其连接到一个在 PC 机上使用一些不知道是什么的软件来干这个。
+
+不过,公平的说,我知道使用 Nexus 的人能从该设备特有的[完整备份服务][6]中得到帮助。对不起,但是谷歌的默认备份方案是不行的。对于通过在 PC 上使用 adb (Android Debug Bridge) 来备份也是一样的——不会总是如预期般的恢复。
+
+等吧,它会变好的。经过了很多失败的失望和挫折之后,我发现有一个应用程序,看起来它“可能”提供了一点点微小的希望,它叫 Helium 。它不像我发现的其他应用程序那样拥有误导性的和令人沮丧的局限性,[Helium][7] 最初看起来像是谷歌应该一直提供的备份应用程序——注意,只是“看起来像”。可悲的是,它绊了我一跤。第一次运行时,我不仅需要将它连接到我的计算机上,甚至使用他们提供的 Linux 脚本都不工作。在删除他们的脚本后,我弄了一个很好的老式 adb 来备份到我的 Linux PC 上。你可能要知道的是:你需要在开发工具里打开一箩筐东西,而且如果你运行 Twilight 应用的话还要关闭它。当 adb 的备份选项在我的手机上不起作用时,我花了一点时间把这个搞好了。
+
+最后,Android 为非 root 用户也提供了可以轻松备份一些如联系人、短信等简单东西的选择。但是,要深度手机备份的话,以我经验还是通过有线连接和 adb。
+
+### Ubuntu 能拯救我们吗?
+
+在手机领域,通过对这两大玩家之间的优劣比较,我们很期望从 Ubuntu 看到更好的表现。但是,迄今为止,它的表现相当低迷。
+
+我喜欢开发人员基于这个操作系统正在做的那些努力,我当然想在 iPhone 和 Android 手机之外有第三种选择。但是不幸的是,它在手机和平板上并不受欢迎,而且由于硬件的低端以及在 YouTube 上的糟糕的演示,有很多不好的传闻。
+
+公平来说,我在以前也用过 iPhone 和 Android 的低端机,所以这不是对 Ubuntu 的挖苦。但是它要表现出准备与 iPhone 和 Android 相竞争的功能生态时,那就另说了,这还不是我现在特别感兴趣的东西。在以后的日子里,也许吧,我会觉得 Ubuntu 手机可以满足我的需要了。
+
+###Android 与 iPhone 之争:为什么 Android 将终究赢得胜利
+
+忽视 Android 那些痛苦的缺点,它起码给我了选择。它并没有把我限制在只有两种备份数据的方式上。是的,一些 Android 的限制事实上是由于它的关注点在于让我选择如何处理我的数据。但是,我可以选择我自己的设备,想加内存就加内存。Android 可以我让做很多很酷的东西,而 iPhone 根本就没有能力这些事情。
+
+从根本上来说, Android 给非 root 用户提供了访问手机功能的更大自由。无论是好是坏,这是人们想要的一种自由。现在你们其中有很多 iPhone 的粉丝应该感谢像 [libimobiledevice][8] 这样项目带来的影响。看看苹果阻止 Linux 用户所做的事情……然后问问你自己:作为一个 Linux 用户这真的值得吗?
+
+发表下评论吧,分享你对 iPhone 、Android 或 Ubuntu 的看法。
+
+------------------------------------------------------------------------------
+
+via: http://www.datamation.com/mobile-wireless/android-vs.-iphone-pros-and-cons.html
+
+作者:[Matt Hartley][a]
+译者:[jovov](https://github.com/jovov)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.datamation.com/author/Matt-Hartley-3080.html
+[1]: http://darkskyapp.com/
+[2]: https://www.facebook.com/paper/
+[3]: https://blog.vellumatlanta.com/2016/05/04/apple-stole-my-music-no-seriously/
+[4]: https://www.airdroid.com/
+[5]: http://tasker.dinglisch.net/
+[6]: https://support.google.com/nexus/answer/2819582?hl=en
+[7]: https://play.google.com/store/apps/details?id=com.koushikdutta.backup&hl=en
+[8]: http://www.libimobiledevice.org/
+
diff --git a/published/20160516 Linux will be the major operating system of 21st century cars.md b/published/20160516 Linux will be the major operating system of 21st century cars.md
new file mode 100644
index 0000000000..08a96d56c9
--- /dev/null
+++ b/published/20160516 Linux will be the major operating system of 21st century cars.md	
@@ -0,0 +1,39 @@
+Linux 将成为 21 世纪汽车的主要操作系统
+===============================================================
+
+> 汽车可不单单是由引擎和华丽外壳组成的,汽车里还有许许多多的计算部件,而 Linux 就在它们里面跑着。
+
+Linux 不只运行在你的服务器和手机(安卓)上。它还运行在你的车里。当然了,没人会因为某个车载系统而去买辆车。但是 Linux 已经为像丰田、日产、捷豹路虎这些大型汽车制造商提供了信息娱乐系统、平视显示以及其联网汽车(connected car)的 4G 与 Wi-Fi 系统,而且  [Linux 即将登陆福特汽车][1]、马自达、三菱、斯巴鲁。
+
+![](http://zdnet4.cbsistatic.com/hub/i/2016/05/10/743f0c14-6458-4d1e-8723-d2d94d0d0e69/c297b7d52e27e97d8721d4cb46bb371b/agl-logo.jpg)
+
+*如今,所有的 Linux 和开源汽车软件的成果都已经在 Linux 基金会的 Automotive Grade Linux (AGL)项目下统一标准化了。*
+
+传统软件公司也进入了移动物联网领域。 Movimento、甲骨文、高通、Texas Instruments、UIEvolution 和  VeriSilicon 都已经[加入 Automotive Grade Linux(AGL)项目][2]。 [AGL][3] 是一个相互协作的开源项目,志在于为联网汽车打造一个基于 Linux 的通用软件栈。
+
+“随着联网汽车技术和信息娱乐系统需求的快速增长,AGL 过去几年中得到了极大的发展,” Linux 基金会汽车总经理 Dan Cauchy 如是说。
+
+Cauchy 又补充道,“我们的会员基础不单单只是迅速壮大,而且通过横跨不同的业界实现了多元化,从半导体和车载软件到 IoT 和连接云服务。这是一个明显的迹象,即联网汽车的革命已经间接影响到许多行业纵向市场。”
+
+这些公司在 AGL 发布了新的 AGL Unified Code Base (UCB) 之后加入了 AGL 项目。这个新的 Linux 发行版基于 AGL 和另外两个汽车开源项目: [Tizen][4] 和 [GENIVI Alliance][5] 。 UCB 是第二代 Linux 汽车系统。它从底层开始开发,一直到特定的汽车应用软件。它能处理导航、通信、安全、安保和信息娱乐系统。
+
+“汽车行业需要一个标准的开源系统和框架来让汽车制造商和供应商能够快速地将类似智能手机的功能带入到汽车中来。” Cauchy 说。“这个新的发行版将 AGL、Tizen、GENIVI 项目和相关开源代码中的精华部分整合进 AGL Unified Code Base (UCB)中,使得汽车制造商能够利用一个通用平台进行快速创新。 在汽车中采用基于 Linux 的系统来实现所有功能时, AGL 的 UCB 发行版将扮演一个重大的角色。”
+
+他说得对。自从 2016 年 1 月发布以来,已有四个汽车公司和十个新的软件厂商加入了 AGL。Esso,如今的 Exxon, 曾让 “把老虎装入油箱” 这条广告语出了名。我怀疑 “把企鹅装到引擎盖下” 这样的广告语是否也会变得家喻户晓,但是它却道出了事实。 Linux 正在慢慢成为 21 世纪汽车的主要操作系统。
+
+------------------------------------------------------------------------------
+
+via: http://www.zdnet.com/article/the-linux-in-your-car-movement-gains-momentum/
+
+作者:[Steven J. Vaughan-Nichols][a]
+译者:[XLCYun](https://github.com/XLCYun)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]:http://www.zdnet.com/meet-the-team/us/steven-j-vaughan-nichols/
+[1]: https://www.automotivelinux.org/news/announcement/2016/01/ford-mazda-mitsubishi-motors-and-subaru-join-linux-foundation-and
+[2]: https://www.automotivelinux.org/news/announcement/2016/05/oracle-qualcomm-innovation-center-texas-instruments-and-others-support
+[3]: https://www.automotivelinux.org/
+[4]: https://www.tizen.org/
+[5]: http://www.genivi.org/
diff --git a/published/20160524 Writing online multiplayer game with python and asyncio - part 1.md b/published/20160524 Writing online multiplayer game with python and asyncio - part 1.md
new file mode 100644
index 0000000000..4bc91af85f
--- /dev/null
+++ b/published/20160524 Writing online multiplayer game with python and asyncio - part 1.md	
@@ -0,0 +1,74 @@
+使用 Python 和 Asyncio 编写在线多人游戏(一)
+===================================================================
+
+你在 Python 中用过异步编程吗?本文中我会告诉你怎样做,而且用一个[能工作的例子][1]来展示它:这是一个流行的贪吃蛇游戏,而且是为多人游戏而设计的。
+
+- [游戏入口在此,点此体验][2]。
+
+###1、简介
+
+在技术和文化领域,大规模多人在线游戏(MMO)毋庸置疑是我们当今世界的潮流之一。很长时间以来,为一个 MMO 游戏写一个服务器这件事总是会涉及到大量的预算与复杂的底层编程技术,不过在最近这几年,事情迅速发生了变化。基于动态语言的现代框架允许在中档的硬件上面处理大量并发的用户连接。同时,HTML5 和 WebSockets 标准使得创建基于实时图形的游戏的直接运行至浏览器上的客户端成为可能,而不需要任何的扩展。
+
+对于创建可扩展的非堵塞性的服务器来说,Python 可能不是最受欢迎的工具,尤其是和在这个领域里最受欢迎的 Node.js 相比而言。但是最近版本的 Python 正在改变这种现状。[asyncio][3] 的引入和一个特别的 [async/await][4] 语法使得异步代码看起来像常规的阻塞代码一样,这使得 Python 成为了一个值得信赖的异步编程语言,所以我将尝试利用这些新特点来创建一个多人在线游戏。
+
+###2、异步
+
+一个游戏服务器应该可以接受尽可能多的用户并发连接,并实时处理这些连接。一个典型的解决方案是创建线程,然而在这种情况下并不能解决这个问题。运行上千的线程需要 CPU 在它们之间不停的切换(这叫做上下文切换),这将导致开销非常大,效率很低下。更糟糕的是使用进程来实现,因为,不但如此,它们还会占用大量的内存。在 Python 中,甚至还有一个问题,Python 的解释器(CPython)并不是针对多线程设计的,相反它主要针对于单线程应用实现最大的性能。这就是为什么它使用 GIL(global interpreter lock),这是一个不允许同时运行多线程 Python 代码的架构,以防止同一个共享对象出现使用不可控。正常情况下,在当前线程正在等待的时候,解释器会转换到另一个线程,通常是等待一个 I/O 的响应(举例说,比如等待 Web 服务器的响应)。这就允许在你的应用中实现非阻塞 I/O 操作,因为每一个操作仅仅阻塞一个线程而不是阻塞整个服务器。然而,这也使得通常的多线程方案变得几近无用,因为它不允许你并发执行 Python 代码,即使是在多核心的 CPU 上也是这样。而与此同时,在一个单一线程中拥有非阻塞 I/O 是完全有可能的,因而消除了经常切换上下文的需要。
+
+实际上,你可以用纯 Python 代码来实现一个单线程的非阻塞 I/O。你所需要的只是标准的 [select][5] 模块,这个模块可以让你写一个事件循环来等待未阻塞的 socket 的 I/O。然而,这个方法需要你在一个地方定义所有 app 的逻辑,用不了多久,你的 app 就会变成非常复杂的状态机。有一些框架可以简化这个任务,比较流行的是 [tornade][6] 和 [twisted][7]。它们被用来使用回调方法实现复杂的协议(这和 Node.js 比较相似)。这种框架运行在它自己的事件循环中,按照定义的事件调用你的回调函数。并且,这或许是一些情况的解决方案,但是它仍然需要使用回调的方式编程,这使你的代码变得碎片化。与写同步代码并且并发地执行多个副本相比,这就像我们在普通的线程上做的一样。在单个线程上这为什么是不可能的呢?
+
+这就是为什么出现微线程(microthread)概念的原因。这个想法是为了在一个线程上并发执行任务。当你在一个任务中调用阻塞的方法时,有一个叫做“manager” (或者“scheduler”)的东西在执行事件循环。当有一些事件准备处理的时候,一个 manager 会转移执行权给一个任务,并等着它执行完毕。任务将一直执行,直到它遇到一个阻塞调用,然后它就会将执行权返还给 manager。
+
+> 微线程也称为轻量级线程(lightweight threads)或绿色线程(green threads)(来自于 Java 中的一个术语)。在伪线程中并发执行的任务叫做 tasklets、greenlets 或者协程(coroutines)。
+
+ Python 中的微线程最早的实现之一是 [Stackless Python][8]。它之所以这么知名是因为它被用在了一个叫 [EVE online][9] 的非常有名的在线游戏中。这个 MMO 游戏自称说在一个持久的“宇宙”中,有上千个玩家在做不同的活动,这些都是实时发生的。Stackless 是一个独立的 Python 解释器,它代替了标准的函数栈调用,并且直接控制程序运行流程来减少上下文切换的开销。尽管这非常有效,这个解决方案不如在标准解释器中使用“软”库更流行,像 [eventlet][10] 和 [gevent][11] 的软件包配备了修补过的标准 I/O 库,I/O 函数会将执行权传递到内部事件循环。这使得将正常的阻塞代码转变成非阻塞的代码变得简单。这种方法的一个缺点是从代码上看这并不分明,它的调用是非阻塞的。新版本的 Python 引入了本地协程作为生成器的高级形式。在 Python 的 3.4 版本之后,引入了 asyncio 库,这个库依赖于本地协程来提供单线程并发。但是仅仅到了 Python  3.5 ,协程就变成了 Python 语言的一部分,使用新的关键字 async 和 await 来描述。这是一个简单的例子,演示了使用 asyncio 来运行并发任务。
+
+```
+import asyncio
+
+async def my_task(seconds):
+    print("start sleeping for {} seconds".format(seconds))
+    await asyncio.sleep(seconds)
+    print("end sleeping for {} seconds".format(seconds))
+
+all_tasks = asyncio.gather(my_task(1), my_task(2))
+loop = asyncio.get_event_loop()
+loop.run_until_complete(all_tasks)
+loop.close()    
+```
+
+我们启动了两个任务,一个睡眠 1 秒钟,另一个睡眠 2 秒钟,输出如下:
+
+```
+start sleeping for 1 seconds
+start sleeping for 2 seconds
+end sleeping for 1 seconds
+end sleeping for 2 seconds
+```
+
+正如你所看到的,协程不会阻塞彼此——第二个任务在第一个结束之前启动。这发生的原因是 asyncio.sleep 是协程,它会返回执行权给调度器,直到时间到了。
+
+在下一节中,我们将会使用基于协程的任务来创建一个游戏循环。
+
+--------------------------------------------------------------------------------
+
+via: https://7webpages.com/blog/writing-online-multiplayer-game-with-python-asyncio-getting-asynchronous/
+
+作者:[Kyrylo Subbotin][a]
+译者:[xinglianfly](https://github.com/xinglianfly)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://7webpages.com/blog/writing-online-multiplayer-game-with-python-asyncio-getting-asynchronous/
+[1]: http://snakepit-game.com/
+[2]: http://snakepit-game.com/
+[3]: https://docs.python.org/3/library/asyncio.html
+[4]: https://docs.python.org/3/whatsnew/3.5.html#whatsnew-pep-492
+[5]: https://docs.python.org/2/library/select.html
+[6]: http://www.tornadoweb.org/
+[7]: http://twistedmatrix.com/
+[8]: http://www.stackless.com/
+[9]: http://www.eveonline.com/
+[10]: http://eventlet.net/
+[11]: http://www.gevent.org/
diff --git a/translated/tech/20160527 A Python Interpreter Written in Python.md b/published/20160527 A Python Interpreter Written in Python.md
similarity index 53%
rename from translated/tech/20160527 A Python Interpreter Written in Python.md
rename to published/20160527 A Python Interpreter Written in Python.md
index dbf8dc7cf4..18c2feedd7 100644
--- a/translated/tech/20160527 A Python Interpreter Written in Python.md	
+++ b/published/20160527 A Python Interpreter Written in Python.md	
@@ -1,37 +1,37 @@
-用Python实现Python解释器
+用 Python 实现 Python 解释器
 ===
 
-_Allison是Dropbox的工程师,在那里她维护着世界上最大的由Python客户组成的网络。在Dropbox之前,她是Recurse Center的导师, 曾在纽约写作。在北美的PyCon做过关于Python内部机制的演讲,并且她喜欢奇怪的bugs。她的博客地址是[akaptur.com](http://akaptur.com)._
+_Allison 是 Dropbox 的工程师,在那里她维护着这个世界上最大的 Python 客户端网络之一。在去 Dropbox 之前,她是 Recurse Center 的协调人, 是这个位于纽约的程序员深造机构的作者。她在北美的 PyCon 做过关于 Python 内部机制的演讲,并且她喜欢研究奇怪的 bug。她的博客地址是 [akaptur.com](http://akaptur.com)。_
 
-## Introduction
+### 介绍
 
-Byterun是一个用Python实现的Python解释器。随着我在Byterun上的工作,我惊讶并很高兴地的发现,这个Python解释器的基础结构可以满足500行的限制。在这一章我们会搞清楚这个解释器的结构,给你足够的知识探索下去。我们的目标不是向你展示解释器的每个细节---像编程和计算机科学其他有趣的领域一样,你可能会投入几年的时间去搞清楚这个主题。
+Byterun 是一个用 Python 实现的 Python 解释器。随着我对 Byterun 的开发,我惊喜地的发现,这个 Python 解释器的基础结构用 500 行代码就能实现。在这一章我们会搞清楚这个解释器的结构,给你足够探索下去的背景知识。我们的目标不是向你展示解释器的每个细节---像编程和计算机科学其他有趣的领域一样,你可能会投入几年的时间去深入了解这个主题。
 
-Byterun是Ned Batchelder和我完成的,建立在Paul Swartz的工作之上。它的结构和主要的Python实现(CPython)差不多,所以理解Byterun会帮助你理解大多数解释器特别是CPython解释器。(如果你不知道你用的是什么Python,那么很可能它就是CPython)。尽管Byterun很小,但它能执行大多数简单的Python程序。
+Byterun 是 Ned Batchelder 和我完成的,建立在 Paul Swartz 的工作之上。它的结构和主要的 Python 实现(CPython)差不多,所以理解 Byterun 会帮助你理解大多数解释器,特别是 CPython 解释器。(如果你不知道你用的是什么 Python,那么很可能它就是 CPython)。尽管 Byterun 很小,但它能执行大多数简单的 Python 程序(这一章是基于 Python 3.5 及其之前版本生成的字节码的,在 Python 3.6 中生成的字节码有一些改变)。
 
-### A Python Interpreter
+#### Python 解释器
 
-在开始之前,让我们缩小一下“Pyhton解释器”的意思。在讨论Python的时候,“解释器”这个词可以用在很多不同的地方。有的时候解释器指的是REPL,当你在命令行下敲下`python`时所得到的交互式环境。有时候人们会相互替代的使用Python解释器和Python来说明执行Python代码的这一过程。在本章,“解释器”有一个更精确的意思:执行Python程序过程中的最后一步。
+在开始之前,让我们限定一下“Pyhton 解释器”的意思。在讨论 Python 的时候,“解释器”这个词可以用在很多不同的地方。有的时候解释器指的是 Python REPL,即当你在命令行下敲下 `python` 时所得到的交互式环境。有时候人们会或多或少的互换使用 “Python 解释器”和“Python”来说明从头到尾执行 Python 代码的这一过程。在本章中,“解释器”有一个更精确的意思:Python 程序的执行过程中的最后一步。
 
-在解释器接手之前,Python会执行其他3个步骤:词法分析,语法解析和编译。这三步合起来把源代码转换成_code object_,它包含着解释器可以理解的指令。而解释器的工作就是解释code object中的指令。
+在解释器接手之前,Python 会执行其他 3 个步骤:词法分析,语法解析和编译。这三步合起来把源代码转换成代码对象(code object),它包含着解释器可以理解的指令。而解释器的工作就是解释代码对象中的指令。
 
-你可能很奇怪执行Python代码会有编译这一步。Python通常被称为解释型语言,就像Ruby,Perl一样,它们和编译型语言相对,比如C,Rust。然而,这里的术语并不是它看起来的那样精确。大多数解释型语言包括Python,确实会有编译这一步。而Python被称为解释型的原因是相对于编译型语言,它在编译这一步的工作相对较少(解释器做相对多的工作)。在这章后面你会看到,Python的编译器比C语言编译器需要更少的关于程序行为的信息。
+你可能很奇怪执行 Python 代码会有编译这一步。Python 通常被称为解释型语言,就像 Ruby,Perl 一样,它们和像 C,Rust 这样的编译型语言相对。然而,这个术语并不是它看起来的那样精确。大多数解释型语言包括 Python 在内,确实会有编译这一步。而 Python 被称为解释型的原因是相对于编译型语言,它在编译这一步的工作相对较少(解释器做相对多的工作)。在这章后面你会看到,Python 的编译器比 C 语言编译器需要更少的关于程序行为的信息。
 
-### A Python Python Interpreter
+#### Python 的 Python 解释器
 
-Byterun是一个用Python写的Python解释器,这点可能让你感到奇怪,但没有比用C语言写C语言编译器更奇怪。(事实上,广泛使用的gcc编译器就是用C语言本身写的)你可以用几乎的任何语言写一个Python解释器。
+Byterun 是一个用 Python 写的 Python 解释器,这点可能让你感到奇怪,但没有比用 C 语言写 C 语言编译器更奇怪的了。(事实上,广泛使用的 gcc 编译器就是用 C 语言本身写的)你可以用几乎任何语言写一个 Python 解释器。
 
-用Python写Python既有优点又有缺点。最大的缺点就是速度:用Byterun执行代码要比用CPython执行慢的多,CPython解释器是用C语言实现的并做了优化。然而Byterun是为了学习而设计的,所以速度对我们不重要。使用Python最大优点是我们可以*仅仅*实现解释器,而不用担心Python运行时的部分,特别是对象系统。比如当Byterun需要创建一个类时,它就会回退到“真正”的Python。另外一个优势是Byterun很容易理解,部分原因是它是用高级语言写的(Python!)(另外我们不会对解释器做优化 --- 再一次,清晰和简单比速度更重要)
+用 Python 写 Python 既有优点又有缺点。最大的缺点就是速度:用 Byterun 执行代码要比用 CPython 执行慢的多,CPython 解释器是用 C 语言实现的,并做了认真优化。然而 Byterun 是为了学习而设计的,所以速度对我们不重要。使用 Python 最大优势是我们可以*仅仅*实现解释器,而不用担心 Python 运行时部分,特别是对象系统。比如当 Byterun 需要创建一个类时,它就会回退到“真正”的 Python。另外一个优势是 Byterun 很容易理解,部分原因是它是用人们很容易理解的高级语言写的(Python !)(另外我们不会对解释器做优化 --- 再一次,清晰和简单比速度更重要)
 
-## Building an Interpreter
+### 构建一个解释器
 
-在我们考察Byterun代码之前,我们需要一些对解释器结构的高层次视角。Python解释器是如何工作的?
+在我们考察 Byterun 代码之前,我们需要从高层次对解释器结构有一些了解。Python 解释器是如何工作的?
 
-Python解释器是一个_虚拟机_,模拟真实计算机的软件。我们这个虚拟机是栈机器,它用几个栈来完成操作(与之相对的是寄存器机器,它从特定的内存地址读写数据)。
+Python 解释器是一个虚拟机(virtual machine),是一个模拟真实计算机的软件。我们这个虚拟机是栈机器(stack machine),它用几个栈来完成操作(与之相对的是寄存器机器(register machine),它从特定的内存地址读写数据)。
 
-Python解释器是一个_字节码解释器_:它的输入是一些命令集合称作_字节码_。当你写Python代码时,词法分析器,语法解析器和编译器生成code object让解释器去操作。每个code object都包含一个要被执行的指令集合 --- 它就是字节码 --- 另外还有一些解释器需要的信息。字节码是Python代码的一个_中间层表示_:它以一种解释器可以理解的方式来表示源代码。这和汇编语言作为C语言和机器语言的中间表示很类似。
+Python 解释器是一个字节码解释器(bytecode interpreter):它的输入是一些称作字节码(bytecode)的指令集。当你写 Python 代码时,词法分析器、语法解析器和编译器会生成代码对象(code object)让解释器去操作。每个代码对象都包含一个要被执行的指令集 —— 它就是字节码 —— 以及还有一些解释器需要的信息。字节码是 Python 代码的一个中间层表示( intermediate representation):它以一种解释器可以理解的方式来表示源代码。这和汇编语言作为 C 语言和机器语言的中间表示很类似。
 
-### A Tiny Interpreter
+#### 微型解释器
 
 为了让说明更具体,让我们从一个非常小的解释器开始。它只能计算两个数的和,只能理解三个指令。它执行的所有代码只是这三个指令的不同组合。下面就是这三个指令:
 
@@ -39,7 +39,7 @@ Python解释器是一个_字节码解释器_:它的输入是一些命令集合
 - `ADD_TWO_VALUES`
 - `PRINT_ANSWER`
 
-我们不关心词法,语法和编译,所以我们也不在乎这些指令是如何产生的。你可以想象,你写下`7 + 5`,然后一个编译器为你生成那三个指令的组合。如果你有一个合适的编译器,你甚至可以用Lisp的语法来写,只要它能生成相同的指令。
+我们不关心词法、语法和编译,所以我们也不在乎这些指令集是如何产生的。你可以想象,当你写下 `7 + 5`,然后一个编译器为你生成那三个指令的组合。如果你有一个合适的编译器,你甚至可以用 Lisp 的语法来写,只要它能生成相同的指令。
 
 假设
 
@@ -58,13 +58,13 @@ what_to_execute = {
     "numbers": [7, 5] }
 ```
 
-Python解释器是一个_栈机器_,所以它必须通过操作栈来完成这个加法。(\aosafigref{500l.interpreter.stackmachine}.)解释器先执行第一条指令,`LOAD_VALUE`,把第一个数压到栈中。接着它把第二个数也压到栈中。然后,第三条指令,`ADD_TWO_VALUES`,先把两个数从栈中弹出,加起来,再把结果压入栈中。最后一步,把结果弹出并输出。
+Python 解释器是一个栈机器(stack machine),所以它必须通过操作栈来完成这个加法(见下图)。解释器先执行第一条指令,`LOAD_VALUE`,把第一个数压到栈中。接着它把第二个数也压到栈中。然后,第三条指令,`ADD_TWO_VALUES`,先把两个数从栈中弹出,加起来,再把结果压入栈中。最后一步,把结果弹出并输出。
 
-\aosafigure[240pt]{interpreter-images/interpreter-stack.png}{A stack machine}{500l.interpreter.stackmachine}
+![栈机器](http://aosabook.org/en/500L/interpreter-images/interpreter-stack.png)
 
-`LOAD_VALUE`这条指令告诉解释器把一个数压入栈中,但指令本身并没有指明这个数是多少。指令需要一个额外的信息告诉解释器去哪里找到这个数。所以我们的指令集有两个部分:指令本身和一个常量列表。(在Python中,字节码就是我们称为的“指令”,而解释器执行的是_code object_。)
+`LOAD_VALUE`这条指令告诉解释器把一个数压入栈中,但指令本身并没有指明这个数是多少。指令需要一个额外的信息告诉解释器去哪里找到这个数。所以我们的指令集有两个部分:指令本身和一个常量列表。(在 Python 中,字节码就是我们所称的“指令”,而解释器“执行”的是代码对象。)
 
-为什么不把数字直接嵌入指令之中?想象一下,如果我们加的不是数字,而是字符串。我们可不想把字符串这样的东西加到指令中,因为它可以有任意的长度。另外,我们这种设计也意味着我们只需要对象的一份拷贝,比如这个加法 `7 + 7`, 现在常量表 `"numbers"`只需包含一个`7`。
+为什么不把数字直接嵌入指令之中?想象一下,如果我们加的不是数字,而是字符串。我们可不想把字符串这样的东西加到指令中,因为它可以有任意的长度。另外,我们这种设计也意味着我们只需要对象的一份拷贝,比如这个加法 `7 + 7`, 现在常量表 `"numbers"`只需包含一个`[7]`。
 
 你可能会想为什么会需要除了`ADD_TWO_VALUES`之外的指令。的确,对于我们两个数加法,这个例子是有点人为制作的意思。然而,这个指令却是建造更复杂程序的轮子。比如,就我们目前定义的三个指令,只要给出正确的指令组合,我们可以做三个数的加法,或者任意个数的加法。同时,栈提供了一个清晰的方法去跟踪解释器的状态,这为我们增长的复杂性提供了支持。
 
@@ -89,7 +89,7 @@ class Interpreter:
         self.stack.append(total)
 ```
 
-这三个方法完成了解释器所理解的三条指令。但解释器还需要一样东西:一个能把所有东西结合在一起并执行的方法。这个方法就叫做`run_code`, 它把我们前面定义的字典结构`what-to-execute`作为参数,循环执行里面的每条指令,如何指令有参数,处理参数,然后调用解释器对象中相应的方法。
+这三个方法完成了解释器所理解的三条指令。但解释器还需要一样东西:一个能把所有东西结合在一起并执行的方法。这个方法就叫做 `run_code`,它把我们前面定义的字典结构 `what-to-execute` 作为参数,循环执行里面的每条指令,如果指令有参数就处理参数,然后调用解释器对象中相应的方法。
 
 ```python
     def run_code(self, what_to_execute):
@@ -106,20 +106,20 @@ class Interpreter:
                 self.PRINT_ANSWER()
 ```
 
-为了测试,我们创建一个解释器对象,然后用前面定义的 7 + 5 的指令集来调用`run_code`。
+为了测试,我们创建一个解释器对象,然后用前面定义的 7 + 5 的指令集来调用 `run_code`。
 
 ```python
     interpreter = Interpreter()
     interpreter.run_code(what_to_execute)
 ```
 
-显然,它会输出12
+显然,它会输出12。
 
-尽管我们的解释器功能受限,但这个加法过程几乎和真正的Python解释器是一样的。这里,我们还有几点要注意。
+尽管我们的解释器功能十分受限,但这个过程几乎和真正的 Python 解释器处理加法是一样的。这里,我们还有几点要注意。
 
-首先,一些指令需要参数。在真正的Python bytecode中,大概有一半的指令有参数。像我们的例子一样,参数和指令打包在一起。注意_指令_的参数和传递给对应方法的参数是不同的。
+首先,一些指令需要参数。在真正的 Python 字节码当中,大概有一半的指令有参数。像我们的例子一样,参数和指令打包在一起。注意指令的参数和传递给对应方法的参数是不同的。
 
-第二,指令`ADD_TWO_VALUES`不需要任何参数,它从解释器栈中弹出所需的值。这正是以栈为基础的解释器的特点。
+第二,指令`ADD_TWO_VALUES`不需要任何参数,它从解释器栈中弹出所需的值。这正是以基于栈的解释器的特点。
 
 记得我们说过只要给出合适的指令集,不需要对解释器做任何改变,我们就能做多个数的加法。考虑下面的指令集,你觉得会发生什么?如果你有一个合适的编译器,什么代码才能编译出下面的指令集?
 
@@ -134,11 +134,11 @@ class Interpreter:
         "numbers": [7, 5, 8] }
 ```
 
-从这点出发,我们开始看到这种结构的可扩展性:我们可以通过向解释器对象增加方法来描述更多的操作(只要有一个编译器能为我们生成组织良好的指令集)。
+从这点出发,我们开始看到这种结构的可扩展性:我们可以通过向解释器对象增加方法来描述更多的操作(只要有一个编译器能为我们生成组织良好的指令集就行)。
 
-#### Variables
+##### 变量
 
-接下来给我们的解释器增加变量的支持。我们需要一个保存变量值的指令,`STORE_NAME`;一个取变量值的指令`LOAD_NAME`;和一个变量到值的映射关系。目前,我们会忽略命名空间和作用域,所以我们可以把变量和值的映射直接存储在解释器对象中。最后,我们要保证`what_to_execute`除了一个常量列表,还要有个变量名字的列表。
+接下来给我们的解释器增加变量的支持。我们需要一个保存变量值的指令 `STORE_NAME`;一个取变量值的指令`LOAD_NAME`;和一个变量到值的映射关系。目前,我们会忽略命名空间和作用域,所以我们可以把变量和值的映射直接存储在解释器对象中。最后,我们要保证`what_to_execute`除了一个常量列表,还要有个变量名字的列表。
 
 ```python
 >>> def s():
@@ -159,9 +159,9 @@ class Interpreter:
         "names":   ["a", "b"] }
 ```
 
-我们的新的的实现在下面。为了跟踪哪名字绑定到那个值,我们在`__init__`方法中增加一个`environment`字典。我们也增加了`STORE_NAME`和`LOAD_NAME`方法,它们获得变量名,然后从`environment`字典中设置或取出这个变量值。
+我们的新的实现在下面。为了跟踪哪个名字绑定到哪个值,我们在`__init__`方法中增加一个`environment`字典。我们也增加了`STORE_NAME`和`LOAD_NAME`方法,它们获得变量名,然后从`environment`字典中设置或取出这个变量值。
 
-现在指令参数就有两个不同的意思,它可能是`numbers`列表的索引,也可能是`names`列表的索引。解释器通过检查所执行的指令就能知道是那种参数。而我们打破这种逻辑 ,把指令和它所用何种参数的映射关系放在另一个单独的方法中。
+现在指令的参数就有两个不同的意思,它可能是`numbers`列表的索引,也可能是`names`列表的索引。解释器通过检查所执行的指令就能知道是那种参数。而我们打破这种逻辑 ,把指令和它所用何种参数的映射关系放在另一个单独的方法中。
 
 ```python
 class Interpreter:
@@ -207,7 +207,7 @@ class Interpreter:
                 self.LOAD_NAME(argument)
 ```
 
-仅仅五个指令,`run_code`这个方法已经开始变得冗长了。如果保持这种结构,那么每条指令都需要一个`if`分支。这里,我们要利用Python的动态方法查找。我们总会给一个称为`FOO`的指令定义一个名为`FOO`的方法,这样我们就可用Python的`getattr`函数在运行时动态查找方法,而不用这个大大的分支结构。`run_code`方法现在是这样:
+仅仅五个指令,`run_code`这个方法已经开始变得冗长了。如果保持这种结构,那么每条指令都需要一个`if`分支。这里,我们要利用 Python 的动态方法查找。我们总会给一个称为`FOO`的指令定义一个名为`FOO`的方法,这样我们就可用 Python 的`getattr`函数在运行时动态查找方法,而不用这个大大的分支结构。`run_code`方法现在是这样:
 
 ```python
     def execute(self, what_to_execute):
@@ -222,9 +222,9 @@ class Interpreter:
                 bytecode_method(argument)
 ```
 
-## Real Python Bytecode
+### 真实的 Python 字节码
 
-现在,放弃我们的小指令集,去看看真正的Python字节码。字节码的结构和我们的小解释器的指令集差不多,除了字节码用一个字节而不是一个名字来指示这条指令。为了理解它的结构,我们将考察一个函数的字节码。考虑下面这个例子:
+现在,放弃我们的小指令集,去看看真正的 Python 字节码。字节码的结构和我们的小解释器的指令集差不多,除了字节码用一个字节而不是一个名字来代表这条指令。为了理解它的结构,我们将考察一个函数的字节码。考虑下面这个例子:
 
 ```python
 >>> def cond():
@@ -236,7 +236,7 @@ class Interpreter:
 ...
 ```
 
-Python在运行时会暴露一大批内部信息,并且我们可以通过REPL直接访问这些信息。对于函数对象`cond`,`cond.__code__`是与其关联的code object,而`cond.__code__.co_code`就是它的字节码。当你写Python代码时,你永远也不会想直接使用这些属性,但是这可以让我们做出各种恶作剧,同时也可以看看内部机制。
+Python 在运行时会暴露一大批内部信息,并且我们可以通过 REPL 直接访问这些信息。对于函数对象`cond`,`cond.__code__`是与其关联的代码对象,而`cond.__code__.co_code`就是它的字节码。当你写 Python 代码时,你永远也不会想直接使用这些属性,但是这可以让我们做出各种恶作剧,同时也可以看看内部机制。
 
 ```python
 >>> cond.__code__.co_code  # the bytecode as raw bytes
@@ -247,9 +247,9 @@ b'd\x01\x00}\x00\x00|\x00\x00d\x02\x00k\x00\x00r\x16\x00d\x03\x00Sd\x04\x00Sd\x0
  100, 4, 0, 83, 100, 0, 0, 83]
 ```
 
-当我们直接输出这个字节码,它看起来完全无法理解 --- 唯一我们了解的是它是一串字节。很幸运,我们有一个很强大的工具可以用:Python标准库中的`dis`模块。
+当我们直接输出这个字节码,它看起来完全无法理解 —— 唯一我们了解的是它是一串字节。很幸运,我们有一个很强大的工具可以用:Python 标准库中的`dis`模块。
 
-`dis`是一个字节码反汇编器。反汇编器以为机器而写的底层代码作为输入,比如汇编代码和字节码,然后以人类可读的方式输出。当我们运行`dis.dis`, 它输出每个字节码的解释。
+`dis`是一个字节码反汇编器。反汇编器以为机器而写的底层代码作为输入,比如汇编代码和字节码,然后以人类可读的方式输出。当我们运行`dis.dis`,它输出每个字节码的解释。
 
 ```python
 >>> dis.dis(cond)
@@ -270,9 +270,9 @@ b'd\x01\x00}\x00\x00|\x00\x00d\x02\x00k\x00\x00r\x16\x00d\x03\x00Sd\x04\x00Sd\x0
              29 RETURN_VALUE
 ```
 
-这些都是什么意思?让我们以第一条指令`LOAD_CONST`为例子。第一列的数字(`2`)表示对应源代码的行数。第二列的数字是字节码的索引,告诉我们指令`LOAD_CONST`在0位置。第三列是指令本身对应的人类可读的名字。如果第四列存在,它表示指令的参数。如果第5列存在,它是一个关于参数是什么的提示。
+这些都是什么意思?让我们以第一条指令`LOAD_CONST`为例子。第一列的数字(`2`)表示对应源代码的行数。第二列的数字是字节码的索引,告诉我们指令`LOAD_CONST`在位置 0 。第三列是指令本身对应的人类可读的名字。如果第四列存在,它表示指令的参数。如果第五列存在,它是一个关于参数是什么的提示。
 
-考虑这个字节码的前几个字节:[100, 1, 0, 125, 0, 0]。这6个字节表示两条带参数的指令。我们可以使用`dis.opname`,一个字节到可读字符串的映射,来找到指令100和指令125代表是什么:
+考虑这个字节码的前几个字节:[100, 1, 0, 125, 0, 0]。这 6 个字节表示两条带参数的指令。我们可以使用`dis.opname`,一个字节到可读字符串的映射,来找到指令 100 和指令 125 代表的是什么:
 
 ```python
 >>> dis.opname[100]
@@ -281,11 +281,11 @@ b'd\x01\x00}\x00\x00|\x00\x00d\x02\x00k\x00\x00r\x16\x00d\x03\x00Sd\x04\x00Sd\x0
 'STORE_FAST'
 ```
 
-第二和第三个字节 --- 1 ,0 ---是`LOAD_CONST`的参数,第五和第六个字节 --- 0,0 --- 是`STORE_FAST`的参数。就像我们前面的小例子,`LOAD_CONST`需要知道的到哪去找常量,`STORE_FAST`需要找到名字。(Python的`LOAD_CONST`和我们小例子中的`LOAD_VALUE`一样,`LOAD_FAST`和`LOAD_NAME`一样)。所以这六个字节代表第一行源代码`x = 3`.(为什么用两个字节表示指令的参数?如果Python使用一个字节,每个code object你只能有256个常量/名字,而用两个字节,就增加到了256的平方,65536个)。
+第二和第三个字节 —— 1 、0 ——是`LOAD_CONST`的参数,第五和第六个字节 —— 0、0 —— 是`STORE_FAST`的参数。就像我们前面的小例子,`LOAD_CONST`需要知道的到哪去找常量,`STORE_FAST`需要知道要存储的名字。(Python 的`LOAD_CONST`和我们小例子中的`LOAD_VALUE`一样,`LOAD_FAST`和`LOAD_NAME`一样)。所以这六个字节代表第一行源代码`x = 3` (为什么用两个字节表示指令的参数?如果 Python 使用一个字节,每个代码对象你只能有 256 个常量/名字,而用两个字节,就增加到了 256 的平方,65536个)。
 
-### Conditionals and Loops
+#### 条件语句与循环语句
 
-到目前为止,我们的解释器只能一条接着一条的执行指令。这有个问题,我们经常会想多次执行某个指令,或者在特定的条件下跳过它们。为了可以写循环和分支结构,解释器必须能够在指令中跳转。在某种程度上,Python在字节码中使用`GOTO`语句来处理循环和分支!让我们再看一个`cond`函数的反汇编结果:
+到目前为止,我们的解释器只能一条接着一条的执行指令。这有个问题,我们经常会想多次执行某个指令,或者在特定的条件下跳过它们。为了可以写循环和分支结构,解释器必须能够在指令中跳转。在某种程度上,Python 在字节码中使用`GOTO`语句来处理循环和分支!让我们再看一个`cond`函数的反汇编结果:
 
 ```python
 >>> dis.dis(cond)
@@ -306,11 +306,11 @@ b'd\x01\x00}\x00\x00|\x00\x00d\x02\x00k\x00\x00r\x16\x00d\x03\x00Sd\x04\x00Sd\x0
              29 RETURN_VALUE
 ```
 
-第三行的条件表达式`if x < 5`被编译成四条指令:`LOAD_FAST`, `LOAD_CONST`, `COMPARE_OP`和 `POP_JUMP_IF_FALSE`。`x < 5`对应加载`x`,加载5,比较这两个值。指令`POP_JUMP_IF_FALSE`完成`if`语句。这条指令把栈顶的值弹出,如果值为真,什么都不发生。如果值为假,解释器会跳转到另一条指令。
+第三行的条件表达式`if x < 5`被编译成四条指令:`LOAD_FAST`、 `LOAD_CONST`、 `COMPARE_OP`和 `POP_JUMP_IF_FALSE`。`x < 5`对应加载`x`、加载 5、比较这两个值。指令`POP_JUMP_IF_FALSE`完成这个`if`语句。这条指令把栈顶的值弹出,如果值为真,什么都不发生。如果值为假,解释器会跳转到另一条指令。
 
-这条将被加载的指令称为跳转目标,它作为指令`POP_JUMP`的参数。这里,跳转目标是22,索引为22的指令是`LOAD_CONST`,对应源码的第6行。(`dis`用`>>`标记跳转目标。)如果`X < 5`为假,解释器会忽略第四行(`return yes`),直接跳转到第6行(`return "no"`)。因此解释器通过跳转指令选择性的执行指令。
+这条将被加载的指令称为跳转目标,它作为指令`POP_JUMP`的参数。这里,跳转目标是 22,索引为 22 的指令是`LOAD_CONST`,对应源码的第 6 行。(`dis`用`>>`标记跳转目标。)如果`X < 5`为假,解释器会忽略第四行(`return yes`),直接跳转到第6行(`return "no"`)。因此解释器通过跳转指令选择性的执行指令。
 
-Python的循环也依赖于跳转。在下面的字节码中,`while x < 5`这一行产生了和`if x < 10`几乎一样的字节码。在这两种情况下,解释器都是先执行比较,然后执行`POP_JUMP_IF_FALSE`来控制下一条执行哪个指令。第四行的最后一条字节码`JUMP_ABSOLUT`(循环体结束的地方),让解释器返回到循环开始的第9条指令处。当 `x < 10`变为假,`POP_JUMP_IF_FALSE`会让解释器跳到循环的终止处,第34条指令。 
+Python 的循环也依赖于跳转。在下面的字节码中,`while x < 5`这一行产生了和`if x < 10`几乎一样的字节码。在这两种情况下,解释器都是先执行比较,然后执行`POP_JUMP_IF_FALSE`来控制下一条执行哪个指令。第四行的最后一条字节码`JUMP_ABSOLUT`(循环体结束的地方),让解释器返回到循环开始的第 9 条指令处。当 `x < 10`变为假,`POP_JUMP_IF_FALSE`会让解释器跳到循环的终止处,第 34 条指令。 
 
 ```python
 >>> def loop():
@@ -340,23 +340,23 @@ Python的循环也依赖于跳转。在下面的字节码中,`while x < 5`这
              38 RETURN_VALUE
 ```
 
-### Explore Bytecode
+#### 探索字节码
 
 我希望你用`dis.dis`来试试你自己写的函数。一些有趣的问题值得探索:
 
-- 对解释器而言for循环和while循环有什么不同?
+- 对解释器而言 for 循环和 while 循环有什么不同?
 - 能不能写出两个不同函数,却能产生相同的字节码?
 - `elif`是怎么工作的?列表推导呢?
 
-## Frames
+### 帧
 
-到目前为止,我们已经知道了Python虚拟机是一个栈机器。它能顺序执行指令,在指令间跳转,压入或弹出栈值。但是这和我们期望的解释器还有一定距离。在前面的那个例子中,最后一条指令是`RETURN_VALUE`,它和`return`语句相对应。但是它返回到哪里去呢?
+到目前为止,我们已经知道了 Python 虚拟机是一个栈机器。它能顺序执行指令,在指令间跳转,压入或弹出栈值。但是这和我们期望的解释器还有一定距离。在前面的那个例子中,最后一条指令是`RETURN_VALUE`,它和`return`语句相对应。但是它返回到哪里去呢?
 
-为了回答这个问题,我们必须再增加一层复杂性:frame。一个frame是一些信息的集合和代码的执行上下文。frames在Python代码执行时动态的创建和销毁。每个frame对应函数的一次调用。--- 所以每个frame只有一个code object与之关联,而一个code object可以有多个frame。比如你有一个函数递归的调用自己10次,这会产生11个frame,每次调用对应一个,再加上启动模块对应的一个frame。总的来说,Python程序的每个作用域有一个frame,比如,模块,函数,类。
+为了回答这个问题,我们必须再增加一层复杂性:帧(frame)。一个帧是一些信息的集合和代码的执行上下文。帧在 Python 代码执行时动态地创建和销毁。每个帧对应函数的一次调用 —— 所以每个帧只有一个代码对象与之关联,而一个代码对象可以有多个帧。比如你有一个函数递归的调用自己 10 次,这会产生 11 个帧,每次调用对应一个,再加上启动模块对应的一个帧。总的来说,Python 程序的每个作用域都有一个帧,比如,模块、函数、类定义。
 
-Frame存在于_调用栈_中,一个和我们之前讨论的完全不同的栈。(你最熟悉的栈就是调用栈,就是你经常看到的异常回溯,每个以"File 'program.py'"开始的回溯对应一个frame。)解释器在执行字节码时操作的栈,我们叫它_数据栈_。其实还有第三个栈,叫做_块栈_,用于特定的控制流块,比如循环和异常处理。调用栈中的每个frame都有它自己的数据栈和块栈。
+帧存在于调用栈(call stack)中,一个和我们之前讨论的完全不同的栈。(你最熟悉的栈就是调用栈,就是你经常看到的异常回溯,每个以"File 'program.py'"开始的回溯对应一个帧。)解释器在执行字节码时操作的栈,我们叫它数据栈(data stack)。其实还有第三个栈,叫做块栈(block stack),用于特定的控制流块,比如循环和异常处理。调用栈中的每个帧都有它自己的数据栈和块栈。
 
-让我们用一个具体的例子来说明。假设Python解释器执行到标记为3的地方。解释器正在`foo`函数的调用中,它接着调用`bar`。下面是frame调用栈,块栈和数据栈的示意图。我们感兴趣的是解释器先从最底下的`foo()`开始,接着执行`foo`的函数体,然后到达`bar`。
+让我们用一个具体的例子来说明一下。假设 Python 解释器执行到下面标记为 3 的地方。解释器正处于`foo`函数的调用中,它接着调用`bar`。下面是帧调用栈、块栈和数据栈的示意图。我们感兴趣的是解释器先从最底下的`foo()`开始,接着执行`foo`的函数体,然后到达`bar`。
 
 ```python
 >>> def bar(y):
@@ -372,32 +372,30 @@ Frame存在于_调用栈_中,一个和我们之前讨论的完全不同的栈
 3
 ```
 
-\aosafigure[240pt]{interpreter-images/interpreter-callstack.png}{The call stack}{500l.interpreter.callstack}
+![调用栈](http://aosabook.org/en/500L/interpreter-images/interpreter-callstack.png)
 
-现在,解释器在`bar`函数的调用中。调用栈中有3个frame:一个对应于模块层,一个对应函数`foo`,别一个对应函数`bar`。(\aosafigref{500l.interpreter.callstack}.)一旦`bar`返回,与它对应的frame就会从调用栈中弹出并丢弃。
+现在,解释器处于`bar`函数的调用中。调用栈中有 3 个帧:一个对应于模块层,一个对应函数`foo`,另一个对应函数`bar`。(见上图)一旦`bar`返回,与它对应的帧就会从调用栈中弹出并丢弃。
 
-字节码指令`RETURN_VALUE`告诉解释器在frame间传递一个值。首先,它把位于调用栈栈顶的frame中的数据栈的栈顶值弹出。然后把整个frame弹出丢弃。最后把这个值压到下一个frame的数据栈中。
+字节码指令`RETURN_VALUE`告诉解释器在帧之间传递一个值。首先,它把位于调用栈栈顶的帧中的数据栈的栈顶值弹出。然后把整个帧弹出丢弃。最后把这个值压到下一个帧的数据栈中。
 
-当Ned Batchelder和我在写Byterun时,很长一段时间我们的实现中一直有个重大的错误。我们整个虚拟机中只有一个数据栈,而不是每个frame都有一个。我们写了很多测试代码,同时在Byterun和真正的Python上运行,希望得到一致结果。我们几乎通过了所有测试,只有一样东西不能通过,那就是生成器。最后,通过仔细的阅读CPython的源码,我们发现了错误所在[^thanks]。把数据栈移到每个frame就解决了这个问题。
+当 Ned Batchelder 和我在写 Byterun 时,很长一段时间我们的实现中一直有个重大的错误。我们整个虚拟机中只有一个数据栈,而不是每个帧都有一个。我们写了很多测试代码,同时在 Byterun 和真正的 Python 上运行,希望得到一致结果。我们几乎通过了所有测试,只有一样东西不能通过,那就是生成器(generators)。最后,通过仔细的阅读 CPython 的源码,我们发现了错误所在(感谢 Michael Arntzenius 对这个 bug 的洞悉)。把数据栈移到每个帧就解决了这个问题。
 
-[^thanks]: 感谢 Michael Arntzenius 对这个bug的洞悉。 
+回头在看看这个 bug,我惊讶的发现 Python 真的很少依赖于每个帧有一个数据栈这个特性。在 Python 中几乎所有的操作都会清空数据栈,所以所有的帧公用一个数据栈是没问题的。在上面的例子中,当`bar`执行完后,它的数据栈为空。即使`foo`公用这一个栈,它的值也不会受影响。然而,对应生成器,它的一个关键的特点是它能暂停一个帧的执行,返回到其他的帧,一段时间后它能返回到原来的帧,并以它离开时的相同状态继续执行。
 
-回头在看看这个bug,我惊讶的发现Python真的很少依赖于每个frame有一个数据栈这个特性。在Python中几乎所有的操作都会清空数据栈,所以所有的frame公用一个数据栈是没问题的。在上面的例子中,当`bar`执行完后,它的数据栈为空。即使`foo`公用这一个栈,它的值也不会受影响。然而,对应生成器,一个关键的特点是它能暂停一个frame的执行,返回到其他的frame,一段时间后它能返回到原来的frame,并以它离开时的相同状态继续执行。
+### Byterun
 
-## Byterun
+现在我们有足够的 Python 解释器的知识背景去考察 Byterun。
 
-现在我们有足够的Python解释器的知识背景去考察Byterun。
+Byterun 中有四种对象。
 
-Byterun中有四种对象。
+- `VirtualMachine`类,它管理高层结构,尤其是帧调用栈,并包含了指令到操作的映射。这是一个比前面`Inteprter`对象更复杂的版本。
+- `Frame`类,每个`Frame`类都有一个代码对象,并且管理着其他一些必要的状态位,尤其是全局和局部命名空间、指向调用它的整的指针和最后执行的字节码指令。 
+- `Function`类,它被用来代替真正的 Python 函数。回想一下,调用函数时会创建一个新的帧。我们自己实现了`Function`,以便我们控制新的`Frame`的创建。
+- `Block`类,它只是包装了块的 3 个属性。(块的细节不是解释器的核心,我们不会花时间在它身上,把它列在这里,是因为 Byterun 需要它。)
 
-- `VirtualMachine`类,它管理高层结构,frame调用栈,指令到操作的映射。这是一个比前面`Inteprter`对象更复杂的版本。
-- `Frame`类,每个`Frame`类都有一个code object,并且管理者其他一些必要的状态信息,全局和局部命名空间,指向调用它的frame的指针和最后执行的字节码指令。 
-- `Function`类,它被用来代替真正的Python函数。回想一下,调用函数时会创建一个新的frame。我们自己实现`Function`,所以我们控制新frame的创建。
-- `Block`类,它只是包装了block的3个属性。(block的细节不是解释器的核心,我们不会花时间在它身上,把它列在这里,是因为Byterun需要它。)
+####  `VirtualMachine` 类
 
-### The `VirtualMachine` Class
-
-程序运行时只有一个`VirtualMachine`被创建,因为我们只有一个解释器。`VirtualMachine`保存调用栈,异常状态,在frame中传递的返回值。它的入口点是`run_code`方法,它以编译后的code object为参数,以创建一个frame为开始,然后运行这个frame。这个frame可能再创建出新的frame;调用栈随着程序的运行增长缩短。当第一个frame返回时,执行结束。
+每次程序运行时只会创建一个`VirtualMachine`实例,因为我们只有一个 Python 解释器。`VirtualMachine` 保存调用栈、异常状态、在帧之间传递的返回值。它的入口点是`run_code`方法,它以编译后的代码对象为参数,以创建一个帧为开始,然后运行这个帧。这个帧可能再创建出新的帧;调用栈随着程序的运行而增长和缩短。当第一个帧返回时,执行结束。
 
 ```python
 class VirtualMachineError(Exception):
@@ -418,9 +416,9 @@ class VirtualMachine(object):
 
 ```
 
-### The `Frame` Class
+#### `Frame` 类
 
-接下来,我们来写`Frame`对象。frame是一个属性的集合,它没有任何方法。前面提到过,这些属性包括由编译器生成的code object;局部,全局和内置命名空间;前一个frame的引用;一个数据栈;一个块栈;最后执行的指令指针。(对于内置命名空间我们需要多做一点工作,Python在不同模块中对这个命名空间有不同的处理;但这个细节对我们的虚拟机不重要。)
+接下来,我们来写`Frame`对象。帧是一个属性的集合,它没有任何方法。前面提到过,这些属性包括由编译器生成的代码对象;局部、全局和内置命名空间;前一个帧的引用;一个数据栈;一个块栈;最后执行的指令指针。(对于内置命名空间我们需要多做一点工作,Python 在不同模块中对这个命名空间有不同的处理;但这个细节对我们的虚拟机不重要。)
 
 ```python
 class Frame(object):
@@ -441,7 +439,7 @@ class Frame(object):
         self.block_stack = []
 ```
 
-接着,我们在虚拟机中增加对frame的操作。这有3个帮助函数:一个创建新的frame的方法,和压栈和出栈的方法。第四个函数,`run_frame`,完成执行frame的主要工作,待会我们再讨论这个方法。
+接着,我们在虚拟机中增加对帧的操作。这有 3 个帮助函数:一个创建新的帧的方法(它负责为新的帧找到名字空间),和压栈和出栈的方法。第四个函数,`run_frame`,完成执行帧的主要工作,待会我们再讨论这个方法。
 
 ```python
 class VirtualMachine(object):
@@ -481,9 +479,9 @@ class VirtualMachine(object):
         # we'll come back to this shortly
 ```
 
-### The `Function` Class
+####  `Function` 类
 
-`Function`的实现有点扭曲,但是大部分的细节对理解解释器不重要。重要的是当调用函数时 --- `__call__`方法被调用 --- 它创建一个新的`Frame`并运行它。
+`Function`的实现有点曲折,但是大部分的细节对理解解释器不重要。重要的是当调用函数时 —— 即调用 `__call__`方法 —— 它创建一个新的`Frame`并运行它。
 
 ```python
 class Function(object):
@@ -534,7 +532,7 @@ def make_cell(value):
     return fn.__closure__[0]
 ```
 
-接着,回到`VirtualMachine`对象,我们对数据栈的操作也增加一些帮助方法。字节码操作的栈总是在当前frame的数据栈。这些帮助函数让我们能实现`POP_TOP`,`LOAD_FAST`字节码,并且让其他操作栈的指令可读性更高。
+接着,回到`VirtualMachine`对象,我们对数据栈的操作也增加一些帮助方法。字节码操作的栈总是在当前帧的数据栈。这些帮助函数让我们的`POP_TOP`、`LOAD_FAST`以及其他操作栈的指令的实现可读性更高。
 
 ```python
 class VirtualMachine(object):
@@ -562,11 +560,11 @@ class VirtualMachine(object):
             return []
 ```
 
-在我们运行frame之前,我们还需两个方法。
+在我们运行帧之前,我们还需两个方法。
 
-第一个方法,`parse_byte_and_args`,以一个字节码为输入,先检查它是否有参数,如果有,就解析它的参数。这个方法同时也更新frame的`last_instruction`属性,它指向最后执行的指令。一条没有参数的指令只有一个字节长度,而有参数的字节有3个字节长。参数的意义依赖于指令是什么。比如,前面说过,指令`POP_JUMP_IF_FALSE`,它的参数指的是跳转目标。`BUILD_LIST`, 它的参数是列表的个数。`LOAD_CONST`,它的参数是常量的索引。
+第一个方法,`parse_byte_and_args` 以一个字节码为输入,先检查它是否有参数,如果有,就解析它的参数。这个方法同时也更新帧的`last_instruction`属性,它指向最后执行的指令。一条没有参数的指令只有一个字节长度,而有参数的字节有3个字节长。参数的意义依赖于指令是什么。比如,前面说过,指令`POP_JUMP_IF_FALSE`,它的参数指的是跳转目标。`BUILD_LIST`,它的参数是列表的个数。`LOAD_CONST`,它的参数是常量的索引。
 
-一些指令用简单的数字作为参数。对于另一些,虚拟机需要一点努力去发现它含意。标准库中的`dis`模块中有一个备忘单,它解释什么参数有什么意思,这让我们的代码更加简洁。比如,列表`dis.hasname`告诉我们`LOAD_NAME`, `IMPORT_NAME`,`LOAD_GLOBAL`,以及另外的9个指令都有同样的意思:名字列表的索引。
+一些指令用简单的数字作为参数。对于另一些,虚拟机需要一点努力去发现它含意。标准库中的`dis`模块中有一个备忘单,它解释什么参数有什么意思,这让我们的代码更加简洁。比如,列表`dis.hasname`告诉我们`LOAD_NAME`、 `IMPORT_NAME`、`LOAD_GLOBAL`,以及另外的 9 个指令的参数都有同样的意义:对于这些指令,它们的参数代表了代码对象中的名字列表的索引。
 
 ```python
 class VirtualMachine(object):
@@ -600,8 +598,7 @@ class VirtualMachine(object):
         return byte_name, argument
 ```
 
-下一个方法是`dispatch`,它查看给定的指令并执行相应的操作。在CPython中,这个分派函数用一个巨大的switch语句实现,有超过1500行的代码。幸运的是,我们用的是Python,我们的代码会简洁的多。我们会为每一个字节码名字定义一个方法,然后用`getattr`来查找。就像我们前面的小解释器一样,如果一条指令叫做`FOO_BAR`,那么它对应的方法就是`byte_FOO_BAR`。现在,我们先把这些方法当做一个黑盒子。每个指令方法都会返回`None`或者一个字符串`why`,有些情况下虚拟机需要这个额外`why`信息。这些指令方法的返回值,仅作为解释器状态的内部指示,千万不要和执行frame的返回值相混淆。
-
+下一个方法是`dispatch`,它查找给定的指令并执行相应的操作。在 CPython 中,这个分派函数用一个巨大的 switch 语句实现,有超过 1500 行的代码。幸运的是,我们用的是 Python,我们的代码会简洁的多。我们会为每一个字节码名字定义一个方法,然后用`getattr`来查找。就像我们前面的小解释器一样,如果一条指令叫做`FOO_BAR`,那么它对应的方法就是`byte_FOO_BAR`。现在,我们先把这些方法当做一个黑盒子。每个指令方法都会返回`None`或者一个字符串`why`,有些情况下虚拟机需要这个额外`why`信息。这些指令方法的返回值,仅作为解释器状态的内部指示,千万不要和执行帧的返回值相混淆。
 
 ```python
 class VirtualMachine(object):
@@ -662,13 +659,13 @@ class VirtualMachine(object):
         return self.return_value
 ```
 
-### The `Block` Class
+####  `Block` 类
 
 在我们完成每个字节码方法前,我们简单的讨论一下块。一个块被用于某种控制流,特别是异常处理和循环。它负责保证当操作完成后数据栈处于正确的状态。比如,在一个循环中,一个特殊的迭代器会存在栈中,当循环完成时它从栈中弹出。解释器需要检查循环仍在继续还是已经停止。
 
-为了跟踪这些额外的信息,解释器设置了一个标志来指示它的状态。我们用一个变量`why`实现这个标志,它可以是`None`或者是下面几个字符串这一,`"continue"`, `"break"`,`"excption"`,`return`。他们指示对块栈和数据栈进行什么操作。回到我们迭代器的例子,如果块栈的栈顶是一个`loop`块,`why`是`continue`,迭代器就因该保存在数据栈上,不是如果`why`是`break`,迭代器就会被弹出。
+为了跟踪这些额外的信息,解释器设置了一个标志来指示它的状态。我们用一个变量`why`实现这个标志,它可以是`None`或者是下面几个字符串之一:`"continue"`、`"break"`、`"excption"`、`return`。它们指示对块栈和数据栈进行什么操作。回到我们迭代器的例子,如果块栈的栈顶是一个`loop`块,`why`的代码是`continue`,迭代器就应该保存在数据栈上,而如果`why`是`break`,迭代器就会被弹出。
 
-块操作的细节比较精细,我们不会花时间在这上面,但是有兴趣的读者值得仔细的看看。
+块操作的细节比这个还要繁琐,我们不会花时间在这上面,但是有兴趣的读者值得仔细的看看。
 
 ```python
 Block = collections.namedtuple("Block", "type, handler, stack_height")
@@ -737,9 +734,9 @@ class VirtualMachine(object):
         return why
 ```
 
-## The Instructions
+### 指令
 
-剩下了的就是完成那些指令方法了:`byte_LOAD_FAST`,`byte_BINARY_MODULO`等等。而这些指令的实现并不是很有趣,这里我们只展示了一小部分,完整的实现在这儿https://github.com/nedbat/byterun。(足够执行我们前面所述的所有代码了。)
+剩下了的就是完成那些指令方法了:`byte_LOAD_FAST`、`byte_BINARY_MODULO`等等。而这些指令的实现并不是很有趣,这里我们只展示了一小部分,完整的实现[在 GitHub 上](https://github.com/nedbat/byterun)。(这里包括的指令足够执行我们前面所述的所有代码了。)
 
 ```python
 class VirtualMachine(object):
@@ -926,11 +923,11 @@ class VirtualMachine(object):
         return "return"
 ```
 
-## Dynamic Typing: What the Compiler Doesn't Know
+### 动态类型:编译器不知道它是什么
 
-你可能听过Python是一种动态语言 --- 是它是动态类型的。在我们建造解释器的过程中,已经透露出这样的信息。
+你可能听过 Python 是一种动态语言 —— 它是动态类型的。在我们建造解释器的过程中,已经透露出这样的信息。
 
-动态的一个意思是很多工作在运行时完成。前面我们看到Python的编译器没有很多关于代码真正做什么的信息。举个例子,考虑下面这个简单的函数`mod`。它取两个参数,返回它们的模运算值。从它的字节码中,我们看到变量`a`和`b`首先被加载,然后字节码`BINAY_MODULO`完成这个模运算。
+动态的一个意思是很多工作是在运行时完成的。前面我们看到 Python 的编译器没有很多关于代码真正做什么的信息。举个例子,考虑下面这个简单的函数`mod`。它取两个参数,返回它们的模运算值。从它的字节码中,我们看到变量`a`和`b`首先被加载,然后字节码`BINAY_MODULO`完成这个模运算。
 
 ```python
 >>> def mod(a, b):
@@ -944,25 +941,25 @@ class VirtualMachine(object):
 4
 ```
 
-计算19 % 5得4,--- 一点也不奇怪。如果我们用不同类的参数呢?
+计算 19 % 5 得4,—— 一点也不奇怪。如果我们用不同类的参数呢?
 
 ```python
 >>> mod("by%sde", "teco")
 'bytecode'
 ```
 
-刚才发生了什么?你可能见过这样的语法,格式化字符串。
+刚才发生了什么?你可能在其它地方见过这样的语法,格式化字符串。
 
 ```
 >>> print("by%sde" % "teco")
 bytecode
 ```
 
-用符号`%`去格式化字符串会调用字节码`BUNARY_MODULO`.它取栈顶的两个值求模,不管这两个值是字符串,数字或是你自己定义的类的实例。字节码在函数编译时生成(或者说,函数定义时)相同的字节码会用于不同类的参数。
+用符号`%`去格式化字符串会调用字节码`BUNARY_MODULO`。它取栈顶的两个值求模,不管这两个值是字符串、数字或是你自己定义的类的实例。字节码在函数编译时生成(或者说,函数定义时)相同的字节码会用于不同类的参数。
 
-Python的编译器关于字节码的功能知道的很少。而取决于解释器来决定`BINAYR_MODULO`应用于什么类型的对象并完成正确的操作。这就是为什么Python被描述为_动态类型_:直到运行前你不必知道这个函数参数的类型。相反,在一个静态类型语言中,程序员需要告诉编译器参数的类型是什么(或者编译器自己推断出参数的类型。)
+Python 的编译器关于字节码的功能知道的很少,而取决于解释器来决定`BINAYR_MODULO`应用于什么类型的对象并完成正确的操作。这就是为什么 Python 被描述为动态类型(dynamically typed):直到运行前你不必知道这个函数参数的类型。相反,在一个静态类型语言中,程序员需要告诉编译器参数的类型是什么(或者编译器自己推断出参数的类型。)
 
-编译器的无知是优化Python的一个挑战 --- 只看字节码,而不真正运行它,你就不知道每条字节码在干什么!你可以定义一个类,实现`__mod__`方法,当你对这个类的实例使用`%`时,Python就会自动调用这个方法。所以,`BINARY_MODULO`其实可以运行任何代码。
+编译器的无知是优化 Python 的一个挑战 —— 只看字节码,而不真正运行它,你就不知道每条字节码在干什么!你可以定义一个类,实现`__mod__`方法,当你对这个类的实例使用`%`时,Python 就会自动调用这个方法。所以,`BINARY_MODULO`其实可以运行任何代码。
 
 看看下面的代码,第一个`a % b`看起来没有用。
 
@@ -972,25 +969,23 @@ def mod(a,b):
     return a %b
 ```
 
-不幸的是,对这段代码进行静态分析 --- 不运行它 --- 不能确定第一个`a % b`没有做任何事。用 `%`调用`__mod__`可能会写一个文件,或是和程序的其他部分交互,或者其他任何可以在Python中完成的事。很难优化一个你不知道它会做什么的函数。在Russell Power和Alex Rubinsteyn的优秀论文中写道,“我们可以用多快的速度解释Python?”,他们说,“在普遍缺乏类型信息下,每条指令必须被看作一个`INVOKE_ARBITRARY_METHOD`。”
+不幸的是,对这段代码进行静态分析 —— 不运行它 —— 不能确定第一个`a % b`没有做任何事。用 `%`调用`__mod__`可能会写一个文件,或是和程序的其他部分交互,或者其他任何可以在 Python 中完成的事。很难优化一个你不知道它会做什么的函数。在 Russell Power 和 Alex Rubinsteyn 的优秀论文中写道,“我们可以用多快的速度解释 Python?”,他们说,“在普遍缺乏类型信息下,每条指令必须被看作一个`INVOKE_ARBITRARY_METHOD`。”
 
-## Conclusion
+### 总结
 
-Byterun是一个比CPython容易理解的简洁的Python解释器。Byterun复制了CPython的主要结构:一个基于栈的指令集称为字节码,它们顺序执行或在指令间跳转,向栈中压入和从中弹出数据。解释器随着函数和生成器的调用和返回,动态的创建,销毁frame,并在frame间跳转。Byterun也有着和真正解释器一样的限制:因为Python使用动态类型,解释器必须在运行时决定指令的正确行为。
+Byterun 是一个比 CPython 容易理解的简洁的 Python 解释器。Byterun 复制了 CPython 的主要结构:一个基于栈的解释器对称之为字节码的指令集进行操作,它们顺序执行或在指令间跳转,向栈中压入和从中弹出数据。解释器随着函数和生成器的调用和返回,动态的创建、销毁帧,并在帧之间跳转。Byterun 也有着和真正解释器一样的限制:因为 Python 使用动态类型,解释器必须在运行时决定指令的正确行为。
 
-我鼓励你去反汇编你的程序,然后用Byterun来运行。你很快会发现这个缩短版的Byterun所没有实现的指令。完整的实现在https://github.com/nedbat/byterun,或者仔细阅读真正的CPython解释器`ceval.c`,你也可以实现自己的解释器!
+我鼓励你去反汇编你的程序,然后用 Byterun 来运行。你很快会发现这个缩短版的 Byterun 所没有实现的指令。完整的实现在 https://github.com/nedbat/byterun ,或者你可以仔细阅读真正的 CPython 解释器`ceval.c`,你也可以实现自己的解释器!
 
-## Acknowledgements
+### 致谢
 
-Thanks to Ned Batchelder for originating this project and guiding my contributions, Michael Arntzenius for his help debugging the code and editing the prose, Leta Montopoli for her edits, and the entire Recurse Center community for their support and interest. Any errors are my own.
+感谢 Ned Batchelder 发起这个项目并引导我的贡献,感谢 Michael Arntzenius 帮助调试代码和这篇文章的修订,感谢 Leta Montopoli 的修订,以及感谢整个 Recurse Center 社区的支持和鼓励。所有的不足全是我自己没搞好。
 
 --------------------------------------
-via:http://aosabook.org/en/500L/a-python-interpreter-written-in-python.html
-
-作者: Allison Kaptur Allison
+via: http://aosabook.org/en/500L/a-python-interpreter-written-in-python.html
 
+作者: Allison Kaptur
 译者:[qingyunha](https://github.com/qingyunha) 
-
-校对:[校对者ID](https://github.com/校对者ID) 
+校对:[wxy](https://github.com/wxy) 
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](http://linux.cn/) 荣誉推出 
diff --git a/published/20160531 Writing online multiplayer game with python and asyncio - part 2.md b/published/20160531 Writing online multiplayer game with python and asyncio - part 2.md
new file mode 100644
index 0000000000..b30b381b7e
--- /dev/null
+++ b/published/20160531 Writing online multiplayer game with python and asyncio - part 2.md	
@@ -0,0 +1,236 @@
+使用 Python 和 Asyncio 编写在线多用人游戏(二)
+==================================================================
+
+![](https://7webpages.com/media/cache/fd/d1/fdd1f8f8bbbf4166de5f715e6ed0ac00.gif)
+
+> 你在 Python 中用过异步编程吗?本文中我会告诉你怎样做,而且用一个[能工作的例子][1]来展示它:这是一个流行的贪吃蛇游戏,而且是为多人游戏而设计的。
+
+介绍和理论部分参见“[第一部分 异步化][2]”。
+
+- [游戏入口在此,点此体验][1]。
+
+### 3、编写游戏循环主体
+
+游戏循环是每一个游戏的核心。它持续地运行以读取玩家的输入、更新游戏的状态,并且在屏幕上渲染游戏结果。在在线游戏中,游戏循环分为客户端和服务端两部分,所以一般有两个循环通过网络通信。通常客户端的角色是获取玩家输入,比如按键或者鼠标移动,将数据传输给服务端,然后接收需要渲染的数据。服务端处理来自玩家的所有数据,更新游戏的状态,执行渲染下一帧的必要计算,然后将结果传回客户端,例如游戏中对象的新位置。如果没有可靠的理由,不混淆客户端和服务端的角色是一件很重要的事。如果你在客户端执行游戏逻辑的计算,很容易就会和其它客户端失去同步,其实你的游戏也可以通过简单地传递客户端的数据来创建。
+
+> 游戏循环的一次迭代称为一个嘀嗒(tick)。嘀嗒是一个事件,表示当前游戏循环的迭代已经结束,下一帧(或者多帧)的数据已经就绪。
+
+在后面的例子中,我们使用相同的客户端,它使用 WebSocket 从一个网页上连接到服务端。它执行一个简单的循环,将按键码发送给服务端,并显示来自服务端的所有信息。[客户端代码戳这里][4]。
+
+### 例子 3.1:基本游戏循环
+
+> [例子 3.1 源码][5]。
+
+我们使用 [aiohttp][6] 库来创建游戏服务器。它可以通过 asyncio 创建网页服务器和客户端。这个库的一个优势是它同时支持普通 http 请求和 websocket。所以我们不用其他网页服务器来渲染游戏的 html 页面。
+
+下面是启动服务器的方法:
+
+```
+app = web.Application()
+app["sockets"] = []
+
+asyncio.ensure_future(game_loop(app))
+
+app.router.add_route('GET', '/connect', wshandler)
+app.router.add_route('GET', '/', handle)
+
+web.run_app(app)
+```
+
+`web.run_app` 是创建服务主任务的快捷方法,通过它的 `run_forever()` 方法来执行 `asyncio` 事件循环。建议你查看这个方法的源码,弄清楚服务器到底是如何创建和结束的。
+
+`app` 变量就是一个类似于字典的对象,它用于在所连接的客户端之间共享数据。我们使用它来存储连接的套接字的列表。随后会用这个列表来给所有连接的客户端发送消息。`asyncio.ensure_future()` 调用会启动主游戏循环的任务,每隔2 秒向客户端发送嘀嗒消息。这个任务会在同样的 asyncio 事件循环中和网页服务器并行执行。
+
+有两个网页请求处理器:`handle` 是提供 html 页面的处理器;`wshandler` 是主要的 websocket 服务器任务,处理和客户端之间的交互。在事件循环中,每一个连接的客户端都会创建一个新的 `wshandler` 任务。这个任务会添加客户端的套接字到列表中,以便 `game_loop` 任务可以给所有的客户端发送消息。然后它将随同消息回显客户端的每个击键。
+
+在启动的任务中,我们在 `asyncio` 的主事件循环中启动 worker 循环。任务之间的切换发生在它们之间任何一个使用 `await`语句来等待某个协程结束时。例如 `asyncio.sleep` 仅仅是将程序执行权交给调度器一段指定的时间;`ws.receive` 等待 websocket 的消息,此时调度器可能切换到其它任务。
+
+在浏览器中打开主页,连接上服务器后,试试随便按下键。它们的键值会从服务端返回,每隔 2 秒这个数字会被游戏循环中发给所有客户端的嘀嗒消息所覆盖。
+
+我们刚刚创建了一个处理客户端按键的服务器,主游戏循环在后台做一些处理,周期性地同时更新所有的客户端。
+
+### 例子 3.2: 根据请求启动游戏
+
+> [例子 3.2 的源码][7]
+
+在前一个例子中,在服务器的生命周期内,游戏循环一直运行着。但是现实中,如果没有一个人连接服务器,空运行游戏循环通常是不合理的。而且,同一个服务器上可能有不同的“游戏房间”。在这种假设下,每一个玩家“创建”一个游戏会话(比如说,多人游戏中的一个比赛或者大型多人游戏中的副本),这样其他用户可以加入其中。当游戏会话开始时,游戏循环才开始执行。
+
+在这个例子中,我们使用一个全局标记来检测游戏循环是否在执行。当第一个用户发起连接时,启动它。最开始,游戏循环没有执行,标记设置为 `False`。游戏循环是通过客户端的处理方法启动的。
+
+```
+  if app["game_is_running"] == False:
+        asyncio.ensure_future(game_loop(app))
+```
+
+当 `game_loop()` 运行时,这个标记设置为 `True`;当所有客户端都断开连接时,其又被设置为 `False`。
+
+### 例子 3.3:管理任务
+
+> [例子3.3源码][8]
+
+这个例子用来解释如何和任务对象协同工作。我们把游戏循环的任务直接存储在游戏循环的全局字典中,代替标记的使用。在像这样的一个简单例子中并不一定是最优的,但是有时候你可能需要控制所有已经启动的任务。
+
+```
+    if app["game_loop"] is None or \
+       app["game_loop"].cancelled():
+        app["game_loop"] = asyncio.ensure_future(game_loop(app))
+```
+
+这里 `ensure_future()` 返回我们存放在全局字典中的任务对象,当所有用户都断开连接时,我们使用下面方式取消任务:
+
+```
+    app["game_loop"].cancel()
+```
+
+这个 `cancel()` 调用将通知调度器不要向这个协程传递执行权,而且将它的状态设置为已取消:`cancelled`,之后可以通过 `cancelled()` 方法来检查是否已取消。这里有一个值得一提的小注意点:当你持有一个任务对象的外部引用时,而这个任务执行中发生了异常,这个异常不会抛出。取而代之的是为这个任务设置一个异常状态,可以通过 `exception()` 方法来检查是否出现了异常。这种悄无声息地失败在调试时不是很有用。所以,你可能想用抛出所有异常来取代这种做法。你可以对所有未完成的任务显式地调用 `result()` 来实现。可以通过如下的回调来实现:
+
+```
+    app["game_loop"].add_done_callback(lambda t: t.result())
+```
+
+如果我们打算在我们代码中取消这个任务,但是又不想产生 `CancelError` 异常,有一个检查 `cancelled` 状态的点:
+
+```
+    app["game_loop"].add_done_callback(lambda t: t.result()
+                                       if not t.cancelled() else None)
+```
+
+注意仅当你持有任务对象的引用时才需要这么做。在前一个例子,所有的异常都是没有额外的回调,直接抛出所有异常。
+
+### 例子 3.4:等待多个事件
+
+> [例子 3.4 源码][9]
+
+在许多场景下,在客户端的处理方法中你需要等待多个事件的发生。除了来自客户端的消息,你可能需要等待不同类型事件的发生。比如,如果你的游戏时间有限制,那么你可能需要等一个来自定时器的信号。或者你需要使用管道来等待来自其它进程的消息。亦或者是使用分布式消息系统的网络中其它服务器的信息。
+
+为了简单起见,这个例子是基于例子 3.1。但是这个例子中我们使用 `Condition` 对象来与已连接的客户端保持游戏循环的同步。我们不保存套接字的全局列表,因为只在该处理方法中使用套接字。当游戏循环停止迭代时,我们使用 `Condition.notify_all()` 方法来通知所有的客户端。这个方法允许在 `asyncio` 的事件循环中使用发布/订阅的模式。
+
+为了等待这两个事件,首先我们使用 `ensure_future()` 来封装任务中这个可等待对象。
+
+```
+    if not recv_task:
+        recv_task = asyncio.ensure_future(ws.receive())
+    if not tick_task:
+        await tick.acquire()
+        tick_task = asyncio.ensure_future(tick.wait())
+```
+
+在我们调用 `Condition.wait()` 之前,我们需要在它后面获取一把锁。这就是我们为什么先调用 `tick.acquire()` 的原因。在调用 `tick.wait()` 之后,锁会被释放,这样其他的协程也可以使用它。但是当我们收到通知时,会重新获取锁,所以在收到通知后需要调用 `tick.release()` 来释放它。
+
+我们使用 `asyncio.wait()` 协程来等待两个任务。
+
+```
+    done, pending = await asyncio.wait(
+        [recv_task,
+         tick_task],
+        return_when=asyncio.FIRST_COMPLETED)
+```
+
+程序会阻塞,直到列表中的任意一个任务完成。然后它返回两个列表:执行完成的任务列表和仍然在执行的任务列表。如果任务执行完成了,其对应变量赋值为 `None`,所以在下一个迭代时,它可能会被再次创建。
+
+### 例子 3.5: 结合多个线程
+
+> [例子 3.5 源码][10]
+
+在这个例子中,我们结合 `asyncio` 循环和线程,在一个单独的线程中执行主游戏循环。我之前提到过,由于 `GIL` 的存在,Python 代码的真正并行执行是不可能的。所以使用其它线程来执行复杂计算并不是一个好主意。然而,在使用 `asyncio` 时结合线程有原因的:当我们使用的其它库不支持 `asyncio` 时就需要。在主线程中调用这些库会阻塞循环的执行,所以异步使用他们的唯一方法是在不同的线程中使用他们。
+
+我们使用 `asyncio` 循环的`run_in_executor()` 方法和 `ThreadPoolExecutor` 来执行游戏循环。注意 `game_loop()` 已经不再是一个协程了。它是一个由其它线程执行的函数。然而我们需要和主线程交互,在游戏事件到来时通知客户端。`asyncio` 本身不是线程安全的,它提供了可以在其它线程中执行你的代码的方法。普通函数有 `call_soon_threadsafe()`,协程有 `run_coroutine_threadsafe()`。我们在 `notify()` 协程中增加了通知客户端游戏的嘀嗒的代码,然后通过另外一个线程执行主事件循环。
+
+```
+def game_loop(asyncio_loop):
+    print("Game loop thread id {}".format(threading.get_ident()))
+    async def notify():
+        print("Notify thread id {}".format(threading.get_ident()))
+        await tick.acquire()
+        tick.notify_all()
+        tick.release()
+
+    while 1:
+        task = asyncio.run_coroutine_threadsafe(notify(), asyncio_loop)
+        # blocking the thread
+        sleep(1)
+        # make sure the task has finished
+        task.result()
+```
+
+当你执行这个例子时,你会看到 “Notify thread id” 和 “Main thread id” 相等,因为 `notify()` 协程在主线程中执行。与此同时 `sleep(1)` 在另外一个线程中执行,因此它不会阻塞主事件循环。
+
+### 例子 3.6:多进程和扩展
+
+> [例子 3.6 源码][11]
+
+单线程的服务器可能运行得很好,但是它只能使用一个 CPU 核。为了将服务扩展到多核,我们需要执行多个进程,每个进程执行各自的事件循环。这样我们需要在进程间交互信息或者共享游戏的数据。而且在一个游戏中经常需要进行复杂的计算,例如路径查找之类。这些任务有时候在一个游戏嘀嗒中没法快速完成。在协程中不推荐进行费时的计算,因为它会阻塞事件的处理。在这种情况下,将这个复杂任务交给其它并行执行的进程可能更合理。
+
+最简单的使用多个核的方法是启动多个使用单核的服务器,就像之前的例子中一样,每个服务器占用不同的端口。你可以使用 `supervisord` 或者其它进程控制的系统。这个时候你需要一个像 `HAProxy` 这样的负载均衡器,使得连接的客户端分布在多个进程间。已经有一些可以连接 asyncio 和一些流行的消息及存储系统的适配系统。例如:
+
+- [aiomcache][12] 用于 memcached 客户端
+- [aiozmq][13] 用于 zeroMQ
+- [aioredis][14] 用于 Redis 存储,支持发布/订阅
+
+你可以在  github 或者 pypi 上找到其它的软件包,大部分以 `aio` 开头。
+
+使用网络服务在存储持久状态和交换某些信息时可能比较有效。但是如果你需要进行进程间通信的实时处理,它的性能可能不足。此时,使用标准的 unix 管道可能更合适。`asyncio` 支持管道,在`aiohttp`仓库有个 [使用管道的服务器的非常底层的例子][15]。
+
+在当前的例子中,我们使用 Python 的高层类库 [multiprocessing][16] 来在不同的核上启动复杂的计算,使用 `multiprocessing.Queue` 来进行进程间的消息交互。不幸的是,当前的 `multiprocessing` 实现与 `asyncio` 不兼容。所以每一个阻塞方法的调用都会阻塞事件循环。但是此时线程正好可以起到帮助作用,因为如果在不同线程里面执行 `multiprocessing` 的代码,它就不会阻塞主线程。所有我们需要做的就是把所有进程间的通信放到另外一个线程中去。这个例子会解释如何使用这个方法。和上面的多线程例子非常类似,但是我们从线程中创建的是一个新的进程。
+
+```
+def game_loop(asyncio_loop):
+    # coroutine to run in main thread
+    async def notify():
+        await tick.acquire()
+        tick.notify_all()
+        tick.release()
+
+    queue = Queue()
+
+    # function to run in a different process
+    def worker():
+        while 1:
+            print("doing heavy calculation in process {}".format(os.getpid()))
+            sleep(1)
+            queue.put("calculation result")
+
+    Process(target=worker).start()
+
+    while 1:
+        # blocks this thread but not main thread with event loop
+        result = queue.get()
+        print("getting {} in process {}".format(result, os.getpid()))
+        task = asyncio.run_coroutine_threadsafe(notify(), asyncio_loop)
+        task.result()
+```
+
+这里我们在另外一个进程中运行 `worker()` 函数。它包括一个执行复杂计算并把计算结果放到 `queue` 中的循环,这个 `queue` 是 `multiprocessing.Queue` 的实例。然后我们就可以在另外一个线程的主事件循环中获取结果并通知客户端,就和例子 3.5 一样。这个例子已经非常简化了,它没有合理的结束进程。而且在真实的游戏中,我们可能需要另外一个队列来将数据传递给 `worker`。
+
+有一个项目叫 [aioprocessing][17],它封装了 `multiprocessing`,使得它可以和 `asyncio` 兼容。但是实际上它只是和上面例子使用了完全一样的方法:从线程中创建进程。它并没有给你带来任何方便,除了它使用了简单的接口隐藏了后面的这些技巧。希望在 Python 的下一个版本中,我们能有一个基于协程且支持 `asyncio` 的 `multiprocessing` 库。
+
+> 注意!如果你从主线程或者主进程中创建了一个不同的线程或者子进程来运行另外一个 `asyncio` 事件循环,你需要显式地使用 `asyncio.new_event_loop()` 来创建循环,不然的话可能程序不会正常工作。
+
+--------------------------------------------------------------------------------
+
+via: https://7webpages.com/blog/writing-online-multiplayer-game-with-python-and-asyncio-writing-game-loop/
+
+作者:[Kyrylo Subbotin][a]
+译者:[chunyang-wen](https://github.com/chunyang-wen)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://7webpages.com/blog/writing-online-multiplayer-game-with-python-and-asyncio-writing-game-loop/
+[1]: http://snakepit-game.com/
+[2]: https://linux.cn/article-7767-1.html
+[3]: http://snakepit-game.com/
+[4]: https://github.com/7WebPages/snakepit-game/blob/master/simple/index.html
+[5]: https://github.com/7WebPages/snakepit-game/blob/master/simple/game_loop_basic.py
+[6]: http://aiohttp.readthedocs.org/
+[7]: https://github.com/7WebPages/snakepit-game/blob/master/simple/game_loop_handler.py
+[8]: https://github.com/7WebPages/snakepit-game/blob/master/simple/game_loop_global.py
+[9]: https://github.com/7WebPages/snakepit-game/blob/master/simple/game_loop_wait.py
+[10]: https://github.com/7WebPages/snakepit-game/blob/master/simple/game_loop_thread.py
+[11]: https://github.com/7WebPages/snakepit-game/blob/master/simple/game_loop_process.py
+[12]: https://github.com/aio-libs/aiomcache
+[13]: https://github.com/aio-libs/aiozmq
+[14]: https://github.com/aio-libs/aioredis
+[15]: https://github.com/KeepSafe/aiohttp/blob/master/examples/mpsrv.py
+[16]: https://docs.python.org/3.5/library/multiprocessing.html
+[17]: https://github.com/dano/aioprocessing
diff --git a/published/20160608 Simple Python Framework from Scratch.md b/published/20160608 Simple Python Framework from Scratch.md
new file mode 100644
index 0000000000..c7f9174dd6
--- /dev/null
+++ b/published/20160608 Simple Python Framework from Scratch.md	
@@ -0,0 +1,464 @@
+从零构建一个简单的 Python 框架
+===================================
+
+为什么你想要自己构建一个 web 框架呢?我想,原因有以下几点:
+
+- 你有一个新奇的想法,觉得将会取代其他的框架
+- 你想要获得一些名气
+- 你遇到的问题很独特,以至于现有的框架不太合适
+- 你对 web 框架是如何工作的很感兴趣,因为你想要成为一位更好的 web 开发者。
+
+接下来的笔墨将着重于最后一点。这篇文章旨在通过对设计和实现过程一步一步的阐述告诉读者,我在完成一个小型的服务器和框架之后学到了什么。你可以在这个[代码仓库][1]中找到这个项目的完整代码。
+
+我希望这篇文章可以鼓励更多的人来尝试,因为这确实很有趣。它让我知道了 web 应用是如何工作的,而且这比我想的要容易的多!
+
+### 范围
+
+框架可以处理请求-响应周期、身份认证、数据库访问、模板生成等部分工作。Web 开发者使用框架是因为,大多数的 web 应用拥有大量相同的功能,而对每个项目都重新实现同样的功能意义不大。
+
+比较大的的框架如 Rails 和 Django 实现了高层次的抽象,或者说“自备电池”(“batteries-included”,这是 Python 的口号之一,意即所有功能都自足。)。而实现所有的这些功能可能要花费数千小时,因此在这个项目上,我们重点完成其中的一小部分。在开始写代码前,我先列举一下所需的功能以及限制。
+
+功能:
+
+- 处理 HTTP 的 GET 和 POST 请求。你可以在[这篇 wiki][2] 中对 HTTP 有个大致的了解。
+- 实现异步操作(我*喜欢* Python 3 的 asyncio 模块)。
+- 简单的路由逻辑以及参数撷取。
+- 像其他微型框架一样,提供一个简单的用户级 API 。
+- 支持身份认证,因为学会这个很酷啊(微笑)。
+
+限制:
+
+- 将只支持 HTTP 1.1 的一个小子集,不支持传输编码(transfer-encoding)、HTTP 认证(http-auth)、内容编码(content-encoding,如 gzip)以及[持久化连接][3]等功能。
+- 不支持对响应内容的 MIME 判断 - 用户需要手动指定。
+- 不支持 WSGI - 仅能处理简单的 TCP 连接。
+- 不支持数据库。
+
+我觉得一个小的用例可以让上述内容更加具体,也可以用来演示这个框架的 API:
+
+```
+from diy_framework import App, Router
+from diy_framework.http_utils import Response
+
+
+# GET simple route
+async def home(r):
+    rsp = Response()
+    rsp.set_header('Content-Type', 'text/html')
+    rsp.body = '<html><body><b>test</b></body></html>'
+    return rsp
+
+
+# GET route + params
+async def welcome(r, name):
+    return "Welcome {}".format(name)
+
+# POST route + body param
+async def parse_form(r):
+    if r.method == 'GET':
+        return 'form'
+    else:
+        name = r.body.get('name', '')[0]
+        password = r.body.get('password', '')[0]
+
+       return "{0}:{1}".format(name, password)
+
+# application = router + http server
+router = Router()
+router.add_routes({
+    r'/welcome/{name}': welcome,
+    r'/': home,
+    r'/login': parse_form,})
+
+app = App(router)
+app.start_server()
+```
+'
+用户需要定义一些能够返回字符串或 `Response` 对象的异步函数,然后将这些函数与表示路由的字符串配对,最后通过一个函数调用(`start_server`)开始处理请求。
+
+完成设计之后,我将它抽象为几个我需要编码的部分:
+
+- 接受 TCP 连接以及调度一个异步函数来处理这些连接的部分
+- 将原始文本解析成某种抽象容器的部分
+- 对于每个请求,用来决定调用哪个函数的部分
+- 将上述部分集中到一起,并为开发者提供一个简单接口的部分
+
+我先编写一些测试,这些测试被用来描述每个部分的功能。几次重构后,整个设计被分成若干部分,每个部分之间是相对解耦的。这样就非常好,因为每个部分可以被独立地研究学习。以下是我上文列出的抽象的具体体现:
+
+- 一个 HTTPServer 对象,需要一个 Router 对象和一个 http_parser 模块,并使用它们来初始化。
+- HTTPConnection 对象,每一个对象表示一个单独的客户端 HTTP 连接,并且处理其请求-响应周期:使用 http_parser 模块将收到的字节流解析为一个 Request 对象;使用一个 Router 实例寻找并调用正确的函数来生成一个响应;最后将这个响应发送回客户端。
+- 一对 Request 和 Response 对象为用户提供了一种友好的方式,来处理实质上是字节流的字符串。用户不需要知道正确的消息格式和分隔符是怎样的。
+- 一个包含“路由:函数”对应关系的 Router 对象。它提供一个添加配对的方法,可以根据 URL 路径查找到相应的函数。
+- 最后,一个 App 对象。它包含配置信息,并使用它们实例化一个 HTTPServer 实例。
+
+让我们从 `HTTPConnection` 开始来讲解各个部分。
+
+### 模拟异步连接
+
+为了满足上述约束条件,每一个 HTTP 请求都是一个单独的 TCP 连接。这使得处理请求的速度变慢了,因为建立多个 TCP 连接需要相对高的花销(DNS 查询,TCP 三次握手,[慢启动][4]等等的花销),不过这样更加容易模拟。对于这一任务,我选择相对高级的 [asyncio-stream][5] 模块,它建立在 [asyncio 的传输和协议][6]的基础之上。我强烈推荐你读一读标准库中的相应代码,很有意思!
+
+一个 `HTTPConnection` 的实例能够处理多个任务。首先,它使用 `asyncio.StreamReader` 对象以增量的方式从 TCP 连接中读取数据,并存储在缓存中。每一个读取操作完成后,它会尝试解析缓存中的数据,并生成一个 `Request` 对象。一旦收到了这个完整的请求,它就生成一个回复,并通过 `asyncio.StreamWriter` 对象发送回客户端。当然,它还有两个任务:超时连接以及错误处理。
+
+你可以在[这里][7]浏览这个类的完整代码。我将分别介绍代码的每一部分。为了简单起见,我移除了代码文档。
+
+```
+class HTTPConnection(object):
+    def init(self, http_server, reader, writer):
+        self.router = http_server.router
+        self.http_parser = http_server.http_parser
+        self.loop = http_server.loop
+
+        self._reader = reader
+        self._writer = writer
+        self._buffer = bytearray()
+        self._conn_timeout = None
+        self.request = Request()
+```
+
+这个 `init` 方法没啥意思,它仅仅是收集了一些对象以供后面使用。它存储了一个 `router` 对象、一个 `http_parser` 对象以及 `loop` 对象,分别用来生成响应、解析请求以及在事件循环中调度任务。
+
+然后,它存储了代表一个 TCP 连接的读写对,和一个充当原始字节缓冲区的空[字节数组][8]。`_conn_timeout` 存储了一个 [asyncio.Handle][9] 的实例,用来管理超时逻辑。最后,它还存储了 `Request` 对象的一个单一实例。
+
+下面的代码是用来接受和发送数据的核心功能:
+
+```
+async def handle_request(self):
+    try:
+        while not self.request.finished and not self._reader.at_eof():
+            data = await self._reader.read(1024)
+            if data:
+                self._reset_conn_timeout()
+                await self.process_data(data)
+        if self.request.finished:
+            await self.reply()
+        elif self._reader.at_eof():
+            raise BadRequestException()
+    except (NotFoundException,
+            BadRequestException) as e:
+        self.error_reply(e.code, body=Response.reason_phrases[e.code])
+    except Exception as e:
+        self.error_reply(500, body=Response.reason_phrases[500])
+
+    self.close_connection()
+```
+
+所有内容被包含在 `try-except` 代码块中,这样在解析请求或响应期间抛出的异常可以被捕获到,然后一个错误响应会发送回客户端。
+
+在 `while` 循环中不断读取请求,直到解析器将 `self.request.finished` 设置为 True ,或者客户端关闭连接所触发的信号使得 `self._reader_at_eof()` 函数返回值为 True 为止。这段代码尝试在每次循环迭代中从 `StreamReader` 中读取数据,并通过调用 `self.process_data(data)` 函数以增量方式生成 `self.request`。每次循环读取数据时,连接超时计数器被重置。
+
+这儿有个错误,你发现了吗?稍后我们会再讨论这个。需要注意的是,这个循环可能会耗尽 CPU 资源,因为如果没有读取到东西 `self._reader.read()` 函数将会返回一个空的字节对象 `b''`。这就意味着循环将会不断运行,却什么也不做。一个可能的解决方法是,用非阻塞的方式等待一小段时间:`await asyncio.sleep(0.1)`。我们暂且不对它做优化。
+
+还记得上一段我提到的那个错误吗?只有从 `StreamReader` 读取数据时,`self._reset_conn_timeout()` 函数才会被调用。这就意味着,**直到第一个字节到达时**,`timeout` 才被初始化。如果有一个客户端建立了与服务器的连接却不发送任何数据,那就永远不会超时。这可能被用来消耗系统资源,从而导致拒绝服务式攻击(DoS)。修复方法就是在 `init` 函数中调用 `self._reset_conn_timeout()` 函数。
+
+当请求接受完成或连接中断时,程序将运行到 `if-else` 代码块。这部分代码会判断解析器收到完整的数据后是否完成了解析。如果是,好,生成一个回复并发送回客户端。如果不是,那么请求信息可能有错误,抛出一个异常!最后,我们调用 `self.close_connection` 执行清理工作。
+
+解析请求的部分在 `self.process_data` 方法中。这个方法非常简短,也易于测试:
+
+```
+async def process_data(self, data):
+    self._buffer.extend(data)
+
+    self._buffer = self.http_parser.parse_into(
+        self.request, self._buffer)
+```
+
+每一次调用都将数据累积到 `self._buffer` 中,然后试着用 `self.http_parser` 来解析已经收集的数据。这里需要指出的是,这段代码展示了一种称为[依赖注入(Dependency Injection)][10]的模式。如果你还记得 `init` 函数的话,应该知道我们传入了一个包含 `http_parser` 对象的 `http_server` 对象。在这个例子里,`http_parser` 对象是 `diy_framework` 包中的一个模块。不过它也可以是任何含有 `parse_into` 函数的类,这个 `parse_into` 函数接受一个 `Request` 对象以及字节数组作为参数。这很有用,原因有二:一是,这意味着这段代码更易扩展。如果有人想通过一个不同的解析器来使用 `HTTPConnection`,没问题,只需将它作为参数传入即可。二是,这使得测试更加容易,因为 `http_parser` 不是硬编码的,所以使用虚假数据或者 [mock][11] 对象来替代是很容易的。
+
+下一段有趣的部分就是 `reply` 方法了:
+
+```
+async def reply(self):
+    request = self.request
+    handler = self.router.get_handler(request.path)
+
+    response = await handler.handle(request)
+
+    if not isinstance(response, Response):
+        response = Response(code=200, body=response)
+
+    self._writer.write(response.to_bytes())
+    await self._writer.drain()
+```
+
+这里,一个 `HTTPConnection` 的实例使用了 `HTTPServer` 中的 `router` 对象来得到一个生成响应的对象。一个路由可以是任何一个拥有 `get_handler` 方法的对象,这个方法接收一个字符串作为参数,返回一个可调用的对象或者抛出 `NotFoundException` 异常。而这个可调用的对象被用来处理请求以及生成响应。处理程序由框架的使用者编写,如上文所说的那样,应该返回字符串或者 `Response` 对象。`Response` 对象提供了一个友好的接口,因此这个简单的 if 语句保证了无论处理程序返回什么,代码最终都得到一个统一的 `Response` 对象。
+
+接下来,被赋值给 `self._writer` 的 `StreamWriter` 实例被调用,将字节字符串发送回客户端。函数返回前,程序在 `await self._writer.drain()` 处等待,以确保所有的数据被发送给客户端。只要缓存中还有未发送的数据,`self._writer.close()` 方法就不会执行。
+
+`HTTPConnection` 类还有两个更加有趣的部分:一个用于关闭连接的方法,以及一组用来处理超时机制的方法。首先,关闭一条连接由下面这个小函数完成:
+
+```
+def close_connection(self):
+    self._cancel_conn_timeout()
+    self._writer.close()
+```
+
+每当一条连接将被关闭时,这段代码首先取消超时,然后把连接从事件循环中清除。
+
+超时机制由三个相关的函数组成:第一个函数在超时后给客户端发送错误消息并关闭连接;第二个函数用于取消当前的超时;第三个函数调度超时功能。前两个函数比较简单,我将详细解释第三个函数 `_reset_cpmm_timeout()` 。
+
+```
+def _conn_timeout_close(self):
+    self.error_reply(500, 'timeout')
+    self.close_connection()
+
+def _cancel_conn_timeout(self):
+    if self._conn_timeout:
+        self._conn_timeout.cancel()
+
+def _reset_conn_timeout(self, timeout=TIMEOUT):
+    self._cancel_conn_timeout()
+    self._conn_timeout = self.loop.call_later(
+        timeout, self._conn_timeout_close)
+```
+
+每当 `_reset_conn_timeout` 函数被调用时,它会先取消之前所有赋值给 `self._conn_timeout` 的 `asyncio.Handle` 对象。然后,使用 [BaseEventLoop.call_later][12] 函数让 `_conn_timeout_close` 函数在超时数秒(`timeout`)后执行。如果你还记得 `handle_request` 函数的内容,就知道每当接收到数据时,这个函数就会被调用。这就取消了当前的超时并且重新安排 `_conn_timeout_close` 函数在超时数秒(`timeout`)后执行。只要接收到数据,这个循环就会不断地重置超时回调。如果在超时时间内没有接收到数据,最后函数 `_conn_timeout_close` 就会被调用。
+
+### 创建连接
+
+我们需要创建 `HTTPConnection` 对象,并且正确地使用它们。这一任务由 `HTTPServer` 类完成。`HTTPServer` 类是一个简单的容器,可以存储着一些配置信息(解析器,路由和事件循环实例),并使用这些配置来创建 `HTTPConnection` 实例:
+
+```
+class HTTPServer(object):
+    def init(self, router, http_parser, loop):
+        self.router = router
+        self.http_parser = http_parser
+        self.loop = loop
+
+    async def handle_connection(self, reader, writer):
+        connection = HTTPConnection(self, reader, writer)
+        asyncio.ensure_future(connection.handle_request(), loop=self.loop)
+```
+
+`HTTPServer` 的每一个实例能够监听一个端口。它有一个 `handle_connection` 的异步方法来创建 `HTTPConnection` 的实例,并安排它们在事件循环中运行。这个方法被传递给 [asyncio.start_server][13] 作为一个回调函数。也就是说,每当一个 TCP 连接初始化时(以 `StreamReader` 和 `StreamWriter` 为参数),它就会被调用。
+
+```
+   self._server = HTTPServer(self.router, self.http_parser, self.loop)
+   self._connection_handler = asyncio.start_server(
+        self._server.handle_connection,
+        host=self.host,
+        port=self.port,
+        reuse_address=True,
+        reuse_port=True,
+        loop=self.loop)
+```
+
+这就是构成整个应用程序工作原理的核心:`asyncio.start_server` 接受 TCP 连接,然后在一个预配置的 `HTTPServer` 对象上调用一个方法。这个方法将处理一条 TCP 连接的所有逻辑:读取、解析、生成响应并发送回客户端、以及关闭连接。它的重点是 IO 逻辑、解析和生成响应。
+
+讲解了核心的 IO 部分,让我们继续。
+
+### 解析请求
+
+这个微型框架的使用者被宠坏了,不愿意和字节打交道。它们想要一个更高层次的抽象 —— 一种更加简单的方法来处理请求。这个微型框架就包含了一个简单的 HTTP 解析器,能够将字节流转化为 Request 对象。
+
+这些 Request 对象是像这样的容器:
+
+```
+class Request(object):
+    def init(self):
+        self.method = None
+        self.path = None
+        self.query_params = {}
+        self.path_params = {}
+        self.headers = {}
+        self.body = None
+        self.body_raw = None
+        self.finished = False
+```
+
+它包含了所有需要的数据,可以用一种容易理解的方法从客户端接受数据。哦,不包括 cookie ,它对身份认证是非常重要的,我会将它留在第二部分。
+
+每一个 HTTP 请求都包含了一些必需的内容,如请求路径和请求方法。它们也包含了一些可选的内容,如请求体、请求头,或是 URL 参数。随着 REST 的流行,除了 URL 参数,URL 本身会包含一些信息。比如,"/user/1/edit" 包含了用户的 id 。
+
+一个请求的每个部分都必须被识别、解析,并正确地赋值给 Request 对象的对应属性。HTTP/1.1 是一个文本协议,事实上这简化了很多东西。(HTTP/2 是一个二进制协议,这又是另一种乐趣了)
+
+解析器不需要跟踪状态,因此 `http_parser` 模块其实就是一组函数。调用函数需要用到 `Request` 对象,并将它连同一个包含原始请求信息的字节数组传递给 `parse_into` 函数。然后解析器会修改 `Request` 对象以及充当缓存的字节数组。字节数组的信息被逐渐地解析到 request 对象中。
+
+`http_parser` 模块的核心功能就是下面这个 `parse_into` 函数:
+
+```
+def parse_into(request, buffer):
+    _buffer = buffer[:]
+    if not request.method and can_parse_request_line(_buffer):
+        (request.method, request.path,
+         request.query_params) = parse_request_line(_buffer)
+        remove_request_line(_buffer)
+
+    if not request.headers and can_parse_headers(_buffer):
+        request.headers = parse_headers(_buffer)
+        if not has_body(request.headers):
+            request.finished = True
+
+        remove_intro(_buffer)
+
+    if not request.finished and can_parse_body(request.headers, _buffer):
+        request.body_raw, request.body = parse_body(request.headers, _buffer)
+        clear_buffer(_buffer)
+        request.finished = True
+    return _buffer
+```
+
+从上面的代码中可以看到,我把解析的过程分为三个部分:解析请求行(这行像这样:GET /resource HTTP/1.1),解析请求头以及解析请求体。
+
+请求行包含了 HTTP 请求方法以及 URL 地址。而 URL 地址则包含了更多的信息:路径、url 参数和开发者自定义的 url 参数。解析请求方法和 URL 还是很容易的 - 合适地分割字符串就好了。函数 `urlparse.parse` 可以用来解析 URL 参数。开发者自定义的 URL 参数可以通过正则表达式来解析。
+
+接下来是 HTTP 头部。它们是一行行由键值对组成的简单文本。问题在于,可能有多个 HTTP 头有相同的名字,却有不同的值。一个值得关注的 HTTP 头部是 `Content-Length`,它描述了请求体的字节长度(不是整个请求,仅仅是请求体)。这对于决定是否解析请求体有很重要的作用。
+
+最后,解析器根据 HTTP 方法和头部来决定是否解析请求体。
+
+### 路由!
+
+在某种意义上,路由就像是连接框架和用户的桥梁,用户用合适的方法创建 `Router` 对象并为其设置路径/函数对,然后将它赋值给 App 对象。而 App 对象依次调用 `get_handler` 函数生成相应的回调函数。简单来说,路由就负责两件事,一是存储路径/函数对,二是返回需要的路径/函数对
+
+`Router` 类中有两个允许最终开发者添加路由的方法,分别是 `add_routes` 和 `add_route`。因为 `add_routes` 就是 `add_route` 函数的一层封装,我们将主要讲解 `add_route` 函数:
+
+```
+def add_route(self, path, handler):
+    compiled_route = self.class.build_route_regexp(path)
+    if compiled_route not in self.routes:
+        self.routes[compiled_route] = handler
+    else:
+        raise DuplicateRoute
+```
+
+首先,这个函数使用 `Router.build_router_regexp` 的类方法,将一条路由规则(如 '/cars/{id}' 这样的字符串),“编译”到一个已编译的正则表达式对象。这些已编译的正则表达式用来匹配请求路径,以及解析开发者自定义的 URL 参数。如果已经存在一个相同的路由,程序就会抛出一个异常。最后,这个路由/处理程序对被添加到一个简单的字典`self.routes`中。
+
+下面展示 Router 是如何“编译”路由的:
+
+```
+@classmethod
+def build_route_regexp(cls, regexp_str):
+    """
+    Turns a string into a compiled regular expression. Parses '{}' into
+    named groups ie. '/path/{variable}' is turned into
+    '/path/(?P<variable>[a-zA-Z0-9_-]+)'.
+
+    :param regexp_str: a string representing a URL path.
+    :return: a compiled regular expression.
+    """
+    def named_groups(matchobj):
+        return '(?P<{0}>[a-zA-Z0-9_-]+)'.format(matchobj.group(1))
+
+    re_str = re.sub(r'{([a-zA-Z0-9_-]+)}', named_groups, regexp_str)
+    re_str = ''.join(('^', re_str, '$',))
+    return re.compile(re_str)
+```
+
+这个方法使用正则表达式将所有出现的 `{variable}` 替换为 `(?P<variable>)`。然后在字符串头尾分别添加 `^` 和 `$` 标记,最后编译正则表达式对象。
+
+完成了路由存储仅成功了一半,下面是如何得到路由对应的函数:
+
+```
+def get_handler(self, path):
+    logger.debug('Getting handler for: {0}'.format(path))
+    for route, handler in self.routes.items():
+        path_params = self.class.match_path(route, path)
+        if path_params is not None:
+            logger.debug('Got handler for: {0}'.format(path))
+            wrapped_handler = HandlerWrapper(handler, path_params)
+            return wrapped_handler
+
+    raise NotFoundException()
+```
+
+一旦 `App` 对象获得一个 `Request` 对象,也就获得了 URL 的路径部分(如 /users/15/edit)。然后,我们需要匹配函数来生成一个响应或者 404 错误。`get_handler` 函数将路径作为参数,循环遍历路由,对每条路由调用 `Router.match_path` 类方法检查是否有已编译的正则对象与这个请求路径匹配。如果存在,我们就调用 `HandleWrapper` 来包装路由对应的函数。`path_params` 字典包含了路径变量(如 '/users/15/edit' 中的 '15'),若路由没有指定变量,字典就为空。最后,我们将包装好的函数返回给 `App` 对象。
+
+如果遍历了所有的路由都找不到与路径匹配的,函数就会抛出 `NotFoundException` 异常。
+
+这个 `Route.match` 类方法挺简单:
+
+```
+def match_path(cls, route, path):
+    match = route.match(path)
+    try:
+        return match.groupdict()
+    except AttributeError:
+        return None
+```
+
+它使用正则对象的 [match 方法][14]来检查路由是否与路径匹配。若果不匹配,则返回 None 。
+
+最后,我们有 `HandleWraapper` 类。它的唯一任务就是封装一个异步函数,存储 `path_params` 字典,并通过 `handle` 方法对外提供一个统一的接口。
+
+```
+class HandlerWrapper(object):
+    def init(self, handler, path_params):
+        self.handler = handler
+        self.path_params = path_params
+        self.request = None
+
+    async def handle(self, request):
+        return await self.handler(request, **self.path_params)
+```
+
+### 组合到一起
+
+框架的最后部分就是用 `App` 类把所有的部分联系起来。
+
+`App` 类用于集中所有的配置细节。一个 `App` 对象通过其 `start_server` 方法,使用一些配置数据创建一个 `HTTPServer` 的实例,然后将它传递给 [asyncio.start_server 函数][15]。`asyncio.start_server` 函数会对每一个 TCP 连接调用 `HTTPServer` 对象的 `handle_connection` 方法。
+
+```
+def start_server(self):
+    if not self._server:
+        self.loop = asyncio.get_event_loop()
+        self._server = HTTPServer(self.router, self.http_parser, self.loop)
+        self._connection_handler = asyncio.start_server(
+            self._server.handle_connection,
+            host=self.host,
+            port=self.port,
+            reuse_address=True,
+            reuse_port=True,
+            loop=self.loop)
+
+        logger.info('Starting server on {0}:{1}'.format(
+            self.host, self.port))
+        self.loop.run_until_complete(self._connection_handler)
+
+        try:
+            self.loop.run_forever()
+        except KeyboardInterrupt:
+            logger.info('Got signal, killing server')
+        except DiyFrameworkException as e:
+            logger.error('Critical framework failure:')
+            logger.error(e.traceback)
+        finally:
+            self.loop.close()
+    else:
+        logger.info('Server already started - {0}'.format(self))
+```
+
+### 总结
+
+如果你查看源码,就会发现所有的代码仅 320 余行(包括测试代码的话共 540 余行)。这么少的代码实现了这么多的功能,让我有点惊讶。这个框架没有提供模板、身份认证以及数据库访问等功能(这些内容也很有趣哦)。这也让我知道,像 Django 和 Tornado 这样的框架是如何工作的,而且我能够快速地调试它们了。
+
+这也是我按照测试驱动开发完成的第一个项目,整个过程有趣而有意义。先编写测试用例迫使我思考设计和架构,而不仅仅是把代码放到一起,让它们可以运行。不要误解我的意思,有很多时候,后者的方式更好。不过如果你想给确保这些不怎么维护的代码在之后的几周甚至几个月依然工作,那么测试驱动开发正是你需要的。
+
+我研究了下[整洁架构][16]以及依赖注入模式,这些充分体现在 `Router` 类是如何作为一个更高层次的抽象的(实体?)。`Router` 类是比较接近核心的,像 `http_parser` 和 `App` 的内容比较边缘化,因为它们只是完成了极小的字符串和字节流、或是中层 IO 的工作。测试驱动开发(TDD)迫使我独立思考每个小部分,这使我问自己这样的问题:方法调用的组合是否易于理解?类名是否准确地反映了我正在解决的问题?我的代码中是否很容易区分出不同的抽象层?
+
+来吧,写个小框架,真的很有趣:)
+
+--------------------------------------------------------------------------------
+
+via: http://mattscodecave.com/posts/simple-python-framework-from-scratch.html
+
+作者:[Matt][a]
+译者:[Cathon](https://github.com/Cathon)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://mattscodecave.com/hire-me.html
+[1]: https://github.com/sirMackk/diy_framework
+[2]:https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
+[3]: https://en.wikipedia.org/wiki/HTTP_persistent_connection
+[4]: https://en.wikipedia.org/wiki/TCP_congestion-avoidance_algorithm#Slow_start
+[5]: https://docs.python.org/3/library/asyncio-stream.html
+[6]: https://docs.python.org/3/library/asyncio-protocol.html
+[7]: https://github.com/sirMackk/diy_framework/blob/88968e6b30e59504251c0c7cd80abe88f51adb79/diy_framework/http_server.py#L46
+[8]: https://docs.python.org/3/library/functions.html#bytearray
+[9]: https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.Handle
+[10]: https://en.wikipedia.org/wiki/Dependency_injection
+[11]: https://docs.python.org/3/library/unittest.mock.html
+[12]: https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.BaseEventLoop.call_later
+[13]: https://docs.python.org/3/library/asyncio-stream.html#asyncio.start_server
+[14]: https://docs.python.org/3/library/re.html#re.match
+[15]: https://docs.python.org/3/library/asyncio-stream.html?highlight=start_server#asyncio.start_server
+[16]: https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
diff --git a/published/20160616 Part 1 - Scientific Audio Processing How to read and write Audio files with Octave 4.0.0 on Ubuntu.md b/published/20160616 Part 1 - Scientific Audio Processing How to read and write Audio files with Octave 4.0.0 on Ubuntu.md
new file mode 100644
index 0000000000..56bddceaa1
--- /dev/null
+++ b/published/20160616 Part 1 - Scientific Audio Processing How to read and write Audio files with Octave 4.0.0 on Ubuntu.md	
@@ -0,0 +1,141 @@
+科学音频处理(一):怎样使用 Octave 对音频文件进行读写操作
+================
+
+Octave 是一个类似于 Linux 上的 Matlab 的软件,它拥有数量众多的函数和命令,支持声音采集、记录、回放以及音频信号的数字化处理,用于娱乐应用、研究、医学以及其它科学领域。在本教程中,我们会在 Ubuntu 上使用 Octave 的 4.0.0 版本读取音频文件,然后通过生成信号并且播放来模仿在各种情况下对音频信号的使用。
+
+本教程中关注的不是安装和学习使用安装好的音频处理软件,而是从设计和音频工程的角度理解它是如何工作的。
+
+### 环境准备
+
+首先是安装 octave,在 Ubuntu 终端运行下面的命令添加 Octave PPA,然后安装 Octave 。
+
+```
+sudo apt-add-repository ppa:octave/stable
+sudo apt-get update
+sudo apt-get install octave
+```
+
+### 步骤1:打开 Octave
+
+在这一步中我们单击软件图标打开 Octave,可以通过单击下拉式按钮选择工作路径。
+
+![](https://www.howtoforge.com/images/how-to-read-and-write-audio-files-with-octave-4-in-ubuntu/initial.png)
+
+### 步骤2:音频信息
+
+使用`audioinfo`命令查看要处理的音频文件的相关信息。
+
+```
+>> info = audioinfo ('testing.ogg')
+```
+
+![](https://www.howtoforge.com/images/how-to-read-and-write-audio-files-with-octave-4-in-ubuntu/audioinfo.png)
+
+
+### 步骤3:读取音频文件
+
+在本教程中我会使用 ogg 文件来读取这种文件的属性,比如采样、音频类型(stereo 和 mono)、信道数量等。必须声明的一点是教程中使用的所有的命令都是在 Octave 终端窗口中执行的。首先,我们必须要把这个 ogg 文件赋给一个变量。注意:**文件必须在 Octave 的工作路径中。**
+
+```
+>> file='yourfile.ogg'
+```
+
+```
+>> [M, fs] = audioread(file)
+```
+
+这里的 M 是一个一列或两列的矩阵,取决于信道的数量,fs 是采样率。
+
+![](https://www.howtoforge.com/images/how-to-read-and-write-audio-files-with-octave-4-in-ubuntu/reading.png)
+
+![](https://www.howtoforge.com/images/how-to-read-and-write-audio-files-with-octave-4-in-ubuntu/matrix.png)
+
+![](https://www.howtoforge.com/images/how-to-read-and-write-audio-files-with-octave-4-in-ubuntu/big/frequency.png)
+
+
+下面的操作都可以读取音频文件:
+
+```
+>> [y, fs] = audioread (filename, samples)
+
+>> [y, fs] = audioread (filename, datatype)
+
+>> [y, fs] = audioread (filename, samples, datatype)
+```
+
+samples 指定开始帧和结束帧,datatype 指定返回的数据类型。可以为所有变量设置值:
+
+```
+>> samples = [1, fs)
+
+>> [y, fs] = audioread (filename, samples)
+```
+
+数据类型:
+
+```
+>> [y,Fs] = audioread(filename,'native')
+```
+
+如果值是“native”,那么它的数据类型就依数据在音频文件中的存储情况而定。
+
+### 步骤4:音频文件的写操作
+
+新建一个 ogg 文件:
+
+我们会从一个余弦值创建一个 ogg 文件。采样率是每秒 44100 次,这个文件最少进行 10 秒的采样。余弦信号的频率是 440 Hz。
+
+```
+>> filename='cosine.ogg';
+>> fs=44100;
+>> t=0:1/fs:10;
+>> w=2*pi*440*t;
+>> signal=cos(w);
+>> audiowrite(filename, signal, fs);
+```
+
+这就在工作路径中创建了一个 'cosine.ogg' 文件,这个文件中包含余弦信号。
+
+![](https://www.howtoforge.com/images/how-to-read-and-write-audio-files-with-octave-4-in-ubuntu/cosinefile.png)
+
+播放这个 'cosine.ogg' 文件就会产生一个 440Hz 的 音调,这个音调正好是乐理中的 'A' 调。如果需要查看保存在文件中的值就必须使用 'audioread' 函数读取文件。在后续的教程中,我们会看到怎样在两个信道中读取一个音频文件。
+
+### 步骤5:播放音频文件
+
+Octave 有一个默认的音频播放器,可以用这个音频播放器进行测试。使用下面的函数:
+
+```
+>> [y,fs]=audioread('yourfile.ogg');
+>> player=audioplayer(y, fs, 8)
+
+  scalar structure containing the fields:
+
+    BitsPerSample =  8
+    CurrentSample = 0
+    DeviceID = -1
+    NumberOfChannels =  1
+    Running = off
+    SampleRate =  44100
+    TotalSamples =  236473
+    Tag =
+    Type = audioplayer
+    UserData = [](0x0)
+>> play(player);
+```
+
+
+在这个教程的续篇,我们会进入音频处理的高级特性部分,可能会接触到一些科学和商业应用中的实例。
+
+--------------------------------------------------------------------------------
+
+via: https://www.howtoforge.com/tutorial/how-to-read-and-write-audio-files-with-octave-4-in-ubuntu/
+
+作者:[David Duarte][a]
+译者:[vim-kakali](https://github.com/vim-kakali)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://twitter.com/intent/follow?original_referer=https%3A%2F%2Fwww.howtoforge.com%2Ftutorial%2Fhow-to-read-and-write-audio-files-with-octave-4-in-ubuntu%2F&ref_src=twsrc%5Etfw&region=follow_link&screen_name=howtoforgecom&tw_p=followbutton
+
+
diff --git a/published/20160620 5 Best Linux Package Managers for Linux Newbies.md b/published/20160620 5 Best Linux Package Managers for Linux Newbies.md
new file mode 100644
index 0000000000..e264bf8819
--- /dev/null
+++ b/published/20160620 5 Best Linux Package Managers for Linux Newbies.md	
@@ -0,0 +1,114 @@
+5 个给 Linux 新手的最佳包管理器
+=====================================================
+
+一个 Linux 新用户应该知道他或她的进步源自于对 Linux 发行版的使用,而 Linux 发行版有好几种,并以不同的方式管理软件包。
+
+在 Linux 中,包管理器非常重要,知道如何使用多种包管理器可以让你像一个高手一样活得很舒适,从在仓库下载软件、安装软件,到更新软件、处理依赖和删除软件是非常重要的,这也是Linux 系统管理的一个重要部分。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Best-Linux-Package-Managers.png)
+
+*最好的Linux包管理器*
+
+成为一个 Linux 高手的一个标志是了解主要的 Linux 发行版如何处理包,在这篇文章中,我们应该看一些你在 Linux 上能找到的最佳的包管理器,
+
+在这里,我们的主要重点是关于一些最佳包管理器的相关信息,但不是如何使用它们,这些留给你亲自发现。但我会提供一些有意义的链接,使用指南或更多。
+
+### 1. DPKG - Debian 包管理系统(Debian Package Management System)
+
+Dpkg 是 Debian Linux 家族的基础包管理系统,它用于安装、删除、存储和提供`.deb`包的信息。
+
+这是一个低层面的工具,并且有多个前端工具可以帮助用户从远程的仓库获取包,或处理复杂的包关系的工具,包括如下:
+
+- 参考:[15 个用于基于 Debian 的发行版的 “dpkg” 命令实例][1]
+
+#### APT (高级打包工具(Advanced Packaging Tool))
+
+这个是一个 dpkg 包管理系统的前端工具,它是一个非常受欢迎的、自由而强大的,有用的命令行包管理器系统。
+
+Debian 及其衍生版,例如 Ubuntu 和 Linux Mint 的用户应该非常熟悉这个包管理工具。
+
+想要了解它是如何工作的,你可以去看看下面这些 HOW TO 指南:
+
+- 参考:[15 个怎样在 Ubuntu/Debian 上使用新的 APT 工具的例子][2]
+- 参考:[25 个用于包管理的有用的 APT-GET 和 APT-CACHE 的基础命令][3]
+
+#### Aptitude 包管理器
+
+这个也是 Debian Linux 家族一个非常出名的命令行前端包管理工具,它工作方式类似 APT ,它们之间有很多可以比较的地方,不过,你应该两个都试试才知道哪个工作的更好。
+
+它最初为 Debian 及其衍生版设计的,但是现在它的功能延伸到 RHEL 家族。你可以参考这个指南了解更多关于 APT 和 Aptitude。
+
+- 参考:[APT 和 Aptitude 是什么?它们知道到底有什么不同?][4]
+
+#### Synaptic 包管理器
+
+Synaptic是一个基于GTK+的APT的可视化包管理器,对于一些不想使用命令行的用户,它非常好用。
+
+### 2. RPM 红帽包管理器(Red Hat Package Manager)
+
+这个是红帽创建的 Linux 基本标准(LSB)打包格式和基础包管理系统。基于这个底层系统,有多个前端包管理工具可供你使用,但我们应该只看那些最好的,那就是:
+
+#### YUM (黄狗更新器,修改版(Yellowdog Updater, Modified))
+
+这个是一个开源、流行的命令行包管理器,它是用户使用 RPM 的界面(之一)。你可以把它和 Debian Linux 系统中的 APT 进行对比,它和 APT 拥有相同的功能。你可以从这个 HOW TO 指南中的例子更加清晰的理解YUM:
+
+- 参考:[20 个用于包管理的 YUM 命令][5]
+
+#### DNF(优美的 Yum(Dandified Yum))
+
+这个也是一个用于基于 RPM 的发行版的包管理器,Fedora 18 引入了它,它是下一代 YUM。
+
+如果你用 Fedora 22 及更新版本,你肯定知道它是默认的包管理器。这里有一些链接,将为你提供更多关于 DNF 的信息和如何使用它。
+
+- 参考:[DNF - 基于 RPM 的发行版的下一代通用包管理软件][6]
+- 参考: [27 个管理 Fedora 软件包的 ‘DNF’ 命令例子][7]
+
+### 3. Pacman 包管理器 – Arch Linux
+
+这个是一个流行的、强大而易用的包管理器,它用于 Arch Linux 和其他的一些小众发行版。它提供了一些其他包管理器提供的基本功能,包括安装、自动解决依赖关系、升级、卸载和降级软件。
+
+但是最大的用处是,它为 Arch 用户创建了一个简单易用的包管理方式。你可以阅读 [Pacman 概览][8],它会解释上面提到的一些功能。
+
+### 4. Zypper 包管理器 – openSUSE
+
+这个是一个使用 libzypp 库制作的用于 OpenSUSE 系统上的命令行包管理器,它的常用功能包括访问仓库、安装包、解决依赖问题和其他功能。
+
+更重要的是,它也可以支持存储库扩展功能,如模式、补丁和产品。新的 OpenSUSE 用户可以参考下面的链接来掌控它。
+
+- 参考:[45 个让你精通 openSUSE 包管理的 Zypper 命令][9]
+
+### 5. Portage 包管理器 – Gentoo
+
+这个是 Gentoo 的包管理器,当下不怎么流行的一个发行版,但是这并不阻止它成为 Linux 下最好的软件包管理器之一。
+
+Portage 项目的主要目标是创建一个简单、无故障的包管理系统,包含向后兼容、自动化等功能。
+
+如果希望理解的更清晰,可以看下: [Portage 项目页][10]。
+
+### 结束语
+
+正如我在开始时提到的,这个指南的主要意图是给 Linux 用户提供一个最佳软件包管理器的列表,但知道如何使用它们可以通过其后提供的重要的链接,并实际去试试它们。
+
+各个发行版的用户需要学习超出他们的发行版之外的一些东西,才能更好理解上述提到的这些不同的包管理器。
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/linux-package-managers/
+
+作者:[Ravi Saive][a]
+译者:[Bestony](https://github.com/bestony)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/admin/
+[1]: http://www.tecmint.com/dpkg-command-examples/
+[2]: http://www.tecmint.com/apt-advanced-package-command-examples-in-ubuntu/
+[3]: http://www.tecmint.com/useful-basic-commands-of-apt-get-and-apt-cache-for-package-management/
+[4]: http://www.tecmint.com/difference-between-apt-and-aptitude/
+[5]: http://www.tecmint.com/20-linux-yum-yellowdog-updater-modified-commands-for-package-mangement/
+[6]: http://www.tecmint.com/dnf-next-generation-package-management-utility-for-linux/
+[7]: http://www.tecmint.com/dnf-commands-for-fedora-rpm-package-management/
+[8]: https://wiki.archlinux.org/index.php/Pacman
+[9]: http://www.tecmint.com/zypper-commands-to-manage-suse-linux-package-management/
+[10]: https://wiki.gentoo.org/wiki/Project:Portage
diff --git a/published/20160625 Ubuntu’s Snap, Red Hat’s Flatpak And Is ‘One Fits All’ Linux Packages Useful.md b/published/20160625 Ubuntu’s Snap, Red Hat’s Flatpak And Is ‘One Fits All’ Linux Packages Useful.md
new file mode 100644
index 0000000000..67a78ae96f
--- /dev/null
+++ b/published/20160625 Ubuntu’s Snap, Red Hat’s Flatpak And Is ‘One Fits All’ Linux Packages Useful.md	
@@ -0,0 +1,110 @@
+Ubuntu 的 Snap、Red Hat 的 Flatpak 这种通吃所有发行版的打包方式真的有用吗?
+=================================================================================
+
+![](http://www.iwillfolo.com/wordpress/wp-content/uploads/2016/06/Flatpak-and-Snap-Packages.jpg)
+
+**对新一代的打包格式开始渗透到 Linux 生态系统中的深入观察**
+
+最近我们听到越来越多的有关于 Ubuntu 的 Snap 包和由 Red Hat 员工 Alexander Larsson 创造的 Flatpak (曾经叫做 xdg-app)的消息。
+
+这两种下一代打包方法在本质上拥有相同的目标和特点:即不依赖于第三方系统功能库的独立包装。
+
+这种 Linux 新技术方向似乎自然会让人脑海中浮现这样的问题:独立包的优点/缺点是什么?这是否让我们拥有更好的 Linux 系统?其背后的动机是什么?
+
+为了回答这些问题,让我们先深入了解一下 Snap 和 Flatpak。
+
+### 动机
+
+根据 [Flatpak][1] 和 [Snap][2] 的声明,背后的主要动机是使同一版本的应用程序能够运行在多个 Linux 发行版。
+
+> “从一开始它的主要目标是允许相同的应用程序运行在各种 Linux 发行版和操作系统上。” —— Flatpak
+
+> “……‘snap’ 通用 Linux 包格式,使简单的二进制包能够完美的、安全的运行在任何 Linux 桌面、服务器、云和设备上。” —— Snap
+
+说得更具体一点,站在 Snap 和 Flatpak (以下称之为 S&F)背后的人认为,Linux 平台存在碎片化的问题。
+
+这个问题导致了开发者们需要做许多不必要的工作来使他的软件能够运行在各种不同的发行版上,这影响了整个平台的前进。
+
+所以,作为 Linux 发行版(Ubuntu 和 Red Hat)的领导者,他们希望消除这个障碍,推动平台发展。
+
+但是,是否是更多的个人收益刺激了 S&F 的开发?
+
+#### 个人收益?
+
+虽然没有任何官方声明,但是试想一下,如果能够创造这种可能会被大多数发行版(即便不是全部)所采用的打包方式,那么这个项目的领导者将可能成为一个能够决定 Linux 大船航向的重要人物。
+
+### 优势
+
+这种独立包的好处多多,并且取决于不同的因素。
+
+这些因素基本上可以归为两类:
+
+#### 用户角度
+
+**+** 从 Liunx 用户的观点来看:Snap 和 Flatpak 带来了将任何软件包(软件或应用)安装在用户使用的任何发行版上的可能性。
+
+例如你在使用一个不是很流行的发行版,由于开发工作的缺乏,它的软件仓库只有很稀少的包。现在,通过 S&F 你就可以显著的增加包的数量,这是一个多么美好的事情。
+
+**+** 同样,对于使用流行的发行版的用户,即使该发行版的软件仓库上有很多的包,他也可以在不改变它现有的功能库的同时安装一个新的包。
+
+比方说, 一个 Debian 的用户想要安装一个 “测试分支” 的包,但是他又不想将他的整个系统变成测试版(来让该包运行在更新的功能库上)。现在,他就可以简单的想安装哪个版本就安装哪个版本,而不需要考虑库的问题。
+
+对于持后者观点的人,可能基本上都是使用源文件编译他们的包的人,然而,除非你使用类似 Gentoo 这样基于源代码的发行版,否则大多数用户将从头编译视为是一个恶心到吐的事情。
+
+**+** 高级用户,或者称之为 “拥有安全意识的用户” 可能会觉得更容易接受这种类型的包,只要它们来自可靠来源,这种包倾向于提供另一层隔离,因为它们通常是与系统包想隔离的。
+
+\* 不论是 Snap 还是 Flatpak 都在不断努力增强它们的安全性,通常他们都使用 “沙盒化” 来隔离,以防止它们可能携带病毒感染整个系统,就像微软 Windows 系统中的 .exe 程序一样。(关于微软和 S&F 后面还会谈到)
+
+#### 开发者角度
+
+与普通用户相比,对于开发者来说,开发 S&F 包的优点可能更加清楚。这一点已经在上一节有所提示。
+
+尽管如此,这些优点有:
+
+**+** S&F 通过统一开发的过程,将多发行版的开发变得简单了起来。对于需要将他的应用运行在多个发行版的开发者来说,这大大的减少了他们的工作量。
+
+**++** 因此,开发者能够更容易的使他的应用运行在更多的发行版上。
+
+**+** S&F 允许开发者私自发布他的包,不需要依靠发行版维护者在每一个/每一次发行版中发布他的包。
+
+**++** 通过上述方法,开发者可以不依赖发行版而直接获取到用户安装和卸载其软件的统计数据。
+
+**++** 同样是通过上述方法,开发者可以更好的直接与用户互动,而不需要通过中间媒介,比如发行版这种中间媒介。
+
+### 缺点
+
+**–** 膨胀。就是这么简单。Flatpak 和 Snap 并不是凭空变出来它的依赖关系。相反,它是通过将依赖关系预构建在其中来代替使用系统中的依赖关系。
+
+就像谚语说的:“山不来就我,我就去就山”。
+
+**–** 之前提到安全意识强的用户会喜欢 S&F 提供的额外的一层隔离,只要该应用来自一个受信任的来源。但是从另外一个角度看,对这方面了解较少的用户,可能会从一个不靠谱的地方弄来一个包含恶意软件的包从而导致危害。
+
+上面提到的观点可以说是有很有意义的,虽说今天的流行方法,像 PPA、overlay 等也可能是来自不受信任的来源。
+
+但是,S&F 包更加增加这个风险,因为恶意软件开发者只需要开发一个版本就可以感染各种发行版。相反,如果没有 S&F,恶意软件的开发者就需要创建不同的版本以适应不同的发行版。
+
+### 原来微软一直是正确的吗?
+
+考虑到上面提到的,很显然,在大多数情况下,使用 S&F 包的优点超过缺点。
+
+至少对于二进制发行版的用户,或者重点不是轻量级的发行版的用户来说是这样的。
+
+这促使我问出这个问题,可能微软一直是正确的吗?如果是的,那么当 S&F 变成 Linux 的标准后,你还会一如既往的使用 Linux 或者类 Unix 系统吗?
+
+很显然,时间会是这个问题的最好答案。
+
+不过,我认为,即使不完全正确,但是微软有些地方也是值得赞扬的,并且以我的观点来看,所有这些方式在 Linux 上都立马能用也确实是一个亮点。
+
+--------------------------------------------------------------------------------
+
+via: http://www.iwillfolo.com/ubuntus-snap-red-hats-flatpack-and-is-one-fits-all-linux-packages-useful/
+
+作者:[Editorials][a]
+译者:[Chao-zhi](https://github.com/Chao-zhi)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.iwillfolo.com/category/editorials/
+[1]: http://flatpak.org/press/2016-06-21-flatpak-released.html
+[2]: https://insights.ubuntu.com/2016/06/14/universal-snap-packages-launch-on-multiple-linux-distros
diff --git a/published/20151117 How bad a boss is Linus Torvalds.md b/published/201607/20151117 How bad a boss is Linus Torvalds.md
similarity index 100%
rename from published/20151117 How bad a boss is Linus Torvalds.md
rename to published/201607/20151117 How bad a boss is Linus Torvalds.md
diff --git a/published/20151215 Securi-Pi--Using the Raspberry Pi as a Secure Landing Point.md b/published/201607/20151215 Securi-Pi--Using the Raspberry Pi as a Secure Landing Point.md
similarity index 100%
rename from published/20151215 Securi-Pi--Using the Raspberry Pi as a Secure Landing Point.md
rename to published/201607/20151215 Securi-Pi--Using the Raspberry Pi as a Secure Landing Point.md
diff --git a/published/201607/20160104 What is good stock portfolio management software on Linux.md b/published/201607/20160104 What is good stock portfolio management software on Linux.md
new file mode 100644
index 0000000000..15c5091b6a
--- /dev/null
+++ b/published/201607/20160104 What is good stock portfolio management software on Linux.md	
@@ -0,0 +1,111 @@
+JStock:Linux 上不错的股票投资组合管理软件
+================================================================================
+
+如果你在股票市场做投资,那么你可能非常清楚投资组合管理计划有多重要。管理投资组合的目标是依据你能承受的风险,时间层面的长短和资金盈利的目标去为你量身打造的一种投资计划。鉴于这类软件的重要性,因此从来不会缺乏商业性的 app 和股票行情检测软件,每一个都可以兜售复杂的投资组合以及跟踪报告功能。
+
+对于我们这些 Linux 爱好者们,我也找到了一些**好用的开源投资组合管理工具**,用来在 Linux 上管理和跟踪股票的投资组合,这里高度推荐一个基于 java 编写的管理软件 [JStock][1]。如果你不是一个 java 粉,也许你会放弃它,JStock 需要运行在沉重的 JVM 环境上。但同时,在每一个安装了 JRE 的环境中它都可以马上运行起来,在你的 Linux 环境中它会运行的很顺畅。
+
+“开源”就意味着免费或标准低下的时代已经过去了。鉴于 JStock 只是一个个人完成的产物,作为一个投资组合管理软件它最令人印象深刻的是包含了非常多实用的功能,以上所有的荣誉属于它的作者 Yan Cheng Cheok!例如,JStock 支持通过监视列表去监控价格,多种投资组合,自选/内置的股票指标与相关监测,支持27个不同的股票市场和跨平台的云端备份/还原。JStock 支持多平台部署(Linux, OS X, Android 和 Windows),你可以通过云端保存你的 JStock 投资组合,并通过云平台无缝的备份/还原到其他的不同平台上面。
+
+现在我将向你展示如何安装以及使用过程的一些具体细节。
+
+### 在 Linux 上安装 JStock ###
+
+因为 JStock 使用Java编写,所以必须[安装 JRE][2]才能让它运行起来。小提示,JStock 需要 JRE1.7 或更高版本。如你的 JRE 版本不能满足这个需求,JStock 将会运行失败然后出现下面的报错。
+
+    Exception in thread "main" java.lang.UnsupportedClassVersionError: org/yccheok/jstock/gui/JStock : Unsupported major.minor version 51.0
+
+
+在你的 Linux 上安装好了 JRE 之后,从其官网下载最新的发布的 JStock,然后加载启动它。
+
+    $ wget https://github.com/yccheok/jstock/releases/download/release_1-0-7-13/jstock-1.0.7.13-bin.zip
+    $ unzip jstock-1.0.7.13-bin.zip
+    $ cd jstock
+    $ chmod +x jstock.sh
+    $ ./jstock.sh
+
+教程的其他部分,让我来给大家展示一些 JStock 的实用功能
+
+### 监视监控列表中股票价格的波动 ###
+
+使用 JStock 你可以创建一个或多个监视列表,它可以自动的监视股票价格的波动并给你提供相应的通知。在每一个监视列表里面你可以添加多个感兴趣的股票进去。之后在“Fall Below”和“Rise Above”的表格里添加你的警戒值,分别设定该股票的最低价格和最高价格。
+
+![](https://c2.staticflickr.com/2/1588/23795349969_37f4b0f23c_c.jpg)
+
+例如你设置了 AAPL 股票的最低/最高价格分别是 $102 和 $115.50,只要在价格低于 $102 或高于 $115.50 时你就得到桌面通知。
+
+你也可以设置邮件通知,这样你将收到一些价格信息的邮件通知。设置邮件通知在“Options”菜单里,在“Alert”标签中国,打开“Send message to email(s)”,填入你的 Gmail 账户。一旦完成 Gmail 认证步骤,JStock 就会开始发送邮件通知到你的 Gmail 账户(也可以设置其他的第三方邮件地址)。
+
+![](https://c2.staticflickr.com/2/1644/24080560491_3aef056e8d_b.jpg)
+
+### 管理多个投资组合 ###
+
+JStock 允许你管理多个投资组合。这个功能对于你使用多个股票经纪人时是非常实用的。你可以为每个经纪人创建一个投资组合去管理你的“买入/卖出/红利”用来了解每一个经纪人的业务情况。你也可以在“Portfolio”菜单里面选择特定的投资组合来切换不同的组合项目。下面是一张截图用来展示一个假设的投资组合。
+
+![](https://c2.staticflickr.com/2/1646/23536385433_df6c036c9a_c.jpg)
+
+你也可以设置付给中介费,你可以为每个买卖交易设置中介费、印花税以及结算费。如果你比较懒,你也可以在选项菜单里面启用自动费用计算,并提前为每一家经济事务所设置费用方案。当你为你的投资组合增加交易之后,JStock 将自动的计算并计入费用。
+
+![](https://c2.staticflickr.com/2/1653/24055085262_0e315c3691_b.jpg)
+
+### 使用内置/自选股票指标来监控 ###
+
+如果你要做一些股票的技术分析,你可能需要基于各种不同的标准来监控股票(这里叫做“股票指标”)。对于股票的跟踪,JStock提供多个[预设的技术指示器][3] 去获得股票上涨/下跌/逆转指数的趋势。下面的列表里面是一些可用的指标。
+
+- 平滑异同移动平均线(MACD)
+- 相对强弱指标 (RSI)
+- 资金流向指标 (MFI)
+- 顺势指标 (CCI)
+- 十字线
+- 黄金交叉线,死亡交叉线
+- 涨幅/跌幅 
+
+开启预设指示器能需要在 JStock 中点击“Stock Indicator Editor”标签。之后点击右侧面板中的安装按钮。选择“Install from JStock server”选项,之后安装你想要的指示器。
+
+![](https://c2.staticflickr.com/2/1476/23867534660_b6a9c95a06_c.jpg)
+
+一旦安装了一个或多个指示器,你可以用他们来扫描股票。选择“Stock Indicator Scanner”标签,点击底部的“Scan”按钮,选择需要的指示器。
+
+![](https://c2.staticflickr.com/2/1653/24137054996_e8fcd10393_c.jpg)
+
+当你选择完需要扫描的股票(例如, NYSE, NASDAQ)以后,JStock 将执行该扫描,并将该指示器捕获的结果通过列表展现。
+
+![](https://c2.staticflickr.com/2/1446/23795349889_0f1aeef608_c.jpg)
+
+除了预设指示器以外,你也可以使用一个图形化的工具来定义自己的指示器。下面这张图例用于监控当前价格小于或等于60天平均价格的股票。
+
+![](https://c2.staticflickr.com/2/1605/24080560431_3d26eac6b5_c.jpg)
+
+### 通过云在 Linux 和 Android JStock 之间备份/恢复###
+
+另一个非常棒的功能是 JStock 支持云备份恢复。Jstock 可以通过 Google Drive 把你的投资组合/监视列表在云上备份和恢复,这个功能可以实现在不同平台上无缝穿梭。如果你在两个不同的平台之间来回切换使用 Jstock,这种跨平台备份和还原非常有用。我在 Linux 桌面和 Android 手机上测试过我的 Jstock 投资组合,工作的非常漂亮。我在 Android 上将 Jstock 投资组合信息保存到 Google Drive 上,然后我可以在我的 Linux 版的 Jstock 上恢复它。如果能够自动同步到云上,而不用我手动地触发云备份/恢复就更好了,十分期望这个功能出现。
+
+![](https://c2.staticflickr.com/2/1537/24163165565_bb47e04d6c_c.jpg)
+
+![](https://c2.staticflickr.com/2/1556/23536385333_9ed1a75d72_c.jpg)
+
+如果你在从 Google Drive 还原之后不能看到你的投资信息以及监视列表,请确认你的国家信息与“Country”菜单里面设置的保持一致。
+
+JStock 的安卓免费版可以从 [Google Play Store][4] 获取到。如果你需要完整的功能(比如云备份,通知,图表等),你需要一次性支付费用升级到高级版。我认为高级版物有所值。
+
+![](https://c2.staticflickr.com/2/1687/23867534720_18b917028c_c.jpg)
+
+写在最后,我应该说一下它的作者,Yan Cheng Cheok,他是一个十分活跃的开发者,有bug及时反馈给他。这一切都要感谢他!!!
+
+关于 JStock 这个投资组合跟踪软件你有什么想法呢?
+
+--------------------------------------------------------------------------------
+
+via: http://xmodulo.com/stock-portfolio-management-software-Linux.html
+
+作者:[Dan Nanni][a]
+译者:[ivo-wang](https://github.com/ivo-wang)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://Linux.cn/) 荣誉推出
+
+[a]:http://xmodulo.com/author/nanni
+[1]:http://jstock.org/
+[2]:http://ask.xmodulo.com/install-java-runtime-Linux.html
+[3]:http://jstock.org/ma_indicator.html
+[4]:https://play.google.com/store/apps/details?id=org.yccheok.jstock.gui
diff --git a/published/20160218 How to Best Manage Encryption Keys on Linux.md b/published/201607/20160218 How to Best Manage Encryption Keys on Linux.md
similarity index 100%
rename from published/20160218 How to Best Manage Encryption Keys on Linux.md
rename to published/201607/20160218 How to Best Manage Encryption Keys on Linux.md
diff --git a/published/20160218 What do Linux developers think of Git and GitHub.md b/published/201607/20160218 What do Linux developers think of Git and GitHub.md
similarity index 100%
rename from published/20160218 What do Linux developers think of Git and GitHub.md
rename to published/201607/20160218 What do Linux developers think of Git and GitHub.md
diff --git a/published/20160219 How to Setup Lighttpd Web server on Ubuntu 15.04 or CentOS 7.md b/published/201607/20160219 How to Setup Lighttpd Web server on Ubuntu 15.04 or CentOS 7.md
similarity index 100%
rename from published/20160219 How to Setup Lighttpd Web server on Ubuntu 15.04 or CentOS 7.md
rename to published/201607/20160219 How to Setup Lighttpd Web server on Ubuntu 15.04 or CentOS 7.md
diff --git a/published/20160220 Best Cloud Services For Linux To Replace Copy.md b/published/201607/20160220 Best Cloud Services For Linux To Replace Copy.md
similarity index 100%
rename from published/20160220 Best Cloud Services For Linux To Replace Copy.md
rename to published/201607/20160220 Best Cloud Services For Linux To Replace Copy.md
diff --git a/published/20160301 The Evolving Market for Commercial Software Built On Open Source.md b/published/201607/20160301 The Evolving Market for Commercial Software Built On Open Source.md
similarity index 100%
rename from published/20160301 The Evolving Market for Commercial Software Built On Open Source.md
rename to published/201607/20160301 The Evolving Market for Commercial Software Built On Open Source.md
diff --git a/published/20160303 Top 5 open source command shells for Linux.md b/published/201607/20160303 Top 5 open source command shells for Linux.md
similarity index 100%
rename from published/20160303 Top 5 open source command shells for Linux.md
rename to published/201607/20160303 Top 5 open source command shells for Linux.md
diff --git a/published/20160304 Microservices with Python RabbitMQ and Nameko.md b/published/201607/20160304 Microservices with Python RabbitMQ and Nameko.md
similarity index 100%
rename from published/20160304 Microservices with Python RabbitMQ and Nameko.md
rename to published/201607/20160304 Microservices with Python RabbitMQ and Nameko.md
diff --git a/published/20160425 How to Use Awk to Print Fields and Columns in File.md b/published/201607/20160425 How to Use Awk to Print Fields and Columns in File.md
similarity index 100%
rename from published/20160425 How to Use Awk to Print Fields and Columns in File.md
rename to published/201607/20160425 How to Use Awk to Print Fields and Columns in File.md
diff --git a/published/20160511 An introduction to data processing with Cassandra and Spark.md b/published/201607/20160511 An introduction to data processing with Cassandra and Spark.md
similarity index 100%
rename from published/20160511 An introduction to data processing with Cassandra and Spark.md
rename to published/201607/20160511 An introduction to data processing with Cassandra and Spark.md
diff --git a/published/201607/20160519 The future of sharing integrating Pydio and ownCloud.md b/published/201607/20160519 The future of sharing integrating Pydio and ownCloud.md
new file mode 100644
index 0000000000..5c1b998508
--- /dev/null
+++ b/published/201607/20160519 The future of sharing integrating Pydio and ownCloud.md	
@@ -0,0 +1,65 @@
+共享的未来:Pydio 与 ownCloud 的联合 
+=========================================================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/BIZ_darwincloud_520x292_0311LL.png?itok=5yWIaEDe)
+
+*图片来源 : opensource.com*
+
+开源共享生态圈内容纳了许多各异的项目,它们每一个都给出了自己的解决方案,且每一个都不按套路来。有很多原因导致你选择开源的解决方案,而非 Dropbox、Google Drive、iCloud 或 OneDrive 这些商业的解决方案。这些商业的解决方案虽然能让你不必为如何管理数据担心,但也理所应当的带着种种限制,其中就包括对于原有基础结构的控制和整合不足。
+
+对于用户而言仍有相当一部分可供选择的文件分享和同步的替代品,其中就包括了 Pydio 和 ownCloud。
+
+### Pydio
+
+Pydio (Put your data in orbit 把你的数据放上轨道) 项目由一位作曲家 Charles du Jeu 发起,起初他只是需要一种与乐队成员分享大型音频文件的方法。[Pydio][1] 是一种文件分享与同步的解决方案,综合了多存储后端,设计时还同时考虑了开发者和系统管理员两方面。在世界各地有逾百万的下载量,已被翻译成 27 种语言。
+
+项目在刚开始的时候便开源了,先是在 [SourceForge][2] 上茁壮的成长,现在已在 [GitHub][3] 上安了家。
+
+用户界面基于 Google 的 [Material 设计风格][4]。用户可以使用现有的传统文件基础结构或是根据预估的需求部署 Pydio,并通过 web、桌面和移动端应用随时随地地管理自己的东西。对于管理员来说,细粒度的访问权限绝对是配置访问时的利器。
+
+在 [Pydio 社区][5],你可以找到许多让你增速的资源。Pydio 网站 [对于如何为 Pydio GitHub 仓库贡献][6] 给出了明确的指导方案。[论坛][7]中也包含了开发者板块和社区。
+
+### ownCloud
+
+[ownCloud][8] 在世界各地拥有逾 8 百万的用户,它是一个开源、自行管理的文件同步共享技术。同步客户端支持所有主流平台并支持 WebDAV 通过 web 界面实现。ownCloud 拥有简单的使用界面,强大的管理工具,和大规模的共享及协作功能——以满足用户管理数据时的需求。
+
+ownCloud 的开放式架构是通过 API 和为应用提供平台来实现可扩展性的。迄今已有逾 300 款应用,功能包括处理像日历、联系人、邮件、音乐、密码、笔记等诸多数据类型。ownCloud 由一个数百位贡献者的国际化的社区开发,安全,并且能做到为小到一个树莓派大到好几百万用户的 PB 级存储集群量身定制。
+
+### 联合共享 (Federated sharing)
+
+文件共享开始转向团队合作时代,而标准化为合作提供了坚实的土壤。
+
+联合共享(Federated sharing)——一个由 [OpenCloudMesh][9] 项目提供的新的开放标准,就是在这个方向迈出的一步。先不说别的,在支持该标准的服务器之间分享文件和文件夹,比如说 Pydio 和 ownCloud。
+
+ownCloud 7 率先引入该标准,这种服务器到服务器的分享方式可以让你挂载远程服务器上共享的文件,实际上就是创建你自己的云上之云。你可以直接为其它支持联合共享的服务器上的用户创建共享链接。
+
+实现这个新的 API 允许存储解决方案之间更深层次的集成,同时保留了原有平台的安全,控制和特性。
+
+“交换和共享文件是当下和未来不可或缺的东西。”ownCloud 的创始人 Frank Karlitschek 说道:“正因如此,采用联合和分布的方式而非集中的数据孤岛就显得至关重要。联合共享的设计初衷便是在保证安全和用户隐私的同时追求分享的无缝、至简之道。”
+
+### 下一步是什么呢?
+
+正如 OpenCloudMesh 做的那样,将会通过像 Pydio 和 ownCloud 这样的机构和公司,合作推广这一文件共享的新开放标准。ownCloud 9 已经引入联合的服务器之间交换用户列表的功能,让你的用户们在你的服务器上享有和你同样的无缝体验。将来,一个中央地址簿服务(联合的)集合,用以检索其他联合云 ID 的构想可能会把云间合作推向一个新的高度。
+
+这一举措无疑有助于日益开放的技术社区中的那些成员方便地讨论,开发,并推动“OCM 分享 API”作为一个厂商中立协议。所有领导 OCM 项目的合作伙伴都全心致力于开放 API 的设计原则,并欢迎其他开源的文件分享和同步社区参与并加入其中。
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/business/16/5/sharing-files-pydio-owncloud
+
+作者:[ben van 't ende][a]
+译者:[martin2011qi](https://github.com/martin2011qi)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/benvantende
+[1]: https://pydio.com/
+[2]: https://sourceforge.net/projects/ajaxplorer/
+[3]: https://github.com/pydio/
+[4]: https://www.google.com/design/spec/material-design/introduction.html
+[5]: https://pydio.com/en/community
+[6]: https://pydio.com/en/community/contribute
+[7]: https://pydio.com/forum/f
+[8]: https://owncloud.org/
+[9]: https://wiki.geant.org/display/OCM/Open+Cloud+Mesh
diff --git a/published/20160527 Turn Your Old Laptop into a Chromebook.md b/published/201607/20160527 Turn Your Old Laptop into a Chromebook.md
similarity index 100%
rename from published/20160527 Turn Your Old Laptop into a Chromebook.md
rename to published/201607/20160527 Turn Your Old Laptop into a Chromebook.md
diff --git a/published/20160530 Install LEMP with MariaDB 10, PHP 7 and HTTP 2.0 Support for Nginx on Ubuntu 16.04.md b/published/201607/20160530 Install LEMP with MariaDB 10, PHP 7 and HTTP 2.0 Support for Nginx on Ubuntu 16.04.md
similarity index 100%
rename from published/20160530 Install LEMP with MariaDB 10, PHP 7 and HTTP 2.0 Support for Nginx on Ubuntu 16.04.md
rename to published/201607/20160530 Install LEMP with MariaDB 10, PHP 7 and HTTP 2.0 Support for Nginx on Ubuntu 16.04.md
diff --git a/published/20160531 HOW TO USE WEBP IMAGES IN UBUNTU LINUX.md b/published/201607/20160531 HOW TO USE WEBP IMAGES IN UBUNTU LINUX.md
similarity index 100%
rename from published/20160531 HOW TO USE WEBP IMAGES IN UBUNTU LINUX.md
rename to published/201607/20160531 HOW TO USE WEBP IMAGES IN UBUNTU LINUX.md
diff --git a/published/20160531 Why Ubuntu-based Distros Are Leaders.md b/published/201607/20160531 Why Ubuntu-based Distros Are Leaders.md
similarity index 100%
rename from published/20160531 Why Ubuntu-based Distros Are Leaders.md
rename to published/201607/20160531 Why Ubuntu-based Distros Are Leaders.md
diff --git a/published/201607/20160602 How to mount your Google Drive on Linux with google-drive-ocamlfuse.md b/published/201607/20160602 How to mount your Google Drive on Linux with google-drive-ocamlfuse.md
new file mode 100644
index 0000000000..e993dc5e58
--- /dev/null
+++ b/published/201607/20160602 How to mount your Google Drive on Linux with google-drive-ocamlfuse.md	
@@ -0,0 +1,64 @@
+教你用 google-drive-ocamlfuse 在 Linux 上挂载 Google Drive
+=====================
+
+> 如果你在找一个方便的方式在 Linux 机器上挂载你的 Google Drive 文件夹, Jack Wallen 将教你怎么使用 google-drive-ocamlfuse 来挂载 Google Drive。
+
+![](http://tr4.cbsistatic.com/hub/i/2016/05/18/ee5d7b81-e5be-4b24-843d-d3ca99230a63/651be96ac8714698f8100afa6883e64d/linuxcloudhero.jpg)
+
+*图片来源: Jack Wallen*
+
+Google 还没有发行 Linux 版本的 Google Drive 应用,尽管现在有很多方法从 Linux 中访问你的 Drive 文件。
+
+如果你喜欢界面化的工具,你可以选择 Insync。如果你喜欢用命令行,有很多像 Grive2 这样的工具,和更容易使用的以 Ocaml 语言编写的基于 FUSE 的文件系统。我将会用后面这种方式演示如何在 Linux 桌面上挂载你的 Google Drive。尽管这是通过命令行完成的,但是它的用法会简单到让你吃惊。它太简单了以至于谁都能做到。
+
+这个系统的特点:
+
+- 对普通文件/文件夹有完全的读写权限
+- 对于 Google Docs,sheets,slides 这三个应用只读
+- 能够访问 Drive 回收站(.trash)
+- 处理重复文件功能
+- 支持多个帐号
+
+让我们接下来完成 google-drive-ocamlfuse 在 Ubuntu 16.04 桌面的安装,然后你就能够访问云盘上的文件了。
+
+### 安装
+
+1. 打开终端。
+2. 用 `sudo add-apt-repository ppa:alessandro-strada/ppa` 命令添加必要的 PPA
+3. 出现提示的时候,输入你的 root 密码并按下回车。
+4. 用 `sudo apt-get update` 命令更新应用。
+5. 输入 `sudo apt-get install google-drive-ocamlfuse` 命令安装软件。
+
+### 授权
+
+接下来就是授权 google-drive-ocamlfuse,让它有权限访问你的 Google 账户。先回到终端窗口敲下命令 `google-drive-ocamlfuse`,这个命令将会打开一个浏览器窗口,它会提示你登陆你的 Google 帐号或者如果你已经登陆了 Google 帐号,它会询问是否允许 google-drive-ocamlfuse 访问 Google 账户。如果你还没有登录,先登录然后点击“允许”。接下来的窗口(在 Ubuntu 16.04 桌面上会出现,但不会出现在 Elementary OS Freya 桌面上)将会询问你是否授给 gdfuse 和 OAuth2 Endpoint 访问你的 Google 账户的权限,再次点击“允许”。然后出现的窗口就会告诉你等待授权令牌下载完成,这个时候就能最小化浏览器了。当你的终端提示如下图一样的内容,你就能知道令牌下载完了,并且你已经可以挂载 Google Drive 了。
+
+![](http://tr4.cbsistatic.com/hub/i/r/2016/05/18/a493122b-445f-4aca-8974-5ec41192eede/resize/620x/6ae5907ad2c08dc7620b7afaaa9e389c/googledriveocamlfuse3.png)
+
+*应用已经得到授权,你可以进行后面的工作。*
+
+### 挂载 Google Drive
+
+在挂载 Google Drive 之前,你得先创建一个文件夹,作为挂载点。在终端里,敲下`mkdir ~/google-drive`命令在你的家目录下创建一个新的文件夹。最后敲下命令`google-drive-ocamlfuse ~/google-drive`将你的 Google Drive 挂载到 google-drive 文件夹中。
+
+这时你可以查看本地 google-drive 文件夹中包含的 Google Drive 文件/文件夹。你可以把 Google Drive 当作本地文件系统来进行工作。
+
+当你想卸载 google-drive 文件夹,输入命令 `fusermount -u ~/google-drive`。
+
+### 没有 GUI,但它特别好用
+
+我发现这个特别的系统非常容易使用,在同步 Google Drive 时它出奇的快,并且这可以作为一种本地备份你的 Google Drive 账户的巧妙方式。(LCTT 译注:然而首先你得能使用……)
+
+试试 google-drive-ocamlfuse,看看你能用它做出什么有趣的事。
+
+--------------------------------------------------------------------------------
+
+via: http://www.techrepublic.com/article/how-to-mount-your-google-drive-on-linux-with-google-drive-ocamlfuse/
+
+作者:[Jack Wallen][a]
+译者:[GitFuture](https://github.com/GitFuture)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.techrepublic.com/search/?a=jack+wallen
diff --git a/published/20160603 How To Install And Use VBoxManage On Ubuntu 16.04 And Use Its Command line Options.md b/published/201607/20160603 How To Install And Use VBoxManage On Ubuntu 16.04 And Use Its Command line Options.md
similarity index 100%
rename from published/20160603 How To Install And Use VBoxManage On Ubuntu 16.04 And Use Its Command line Options.md
rename to published/201607/20160603 How To Install And Use VBoxManage On Ubuntu 16.04 And Use Its Command line Options.md
diff --git a/published/20160606 Basic Git Commands You Must Know.md b/published/201607/20160606 Basic Git Commands You Must Know.md
similarity index 100%
rename from published/20160606 Basic Git Commands You Must Know.md
rename to published/201607/20160606 Basic Git Commands You Must Know.md
diff --git a/published/20160609 How to record your terminal session on Linux.md b/published/201607/20160609 How to record your terminal session on Linux.md
similarity index 100%
rename from published/20160609 How to record your terminal session on Linux.md
rename to published/201607/20160609 How to record your terminal session on Linux.md
diff --git a/published/20160609 INSTALL MATE 1.14 IN UBUNTU MATE 16.04  VIA PPA.md b/published/201607/20160609 INSTALL MATE 1.14 IN UBUNTU MATE 16.04  VIA PPA.md
similarity index 100%
rename from published/20160609 INSTALL MATE 1.14 IN UBUNTU MATE 16.04  VIA PPA.md
rename to published/201607/20160609 INSTALL MATE 1.14 IN UBUNTU MATE 16.04  VIA PPA.md
diff --git a/published/20160610 Getting started with ReactOS.md b/published/201607/20160610 Getting started with ReactOS.md
similarity index 100%
rename from published/20160610 Getting started with ReactOS.md
rename to published/201607/20160610 Getting started with ReactOS.md
diff --git a/published/20160616 10 Basic Linux Commands That Every Linux Newbies Should Remember.md b/published/201607/20160616 10 Basic Linux Commands That Every Linux Newbies Should Remember.md
similarity index 100%
rename from published/20160616 10 Basic Linux Commands That Every Linux Newbies Should Remember.md
rename to published/201607/20160616 10 Basic Linux Commands That Every Linux Newbies Should Remember.md
diff --git a/published/20160616 6 Amazing Linux Distributions For Kids.md b/published/201607/20160616 6 Amazing Linux Distributions For Kids.md
similarity index 100%
rename from published/20160616 6 Amazing Linux Distributions For Kids.md
rename to published/201607/20160616 6 Amazing Linux Distributions For Kids.md
diff --git a/published/201607/20160620 Detecting cats in images with OpenCV.md b/published/201607/20160620 Detecting cats in images with OpenCV.md
new file mode 100644
index 0000000000..55b80cc51e
--- /dev/null
+++ b/published/201607/20160620 Detecting cats in images with OpenCV.md	
@@ -0,0 +1,227 @@
+使用 OpenCV 识别图片中的猫咪
+=======================================
+
+![](http://www.pyimagesearch.com/wp-content/uploads/2016/05/cat_face_detector_result_04.jpg)
+
+你知道 OpenCV 可以识别在图片中小猫的脸吗?而且是拿来就能用,不需要其它的库之类的。
+
+之前我也不知道。
+
+但是在 [Kendrick Tan 曝出这个功能][1]后,我需要亲自体验一下……去看看到 OpenCV 是如何在我没有察觉到的情况下,将这一个功能添加进了他的软件库(就像一只悄悄溜进空盒子的猫咪一样,等待别人发觉)。
+
+下面,我将会展示如何使用 OpenCV 的猫咪检测器在图片中识别小猫的脸。同样的,该技术也可以用在视频流中。
+
+### 使用 OpenCV 在图片中检测猫咪
+
+如果你查找过 [OpenCV 的代码仓库][3],尤其是在 [haarcascades 目录][4]里(OpenCV 在这里保存处理它预先训练好的 Haar 分类器,以检测各种物体、身体部位等), 你会看到这两个文件:
+
+- haarcascade_frontalcatface.xml
+- haarcascade\_frontalcatface\_extended.xml
+
+这两个 Haar Cascade 文件都将被用来在图片中检测小猫的脸。实际上,我使用了相同的 cascades 分类器来生成这篇博文顶端的图片。
+
+在做了一些调查工作之后,我发现这些 cascades 分类器是由鼎鼎大名的 [Joseph Howse][5]训练和贡献给 OpenCV 仓库的,他写了很多很棒的教程和书籍,在计算机视觉领域有着很高的声望。
+
+下面,我将会展示给你如何使用 Howse 的 Haar cascades 分类器来检测图片中的小猫。
+
+### 猫咪检测代码
+
+让我们开始使用 OpenCV 来检测图片中的猫咪。新建一个叫 cat_detector.py 的文件,并且输入如下的代码:
+
+```
+# import the necessary packages
+import argparse
+import cv2
+ 
+# construct the argument parse and parse the arguments
+ap = argparse.ArgumentParser()
+ap.add_argument("-i", "--image", required=True,
+	help="path to the input image")
+ap.add_argument("-c", "--cascade",
+	default="haarcascade_frontalcatface.xml",
+	help="path to cat detector haar cascade")
+args = vars(ap.parse_args())
+```
+
+第 2 和第 3 行主要是导入了必要的 python 包。6-12 行用于解析我们的命令行参数。我们仅要求一个必需的参数 `--image` ,它是我们要使用 OpenCV 检测猫咪的图片。
+
+我们也可以(可选的)通过 `--cascade` 参数指定我们的 Haar cascade 分类器的路径。默认使用 `haarcascades_frontalcatface.xml`,假定这个文件和你的 `cat_detector.py` 在同一目录下。
+
+注意:我已经打包了猫咪的检测代码,还有在这个教程里的样本图片。你可以在博文原文的 “下载” 部分下载到。如果你是刚刚接触 Python+OpenCV(或者 Haar cascade),我建议你下载这个 zip 压缩包,这个会方便你跟着教程学习。
+
+接下来,就是检测猫的时刻了:
+
+```
+# load the input image and convert it to grayscale
+image = cv2.imread(args["image"])
+gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
+ 
+# load the cat detector Haar cascade, then detect cat faces
+# in the input image
+detector = cv2.CascadeClassifier(args["cascade"])
+rects = detector.detectMultiScale(gray, scaleFactor=1.3,
+	minNeighbors=10, minSize=(75, 75))
+```
+
+在 15、16 行,我们从硬盘上读取了图片,并且进行灰度化(这是一个在将图片传给 Haar cascade 分类器之前的常用的图片预处理步骤,尽管不是必须的)
+
+20 行,从硬盘加载 Haar casacade 分类器,即猫咪检测器,并且实例化 `cv2.CascadeClassifier` 对象。
+
+在 21、22 行通过调用 `detector`  的 `detectMultiScale` 方法使用 OpenCV 完成猫脸检测。我们给 `detectMultiScale` 方法传递了四个参数。包括:
+
+1. 图片 `gray`,我们要在该图片中检测猫脸。
+2. 检测猫脸时的[图片金字塔][6] 的检测粒度 `scaleFactor` 。更大的粒度将会加快检测的速度,但是会对检测准确性( true-positive)产生影响。相反的,一个更小的粒度将会影响检测的时间,但是会增加准确性( true-positive)。但是,细粒度也会增加误报率(false-positive)。你可以看这篇博文的“ Haar cascades 注意事项”部分来获得更多的信息。
+3. `minNeighbors` 参数控制了检定框的最少数量,即在给定区域内被判断为猫脸的最少数量。这个参数可以很好的排除误报(false-positive)结果。
+4. 最后,`minSize` 参数不言自明。这个值描述每个检定框的最小宽高尺寸(单位是像素),这个例子中就是 75\*75
+
+`detectMultiScale` 函数会返回 `rects`,这是一个 4 元组列表。这些元组包含了每个检测到的猫脸的 (x,y) 坐标值,还有宽度、高度。
+
+最后,让我们在图片上画下这些矩形来标识猫脸:
+
+```
+# loop over the cat faces and draw a rectangle surrounding each
+for (i, (x, y, w, h)) in enumerate(rects):
+	cv2.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)
+	cv2.putText(image, "Cat #{}".format(i + 1), (x, y - 10),
+		cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 0, 255), 2)
+ 
+# show the detected cat faces
+cv2.imshow("Cat Faces", image)
+cv2.waitKey(0)
+```
+
+给我们这些框(比如,rects)的数据,我们在 25 行依次遍历它。
+
+在 26 行,我们在每张猫脸的周围画上一个矩形。27、28 行展示了一个整数,即图片中猫咪的数量。
+
+最后,31,32 行在屏幕上展示了输出的图片。
+
+### 猫咪检测结果
+
+为了测试我们的 OpenCV 猫咪检测器,可以在原文的最后,下载教程的源码。
+
+然后,在你解压缩之后,你将会得到如下的三个文件/目录:
+
+1. cat_detector.py:我们的主程序
+2. haarcascade_frontalcatface.xml: 猫咪检测器 Haar cascade 
+3. images:我们将会使用的检测图片目录。
+
+到这一步,执行以下的命令:
+
+```
+$ python cat_detector.py --image images/cat_01.jpg
+```
+
+![](http://www.pyimagesearch.com/wp-content/uploads/2016/05/cat_face_detector_result_01.jpg)
+
+*图 1. 在图片中检测猫脸,甚至是猫咪部分被遮挡了。*
+
+注意,我们已经可以检测猫脸了,即使它的其余部分是被遮挡的。
+
+试下另外的一张图片:
+
+```
+python cat_detector.py --image images/cat_02.jpg
+```
+
+![](http://www.pyimagesearch.com/wp-content/uploads/2016/05/cat_face_detector_result_02.jpg)
+
+*图 2. 使用 OpenCV 检测猫脸的第二个例子,这次猫脸稍有不同。*
+
+这次的猫脸和第一次的明显不同,因为它正在发出“喵呜”叫声的当中。这种情况下,我们依旧能检测到正确的猫脸。
+
+在下面这张图片的结果也是正确的:
+
+```
+$ python cat_detector.py --image images/cat_03.jpg
+```
+
+![](http://www.pyimagesearch.com/wp-content/uploads/2016/05/cat_face_detector_result_03.jpg)
+
+*图 3. 使用 OpenCV 和 python 检测猫脸*
+
+我们最后的一个样例就是在一张图中检测多张猫脸:
+
+```
+$ python cat_detector.py --image images/cat_04.jpg
+```
+
+![](http://www.pyimagesearch.com/wp-content/uploads/2016/05/cat_face_detector_result_04.jpg)
+
+*图 4. 在同一张图片中使用 OpenCV 检测多只猫*
+
+注意,Haar cascade 返回的检定框不一定是以你预期的顺序。这种情况下,中间的那只猫会被标记成第三只。你可以通过判断他们的 (x, y) 坐标来自己排序这些检定框。
+
+#### 关于精度的说明
+
+在这个 xml 文件中的注释非常重要,Joseph Hower 提到了这个猫脸检测器有可能会将人脸识别成猫脸。
+
+这种情况下,他推荐使用两种检测器(人脸 & 猫脸),然后将出现在人脸识别结果中的结果剔除掉。
+
+#### Haar cascades 注意事项
+
+这个方法首先出现在 Paul Viola 和 Michael Jones 2001 年出版的 [Rapid Object Detection using a Boosted Cascade of Simple Features][7] 论文中。现在它已经成为了计算机识别领域引用最多的论文之一。
+
+这个算法能够识别图片中的对象,无论它们的位置和比例。而且最令人感兴趣的或许是它能在现有的硬件条件下实现实时检测。
+
+在他们的论文中,Viola 和 Jones 关注在训练人脸检测器;但是,这个框架也能用来检测各类事物,如汽车、香蕉、路标等等。
+
+#### 问题是?
+
+Haar cascades 最大的问题就是如何确定 `detectMultiScale` 方法的参数正确。特别是 `scaleFactor` 和 `minNeighbors` 参数。你很容易陷入一张一张图片调参数的坑,这个就是该对象检测器很难被实用化的原因。
+
+这个 `scaleFactor` 变量控制了用来检测对象的图片的各种比例的[图像金字塔][8]。如果 `scaleFactor` 参数过大,你就只需要检测图像金字塔中较少的层,这可能会导致你丢失一些在图像金字塔层之间缩放时少了的对象。
+
+换句话说,如果 `scaleFactor` 参数过低,你会检测过多的金字塔图层。这虽然可以能帮助你检测到更多的对象。但是他会造成计算速度的降低,还会**明显**提高误报率。Haar cascades 分类器就是这样。
+
+为了避免这个,我们通常使用 [Histogram of Oriented Gradients + 线性 SVM 检测][9] 替代。
+
+上述的 HOG + 线性 SVM 框架的参数更容易调优。而且更好的误报率也更低,但是唯一不好的地方是无法实时运算。
+
+### 对对象识别感兴趣?并且希望了解更多?
+
+![](http://www.pyimagesearch.com/wp-content/uploads/2016/05/custom_object_detector_example.jpg)
+
+*图 5. 在 PyImageSearch Gurus 课程中学习如何构建自定义的对象识别器。*
+
+如果你对学习如何训练自己的自定义对象识别器感兴趣,请务必要去了解下 PyImageSearch Gurus 课程。
+
+在这个课程中,我提供了 15 节课,覆盖了超过 168 页的教程,来教你如何从 0 开始构建自定义的对象识别器。你会掌握如何应用 HOG + 线性 SVM 框架来构建自己的对象识别器来识别路标、面孔、汽车(以及附近的其它东西)。
+
+要学习 PyImageSearch Gurus 课程(有 10 节示例免费课程),点此:https://www.pyimagesearch.com/pyimagesearch-gurus/?src=post-cat-detection 
+
+### 总结
+
+在这篇博文里,我们学习了如何使用 OpenCV 默认就有的 Haar cascades 分类器来识别图片中的猫脸。这些 Haar casacades 是由  [Joseph Howse][9] 训练兵贡献给 OpenCV 项目的。我是在 Kendrick Tan 的[这篇文章][10]中开始注意到这个。
+
+尽管 Haar cascades 相当有用,但是我们也经常用 HOG + 线性 SVM 替代。因为后者相对而言更容易使用,并且可以有效地降低误报率。
+
+我也会[在 PyImageSearch Gurus 课程中][11]详细的讲述如何构建定制的 HOG + 线性 SVM 对象识别器,来识别包括汽车、路标在内的各种事物。
+
+不管怎样,我希望你喜欢这篇博文。
+
+--------------------------------------------------------------------------------
+
+via: http://www.pyimagesearch.com/2016/06/20/detecting-cats-in-images-with-opencv/
+
+作者:[Adrian Rosebrock][a]
+译者:[MikeCoder](https://github.com/MikeCoder)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.pyimagesearch.com/author/adrian/
+[1]: http://kendricktan.github.io/find-cats-in-photos-using-computer-vision.html
+[2]: http://www.pyimagesearch.com/2016/06/20/detecting-cats-in-images-with-opencv/#
+[3]: https://github.com/Itseez/opencv
+[4]: https://github.com/Itseez/opencv/tree/master/data/haarcascades
+[5]: http://nummist.com/
+[6]: http://www.pyimagesearch.com/2015/03/16/image-pyramids-with-python-and-opencv/
+[7]: https://www.cs.cmu.edu/~efros/courses/LBMV07/Papers/viola-cvpr-01.pdf
+[8]: http://www.pyimagesearch.com/2015/03/16/image-pyramids-with-python-and-opencv/
+[9]: http://www.pyimagesearch.com/2014/11/10/histogram-oriented-gradients-object-detection/
+[10]: http://kendricktan.github.io/find-cats-in-photos-using-computer-vision.html
+[11]: https://www.pyimagesearch.com/pyimagesearch-gurus/
+
+
+
diff --git a/published/20160620 Monitor Linux With Netdata.md b/published/201607/20160620 Monitor Linux With Netdata.md
similarity index 100%
rename from published/20160620 Monitor Linux With Netdata.md
rename to published/201607/20160620 Monitor Linux With Netdata.md
diff --git a/published/20160620 PowerPC gains an Android 4.4 port with Big Endian support.md b/published/201607/20160620 PowerPC gains an Android 4.4 port with Big Endian support.md
similarity index 100%
rename from published/20160620 PowerPC gains an Android 4.4 port with Big Endian support.md
rename to published/201607/20160620 PowerPC gains an Android 4.4 port with Big Endian support.md
diff --git a/published/201607/20160621 Container technologies in Fedora - systemd-nspawn.md b/published/201607/20160621 Container technologies in Fedora - systemd-nspawn.md
new file mode 100644
index 0000000000..69cbf688d8
--- /dev/null
+++ b/published/201607/20160621 Container technologies in Fedora - systemd-nspawn.md	
@@ -0,0 +1,105 @@
+Fedora 中的容器技术:systemd-nspawn
+===
+
+欢迎来到“Fedora 中的容器技术”系列!本文是该系列文章中的第一篇,它将说明你可以怎样使用 Fedora 中各种可用的容器技术。本文将学习 `systemd-nspawn` 的相关知识。
+
+### 容器是什么?
+
+一个容器就是一个用户空间实例,它能够在与托管容器的系统(叫做宿主系统)相隔离的环境中运行一个程序或者一个操作系统。这和 `chroot` 或 [虚拟机][1] 的思想非常类似。运行在容器中的进程是由与宿主操作系统相同的内核来管理的,但它们是与宿主文件系统以及其它进程隔离开的。
+
+### 什么是 systemd-nspawn?
+
+systemd 项目认为应当将容器技术变成桌面的基础部分,并且应当和用户的其余系统集成在一起。为此,systemd 提供了 `systemd-nspawn`,这款工具能够使用多种 Linux 技术创建容器。它也提供了一些容器管理工具。
+
+`systemd-nspawn` 和 `chroot` 在许多方面都是类似的,但是前者更加强大。它虚拟化了文件系统、进程树以及客户系统中的进程间通信。它的吸引力在于它提供了很多用于管理容器的工具,例如用来管理容器的 `machinectl`。由 `systemd-nspawn` 运行的容器将会与 systemd 组件一同运行在宿主系统上。举例来说,一个容器的日志可以输出到宿主系统的日志中。
+
+在 Fedora 24 上,`systemd-nspawn` 已经从 systemd 软件包分离出来了,所以你需要安装 `systemd-container` 软件包。一如往常,你可以使用 `dnf install systemd-container` 进行安装。
+
+### 创建容器
+
+使用 `systemd-nspawn` 创建一个容器是很容易的。假设你有一个专门为 Debian 创造的应用,并且无法在其它发行版中正常运行。那并不是一个问题,我们可以创造一个容器!为了设置容器使用最新版本的 Debian(现在是 Jessie),你需要挑选一个目录来放置你的系统。我暂时将使用目录 `~/DebianJessie`。
+
+一旦你创建完目录,你需要运行 `debootstrap`,你可以从 Fedora 仓库中安装它。对于 Debian Jessie,你运行下面的命令来初始化一个 Debian 文件系统。
+
+```
+$ debootstrap --arch=amd64 stable ~/DebianJessie
+```
+
+以上默认你的架构是 x86_64。如果不是的话,你必须将架构的名称改为 `amd64`。你可以使用 `uname -m` 得知你的机器架构。
+
+一旦设置好你的根目录,你就可以使用下面的命令来启动你的容器。
+
+```
+$ systemd-nspawn -bD ~/DebianJessie
+```
+
+容器将会在数秒后准备好并运行,当你试图登录时就会注意到:你无法使用你的系统上任何账户。这是因为 `systemd-nspawn` 虚拟化了用户。修复的方法很简单:将之前的命令中的 `-b` 移除即可。你将直接进入容器的 root 用户的 shell。此时,你只能使用 `passwd` 命令为 root 设置密码,或者使用 `adduser` 命令添加一个新用户。一旦设置好密码或添加好用户,你就可以把 `-b` 标志添加回去然后继续了。你会进入到熟悉的登录控制台,然后你使用设置好的认证信息登录进去。
+
+以上对于任意你想在容器中运行的发行版都适用,但前提是你需要使用正确的包管理器创建系统。对于 Fedora,你应使用 DNF 而非 `debootstrap`。想要设置一个最小化的 Fedora 系统,你可以运行下面的命令,要将“/absolute/path/”替换成任何你希望容器存放的位置。
+
+```
+$ sudo dnf --releasever=24 --installroot=/absolute/path/ install systemd passwd dnf fedora-release
+```
+
+![](https://cdn.fedoramagazine.org/wp-content/uploads/2016/06/Screenshot-from-2016-06-17-15-04-14.png)
+
+### 设置网络
+
+如果你尝试启动一个服务,但它绑定了你宿主机正在使用的端口,你将会注意到这个问题:你的容器正在使用和宿主机相同的网络接口。幸运的是,`systemd-nspawn` 提供了几种可以将网络从宿主机分开的方法。
+
+#### 本地网络
+
+第一种方法是使用 `--private-network` 标志,它默认仅创建一个回环设备。这对于你不需要使用网络的环境是非常理想的,例如构建系统和其它持续集成系统。
+
+#### 多个网络接口
+
+如果你有多个网络接口设备,你可以使用 `--network-interface` 标志给容器分配一个接口。想要给我的容器分配 `eno1`,我会添加选项 `--network-interface=eno1`。当某个接口分配给一个容器后,宿主机就不能同时使用那个接口了。只有当容器彻底关闭后,宿主机才可以使用那个接口。
+
+#### 共享网络接口
+
+对于我们中那些并没有额外的网络设备的人来说,还有其它方法可以访问容器。一种就是使用 `--port` 选项。这会将容器中的一个端口定向到宿主机。使用格式是 `协议:宿主机端口:容器端口`,这里的协议可以是 `tcp` 或者 `udp`,`宿主机端口` 是宿主机的一个合法端口,`容器端口` 则是容器中的一个合法端口。你可以省略协议,只指定 `宿主机端口:容器端口`。我通常的用法类似 `--port=2222:22`。
+
+你可以使用 `--network-veth` 启用完全的、仅宿主机模式的网络,这会在宿主机和容器之间创建一个虚拟的网络接口。你也可以使用 `--network-bridge` 桥接二者的连接。
+
+### 使用 systemd 组件
+
+如果你容器中的系统含有 D-Bus,你可以使用 systemd 提供的实用工具来控制并监视你的容器。基础安装的 Debian 并不包含 `dbus`。如果你想在 Debian Jessie 中使用 `dbus`,你需要运行命令 `apt install dbus`。
+
+#### machinectl
+
+为了能够轻松地管理容器,systemd 提供了 `machinectl` 实用工具。使用 `machinectl`,你可以使用 `machinectl login name` 登录到一个容器中、使用 `machinectl status name`检查状态、使用 `machinectl reboot name` 启动容器或者使用 `machinectl poweroff name`  关闭容器。
+
+### 其它 systemd 命令
+
+多数 systemd 命令,例如 `journalctl`, `systemd-analyze` 和 `systemctl`,都支持使用 `--machine` 选项来指定容器。例如,如果你想查看一个名为 “foobar” 的容器的日志,你可以使用 `journalctl --machine=foobar`。你也可以使用 `systemctl --machine=foobar status service` 来查看运行在这个容器中的服务状态。
+
+![](https://cdn.fedoramagazine.org/wp-content/uploads/2016/06/Screenshot-from-2016-06-17-15-09-25.png)
+
+### 和 SELinux 一起工作
+
+如果你要使用 SELinux 强制模式(Fedora 默认模式),你需要为你的容器设置 SELinux 环境。想要那样的话,你需要在宿主系统上运行下面两行命令。
+
+```
+$ semanage fcontext -a -t svirt_sandbox_file_t "/path/to/container(/.*)?"
+$ restorecon -R /path/to/container/
+```
+
+确保使用你的容器路径替换 “/path/to/container”。对于我的容器 "DebianJessie",我会运行下面的命令:
+
+```
+$ semanage fcontext -a -t svirt_sandbox_file_t "/home/johnmh/DebianJessie(/.*)?"
+$ restorecon -R /home/johnmh/DebianJessie/
+```
+
+--------------------------------------------------------------------------------
+
+via: https://fedoramagazine.org/container-technologies-fedora-systemd-nspawn/
+
+作者:[John M. Harris, Jr.][a]
+译者:[ChrisLeeGit](https://github.com/chrisleegit)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://fedoramagazine.org/container-technologies-fedora-systemd-nspawn/
+[1]: https://en.wikipedia.org/wiki/Virtual_machine
diff --git a/published/20160624 How to permanently mount a Windows share on Linux.md b/published/201607/20160624 How to permanently mount a Windows share on Linux.md
similarity index 100%
rename from published/20160624 How to permanently mount a Windows share on Linux.md
rename to published/201607/20160624 How to permanently mount a Windows share on Linux.md
diff --git a/published/20160624 IT runs on the cloud and the cloud runs on Linux. Any questions.md b/published/201607/20160624 IT runs on the cloud and the cloud runs on Linux. Any questions.md
similarity index 100%
rename from published/20160624 IT runs on the cloud and the cloud runs on Linux. Any questions.md
rename to published/201607/20160624 IT runs on the cloud and the cloud runs on Linux. Any questions.md
diff --git a/published/20160624 Industrial SBC builds on Raspberry Pi Compute Module.md b/published/201607/20160624 Industrial SBC builds on Raspberry Pi Compute Module.md
similarity index 100%
rename from published/20160624 Industrial SBC builds on Raspberry Pi Compute Module.md
rename to published/201607/20160624 Industrial SBC builds on Raspberry Pi Compute Module.md
diff --git a/published/20160625 How to Hide Linux Command Line History by Going Incognito.md b/published/201607/20160625 How to Hide Linux Command Line History by Going Incognito.md
similarity index 100%
rename from published/20160625 How to Hide Linux Command Line History by Going Incognito.md
rename to published/201607/20160625 How to Hide Linux Command Line History by Going Incognito.md
diff --git a/published/201607/20160628 How To Setup Open Source Discussion Platform Discourse On Ubuntu Linux 16.04.md b/published/201607/20160628 How To Setup Open Source Discussion Platform Discourse On Ubuntu Linux 16.04.md
new file mode 100644
index 0000000000..45c5d634dc
--- /dev/null
+++ b/published/201607/20160628 How To Setup Open Source Discussion Platform Discourse On Ubuntu Linux 16.04.md	
@@ -0,0 +1,101 @@
+如何在 Ubuntu Linux 16.04上安装开源的 Discourse 论坛
+===============================================================================
+
+Discourse 是一个开源的论坛,它可以以邮件列表、聊天室或者论坛等多种形式工作。它是一个广受欢迎的现代的论坛工具。在服务端,它使用 Ruby on Rails 和  Postgres 搭建,  并且使用 Redis 缓存来减少读取时间 , 在客户端,它使用支持 Java Script 的浏览器。它非常容易定制,结构良好,并且它提供了转换插件,可以对你现存的论坛、公告板进行转换,例如:  vBulletin、phpBB、Drupal、SMF 等等。在这篇文章中,我们将学习在 Ubuntu 操作系统下安装 Discourse。
+
+它以安全作为设计思想,所以发垃圾信息的人和黑客们不能轻易的实现其企图。它能很好的支持各种现代设备,并可以相应的调整以手机和平板的显示。
+
+### 在 Ubuntu 16.04 上安装 Discourse
+
+让我们开始吧 ! 最少需要 1G 的内存,并且官方支持的安装过程需要已经安装了  docker。 说到 docker,它还需要安装Git。要满足以上的两点要求我们只需要运行下面的命令:
+
+```
+wget -qO- https://get.docker.com/ | sh
+```
+
+![](http://linuxpitstop.com/wp-content/uploads/2016/06/124.png)
+
+用不了多久就安装好了 docker 和 Git,安装结束以后,在你的系统上的 /var 分区创建一个 Discourse 文件夹(当然你也可以选择其他的分区)。
+
+```
+mkdir /var/discourse 
+```
+
+现在我们来克隆 Discourse 的 Github  仓库到这个新建的文件夹。
+
+```
+git clone https://github.com/discourse/discourse_docker.git /var/discourse
+```
+
+进入这个克隆的文件夹。
+
+```
+cd /var/discourse
+```
+
+![](http://linuxpitstop.com/wp-content/uploads/2016/06/314.png)
+
+你将看到“discourse-setup” 脚本文件,运行这个脚本文件进行 Discourse 的初始化。
+
+```
+./discourse-setup
+```
+
+**备注: 在安装 discourse 之前请确保你已经安装好了邮件服务器。**
+
+安装向导将会问你以下六个问题:
+
+```
+Hostname for your Discourse?
+Email address for admin account? 
+SMTP server address? 
+SMTP user name? 
+SMTP port [587]:
+SMTP password? []:
+```
+
+![](http://linuxpitstop.com/wp-content/uploads/2016/06/411.png)
+
+当你提交了以上信息以后, 它会让你提交确认, 如果一切都很正常,点击回车以后安装开始。
+
+![](http://linuxpitstop.com/wp-content/uploads/2016/06/511.png)
+
+现在“坐等放宽”,需要花费一些时间来完成安装,倒杯咖啡,看看有什么错误信息没有。
+
+![](http://linuxpitstop.com/wp-content/uploads/2016/06/610.png)
+
+安装成功以后看起来应该像这样。
+
+![](http://linuxpitstop.com/wp-content/uploads/2016/06/710.png)
+
+现在打开浏览器,如果已经做了域名解析,你可以使用你的域名来连接 Discourse 页面 ,否则你只能使用IP地址了。你将看到如下信息:
+
+![](http://linuxpitstop.com/wp-content/uploads/2016/06/85.png)
+
+就是这个,点击 “Sign Up” 选项创建一个新的账户,然后进行你的 Discourse 设置。
+
+![](http://linuxpitstop.com/wp-content/uploads/2016/06/106.png)
+
+### 结论
+
+它安装简便,运行完美。 它拥有现代论坛所有必备功能。它以 GPL 发布,是完全开源的产品。简单、易用、以及特性丰富是它的最大特点。希望你喜欢这篇文章,如果有问题,你可以给我们留言。
+
+--------------------------------------------------------------------------------
+
+via: http://linuxpitstop.com/install-discourse-on-ubuntu-linux-16-04/
+
+作者:[Aun][a]
+译者:[kokialoves](https://github.com/kokialoves)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://linuxpitstop.com/author/aun/
+
+
+
+
+
+
+
+
diff --git a/published/20160629 USE TASK MANAGER EQUIVALENT IN LINUX.md b/published/201607/20160629 USE TASK MANAGER EQUIVALENT IN LINUX.md
similarity index 100%
rename from published/20160629 USE TASK MANAGER EQUIVALENT IN LINUX.md
rename to published/201607/20160629 USE TASK MANAGER EQUIVALENT IN LINUX.md
diff --git a/published/201607/20160630 What makes up the Fedora kernel.md b/published/201607/20160630 What makes up the Fedora kernel.md
new file mode 100644
index 0000000000..67c26ee93c
--- /dev/null
+++ b/published/201607/20160630 What makes up the Fedora kernel.md	
@@ -0,0 +1,31 @@
+Fedora 内核是由什么构成的?
+====================================
+
+![](https://cdn.fedoramagazine.org/wp-content/uploads/2016/06/kernel-945x400.png)
+
+每个 Fedora 系统都运行着一个内核。许多代码片段组合在一起使之成为现实。
+
+每个 Fedora 内核都起始于一个来自于[上游社区][1]的基线版本——通常称之为 vanilla 内核。上游内核就是标准。(Fedora 的)目标是包含尽可能多的上游代码,这样使得 bug 修复和 API 更新更加容易,同时也会有更多的人审查代码。理想情况下,Fedora 能够直接获取 kernel.org 的内核,然后发送给所有用户。
+
+现实情况是,使用 vanilla 内核并不能完全满足 Fedora。Vanilla 内核可能并不支持一些 Fedora 用户希望拥有的功能。用户接收的 [Fedora 内核] 是在 vanilla 内核之上打了很多补丁的内核。这些补丁被认为“不在树上(out of tree)”。许多这些位于补丁树之外的补丁都不会存在太久。如果某补丁能够修复一个问题,那么该补丁可能会被合并到 Fedora 树,以便用户能够更快地收到修复。当内核变基到一个新版本时,在新版本中的补丁都将被清除。
+
+一些补丁会在 Fedora 内核树上存在很长时间。一个很好的例子是,安全启动补丁就是这类补丁。这些补丁提供了 Fedora 希望支持的功能,即使上游社区还没有接受它们。保持这些补丁更新是需要付出很多努力的,所以 Fedora 尝试减少不被上游内核维护者接受的补丁数量。
+
+通常来说,想要在 Fedora 内核中获得一个补丁的最佳方法是先给 [Linux 内核邮件列表(LKML)][3] 发送补丁,然后请求将该补丁包含到 Fedora 中。如果某个维护者接受了补丁,就意味着 Fedora 内核树中将来很有可能会包含该补丁。一些来自于 GitHub 等地方的还没有提交给 LKML 的补丁是不可能进入内核树的。首先向 LKML 发送补丁是非常重要的,它能确保 Fedora 内核树中携带的补丁是功能正常的。如果没有社区审查,Fedora 最终携带的补丁将会充满 bug 并会导致问题。
+
+Fedora 内核中包含的代码来自许多地方。一切都需要提供最佳的体验。
+
+--------------------------------------------------------------------------------
+
+via: https://fedoramagazine.org/makes-fedora-kernel/
+
+作者:[Laura Abbott][a]
+译者:[ChrisLeeGit](https://github.com/chrisleegit)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://fedoramagazine.org/makes-fedora-kernel/
+[1]: http://www.kernel.org/
+[2]: http://pkgs.fedoraproject.org/cgit/rpms/kernel.git/
+[3]: http://www.labbott.name/blog/2015/10/02/the-art-of-communicating-with-lkml/
diff --git a/translated/tech/20160701 How To Setup Bridge (br0) Network on Ubuntu Linux 14.04 and 16.04 LTS.md b/published/201607/20160701 How To Setup Bridge (br0) Network on Ubuntu Linux 14.04 and 16.04 LTS.md
similarity index 67%
rename from translated/tech/20160701 How To Setup Bridge (br0) Network on Ubuntu Linux 14.04 and 16.04 LTS.md
rename to published/201607/20160701 How To Setup Bridge (br0) Network on Ubuntu Linux 14.04 and 16.04 LTS.md
index ed2f37235a..61fba7be98 100644
--- a/translated/tech/20160701 How To Setup Bridge (br0) Network on Ubuntu Linux 14.04 and 16.04 LTS.md	
+++ b/published/201607/20160701 How To Setup Bridge (br0) Network on Ubuntu Linux 14.04 and 16.04 LTS.md	
@@ -1,23 +1,21 @@
-如何在 Ubuntu 14.04 和 16.04 上建立网桥(br0)
+如何在 Ubuntu 上建立网桥
 =======================================================================
 
 > 作为一个 Ubuntu 16.04 LTS 的初学者。如何在 Ubuntu 14.04 和 16.04 的主机上建立网桥呢?
 
-![](http://s0.cyberciti.org/images/category/old/ubuntu-logo.jpg)
+顾名思义,网桥的作用是通过物理接口连接内部和外部网络。对于虚拟端口或者 LXC/KVM/Xen/容器来说,这非常有用。网桥虚拟端口看起来是网络上的一个常规设备。在这个教程中,我将会介绍如何在 Ubuntu 服务器上通过 bridge-utils (brctl) 命令行来配置 Linux 网桥。
 
-顾名思义,网桥的作用是通过物理接口连接内部和外部网络。对于虚拟端口或者 LXC/KVM/Xen/容器来说,这非常有用。通过网桥虚拟端口看起来是网络上的一个常规设备。在这个教程中,我将会介绍如何在 Ubuntu 服务器上通过 bridge-utils(brctl) 命令行来配置 Linux 网桥。
-
-### 网桥网络示例
+### 网桥化的网络示例
 
 ![](http://s0.cyberciti.org/uploads/faq/2016/07/my-br0-br1-setup.jpg)
->Fig.01: Kvm/Xen/LXC 容器网桥实例 (br0)
 
-In this example eth0 and eth1 is the physical network interface. eth0 connected to the LAN and eth1 is attached to the upstream ISP router/Internet.
-在这个例子中,eth0 和 eth1 是物理网络接口。eth0 连接着局域网,eth1 连接着上游路由器/网络。
+*图 01: Kvm/Xen/LXC 容器网桥示例 (br0)*
+
+在这个例子中,eth0 和 eth1 是物理网络接口。eth0 连接着局域网,eth1 连接着上游路由器和互联网。
 
 ### 安装 bridge-utils
 
-使用[apt-get 命令][1] 安装 bridge-utils:
+使用 [apt-get 命令][1] 安装 bridge-utils:
 
 ```
 $ sudo apt-get install bridge-utils
@@ -32,7 +30,8 @@ $ sudo apt install bridge-utils
 样例输出:
 
 ![](http://s0.cyberciti.org/uploads/faq/2016/07/ubuntu-install-bridge-utils.jpg)
->Fig.02: Ubuntu 安装 bridge-utils 包
+
+*图 02: Ubuntu 安装 bridge-utils 包*
 
 ### 在 Ubuntu 服务器上创建网桥
 
@@ -43,10 +42,10 @@ $ sudo cp /etc/network/interfaces /etc/network/interfaces.bakup-1-july-2016
 $ sudo vi /etc/network/interfaces
 ```
 
-接下来设置 eth1 并且将他绑定到 br1 ,输入(删除或者注释所有 eth1 相关配置):
+接下来设置 eth1 并且将它映射到 br1 ,输入如下(删除或者注释所有 eth1 相关配置):
 
 ```
-# br1 setup with static wan IPv4 with ISP router as gateway
+# br1 使用静态公网 IP 地址,并以 ISP 的路由器作为网关
 auto br1
 iface br1 inet static
         address 208.43.222.51
@@ -60,7 +59,7 @@ iface br1 inet static
         bridge_maxwait 0
 ```
 
-接下来设置 eth0 并将它绑定到 br0,输入(删除或者注释所有 eth1 相关配置):
+接下来设置 eth0 并将它映射到 br0,输入如下(删除或者注释所有 eth0 相关配置):
 
 ```
 auto br0
@@ -70,8 +69,8 @@ iface br0 inet static
         broadcast 10.18.44.63
         dns-nameservers 10.0.80.11 10.0.80.12
         # set static route for LAN
-	post-up route add -net 10.0.0.0 netmask 255.0.0.0 gw 10.18.44.1
-	post-up route add -net 161.26.0.0 netmask 255.255.0.0 gw 10.18.44.1
+	    post-up route add -net 10.0.0.0 netmask 255.0.0.0 gw 10.18.44.1
+	    post-up route add -net 161.26.0.0 netmask 255.255.0.0 gw 10.18.44.1
         bridge_ports eth0
         bridge_stp off
         bridge_fd 0
@@ -80,7 +79,7 @@ iface br0 inet static
 
 ### 关于 br0 和 DHCP 的一点说明
 
-DHCP 的配置选项:
+如果使用 DHCP ,配置选项是这样的:
 
 ```
 auto br0
@@ -95,7 +94,7 @@ iface br0 inet dhcp
 
 ### 重启服务器或者网络服务
 
-你需要重启服务器或者输入下列命令来重启网络服务(在 SSH 登陆的会话中这可能不管用):
+你需要重启服务器或者输入下列命令来重启网络服务(在 SSH 登录的会话中这可能不管用):
 
 ```
 $ sudo systemctl restart networking
@@ -111,20 +110,21 @@ $ sudo /etc/init.d/restart networking
 
 使用 ping/ip 命令来验证 LAN 和 WAN 网络接口运行正常:
 ```
-# See br0 and br1
+# 查看 br0 和 br1
 ip a show
-# See routing info
+# 查看路由信息
 ip r
-# ping public site
+# ping 外部站点
 ping -c 2 cyberciti.biz
-# ping lan server
+# ping 局域网服务器
 ping -c 2 10.0.80.12
 ```
 
 样例输出:
 
 ![](http://s0.cyberciti.org/uploads/faq/2016/07/br0-br1-eth0-eth1-configured-on-ubuntu.jpg)
->Fig.03: 验证网桥的以太网连接
+
+*图 03: 验证网桥的以太网连接*
 
 现在,你就可以配置 br0 和 br1 来让 XEN/KVM/LXC 容器访问因特网或者私有局域网了。再也没有必要去设置特定路由或者 iptables 的 SNAT 规则了。
 
@@ -134,8 +134,8 @@ ping -c 2 10.0.80.12
 via: http://www.cyberciti.biz/faq/how-to-create-bridge-interface-ubuntu-linux/
 
 作者:[VIVEK GITE][a]
-译者:[译者ID](https://github.com/MikeCoder)
-校对:[校对者ID](https://github.com/校对者ID)
+译者:[MikeCoder](https://github.com/MikeCoder)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
 
diff --git a/translated/tech/20160705 Create Your Own Shell in Python - Part I.md b/published/201607/20160705 Create Your Own Shell in Python - Part I.md
similarity index 67%
rename from translated/tech/20160705 Create Your Own Shell in Python - Part I.md
rename to published/201607/20160705 Create Your Own Shell in Python - Part I.md
index b54d0bff29..3aee650db0 100644
--- a/translated/tech/20160705 Create Your Own Shell in Python - Part I.md	
+++ b/published/201607/20160705 Create Your Own Shell in Python - Part I.md	
@@ -1,7 +1,7 @@
-使用 Python 创建你自己的 Shell:Part I
+使用 Python 创建你自己的 Shell (一)
 ==========================================
 
-我很想知道一个 shell (像 bash,csh 等)内部是如何工作的。为了满足自己的好奇心,我使用 Python 实现了一个名为 **yosh** (Your Own Shell)的 Shell。本文章所介绍的概念也可以应用于其他编程语言。
+我很想知道一个 shell (像 bash,csh 等)内部是如何工作的。于是为了满足自己的好奇心,我使用 Python 实现了一个名为 **yosh** (Your Own Shell)的 Shell。本文章所介绍的概念也可以应用于其他编程语言。
 
 (提示:你可以在[这里](https://github.com/supasate/yosh)查找本博文使用的源代码,代码以 MIT 许可证发布。在 Mac OS X 10.11.5 上,我使用 Python 2.7.10 和 3.4.3 进行了测试。它应该可以运行在其他类 Unix 环境,比如 Linux 和 Windows 上的 Cygwin。)
 
@@ -20,15 +20,15 @@ yosh_project
 
 `yosh_project` 为项目根目录(你也可以把它简单命名为 `yosh`)。
 
-`yosh` 为包目录,且 `__init__.py` 可以使它成为与包目录名字相同的包(如果你不写 Python,可以忽略它。)
+`yosh` 为包目录,且 `__init__.py` 可以使它成为与包的目录名字相同的包(如果你不用 Python 编写的话,可以忽略它。)
 
 `shell.py` 是我们主要的脚本文件。
 
 ### 步骤 1:Shell 循环
 
-当启动一个 shell,它会显示一个命令提示符并等待你的命令输入。在接收了输入的命令并执行它之后(稍后文章会进行详细解释),你的 shell 会重新回到循环,等待下一条指令。
+当启动一个 shell,它会显示一个命令提示符并等待你的命令输入。在接收了输入的命令并执行它之后(稍后文章会进行详细解释),你的 shell 会重新回到这里,并循环等待下一条指令。
 
-在 `shell.py`,我们会以一个简单的 mian 函数开始,该函数调用了 shell_loop() 函数,如下:
+在 `shell.py` 中,我们会以一个简单的 main 函数开始,该函数调用了 shell_loop() 函数,如下:
 
 ```
 def shell_loop():
@@ -43,7 +43,7 @@ if __name__ == "__main__":
     main()
 ```    
 
-接着,在 `shell_loop()`,为了指示循环是否继续或停止,我们使用了一个状态标志。在循环的开始,我们的 shell 将显示一个命令提示符,并等待读取命令输入。
+接着,在 `shell_loop()` 中,为了指示循环是否继续或停止,我们使用了一个状态标志。在循环的开始,我们的 shell 将显示一个命令提示符,并等待读取命令输入。
 
 ```
 import sys
@@ -56,15 +56,15 @@ def shell_loop():
     status = SHELL_STATUS_RUN
 
     while status == SHELL_STATUS_RUN:
-        # Display a command prompt
+        ### 显示命令提示符
         sys.stdout.write('> ')
         sys.stdout.flush()
 
-        # Read command input
+        ### 读取命令输入
         cmd = sys.stdin.readline()
 ```
 
-之后,我们切分命令输入并进行执行(我们即将实现`命令切分`和`执行`函数)。
+之后,我们切分命令(tokenize)输入并进行执行(execute)(我们即将实现 `tokenize` 和 `execute` 函数)。
 
 因此,我们的 shell_loop() 会是如下这样:
 
@@ -79,33 +79,33 @@ def shell_loop():
     status = SHELL_STATUS_RUN
 
     while status == SHELL_STATUS_RUN:
-        # Display a command prompt
+        ### 显示命令提示符
         sys.stdout.write('> ')
         sys.stdout.flush()
 
-        # Read command input
+        ### 读取命令输入
         cmd = sys.stdin.readline()
 
-        # Tokenize the command input
+        ### 切分命令输入
         cmd_tokens = tokenize(cmd)
 
-        # Execute the command and retrieve new status
+        ### 执行该命令并获取新的状态
         status = execute(cmd_tokens)
 ```
 
-这就是我们整个 shell 循环。如果我们使用 `python shell.py` 启动我们的 shell,它会显示命令提示符。然而如果我们输入命令并按回车,它会抛出错误,因为我们还没定义`命令切分`函数。
+这就是我们整个 shell 循环。如果我们使用 `python shell.py` 启动我们的 shell,它会显示命令提示符。然而如果我们输入命令并按回车,它会抛出错误,因为我们还没定义 `tokenize` 函数。
 
 为了退出 shell,可以尝试输入 ctrl-c。稍后我将解释如何以优雅的形式退出 shell。
 
-### 步骤 2:命令切分
+### 步骤 2:命令切分(tokenize)
 
-当用户在我们的 shell 中输入命令并按下回车键,该命令将会是一个包含命令名称及其参数的很长的字符串。因此,我们必须切分该字符串(分割一个字符串为多个标记)。
+当用户在我们的 shell 中输入命令并按下回车键,该命令将会是一个包含命令名称及其参数的长字符串。因此,我们必须切分该字符串(分割一个字符串为多个元组)。
 
 咋一看似乎很简单。我们或许可以使用 `cmd.split()`,以空格分割输入。它对类似 `ls -a my_folder` 的命令起作用,因为它能够将命令分割为一个列表 `['ls', '-a', 'my_folder']`,这样我们便能轻易处理它们了。
 
 然而,也有一些类似 `echo "Hello World"` 或 `echo 'Hello World'` 以单引号或双引号引用参数的情况。如果我们使用 cmd.spilt,我们将会得到一个存有 3 个标记的列表 `['echo', '"Hello', 'World"']` 而不是 2 个标记的列表 `['echo', 'Hello World']`。
 
-幸运的是,Python 提供了一个名为 `shlex` 的库,它能够帮助我们效验如神地分割命令。(提示:我们也可以使用正则表达式,但它不是本文的重点。)
+幸运的是,Python 提供了一个名为 `shlex` 的库,它能够帮助我们如魔法般地分割命令。(提示:我们也可以使用正则表达式,但它不是本文的重点。)
 
 
 ```
@@ -120,23 +120,23 @@ def tokenize(string):
 ...
 ```
 
-然后我们将这些标记发送到执行进程。
+然后我们将这些元组发送到执行进程。
 
 ### 步骤 3:执行
 
-这是 shell 中核心和有趣的一部分。当 shell 执行 `mkdir test_dir` 时,到底发生了什么?(提示: `mkdir` 是一个带有 `test_dir` 参数的执行程序,用于创建一个名为 `test_dir` 的目录。)
+这是 shell 中核心而有趣的一部分。当 shell 执行 `mkdir test_dir` 时,到底发生了什么?(提示: `mkdir` 是一个带有 `test_dir` 参数的执行程序,用于创建一个名为 `test_dir` 的目录。)
 
-`execvp` 是涉及这一步的首个函数。在我们解释 `execvp` 所做的事之前,让我们看看它的实际效果。
+`execvp` 是这一步的首先需要的函数。在我们解释 `execvp` 所做的事之前,让我们看看它的实际效果。
 
 ```
 import os
 ...
 
 def execute(cmd_tokens):
-    # Execute command
+    ### 执行命令
     os.execvp(cmd_tokens[0], cmd_tokens)
 
-    # Return status indicating to wait for next command in shell_loop
+    ### 返回状态以告知在 shell_loop 中等待下一个命令
     return SHELL_STATUS_RUN
 
 ...
@@ -144,11 +144,11 @@ def execute(cmd_tokens):
 
 再次尝试运行我们的 shell,并输入 `mkdir test_dir` 命令,接着按下回车键。
 
-在我们敲下回车键之后,问题是我们的 shell 会直接退出而不是等待下一个命令。然而,目标正确地被创建。
+在我们敲下回车键之后,问题是我们的 shell 会直接退出而不是等待下一个命令。然而,目录正确地创建了。
 
 因此,`execvp` 实际上做了什么?
 
-`execvp` 是系统调用 `exec` 的一个变体。第一个参数是程序名字。`v` 表示第二个参数是一个程序参数列表(可变参数)。`p` 表示环境变量 `PATH` 会被用于搜索给定的程序名字。在我们上一次的尝试中,它将会基于我们的 `PATH` 环境变量查找`mkdir` 程序。
+`execvp` 是系统调用 `exec` 的一个变体。第一个参数是程序名字。`v` 表示第二个参数是一个程序参数列表(参数数量可变)。`p` 表示将会使用环境变量 `PATH` 搜索给定的程序名字。在我们上一次的尝试中,它将会基于我们的 `PATH` 环境变量查找`mkdir` 程序。
 
 (还有其他 `exec` 变体,比如 execv、execvpe、execl、execlp、execlpe;你可以 google 它们获取更多的信息。)
 
@@ -158,7 +158,7 @@ def execute(cmd_tokens):
 
 因此,我们需要其他的系统调用来解决问题:`fork`。
 
-`fork` 会开辟新的内存并拷贝当前进程到一个新的进程。我们称这个新的进程为**子进程**,调用者进程为**父进程**。然后,子进程内存会被替换为被执行的程序。因此,我们的 shell,也就是父进程,可以免受内存替换的危险。
+`fork` 会分配新的内存并拷贝当前进程到一个新的进程。我们称这个新的进程为**子进程**,调用者进程为**父进程**。然后,子进程内存会被替换为被执行的程序。因此,我们的 shell,也就是父进程,可以免受内存替换的危险。
 
 让我们看看修改的代码。
 
@@ -166,34 +166,34 @@ def execute(cmd_tokens):
 ...
 
 def execute(cmd_tokens):
-    # Fork a child shell process
-    # If the current process is a child process, its `pid` is set to `0`
-    # else the current process is a parent process and the value of `pid`
-    # is the process id of its child process.
+    ### 分叉一个子 shell 进程
+    ### 如果当前进程是子进程,其 `pid` 被设置为 `0`
+    ### 否则当前进程是父进程的话,`pid` 的值
+    ### 是其子进程的进程 ID。
     pid = os.fork()
 
     if pid == 0:
-    # Child process
-        # Replace the child shell process with the program called with exec
+    ### 子进程
+        ### 用被 exec 调用的程序替换该子进程
         os.execvp(cmd_tokens[0], cmd_tokens)
     elif pid > 0:
-    # Parent process
+    ### 父进程
         while True:
-            # Wait response status from its child process (identified with pid)
+            ### 等待其子进程的响应状态(以进程 ID 来查找)
             wpid, status = os.waitpid(pid, 0)
 
-            # Finish waiting if its child process exits normally
-            # or is terminated by a signal
+            ### 当其子进程正常退出时
+            ### 或者其被信号中断时,结束等待状态
             if os.WIFEXITED(status) or os.WIFSIGNALED(status):
                 break
 
-    # Return status indicating to wait for next command in shell_loop
+    ### 返回状态以告知在 shell_loop 中等待下一个命令
     return SHELL_STATUS_RUN
 
 ...
 ```
 
-当我们的父进程调用 `os.fork()`时,你可以想象所有的源代码被拷贝到了新的子进程。此时此刻,父进程和子进程看到的是相同的代码,且并行运行着。
+当我们的父进程调用 `os.fork()` 时,你可以想象所有的源代码被拷贝到了新的子进程。此时此刻,父进程和子进程看到的是相同的代码,且并行运行着。
 
 如果运行的代码属于子进程,`pid` 将为 `0`。否则,如果运行的代码属于父进程,`pid` 将会是子进程的进程 id。
 
@@ -205,13 +205,13 @@ def execute(cmd_tokens):
 
 现在,你可以尝试运行我们的 shell 并输入 `mkdir test_dir2`。它应该可以正确执行。我们的主 shell 进程仍然存在并等待下一条命令。尝试执行 `ls`,你可以看到已创建的目录。
 
-但是,这里仍有许多问题。
+但是,这里仍有一些问题。
 
 第一,尝试执行 `cd test_dir2`,接着执行 `ls`。它应该会进入到一个空的 `test_dir2` 目录。然而,你将会看到目录并没有变为 `test_dir2`。
 
 第二,我们仍然没有办法优雅地退出我们的 shell。
 
-我们将会在 [Part 2][1] 解决诸如此类的问题。
+我们将会在 [第二部分][1] 解决诸如此类的问题。
 
 
 --------------------------------------------------------------------------------
@@ -219,8 +219,8 @@ def execute(cmd_tokens):
 via: https://hackercollider.com/articles/2016/07/05/create-your-own-shell-in-python-part-1/
 
 作者:[Supasate Choochaisri][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
+译者:[cposture](https://github.com/cposture)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
 
diff --git a/translated/tech/20160706 Create Your Own Shell in Python - Part II.md b/published/201607/20160706 Create Your Own Shell in Python - Part II.md
similarity index 76%
rename from translated/tech/20160706 Create Your Own Shell in Python - Part II.md
rename to published/201607/20160706 Create Your Own Shell in Python - Part II.md
index 0f0cd6a878..9e66d5643c 100644
--- a/translated/tech/20160706 Create Your Own Shell in Python - Part II.md	
+++ b/published/201607/20160706 Create Your Own Shell in Python - Part II.md	
@@ -1,17 +1,17 @@
-使用 Python 创建你自己的 Shell:Part II
+使用 Python 创建你自己的 Shell(下)
 ===========================================
 
-在[part 1][1] 中,我们已经创建了一个主要的 shell 循环、切分了的命令输入,以及通过 `fork` 和 `exec` 执行命令。在这部分,我们将会解决剩下的问题。首先,`cd test_dir2` 命令无法修改我们的当前目录。其次,我们仍无法优雅地从 shell 中退出。
+在[上篇][1]中,我们已经创建了一个 shell 主循环、切分了命令输入,以及通过 `fork` 和 `exec` 执行命令。在这部分,我们将会解决剩下的问题。首先,`cd test_dir2` 命令无法修改我们的当前目录。其次,我们仍无法优雅地从 shell 中退出。
 
 ### 步骤 4:内置命令
 
-“cd test_dir2 无法修改我们的当前目录” 这句话是对的,但在某种意义上也是错的。在执行完该命令之后,我们仍然处在同一目录,从这个意义上讲,它是对的。然而,目录实际上已经被修改,只不过它是在子进程中被修改。
+“`cd test_dir2` 无法修改我们的当前目录” 这句话是对的,但在某种意义上也是错的。在执行完该命令之后,我们仍然处在同一目录,从这个意义上讲,它是对的。然而,目录实际上已经被修改,只不过它是在子进程中被修改。
 
-还记得我们 fork 了一个子进程,然后执行命令,执行命令的过程没有发生在父进程上。结果是我们只是改变了子进程的当前目录,而不是父进程的目录。
+还记得我们分叉(fork)了一个子进程,然后执行命令,执行命令的过程没有发生在父进程上。结果是我们只是改变了子进程的当前目录,而不是父进程的目录。
 
 然后子进程退出,而父进程在原封不动的目录下继续运行。
 
-因此,这类与 shell 自己相关的命令必须是内置命令。它必须在 shell 进程中执行而没有分叉(forking)。
+因此,这类与 shell 自己相关的命令必须是内置命令。它必须在 shell 进程中执行而不是在分叉中(forking)。
 
 #### cd
 
@@ -35,7 +35,6 @@ yosh_project
 import os
 from yosh.constants import *
 
-
 def cd(args):
     os.chdir(args[0])
 
@@ -66,23 +65,21 @@ SHELL_STATUS_RUN = 1
 
 ```python
 ...
-# Import constants
+### 导入常量
 from yosh.constants import *
 
-# Hash map to store built-in function name and reference as key and value
+### 使用哈希映射来存储内建的函数名及其引用
 built_in_cmds = {}
 
-
 def tokenize(string):
     return shlex.split(string)
 
-
 def execute(cmd_tokens):
-    # Extract command name and arguments from tokens
+    ### 从元组中分拆命令名称与参数
     cmd_name = cmd_tokens[0]
     cmd_args = cmd_tokens[1:]
 
-    # If the command is a built-in command, invoke its function with arguments
+    ### 如果该命令是一个内建命令,使用参数调用该函数
     if cmd_name in built_in_cmds:
         return built_in_cmds[cmd_name](cmd_args)
 
@@ -91,29 +88,29 @@ def execute(cmd_tokens):
 
 我们使用一个 python 字典变量 `built_in_cmds` 作为哈希映射(hash map),以存储我们的内置函数。我们在 `execute` 函数中提取命令的名字和参数。如果该命令在我们的哈希映射中,则调用对应的内置函数。
 
-(提示:`built_in_cmds[cmd_name]` 返回能直接使用参数调用的函数引用的。)
+(提示:`built_in_cmds[cmd_name]` 返回能直接使用参数调用的函数引用。)
 
 我们差不多准备好使用内置的 `cd` 函数了。最后一步是将 `cd` 函数添加到 `built_in_cmds` 映射中。
 
 ```
 ...
-# Import all built-in function references
+### 导入所有内建函数引用
 from yosh.builtins import *
 
 ...
 
-# Register a built-in function to built-in command hash map
+### 注册内建函数到内建命令的哈希映射中
 def register_command(name, func):
     built_in_cmds[name] = func
 
 
-# Register all built-in commands here
+### 在此注册所有的内建命令
 def init():
     register_command("cd", cd)
 
 
 def main():
-    # Init shell before starting the main loop
+    ###在开始主循环之前初始化 shell
     init()
     shell_loop()
 ```
@@ -138,7 +135,7 @@ from yosh.builtins.cd import *
 
 我们需要一个可以修改 shell 状态为 `SHELL_STATUS_STOP` 的函数。这样,shell 循环可以自然地结束,shell 将到达终点而退出。
 
-和 `cd` 一样,如果我们在子进程中 fork 和执行 `exit` 函数,其对父进程是不起作用的。因此,`exit` 函数需要成为一个 shell 内置函数。
+和 `cd` 一样,如果我们在子进程中分叉并执行 `exit` 函数,其对父进程是不起作用的。因此,`exit` 函数需要成为一个 shell 内置函数。
 
 让我们从这开始:在 `builtins` 目录下创建一个名为 `exit.py` 的新文件。
 
@@ -159,7 +156,6 @@ yosh_project
 ```
 from yosh.constants import *
 
-
 def exit(args):
     return SHELL_STATUS_STOP
 ```
@@ -173,11 +169,10 @@ from yosh.builtins.exit import *
 
 最后,我们在 `shell.py` 中的 `init()` 函数注册 `exit` 命令。
 
-
 ```
 ...
 
-# Register all built-in commands here
+### 在此注册所有的内建命令
 def init():
     register_command("cd", cd)
     register_command("exit", exit)
@@ -193,7 +188,7 @@ def init():
 
 我希望你能像我一样享受创建 `yosh` (**y**our **o**wn **sh**ell)的过程。但我的 `yosh` 版本仍处于早期阶段。我没有处理一些会使 shell 崩溃的极端状况。还有很多我没有覆盖的内置命令。为了提高性能,一些非内置命令也可以实现为内置命令(避免新进程创建时间)。同时,大量的功能还没有实现(请看 [公共特性](http://tldp.org/LDP/Bash-Beginners-Guide/html/x7243.html) 和 [不同特性](http://www.tldp.org/LDP/intro-linux/html/x12249.html))
 
-我已经在 github.com/supasate/yosh 中提供了源代码。请随意 fork 和尝试。
+我已经在 https://github.com/supasate/yosh 中提供了源代码。请随意 fork 和尝试。
 
 现在该是创建你真正自己拥有的 Shell 的时候了。
 
@@ -205,12 +200,12 @@ via: https://hackercollider.com/articles/2016/07/06/create-your-own-shell-in-pyt
 
 作者:[Supasate Choochaisri][a]
 译者:[cposture](https://github.com/cposture)
-校对:[校对者ID](https://github.com/校对者ID)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
 
 [a]: https://disqus.com/by/supasate_choochaisri/
-[1]: https://hackercollider.com/articles/2016/07/05/create-your-own-shell-in-python-part-1/
+[1]: https://linux.cn/article-7624-1.html
 [2]: http://tldp.org/LDP/Bash-Beginners-Guide/html/x7243.html
 [3]: http://www.tldp.org/LDP/intro-linux/html/x12249.html
 [4]: https://github.com/supasate/yosh
diff --git a/published/20160708 Using Vagrant to control your DigitalOcean cloud instances.md b/published/201607/20160708 Using Vagrant to control your DigitalOcean cloud instances.md
similarity index 100%
rename from published/20160708 Using Vagrant to control your DigitalOcean cloud instances.md
rename to published/201607/20160708 Using Vagrant to control your DigitalOcean cloud instances.md
diff --git a/published/201607/20160718 OPEN SOURCE ACCOUNTING SOFTWARE.md b/published/201607/20160718 OPEN SOURCE ACCOUNTING SOFTWARE.md
new file mode 100644
index 0000000000..7598de6bf1
--- /dev/null
+++ b/published/201607/20160718 OPEN SOURCE ACCOUNTING SOFTWARE.md	
@@ -0,0 +1,80 @@
+GNU KHATA:开源的会计管理软件
+============================================
+
+作为一个活跃的 Linux 爱好者,我经常向我的朋友们介绍 Linux,帮助他们选择最适合他们的发行版本,同时也会帮助他们安装一些适用于他们工作的开源软件。
+
+但是在这一次,我就变得很无奈。我的叔叔,他是一个自由职业的会计师。他会有一系列的为了会计工作的漂亮而成熟的付费软件。我不那么确定我能在在开源软件中找到这么一款可以替代的软件——直到昨天。
+
+Abhishek 给我推荐了一些[很酷的软件][1],而其中 GNU Khata 脱颖而出。
+
+[GNU Khata][2] 是一个会计工具。 或者,我应该说成是一系列的会计工具集合?它就像经济管理方面的 [Evernote][3] 一样。它的应用是如此之广,以至于它不但可以用于个人的财务管理,也可以用于大型公司的管理,从店铺存货管理到税率计算,都可以有效处理。
+
+有个有趣的地方,Khata 这个词在印度或者是其他的印度语国家中意味着账户,所以这个会计软件叫做 GNU Khata。
+
+### 安装
+
+互联网上有很多关于旧的 Web 版本的 Khata 安装介绍。现在,GNU Khata 只能用在 Debian/Ubuntu 和它们的衍生版本中。我建议你按照 GNU Khata 官网给出的如下步骤来安装。我们来快速过一下。
+
+- 从[这里][4]下载安装器。
+- 在下载目录打开终端。
+- 粘贴复制以下的代码到终端,并且执行。
+
+```
+sudo chmod 755 GNUKhatasetup.run
+sudo ./GNUKhatasetup.run
+```
+
+这就结束了,从你的 Dash 或者是应用菜单中启动 GNU Khata 吧。
+
+### 第一次启动
+
+GNU Khata 在浏览器中打开,并且展现以下的画面。
+
+![](https://itsfoss.com/wp-content/uploads/2016/07/GNU-khata-1.jpg)
+
+填写组织的名字、组织形式,财务年度并且点击 proceed 按钮进入管理设置页面。
+
+![](https://itsfoss.com/wp-content/uploads/2016/07/GNU-khata-2.jpg)
+
+仔细填写你的用户名、密码、安全问题及其答案,并且点击“create and login”。
+
+![](https://itsfoss.com/wp-content/uploads/2016/07/GNU-khata-3.jpg)
+
+你已经全部设置完成了。使用菜单栏来开始使用 GNU Khata 来管理你的财务吧。这很容易。
+
+### 移除 GNU KHATA
+
+如果你不想使用 GNU Khata 了,你可以执行如下命令移除:
+
+```
+sudo apt-get remove --auto-remove gnukhata-core-engine
+```
+
+你也可以通过新立得软件管理来删除它。
+
+### GNU KHATA 真的是市面上付费会计应用的竞争对手吗?
+
+首先,GNU Khata 以简化为设计原则。顶部的菜单栏组织的很方便,可以帮助你有效的进行工作。你可以选择管理不同的账户和项目,并且切换非常容易。[它们的官网][5]表明,GNU Khata 可以“像说印度语一样方便”(LCTT 译注:原谅我,这个软件作者和本文作者是印度人……)。同时,你知道 GNU Khata 也可以在云端使用吗?
+
+所有的主流的账户管理工具,比如分类账簿、项目报表、财务报表等等都用专业的方式整理,并且支持自定义格式和即时展示。这让会计和仓储管理看起来如此的简单。
+
+这个项目正在积极的发展,正在寻求实操中的反馈以帮助这个软件更加进步。考虑到软件的成熟性、使用的便利性还有免费的情况,GNU Khata 可能会成为你最好的账簿助手。
+
+请在评论框里留言吧,让我们知道你是如何看待 GNU Khata 的。
+
+--------------------------------------------------------------------------------
+
+via: https://itsfoss.com/using-gnu-khata/
+
+作者:[Aquil Roshan][a]
+译者:[MikeCoder](https://github.com/MikeCoder)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://itsfoss.com/author/aquil/
+[1]: https://itsfoss.com/category/apps/
+[2]: http://www.gnukhata.in/
+[3]: https://evernote.com/
+[4]: https://cloud.openmailbox.org/index.php/s/L8ppsxtsFq1345E/download
+[5]: http://www.gnukhata.in/
diff --git a/published/201607/20160721 YOU CAN TRY A DEMO UBUNTU VERSION IN A WEB BROWSER.md b/published/201607/20160721 YOU CAN TRY A DEMO UBUNTU VERSION IN A WEB BROWSER.md
new file mode 100644
index 0000000000..0fd776c078
--- /dev/null
+++ b/published/201607/20160721 YOU CAN TRY A DEMO UBUNTU VERSION IN A WEB BROWSER.md	
@@ -0,0 +1,36 @@
+在浏览器中体验 Ubuntu
+=====================================================
+
+[Ubuntu][2] 的背后的公司 [Canonical][1] 为 Linux 推广做了很多努力。无论你有多么不喜欢 Ubuntu,你必须承认它对 “Linux 易用性”的影响。Ubuntu 以及其衍生是使用最多的 Linux 版本。
+
+为了进一步推广 Ubuntu Linux,Canonical 把它放到了浏览器里,你可以在任何地方使用这个 [Ubuntu 演示版][0]。 它将帮你更好的体验 Ubuntu,以便让新人更容易决定是否使用它。
+
+你可能争辩说 USB 版的 Linux 更好。我同意,但是你要知道你要下载 ISO,创建 USB 启动盘,修改配置文件,然后才能使用这个 USB 启动盘来体验。这么乏味并不是每个人都乐意这么干的。 在线体验是一个更好的选择。
+
+那么,你能在 Ubuntu 在线看到什么。实际上并不多。
+
+你可以浏览文件,你可以使用 Unity Dash,浏览 Ubuntu 软件中心,甚至装几个应用(当然它们不会真的安装),看一看文件浏览器和其它一些东西。以上就是全部了。但是在我看来,这已经做的很好了,让你知道它是个什么,对这个流行的操作系统有个直接感受。
+
+![](https://itsfoss.com/wp-content/uploads/2016/07/Ubuntu-online-demo.jpeg)
+
+![](https://itsfoss.com/wp-content/uploads/2016/07/Ubuntu-online-demo-1.jpeg)
+
+![](https://itsfoss.com/wp-content/uploads/2016/07/Ubuntu-online-demo-2.jpeg)
+
+如果你的朋友或者家人对试试 Linux 抱有兴趣,但是想在安装前想体验一下 Linux 。你可以给他们以下链接:[Ubuntu 在线导览][0] 。
+
+
+--------------------------------------------------------------------------------
+
+via: https://itsfoss.com/ubuntu-online-demo/ 
+
+作者:[Abhishek Prakash][a]
+译者:[kokialoves](https://github.com/kokialoves)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://itsfoss.com/author/abhishek/
+[0]: http://tour.ubuntu.com/en/
+[1]: http://www.canonical.com/
+[2]: http://www.ubuntu.com/
diff --git a/published/201607/20160722 7 Best Markdown Editors for Linux.md b/published/201607/20160722 7 Best Markdown Editors for Linux.md
new file mode 100644
index 0000000000..7d1ffe0c86
--- /dev/null
+++ b/published/201607/20160722 7 Best Markdown Editors for Linux.md	
@@ -0,0 +1,200 @@
+Linux 上 10 个最好的 Markdown 编辑器
+======================================
+
+在这篇文章中,我们会点评一些可以在 Linux 上安装使用的最好的 Markdown 编辑器。 你可以找到非常多的 Linux 平台上的 Markdown 编辑器,但是在这里我们将尽可能地为您推荐那些最好的。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Best-Linux-Markdown-Editors.png)
+
+*Best Linux Markdown Editors*
+
+对于不了解 Markdown 的人做个简单介绍,Markdown 是由著名的 Aaron Swartz  和 John Gruber 发明的标记语言,其最初的解析器是一个用 Perl 写的简单、轻量的[同名工具][1]。它可以将用户写的纯文本转为可用的 HTML(或 XHTML)。它实际上是一门易读,易写的纯文本语言,以及一个用于将文本转为 HTML 的转换工具。
+
+希望你先对 Markdown 有一个稍微的了解,接下来让我们逐一列出这些编辑器。
+
+### 1. Atom
+
+Atom 是一个现代的、跨平台、开源且强大的文本编辑器,它可以运行在 Linux、Windows 和 MAC OS X 等操作系统上。用户可以在它的基础上进行定制,删减修改任何配置文件。
+
+它包含了一些非常杰出的特性:
+
+- 内置软件包管理器
+- 智能自动补全功能
+- 提供多窗口操作
+- 支持查找替换功能
+- 包含一个文件系统浏览器
+- 轻松自定义主题
+- 开源、高度扩展性的软件包等
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Atom-Markdown-Editor-for-Linux.png)
+
+*Atom Markdown Editor for Linux*
+
+访问主页: <https://atom.io/>
+
+### 2. GNU Emacs
+
+Emacs 是 Linux 平台上一款的流行文本编辑器。它是一个非常棒的、具备高扩展性和定制性的 Markdown 语言编辑器。
+
+它综合了以下这些神奇的特性:
+
+- 带有丰富的内置文档,包括适合初学者的教程
+- 有完整的 Unicode 支持,可显示所有的人类符号
+- 支持内容识别的文本编辑模式
+- 包括多种文件类型的语法高亮
+- 可用 Emacs Lisp 或 GUI 对其进行高度定制
+- 提供了一个包系统可用来下载安装各种扩展等
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Emacs-Markdown-Editor-for-Linux.png)
+
+*Emacs Markdown Editor for Linux*
+
+访问主页: <https://www.gnu.org/software/emacs/>
+
+### 3. Remarkable
+
+Remarkable 可能是 Linux 上最好的 Markdown 编辑器了,它也适用于 Windows 操作系统。它的确是是一个卓越且功能齐全的 Markdown 编辑器,为用户提供了一些令人激动的特性。
+
+一些卓越的特性:
+
+- 支持实时预览
+- 支持导出 PDF 和 HTML
+- 支持 Github Markdown 语法
+- 支持定制 CSS
+- 支持语法高亮
+- 提供键盘快捷键
+- 高可定制性和其他
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Remarkable-Markdown-Editor-for-Linux.png)
+
+*Remarkable Markdown Editor for Linux*
+
+访问主页: <https://remarkableapp.github.io>
+
+### 4. Haroopad
+
+Haroopad 是为 Linux,Windows 和 Mac OS X 构建的跨平台 Markdown 文档处理程序。用户可以用它来书写许多专家级格式的文档,包括电子邮件、报告、博客、演示文稿和博客文章等等。
+
+功能齐全且具备以下的亮点:
+
+- 轻松导入内容
+- 支持导出多种格式
+- 广泛支持博客和邮件
+- 支持许多数学表达式
+- 支持 Github Markdown 扩展
+- 为用户提供了一些令人兴奋的主题、皮肤和 UI 组件等等
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Haroopad-Markdown-Editor-for-Linux.png)
+
+*Haroopad Markdown Editor for Linux*
+
+访问主页: <http://pad.haroopress.com/>
+
+### 5. ReText
+
+ReText 是为 Linux 和其它几个 POSIX 兼容操作系统提供的简单、轻量、强大的 Markdown 编辑器。它还可以作为一个 reStructuredText 编辑器,并且具有以下的特性:
+
+- 简单直观的 GUI
+- 具备高定制性,用户可以自定义语法文件和配置选项
+- 支持多种配色方案
+- 支持使用多种数学公式
+- 启用导出扩展等等
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/ReText-Markdown-Editor-for-Linux.png)
+
+*ReText Markdown Editor for Linux*
+
+访问主页: <https://github.com/retext-project/retext>
+
+### 6. UberWriter
+
+UberWriter 是一个简单、易用的 Linux Markdown 编辑器。它的开发受 Mac OS X 上的 iA writer 影响很大,同样它也具备这些卓越的特性:
+
+- 使用 pandoc 进行所有的文本到 HTML 的转换
+- 提供了一个简洁的 UI 界面
+- 提供了一种专心(distraction free)模式,高亮用户最后的句子
+- 支持拼写检查
+- 支持全屏模式
+- 支持用 pandoc 导出 PDF、HTML 和 RTF
+- 启用语法高亮和数学函数等等
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/UberWriter-Markdown-Editor-for-Linux.png)
+
+*UberWriter Markdown Editor for Linux*
+
+访问主页: <http://uberwriter.wolfvollprecht.de/>
+
+### 7. Mark My Words
+
+Mark My Words 同样也是一个轻量、强大的 Markdown 编辑器。它是一个相对比较新的编辑器,因此提供了包含语法高亮在内的大量的功能,简单和直观的 UI。
+
+下面是一些棒极了,但还未捆绑到应用中的功能:
+
+- 实时预览
+- Markdown 解析和文件 IO
+- 状态管理
+- 支持导出 PDF 和 HTML
+- 监测文件的修改
+- 支持首选项设置
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/MarkMyWords-Markdown-Editor-for-Linux.png)
+
+*MarkMyWords Markdown Editor for-Linux*
+
+访问主页: <https://github.com/voldyman/MarkMyWords>
+
+### 8. Vim-Instant-Markdown 插件
+
+Vim 是 Linux 上的一个久经考验的强大、流行而开源的文本编辑器。它用于编程极棒。它也高度支持插件功能,可以让用户为其增加一些其它功能,包括 Markdown 预览。
+
+有好几种 Vim 的 Markdown 预览插件,但是 [Vim-Instant-Markdown][2] 的表现最佳。
+
+###9. Bracket-MarkdownPreview 插件
+
+Brackets 是一个现代、轻量、开源且跨平台的文本编辑器。它特别为 Web 设计和开发而构建。它的一些重要功能包括:支持内联编辑器、实时预览、预处理支持及更多。
+
+它也是通过插件高度可扩展的,你可以使用 [Bracket-MarkdownPreview][3] 插件来编写和预览 Markdown 文档。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Brackets-Markdown-Plugin.png)
+
+*Brackets Markdown Plugin Preview*
+
+### 10. SublimeText-Markdown 插件
+
+Sublime Text 是一个精心打造的、流行的、跨平台文本编辑器,用于代码、markdown 和普通文本。它的表现极佳,包括如下令人兴奋的功能:
+
+- 简洁而美观的 GUI
+- 支持多重选择
+- 提供专心模式
+- 支持窗体分割编辑
+- 通过 Python 插件 API 支持高度插件化
+- 完全可定制化,提供命令查找模式
+
+[SublimeText-Markdown][4] 插件是一个支持格式高亮的软件包,带有一些漂亮的颜色方案。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/SublimeText-Markdown-Plugin-Preview.png)
+
+*SublimeText Markdown Plugin Preview*
+
+### 结论
+
+通过上面的列表,你大概已经知道要为你的 Linux 桌面下载、安装什么样的 Markdown 编辑器和文档处理程序了。
+
+请注意,这里提到的最好的 Markdown 编辑器可能对你来说并不是最好的选择。因此你可以通过下面的反馈部分,为我们展示你认为列表中未提及的,并且具备足够的资格的,令人兴奋的 Markdown 编辑器。
+
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/best-markdown-editors-for-linux/
+
+作者:[Aaron Kili][a]
+译者:[Locez](https://github.com/locez)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
+[1]: https://daringfireball.net/projects/markdown/
+[2]: https://github.com/suan/vim-instant-markdown
+[3]: https://github.com/gruehle/MarkdownPreview
+[4]: https://github.com/SublimeText-Markdown/MarkdownEditing
+
diff --git a/published/201607/20160722 HOW TO CHANGE DEFAULT APPLICATIONS IN UBUNTU.md b/published/201607/20160722 HOW TO CHANGE DEFAULT APPLICATIONS IN UBUNTU.md
new file mode 100644
index 0000000000..b485632d6c
--- /dev/null
+++ b/published/201607/20160722 HOW TO CHANGE DEFAULT APPLICATIONS IN UBUNTU.md	
@@ -0,0 +1,69 @@
+怎样在 Ubuntu 中修改默认程序
+==============================================
+
+![](https://itsfoss.com/wp-content/uploads/2016/07/change-default-applications-ubuntu.jpg)
+
+> 简介: 这个新手指南会向你展示如何在 Ubuntu Linux 中修改默认程序
+
+对于我来说,安装 [VLC 多媒体播放器][1]是[安装完 Ubuntu 16.04 该做的事][2]中最先做的几件事之一。为了能够使我双击一个视频就用 VLC 打开,在我安装完 VLC 之后我会设置它为默认程序。
+
+作为一个新手,你需要知道如何在 Ubuntu 中修改任何默认程序,这也是我今天在这篇指南中所要讲的。
+
+### 在 UBUNTU 中修改默认程序
+
+这里提及的方法适用于所有的 Ubuntu 12.04,Ubuntu 14.04 和Ubuntu 16.04。在 Ubuntu 中,这里有两种基本的方法可以修改默认程序:
+
+- 通过系统设置
+- 通过右键菜单
+
+#### 1.通过系统设置修改 Ubuntu 的默认程序
+
+进入 Unity 面板并且搜索系统设置(System Settings):
+
+![](https://itsfoss.com/wp-content/uploads/2013/11/System_Settings_Ubuntu.jpeg)
+
+在系统设置(System Settings)中,选择详细选项(Details):
+
+![](https://itsfoss.com/wp-content/uploads/2016/07/System-settings-detail-ubuntu.jpeg)
+
+在左边的面板中选择默认程序(Default Applications),你会发现在右边的面板中可以修改默认程序。
+
+![](https://itsfoss.com/wp-content/uploads/2016/07/System-settings-default-applications.jpeg)
+
+正如看到的那样,这里只有少数几类的默认程序可以被改变。你可以在这里改变浏览器、邮箱客户端、日历、音乐、视频和相册的默认程序。那其他类型的默认程序怎么修改?
+
+不要担心,为了修改其他类型的默认程序,我们会用到右键菜单。
+
+#### 2.通过右键菜单修改默认程序
+
+如果你使用过 Windows 系统,你应该看见过右键菜单的“打开方式”,可以通过这个来修改默认程序。我们在 Ubuntu 中也有相似的方法。
+
+右键一个还没有设置默认打开程序的文件,选择“属性(properties)”
+
+![](https://itsfoss.com/wp-content/uploads/2016/05/WebP-images-Ubuntu-Linux-3.png)
+
+*从右键菜单中选择属性*
+
+在这里,你可以选择使用什么程序打开,并且设置为默认程序。
+
+![](https://itsfoss.com/wp-content/uploads/2016/05/WebP-images-Ubuntu-Linux-4.png)
+
+*在 Ubuntu 中设置打开 WebP 图片的默认程序为 gThumb*
+
+小菜一碟不是么?一旦你做完这些,所有同样类型的文件都会用你选择的默认程序打开。
+
+我很希望这个新手指南对你在修改 Ubuntu 的默认程序时有帮助。如果你有任何的疑问或者建议,可以随时在下面评论。
+
+--------------------------------------------------------------------------------
+
+via: https://itsfoss.com/change-default-applications-ubuntu/
+
+作者:[Abhishek Prakash][a]
+译者:[Locez](https://github.com/locez)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://itsfoss.com/author/abhishek/
+[1]: http://www.videolan.org/vlc/index.html
+[2]: https://linux.cn/article-7453-1.html
diff --git a/published/201607/20160722 Terminix – A New GTK 3 Tiling Terminal Emulator for Linux.md b/published/201607/20160722 Terminix – A New GTK 3 Tiling Terminal Emulator for Linux.md
new file mode 100644
index 0000000000..e9ec640549
--- /dev/null
+++ b/published/201607/20160722 Terminix – A New GTK 3 Tiling Terminal Emulator for Linux.md	
@@ -0,0 +1,136 @@
+Terminix:一个很赞的基于 GTK3 的平铺式 Linux 终端模拟器
+============================================================
+
+现在,你可以很容易的找到[大量的 Linux 终端模拟器][1],每一个都可以给用户留下深刻的印象。
+
+但是,很多时候,我们会很难根据我们的喜好来找到一款心仪的日常使用的终端模拟器。这篇文章中,我们将会推荐一款叫做 Terminix 的令人激动的终端模拟机。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Terminix-Terminal-Emulator-for-Linux.png)
+
+*Terminix Linux 终端模拟器*
+
+Terminix 是一个使用 VTE GTK+ 3 组件的平铺式终端模拟器。使用 GTK 3 开发的原因主要是为了符合 GNOME  HIG(人机接口 Human Interface Guidelines) 标准。另外,Terminix 已经在 GNOME 和 Unity 桌面环境下测试过了,也有用户在其他的 Linux 桌面环境下测试成功。
+
+和其他的终端模拟器一样,Terminix 有着很多知名的特征,列表如下:
+
+- 允许用户进行任意的垂直或者水平分屏
+- 支持拖拽功能来进行重新排布终端
+- 支持使用拖拽的方式终端从窗口中将脱离出来
+- 支持终端之间的输入同步,因此,可以在一个终端输入命令,而在另一个终端同步复现
+- 终端的分组配置可以保存在硬盘,并再次加载
+- 支持透明背景
+- 允许使用背景图片
+- 基于主机和目录来自动切换配置
+- 支持进程完成的通知信息
+- 配色方案采用文件存储,同时支持自定义配色方案
+
+### 如何在 Linux 系统上安装 Terminix
+
+现在来详细说明一下在不同的 Linux 发行版本上安装 Terminix 的步骤。首先,在此列出 Terminix 在 Linux 所需要的环境需求。
+
+#### 依赖组件
+
+为了正常运行,该应用需要使用如下库:
+
+- GTK 3.14 或者以上版本
+- GTK VTE 0.42 或者以上版本
+- Dconf
+- GSettings
+- Nautilus 的 iNautilus-Python 插件
+
+如果你已经满足了如上的系统要求,接下来就是安装 Terminix 的步骤。
+
+#### 在 RHEL/CentOS 7 或者 Fedora 22-24 上
+
+首先,你需要通过新建文件 `/etc/yum.repos.d/terminix.repo` 来增加软件仓库,使用你最喜欢的文本编辑器来进行编辑:
+
+```
+# vi /etc/yum.repos.d/terminix.repo
+```
+
+然后拷贝如下的文字到我们刚新建的文件中:
+
+```
+[heikoada-terminix]
+name=Copr repo for terminix owned by heikoada
+baseurl=https://copr-be.cloud.fedoraproject.org/results/heikoada/terminix/fedora-$releasever-$basearch/
+skip_if_unavailable=True
+gpgcheck=1
+gpgkey=https://copr-be.cloud.fedoraproject.org/results/heikoada/terminix/pubkey.gpg
+enabled=1
+enabled_metadata=1
+```
+
+保存文件并退出。
+
+然后更新你的系统,并且安装 Terminix,步骤如下:
+
+```
+---------------- On RHEL/CentOS 7 ---------------- 
+# yum update
+# yum install terminix
+
+---------------- On Fedora 22-24 ---------------- 
+# dnf update
+# dnf install terminix
+```
+
+#### 在 Ubuntu 16.04-14.04 和 Linux Mint 18-17
+
+虽然没有基于 Debian/Ubuntu 发行版本的官方的软件包,但是你依旧可以通过如下的命令手动安装。
+
+```
+$ wget -c https://github.com/gnunn1/terminix/releases/download/1.1.1/terminix.zip
+$ sudo unzip terminix.zip -d / 
+$ sudo glib-compile-schemas /usr/share/glib-2.0/schemas/
+```
+
+#### 其它 Linux 发行版
+
+OpenSUSE 用户可以从默认仓库中安装 Terminix,Arch Linux 用户也可以安装 [AUR Terminix 软件包][2]。
+
+### Terminix 截图教程
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Terminix-Terminal.png)
+
+*Terminix 终端*
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Terminix-Terminal-Settings.png)
+
+*Terminix 终端设置*
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Terminix-Terminal-Tabs.png)
+
+*Terminix 多终端界面*
+
+### 如何卸载删除 Terminix
+
+
+如果你是手动安装的 Terminix 并且想要删除它,那么你可以参照如下的步骤来卸载它。从 [Github 仓库][3]上下载 uninstall.sh,并且给它可执行权限并且执行它:
+
+```
+$ wget -c https://github.com/gnunn1/terminix/blob/master/uninstall.sh
+$ chmod +x uninstall.sh
+$ sudo sh uninstall.sh
+```
+
+但是如果你是通过包管理器安装的 Terminix,你可以使用包管理器来卸载它。
+
+在这篇介绍中,我们在众多优秀的终端模拟器中发现了一个重要的 Linux 终端模拟器。你可以尝试着去体验下它的新特性,并且可以将它和你现在使用的终端进行比较。
+
+重要的一点,如果你想得到更多信息或者有疑问,请使用评论区,而且不要忘了,给我一个关于你使用体验的反馈。
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/terminix-tiling-terminal-emulator-for-linux/
+
+作者:[Aaron Kili][a]
+译者:[MikeCoder](https://github.com/MikeCoder)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
+[1]: http://www.tecmint.com/linux-terminal-emulators/
+[2]: https://aur.archlinux.org/packages/terminix
+[3]: https://github.com/gnunn1/terminix
diff --git a/published/201607/20160726 Set a Real Time Photo of Earth as Your Linux Desktop Wallpaper.md b/published/201607/20160726 Set a Real Time Photo of Earth as Your Linux Desktop Wallpaper.md
new file mode 100644
index 0000000000..40ba237020
--- /dev/null
+++ b/published/201607/20160726 Set a Real Time Photo of Earth as Your Linux Desktop Wallpaper.md	
@@ -0,0 +1,90 @@
+为你的 Linux 桌面设置一张实时的地球照片
+=================================================================
+
+![](http://www.omgubuntu.co.uk/wp-content/uploads/2016/07/Screen-Shot-2016-07-26-at-16.36.47-1.jpg)
+
+厌倦了看同样的桌面背景了么?这里有一个(可能是)世界上最棒的东西。
+
+‘[Himawaripy][1]’ 是一个 Python 3 小脚本,它会抓取由[日本 Himawari 8 气象卫星][2]拍摄的接近实时的地球照片,并将它设置成你的桌面背景。
+
+安装完成后,你可以将它设置成每 10 分钟运行的定时任务(自然,它要在后台运行),这样它就可以实时地取回地球的照片并设置成背景了。
+
+因为 Himawari-8 是一颗同步轨道卫星,你只能看到澳大利亚上空的地球的图片——但是它实时的天气形态、云团和光线仍使它很壮丽,对我而言要是看到英国上方的就更好了!
+
+高级设置允许你配置从卫星取回的图片质量,但是要记住增加图片质量会增加文件大小及更长的下载等待!
+
+最后,虽然这个脚本与其他我们提到过的其他脚本类似,它还仍保持更新及可用。
+
+###获取 Himawaripy
+
+Himawaripy 已经在一系列的桌面环境中都测试过了,包括 Unity、LXDE、i3、MATE 和其他桌面环境。它是自由开源软件,但是整体来说安装及配置不太简单。
+
+在该项目的 [Github 主页][0]上可以找到安装和设置该应用程序的所有指导(提示:没有一键安装功能)。
+
+- [实时地球壁纸脚本的 GitHub 主页][0]
+
+### 安装及使用
+
+![](http://www.omgubuntu.co.uk/wp-content/uploads/2016/07/Screen-Shot-2016-07-26-at-16.46.13-750x143.png)
+
+一些读者请我在本文中补充一下一步步安装该应用的步骤。以下所有步骤都在其 GitHub 主页上,这里再贴一遍。
+
+1、下载及解压 Himawaripy
+
+这是最容易的步骤。点击下面的下载链接,然后下载最新版本,并解压到你的下载目录里面。
+
+ - [下载 Himawaripy 主干文件(.zip 格式)][3]
+
+2、安装 python3-setuptools
+
+你需要手工来安装主干软件包,Ubuntu 里面默认没有安装它:
+
+```
+sudo apt install python3-setuptools
+```
+
+3、安装 Himawaripy
+
+在终端中,你需要切换到之前解压的目录中,并运行如下安装命令:
+
+```
+cd ~/Downloads/himawaripy-master
+sudo python3 setup.py install
+```
+
+4、 看看它是否可以运行并下载最新的实时图片:
+```
+himawaripy
+```
+5、 设置定时任务
+
+如果你希望该脚本可以在后台自动运行并更新(如果你需要手动更新,只需要运行 ‘himarwaripy’ 即可)
+
+在终端中运行:
+```
+crontab -e
+```
+在其中新加一行(默认每10分钟运行一次)
+```
+*/10 * * * * /usr/local/bin/himawaripy
+```
+关于[配置定时任务][4]可以在 Ubuntu Wiki 上找到更多信息。
+
+该脚本安装后你不需要不断运行它,它会自动的每十分钟在后台运行一次。
+
+--------------------------------------------------------------------------------
+
+via: http://www.omgubuntu.co.uk/2016/07/set-real-time-earth-wallpaper-ubuntu-desktop
+
+作者:[JOEY-ELIJAH SNEDDON][a]
+译者:[geekpi](https://github.com/geekpi)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://plus.google.com/117485690627814051450/?rel=author
+[1]: https://github.com/boramalper/himawaripy
+[2]: https://en.wikipedia.org/wiki/Himawari_8
+[0]: https://github.com/boramalper/himawaripy
+[3]: https://github.com/boramalper/himawaripy/archive/master.zip
+[4]: https://help.ubuntu.com/community/CronHowto
\ No newline at end of file
diff --git a/published/201608/20151118 Fabric – Automate Your Linux Administration Tasks and Application Deployments Over SSH.md b/published/201608/20151118 Fabric – Automate Your Linux Administration Tasks and Application Deployments Over SSH.md
new file mode 100644
index 0000000000..fdd53956c4
--- /dev/null
+++ b/published/201608/20151118 Fabric – Automate Your Linux Administration Tasks and Application Deployments Over SSH.md	
@@ -0,0 +1,257 @@
+Fabric - 通过 SSH 来自动化管理 Linux 任务和布署应用
+===========================
+
+当要管理远程机器或者要布署应用时,虽然你有多种命令行工具可以选择,但是其中很多工具都缺少详细的使用文档。
+
+在这篇教程中,我们将会一步一步地向你介绍如何使用 fabric 来帮助你更好得管理多台服务器。
+
+![](http://www.tecmint.com/wp-content/uploads/2015/11/Automate-Linux-Administration-Tasks-Using-Fabric.png)
+
+*使用 Fabric 来自动化地管理 Linux 任务*
+
+Fabric 是一个用 Python 编写的命令行工具库,它可以帮助系统管理员高效地执行某些任务,比如通过 SSH 到多台机器上执行某些命令,远程布署应用等。
+
+在使用之前,如果你拥有使用 Python 的经验能帮你更好的使用 Fabric。当然,如果没有那也不影响使用 Fabric。
+
+我们为什么要选择 Fabric:
+
+- 简单
+- 完备的文档
+- 如果你会 Python,不用增加学习其他语言的成本
+- 易于安装使用
+- 使用便捷
+- 支持多台机器并行操作
+
+### 在 Linux 上如何安装 Fabric
+
+Fabric 有一个特点就是要远程操作的机器只需要支持标准的 OpenSSH 服务即可。只要保证在机器上安装并开启了这个服务就能使用 Fabric 来管理机器。
+
+#### 依赖
+
+- Python 2.5 或更新版本,以及对应的开发组件
+- Python-setuptools 和 pip(可选,但是非常推荐)gcc
+
+我们推荐使用 pip 安装 Fabric,但是你也可以使用系统自带的包管理器如 `yum`, `dnf` 或 `apt-get` 来安装,包名一般是 `fabric` 或 `python-fabric`。
+
+如果是基于 RHEL/CentOS 的发行版本的系统,你可以使用系统自带的 [EPEL 源][1] 来安装 fabric。
+
+```
+# yum install fabric   [适用于基于 RedHat 系统]
+# dnf install fabric   [适用于 Fedora 22+ 版本]
+```
+
+如果你是 Debian 或者其派生的系统如 Ubuntu 和 Mint 的用户,你可以使用 apt-get 来安装,如下所示:
+
+```
+# apt-get install fabric
+```
+
+如果你要安装开发版的 Fabric,你需要安装 pip 来安装 master 分支上最新版本。
+
+```
+# yum install python-pip       [适用于基于 RedHat 系统]
+# dnf install python-pip       [适用于Fedora 22+ 版本]
+# apt-get install python-pip   [适用于基于 Debian 系统]
+```
+
+安装好 pip 后,你可以使用 pip 获取最新版本的 Fabric。
+
+```
+# pip install fabric
+```
+
+### 如何使用 Fabric 来自动化管理 Linux 任务
+
+现在我们来开始使用 Fabric,在之前的安装的过程中,Fabric Python 脚本已经被放到我们的系统目录,当我们要运行 Fabric 时输入 `fab` 命令即可。
+
+#### 在本地 Linux 机器上运行命令行
+
+按照惯例,先用你喜欢的编辑器创建一个名为 fabfile.py 的 Python 脚本。你可以使用其他名字来命名脚本,但是就需要指定这个脚本的路径,如下所示:
+
+```
+# fabric --fabfile /path/to/the/file.py
+```
+
+Fabric 使用 `fabfile.py` 来执行任务,这个文件应该放在你执行 Fabric 命令的目录里面。
+
+**例子 1**:创建入门的 `Hello World` 任务:
+
+```
+# vi fabfile.py
+```
+
+在文件内输入如下内容:
+
+```
+def hello():
+    print('Hello world, Tecmint community')
+```
+
+保存文件并执行以下命令:
+
+```
+# fab hello
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2015/11/Create-Fabric-Fab-Python-File.gif)
+
+*Fabric 工具使用说明*
+
+**例子 2**:新建一个名为 fabfile.py 的文件并打开:
+
+粘贴以下代码至文件:
+
+```
+#!  /usr/bin/env python
+from fabric.api import local
+def uptime():
+    local('uptime')
+```
+
+保存文件并执行以下命令:
+
+```
+# fab uptime
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2015/11/Fabric-Uptime.gif)
+
+*Fabric: 检查系统运行时间*
+
+让我们看看这个例子,fabfile.py 文件在本机执行了 uptime 这个命令。
+
+#### 在远程 Linux 机器上运行命令来执行自动化任务
+
+Fabric API 使用了一个名为 `env` 的关联数组(Python 中的词典)作为配置目录,来储存 Fabric 要控制的机器的相关信息。
+
+`env.hosts` 是一个用来存储你要执行 Fabric 任务的机器的列表,如果你的 IP 地址是 192.168.0.0,想要用 Fabric 来管理地址为 192.168.0.2 和 192.168.0.6 的机器,需要的配置如下所示:
+
+```
+#!/usr/bin/env python
+from fabric.api import env
+    env.hosts = [ '192.168.0.2', '192.168.0.6' ]
+```
+
+上面这几行代码只是声明了你要执行 Fabric 任务的主机地址,但是实际上并没有执行任何任务,下面我们就来定义一些任务。Fabric 提供了一系列可以与远程服务器交互的方法。
+
+Fabric 提供了众多的方法,这里列出几个经常会用到的:
+
+- run - 可以在远程机器上运行的 shell 命令
+- local - 可以在本机上运行的 shell 命令
+- sudo - 使用 root 权限在远程机器上运行的 shell 命令
+- get - 从远程机器上下载一个或多个文件
+- put - 上传一个或多个文件到远程机器
+
+**例子 3**:在多台机子上输出信息,新建新的 fabfile.py 文件如下所示
+
+```
+#!/usr/bin/env python
+from fabric.api import env, run
+env.hosts = ['192.168.0.2','192.168.0.6']
+def echo():
+    run("echo -n 'Hello, you are tuned to Tecmint ' ")
+```
+
+运行以下命令执行 Fabric 任务
+
+```
+# fab echo
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2015/11/Fabrick-Automate-Linux-Tasks.gif)
+
+*fabric: 自动在远程 Linux 机器上执行任务*
+
+**例子 4**:你可以继续改进之前创建的执行 uptime 任务的 fabfile.py 文件,让它可以在多台服务器上运行 uptime 命令,也可以检查其磁盘使用情况,如下所示:
+
+```
+#!/usr/bin/env python
+from fabric.api import env, run
+env.hosts = ['192.168.0.2','192.168.0.6']
+def uptime():
+    run('uptime')
+def disk_space():
+    run('df -h')
+```
+
+保存并执行以下命令
+
+```
+# fab uptime
+# fab disk_space
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2015/11/Fabric-Run-Multiple-Commands-on-Multiple-Linux-Systems.gif)
+
+*Fabric:自动在多台服务器上执行任务*
+
+#### 在远程服务器上自动化布署 LAMP
+
+**例子 5**:我们来尝试一下在远程服务器上布署 LAMP(Linux, Apache, MySQL/MariaDB and PHP)
+
+我们要写个函数在远程使用 root 权限安装 LAMP。
+
+##### 在 RHEL/CentOS 或 Fedora 上
+
+```
+#!/usr/bin/env python
+from fabric.api import env, run
+env.hosts = ['192.168.0.2','192.168.0.6']
+def deploy_lamp():
+    run ("yum install -y httpd mariadb-server php php-mysql")
+```
+
+##### 在 Debian/Ubuntu 或 Linux Mint 上
+
+```
+#!/usr/bin/env python
+from fabric.api import env, run
+env.hosts = ['192.168.0.2','192.168.0.6']
+def deploy_lamp():
+    sudo("apt-get install -q apache2 mysql-server libapache2-mod-php5 php5-mysql")
+```
+
+保存并执行以下命令:
+
+```
+# fab deploy_lamp
+```
+
+注:由于安装时会输出大量信息,这个例子我们就不提供屏幕 gif 图了
+
+现在你可以使用 Fabric 和上文例子所示的功能来[自动化的管理 Linux 服务器上的任务][2]了。
+
+#### 一些 Fabric 有用的选项
+
+- 你可以运行 `fab -help` 输出帮助信息,里面列出了所有可以使用的命令行信息
+- `–fabfile=PATH` 选项可以让你定义除了名为 fabfile.py 之外的模块
+- 如果你想用指定的用户名登录远程主机,请使用 `-user=USER` 选项
+- 如果你需要密码进行验证或者 sudo 提权,请使用 `–password=PASSWORD` 选项
+- 如果需要输出某个命令的详细信息,请使用 `–display=命令名` 选项
+- 使用 `--list` 输出所有可用的任务
+- 使用 `--list-format=FORMAT` 选项能格式化 `-list` 选项输出的信息,可选的有 short、normal、 nested
+- `--config=PATH` 选项可以指定读取配置文件的地址
+- `-–colorize-errors` 能显示彩色的错误输出信息
+- `--version` 输出当前版本
+
+### 总结
+
+Fabric 是一个强大并且文档完备的工具,对于新手来说也能很快上手,阅读提供的文档能帮助你更好的了解它。如果你在安装和使用 Fabric 时发现什么问题可以在评论区留言,我们会及时回复。
+
+参考:[Fabric 文档][3]
+
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/automating-linux-system-administration-tasks/
+
+作者:[Aaron Kili][a]
+译者:[NearTan](https://github.com/NearTan)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
+[1]: https://linux.cn/article-2324-1.html
+[2]: http://www.tecmint.com/use-ansible-playbooks-to-automate-complex-tasks-on-multiple-linux-servers/
+[3]: http://docs.fabfile.org/en/1.4.0/usage/env.html
diff --git a/published/201608/20151208 6 creative ways to use ownCloud.md b/published/201608/20151208 6 creative ways to use ownCloud.md
new file mode 100644
index 0000000000..02824a4255
--- /dev/null
+++ b/published/201608/20151208 6 creative ways to use ownCloud.md	
@@ -0,0 +1,96 @@
+ownCloud 的六大神奇用法
+================================================================================
+
+![Yearbook cover 2015](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/osdc-open-source-yearbook-lead1-inc0335020sw-201511-01.png)
+
+(图片来源:Opensource.com)
+
+[ownCloud][1] 是一个自行托管的开源文件同步和共享服务器。就像“行业老大” Dropbox、Google Drive、Box 和其他的同类服务一样,ownCloud 也可以让你访问自己的文件、日历、联系人和其他数据。你可以在自己设备之间同步任意数据(或部分数据)并分享给其他人。然而,ownCloud 要比其它的商业解决方案更棒,可以[将 ownCloud 运行在自己的服务器][2]而不是其它人的服务器上。
+
+现在,让我们一起来看看在 ownCloud 上的六个创造性的应用方式。其中一些是由于 ownCloud 的开源才得以完成,而另外的则是 ownCloud 自身特有的功能。
+
+### 1. 可扩展的 ownCloud “派”集群 ###
+
+由于 ownCloud 是开源的,你可以选择将它运行在自己的服务器中,或者从你信任的服务商那里获取空间——没必要将你的文件存储在那些大公司的服务器中,谁知他们将你的文件存储到哪里去。[点击此处查看部分 ownCloud 服务商][3],或者下载该服务软件到你的虚拟主机中[搭建自己的服务器][4].
+
+![](https://opensource.com/sites/default/files/images/life-uploads/banana-pi-owncloud-cluster.jpg)
+
+*拍摄: Jörn Friedrich Dreyer. [CC BY-SA 4.0.][5]*
+
+我们见过最具创意的事情就是架设[香蕉派集群][6]和[树莓派集群][7]。ownCloud 的扩展性通常用于支持成千上万的用户,但有些人则将它往不同方向发展,通过将多个微型系统集群在一起,就可以创建出运行速度超快的 ownCloud。酷毙了!
+
+### 2. 密码同步 ###
+
+为了让 ownCloud 更容易扩展,我们将它变得超级的模块化,甚至还有一个 [ownCloud 应用商店][8]。你可以在里边找到音乐和视频播放器、日历、联系人、生产力应用、游戏、应用模板(sketching app)等等。
+
+从近 200 多个应用中仅挑选一个是一件非常困难的事,但密码管理则是一个很独特的功能。只有不超过三个应用提供这个功能:[Passwords][9]、[Secure Container][10] 和 [Passman][11]。
+
+![](https://opensource.com/sites/default/files/images/life-uploads/password.png)
+
+### 3. 随心所欲地存储文件 ###
+
+外部存储可以让你将现有数据挂载到 ownCloud 上,让你通过一个界面来访问存储在 FTP、WebDAV、Amazon S3,甚至 Dropbox 和 Google Drive 的文件。
+
+注:youtube 视频
+<iframe width="520" height="315" frameborder="0" allowfullscreen="" src="https://www.youtube.com/embed/uezzFDRnoPY"></iframe>
+
+行业老大们喜欢创建自己的 “藩篱花园”,Box 的用户只能和其它的 Box 用户协作;假如你想从 Google Drive 分享你的文件,你的同伴也必须要有一个 Google 账号才可以访问的分享。通过 ownCloud 的外部存储功能,你可以轻松打破这些。
+
+最有创意的就是把 Google Drive 和 Dropbox 添加为外部存储。这样你就可以无缝连接它们,通过一个简单的链接即可分享给其它人——并不需要账户。
+
+### 4. 获取上传的文件 ###
+
+由于 ownCloud 是开源开,人们可以不受公司需求的制约而向它贡献感兴趣的功能。我们的贡献者总是很在意安全和隐私,所以 ownCloud 引入的通过密码保护公共链接并[设置失效期限][12]的功能要比其它人早很多。
+
+现在,ownCloud 可以配置分享链接的读写权限了,这就是说链接的访问者可以无缝的编辑你分享给他们的文件(可以有密码保护,也可以没有),或者将文件上传到服务器前不用强制他们提供私人信息来注册服务。
+
+注:youtube 视频
+<iframe width="520" height="315" frameborder="0" allowfullscreen="" src="https://www.youtube.com/embed/3GSppxEhmZY"></iframe>
+
+对于有人想给你分享大体积的文件时,这个特性就非常有用了。相比于上传到第三方站点、然后给你发送一个连接、你再去下载文件(通常需要登录),ownCloud 仅需要上传文件到你提供的分享文件夹,你就可以马上获取到文件了。
+
+### 5. 免费却又安全的存储空间 ###
+
+之前就强调过,我们的代码贡献者最关注的就是安全和隐私,这就是 ownCloud 中有用于加密和解密存储数据的应用的原因。
+
+通过使用 ownCloud 将你的文件存储到 Dropbox 或者 Google Drive,则会违背夺回数据的控制权并保持数据隐私的初衷。但是加密应用则可以改变这个状况。在发送数据给这些提供商前进行数据加密,并在取回数据的时候进行解密,你的数据就会变得很安全。
+
+### 6. 在你的可控范围内分享文件 ###
+
+作为开源项目,ownCloud 没有必要自建 “藩篱花园”。通过“联邦云共享(Federated Cloud Sharing)”:这个[由 ownCloud 开发和发布的][13]协议使不同的文件同步和共享服务器可以彼此之间进行通信,并能够安全地传输文件。联邦云共享本身来自一个有趣的事情:有 [22 所德国大学][14] 想要为自身的 50 万名学生建立一个庞大的云服务,但是每个大学都想控制自己学生的数据。于是乎,我们需要一个创造性的解决方案:也就是联邦云服务。该解决方案可以连接全部的大学,使得学生们可以无缝的协同工作。同时,每个大学的系统管理员保持着对自己学生创建的文件的控制权,并可采用自己的策略,如限制限额,或者限制什么人、什么文件以及如何共享。
+
+注:youtube 视频
+<iframe width="520" height="315" frameborder="0" allowfullscreen="" src="https://www.youtube.com/embed/9-JEmlH2DEg"></iframe>
+
+并且,这项神奇的技术并没有限制于德国的大学之间,每个 ownCloud 用户都能在自己的用户设置中找到自己的[联邦云 ID][15],并将之分享给同伴。
+
+现在你明白了吧。通过这六个方式,ownCloud 就能让人们做一些特殊而独特的事。而使这一切成为可能的,就是 ownCloud 是开源的,其设计目标就是让你的数据自由。
+
+你有其它的 ownCloud 的创意用法吗?请发表评论让我们知道。
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/life/15/12/6-creative-ways-use-owncloud
+
+作者:[Jos Poortvliet][a]
+译者:[GHLandy](https://github.com/GHLandy)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]:https://opensource.com/users/jospoortvliet
+[1]:https://owncloud.com/
+[2]:https://blogs.fsfe.org/mk/new-stickers-and-leaflets-no-cloud-and-e-mail-self-defense/
+[3]:https://owncloud.org/providers
+[4]:https://owncloud.org/install/#instructions-server
+[5]:https://creativecommons.org/licenses/by-sa/4.0/
+[6]:http://www.owncluster.de/
+[7]:https://christopherjcoleman.wordpress.com/2013/01/05/host-your-owncloud-on-a-raspberry-pi-cluster/
+[8]:https://apps.owncloud.com/
+[9]:https://apps.owncloud.com/content/show.php/Passwords?content=170480
+[10]:https://apps.owncloud.com/content/show.php/Secure+Container?content=167268
+[11]:https://apps.owncloud.com/content/show.php/Passman?content=166285
+[12]:https://owncloud.com/owncloud45-community/
+[13]:http://karlitschek.de/2015/08/announcing-the-draft-federated-cloud-sharing-api/
+[14]:https://owncloud.com/customer/sciebo/
+[15]:https://owncloud.org/federation/
diff --git a/published/201608/20151220 GCC-Inline-Assembly-HOWTO.md b/published/201608/20151220 GCC-Inline-Assembly-HOWTO.md
new file mode 100644
index 0000000000..e89e7fa256
--- /dev/null
+++ b/published/201608/20151220 GCC-Inline-Assembly-HOWTO.md	
@@ -0,0 +1,498 @@
+* * *
+
+#  GCC 内联汇编 HOWTO
+
+v0.1, 01 March 2003.
+* * *
+
+_本 HOWTO 文档将讲解 GCC 提供的内联汇编特性的用途和用法。对于阅读这篇文章,这里只有两个前提要求,很明显,就是 x86 汇编语言和 C 语言的基本认识。_
+
+* * *
+
+## 1. 简介
+
+### 1.1 版权许可
+
+Copyright (C)2003 Sandeep S.
+
+本文档自由共享;你可以重新发布它,并且/或者在遵循自由软件基金会发布的 GNU 通用公共许可证下修改它;也可以是该许可证的版本 2 或者(按照你的需求)更晚的版本。
+
+发布这篇文档是希望它能够帮助别人,但是没有任何担保;甚至不包括可售性和适用于任何特定目的的担保。关于更详细的信息,可以查看 GNU 通用许可证。
+
+### 1.2 反馈校正
+
+请将反馈和批评一起提交给 [Sandeep.S](mailto:busybox@sancharnet.in) 。我将感谢任何一个指出本文档中错误和不准确之处的人;一被告知,我会马上改正它们。
+
+### 1.3 致谢
+
+我对提供如此棒的特性的 GNU 人们表示真诚的感谢。感谢 Mr.Pramode C E 所做的所有帮助。感谢在 Govt Engineering College 和 Trichur 的朋友们的精神支持和合作,尤其是 Nisha Kurur 和 Sakeeb S 。 感谢在 Gvot Engineering College 和 Trichur 的老师们的合作。
+
+另外,感谢 Phillip ,  Brennan Underwood 和 colin@nyx.net ;这里的许多东西都厚颜地直接取自他们的工作成果。
+
+* * *
+
+## 2. 概览
+
+在这里,我们将学习 GCC 内联汇编。这里内联(inline)表示的是什么呢?
+
+我们可以要求编译器将一个函数的代码插入到调用者代码中函数被实际调用的地方。这样的函数就是内联函数。这听起来和宏差不多?这两者确实有相似之处。
+
+内联函数的优点是什么呢?
+
+这种内联方法可以减少函数调用开销。同时如果所有实参的值为常量,它们的已知值可以在编译期允许简化,因此并非所有的内联函数代码都需要被包含进去。代码大小的影响是不可预测的,这取决于特定的情况。为了声明一个内联函数,我们必须在函数声明中使用 `inline` 关键字。
+
+现在我们正处于一个猜测内联汇编到底是什么的点上。它只不过是一些写为内联函数的汇编程序。在系统编程上,它们方便、快速并且极其有用。我们主要集中学习(GCC)内联汇编函数的基本格式和用法。为了声明内联汇编函数,我们使用 `asm` 关键词。
+
+内联汇编之所以重要,主要是因为它可以操作并且使其输出通过 C 变量显示出来。正是因为此能力, "asm" 可以用作汇编指令和包含它的 C 程序之间的接口。 
+
+* * *
+
+## 3. GCC 汇编语法
+
+Linux上的 GNU C 编译器 GCC ,使用 **AT&T** / **UNIX** 汇编语法。在这里,我们将使用 AT&T 语法 进行汇编编码。如果你对 AT&T 语法不熟悉的话,请不要紧张,我会教你的。AT&T 语法和 Intel 语法的差别很大。我会给出主要的区别。
+
+1.  源操作数和目的操作数顺序 
+
+	AT&T 语法的操作数方向和 Intel 语法的刚好相反。在Intel 语法中,第一操作数为目的操作数,第二操作数为源操作数,然而在 AT&T 语法中,第一操作数为源操作数,第二操作数为目的操作数。也就是说,
+
+	Intel 语法中的 `Op-code dst src` 变为 
+	
+	AT&T 语法中的 `Op-code src dst`。
+
+2.  寄存器命名 
+
+	寄存器名称有 `%` 前缀,即如果必须使用 `eax`,它应该用作 `%eax`。
+
+3.  立即数
+
+    AT&T 立即数以 `$` 为前缀。静态 "C" 变量也使用 `$` 前缀。在 Intel 语法中,十六进制常量以 `h` 为后缀,然而 AT&T 不使用这种语法,这里我们给常量添加前缀 `0x`。所以,对于十六进制,我们首先看到一个 `$`,然后是 `0x`,最后才是常量。
+
+4.  操作数大小
+
+	在 AT&T 语法中,存储器操作数的大小取决于操作码名字的最后一个字符。操作码后缀 ’b’ 、’w’、’l’ 分别指明了字节(byte)(8位)、字(word)(16位)、长型(long)(32位)存储器引用。Intel 语法通过给存储器操作数添加 `byte ptr`、 `word ptr` 和 `dword ptr` 前缀来实现这一功能。
+
+	因此,Intel的 `mov al, byte ptr foo` 在 AT&T 语法中为 `movb foo, %al`。
+
+5.	存储器操作数
+	
+	在 Intel 语法中,基址寄存器包含在 `[` 和 `]` 中,然而在 AT&T 中,它们变为 `(` 和 `)`。另外,在 Intel 语法中, 间接内存引用为
+
+    `section:[base + index*scale + disp]`,在 AT&T中变为 `section:disp(base, index, scale)`。
+
+	需要牢记的一点是,当一个常量用于 disp 或 scale,不能添加 `$` 前缀。
+
+现在我们看到了 Intel 语法和 AT&T 语法之间的一些主要差别。我仅仅写了它们差别的一部分而已。关于更完整的信息,请参考 GNU 汇编文档。现在为了更好地理解,我们可以看一些示例。
+
+```
++------------------------------+------------------------------------+
+|       Intel Code             |      AT&T Code                     |
++------------------------------+------------------------------------+
+| mov     eax,1                |  movl    $1,%eax                   |   
+| mov     ebx,0ffh             |  movl    $0xff,%ebx                |   
+| int     80h                  |  int     $0x80                     |   
+| mov     ebx, eax             |  movl    %eax, %ebx                |
+| mov     eax,[ecx]            |  movl    (%ecx),%eax               |
+| mov     eax,[ebx+3]          |  movl    3(%ebx),%eax              | 
+| mov     eax,[ebx+20h]        |  movl    0x20(%ebx),%eax           |
+| add     eax,[ebx+ecx*2h]     |  addl    (%ebx,%ecx,0x2),%eax      |
+| lea     eax,[ebx+ecx]        |  leal    (%ebx,%ecx),%eax          |
+| sub     eax,[ebx+ecx*4h-20h] |  subl    -0x20(%ebx,%ecx,0x4),%eax |
++------------------------------+------------------------------------+
+```
+* * *
+
+## 4. 基本内联
+
+基本内联汇编的格式非常直接了当。它的基本格式为
+
+`asm("汇编代码");`
+
+示例
+
+```
+asm("movl %ecx %eax"); /* 将 ecx 寄存器的内容移至 eax  */
+__asm__("movb %bh (%eax)"); /* 将 bh 的一个字节数据 移至 eax 寄存器指向的内存 */
+```
+
+你可能注意到了这里我使用了 `asm ` 和 `__asm__`。这两者都是有效的。如果关键词 `asm` 和我们程序的一些标识符冲突了,我们可以使用 `__asm__`。如果我们的指令多于一条,我们可以每个一行,并用双引号圈起,同时为每条指令添加 ’\n’ 和 ’\t’ 后缀。这是因为 gcc 将每一条当作字符串发送给 **as**(GAS)(LCTT 译注: GAS 即 GNU 汇编器),并且通过使用换行符/制表符发送正确格式化后的行给汇编器。
+
+示例
+
+```
+__asm__ ("movl %eax, %ebx\n\t"
+         "movl $56, %esi\n\t"
+         "movl %ecx, $label(%edx,%ebx,$4)\n\t"
+         "movb %ah, (%ebx)");
+```
+
+如果在代码中,我们涉及到一些寄存器(即改变其内容),但在没有恢复这些变化的情况下从汇编中返回,这将会导致一些意想不到的事情。这是因为 GCC 并不知道寄存器内容的变化,这会导致问题,特别是当编译器做了某些优化。在没有告知 GCC 的情况下,它将会假设一些寄存器存储了一些值——而我们可能已经改变却没有告知 GCC——它会像什么事都没发生一样继续运行(LCTT 译注:什么事都没发生一样是指GCC不会假设寄存器装入的值是有效的,当退出改变了寄存器值的内联汇编后,寄存器的值不会保存到相应的变量或内存空间)。我们所可以做的是使用那些没有副作用的指令,或者当我们退出时恢复这些寄存器,要不就等着程序崩溃吧。这是为什么我们需要一些扩展功能,扩展汇编给我们提供了那些功能。
+
+* * *
+
+## 5. 扩展汇编
+
+在基本内联汇编中,我们只有指令。然而在扩展汇编中,我们可以同时指定操作数。它允许我们指定输入寄存器、输出寄存器以及修饰寄存器列表。GCC 不强制用户必须指定使用的寄存器。我们可以把头疼的事留给 GCC ,这可能可以更好地适应 GCC 的优化。不管怎么说,基本格式为:
+
+```
+asm ( 汇编程序模板 
+    : 输出操作数					/* 可选的 */
+    : 输入操作数                  /* 可选的 */
+    : 修饰寄存器列表			    /* 可选的 */
+    );
+```
+
+汇编程序模板由汇编指令组成。每一个操作数由一个操作数约束字符串所描述,其后紧接一个括弧括起的 C 表达式。冒号用于将汇编程序模板和第一个输出操作数分开,另一个(冒号)用于将最后一个输出操作数和第一个输入操作数分开(如果存在的话)。逗号用于分离每一个组内的操作数。总操作数的数目限制在 10 个,或者机器描述中的任何指令格式中的最大操作数数目,以较大者为准。
+
+如果没有输出操作数但存在输入操作数,你必须将两个连续的冒号放置于输出操作数原本会放置的地方周围。
+
+示例:
+
+```
+asm ("cld\n\t"
+     "rep\n\t"
+     "stosl"
+     : /* 无输出寄存器 */
+     : "c" (count), "a" (fill_value), "D" (dest)
+     : "%ecx", "%edi" 
+     );
+```
+
+现在来看看这段代码是干什么的?以上的内联汇编是将 `fill_value` 值连续 `count` 次拷贝到寄存器 `edi` 所指位置(LCTT 译注:每执行 stosl 一次,寄存器 edi 的值会递增或递减,这取决于是否设置了 direction 标志,因此以上代码实则初始化一个内存块)。 它也告诉 gcc 寄存器 `ecx` 和 `edi` 一直无效(LCTT 译注:原文为 eax ,但代码修饰寄存器列表中为 ecx,因此这可能为作者的纰漏。)。为了更加清晰地说明,让我们再看一个示例。
+
+```
+int a=10, b;
+asm ("movl %1, %%eax; 
+      movl %%eax, %0;"
+     :"=r"(b)        /* 输出 */
+     :"r"(a)         /* 输入 */
+     :"%eax"         /* 修饰寄存器 */
+     );       
+```
+
+这里我们所做的是使用汇编指令使 ’b’ 变量的值等于 ’a’ 变量的值。一些有意思的地方是:
+
+* "b" 为输出操作数,用 %0 引用,并且 "a" 为输入操作数,用 %1 引用。
+* "r" 为操作数约束。之后我们会更详细地了解约束(字符串)。目前,"r" 告诉 GCC 可以使用任一寄存器存储操作数。输出操作数约束应该有一个约束修饰符 "=" 。这修饰符表明它是一个只读的输出操作数。
+* 寄存器名字以两个 % 为前缀。这有利于 GCC 区分操作数和寄存器。操作数以一个 % 为前缀。
+* 第三个冒号之后的修饰寄存器 %eax 用于告诉 GCC %eax 的值将会在 "asm" 内部被修改,所以 GCC 将不会使用此寄存器存储任何其他值。
+
+当 “asm” 执行完毕, "b" 变量会映射到更新的值,因为它被指定为输出操作数。换句话说, “asm” 内 "b" 变量的修改应该会被映射到 “asm” 外部。
+
+现在,我们可以更详细地看看每一个域。
+
+### 5.1 汇编程序模板
+
+汇编程序模板包含了被插入到 C 程序的汇编指令集。其格式为:每条指令用双引号圈起,或者整个指令组用双引号圈起。同时每条指令应以分界符结尾。有效的分界符有换行符(`\n`)和分号(`;`)。`\n` 可以紧随一个制表符(`\t`)。我们应该都明白使用换行符或制表符的原因了吧(LCTT 译注:就是为了排版和分隔)?和 C 表达式对应的操作数使用 %0、%1 ... 等等表示。
+
+### 5.2 操作数
+
+C 表达式用作 “asm” 内的汇编指令操作数。每个操作数前面是以双引号圈起的操作数约束。对于输出操作数,在引号内还有一个约束修饰符,其后紧随一个用于表示操作数的 C 表达式。即,“操作数约束”(C 表达式)是一个通用格式。对于输出操作数,还有一个额外的修饰符。约束字符串主要用于决定操作数的寻址方式,同时也用于指定使用的寄存器。
+
+如果我们使用的操作数多于一个,那么每一个操作数用逗号隔开。
+
+在汇编程序模板中,每个操作数用数字引用。编号方式如下。如果总共有 n 个操作数(包括输入和输出操作数),那么第一个输出操作数编号为 0 ,逐项递增,并且最后一个输入操作数编号为 n - 1 。操作数的最大数目在前一节我们讲过。
+
+输出操作数表达式必须为左值。输入操作数的要求不像这样严格。它们可以为表达式。扩展汇编特性常常用于编译器所不知道的机器指令 ;-)。如果输出表达式无法直接寻址(即,它是一个位域),我们的约束字符串必须给定一个寄存器。在这种情况下,GCC 将会使用该寄存器作为汇编的输出,然后存储该寄存器的内容到输出。
+
+正如前面所陈述的一样,普通的输出操作数必须为只写的; GCC 将会假设指令前的操作数值是死的,并且不需要被(提前)生成。扩展汇编也支持输入-输出或者读-写操作数。
+
+所以现在我们来关注一些示例。我们想要求一个数的5次方结果。为了计算该值,我们使用 `lea` 指令。
+
+```
+asm ("leal (%1,%1,4), %0"
+     : "=r" (five_times_x)
+     : "r" (x) 
+     );
+```
+
+这里我们的输入为 x。我们不指定使用的寄存器。 GCC 将会选择一些输入寄存器,一个输出寄存器,来做我们预期的工作。如果我们想要输入和输出放在同一个寄存器里,我们也可以要求 GCC 这样做。这里我们使用那些读-写操作数类型。这里我们通过指定合适的约束来实现它。
+
+```
+asm ("leal (%0,%0,4), %0"
+     : "=r" (five_times_x)
+     : "0" (x) 
+     );
+```
+
+现在输出和输出操作数位于同一个寄存器。但是我们无法得知是哪一个寄存器。现在假如我们也想要指定操作数所在的寄存器,这里有一种方法。
+
+```
+asm ("leal (%%ecx,%%ecx,4), %%ecx"
+     : "=c" (x)
+     : "c" (x) 
+     );
+```
+
+在以上三个示例中,我们并没有在修饰寄存器列表里添加任何寄存器,为什么?在头两个示例, GCC 决定了寄存器并且它知道发生了什么改变。在最后一个示例,我们不必将 'ecx' 添加到修饰寄存器列表(LCTT 译注: 原文修饰寄存器列表这个单词拼写有错,这里已修正),gcc 知道它表示 x。因此,因为它可以知道 `ecx` 的值,它就不被当作修饰的(寄存器)了。
+
+### 5.3 修饰寄存器列表
+
+一些指令会破坏一些硬件寄存器内容。我们不得不在修饰寄存器中列出这些寄存器,即汇编函数内第三个 ’**:**’ 之后的域。这可以通知 gcc 我们将会自己使用和修改这些寄存器,这样 gcc 就不会假设存入这些寄存器的值是有效的。我们不用在这个列表里列出输入、输出寄存器。因为 gcc 知道 “asm” 使用了它们(因为它们被显式地指定为约束了)。如果指令隐式或显式地使用了任何其他寄存器,(并且寄存器没有出现在输出或者输出约束列表里),那么就需要在修饰寄存器列表中指定这些寄存器。
+
+如果我们的指令可以修改条件码寄存器(cc),我们必须将 "cc" 添加进修饰寄存器列表。
+
+如果我们的指令以不可预测的方式修改了内存,那么需要将 "memory" 添加进修饰寄存器列表。这可以使 GCC 不会在汇编指令间保持缓存于寄存器的内存值。如果被影响的内存不在汇编的输入或输出列表中,我们也必须添加 **volatile** 关键词。
+
+我们可以按我们的需求多次读写修饰寄存器。参考一下模板内的多指令示例;它假设子例程 _foo 接受寄存器 `eax` 和 `ecx` 里的参数。
+
+```
+asm ("movl %0,%%eax;
+      movl %1,%%ecx;
+      call _foo"
+     : /* no outputs */
+     : "g" (from), "g" (to)
+     : "eax", "ecx"
+     );
+```
+
+### 5.4 Volatile ...?
+
+如果你熟悉内核源码或者类似漂亮的代码,你一定见过许多声明为 `volatile` 或者 `__volatile__`的函数,其跟着一个 `asm` 或者 `__asm__`。我之前提到过关键词 `asm` 和 `__asm__`。那么什么是 `volatile` 呢?
+
+如果我们的汇编语句必须在我们放置它的地方执行(例如,不能为了优化而被移出循环语句),将关键词 `volatile` 放置在 asm 后面、()的前面。以防止它被移动、删除或者其他操作,我们将其声明为 `asm volatile ( ... : ... : ... : ...);`
+
+如果担心发生冲突,请使用 `__volatile__`。
+
+如果我们的汇编只是用于一些计算并且没有任何副作用,不使用 `volatile` 关键词会更好。不使用 `volatile` 可以帮助 gcc 优化代码并使代码更漂亮。
+
+
+在“一些实用的诀窍”一节中,我提供了多个内联汇编函数的例子。那里我们可以了解到修饰寄存器列表的细节。
+
+* * *
+
+## 6. 更多关于约束
+
+到这个时候,你可能已经了解到约束和内联汇编有很大的关联。但我们对约束讲的还不多。约束用于表明一个操作数是否可以位于寄存器和位于哪种寄存器;操作数是否可以为一个内存引用和哪种地址;操作数是否可以为一个立即数和它可能的取值范围(即值的范围),等等。
+
+### 6.1 常用约束
+
+在许多约束中,只有小部分是常用的。我们来看看这些约束。
+
+1.  **寄存器操作数约束(r)**
+
+	当使用这种约束指定操作数时,它们存储在通用寄存器(GPR)中。请看下面示例:
+
+    `asm ("movl %%eax, %0\n" :"=r"(myval));`
+
+	这里,变量 myval 保存在寄存器中,寄存器 eax 的值被复制到该寄存器中,并且 myval 的值从寄存器更新到了内存。当指定 "r" 约束时, gcc 可以将变量保存在任何可用的 GPR 中。要指定寄存器,你必须使用特定寄存器约束直接地指定寄存器的名字。它们为:
+
+	```
+    +---+--------------------+
+    | r |    Register(s)     |
+    +---+--------------------+
+    | a |   %eax, %ax, %al   |
+    | b |   %ebx, %bx, %bl   |
+    | c |   %ecx, %cx, %cl   |
+    | d |   %edx, %dx, %dl   |
+    | S |   %esi, %si        |
+    | D |   %edi, %di        |
+    +---+--------------------+
+    ```
+
+2.	**内存操作数约束(m)**
+
+	当操作数位于内存时,任何对它们的操作将直接发生在内存位置,这与寄存器约束相反,后者首先将值存储在要修改的寄存器中,然后将它写回到内存位置。但寄存器约束通常用于一个指令必须使用它们或者它们可以大大提高处理速度的地方。当需要在 “asm” 内更新一个 C 变量,而又不想使用寄存器去保存它的值,使用内存最为有效。例如,IDTR 寄存器的值存储于内存位置 loc 处:
+
+    `asm("sidt %0\n" : :"m"(loc));`
+
+3.	**匹配(数字)约束**
+
+	在某些情况下,一个变量可能既充当输入操作数,也充当输出操作数。可以通过使用匹配约束在 "asm" 中指定这种情况。
+
+    `asm ("incl %0" :"=a"(var):"0"(var));`
+
+	在操作数那一节中,我们也看到了一些类似的示例。在这个匹配约束的示例中,寄存器 "%eax" 既用作输入变量,也用作输出变量。 var 输入被读进 %eax,并且等递增后更新的 %eax 再次被存储进 var。这里的 "0" 用于指定与第 0 个输出变量相同的约束。也就是,它指定 var 输出实例应只被存储在 "%eax" 中。该约束可用于:
+	  - 在输入从变量读取或变量修改后且修改被写回同一变量的情况
+	  - 在不需要将输入操作数实例和输出操作数实例分开的情况
+	  
+	使用匹配约束最重要的意义在于它们可以有效地使用可用寄存器。
+
+其他一些约束:
+
+1. "m" : 允许一个内存操作数,可以使用机器普遍支持的任一种地址。
+2. "o" : 允许一个内存操作数,但只有当地址是可偏移的。即,该地址加上一个小的偏移量可以得到一个有效地址。
+3. "V" : 一个不允许偏移的内存操作数。换言之,任何适合 "m" 约束而不适合 "o" 约束的操作数。
+4. "i" : 允许一个(带有常量)的立即整形操作数。这包括其值仅在汇编时期知道的符号常量。
+5. "n" : 允许一个带有已知数字的立即整形操作数。许多系统不支持汇编时期的常量,因为操作数少于一个字宽。对于此种操作数,约束应该使用 'n' 而不是'i'。
+6. "g" : 允许任一寄存器、内存或者立即整形操作数,不包括通用寄存器之外的寄存器。
+
+以下约束为 x86 特有。
+
+1. "r" : 寄存器操作数约束,查看上面给定的表格。
+2. "q" : 寄存器 a、b、c 或者 d。
+3. "I" : 范围从 0 到 31 的常量(对于 32 位移位)。
+4. "J" : 范围从 0 到 63 的常量(对于 64 位移位)。
+5. "K" : 0xff。
+6. "L" : 0xffff。
+7. "M" : 0、1、2 或 3 (lea 指令的移位)。
+8. "N" : 范围从 0 到 255 的常量(对于 out 指令)。
+9. "f" : 浮点寄存器
+10. "t" : 第一个(栈顶)浮点寄存器
+11. "u" : 第二个浮点寄存器
+12. "A" : 指定 `a` 或 `d` 寄存器。这主要用于想要返回 64 位整形数,使用 `d` 寄存器保存最高有效位和 `a` 寄存器保存最低有效位。
+
+### 6.2 约束修饰符
+
+当使用约束时,对于更精确的控制超过了对约束作用的需求,GCC 给我们提供了约束修饰符。最常用的约束修饰符为:
+
+1. "=" : 意味着对于这条指令,操作数为只写的;旧值会被忽略并被输出数据所替换。
+2. "&" : 意味着这个操作数为一个早期改动的操作数,其在该指令完成前通过使用输入操作数被修改了。因此,这个操作数不可以位于一个被用作输出操作数或任何内存地址部分的寄存器。如果在旧值被写入之前它仅用作输入而已,一个输入操作数可以为一个早期改动操作数。
+
+上述的约束列表和解释并不完整。示例可以让我们对内联汇编的用途和用法更好的理解。在下一节,我们会看到一些示例,在那里我们会发现更多关于修饰寄存器列表的东西。
+
+* * *
+
+## 7. 一些实用的诀窍
+
+现在我们已经介绍了关于 GCC 内联汇编的基础理论,现在我们将专注于一些简单的例子。将内联汇编函数写成宏的形式总是非常方便的。我们可以在 Linux 内核代码里看到许多汇编函数。(usr/src/linux/include/asm/*.h)。
+
+1. 首先我们从一个简单的例子入手。我们将写一个两个数相加的程序。
+
+	```
+    int main(void)
+    {
+            int foo = 10, bar = 15;
+            __asm__ __volatile__("addl  %%ebx,%%eax"
+                                 :"=a"(foo)
+                                 :"a"(foo), "b"(bar)
+                                 );
+            printf("foo+bar=%d\n", foo);
+            return 0;
+    }
+    ```
+
+    这里我们要求 GCC 将 foo 存放于 %eax,将 bar 存放于 %ebx,同时我们也想要在 %eax 中存放结果。'=' 符号表示它是一个输出寄存器。现在我们可以以其他方式将一个整数加到一个变量。
+    
+    ```
+    __asm__ __volatile__(
+                         "   lock       ;\n"
+                         "   addl %1,%0 ;\n"
+                         : "=m"  (my_var)
+                         : "ir"  (my_int), "m" (my_var)
+                         :                                 /* 无修饰寄存器列表 */
+                         );
+    ```
+
+    这是一个原子加法。为了移除原子性,我们可以移除指令 'lock'。在输出域中,"=m" 表明 my_var 是一个输出且位于内存。类似地,"ir" 表明 my_int 是一个整型,并应该存在于其他寄存器(回想我们上面看到的表格)。没有寄存器位于修饰寄存器列表中。
+
+2.  现在我们将在一些寄存器/变量上展示一些操作,并比较值。
+
+	```
+    __asm__ __volatile__(  "decl %0; sete %1"
+                         : "=m" (my_var), "=q" (cond)
+                         : "m" (my_var) 
+                         : "memory"
+                         );
+    ```
+
+    这里,my_var 的值减 1 ,并且如果结果的值为 0,则变量 cond 置 1。我们可以通过将指令 "lock;\n\t" 添加为汇编模板的第一条指令以增加原子性。
+
+    以类似的方式,为了增加 my_var,我们可以使用 "incl %0" 而不是 "decl %0"。
+
+    这里需要注意的地方是(i)my_var 是一个存储于内存的变量。(ii)cond 位于寄存器 eax、ebx、ecx、edx 中的任何一个。约束 "=q" 保证了这一点。(iii)同时我们可以看到 memory 位于修饰寄存器列表中。也就是说,代码将改变内存中的内容。
+
+3.  如何置 1 或清 0 寄存器中的一个比特位。作为下一个诀窍,我们将会看到它。
+
+    ```
+    __asm__ __volatile__(   "btsl %1,%0"
+                          : "=m" (ADDR)
+                          : "Ir" (pos)
+                          : "cc"
+                          );
+    ```
+
+    这里,ADDR 变量(一个内存变量)的 'pos' 位置上的比特被设置为 1。我们可以使用 'btrl' 来清除由 'btsl' 设置的比特位。pos 的约束 "Ir" 表明 pos 位于寄存器,并且它的值为 0-31(x86 相关约束)。也就是说,我们可以设置/清除 ADDR 变量上第 0 到 31 位的任一比特位。因为条件码会被改变,所以我们将 "cc" 添加进修饰寄存器列表。
+
+4.  现在我们看看一些更为复杂而有用的函数。字符串拷贝。
+
+    ```
+    static inline char * strcpy(char * dest,const char *src)
+    {
+    int d0, d1, d2;
+    __asm__ __volatile__(  "1:\tlodsb\n\t"
+                           "stosb\n\t"
+                           "testb %%al,%%al\n\t"
+                           "jne 1b"
+                         : "=&S" (d0), "=&D" (d1), "=&a" (d2)
+                         : "0" (src),"1" (dest) 
+                         : "memory");
+    return dest;
+    }
+    ```
+
+    源地址存放于 esi,目标地址存放于 edi,同时开始拷贝,当我们到达 **0** 时,拷贝完成。约束 "&S"、"&D"、"&a" 表明寄存器 esi、edi 和 eax 早期修饰寄存器,也就是说,它们的内容在函数完成前会被改变。这里很明显可以知道为什么 "memory" 会放在修饰寄存器列表。
+
+    我们可以看到一个类似的函数,它能移动双字块数据。注意函数被声明为一个宏。
+
+	```
+    #define mov_blk(src, dest, numwords) \
+    __asm__ __volatile__ (                                          \
+                           "cld\n\t"                                \
+                           "rep\n\t"                                \
+                           "movsl"                                  \
+                           :                                        \
+                           : "S" (src), "D" (dest), "c" (numwords)  \
+                           : "%ecx", "%esi", "%edi"                 \
+                           )
+    ```
+
+    这里我们没有输出,寄存器 ecx、esi和 edi 的内容发生了改变,这是块移动的副作用。因此我们必须将它们添加进修饰寄存器列表。
+
+5.  在 Linux 中,系统调用使用 GCC 内联汇编实现。让我们看看如何实现一个系统调用。所有的系统调用被写成宏(linux/unistd.h)。例如,带有三个参数的系统调用被定义为如下所示的宏。
+
+    ```
+    type name(type1 arg1,type2 arg2,type3 arg3) \
+    { \
+    long __res; \
+    __asm__ volatile (  "int $0x80" \
+                      : "=a" (__res) \
+                      : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
+                        "d" ((long)(arg3))); \
+    __syscall_return(type,__res); \
+    }
+    ```
+
+    无论何时调用带有三个参数的系统调用,以上展示的宏就会用于执行调用。系统调用号位于 eax 中,每个参数位于 ebx、ecx、edx 中。最后 "int 0x80" 是一条用于执行系统调用的指令。返回值被存储于 eax 中。
+
+    每个系统调用都以类似的方式实现。Exit 是一个单一参数的系统调用,让我们看看它的代码看起来会是怎样。它如下所示。
+    
+    ```
+    {
+            asm("movl $1,%%eax;         /* SYS_exit is 1 */
+                 xorl %%ebx,%%ebx;      /* Argument is in ebx, it is 0 */
+                 int  $0x80"            /* Enter kernel mode */
+                );
+    }
+    ```
+
+    Exit 的系统调用号是 1,同时它的参数是 0。因此我们分配 eax 包含 1,ebx 包含 0,同时通过 `int $0x80` 执行 `exit(0)`。这就是 exit 的工作原理。
+
+* * *
+
+## 8. 结束语
+
+这篇文档已经将 GCC 内联汇编过了一遍。一旦你理解了基本概念,你就可以按照自己的需求去使用它们了。我们看了许多例子,它们有助于理解 GCC 内联汇编的常用特性。
+
+GCC 内联是一个极大的主题,这篇文章是不完整的。更多关于我们讨论过的语法细节可以在 GNU 汇编器的官方文档上获取。类似地,要获取完整的约束列表,可以参考 GCC 的官方文档。
+
+当然,Linux 内核大量地使用了 GCC 内联。因此我们可以在内核源码中发现许多各种各样的例子。它们可以帮助我们很多。
+
+如果你发现任何的错别字,或者本文中的信息已经过时,请告诉我们。
+
+* * *
+
+## 9. 参考
+
+1.  [Brennan’s Guide to Inline Assembly](http://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html)
+2.  [Using Assembly Language in Linux](http://linuxassembly.org/articles/linasm.html)
+3.  [Using as, The GNU Assembler](http://www.gnu.org/manual/gas-2.9.1/html_mono/as.html)
+4.  [Using and Porting the GNU Compiler Collection (GCC)](http://gcc.gnu.org/onlinedocs/gcc_toc.html)
+5.  [Linux Kernel Source](http://ftp.kernel.org/)
+
+* * *
+
+via: http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
+
+  作者:[Sandeep.S](mailto:busybox@sancharnet.in) 译者:[cposture](https://github.com/cposture) 校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](http://linux.cn/) 荣誉推出
diff --git a/published/201608/20160306 5 Favorite Open Source Django Packages.md b/published/201608/20160306 5 Favorite Open Source Django Packages.md
new file mode 100644
index 0000000000..8c73c702e1
--- /dev/null
+++ b/published/201608/20160306 5 Favorite Open Source Django Packages.md	
@@ -0,0 +1,161 @@
+5 个最受人喜爱的开源 Django 包
+================================================================================
+
+![Yearbook cover 2015](https://opensource.com/sites/default/files/styles/image-full-size/public/u23316/osdc-open-source-yearbook-lead8.png?itok=0_5-hdFE)
+
+图片来源:Opensource.com
+
+_Jacob Kaplan-Moss 和 Frank Wiles 也参与了本文的写作。_
+
+Django 围绕“[可重用应用][1]”的思想建立:自包含的包提供了可重复使用的特性。你可以将这些可重用应用组装起来,在加上适用于你的网站的特定代码,来搭建你自己的网站。Django 具有一个丰富多样的、由可供你使用的可重用应用组建起来的生态系统——PyPI 列出了[超过 8000个 Django 应用][2]——可你该如何知道哪些是最好的呢?
+
+为了节省你的时间,我们总结了五个最受喜爱的 Django 应用。它们是:
+
+- [Cookiecutter][3]: 建立 Django 网站的最佳方式。
+- [Whitenoise][4]: 最棒的静态资源服务器。
+- [Django Rest Framework][5]: 使用 Django 开发 REST API 的最佳方式。
+- [Wagtail][6]: 基于 Django 的最佳内容管理系统(CMS)。
+- [django-allauth][7]: 提供社交账户登录的最佳应用(如 Twitter, Facebook, GitHub 等)。
+
+我们同样推荐你看看 [Django Packages][8],这是一个可重用 Django 应用的目录。Django Packages 将 Django 应用组织成“表格”,你可以在功能相似的不同应用之间进行比较并做出选择。你可以查看每个包中提供的特性和使用统计情况。(比如:这是[ REST 工具的表格][9],也许可以帮助你理解我们为何推荐 Django REST Framework。
+
+### 为什么你应该相信我们?
+
+我们使用 Django 的时间几乎比任何人都长。在 Django 发布之前,我们当中的两个人(Frank 和 Jacob)就在 [Lawrence Journal-World][10] (Django 的发源地)工作(事实上,是他们两人推动了 Django 开源发布的进程)。我们在过去的八年当中运行着一个咨询公司,来建议公司怎样最好地应用 Django。
+
+所以,我们见证了 Django 项目和社群的完整历史,我们见证了那些流行的软件包的兴起和没落。在我们三个之中,我们个人可能试用了 8000 个应用中至少一半以上,或者我们知道谁试用过这些。我们对如何使应用变得坚实可靠有着深刻的理解,并且我们对给予这些应用持久力量的来源也有着深入的了解。
+
+### 建立 Django 网站的最佳方式:[Cookiecutter][3]
+
+建立一个新项目或应用总是有些痛苦。你可以用 Django 内建的 `startproject`。不过,如果你像我们一样,对如何做事比较挑剔。Cookiecutter 为你提供了一个快捷简单的方式来构建项目或易于重用的应用模板,从而解决了这个问题。一个简单的例子:键入 `pip install cookiecutter`,然后在命令行中运行以下命令:
+
+```bash
+$ cookiecutter https://github.com/marcofucci/cookiecutter-simple-django
+```
+
+接下来你需要回答几个简单的问题,比如你的项目名称、目录(repo)、作者名字、E-Mail 和其他几个关于配置的小问题。这些能够帮你补充项目相关的细节。我们使用最最原始的 “_foo_” 作为我们的目录名称。所以 cokkiecutter 在子目录 “_foo_” 下建立了一个简单的 Django 项目。
+
+如果你在 “_foo_” 项目中闲逛,你会看见你刚刚选择的其它设置已通过模板,连同所需的子目录一同嵌入到文件当中。这个“模板”在我们刚刚在执行 `cookiecutter` 命令时输入的唯一一个参数 Github 仓库 URL 中定义。这个样例工程使用了一个 Github 远程仓库作为模板;不过你也可以使用本地的模板,这在建立非重用项目时非常有用。
+
+我们认为 cookiecutter 是一个极棒的 Django 包,但是,事实上其实它在面对纯 Python 甚至非 Python 相关需求时也极为有用。你能够将所有文件以一种可重复的方式精确地摆放在任何位置上,使得 cookiecutter 成为了一个简化(DRY)工作流程的极佳工具。
+
+### 最棒的静态资源服务器:[Whitenoise][4]
+
+多年来,托管网站的静态资源——图片、Javascript、CSS——都是一件很痛苦的事情。Django 内建的 [django.views.static.serve][11] 视图,就像 Django 文章所述的那样,“在生产环境中不可靠,所以只应为开发环境的提供辅助功能。”但使用一个“真正的” Web 服务器,如 NGINX 或者借助 CDN 来托管媒体资源,配置起来会比较困难。
+
+Whitenoice 很简洁地解决了这个问题。它可以像在开发环境那样轻易地在生产环境中设置静态服务器,并且针对生产环境进行了加固和优化。它的设置方法极为简单:
+
+1. 确保你在使用 Django 的 [contrib.staticfiles][12] 应用,并确认你在配置文件中正确设置了 `STATIC_ROOT` 变量。
+
+2. 在 `wsgi.py` 文件中启用 Whitenoise:
+
+   ```python
+   from django.core.wsgi import get_wsgi_application
+   from whitenoise.django import DjangoWhiteNoise
+
+   application = get_wsgi_application()
+   application = DjangoWhiteNoise(application)
+   ```
+
+配置它真的就这么简单!对于大型应用,你可能想要使用一个专用的媒体服务器和/或一个 CDN,但对于大多数小型或中型 Django 网站,Whitenoise 已经足够强大。
+
+如需查看更多关于 Whitenoise 的信息,[请查看文档][13]。
+
+### 开发 REST API 的最佳工具:[Django REST Framework][5]
+
+REST API 正在迅速成为现代 Web 应用的标准功能。 API 就是简单的使用 JSON 对话而不是 HTML,当然你可以只用 Django 做到这些。你可以制作自己的视图,设置合适的 `Content-Type`,然后返回 JSON 而不是渲染后的 HTML 响应。这是在像 [Django Rest Framework][14](下称 DRF)这样的 API 框架发布之前,大多数人所做的。
+
+如果你对 Django 的视图类很熟悉,你会觉得使用 DRF 构建 REST API 与使用它们很相似,不过 DRF 只针对特定 API 使用场景而设计。一般的 API 设置只需要一点代码,所以我们没有提供一份让你兴奋的示例代码,而是强调了一些可以让你生活的更舒适的 DRF 特性:
+
+* 可自动预览的 API 可以使你的开发和人工测试轻而易举。你可以查看 DRF 的[示例代码][15]。你可以查看 API 响应,并且不需要你做任何事就可以支持 POST/PUT/DELETE 类型的操作。
+* 便于集成各种认证方式,如 OAuth, Basic Auth, 或API Tokens。
+* 内建请求速率限制。
+* 当与 [django-rest-swagger][16] 组合使用时,API 文档几乎可以自动生成。
+* 广泛的第三方库生态。
+
+当然,你可以不依赖 DRF 来构建 API,但我们无法想象你不去使用 DRF 的原因。就算你不使用 DRF 的全部特性,使用一个成熟的视图库来构建你自己的 API 也会使你的 API 更加一致、完全,更能提高你的开发速度。如果你还没有开始使用 DRF, 你应该找点时间去体验一下。
+
+### 基于 Django 的最佳 CMS:[Wagtail][6]
+
+Wagtail 是当下 Django CMS(内容管理系统)世界中最受人青睐的应用,并且它的热门有足够的理由。就像大多数的 CMS 一样,它具有极佳的灵活性,可以通过简单的 Django 模型来定义不同类型的页面及其内容。使用它,你可以从零开始在几个小时而不是几天之内来和建造一个基本可以运行的内容管理系统。举一个小例子,为你公司的员工定义一个员工页面类型可以像下面一样简单:
+
+```python
+from wagtail.wagtailcore.models import Page
+from wagtail.wagtailcore.fields import RichTextField
+from wagtail.wagtailadmin.edit_handlers import FieldPanel, MultiFieldPanel
+from wagtail.wagtailimages.edit_handlers import ImageChooserPanel 
+
+class StaffPage(Page):
+    name = models.CharField(max_length=100)
+    hire_date = models.DateField()
+    bio = models.RichTextField()
+    email = models.EmailField()
+    headshot = models.ForeignKey('wagtailimages.Image', null=True, blank=True) 
+    content_panels = Page.content_panels + [
+                                FieldPanel('name'),
+                                FieldPanel('hire_date'),
+                                FieldPanel('email'),
+                                FieldPanel('bio',classname="full"),
+                                ImageChoosePanel('headshot'),
+                                ] 
+```
+
+然而,Wagtail 真正出彩的地方在于它的灵活性及其易于使用的现代化管理页面。你可以控制不同类型的页面在哪网站的哪些区域可以访问,为页面添加复杂的附加逻辑,还天生就支持标准的适应/审批工作流。在大多数 CMS 系统中,你会在开发时在某些点上遇到困难。而使用 Wagtail 时,我们经过不懈努力找到了一个突破口,使得让我们轻易地开发出一套简洁稳定的系统,使得程序完全依照我们的想法运行。如果你对此感兴趣,我们写了一篇[深入理解 Wagtail][17。
+
+### 提供社交账户登录的最佳工具:[django-allauth][7]
+
+django-allauth 是一个能够解决你的注册和认证需求的、可重用的 Django 应用。无论你需要构建本地注册系统还是社交账户注册系统,django-allauth 都能够帮你做到。
+
+这个应用支持多种认证体系,比如用户名或电子邮件。一旦用户注册成功,它还可以提供从无需认证到电子邮件认证的多种账户验证的策略。同时,它也支持多种社交账户和电子邮件账户。它还支持插拔式注册表单,可让用户在注册时回答一些附加问题。
+
+django-allauth 支持多于 20 种认证提供者,包括 Facebook、Github、Google 和 Twitter。如果你发现了一个它不支持的社交网站,很有可能通过第三方插件提供该网站的接入支持。这个项目还支持自定义后端,可以支持自定义的认证方式,对每个有定制认证需求的人来说这都很棒。
+
+django-allauth 易于配置,且有[完善的文档][18]。该项目通过了很多测试,所以你可以相信它的所有部件都会正常运作。
+
+你有最喜爱的 Django 包吗?请在评论中告诉我们。
+
+### 关于作者
+
+![Photo](https://opensource.com/sites/default/files/styles/profile_pictures/public/pictures/main-one-i-use-everywhere.png?itok=66GC-D1q)
+
+*Jeff Triplett 劳伦斯,堪萨斯州 <http://www.jefftriplett.com/>*
+
+我在 2007 年搬到了堪萨斯州的劳伦斯,在 Django 的发源地—— Lawrence Journal-World 工作。我现在在劳伦斯市的 [Revolution Systems (Revsys)][19] 工作,做一位开发者兼顾问。
+
+我是[北美 Django 运动基金会(DEFNA)][20]的联合创始人,2015 和 2016 年 [DjangoCon US][21] 的会议主席,而且我在 Django 的发源地劳伦斯参与组织了 [Django Birthday][22] 来庆祝 Django 的 10 岁生日。
+
+我是当地越野跑小组的成员,我喜欢篮球,我还喜欢梦见自己随着一道气流游遍美国。
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/business/15/12/5-favorite-open-source-django-packages
+
+作者:[Jeff Triplett][a]
+译者:[StdioA](https://github.com/StdioA)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]:https://opensource.com/users/jefftriplett
+[1]:https://docs.djangoproject.com/en/1.8/intro/reusable-apps/
+[2]:https://pypi.python.org/pypi?:action=browse&c=523
+[3]:https://github.com/audreyr/cookiecutter
+[4]:http://whitenoise.evans.io/en/latest/base.html
+[5]:http://www.django-rest-framework.org/
+[6]:https://wagtail.io/
+[7]:http://www.intenct.nl/projects/django-allauth/
+[8]:https://www.djangopackages.com/
+[9]:https://www.djangopackages.com/grids/g/rest/
+[10]:http://www2.ljworld.com/news/2015/jul/09/happy-birthday-django/
+[11]:https://docs.djangoproject.com/en/1.8/ref/views/#django.views.static.serve
+[12]:https://docs.djangoproject.com/en/1.8/ref/contrib/staticfiles/
+[13]:http://whitenoise.evans.io/en/latest/index.html
+[14]:http://www.django-rest-framework.org/
+[15]:http://restframework.herokuapp.com/
+[16]:http://django-rest-swagger.readthedocs.org/en/latest/index.html
+[17]:https://opensource.com/business/15/5/wagtail-cms
+[18]:http://django-allauth.readthedocs.org/en/latest/
+[19]:http://www.revsys.com/
+[20]:http://defna.org/
+[21]:https://2015.djangocon.us/
+[22]:https://djangobirthday.com/
diff --git a/published/201608/20160309 Let’s Build A Web Server. Part 1.md b/published/201608/20160309 Let’s Build A Web Server. Part 1.md
new file mode 100644
index 0000000000..d55d5720d2
--- /dev/null
+++ b/published/201608/20160309 Let’s Build A Web Server. Part 1.md	
@@ -0,0 +1,150 @@
+搭个 Web 服务器(一)
+=====================================
+
+一天,有一个正在散步的妇人恰好路过一个建筑工地,看到三个正在工作的工人。她问第一个人:“你在做什么?”第一个人没好气地喊道:“你没看到我在砌砖吗?”妇人对这个答案不满意,于是问第二个人:“你在做什么?”第二个人回答说:“我在建一堵砖墙。”说完,他转向第一个人,跟他说:“嗨,你把墙砌过头了。去把刚刚那块砖弄下来!”然而,妇人对这个答案依然不满意,于是又问了第三个人相同的问题。第三个人仰头看着天,对她说:“我在建造世界上最大的教堂。”当他回答时,第一个人和第二个人在为刚刚砌错的砖而争吵。他转向那两个人,说:“不用管那块砖了。这堵墙在室内,它会被水泥填平,没人会看见它的。去砌下一层吧。”
+
+这个故事告诉我们:如果你能够理解整个系统的构造,了解系统的各个部件如何相互结合(如砖、墙还有整个教堂),你就能够更快地定位及修复问题(那块砌错的砖)。
+
+如果你想从头开始创造一个 Web 服务器,那么你需要做些什么呢?
+
+我相信,如果你想成为一个更好的开发者,你**必须**对日常使用的软件系统的内部结构有更深的理解,包括编程语言、编译器与解释器、数据库及操作系统、Web 服务器及 Web 框架。而且,为了更好更深入地理解这些系统,你**必须**从头开始,用一砖一瓦来重新构建这个系统。
+
+荀子曾经用这几句话来表达这种思想:
+
+>“不闻不若闻之。(I hear and I forget.)”
+
+![](https://ruslanspivak.com/lsbasi-part4/LSBAWS_confucius_hear.png)
+
+>“闻之不若见之。(I see and I remember.)”
+
+![](https://ruslanspivak.com/lsbasi-part4/LSBAWS_confucius_see.png)
+
+>“知之不若行之。(I do and I understand.)”
+
+![](https://ruslanspivak.com/lsbasi-part4/LSBAWS_confucius_do.png)
+
+我希望你现在能够意识到,重新建造一个软件系统来了解它的工作方式是一个好主意。
+
+在这个由三篇文章组成的系列中,我将会教你构建你自己的 Web 服务器。我们开始吧~
+
+先说首要问题:Web 服务器是什么?
+
+![](https://ruslanspivak.com/lsbaws-part1/LSBAWS_HTTP_request_response.png)
+
+简而言之,它是一个运行在一个物理服务器上的网络服务器(啊呀,服务器套服务器),等待客户端向其发送请求。当它接收请求后,会生成一个响应,并回送至客户端。客户端和服务端之间通过 HTTP 协议来实现相互交流。客户端可以是你的浏览器,也可以是使用 HTTP 协议的其它任何软件。
+
+最简单的 Web 服务器实现应该是什么样的呢?这里我给出我的实现。这个例子由 Python 写成,即使你没听说过 Python(它是一门超级容易上手的语言,快去试试看!),你也应该能够从代码及注释中理解其中的理念:
+
+```
+import socket
+
+HOST, PORT = '', 8888
+
+listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+listen_socket.bind((HOST, PORT))
+listen_socket.listen(1)
+print 'Serving HTTP on port %s ...' % PORT
+while True:
+    client_connection, client_address = listen_socket.accept()
+    request = client_connection.recv(1024)
+    print request
+
+    http_response = """\
+HTTP/1.1 200 OK
+
+Hello, World!
+"""
+    client_connection.sendall(http_response)
+    client_connection.close()
+```
+
+将以上代码保存为 webserver1.py,或者直接从 [GitHub][1] 上下载这个文件。然后,在命令行中运行这个程序。像这样:
+
+```
+$ python webserver1.py
+Serving HTTP on port 8888 …
+```
+
+现在,在你的网页浏览器的地址栏中输入 URL:http://localhost:8888/hello ,敲一下回车,然后来见证奇迹。你应该看到“Hello, World!”显示在你的浏览器中,就像下图那样:
+
+![](https://ruslanspivak.com/lsbaws-part1/browser_hello_world.png)
+
+说真的,快去试一试。你做实验的时候,我会等着你的。
+
+完成了?不错!现在我们来讨论一下它实际上是怎么工作的。
+
+首先我们从你刚刚输入的 Web 地址开始。它叫 [URL][2],这是它的基本结构:
+
+![](https://ruslanspivak.com/lsbaws-part1/LSBAWS_URL_Web_address.png)
+
+URL 是一个 Web 服务器的地址,浏览器用这个地址来寻找并连接 Web 服务器,并将上面的内容返回给你。在你的浏览器能够发送 HTTP 请求之前,它需要与 Web 服务器建立一个 TCP 连接。然后会在 TCP 连接中发送 HTTP 请求,并等待服务器返回 HTTP 响应。当你的浏览器收到响应后,就会显示其内容,在上面的例子中,它显示了“Hello, World!”。
+
+我们来进一步探索在发送 HTTP 请求之前,客户端与服务器建立 TCP 连接的过程。为了建立链接,它们使用了所谓“套接字(socket)”。我们现在不直接使用浏览器发送请求,而在命令行中使用 `telnet` 来人工模拟这个过程。
+
+在你运行 Web 服务器的电脑上,在命令行中建立一个 telnet 会话,指定一个本地域名,使用端口 8888,然后按下回车:
+
+```
+$ telnet localhost 8888
+Trying 127.0.0.1 …
+Connected to localhost.
+```
+
+这个时候,你已经与运行在你本地主机的服务器建立了一个 TCP 连接。在下图中,你可以看到一个服务器从头开始,到能够建立 TCP 连接的基本过程。
+
+![](https://ruslanspivak.com/lsbaws-part1/LSBAWS_socket.png)
+
+在同一个 telnet 会话中,输入 `GET /hello HTTP/1.1`,然后输入回车:
+
+```
+$ telnet localhost 8888
+Trying 127.0.0.1 …
+Connected to localhost.
+GET /hello HTTP/1.1
+
+HTTP/1.1 200 OK
+Hello, World!
+```
+
+你刚刚手动模拟了你的浏览器(的工作)!你发送了 HTTP 请求,并且收到了一个 HTTP 应答。下面是一个 HTTP 请求的基本结构:
+
+![](https://ruslanspivak.com/lsbaws-part1/LSBAWS_HTTP_request_anatomy.png)
+
+HTTP 请求的第一行由三部分组成:HTTP 方法(`GET`,因为我们想让我们的服务器返回一些内容),以及标明所需页面的路径 `/hello`,还有协议版本。
+
+为了简单一些,我们刚刚构建的 Web 服务器完全忽略了上面的请求内容。你也可以试着输入一些无用内容而不是“GET /hello HTTP/1.1”,但你仍然会收到一个“Hello, World!”响应。
+
+一旦你输入了请求行并敲了回车,客户端就会将请求发送至服务器;服务器读取请求行,就会返回相应的 HTTP 响应。
+
+下面是服务器返回客户端(在上面的例子里是 telnet)的响应内容:
+
+![](https://ruslanspivak.com/lsbaws-part1/LSBAWS_HTTP_response_anatomy.png)
+
+我们来解析它。这个响应由三部分组成:一个状态行 `HTTP/1.1 200 OK`,后面跟着一个空行,再下面是响应正文。
+
+HTTP 响应的状态行 HTTP/1.1 200 OK 包含了 HTTP 版本号,HTTP 状态码以及 HTTP 状态短语“OK”。当浏览器收到响应后,它会将响应正文显示出来,这也就是为什么你会在浏览器中看到“Hello, World!”。
+
+以上就是 Web 服务器的基本工作模型。总结一下:Web 服务器创建一个处于监听状态的套接字,循环接收新的连接。客户端建立 TCP 连接成功后,会向服务器发送  HTTP 请求,然后服务器会以一个 HTTP 响应做应答,客户端会将 HTTP 的响应内容显示给用户。为了建立 TCP 连接,客户端和服务端均会使用套接字。
+
+现在,你应该了解了 Web 服务器的基本工作方式,你可以使用浏览器或其它 HTTP 客户端进行试验。如果你尝试过、观察过,你应该也能够使用 telnet,人工编写 HTTP 请求,成为一个“人形” HTTP 客户端。
+
+现在留一个小问题:“你要如何在不对程序做任何改动的情况下,在你刚刚搭建起来的 Web 服务器上适配 Django, Flask 或 Pyramid 应用呢?”
+
+我会在本系列的第二部分中来详细讲解。敬请期待。
+
+顺便,我在撰写一本名为《搭个 Web 服务器:从头开始》的书。这本书讲解了如何从头开始编写一个基本的 Web 服务器,里面包含本文中没有的更多细节。订阅邮件列表,你就可以获取到这本书的最新进展,以及发布日期。
+
+--------------------------------------------------------------------------------
+
+via: https://ruslanspivak.com/lsbaws-part1/
+
+作者:[Ruslan][a]
+译者:[StdioA](https://github.com/StdioA)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://linkedin.com/in/ruslanspivak/
+[1]: https://github.com/rspivak/lsbaws/blob/master/part1/webserver1.py
+[2]: http://en.wikipedia.org/wiki/Uniform_resource_locator
+    
diff --git a/published/201608/20160316 Growing a career alongside Linux.md b/published/201608/20160316 Growing a career alongside Linux.md
new file mode 100644
index 0000000000..d536dea1bb
--- /dev/null
+++ b/published/201608/20160316 Growing a career alongside Linux.md	
@@ -0,0 +1,49 @@
+伴随 Linux 成长的职业生涯
+==================================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/OPENHERE_blue.png?itok=3eqp-7gT)
+
+我与 Linux 的故事开始于 1998 年,一直延续到今天。 当时我在 Gap 公司工作,管理着成千台运行着 [OS/2][1] 系统的台式机 ( 在随后的几年里变成了 [Warp 3.0][2])。 作为一个 OS/2 的粉丝,那时我非常喜欢那个时候。 随着这些台式机的嗡鸣,我们使用 Gap 开发的工具轻而易举地就能支撑起对成千的用户的服务支持。 然而,一切都将改变了。
+
+在 1998 年的 11 月, 我收到邀请加入一个新成立的公司,这家公司将专注于企业级 Linux 上。 这就是后来非常出名的 [Linuxcare][2]。
+
+### 我在 Linuxcare 的时光
+
+我曾经接触过一些 Linux , 但我从未想过要把它提供给企业客户。仅仅几个月后 ( 从这里开始成为了空间和时间上的转折点 ), 我就在管理一整条线的业务,让企业获得他们的软件,硬件,甚至是证书认证等各种在当时非常盛行的 Linux 服务。
+
+我支持的客户包括像 IBM ,Dell ,HP 这样的厂商以确保他们的硬件能够成功的运行 Linux 。 今天你们应该都听过许多关于在硬件上预装 Linux 的事, 但当时 Dell 邀请我去讨论为即将到来的贸易展上将 Linux 运行在认证的笔记本电脑上。  这是多么激动人心的时刻 !同时我们在之后几年内也支持了 IBM 和 HP 等多项认证工作。
+
+Linux 变化得非常快,并且它总是这样。 它也获得了更多的关键设备的支持,比如声音,网络和图形。在这段时间, 我把个人使用的系统从基于 RPM 的系统换成了 [Debian][3] 。
+
+### 使用 Linux 的这些年
+
+几年前我在一些做 Linux 硬件设备、Linux 定制软件以及 Linux 数据中心的公司工作。而在二十世纪中期的时候,那时我正在忙为那些在雷蒙德附近(微软公司所在地)的大一些的软件公司做咨询工作,为他们对比 Linux 解决方案及其自己的解决方案做分析和验证。 我个人使用的系统一直没有改变,我仍会在尽可能的情况下运行 Debian 测试系统。
+
+我真的非常欣赏发行版的灵活性和永久更新状态。 Debian 是我所使用过的最有趣且拥有良好支持的发行版,并且它拥有最好的社区,而我是社区的一份子。
+
+当我回首我使用 Linux 的这几年,我仍记得大约在二十世纪前期和中期的时候在圣何塞,旧金山,波士顿和纽约召开的那些 Linux Expo 大会。在 Linuxcare 时我们总是会摆一些有趣而且时髦的展位,在那边逛的时候总会碰到一些老朋友。这一切工作都是需要付出代价的,所有的这一切都是在努力地强调使用 Linux 的乐趣。
+
+随着虚拟化和云的崛起也让 Linux 变得更加有趣。 当我在 Linuxcare 的时候, 我们常和斯坦福大学附近的帕洛阿尔托的一个约 30 人左右的小公司在一块。我们会开车到他们的办公处,然后帮他们准备和我们一起参加展览的东西。 谁会想得到这个小小的初创公司会成就后来的 VMware ?
+
+我还有许多的故事,能认识这些人并和他们一起工作我感到很幸运。 Linux 在各方面都不断发展且变得尤为重要。 并且甚至随着它重要性的提升,它使用起来仍然非常有趣。 我认为它的开放性和可定制能力给它带来了大量的新用户,这也是让我感到非常震惊的一点。
+
+### 现在
+
+在过去的五年里我的工作重心逐渐离开 Linux。 我所管理的大规模基础设施项目中包含着许多不同的操作系统 ( 包括非开源的和开源的 ), 但我的心一直以来都是和 Linux 在一起的。
+
+在使用 Linux 过程中的乐趣和不断进步是在过去的 18 年里一直驱动我的动力。我从 Linux 2.0 内核开始看着它变成现在的这样。 Linux 是一个卓越的、生机勃勃的且非常酷的东西。
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/life/16/3/my-linux-story-michael-perry
+
+作者:[Michael Perry][a]
+译者:[chenxinlong](https://github.com/chenxinlong)
+校对:[wxy](https://github.com/wxy)
+
+[a]: https://opensource.com/users/mpmilestogo
+[1]: https://en.wikipedia.org/wiki/OS/2
+[2]: https://archive.org/details/IBMOS2Warp3Collection
+[3]: https://en.wikipedia.org/wiki/Linuxcare
+[4]: https://www.debian.org/
+[5]: 
diff --git a/published/201608/20160406 Let’s Build A Web Server. Part 2.md b/published/201608/20160406 Let’s Build A Web Server. Part 2.md
new file mode 100644
index 0000000000..38c09e9915
--- /dev/null
+++ b/published/201608/20160406 Let’s Build A Web Server. Part 2.md	
@@ -0,0 +1,430 @@
+搭个 Web 服务器(二)
+===================================
+
+在[第一部分][1]中,我提出了一个问题:“如何在你刚刚搭建起来的 Web 服务器上适配 Django, Flask 或 Pyramid 应用,而不用单独对 Web 服务器做做出改动以适应各种不同的 Web 框架呢?”我们可以从这一篇中找到答案。
+
+曾几何时,你所选择的 Python Web 框架会限制你所可选择的 Web 服务器,反之亦然。如果某个框架及服务器设计用来协同工作的,那么一切正常:
+
+![](https://ruslanspivak.com/lsbaws-part2/lsbaws_part2_before_wsgi.png)
+
+但你可能正面对着(或者曾经面对过)尝试将一对无法适配的框架和服务器搭配在一起的问题:
+
+![](https://ruslanspivak.com/lsbaws-part2/lsbaws_part2_after_wsgi.png)
+
+基本上,你需要选择那些能够一起工作的框架和服务器,而不能选择你想用的那些。
+
+所以,你该如何确保在不对 Web 服务器或框架的代码做任何更改的情况下,让你的 Web 服务器和多个不同的 Web 框架一同工作呢?这个问题的答案,就是 Python Web 服务器网关接口(Web Server Gateway Interface )(缩写为 [WSGI][2],念做“wizgy”)。
+
+![](https://ruslanspivak.com/lsbaws-part2/lsbaws_part2_wsgi_idea.png)
+
+WSGI 允许开发者互不干扰地选择 Web 框架及 Web 服务器的类型。现在,你可以真正将 Web 服务器及框架任意搭配,然后选出你最中意的那对组合。比如,你可以使用 [Django][3],[Flask][4] 或者 [Pyramid][5],与 [Gunicorn][6],[Nginx/uWSGI][7] 或 [Waitress][8] 进行结合。感谢 WSGI 同时对服务器与框架的支持,我们可以真正随意选择它们的搭配了。
+
+![](https://ruslanspivak.com/lsbaws-part2/lsbaws_part2_wsgi_interop.png)
+
+所以,WSGI 就是我在第一部分中提出,又在本文开头重复了一遍的那个问题的答案。你的 Web 服务器必须实现 WSGI 接口的服务器部分,而现代的 Python Web 框架均已实现了 WSGI 接口的框架部分,这使得你可以直接在 Web 服务器中使用任意框架,而不需要更改任何服务器代码,以对特定的 Web 框架实现兼容。
+
+现在,你已经知道 Web 服务器及 Web 框架对 WSGI 的支持使得你可以选择最合适的一对来使用,而且它也有利于服务器和框架的开发者,这样他们只需专注于其擅长的部分来进行开发,而不需要触及另一部分的代码。其它语言也拥有类似的接口,比如:Java 拥有 Servlet API,而 Ruby 拥有 Rack。
+
+这些理论都不错,但是我打赌你在说:“Show me the code!” 那好,我们来看看下面这个很小的 WSGI 服务器实现:
+
+```
+### 使用 Python 2.7.9,在 Linux 及 Mac OS X 下测试通过
+import socket
+import StringIO
+import sys
+
+
+class WSGIServer(object):
+
+    address_family = socket.AF_INET
+    socket_type = socket.SOCK_STREAM
+    request_queue_size = 1
+
+    def __init__(self, server_address):
+        ### 创建一个监听的套接字
+        self.listen_socket = listen_socket = socket.socket(
+            self.address_family,
+            self.socket_type
+        )
+        ### 允许复用同一地址
+        listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        ### 绑定地址
+        listen_socket.bind(server_address)
+        ### 激活套接字
+        listen_socket.listen(self.request_queue_size)
+        ### 获取主机的名称及端口
+        host, port = self.listen_socket.getsockname()[:2]
+        self.server_name = socket.getfqdn(host)
+        self.server_port = port
+        ### 返回由 Web 框架/应用设定的响应头部字段
+        self.headers_set = []
+
+    def set_app(self, application):
+        self.application = application
+
+    def serve_forever(self):
+        listen_socket = self.listen_socket
+        while True:
+            ### 获取新的客户端连接
+            self.client_connection, client_address = listen_socket.accept()
+            ### 处理一条请求后关闭连接,然后循环等待另一个连接建立
+            self.handle_one_request()
+
+    def handle_one_request(self):
+        self.request_data = request_data = self.client_connection.recv(1024)
+        ### 以 'curl -v' 的风格输出格式化请求数据
+        print(''.join(
+            '< {line}\n'.format(line=line)
+            for line in request_data.splitlines()
+        ))
+
+        self.parse_request(request_data)
+
+        ### 根据请求数据构建环境变量字典
+        env = self.get_environ()
+
+        ### 此时需要调用 Web 应用来获取结果,
+        ### 取回的结果将成为 HTTP 响应体
+        result = self.application(env, self.start_response)
+
+        ### 构造一个响应,回送至客户端
+        self.finish_response(result)
+
+    def parse_request(self, text):
+        request_line = text.splitlines()[0]
+        request_line = request_line.rstrip('\r\n')
+        ### 将请求行分成几个部分
+        (self.request_method,  # GET
+         self.path,            # /hello
+         self.request_version  # HTTP/1.1
+         ) = request_line.split()
+
+    def get_environ(self):
+        env = {}
+        ### 以下代码段没有遵循 PEP8 规则,但这样排版,是为了通过强调
+        ### 所需变量及它们的值,来达到其展示目的。
+        ###
+        ### WSGI 必需变量
+        env['wsgi.version']      = (1, 0)
+        env['wsgi.url_scheme']   = 'http'
+        env['wsgi.input']        = StringIO.StringIO(self.request_data)
+        env['wsgi.errors']       = sys.stderr
+        env['wsgi.multithread']  = False
+        env['wsgi.multiprocess'] = False
+        env['wsgi.run_once']     = False
+        ### CGI 必需变量
+        env['REQUEST_METHOD']    = self.request_method    # GET
+        env['PATH_INFO']         = self.path              # /hello
+        env['SERVER_NAME']       = self.server_name       # localhost
+        env['SERVER_PORT']       = str(self.server_port)  # 8888
+        return env
+
+    def start_response(self, status, response_headers, exc_info=None):
+        ### 添加必要的服务器头部字段
+        server_headers = [
+            ('Date', 'Tue, 31 Mar 2015 12:54:48 GMT'),
+            ('Server', 'WSGIServer 0.2'),
+        ]
+        self.headers_set = [status, response_headers + server_headers]
+        ### 为了遵循 WSGI 协议,start_response 函数必须返回一个 'write'
+        ### 可调用对象(返回值.write 可以作为函数调用)。为了简便,我们
+        ### 在这里无视这个细节。
+        ### return self.finish_response
+
+    def finish_response(self, result):
+        try:
+            status, response_headers = self.headers_set
+            response = 'HTTP/1.1 {status}\r\n'.format(status=status)
+            for header in response_headers:
+                response += '{0}: {1}\r\n'.format(*header)
+            response += '\r\n'
+            for data in result:
+                response += data
+            ### 以 'curl -v' 的风格输出格式化请求数据
+            print(''.join(
+                '> {line}\n'.format(line=line)
+                for line in response.splitlines()
+            ))
+            self.client_connection.sendall(response)
+        finally:
+            self.client_connection.close()
+
+SERVER_ADDRESS = (HOST, PORT) = '', 8888
+
+def make_server(server_address, application):
+    server = WSGIServer(server_address)
+    server.set_app(application)
+    return server
+
+if __name__ == '__main__':
+    if len(sys.argv) < 2:
+        sys.exit('Provide a WSGI application object as module:callable')
+    app_path = sys.argv[1]
+    module, application = app_path.split(':')
+    module = __import__(module)
+    application = getattr(module, application)
+    httpd = make_server(SERVER_ADDRESS, application)
+    print('WSGIServer: Serving HTTP on port {port} ...\n'.format(port=PORT))
+    httpd.serve_forever()
+```
+
+当然,这段代码要比第一部分的服务器代码长不少,但它仍然很短(只有不到 150 行),你可以轻松理解它,而不需要深究细节。上面的服务器代码还可以做更多——它可以用来运行一些你喜欢的框架写出的 Web 应用,可以是 Pyramid,Flask,Django 或其它 Python WSGI 框架。
+
+不相信吗?自己来试试看吧。把以上的代码保存为 `webserver2.py`,或直接从 [Github][9] 上下载它。如果你打算不加任何参数而直接运行它,它会抱怨一句,然后退出。
+
+```
+$ python webserver2.py
+Provide a WSGI application object as module:callable
+```
+
+它想做的其实是为你的 Web 应用服务,而这才是重头戏。为了运行这个服务器,你唯一需要的就是安装好 Python。不过,如果你希望运行 Pyramid,Flask 或 Django 应用,你还需要先安装那些框架。那我们把这三个都装上吧。我推荐的安装方式是通过 `virtualenv` 安装。按照以下几步来做,你就可以创建并激活一个虚拟环境,并在其中安装以上三个 Web 框架。
+
+```
+$ [sudo] pip install virtualenv
+$ mkdir ~/envs
+$ virtualenv ~/envs/lsbaws/
+$ cd ~/envs/lsbaws/
+$ ls
+bin  include  lib
+$ source bin/activate
+(lsbaws) $ pip install pyramid
+(lsbaws) $ pip install flask
+(lsbaws) $ pip install django
+```
+
+现在,你需要创建一个 Web 应用。我们先从 Pyramid 开始吧。把以下代码保存为 `pyramidapp.py`,并与刚刚的 `webserver2.py` 放置在同一目录,或直接从 [Github][10] 下载该文件:
+
+```
+from pyramid.config import Configurator
+from pyramid.response import Response
+
+
+def hello_world(request):
+    return Response(
+        'Hello world from Pyramid!\n',
+        content_type='text/plain',
+    )
+
+config = Configurator()
+config.add_route('hello', '/hello')
+config.add_view(hello_world, route_name='hello')
+app = config.make_wsgi_app()
+```
+
+现在,你可以用你自己的 Web 服务器来运行你的 Pyramid 应用了:
+
+```
+(lsbaws) $ python webserver2.py pyramidapp:app
+WSGIServer: Serving HTTP on port 8888 ...
+```
+
+你刚刚让你的服务器去加载 Python 模块 `pyramidapp` 中的可执行对象 `app`。现在你的服务器可以接收请求,并将它们转发到你的 Pyramid 应用中了。在浏览器中输入 http://localhost:8888/hello ,敲一下回车,然后看看结果:
+
+![](https://ruslanspivak.com/lsbaws-part2/lsbaws_part2_browser_pyramid.png)
+
+你也可以使用命令行工具 `curl` 来测试服务器:
+
+```
+$ curl -v http://localhost:8888/hello
+...
+```
+
+看看服务器和 `curl` 向标准输出流打印的内容吧。
+
+现在来试试 `Flask`。运行步骤跟上面的一样。
+
+```
+from flask import Flask
+from flask import Response
+flask_app = Flask('flaskapp')
+
+
+@flask_app.route('/hello')
+def hello_world():
+    return Response(
+        'Hello world from Flask!\n',
+        mimetype='text/plain'
+    )
+
+app = flask_app.wsgi_app
+```
+
+将以上代码保存为 `flaskapp.py`,或者直接从 [Github][11] 下载,然后输入以下命令运行服务器:
+
+```
+(lsbaws) $ python webserver2.py flaskapp:app
+WSGIServer: Serving HTTP on port 8888 ...
+```
+
+现在在浏览器中输入 http://localhost:8888/hello ,敲一下回车:
+
+![](https://ruslanspivak.com/lsbaws-part2/lsbaws_part2_browser_flask.png)
+
+同样,尝试一下 `curl`,然后你会看到服务器返回了一条 `Flask` 应用生成的信息:
+
+```
+$ curl -v http://localhost:8888/hello
+...
+```
+
+这个服务器能处理 Django 应用吗?试试看吧!不过这个任务可能有点复杂,所以我建议你将整个仓库克隆下来,然后使用 [Github][13] 仓库中的 [djangoapp.py][12] 来完成这个实验。这里的源代码主要是将 Django 的 helloworld 工程(已使用 `Django` 的 `django-admin.py startproject` 命令创建完毕)添加到了当前的 Python 路径中,然后导入了这个工程的 WSGI 应用。(LCTT 译注:除了这里展示的代码,还需要一个配合的 helloworld 工程才能工作,代码可以参见 [Github][13] 仓库。)
+
+```
+import sys
+sys.path.insert(0, './helloworld')
+from helloworld import wsgi
+
+
+app = wsgi.application
+```
+
+将以上代码保存为 `djangoapp.py`,然后用你的 Web 服务器运行这个 Django 应用:
+
+```
+(lsbaws) $ python webserver2.py djangoapp:app
+WSGIServer: Serving HTTP on port 8888 ...
+```
+
+输入以下链接,敲回车:
+
+![](https://ruslanspivak.com/lsbaws-part2/lsbaws_part2_browser_django.png)
+
+你这次也可以在命令行中测试——你之前应该已经做过两次了——来确认 Django 应用处理了你的请求:
+
+```
+$ curl -v http://localhost:8888/hello
+...
+```
+
+你试过了吗?你确定这个服务器可以与那三个框架搭配工作吗?如果没试,请去试一下。阅读固然重要,但这个系列的内容是**重新搭建**,这意味着你需要亲自动手干点活。去试一下吧。别担心,我等着你呢。不开玩笑,你真的需要试一下,亲自尝试每一步,并确保它像预期的那样工作。
+
+好,你已经体验到了 WSGI 的威力:它可以使 Web 服务器及 Web 框架随意搭配。WSGI 在 Python Web 服务器及框架之间提供了一个微型接口。它非常简单,而且在服务器和框架端均可以轻易实现。下面的代码片段展示了 WSGI 接口的服务器及框架端实现:
+
+```
+def run_application(application):
+    """服务器端代码。"""
+    ### Web 应用/框架在这里存储 HTTP 状态码以及 HTTP 响应头部,
+    ### 服务器会将这些信息传递给客户端
+    headers_set = []
+    ### 用于存储 WSGI/CGI 环境变量的字典
+    environ = {}
+
+    def start_response(status, response_headers, exc_info=None):
+        headers_set[:] = [status, response_headers]
+
+    ### 服务器唤醒可执行变量“application”,获得响应头部
+    result = application(environ, start_response)
+    ### 服务器组装一个 HTTP 响应,将其传送至客户端
+    …
+
+def app(environ, start_response):
+    """一个空的 WSGI 应用"""
+    start_response('200 OK', [('Content-Type', 'text/plain')])
+    return ['Hello world!']
+
+run_application(app)
+```
+
+这是它的工作原理:
+
+1. Web 框架提供一个可调用对象 `application` (WSGI 规范没有规定它的实现方式)。
+2. Web 服务器每次收到来自客户端的 HTTP 请求后,会唤醒可调用对象 `applition`。它会向该对象传递一个包含 WSGI/CGI 变量的环境变量字典 `environ`,以及一个可调用对象 `start_response`。
+3. Web 框架或应用生成 HTTP 状态码和 HTTP 响应头部,然后将它传给 `start_response` 函数,服务器会将其存储起来。同时,Web 框架或应用也会返回 HTTP 响应正文。
+4. 服务器将状态码、响应头部及响应正文组装成一个 HTTP 响应,然后将其传送至客户端(这一步并不在 WSGI 规范中,但从逻辑上讲,这一步应该包含在工作流程之中。所以为了明确这个过程,我把它写了出来)
+
+这是这个接口规范的图形化表达:
+
+![](https://ruslanspivak.com/lsbaws-part2/lsbaws_part2_wsgi_interface.png)
+
+到现在为止,你已经看过了用 Pyramid、Flask 和 Django 写出的 Web 应用的代码,你也看到了一个 Web 服务器如何用代码来实现另一半(服务器端的) WSGI 规范。你甚至还看到了我们如何在不使用任何框架的情况下,使用一段代码来实现一个最简单的 WSGI Web 应用。
+
+其实,当你使用上面的框架编写一个 Web 应用时,你只是在较高的层面工作,而不需要直接与 WSGI 打交道。但是我知道你一定也对 WSGI 接口的框架部分感兴趣,因为你在看这篇文章呀。所以,我们不用 Pyramid、Flask 或 Django,而是自己动手来创造一个最朴素的 WSGI Web 应用(或 Web 框架),然后将它和你的服务器一起运行:
+
+```
+def app(environ, start_response):
+    """一个最简单的 WSGI 应用。
+
+    这是你自己的 Web 框架的起点 ^_^
+    """
+    status = '200 OK'
+    response_headers = [('Content-Type', 'text/plain')]
+    start_response(status, response_headers)
+    return ['Hello world from a simple WSGI application!\n']
+```
+
+同样,将上面的代码保存为 `wsgiapp.py` 或直接从 [Github][14] 上下载该文件,然后在 Web 服务器上运行这个应用,像这样:
+
+```
+(lsbaws) $ python webserver2.py wsgiapp:app
+WSGIServer: Serving HTTP on port 8888 ...
+```
+
+在浏览器中输入下面的地址,然后按下回车。这是你应该看到的结果:
+
+![](https://ruslanspivak.com/lsbaws-part2/lsbaws_part2_browser_simple_wsgi_app.png)
+
+你刚刚在学习如何创建一个 Web 服务器的过程中自己编写了一个最朴素的 WSGI Web 框架!棒极了!
+
+现在,我们再回来看看服务器传给客户端的那些东西。这是在使用 HTTP 客户端调用你的 Pyramid 应用时,服务器生成的 HTTP 响应内容:
+
+![](https://ruslanspivak.com/lsbaws-part2/lsbaws_part2_http_response.png)
+
+这个响应和你在本系列第一部分中看到的 HTTP 响应有一部分共同点,但它还多出来了一些内容。比如说,它拥有四个你曾经没见过的 [HTTP 头部][15]:`Content-Type`, `Content-Length`, `Date` 以及 `Server`。这些头部内容基本上在每个 Web 服务器返回的响应中都会出现。不过,它们都不是被严格要求出现的。这些 HTTP 请求/响应头部字段的目的在于它可以向你传递一些关于 HTTP 请求/响应的额外信息。
+
+既然你对 WSGI 接口了解的更深了一些,那我再来展示一下上面那个 HTTP 响应中的各个部分的信息来源:
+
+![](https://ruslanspivak.com/lsbaws-part2/lsbaws_part2_http_response_explanation.png)
+
+我现在还没有对上面那个 `environ` 字典做任何解释,不过基本上这个字典必须包含那些被 WSGI 规范事先定义好的 WSGI 及 CGI 变量值。服务器在解析 HTTP 请求时,会从请求中获取这些变量的值。这是 `environ` 字典应该有的样子:
+
+![](https://ruslanspivak.com/lsbaws-part2/lsbaws_part2_environ.png)
+
+Web 框架会利用以上字典中包含的信息,通过字典中的请求路径、请求动作等等来决定使用哪个视图来处理响应、在哪里读取请求正文、在哪里输出错误信息(如果有的话)。
+
+现在,你已经创造了属于你自己的 WSGI Web 服务器,你也使用不同 Web 框架做了几个 Web 应用。而且,你在这个过程中也自己创造出了一个朴素的 Web 应用及框架。这个过程真是累人。现在我们来回顾一下,你的 WSGI Web 服务器在服务请求时,需要针对 WSGI 应用做些什么:
+
+- 首先,服务器开始工作,然后会加载一个可调用对象 `application`,这个对象由你的 Web 框架或应用提供
+- 然后,服务器读取一个请求
+- 然后,服务器会解析这个请求
+- 然后,服务器会使用请求数据来构建一个 `environ` 字典
+- 然后,它会用 `environ` 字典及一个可调用对象 `start_response` 作为参数,来调用 `application`,并获取响应体内容。
+- 然后,服务器会使用 `application` 返回的响应体,和 `start_response` 函数设置的状态码及响应头部内容,来构建一个 HTTP 响应。
+- 最终,服务器将 HTTP 响应回送给客户端。
+
+![](https://ruslanspivak.com/lsbaws-part2/lsbaws_part2_server_summary.png)
+
+这基本上是服务器要做的全部内容了。你现在有了一个可以正常工作的 WSGI 服务器,它可以为使用任何遵循 WSGI 规范的 Web 框架(如 Django、Flask、Pyramid,还有你刚刚自己写的那个框架)构建出的 Web 应用服务。最棒的部分在于,它可以在不用更改任何服务器代码的情况下,与多个不同的 Web 框架一起工作。真不错。
+
+在结束之前,你可以想想这个问题:“你该如何让你的服务器在同一时间处理多个请求呢?”
+
+敬请期待,我会在第三部分向你展示一种解决这个问题的方法。干杯!
+
+顺便,我在撰写一本名为《搭个 Web 服务器:从头开始》的书。这本书讲解了如何从头开始编写一个基本的 Web 服务器,里面包含本文中没有的更多细节。[订阅邮件列表][16],你就可以获取到这本书的最新进展,以及发布日期。
+
+--------------------------------------------------------------------------------
+
+via: https://ruslanspivak.com/lsbaws-part2/
+
+作者:[Ruslan][a]
+译者:[StdioA](https://github.com/StdioA)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://github.com/rspivak/
+[1]: https://linux.cn/article-7662-1.html
+[2]: https://www.python.org/dev/peps/pep-0333/
+[3]: https://www.djangoproject.com/
+[4]: http://flask.pocoo.org/
+[5]: http://trypyramid.com/
+[6]: http://gunicorn.org/
+[7]: http://uwsgi-docs.readthedocs.org/
+[8]: http://waitress.readthedocs.org/
+[9]: https://github.com/rspivak/lsbaws/blob/master/part2/webserver2.py
+[10]: https://github.com/rspivak/lsbaws/blob/master/part2/pyramidapp.py
+[11]: https://github.com/rspivak/lsbaws/blob/master/part2/flaskapp.py
+[12]: https://github.com/rspivak/lsbaws/blob/master/part2/flaskapp.py
+[13]: https://github.com/rspivak/lsbaws/
+[14]: https://github.com/rspivak/lsbaws/blob/master/part2/wsgiapp.py
+[15]: http://en.wikipedia.org/wiki/List_of_HTTP_header_fields
+[16]: https://ruslanspivak.com/lsbaws-part2/
\ No newline at end of file
diff --git a/published/201608/20160416 A newcomer's guide to navigating OpenStack Infrastructure.md b/published/201608/20160416 A newcomer's guide to navigating OpenStack Infrastructure.md
new file mode 100644
index 0000000000..952fc6f594
--- /dev/null
+++ b/published/201608/20160416 A newcomer's guide to navigating OpenStack Infrastructure.md	
@@ -0,0 +1,94 @@
+给学习 OpenStack 架构的新手入门指南
+===========================================================
+
+OpenStack 欢迎新成员的到来,但是,对于这个发展趋近成熟并且快速迭代的开源社区而言,能够拥有一个新手指南并不是件坏事。在奥斯汀举办的 OpenStack 峰会上,[Paul Belanger][1] (来自红帽公司)、 [Elizabeth K. Joseph][2] (来自 HPE 公司)和 [Christopher Aedo][3] (来自 IBM 公司)就[针对新人的 OpenStack 架构][4]作了一场专门的讲演。在这次采访中,他们提供了一些建议和资源来帮助新人成为 OpenStack 贡献者中的一员。
+
+![](https://opensource.com/sites/default/files/images/life/Interview%20banner%20Q%26A.png)
+
+**你的讲演介绍中说你将“深入架构核心,并解释你需要知道的关于让 OpenStack 工作起来的每一件事情”。这对于 40 分钟的讲演来说是一个艰巨的任务。那么,对于学习 OpenStack 架构的新手来说最需要知道那些事情呢?**
+
+**Elizabeth K. Joseph (EKJ)**: 我们没有为 OpenStack 使用 GitHub 这种提交补丁的方式,这是因为这样做会对新手造成巨大的困扰,尽管由于历史原因我们还是在 GitHub 上维护了所有库的一个镜像。相反,我们使用了一种完全开源的评审形式,而且持续集成(CI)是由 OpenStack 架构团队维护的。与之有关的,自从我们使用了 CI 系统,每一个提交给 OpenStack 的改变都会在被合并之前进行测试。
+
+**Paul Belanger (PB)**: 这个项目中的大多数都是富有激情的人,因此当你提交的补丁被某个人否定时不要感到沮丧。
+
+**Christopher Aedo (CA)**:社区会帮助你取得成功,因此不要害怕提问或者寻求更多的那些能够促进你理解某些事物的引导者。
+
+**在你的讲话中,对于一些你无法涉及到的方面,你会向新手推荐哪些在线资源来让他们更加容易入门?**
+
+**PB**:当然是我们的 [OpenStack 项目架构文档][5]。我们已经花了足够大的努力来尽可能让这些文档能够随时保持最新状态。在 OpenStack 运行中使用的每个系统都作为一个项目,都制作了专门的页面来进行说明。甚至于连 OpenStack 云这种架构团队也会放到线上。
+
+**EKJ**:我对于架构文档这件事上的观点和 Paul 是一致的,另外,我们十分乐意看到来自那些正在学习项目的人们提交上来的补丁。我们通常不会意识到我们忽略了文档中的某些内容,除非它们恰好被人问起。因此,阅读、学习,会帮助我们修补这些知识上的漏洞。你可以在 [OpenStack 架构邮件清单]提出你的问题,或者在我们位于 FreeNode 上的 #OpenStack-infra 的 IRC 专栏发起你的提问。
+
+**CA**:我喜欢[这个详细的帖子][7],它是由 Ian Wienand 写的一篇关于构建镜像的文章。
+
+**"gotchas" 会是 OpenStack 新的贡献者们所寻找的吗?**
+
+**EKJ**:向项目作出贡献并不仅仅是提交新的代码和新的特性;OpenStack 社区高度重视代码评审。如果你想要别人查看你的补丁,那你最好先看看其他人是如何做的,然后参考他们的风格,最后一步步做到你也能够向其他人一样提交清晰且结构分明的代码补丁。你越是能让你的同伴了解你的工作并知道你正在做的评审,那他们也就越有可能及时评审你的代码。
+
+**CA**:我看到过大量的新手在面对 [Gerrit][8] 时受挫,阅读开发者引导中的[开发者工作步骤][9],有可能的话多读几遍。如果你没有用过 Gerrit,那你最初对它的感觉可能是困惑和无力的。但是,如果你随后做了一些代码评审的工作,那么你就能轻松应对它。此外,我是 IRC 的忠实粉丝,它可能是一个获得帮助的好地方,但是,你最好保持一个长期在线的状态,这样,尽管你在某个时候没有出现,人们也可以回答你的问题。(阅读 [IRC,开源成功的秘诀][10])你不必总是在线,但是你最好能够轻松的在一个频道中回溯之前信息,以此来跟上最新的动态,这种能力非常重要。
+
+**PB**:我同意 Elizabeth 和 Chris 的观点, Gerrit 是需要花点精力的,它将汇聚你的开发方面的努力。你不仅仅要提交代码给别人去评审,同时,你也要能够评审其他人的代码。看到 Gerrit 的界面,你可能一时会变的很困惑。我推荐新手去尝试 [Gertty][11],它是一个基于控制台的终端界面,用于 Gerrit 代码评审系统,而它恰好也是 OpenStack 架构所驱动的一个项目。
+
+**你对于 OpenStack 新手如何通过网络与其他贡献者交流方面有什么好的建议?**
+
+**PB**:对我来说,是通过 IRC 并在 Freenode 上参加 #OpenStack-infra 频道([IRC 日志][12])。这频道上面有很多对新手来说很有价值的资源。你可以看到 OpenStack 项目日复一日的运作情况,同时,一旦你知道了 OpenStack 项目的工作原理,你将更好的知道如何为 OpenStack 的未来发展作出贡献。
+
+**CA**:我想要为 IRC 再次说明一点,在 IRC 上保持全天在线记录对我来说有非常重大的意义,因为我会时刻保持连接并随时接到提醒。这也是一种非常好的获得帮助的方式,特别是当你和某人卡在了项目中出现的某一个难题的时候,而在一个活跃的 IRC 频道中,总会有一些人很乐意为你解决问题。
+
+**EKJ**:[OpenStack 开发邮件列表][13]对于能够时刻查看到你所致力于的 OpenStack 项目的最新情况是非常重要的。因此,我推荐一定要订阅它。邮件列表使用主题标签来区分项目,因此你可以设置你的邮件客户端来使用它来专注于你所关心的项目。除了在线资源之外,全世界范围内也成立了一些 OpenStack 小组,他们被用来为 OpenStack 的用户和贡献者提供服务。这些小组可能会定期要求 OpenStack 主要贡献者们举办座谈和活动。你可以在 MeetUp.com 上搜素你所在地域的贡献者活动聚会,或者在 [groups.openstack.org][14] 上查看你所在的地域是否存在 OpenStack 小组。最后,还有一个每六个月举办一次的 [OpenStack 峰会][15],这个峰会上会作一些关于架构的演说。当前状态下,这个峰会包含了用户会议和开发者会议,会议内容都是和 OpenStack 相关的东西,包括它的过去,现在和未来。
+
+**OpenStack 需要在那些方面得到提升来让新手更加容易学会并掌握?**
+
+**PB**: 我认为我们的 [account-setup][16] 环节对于新的贡献者已经做的比较容易了,特别是教他们如何提交他们的第一个补丁。真正参与到 OpenStack 开发者模式中是需要花费很大的努力的,相比贡献者来说已经显得非常多了;然而,一旦融入进去了,这个模式将会运转的十分高效和令人满意。
+
+**CA**: 我们拥有一个由专业开发者组成的社区,而且我们的关注点都是发展 OpenStack 本身,同时,我们致力于让用户付出更小的代价去使用 OpenStack 云架构平台。我们需要发掘更多的应用开发者,并且鼓励更多的人去开发能在 OpenStack 云上完美运行的云应用程序,我们还鼓励他们在[社区 App 目录][17]上去贡献那些由他们开发的应用。我们可以通过不断提升我们的 API 标准和保证我们不同的库(比如 libcloud,phpopencloud 以及其他一些库)持续地为开发者提供可信赖的支持来实现这一目标。还有一点就是通过举办更多的 OpenStack 黑客比赛。所有的这些事情都可以降低新人的学习门槛,这样也能引导他们与这个社区之间的关系更加紧密。
+
+**EKJ**: 我已经致力于开源软件很多年了。但是,对于大量的 OpenStack 开发者而言,这是一个他们自己所从事的第一个开源项目。我发现他们之前使用私有软件的背景并没有为他们塑造开源的观念、方法论,以及在开源项目中需要具备的合作技巧。我乐于看到我们能够让那些曾经一直在使用私有软件工作的人能够真正的明白他们在开源如软件社区所从事的事情的巨大价值。
+
+**我想把 2016 年打造成开源俳句之年。请用俳句来向新手解释 OpenStack 一下。**
+
+(LCTT 译注:俳句(Haiku)是一种日本古典短诗,以5-7-5音节为三句,校对者不揣浅陋,诌了几句歪诗,勿笑 :D,另外 OpenStack 本身音节太长,就捏造了一个中文译名“开栈”——明白就好。)
+
+**PB**: 开栈在云上//倘钟情自由软件//先当造补丁(OpenStack runs clouds
+If you enjoy free software
+Submit your first patch)
+
+**CA**:时光不必久//开栈将支配世界//协力早来到(In the near future
+OpenStack will rule the world
+Help make it happen!)
+
+**EKJ**:开栈有自由//放在自家服务器//运行你的云(OpenStack is free
+Deploy on your own servers
+And run your own cloud!)
+
+*Paul、Elizabeth 和 Christopher 在 4 月 25 号星期一上午 11:15 于奥斯汀举办的 OpenStack 峰会发表了[演说][18]。
+
+------------------------------------------------------------------------------
+
+via: https://opensource.com/business/16/4/interview-openstack-infrastructure-beginners
+
+作者:[Rikki Endsley][a]
+译者:[kylepeng93](https://github.com/kylepeng93)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://rikkiendsley.com/
+[1]: https://twitter.com/pabelanger
+[2]: https://twitter.com/pleia2
+[3]: https://twitter.com/docaedo
+[4]: https://www.openstack.org/summit/austin-2016/summit-schedule/events/7337
+[5]: http://docs.openstack.org/infra/system-config/
+[6]: http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-infra
+[7]: https://www.technovelty.org/openstack/image-building-in-openstack-ci.html
+[8]: https://code.google.com/p/gerrit/
+[9]: http://docs.openstack.org/infra/manual/developers.html#development-workflow
+[10]: https://developer.ibm.com/opentech/2015/12/20/irc-the-secret-to-success-in-open-source/
+[11]: https://pypi.python.org/pypi/gertty
+[12]: http://eavesdrop.openstack.org/irclogs/%23openstack-infra/
+[13]: http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev
+[14]: https://groups.openstack.org/
+[15]: https://www.openstack.org/summit/
+[16]: http://docs.openstack.org/infra/manual/developers.html#account-setup
+[17]: https://apps.openstack.org/
+[18]: https://www.openstack.org/summit/austin-2016/summit-schedule/events/7337
diff --git a/published/201608/20160502 The intersection of Drupal, IoT, and open hardware.md b/published/201608/20160502 The intersection of Drupal, IoT, and open hardware.md
new file mode 100644
index 0000000000..168bbce778
--- /dev/null
+++ b/published/201608/20160502 The intersection of Drupal, IoT, and open hardware.md	
@@ -0,0 +1,61 @@
+Drupal、IoT 和开源硬件之间的交集
+=======================================================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/drupal_blue_gray_lead.jpeg?itok=t7W_KD-D)
+
+
+来认识一下 [Amber Matz][1],她是来自 Lullabot Education 旗下的 [Drupalize.Me][3] 的产品经理以及培训师。当她没有倒腾 Arduino、Raspberry Pi 以及电子穿戴设备时,通常会在波特兰 Drupal 用户组里担任辩论主持人。
+
+在即将举行的 [DrupalCon NOLA][3] 大会上,Amber 将主持一个关于 Drupal 和 IoT 的主题。如果你会去参加,也想了解下开源硬件,IoT 和 Drupal 之间的交集,那这个将很合适。如果你去不了新奥尔良的现场也没关系,Amber 还分享了许多很酷的事情。在这次采访中,她讲述了自己参与 Drupal 的原因,一些她自己喜欢的开源硬件项目,以及 IoT 和 Drupal 的未来。
+
+![](https://opensource.com/sites/default/files/images/life/Interview%20banner%20Q%26A.png)
+
+**你是怎么加入 Drupal 社区的?**
+
+在这之前,我在一家大型非盈利性机构市场部的“网站管理部”工作,飞快地批量生产出各种定制 PHP/MySQL 表单。最终我厌烦了这一切,并开始在网上寻找更好的方式。然后我找到了 Drupal 6 并开始沉迷进去。过了几年,在一次跳槽之后,我发现了波特兰 Drupal 用户组,然后在里面找了一份全职的 Drupal 开发者工作。我一直经常参加在波特兰的聚会,在那里我找到了大量的社区、朋友和专业方面的发展。一个偶然的机会,我在 Lullabot 找了一份培训师的工作,为 Drupalize.Me 提供内容。现在,我管理着 Drupalize.Me 的内容输出,负责编撰 Drupal 8 相关的内容,还很大程度地参与到波特兰 Drupal 社区中。我是今年的协调员,寻找并安排演讲者们。
+
+**我们想知道:什么是 Arduino 原型,你是怎么找到它的,以及你用 Arduino 做过的最酷的事是什么?**
+
+Arduino,Raspberry Pi,以及可穿戴电子设备,这些年到处都能听到这些术语。我在几年前通过 Becky Stern 的 YouTube 秀(最近由 Becky 继续主持,每周三播出)发现了 [Adafruit 的可穿戴电子设备][4]。我被那些可穿戴设备迷住了,还订了一套 LED 缝制工具,不过没做出任何东西。我不太适合它。我没有任何电子相关的背景,而且在我被那些项目吸引的时候,我根本不知道怎么做出那样的东西,它似乎看上去太遥远了。
+
+后来,我在 Coursera 上找到了一个“物联网”专题。(很时髦,对吧?)我很快就喜欢上了。我最终找到了 Arduino 是什么的解释,以及所有这些其他的重要术语和概念。我订了一套推荐的 Arduino 初学者套件,还附带了一本如何上手的小册子。当我第一次让 LED 闪烁的时候,开心极了。我在圣诞节以及之后有两个星期的假期,然而我什么都没干,就一直根据初学者小册子给 Arduino 电路编程。很奇怪我觉得很放松!我太喜欢了。
+
+在一月份的时候,我开始构思我自己的原型设备。在知道我需要主持公司培训的开场白时,我用五个 LED 灯和 Arduino 搭建了一个开场白视觉计时器的原型。
+
+![](https://opensource.com/sites/default/files/resize/amber-arduino-lightning-talk-timer-400x400.jpg)
+
+这是一次巨大的成功。我还做了我的第一个可穿戴项目,一件会发光的连帽衫,使用了和 Arduino IDE 兼容的 Gemma 微控制器,一个小的圆形可缝制部件,然后用可导电的线缝起来,将一个滑动可变电阻和衣服帽口的收缩绳连在一起,用来控制缝到帽子里的五个 NeoPixel 灯的颜色。这就是我对原型设计的看法:做一些很好玩也可能会有点实际用途的疯狂项目。
+
+**Drupal 和 IoT 带来的最大机遇是什么??**
+
+IoT 与 Web Service 以及 Drupal 分层趋势实际并没有太大差别。就是将数据从一个东西传送到另一个东西,然后将数据转换成一些有用的东西。但数据是如何送达?能用来做点什么?你觉得现在就有一大堆现成的解决方案、应用、中间层,以及 API 吗?采用 IoT,这只会继续成几何指数级的增长。我觉得,给我任何一个设备或“东西”,总有办法来将它连接到互联网上,有很多办法。而且有大量现成的代码库来帮助创客们将他们的数据从一个东西传到另一个东西。
+
+那么 Drupal 在这里处于什么位置?首先,Web services 将是第一个明显的地方。但作为一个创客,我不希望将时间花在编写 Drupal 的订制模块上。我想要的是即插即用!所以我很高兴出现这样的模块能连接 IoT 云端 API 和服务,比如 ThingSpeak,Adafruit.io,IFTTT,以及其他的。我觉得也有一个很好的商业机会,在 Drupal 里构建一套 IoT 云服务,允许用户发送和存储他们的传感器数据,并可以制成表格和图像,还可以写一些插件可以响应特定数据或阙值。每一个 IoT 云 API 服务都是一个细分的机会,所以能留下很大空间给其他人。
+
+**这次 DrupalCon 你有哪些期待?**
+
+我喜欢与 Drupal 上的朋友重逢,认识一些新的人,还能见到 Lullabot 和 Drupalize.Me 的同事(我们是分布式的公司)!Drupal 8 有太多东西可以去探索了,我们给我们的客户们提供了海量的培训资料。所以,我很期待参与一些 Drupal 8 相关的主题,以及跟上最新的开发进度。最后,我对新奥尔良也很感兴趣!我曾经在 2004 年去过,很期待将这次将看到哪些改变。
+
+**谈一谈你这次 DrupalCon 上的演讲:“超越闪烁:将 Drupal 加到你的 IoT 游乐场中”。别人为什么要参加?他们最重要的收获会是什么?**
+
+我的主题的标题是,“超越闪烁:将 Drupal 加到你的 IoT 游乐场中”,假设我们所有人都处在同一进度和层次,你不需要了解任何关于 Arduino、物联网、甚至是 Drupal,都能跟上。我将从用 Arduino 让 LED 灯闪烁开始,然后我会谈一下我自己在这里面的最大收获:玩、学、教和做。我会列出一些曾经激励过我的例子,它们也很有希望能激发和鼓励其他听众去尝试一下。然后,就是展示时间!
+
+首先,第一个东西。它是一个建筑提醒信号灯。在这个展示里,我会说明如何将信号灯连到互联网上,以及如何响应从云 API 服务收到的数据。然后,第二个东西。它是一个蒸汽朋克风格 iPhone 外壳形式的“天气手表”。有一个小型 LED 矩阵用来显示我的天气的图标,一个气压和温度传感器,一个 GPS 模块,以及一个 Bluetooth LE 模块,都连接到一个 Adafruit Flora 微控制器上。第二个东西能通过蓝牙连接到我的 iPhone 上的一个应用,并将天气和位置数据通过 MQTT 协议发到 Adafruit.io 的服务器!然后,在 Drupal 这边,我会从云端下载这些数据,更新天气信息,然后更新地图。所以大家也能体验一下通过web service、地图和 Drupal 8 的功能块所能做的事情。
+
+学习和制作这些展示原型是一次烧脑的探险,我也希望有人能参与这个主题并感染一点我对这种技术交叉的传染性热情!我很兴奋能分享一些我的发现。
+
+------------------------------------------------------------------------------
+
+via: https://opensource.com/business/16/5/drupalcon-interview-amber-matz
+
+作者:[Jason Hibbets][a]
+译者:[zpl1025](https://github.com/zpl1025)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/jhibbets
+[1]: https://www.drupal.org/u/amber-himes-matz
+[2]: https://drupalize.me/
+[3]: https://events.drupal.org/neworleans2016/
+[4]: https://www.adafruit.com/beckystern
diff --git a/published/201608/20160506 Linus Torvalds Talks IoT Smart Devices Security Concerns and More.md b/published/201608/20160506 Linus Torvalds Talks IoT Smart Devices Security Concerns and More.md
new file mode 100644
index 0000000000..78af0dc3de
--- /dev/null
+++ b/published/201608/20160506 Linus Torvalds Talks IoT Smart Devices Security Concerns and More.md	
@@ -0,0 +1,49 @@
+Linus Torvalds 谈及物联网、智能设备、安全连接等问题
+===========================================================================
+
+![](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/elc-linus-b.jpg?itok=6WwnCSjL)
+
+*Dirk Hohndel 在嵌入式大会上采访 Linus Torvalds 。*
+
+
+4 月 4 日到 6 日,在圣迭戈召开的[嵌入式 Linux 大会(Embedded Linux Conference)][0](ELC) 从首次举办到现在已经有 11 年了,该会议包括了与 Linus Torvalds 的主题讨论。作为 Linux 内核的缔造者和最高决策者——用采访他的英特尔 Linux 和开源技术总监 Dirk Hohndel 的话说,“(他是)我们聚在一起的理由”——他对 Linux 在嵌入式和物联网应用程序领域的发展表示乐观。Torvalds 很明确地力挺了嵌入式 Linux,它被 Linux 桌面、服务器和云技术这些掩去光芒已经很多年了。
+
+![](https://www.linux.com/sites/lcom/files/styles/floated_images/public/elc-linus_0.jpg?itok=FNPIDe8k)
+
+*Linus Torvalds 在嵌入式 Linux 大会上的演讲。*
+
+物联网是嵌入式大会的主题,在 OpenIoT 峰会讲演中谈到了,在 Torvalds 的访谈中也是主要话题。
+
+Torvalds 对 Hohndel 说到,“或许你不会在物联网末端设备上看到 Linux 的影子,但是在你有一个中心设备的时候,你就会需要它。尤其是物联网标准都有 23 个的时候,你就更需要智能设备了。如果你全部使用的是低级设备,它们没必要一定运行 Linux;如果它们采用的标准稍有差异,你就需要很多的智能设备。我们将来也不会有一个完全开放的标准来将这些物联网设备统一到一起,但是我们会有 3/4 的主要协议是一样的,然后那些智能的中心设备就可以对它们进行互相转换。”
+
+当 Hohndel 问及在物联网的巨大安全漏洞的时候,Torvalds 神情如常。他说:“我不担心安全问题因为我们能做的不是很多,物联网(设备)是不能更新的,这是我们面对的事实。"
+
+Linux 缔造者看起来更关心的是一次性嵌入式项目缺少对上游的及时贡献,尽管他注意到近年来这些有了一些显著改善,特别是在硬件整合方面。
+
+“嵌入式领域历来就很难与开源开发者有所联系,但是我认为这些都在发生改变。”Torvalds 说:“ARM 社区变得越来越好了。内核维护者实际上现在也能跟上了一些硬件的更新换代。一切都在变好,但是还不够。”
+
+Torvalds 承认他在家经常使用桌面系统而不是嵌入式系统,并且对硬件不是很熟悉。
+
+“我已经用电烙铁弄坏了很多东西。”他说到。“我真的不适合搞硬件开发。”另一方面,Torvalds 设想如果他现在是个年轻人,他可能也在摆弄 Raspberry Pi 和 BeagleBone(猎兔犬板)。“最棒的是你不需要精通焊接,你只需要买个新的板子就行。”
+
+同时,Torvalds 也承诺他要为 Linux 桌面再奋斗一个 25 年。他笑着说:“我要为它工作一生。”
+
+下面,请看完整[视频](https://youtu.be/tQKUWkR-wtM)。
+
+
+要获取关于嵌入式 Linux 和物联网的最新信息,请访问 2016 年嵌入式 Linux 大会 150+ 分钟的会议全程。[现在观看][1]。
+
+--------------------------------------------------------------------------------
+
+via: https://www.linux.com/news/linus-torvalds-talks-iot-smart-devices-security-concerns-and-more-video
+
+作者:[ERIC BROWN][a]
+译者:[vim-kakali](https://github.com/vim-kakali)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.linux.com/users/ericstephenbrown
+[0]: http://events.linuxfoundation.org/events/embedded-linux-conference
+[1]: http://go.linuxfoundation.org/elc-openiot-summit-2016-videos?utm_source=lf&utm_medium=blog&utm_campaign=linuxcom
+
diff --git a/translated/tech/20160512 Bitmap in Linux Kernel.md b/published/201608/20160512 Bitmap in Linux Kernel.md
similarity index 68%
rename from translated/tech/20160512 Bitmap in Linux Kernel.md
rename to published/201608/20160512 Bitmap in Linux Kernel.md
index 6475b9260e..3c4117aece 100644
--- a/translated/tech/20160512 Bitmap in Linux Kernel.md	
+++ b/published/201608/20160512 Bitmap in Linux Kernel.md	
@@ -1,16 +1,10 @@
----
-date: 2016-07-09 14:42
-status: public
-title: 20160512 Bitmap in Linux Kernel
----
-
-Linux 内核里的数据结构
+Linux 内核里的数据结构——位数组
 ================================================================================
 
 Linux 内核中的位数组和位操作
 --------------------------------------------------------------------------------
 
-除了不同的基于[链式](https://en.wikipedia.org/wiki/Linked_data_structure)和[树](https://en.wikipedia.org/wiki/Tree_%28data_structure%29)的数据结构以外,Linux 内核也为[位数组](https://en.wikipedia.org/wiki/Bit_array)或`位图`提供了 [API](https://en.wikipedia.org/wiki/Application_programming_interface)。位数组在 Linux 内核里被广泛使用,并且在以下的源代码文件中包含了与这样的结构搭配使用的通用 `API`:
+除了不同的基于[链式](https://en.wikipedia.org/wiki/Linked_data_structure)和[树](https://en.wikipedia.org/wiki/Tree_%28data_structure%29)的数据结构以外,Linux 内核也为[位数组](https://en.wikipedia.org/wiki/Bit_array)(或称为位图(bitmap))提供了 [API](https://en.wikipedia.org/wiki/Application_programming_interface)。位数组在 Linux 内核里被广泛使用,并且在以下的源代码文件中包含了与这样的结构搭配使用的通用 `API`:
 
 * [lib/bitmap.c](https://github.com/torvalds/linux/blob/master/lib/bitmap.c)
 * [include/linux/bitmap.h](https://github.com/torvalds/linux/blob/master/include/linux/bitmap.h)
@@ -19,14 +13,14 @@ Linux 内核中的位数组和位操作
 
 * [arch/x86/include/asm/bitops.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h)
 
-头文件。正如我上面所写的,`位图`在 Linux 内核中被广泛地使用。例如,`位数组`常常用于保存一组在线/离线处理器,以便系统支持[热插拔](https://www.kernel.org/doc/Documentation/cpu-hotplug.txt)的 CPU(你可以在 [cpumasks](https://0xax.gitbooks.io/linux-insides/content/Concepts/cpumask.html) 部分阅读更多相关知识 ),一个`位数组`可以在 Linux 内核初始化等期间保存一组已分配的中断处理。
+头文件。正如我上面所写的,`位图`在 Linux 内核中被广泛地使用。例如,`位数组`常常用于保存一组在线/离线处理器,以便系统支持[热插拔](https://www.kernel.org/doc/Documentation/cpu-hotplug.txt)的 CPU(你可以在 [cpumasks](https://0xax.gitbooks.io/linux-insides/content/Concepts/cpumask.html) 部分阅读更多相关知识 ),一个位数组(bit array)可以在 Linux 内核初始化等期间保存一组已分配的[中断处理](https://en.wikipedia.org/wiki/Interrupt_request_%28PC_architecture%29)。
 
-因此,本部分的主要目的是了解位数组是如何在 Linux 内核中实现的。让我们现在开始吧。
+因此,本部分的主要目的是了解位数组(bit array)是如何在 Linux 内核中实现的。让我们现在开始吧。
 
 位数组声明
 ================================================================================
 
-在我们开始查看位图操作的 `API` 之前,我们必须知道如何在 Linux 内核中声明它。有两中通用的方法声明位数组。第一种简单的声明一个位数组的方法是,定义一个 unsigned long 的数组,例如:
+在我们开始查看`位图`操作的 `API` 之前,我们必须知道如何在 Linux 内核中声明它。有两种声明位数组的通用方法。第一种简单的声明一个位数组的方法是,定义一个 `unsigned long` 的数组,例如:
 
 ```C
 unsigned long my_bitmap[8]
@@ -44,7 +38,7 @@ unsigned long my_bitmap[8]
 * `name` - 位图名称;
 * `bits` - 位图中位数;
 
-并且只是使用 `BITS_TO_LONGS(bits)` 元素展开 `unsigned long` 数组的定义。 `BITS_TO_LONGS` 宏将一个给定的位数转换为 `longs` 的个数,换言之,就是计算 `bits` 中有多少个 `8` 字节元素:
+并且只是使用 `BITS_TO_LONGS(bits)` 元素展开 `unsigned long` 数组的定义。 `BITS_TO_LONGS` 宏将一个给定的位数转换为 `long` 的个数,换言之,就是计算 `bits` 中有多少个 `8` 字节元素:
 
 ```C
 #define BITS_PER_BYTE           8
@@ -70,18 +64,18 @@ unsigned long my_bitmap[1];
 体系结构特定的位操作
 ================================================================================
 
-我们已经看了以上一对源文件和头文件,它们提供了位数组操作的 [API](https://en.wikipedia.org/wiki/Application_programming_interface)。其中重要且广泛使用的位数组 API 是体系结构特定的且位于已提及的头文件中 [arch/x86/include/asm/bitops.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h)。
+我们已经看了上面提及的一对源文件和头文件,它们提供了位数组操作的 [API](https://en.wikipedia.org/wiki/Application_programming_interface)。其中重要且广泛使用的位数组 API 是体系结构特定的且位于已提及的头文件中 [arch/x86/include/asm/bitops.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h)。
 
 首先让我们查看两个最重要的函数:
 
 * `set_bit`;
 * `clear_bit`.
 
-我认为没有必要解释这些函数的作用。从它们的名字来看,这已经很清楚了。让我们直接查看它们的实现。如果你浏览 [arch/x86/include/asm/bitops.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h) 头文件,你将会注意到这些函数中的每一个都有[原子性](https://en.wikipedia.org/wiki/Linearizability)和非原子性两种变体。在我们开始深入这些函数的实现之前,首先,我们必须了解一些有关原子操作的知识。
+我认为没有必要解释这些函数的作用。从它们的名字来看,这已经很清楚了。让我们直接查看它们的实现。如果你浏览 [arch/x86/include/asm/bitops.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h) 头文件,你将会注意到这些函数中的每一个都有[原子性](https://en.wikipedia.org/wiki/Linearizability)和非原子性两种变体。在我们开始深入这些函数的实现之前,首先,我们必须了解一些有关原子(atomic)操作的知识。
 
-简而言之,原子操作保证两个或以上的操作不会并发地执行同一数据。`x86` 体系结构提供了一系列原子指令,例如, [xchg](http://x86.renejeschke.de/html/file_module_x86_id_328.html)、[cmpxchg](http://x86.renejeschke.de/html/file_module_x86_id_41.html) 等指令。除了原子指令,一些非原子指令可以在 [lock](http://x86.renejeschke.de/html/file_module_x86_id_159.html) 指令的帮助下具有原子性。目前已经对原子操作有了充分的理解,我们可以接着探讨 `set_bit` 和 `clear_bit` 函数的实现。
+简而言之,原子操作保证两个或以上的操作不会并发地执行同一数据。`x86` 体系结构提供了一系列原子指令,例如, [xchg](http://x86.renejeschke.de/html/file_module_x86_id_328.html)、[cmpxchg](http://x86.renejeschke.de/html/file_module_x86_id_41.html) 等指令。除了原子指令,一些非原子指令可以在 [lock](http://x86.renejeschke.de/html/file_module_x86_id_159.html) 指令的帮助下具有原子性。现在你已经对原子操作有了足够的了解,我们可以接着探讨 `set_bit` 和 `clear_bit` 函数的实现。
 
-我们先考虑函数的非原子性变体。非原子性的 `set_bit` 和 `clear_bit` 的名字以双下划线开始。正如我们所知道的,所有这些函数都定义于 [arch/x86/include/asm/bitops.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h) 头文件,并且第一个函数就是 `__set_bit`:
+我们先考虑函数的非原子性(non-atomic)变体。非原子性的 `set_bit` 和 `clear_bit` 的名字以双下划线开始。正如我们所知道的,所有这些函数都定义于 [arch/x86/include/asm/bitops.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h) 头文件,并且第一个函数就是 `__set_bit`:
 
 ```C
 static inline void __set_bit(long nr, volatile unsigned long *addr)
@@ -92,25 +86,25 @@ static inline void __set_bit(long nr, volatile unsigned long *addr)
 
 正如我们所看到的,它使用了两个参数:
 
-* `nr` - 位数组中的位号(从0开始,译者注)
+* `nr` - 位数组中的位号(LCTT 译注:从 0开始)
 * `addr` - 我们需要置位的位数组地址
 
-注意,`addr` 参数使用 `volatile` 关键字定义,以告诉编译器给定地址指向的变量可能会被修改。 `__set_bit` 的实现相当简单。正如我们所看到的,它仅包含一行[内联汇编代码](https://en.wikipedia.org/wiki/Inline_assembler)。在我们的例子中,我们使用 [bts](http://x86.renejeschke.de/html/file_module_x86_id_25.html) 指令,从位数组中选出一个第一操作数(我们的例子中的 `nr`),存储选出的位的值到 [CF](https://en.wikipedia.org/wiki/FLAGS_register) 标志寄存器并设置该位(即 `nr` 指定的位置为1,译者注)。
+注意,`addr` 参数使用 `volatile` 关键字定义,以告诉编译器给定地址指向的变量可能会被修改。 `__set_bit` 的实现相当简单。正如我们所看到的,它仅包含一行[内联汇编代码](https://en.wikipedia.org/wiki/Inline_assembler)。在我们的例子中,我们使用 [bts](http://x86.renejeschke.de/html/file_module_x86_id_25.html) 指令,从位数组中选出一个第一操作数(我们的例子中的 `nr`)所指定的位,存储选出的位的值到 [CF](https://en.wikipedia.org/wiki/FLAGS_register) 标志寄存器并设置该位(LCTT 译注:即 `nr` 指定的位置为 1)。
 
-注意,我们了解了 `nr` 的用法,但这里还有一个参数 `addr` 呢!你或许已经猜到秘密就在 `ADDR`。 `ADDR` 是一个定义在同一头文件的宏,它展开为一个包含给定地址和 `+m` 约束的字符串:
+注意,我们了解了 `nr` 的用法,但这里还有一个参数 `addr` 呢!你或许已经猜到秘密就在 `ADDR`。 `ADDR` 是一个定义在同一个头文件中的宏,它展开为一个包含给定地址和 `+m` 约束的字符串:
 
 ```C
 #define ADDR				BITOP_ADDR(addr)
 #define BITOP_ADDR(x) "+m" (*(volatile long *) (x))
 ```
 
-除了 `+m` 之外,在 `__set_bit` 函数中我们可以看到其他约束。让我们查看并试图理解它们所表示的意义:
+除了 `+m` 之外,在 `__set_bit` 函数中我们可以看到其他约束。让我们查看并试着理解它们所表示的意义:
 
-* `+m` - 表示内存操作数,这里的 `+` 表明给定的操作数为输入输出操作数;
-* `I` - 表示整型常量;
+* `+m` - 表示内存操作数,这里的 `+` 表明给定的操作数为输入输出操作数;
+* `I` - 表示整型常量;
 * `r` - 表示寄存器操作数
 
-除了这些约束之外,我们也能看到 `memory` 关键字,其告诉编译器这段代码会修改内存中的变量。到此为止,现在我们看看相同的原子性变体函数。它看起来比非原子性变体更加复杂:
+除了这些约束之外,我们也能看到 `memory` 关键字,其告诉编译器这段代码会修改内存中的变量。到此为止,现在我们看看相同的原子性(atomic)变体函数。它看起来比非原子性(non-atomic)变体更加复杂:
 
 ```C
 static __always_inline void
@@ -128,7 +122,7 @@ set_bit(long nr, volatile unsigned long *addr)
 }
 ```
 
-(BITOP_ADDR 的定义为:`#define BITOP_ADDR(x) "=m" (*(volatile long *) (x))`,ORB 为字节按位或,译者注)
+(LCTT 译注:BITOP_ADDR 的定义为:`#define BITOP_ADDR(x) "=m" (*(volatile long *) (x))`,ORB 为字节按位或。)
 
 首先注意,这个函数使用了与 `__set_bit` 相同的参数集合,但额外地使用了 `__always_inline` 属性标记。 `__always_inline` 是一个定义于 [include/linux/compiler-gcc.h](https://github.com/torvalds/linux/blob/master/include/linux/compiler-gcc.h) 的宏,并且只是展开为 `always_inline` 属性:
 
@@ -136,19 +130,19 @@ set_bit(long nr, volatile unsigned long *addr)
 #define __always_inline inline __attribute__((always_inline))
 ```
 
-其意味着这个函数总是内联的,以减少 Linux 内核映像的大小。现在我们试着了解 `set_bit` 函数的实现。首先我们在 `set_bit` 函数的开头检查给定的位数量。`IS_IMMEDIATE` 宏定义于相同[头文件](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h),并展开为 gcc 内置函数的调用:
+其意味着这个函数总是内联的,以减少 Linux 内核映像的大小。现在让我们试着了解下 `set_bit` 函数的实现。首先我们在 `set_bit` 函数的开头检查给定的位的数量。`IS_IMMEDIATE` 宏定义于相同的[头文件](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h),并展开为 [gcc](https://en.wikipedia.org/wiki/GNU_Compiler_Collection) 内置函数的调用:
 
 ```C
 #define IS_IMMEDIATE(nr)		(__builtin_constant_p(nr))
 ```
 
-如果给定的参数是编译期已知的常量,`__builtin_constant_p` 内置函数则返回 `1`,其他情况返回 `0`。假若给定的位数是编译期已知的常量,我们便无须使用效率低下的 `bts` 指令去设置位。我们可以只需在给定地址指向的字节和和掩码上执行 [按位或](https://en.wikipedia.org/wiki/Bitwise_operation#OR) 操作,其字节包含给定的位,而掩码为位号高位 `1`,其他位为 0。在其他情况下,如果给定的位号不是编译期已知常量,我们便做和 `__set_bit` 函数一样的事。`CONST_MASK_ADDR` 宏:
+如果给定的参数是编译期已知的常量,`__builtin_constant_p` 内置函数则返回 `1`,其他情况返回 `0`。假若给定的位数是编译期已知的常量,我们便无须使用效率低下的 `bts` 指令去设置位。我们可以只需在给定地址指向的字节上执行 [按位或](https://en.wikipedia.org/wiki/Bitwise_operation#OR) 操作,其字节包含给定的位,掩码位数表示高位为 `1`,其他位为 0 的掩码。在其他情况下,如果给定的位号不是编译期已知常量,我们便做和 `__set_bit` 函数一样的事。`CONST_MASK_ADDR` 宏:
 
 ```C
 #define CONST_MASK_ADDR(nr, addr)	BITOP_ADDR((void *)(addr) + ((nr)>>3))
 ```
 
-展开为带有到包含给定位的字节偏移的给定地址,例如,我们拥有地址 `0x1000` 和 位号是 `0x9`。因为 `0x9` 是 `一个字节 + 一位`,所以我们的地址是 `addr + 1`:
+展开为带有到包含给定位的字节偏移的给定地址,例如,我们拥有地址 `0x1000` 和位号 `0x9`。因为 `0x9` 代表 `一个字节 + 一位`,所以我们的地址是 `addr + 1`:
 
 ```python
 >>> hex(0x1000 + (0x9 >> 3))
@@ -166,7 +160,7 @@ set_bit(long nr, volatile unsigned long *addr)
 '0b10'
 ```
 
-最后,我们应用 `按位或` 运算到这些变量上面,因此,假如我们的地址是 `0x4097` ,并且我们需要置位号为 `9` 的位 为 1:
+最后,我们应用 `按位或` 运算到这些变量上面,因此,假如我们的地址是 `0x4097` ,并且我们需要置位号为 `9` 的位为 1:
 
 ```python
 >>> bin(0x4097)
@@ -175,11 +169,11 @@ set_bit(long nr, volatile unsigned long *addr)
 '0b100010'
 ```
 
-`第 9 位` 将会被置位。(这里的 9 是从 0 开始计数的,比如0010,按照作者的意思,其中的 1 是第 1 位,译者注)
+`第 9 位` 将会被置位。(LCTT 译注:这里的 9 是从 0 开始计数的,比如0010,按照作者的意思,其中的 1 是第 1 位)
 
 注意,所有这些操作使用 `LOCK_PREFIX` 标记,其展开为 [lock](http://x86.renejeschke.de/html/file_module_x86_id_159.html) 指令,保证该操作的原子性。
 
-正如我们所知,除了 `set_bit` 和 `__set_bit` 操作之外,Linux 内核还提供了两个功能相反的函数,在原子性和非原子性的上下文中清位。它们为 `clear_bit` 和 `__clear_bit`。这两个函数都定义于同一个[头文件](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h) 并且使用相同的参数集合。不仅参数相似,一般而言,这些函数与  `set_bit` 和 `__set_bit` 也非常相似。让我们查看非原子性 `__clear_bit` 的实现吧: 
+正如我们所知,除了 `set_bit` 和 `__set_bit` 操作之外,Linux 内核还提供了两个功能相反的函数,在原子性和非原子性的上下文中清位。它们是 `clear_bit` 和 `__clear_bit`。这两个函数都定义于同一个[头文件](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h) 并且使用相同的参数集合。不仅参数相似,一般而言,这些函数与  `set_bit` 和 `__set_bit` 也非常相似。让我们查看非原子性 `__clear_bit` 的实现吧: 
 
 ```C
 static inline void __clear_bit(long nr, volatile unsigned long *addr)
@@ -188,7 +182,7 @@ static inline void __clear_bit(long nr, volatile unsigned long *addr)
 }
 ```
 
-没错,正如我们所见,`__clear_bit` 使用相同的参数集合,并包含极其相似的内联汇编代码块。它仅仅使用 [btr](http://x86.renejeschke.de/html/file_module_x86_id_24.html) 指令替换 `bts`。正如我们从函数名所理解的一样,通过给定地址,它清除了给定的位。`btr` 指令表现得像 `bts`(原文这里为 btr,可能为笔误,修正为 bts,译者注)。该指令选出第一操作数指定的位,存储它的值到 `CF` 标志寄存器,并且清楚第二操作数指定的位数组中的对应位。
+没错,正如我们所见,`__clear_bit` 使用相同的参数集合,并包含极其相似的内联汇编代码块。它只是使用 [btr](http://x86.renejeschke.de/html/file_module_x86_id_24.html) 指令替换了 `bts`。正如我们从函数名所理解的一样,通过给定地址,它清除了给定的位。`btr` 指令表现得像 `bts`(LCTT 译注:原文这里为 btr,可能为笔误,修正为 bts)。该指令选出第一操作数所指定的位,存储它的值到 `CF` 标志寄存器,并且清除第二操作数指定的位数组中的对应位。
 
 `__clear_bit` 的原子性变体为 `clear_bit`:
 
@@ -208,11 +202,11 @@ clear_bit(long nr, volatile unsigned long *addr)
 }
 ```
 
-并且正如我们所看到的,它与 `set_bit` 非常相似,同时只包含了两处差异。第一处差异为 `clear_bit` 使用 `btr` 指令来清位,而 `set_bit` 使用 `bts` 指令来置位。第二处差异为 `clear_bit` 使用否定的位掩码和 `按位与` 在给定的字节上置位,而 `set_bit` 使用 `按位或` 指令。
+并且正如我们所看到的,它与 `set_bit` 非常相似,只有两处不同。第一处差异为 `clear_bit` 使用 `btr` 指令来清位,而 `set_bit` 使用 `bts` 指令来置位。第二处差异为 `clear_bit` 使用否定的位掩码和 `按位与` 在给定的字节上置位,而 `set_bit` 使用 `按位或` 指令。
 
-到此为止,我们可以在任何位数组置位和清位了,并且能够转到位掩码上的其他操作。
+到此为止,我们可以在任意位数组置位和清位了,我们将看看位掩码上的其他操作。
 
-在 Linux 内核位数组上最广泛使用的操作是设置和清除位,但是除了这两个操作外,位数组上其他操作也是非常有用的。Linux 内核里另一种广泛使用的操作是知晓位数组中一个给定的位是否被置位。我们能够通过 `test_bit` 宏的帮助实现这一功能。这个宏定义于 [arch/x86/include/asm/bitops.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h) 头文件,并展开为 `constant_test_bit` 或 `variable_test_bit` 的调用,这要取决于位号。
+在 Linux 内核中对位数组最广泛使用的操作是设置和清除位,但是除了这两个操作外,位数组上其他操作也是非常有用的。Linux 内核里另一种广泛使用的操作是知晓位数组中一个给定的位是否被置位。我们能够通过 `test_bit` 宏的帮助实现这一功能。这个宏定义于 [arch/x86/include/asm/bitops.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h) 头文件,并根据位号分别展开为 `constant_test_bit` 或 `variable_test_bit` 调用。
 
 ```C
 #define test_bit(nr, addr)			\
@@ -221,7 +215,7 @@ clear_bit(long nr, volatile unsigned long *addr)
 	 : variable_test_bit((nr), (addr)))
 ```
 
-因此,如果 `nr` 是编译期已知常量,`test_bit` 将展开为 `constant_test_bit` 函数的调用,而其他情况则为 `variable_test_bit`。现在让我们看看这些函数的实现,我们从 `variable_test_bit` 开始看起:  
+因此,如果 `nr` 是编译期已知常量,`test_bit` 将展开为 `constant_test_bit` 函数的调用,而其他情况则为 `variable_test_bit`。现在让我们看看这些函数的实现,让我们从 `variable_test_bit` 开始看起:  
 
 ```C
 static inline int variable_test_bit(long nr, volatile const unsigned long *addr)
@@ -237,7 +231,7 @@ static inline int variable_test_bit(long nr, volatile const unsigned long *addr)
 }
 ```
 
-`variable_test_bit` 函数调用了与 `set_bit` 及其他函数使用的相似的参数集合。我们也可以看到执行 [bt](http://x86.renejeschke.de/html/file_module_x86_id_22.html) 和 [sbb](http://x86.renejeschke.de/html/file_module_x86_id_286.html) 指令的内联汇编代码。`bt` 或 `bit test` 指令从第二操作数指定的位数组选出第一操作数指定的一个指定位,并且将该位的值存进标志寄存器的 [CF](https://en.wikipedia.org/wiki/FLAGS_register) 位。第二个指令 `sbb` 从第二操作数中减去第一操作数,再减去 `CF` 的值。因此,这里将一个从给定位数组中的给定位号的值写进标志寄存器的 `CF` 位,并且执行 `sbb` 指令计算: `00000000 - CF`,并将结果写进 `oldbit` 变量。
+`variable_test_bit` 函数使用了与 `set_bit` 及其他函数使用的相似的参数集合。我们也可以看到执行 [bt](http://x86.renejeschke.de/html/file_module_x86_id_22.html) 和 [sbb](http://x86.renejeschke.de/html/file_module_x86_id_286.html) 指令的内联汇编代码。`bt` (或称 `bit test`)指令从第二操作数指定的位数组选出第一操作数指定的一个指定位,并且将该位的值存进标志寄存器的 [CF](https://en.wikipedia.org/wiki/FLAGS_register) 位。第二个指令 `sbb` 从第二操作数中减去第一操作数,再减去 `CF` 的值。因此,这里将一个从给定位数组中的给定位号的值写进标志寄存器的 `CF` 位,并且执行 `sbb` 指令计算: `00000000 - CF`,并将结果写进 `oldbit` 变量。
 
 `constant_test_bit` 函数做了和我们在 `set_bit` 所看到的一样的事:
 
@@ -251,7 +245,7 @@ static __always_inline int constant_test_bit(long nr, const volatile unsigned lo
 
 它生成了一个位号对应位为高位 `1`,而其他位为 `0` 的字节(正如我们在 `CONST_MASK` 所看到的),并将 [按位与](https://en.wikipedia.org/wiki/Bitwise_operation#AND) 应用于包含给定位号的字节。
 
-下一广泛使用的位数组相关操作是改变一个位数组中的位。为此,Linux 内核提供了两个辅助函数:
+下一个被广泛使用的位数组相关操作是改变一个位数组中的位。为此,Linux 内核提供了两个辅助函数:
 
 * `__change_bit`;
 * `change_bit`.
@@ -274,7 +268,7 @@ static inline void __change_bit(long nr, volatile unsigned long *addr)
 1
 ```
 
- `__change_bit` 的原子版本为 `change_bit` 函数:
+`__change_bit` 的原子版本为 `change_bit` 函数:
 
 ```C
 static inline void change_bit(long nr, volatile unsigned long *addr)
@@ -291,14 +285,14 @@ static inline void change_bit(long nr, volatile unsigned long *addr)
 }
 ```
 
-它和 `set_bit` 函数很相似,但也存在两点差异。第一处差异为 `xor` 操作而不是 `or`。第二处差异为 `btc`(原文为 `bts`,为作者笔误,译者注) 而不是 `bts`。
+它和 `set_bit` 函数很相似,但也存在两点不同。第一处差异为 `xor` 操作而不是 `or`。第二处差异为 `btc`( LCTT 译注:原文为 `bts`,为作者笔误) 而不是 `bts`。
 
 目前,我们了解了最重要的体系特定的位数组操作,是时候看看一般的位图 API 了。
 
 通用位操作
 ================================================================================
 
-除了 [arch/x86/include/asm/bitops.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h)  中体系特定的 API 外,Linux 内核提供了操作位数组的通用 API。正如我们本部分开头所了解的一样,我们可以在 [include/linux/bitmap.h](https://github.com/torvalds/linux/blob/master/include/linux/bitmap.h) 头文件和* [lib/bitmap.c](https://github.com/torvalds/linux/blob/master/lib/bitmap.c)  源文件中找到它。但在查看这些源文件之前,我们先看看 [include/linux/bitops.h](https://github.com/torvalds/linux/blob/master/include/linux/bitops.h) 头文件,其提供了一系列有用的宏,让我们看看它们当中一部分。
+除了 [arch/x86/include/asm/bitops.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h) 中体系特定的 API 外,Linux 内核提供了操作位数组的通用 API。正如我们本部分开头所了解的一样,我们可以在 [include/linux/bitmap.h](https://github.com/torvalds/linux/blob/master/include/linux/bitmap.h) 头文件和 [lib/bitmap.c](https://github.com/torvalds/linux/blob/master/lib/bitmap.c)  源文件中找到它。但在查看这些源文件之前,我们先看看 [include/linux/bitops.h](https://github.com/torvalds/linux/blob/master/include/linux/bitops.h) 头文件,其提供了一系列有用的宏,让我们看看它们当中一部分。
 
 首先我们看看以下 4 个 宏:
 
@@ -307,7 +301,7 @@ static inline void change_bit(long nr, volatile unsigned long *addr)
 * `for_each_clear_bit`
 * `for_each_clear_bit_from`
 
-所有这些宏都提供了遍历位数组中某些位集合的迭代器。第一个红迭代那些被置位的位。第二个宏也是一样,但它是从某一确定位开始。最后两个宏做的一样,但是迭代那些被清位的位。让我们看看 `for_each_set_bit` 宏:
+所有这些宏都提供了遍历位数组中某些位集合的迭代器。第一个宏迭代那些被置位的位。第二个宏也是一样,但它是从某一个确定的位开始。最后两个宏做的一样,但是迭代那些被清位的位。让我们看看 `for_each_set_bit` 宏:
 
 ```C
 #define for_each_set_bit(bit, addr, size) \
@@ -316,7 +310,7 @@ static inline void change_bit(long nr, volatile unsigned long *addr)
 	     (bit) = find_next_bit((addr), (size), (bit) + 1))
 ```
 
-正如我们所看到的,它使用了三个参数,并展开为一个循环,该循环从作为 `find_first_bit` 函数返回结果的第一个置位开始到最后一个置位且小于给定大小为止。
+正如我们所看到的,它使用了三个参数,并展开为一个循环,该循环从作为 `find_first_bit` 函数返回结果的第一个置位开始,到小于给定大小的最后一个置位为止。
 
 除了这四个宏, [arch/x86/include/asm/bitops.h](https://github.com/torvalds/linux/blob/master/arch/x86/include/asm/bitops.h) 也提供了 `64-bit` 或 `32-bit` 变量循环的 API 等等。
 
@@ -339,7 +333,7 @@ static inline void bitmap_zero(unsigned long *dst, unsigned int nbits)
 }
 ```
 
-首先我们可以看到对 `nbits` 的检查。 `small_const_nbits` 是一个定义在同一[头文件](https://github.com/torvalds/linux/blob/master/include/linux/bitmap.h) 的宏:
+首先我们可以看到对 `nbits` 的检查。 `small_const_nbits` 是一个定义在同一个[头文件](https://github.com/torvalds/linux/blob/master/include/linux/bitmap.h) 的宏:
 
 ```C
 #define small_const_nbits(nbits) \
@@ -398,7 +392,7 @@ via: https://github.com/0xAX/linux-insides/blob/master/DataStructures/bitmap.md
 
 作者:[0xAX][a]
 译者:[cposture](https://github.com/cposture)
-校对:[校对者ID](https://github.com/校对者ID)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
 
diff --git a/published/201608/20160516 Scaling Collaboration in DevOps.md b/published/201608/20160516 Scaling Collaboration in DevOps.md
new file mode 100644
index 0000000000..e58838cd48
--- /dev/null
+++ b/published/201608/20160516 Scaling Collaboration in DevOps.md	
@@ -0,0 +1,66 @@
+DevOps 的弹性合作
+=================================
+
+![](http://devops.com/wp-content/uploads/2016/05/ScalingCollaboration.jpg)
+
+那些熟悉 DevOps 的人通常认为与其说 DevOps 是一种技术不如说是一种文化。在 DevOps 的有效实践上需要一些特定的工具和经验,但是 DevOps 成功的基础在于企业内如何做好[团队和个体协作][1],从而可以让事情更快、更高效而有效的完成。
+
+大多数的 DevOps 平台和工具都是以可扩展性为设计理念的。DevOps 环境通常运行在云端,并且容易发生变化。对于DevOps 软件来说,支持实时伸缩以解决冲突和摩擦是重要的。这同样对于人的因素也是一样的,但弹性合作却是完全不同的。
+
+跨企业协同是 DevOps 成功的关键。好的代码和开发最终需要形成产品才能给用户带来价值。公司所面临的挑战是如何做到无缝衔接和尽可能的提高速度及自动化水平,而不是牺牲质量或性能。企业如何才能流水线化代码的开发和部署,同时保持维护工作的明晰、可控和合规?
+
+### 新兴趋势
+
+首先,我先提供一些背景,分享一些 [451 Research][2] 在 DevOps 及其常规应用方面获取的数据。云、敏捷和Devops 的能力在今天是非常重要的,不管是理念还是现实。451 研究公司发现采用这些东西以及容器技术的企业在不断增多,包括在生产环境中的大量使用。
+
+拥抱这些技术和方式有许多优点,比如提高灵活性和速度,降低成本,提高适应能力和可靠性,适应新的或新兴的应用。据 451 Research 称,团队也面临着一些障碍,包括缺乏熟悉其中所需的技能的人、这些新兴技术的不成熟、成本和安全问题等。
+
+在 “[Voice of the Enterprise: SDI Q4 2015 survey][2]” 报告中,451 Research 发现超过一半的受访者(57.1%)考虑他们稍晚些再采用,甚至会最后才采用这些新技术。另一方面,近半受访者(48.3 %)认为自己是率先或早期的采用者。
+
+这些普遍性的情绪也表现在对其他问题的调查中。当问起容器的执行情况时,50.3% 的人表示这根本不在他们的计划中。剩下 49.7% 的人则是在计划、试点或积极使用容器技术。近 2/3(65.1%)的人表示,他们用敏捷开发方式来开发应用,但是只有 39.6% 的人回应称他们正在积极拥抱 DevOps。然而,敏捷软件开发已经在行业内存在了多年,451 Research 注意到容器和 Devops 的采用率显著提升,这是一个新的趋势。
+
+当被问及首要的三个 IT 痛点是什么,被提及最多的是成本或预算、人员不足和遗留软件问题。随着企业向云、DevOps、和容器等转型,这些问题都需要加以解决,以及如何规划技术和有效协作。
+
+### 当前状况
+
+软件行业正处于急剧变化之中,这很大程度是由 DevOps 所推动的,它使得软件开发变得越来越横跨整个业务高度集成。软件的开发变得不再闭门造车,而越来越体现协作和社交化的功能。
+
+几年还是在小说和展板中的理念和方法迅速成熟,成为了今天推动价值的主流技术和框架。企业依靠如敏捷、精益、虚拟化、云计算、自动化和微服务等概念来简化开发,同时使工作更加有效和高效。
+
+为了适应和发展,企业需要完成一系列的关键任务。当今面临的挑战是如何加快发展的同时降低成本。团队需要消除 IT 和其他业务之间存在的障碍,并在一个由技术驱动的竞争环境中提供更多有效的战略合作。
+
+敏捷、云计算、DevOps 和容器在这个过程中起着重要的作用,而将它们连接在一起的是有效的合作。每一种技术和方法都提供了独特的优势,但真正的价值来自于团队作为一个整体能够进行规模协同,以及团队所使用的工具和平台。成功的 DevOps 的实现也需要开发和 IT 运营团队之外其他利益相关者的参与,包括安全、数据库、存储和业务队伍。
+
+### 合作即平台
+
+有一些在线的服务和平台,比如 Github 促进和增进了协作。这个在线平台的功能是一个在线代码库,但是所产生的价值远超乎存储代码。
+
+这样一个[协作平台][4]之所以有助于开发人员和团队合作,是因为它提供了一个可以分享和讨论代码和流程的社区。管理者可以监视进度和跟踪将要发布的代码。开发人员在将实验性的想法放到实际的产品环境中之前,可以在一个安全的环境中进行实验,新的想法和实验可以有效地与适当的团队进行沟通。
+
+更加敏捷的开发和 DevOps 的关键之一是允许开发人员测试一些东西并快速收集相关的反馈。目标是生产高质量的代码和功能,而不是浪费时间建立和管理基础设施或者安排更多的会议来讨论这个问题。比如 GitHub 平台,能够更有效的和可扩展的协作是因为当参与者想要进行代码审查时很方便。不需要尝试协调和安排代码审查会议,所以开发人员可以继续工作而不被打断,从而产生更大的生产力和工作满意度。
+
+Sendachi 的 Steven Anderson 指出,Github 是一个协作平台,但它也是一个和你一起工作的工具。这样意味着它不仅可以帮助协作和持续集成,还影响了代码质量。
+
+合作平台的好处之一是,大型团队的开发人员可以分解成更小的团队,可以更有效地专注于特定的组件。它还提供了诸如文件共享这样的代码之外的功能,模糊了技术和非技术的贡献,增加了协作和可见性。
+
+### 合作是关键
+
+合作的重要性不言而喻。合作是 DevOps 文化的关键,也是在当今世界能够进行敏捷开发并保持竞争优势的决定因素。执行或管理支持以及内部传道是很重要的。团队还需要拥抱文化的转变---迈向共同目标的跨职能部门的技能融合。
+
+要建立起来这样的文化,有效的合作是至关重要的。一个合作平台是弹性合作的必要组件,因为简化了生产活动,并且减少了冗余和尝试,同时还产生了更高质量的结果。
+
+--------------------------------------------------------------------------------
+
+via: http://devops.com/2016/05/16/scaling-collaboration-devops/
+
+作者:[TONY BRADLEY][a]
+译者:[Bestony](https://github.com/Bestony)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://devops.com/author/tonybsg/
+[1]: http://devops.com/2014/12/15/four-strategies-supporting-devops-collaboration/
+[2]: https://451research.com/
+[3]: https://451research.com/customer-insight-voice-of-the-enterprise-overview
+[4]: http://devops.com/events/analytics-of-collaboration-on-github/
diff --git a/translated/tech/20160518 Python 3 - An Intro to Encryption.md b/published/201608/20160518 Python 3 - An Intro to Encryption.md
similarity index 74%
rename from translated/tech/20160518 Python 3 - An Intro to Encryption.md
rename to published/201608/20160518 Python 3 - An Intro to Encryption.md
index 5c57264aa7..cb37986f4c 100644
--- a/translated/tech/20160518 Python 3 - An Intro to Encryption.md	
+++ b/published/201608/20160518 Python 3 - An Intro to Encryption.md	
@@ -1,15 +1,13 @@
 Python 3: 加密简介
 ===================================
 
-Python 3 的标准库中没什么用来解决加密的,不过却有用于处理哈希的库。在这里我们会对其进行一个简单的介绍,但重点会放在两个第三方的软件包: PyCrypto 和 cryptography 上。我们将学习如何使用这两个库,来加密和解密字符串。
-
----
+Python 3 的标准库中没多少用来解决加密的,不过却有用于处理哈希的库。在这里我们会对其进行一个简单的介绍,但重点会放在两个第三方的软件包:PyCrypto 和 cryptography 上。我们将学习如何使用这两个库,来加密和解密字符串。
 
 ### 哈希
 
-如果需要用到安全哈希算法或是消息摘要算法,那么你可以使用标准库中的 **hashlib** 模块。这个模块包含了标准的安全哈希算法,包括 SHA1,SHA224,SHA256,SHA384,SHA512 以及 RSA 的 MD5 算法。Python 的 **zlib** 模块也提供 adler32 以及 crc32 哈希函数。
+如果需要用到安全哈希算法或是消息摘要算法,那么你可以使用标准库中的 **hashlib** 模块。这个模块包含了符合 FIPS(美国联邦信息处理标准)的安全哈希算法,包括 SHA1,SHA224,SHA256,SHA384,SHA512 以及 RSA 的 MD5 算法。Python 也支持 adler32 以及 crc32 哈希函数,不过它们在 **zlib** 模块中。
 
-一个哈希最常见的用法是,存储密码的哈希值而非密码本身。当然了,使用的哈希函数需要稳健一点,否则容易被破解。另一个常见的用法是,计算一个文件的哈希值,然后将这个文件和它的哈希值分别发送。接受到文件的人可以计算文件的哈希值,检验是否与接受到的哈希值相符。如果两者相符,就说明文件在传送的过程中未经篡改。
+哈希的一个最常见的用法是,存储密码的哈希值而非密码本身。当然了,使用的哈希函数需要稳健一点,否则容易被破解。另一个常见的用法是,计算一个文件的哈希值,然后将这个文件和它的哈希值分别发送。接收到文件的人可以计算文件的哈希值,检验是否与接受到的哈希值相符。如果两者相符,就说明文件在传送的过程中未经篡改。
 
 让我们试着创建一个 md5 哈希:
 
@@ -26,8 +24,6 @@ TypeError: Unicode-objects must be encoded before hashing
 b'\x14\x82\xec\x1b#d\xf6N}\x16*+[\x16\xf4w'
 ```
 
-Let’s take a moment to break this down a bit. First off, we import **hashlib** and then we create an instance of an md5 HASH object. Next we add some text to the hash object and we get a traceback. It turns out that to use the md5 hash, you have to pass it a byte string instead of a regular string. So we try that and then call it’s **digest** method to get our hash. If you prefer the hex digest, we can do that too:
-
 让我们花点时间一行一行来讲解。首先,我们导入 **hashlib** ,然后创建一个 md5 哈希对象的实例。接着,我们向这个实例中添加一个字符串后,却得到了报错信息。原来,计算 md5 哈希时,需要使用字节形式的字符串而非普通字符串。正确添加字符串后,我们调用它的 **digest** 函数来得到哈希值。如果你想要十六进制的哈希值,也可以用以下方法:
 
 ```
@@ -35,7 +31,7 @@ Let’s take a moment to break this down a bit. First off, we import **hashlib**
 '1482ec1b2364f64e7d162a2b5b16f477'
 ```
 
-实际上,有一种精简的方法来创建哈希,下面我们看一下用这种方法创建一个 sha512 哈希:
+实际上,有一种精简的方法来创建哈希,下面我们看一下用这种方法创建一个 sha1 哈希:
 
 ```
 >>> sha = hashlib.sha1(b'Hello Python').hexdigest()
@@ -45,14 +41,11 @@ Let’s take a moment to break this down a bit. First off, we import **hashlib**
 
 可以看到,我们可以同时创建一个哈希实例并且调用其 digest 函数。然后,我们打印出这个哈希值看一下。这里我使用 sha1 哈希函数作为例子,但它不是特别安全,读者可以随意尝试其他的哈希函数。
 
-
----
-
 ### 密钥导出
 
-Python 的标准库对密钥导出支持较弱。实际上,hashlib 函数库提供的唯一方法就是 **pbkdf2_hmac** 函数。它是基于口令的密钥导出函数 PKCS#5 ,并使用 HMAC 作为伪随机函数。因为它支持加盐和迭代操作,你可以使用类似的方法来哈希你的密码。例如,如果你打算使用 SHA-256 加密方法,你将需要至少 16 个字节的盐,以及最少 100000 次的迭代操作。
+Python 的标准库对密钥导出支持较弱。实际上,hashlib 函数库提供的唯一方法就是 **pbkdf2_hmac** 函数。它是 PKCS#5 的基于口令的第二个密钥导出函数,并使用 HMAC 作为伪随机函数。因为它支持“加盐(salt)”和迭代操作,你可以使用类似的方法来哈希你的密码。例如,如果你打算使用 SHA-256 加密方法,你将需要至少 16 个字节的“盐”,以及最少 100000 次的迭代操作。
 
-简单来说,盐就是随机的数据,被用来加入到哈希的过程中,以加大破解的难度。这基本可以保护你的密码免受字典和彩虹表的攻击。
+简单来说,“盐”就是随机的数据,被用来加入到哈希的过程中,以加大破解的难度。这基本可以保护你的密码免受字典和彩虹表(rainbow table)的攻击。
 
 让我们看一个简单的例子:
 
@@ -66,9 +59,7 @@ Python 的标准库对密钥导出支持较弱。实际上,hashlib 函数库
 b'6e97bad21f6200f9087036a71e7ca9fa01a59e1d697f7e0284cd7f9b897d7c02'
 ```
 
-这里,我们用 SHA256 对一个密码进行哈希,使用了一个糟糕的盐,但经过了 100000 次迭代操作。当然,SHA 实际上并不被推荐用来创建密码的密钥。你应该使用类似 **scrypt** 的算法来替代。另一个不错的选择是使用一个叫 **bcrypt** 的第三方库。它是被专门设计出来哈希密码的。
-
----
+这里,我们用 SHA256 对一个密码进行哈希,使用了一个糟糕的盐,但经过了 100000 次迭代操作。当然,SHA 实际上并不被推荐用来创建密码的密钥。你应该使用类似 **scrypt** 的算法来替代。另一个不错的选择是使用一个叫 **bcrypt** 的第三方库,它是被专门设计出来哈希密码的。
 
 ### PyCryptodome
 
@@ -86,11 +77,11 @@ pip install pycryptodome
 pip install pycryptodomex
 ```
 
-如果你遇到了问题,可能是因为你没有安装正确的依赖包(译者注:如 python-devel),或者你的 Windows 系统需要一个编译器。如果你需要安装上的帮助或技术支持,可以访问 PyCryptodome 的[网站][1]。
+如果你遇到了问题,可能是因为你没有安装正确的依赖包(LCTT 译注:如 python-devel),或者你的 Windows 系统需要一个编译器。如果你需要安装上的帮助或技术支持,可以访问 PyCryptodome 的[网站][1]。
 
 还值得注意的是,PyCryptodome 在 PyCrypto 最后版本的基础上有很多改进。非常值得去访问它们的主页,看看有什么新的特性。
 
-### 加密字符串
+#### 加密字符串
 
 访问了他们的主页之后,我们可以看一些例子。在第一个例子中,我们将使用 DES 算法来加密一个字符串:
 
@@ -116,8 +107,7 @@ ValueError: Input strings must be a multiple of 8 in length
 b'>\xfc\x1f\x16x\x87\xb2\x93\x0e\xfcH\x02\xd59VQ'
 ```
 
-这段代码稍有些复杂,让我们一点点来看。首先需要注意的是,DES 加密使用的密钥长度为 8 个字节,这也是我们将密钥变量设置为 8 个字符的原因。而我们需要加密的字符串的长度必须是 8 的倍数,所以我们创建了一个名为 **pad** 的函数,来给一个字符串末尾添加空格,直到它的长度是 8 的倍数。然后,我们创建了一个 DES 的实例,以及我们需要加密的文本。我们还创建了一个经过填充处理的文本。我们尝试这对未经填充处理的文本进行加密,啊欧,报错了!我们需要对经过填充处理的文本进行加密,然后得到加密的字符串。
-(译者注:encrypt 函数的参数应为 byte 类型字符串,代码为:`encrypted_text = des.encrypt(padded_textpadded_text.encode('uf-8'))`)
+这段代码稍有些复杂,让我们一点点来看。首先需要注意的是,DES 加密使用的密钥长度为 8 个字节,这也是我们将密钥变量设置为 8 个字符的原因。而我们需要加密的字符串的长度必须是 8 的倍数,所以我们创建了一个名为 **pad** 的函数,来给一个字符串末尾填充空格,直到它的长度是 8 的倍数。然后,我们创建了一个 DES 的实例,以及我们需要加密的文本。我们还创建了一个经过填充处理的文本。我们尝试着对未经填充处理的文本进行加密,啊欧,报了一个 ValueError 错误!我们需要对经过填充处理的文本进行加密,然后得到加密的字符串。(LCTT 译注:encrypt 函数的参数应为 byte 类型字符串,代码为:`encrypted_text = des.encrypt(padded_text.encode('utf-8'))`)
 
 知道了如何加密,还要知道如何解密:
 
@@ -128,7 +118,7 @@ b'Python rocks!   '
 
 幸运的是,解密非常容易,我们只需要调用 des 对象的 **decrypt** 方法就可以得到我们原来的 byte 类型字符串了。下一个任务是学习如何用 RSA 算法加密和解密一个文件。首先,我们需要创建一些 RSA 密钥。
 
-### 创建 RSA 密钥
+#### 创建 RSA 密钥
 
 如果你希望使用 RSA 算法加密数据,那么你需要拥有访问 RAS 公钥和私钥的权限,否则你需要生成一组自己的密钥对。在这个例子中,我们将生成自己的密钥对。创建 RSA 密钥非常容易,所以我们将在 Python 解释器中完成。
 
@@ -148,9 +138,9 @@ b'Python rocks!   '
 
 接下来,我们通过 RSA 密钥实例的 **publickey** 方法创建我们的公钥。我们使用方法链调用 publickey 和 exportKey 方法生成公钥,同样将它写入磁盘上的文件。
 
-### 加密文件
+#### 加密文件
 
-有了私钥和公钥之后,我们就可以加密一些数据,并写入文件了。这儿有个比较标准的例子:
+有了私钥和公钥之后,我们就可以加密一些数据,并写入文件了。这里有个比较标准的例子:
 
 ```
 from Crypto.PublicKey import RSA
@@ -204,17 +194,15 @@ with open('/path/to/encrypted_data.bin', 'rb') as fobj:
 print(data)
 ```
 
-如果你认真看了上一个例子,这段代码应该很容易解析。在这里,我们先读取二进制的加密文件,然后导入私钥。注意,当你导入私钥时,需要提供一个密码,否则会出现错误。然后,我们文件中读取数据,首先是加密的会话密钥,然后是 16 字节的随机数和 16 字节的消息认证码,最后是剩下的加密的数据。
+如果你认真看了上一个例子,这段代码应该很容易解析。在这里,我们先以二进制模式读取我们的加密文件,然后导入私钥。注意,当你导入私钥时,需要提供一个密码,否则会出现错误。然后,我们文件中读取数据,首先是加密的会话密钥,然后是 16 字节的随机数和 16 字节的消息认证码,最后是剩下的加密的数据。
 
 接下来我们需要解密出会话密钥,重新创建 AES 密钥,然后解密出数据。
 
 你还可以用 PyCryptodome 库做更多的事。不过我们要接着讨论在 Python 中还可以用什么来满足我们加密解密的需求。
 
----
-
 ### cryptography 包
 
-**cryptography** 的目标是成为人类易于使用的密码学包,就像 **requests** 是人类易于使用的 HTTP 库一样。这个想法使你能够创建简单安全,易于使用的加密方案。如果有需要的话,你也可以使用一些底层的密码学基元,但这也需要你知道更多的细节,否则创建的东西将是不安全的。
+**cryptography** 的目标是成为“人类易于使用的密码学包(cryptography for humans)”,就像 **requests** 是“人类易于使用的 HTTP 库(HTTP for Humans)”一样。这个想法使你能够创建简单安全、易于使用的加密方案。如果有需要的话,你也可以使用一些底层的密码学基元,但这也需要你知道更多的细节,否则创建的东西将是不安全的。
 
 如果你使用的 Python 版本是 3.5, 你可以使用 pip 安装,如下:
 
@@ -222,7 +210,7 @@ print(data)
 pip install cryptography
 ```
 
-你会看到 cryptography 包还安装了一些依赖包(译者注:如 libopenssl-devel)。如果安装都顺利,我们就可以试着加密一些文本了。让我们使用 **Fernet** 对称加密算法,它保证了你加密的任何信息在不知道密码的情况下不能被篡改或读取。Fernet 还通过 **MultiFernet** 支持密钥轮换。下面让我们看一个简单的例子:
+你会看到 cryptography 包还安装了一些依赖包(LCTT 译注:如 libopenssl-devel)。如果安装都顺利,我们就可以试着加密一些文本了。让我们使用 **Fernet** 对称加密算法,它保证了你加密的任何信息在不知道密码的情况下不能被篡改或读取。Fernet 还通过 **MultiFernet** 支持密钥轮换。下面让我们看一个简单的例子:
 
 ```
 >>> from cryptography.fernet import Fernet
@@ -242,9 +230,8 @@ b'My super secret message'
 
 首先我们需要导入 Fernet,然后生成一个密钥。我们输出密钥看看它是什么样儿。如你所见,它是一个随机的字节串。如果你愿意的话,可以试着多运行 **generate_key** 方法几次,生成的密钥会是不同的。然后我们使用这个密钥生成 Fernet 密码实例。
 
-现在我们有了用来加密和解密消息的密码。下一步是创建一个需要加密的消息,然后使用 **encrypt** 方法对它加密。我打印出加密的文本,然后你可以看到你不再能够读懂它。为了**解密**出我们的秘密消息,我们只需调用 decrypt 方法,并传入加密的文本作为参数。结果就是我们得到了消息字节串形式的纯文本。
+现在我们有了用来加密和解密消息的密码。下一步是创建一个需要加密的消息,然后使用 **encrypt** 方法对它加密。我打印出加密的文本,然后你可以看到你再也读不懂它了。为了解密出我们的秘密消息,我们只需调用 **decrypt** 方法,并传入加密的文本作为参数。结果就是我们得到了消息字节串形式的纯文本。
 
----
 
 ### 小结
 
@@ -268,7 +255,7 @@ via: http://www.blog.pythonlibrary.org/2016/05/18/python-3-an-intro-to-encryptio
 
 作者:[Mike][a]
 译者:[Cathon](https://github.com/Cathon)
-校对:[校对者ID](https://github.com/校对者ID)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
 
diff --git a/translated/tech/20160525 Getting Started with Python Programming and Scripting in Linux – Part 1.md b/published/201608/20160525 Getting Started with Python Programming and Scripting in Linux – Part 1.md
similarity index 76%
rename from translated/tech/20160525 Getting Started with Python Programming and Scripting in Linux – Part 1.md
rename to published/201608/20160525 Getting Started with Python Programming and Scripting in Linux – Part 1.md
index 24df8ca66b..a0caa58efa 100644
--- a/translated/tech/20160525 Getting Started with Python Programming and Scripting in Linux – Part 1.md	
+++ b/published/201608/20160525 Getting Started with Python Programming and Scripting in Linux – Part 1.md	
@@ -1,21 +1,19 @@
-Linux 平台下 Python 脚本编程入门 – Part 1
+Linux 平台下 Python 脚本编程入门(一)
 ===============================================================================
 
-
 众所周知,系统管理员需要精通一门脚本语言,而且招聘机构列出的职位需求上也会这么写。大多数人会认为 Bash (或者其他的 shell 语言)用起来很方便,但一些强大的语言(比如 Python)会给你带来一些其它的好处。
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/05/Learn-Python-Programming-Scripting-in-Linux.png)
-> 在 Linux 中学习 Python 脚本编程
+
+*在 Linux 中学习 Python 脚本编程*
 
 首先,我们会使用 Python 的命令行工具,还会接触到 Python 的面向对象特性(这篇文章的后半部分会谈到它)。
 
-最后,学习 Python
- 可以助力于你在[桌面应用开发][2]及[数据科学领域][3]的事业。
+学习 Python 可以助力于你在[桌面应用开发][2]及[数据科学领域][3]的职业发展。
 
-容易上手,广泛使用,拥有海量“开箱即用”的模块(它是一组包含 Python 声明的外部文件),Python 理所当然地成为了美国计算机专业大学生在一年级时所上的程序设计课所用语言的不二之选。
+容易上手,广泛使用,拥有海量“开箱即用”的模块(它是一组包含 Python 语句的外部文件),Python 理所当然地成为了美国计算机专业大学生在一年级时所上的程序设计课所用语言的不二之选。
 
-在这个由两篇文章构成的系列中,我们将回顾 Python
-的基础部分,希望初学编程的你能够将这篇实用的文章作为一个编程入门的跳板,和日后使用 Python 时的一篇快速指引。
+在这个由两篇文章构成的系列中,我们将回顾 Python 的基础部分,希望初学编程的你能够将这篇实用的文章作为一个编程入门的跳板,和日后使用 Python 时的一篇快速指引。
 
 ### Linux 中的 Python
 
@@ -33,7 +31,8 @@ $ python3
 ```
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/05/Running-Python-Commands-on-Linux.png)
-> 在 Linux 中运行 Python 命令
+
+*在 Linux 中运行 Python 命令*
 
 如果你希望在键入 `python` 时使用 Python 3.x 而不是 2.x,你可以像下面一样更改对应的符号链接:
 
@@ -44,11 +43,12 @@ $ ln -s python3.2 python # Choose the Python 3.x binary here
 ```
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/05/Remove-Python-2-and-Use-Python-3.png)
-> 删除 Python 2,使用 Python 3
+
+*删除 Python 2,使用 Python 3*
 
 顺便一提,有一点需要注意:尽管 Python 2.x 仍旧被使用,但它并不会被积极维护。因此,你可能要考虑像上面指示的那样来切换到 3.x。2.x 和 3.x 的语法有一些不同,我们会在这个系列文章中使用后者。
 
-另一个在 Linux 中使用 Python 的方法是通过 IDLE (the Python Integrated Development Environment),一个为编写 Python 代码而生的图形用户界面。在安装它之前,你最好查看一下适用于你的 Linux 发行版的 IDLE 可用版本。
+另一个在 Linux 中使用 Python 的方法是通过 IDLE (the Python Integrated Development Environment),这是一个为编写 Python 代码而生的图形用户界面。在安装它之前,你最好查看一下适用于你的 Linux 发行版的 IDLE 可用版本。
 
 ```
 # aptitude search idle     [Debian 及其衍生发行版]
@@ -68,23 +68,24 @@ $ sudo aptitude install idle-python3.2    # I'm using Linux Mint 13
 
 1. 轻松打开外部文件 (File → Open);
 
-![](http://www.tecmint.com/wp-content/uploads/2016/05/Python-Shell.png)
-> Python Shell
+	![](http://www.tecmint.com/wp-content/uploads/2016/05/Python-Shell.png)
+	
+	*Python Shell*
 
 2. 复制 (`Ctrl + C`) 和粘贴 (`Ctrl + V`) 文本;
 3. 查找和替换文本;
-4. 显示可能的代码补全(一个在其他 IDE 里可能叫做 Intellisense 或者 Autocompletion 的功能);
+4. 显示可能的代码补全(一个在其他 IDE 里可能叫做“智能感知”或者“自动补完”的功能);
 5. 更改字体和字号,等等。
 
-最厉害的是,你可以用 IDLE 创建桌面工程。
+最厉害的是,你可以用 IDLE 创建桌面应用。
 
 我们在这两篇文章中不会开发桌面应用,所以你可以根据喜好来选择 IDLE 或 Python shell 去运行下面的例子。
 
 ### Python 中的基本运算
 
-就像你预料的那样,你能够直接进行算术操作(你可以在所有操作中使用足够多的括号!),还可以轻松地使用 Python 拼接字符串。
+就像你预料的那样,你能够直接进行算术操作(你可以在你的所有运算中使用足够多的括号!),还可以轻松地使用 Python 拼接字符串。
 
-你还可以将运算结果赋给一个变量,然后在屏幕上显示它。Python 有一个叫做输出 (concatenation) 的实用功能——把一串变量和/或字符串用逗号分隔,然后在 print 函数中插入,它会返回一个由你刚才提供的变量依序构成的句子:
+你还可以将运算结果赋给一个变量,然后在屏幕上显示它。Python 有一个叫做拼接 (concatenation) 的实用功能——给 print 函数提供一串用逗号分隔的变量和/或字符串,它会返回一个由你刚才提供的变量依序构成的句子:
 
 ```
 >>> a = 5
@@ -100,15 +101,16 @@ $ sudo aptitude install idle-python3.2    # I'm using Linux Mint 13
 如果你尝试在静态类型语言中(如 Java 或 C#)做这件事,它将抛出一个错误。
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/05/Learn-Python-Basic-Operations.png)
-> 学习 Python 的基本操作
+
+*学习 Python 的基本操作*
 
 ### 面向对象编程的简单介绍
 
-在面向对象编程(OOP)中,程序中的所有实体都会由对象的形式呈现,所以它们可以与其他对象交互。因此,对象拥有属性,而且大多数对象可以完成动作(这被称为对象的方法)。
+在面向对象编程(OOP)中,程序中的所有实体都会由对象的形式呈现,并且它们可以与其他对象交互。因此,对象拥有属性,而且大多数对象可以执行动作(这被称为对象的方法)。
 
 举个例子:我们来想象一下,创建一个对象“狗”。它可能拥有的一些属性有`颜色`、`品种`、`年龄`等等,而它可以完成的动作有 `叫()`、`吃()`、`睡觉()`,诸如此类。
 
-你可以看到,方法名后面会跟着一对括号,他们当中可能会包含一个或多个参数(向方法中传递的值),也有可能什么都不包含。
+你可以看到,方法名后面会跟着一对括号,括号当中可能会包含一个或多个参数(向方法中传递的值),也有可能什么都不包含。
 
 我们用 Python 的基本对象类型之一——列表来解释这些概念。
 
@@ -139,19 +141,21 @@ $ sudo aptitude install idle-python3.2    # I'm using Linux Mint 13
 >>> rockBands.pop(0)
 ```
 
-如果你输入了对象的名字,然后在后面输入了一个点,你可以按 Ctrl + 空格来显示这个对象的可用方法列表。
+如果你输入了对象的名字,然后在后面输入了一个点,你可以按 `Ctrl + space` 来显示这个对象的可用方法列表。
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/05/List-Available-Python-Methods.png)
-> 列出可用的 Python 方法
+
+*列出可用的 Python 方法*
 
 列表中含有的元素个数是它的一个属性。它通常被叫做“长度”,你可以通过向内建函数 `len` 传递一个列表作为它的参数来显示该列表的长度(顺便一提,之前的例子中提到的 print 语句,是 Python 的另一个内建函数)。
 
 如果你在 IDLE 中输入 `len`,然后跟上一个不闭合的括号,你会看到这个函数的默认语法:
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/05/Python-len-Function.png)
-> Python 的 len 函数
 
-现在我们来看看列表中的特定条目。他们也有属性和方法吗?答案是肯定的。比如,你可以将一个字符串条目装换为大写形式,并获取这个字符串所包含的字符数量。像下面这样做:
+*Python 的 len 函数*
+
+现在我们来看看列表中的特定条目。它们也有属性和方法吗?答案是肯定的。比如,你可以将一个字符串条目转换为大写形式,并获取这个字符串所包含的字符数量。像下面这样做:
 
 ```
 >>> rockBands[0].upper()
@@ -162,11 +166,11 @@ $ sudo aptitude install idle-python3.2    # I'm using Linux Mint 13
 
 ### 总结
 
-在这篇文章中,我们简要介绍了 Python,它的命令行 shell,IDLE,展示了如何执行算术运算,如何在变量中存储数据,如何使用 `print` 函数在屏幕上重新显示那些数据(无论是它们本身还是它们的一部分),还通过一个实际的例子解释了对象的属性和方法。
+在这篇文章中,我们简要介绍了 Python、它的命令行 shell、IDLE,展示了如何执行算术运算,如何在变量中存储数据,如何使用 `print` 函数在屏幕上重新显示那些数据(无论是它们本身还是它们的一部分),还通过一个实际的例子解释了对象的属性和方法。
 
 下一篇文章中,我们会展示如何使用条件语句和循环语句来实现流程控制。我们也会解释如何编写一个脚本来帮助我们完成系统管理任务。
 
-你是不是想继续学习一些有关 Python 的知识呢?敬请期待本系列的第二部分(我们会将 Python 的慷慨、脚本中的命令行工具与其他部分结合在一起),你还可以考虑购买我们的《终极 Python 编程》系列教程([这里][4]有详细信息)。
+你是不是想继续学习一些有关 Python 的知识呢?敬请期待本系列的第二部分(我们会在脚本中将 Python 和命令行工具的优点结合在一起),你还可以考虑购买我们的《终极 Python 编程》系列教程([这里][4]有详细信息)。
 
 像往常一样,如果你对这篇文章有什么问题,可以向我们寻求帮助。你可以使用下面的联系表单向我们发送留言,我们会尽快回复你。
 
@@ -176,7 +180,7 @@ via: http://www.tecmint.com/learn-python-programming-and-scripting-in-linux/
 
 作者:[Gabriel Cánepa][a]
 译者:[StdioA](https://github.com/StdioA)
-校对:[校对者ID](https://github.com/校对者ID)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
 
diff --git a/published/201608/20160531 The Anatomy of a Linux User.md b/published/201608/20160531 The Anatomy of a Linux User.md
new file mode 100644
index 0000000000..750df665f5
--- /dev/null
+++ b/published/201608/20160531 The Anatomy of a Linux User.md	
@@ -0,0 +1,54 @@
+深入理解 Linux 用戶
+================================
+
+**一些新的 GNU/Linux 用户很清楚 Linux 不是 Windows,但其他人对此则不甚了解,而最好的发行版设计者们则会谨记着这两种人的存在。**
+
+### Linux 的核心
+
+不管怎么说,Nicky 看起来都不太引人注目。她已经三十岁了,却决定在离开学校多年后回到学校学习。她在海军待了六年,后来接受了一份老友给她的新工作,想试试这份工作会不会比她在军队的工作更有前途。这种换工作的事情在战后的军事后勤处非常常见。我正是因此而认识的她。她那时是一个八个州的货车运输业中介组织的区域经理,而那会我在达拉斯跑肉品包装工具的运输。
+
+![](http://i2.wp.com/fossforce.com/wp-content/uploads/2016/05/anatomy.jpg?w=525)
+
+Nicky 和我在 2006 年成为了好朋友。她很外向,几乎每一个途经她负责的线路上的人她都乐于接触。我们经常星期五晚上相约去一家室内激光枪战中心打真人 CS。像这样一次就打三个的半小时战役对我们来说并不鲜见。或许这并不像彩弹游戏(LCTT 译注:一种军事游戏,双方以汽枪互射彩色染料弹丸,对方被击中后衣服上会留下彩色印渍即表示“被消灭”——必应词典)一样便宜,但是它很有临场感,还稍微有点恐怖游戏的感觉。某次活动的时候,她问我能否帮她维修电脑。
+
+她知道我在为了让一些贫穷的孩子能拥有他们自己的电脑而奔走,当她抱怨她的电脑很慢的时候,我开玩笑地说她可以给比尔盖茨的 401k 计划交钱了(LCTT 译注:401k 计划始于20世纪80年代初,是一种由雇员、雇主共同缴费建立起来的完全基金式的养老保险制度。此处隐喻需要购买新电脑而向比尔盖茨的微软公司付费软件费用。)。Nicky 却说这是了解 Linux 的最佳时间。
+
+她的电脑是品牌机,是个带有 Dell 19'' 显示器的 2005 年中款的华硕电脑。不幸的是,这台电脑没有好好照料,上面充斥着所能找到的各种关都关不掉的工具栏和弹窗软件。我们把电脑上的文件都做了备份之后就开始安装 Linux 了。我们一起完成了安装,并且确信她知道了如何分区。不到一个小时,她的电脑上就有了一个“金闪闪”的 PCLinuxOS 桌面。
+
+在她操作新系统时,她经常评论这个系统看起来多么漂亮。她并非随口一说;她为眼前光鲜亮丽的桌面着了魔。她说她的桌面漂亮的就像化了“彩妆”一样。这是我在安装系统期间特意设置的,我每次安装 Linux 的时候都会把它打扮的漂漂亮亮的。我希望这些桌面让每个人看起来都觉得漂亮。
+
+大概第一周左右,她通过电话和邮件问了我一些常规问题,而最主要的问题还是她想知道如何保存她 OpenOffice 文件才可以让她的同事也可以打开这些文件。教一个人使用 Linux 或者 Open/LibreOffice 的时候最重要的就是教她保存文件。大多数用户在弹出的对话框中直接点了保存,结果就用默认的开放文档格式(Open Document Format)保存了,这让他们吃了不少苦头。
+
+曾经有过这么一件事,大约一年前或者更久,一个高中生说他没有通过期末考试,因为教授不能打开包含他的论文的文件。这引来了一些读者的激烈评论,大家都不知道这件事该怪谁,这孩子没错,而他的教授,似乎也没错。
+
+我认识的一些大学教授他们每一个人都知道怎么打开 ODF 文件。另外,那个该死的微软在这方面做得真 XX 的不错,我觉得微软 Office 现在已经能打开 ODT 或者 ODF 文件了。不过我也不确定,毕竟我从 2005 年就没用过 Microsoft Office 了。
+
+甚至在过去糟糕的日子里,微软公开而悍然地通过产品绑架的方式来在企业桌面领域推行他们的软件时,我和一些微软 Office 的用户在开展业务和洽谈合作时从来没有出现过问题,因为我会提前想到可能出现的问题并且不会有侥幸心理。我会发邮件给他们询问他们正在使用的 Office 版本。这样,我就可以确保以他们能够读写的格式保存文件。
+
+说回 Nicky ,她花了很多时间学习她的 Linux 系统。我很惊奇于她的热情。
+
+当人们意识到需要抛弃所有的 Windows 的使用习惯和工具的时候,学习 Linux 系统就会很容易。甚至在告诉那些淘气的孩子们如何使用之后,再次回来检查的时候,他们都不会试图把 .exe 文件下载到桌面上或某个下载文件夹。
+
+在我们通常讨论这些文件的时候,我们也会提及关于更新的问题。长久以来我一直反对在一台机器上有多个软件安装系统和更新管理软件。以 Mint 来说,它完全禁用了 Synaptic 中的更新功能,这让我失去兴趣。但是即便对于我们这些仍然在使用 dpkg 和 apt 的老家伙们来说,睿智的脑袋也已经开始意识到命令行对新用户来说并不那么温馨而友好。
+
+我曾严正抗议并强烈谴责 Synaptic 功能上的削弱,直到它说服了我。你记得什么时候第一次使用的新打造的 Linux 发行版,并拥有了最高管理权限吗? 你记得什么时候对 Synaptic 中列出的大量软件进行过梳理吗?你记得怎样开始安装每个你发现的很酷的程序吗?你记得有多少这样的程序都是以字母"lib"开头的吗?
+
+我也曾做过那样的事。我安装又弄坏了好几次 Linux,后来我才发现那些库(lib)文件是应用程序的螺母和螺栓,而不是应用程序本身。这就是 Linux Mint 和 Ubuntu 幕后那些聪明的开发者创造了智能、漂亮和易用的应用安装器的原因。Synaptic 仍然是我们这些老玩家爱用的工具,但是对于那些在我们之后才来的新手来说,有太多的方式可以让他们安装库文件和其他类似的包。在新的安装程序中,这些文件的显示会被折叠起来,不会展示给用户。真的,这才是它应该做的。
+
+除非你要准备好了打很多支持电话。
+
+现在的 Linux 发行版中藏了很多智慧的结晶,我也很感谢这些开发者们,因为他们,我的工作变得更容易。不是每一个 Linux 新用户都像 Nicky 这样富有学习能力和热情。她对我来说就是一个“装好就行”的项目,只需要为她解答一些问题,其它的她会自己研究解决。像她这样极具学习能力和热情的用户的毕竟是少数。这样的 Linux 新人任何时候都是珍稀物种。
+
+很不错,他们都是要教自己的孩子使用 Linux 的人。
+
+--------------------------------------------------------------------------------
+
+via: http://fossforce.com/2016/05/anatomy-linux-user/
+
+作者:[Ken Starks][a]
+译者:[vim-kakali](https://github.com/vim-kakali)
+校对:[PurlingNayuki](https://github.com/PurlingNayuki), [wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://linuxlock.blogspot.com/
diff --git a/published/201608/20160601 Learn Python Control Flow and Loops to Write and Tune Shell Scripts – Part 2.md b/published/201608/20160601 Learn Python Control Flow and Loops to Write and Tune Shell Scripts – Part 2.md
new file mode 100644
index 0000000000..b2bc20848a
--- /dev/null
+++ b/published/201608/20160601 Learn Python Control Flow and Loops to Write and Tune Shell Scripts – Part 2.md	
@@ -0,0 +1,292 @@
+Linux 平台下 Python 脚本编程入门(二)
+======================================================================================
+
+在“[Linux 平台下 Python 脚本编程入门][1]”系列之前的文章里,我们向你介绍了 Python 的简介,它的命令行 shell 和 IDLE(LCTT 译注:python 自带的一个 IDE)。我们也演示了如何进行算术运算、如何在变量中存储值、还有如何打印那些值到屏幕上。最后,我们通过一个练习示例讲解了面向对象编程中方法和属性概念。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Write-Shell-Scripts-in-Python-Programming.png)
+
+*在 Python 编程中写 Linux Shell 脚本*
+
+本篇中,我们会讨论控制流(根据用户输入的信息、计算的结果,或者一个变量的当前值选择不同的动作行为)和循环(自动重复执行任务),接着应用我们目前所学东西来编写一个简单的 shell 脚本,这个脚本会显示操作系统类型、主机名、内核版本、版本号和机器硬件架构。
+
+这个例子尽管很基础,但是会帮助我们证明,比起使用一般的 bash 工具,我们通过发挥 Python 面向对象的特性来编写 shell 脚本会更简单些。
+
+换句话说,我们想从这里出发:
+
+```
+# uname -snrvm
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/05/Check-Hostname-of-Linux.png)
+
+*检查 Linux 的主机名*
+
+到
+
+![](http://www.tecmint.com/wp-content/uploads/2016/05/Check-Linux-Hostname-Using-Python-Script.png)
+
+*用 Python 脚本来检查 Linux 的主机名*
+
+或者
+
+![](http://www.tecmint.com/wp-content/uploads/2016/05/Script-to-Check-Linux-System-Information.png)
+
+*用脚本检查 Linux 系统信息*
+
+看着不错,不是吗?那我们就挽起袖子,开干吧。
+
+### Python 中的控制流
+
+如我们刚说那样,控制流允许我们根据一个给定的条件,选择不同的输出结果。在 Python 中最简单的实现就是一个 `if`/`else` 语句。
+
+基本语法是这样的:
+
+```
+if 条件:
+    # 动作 1
+else:
+    # 动作 2
+```
+
+当“条件”求值为真(true),下面的代码块就会被执行(`# 动作 1`代表的部分)。否则,else 下面的代码就会运行。
+“条件”可以是任何表达式,只要可以求得值为真或者假。
+
+举个例子:
+
+1. `1 < 3` # 真
+2. `firstName == "Gabriel"` # 对 firstName 为 Gabriel 的人是真,对其他不叫 Gabriel 的人为假
+
+- 在第一个例子中,我们比较了两个值,判断 1 是否小于 3。
+- 在第二个例子中,我们比较了 firstName(一个变量)与字符串 “Gabriel”,看在当前执行的位置,firstName 的值是否等于该字符串。
+- 条件和 else 表达式都必须跟着一个冒号(`:`)。
+- **缩进在 Python 中非常重要**。同样缩进下的行被认为是相同的代码块。
+
+请注意,`if`/`else` 表达式只是 Python 中许多控制流工具的一个而已。我们先在这里了解以下,后面会用在我们的脚本中。你可以在[官方文档][2]中学到更多工具。
+
+### Python 中的循环
+
+简单来说,一个循环就是一组指令或者表达式序列,可以按顺序一直执行,只要条件为真,或者对列表里每个项目执行一一次。
+
+Python 中最简单的循环,就是用 for 循环迭代一个给定列表的元素,或者对一个字符串从第一个字符开始到执行到最后一个字符结束。
+
+基本语句:
+
+```
+for x in example:
+	# do this
+```
+
+这里的 example 可以是一个列表或者一个字符串。如果是列表,变量 x 就代表列表中每个元素;如果是字符串,x 就代表字符串中每个字符。
+
+```
+>>> rockBands = []
+>>> rockBands.append("Roxette")
+>>> rockBands.append("Guns N' Roses")
+>>> rockBands.append("U2")
+>>> for x in rockBands:
+    	print(x)
+或
+>>> firstName = "Gabriel"
+>>> for x in firstName:
+    	print(x)
+```
+
+上面例子的输出如下图所示:
+
+![](http://www.tecmint.com/wp-content/uploads/2016/05/Learn-Loops-in-Python.png)
+
+*学习 Python 中的循环*
+
+### Python 模块
+
+很明显,必须有个办法将一系列的 Python 指令和表达式保存到文件里,然后在需要的时候取出来。
+
+准确来说模块就是这样的。比如,os 模块提供了一个到操作系统的底层的接口,可以允许我们做许多通常在命令行下执行的操作。
+
+没错,os 模块包含了许多可以用来调用的方法和属性,就如我们之前文章里讲解的那样。不过,我们需要使用 `import` 关键词导入(或者叫包含)模块到运行环境里来:
+
+```
+>>> import os
+```
+
+我们来打印出当前的工作目录:
+
+```
+>>> os.getcwd()
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/05/Learn-Python-Modules.png)
+
+*学习 Python 模块*
+
+现在,让我们把这些结合在一起(包括之前文章里讨论的概念),编写需要的脚本。
+
+### Python 脚本
+
+以一段声明文字开始一个脚本是个不错的想法,它可以表明脚本的目的、发布所依据的许可证,以及一个列出做出的修改的修订历史。尽管这主要是个人喜好,但这会让我们的工作看起来比较专业。
+
+这里有个脚本,可以输出这篇文章最前面展示的那样。脚本做了大量的注释,可以让大家可以理解发生了什么。
+
+在进行下一步之前,花点时间来理解它。注意,我们是如何使用一个 `if`/`else` 结构,判断每个字段标题的长度是否比字段本身的值还大。
+
+基于比较结果,我们用空字符去填充一个字段标题和下一个之间的空格。同时,我们使用一定数量的短线作为字段标题与其值之间的分割符。
+
+```
+#!/usr/bin/python3
+# 如果你没有安装 Python 3 ,那么修改这一行为 #!/usr/bin/python
+
+# Script name: uname.py
+# Purpose: Illustrate Python's OOP capabilities to write shell scripts more easily
+# License: GPL v3 (http://www.gnu.org/licenses/gpl.html)
+
+# Copyright (C) 2016 Gabriel Alejandro Cánepa
+# ​Facebook / Skype / G+ / Twitter / Github: gacanepa
+# Email: gacanepa (at) gmail (dot) com
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see .
+
+# REVISION HISTORY
+# DATE   	  VERSION AUTHOR    	 CHANGE DESCRIPTION
+# ---------- ------- --------------
+# 2016-05-28 1.0     Gabriel Cánepa    Initial version
+
+### 导入 os 模块
+import os
+
+### 将 os.uname() 的输出赋值给 systemInfo 变量
+### os.uname() 会返回五个字符串元组(sysname, nodename, release, version, machine)
+### 参见文档:https://docs.python.org/3.2/library/os.html#module-os
+systemInfo = os.uname()
+
+### 这是一个固定的数组,用于描述脚本输出的字段标题
+headers = ["Operating system","Hostname","Release","Version","Machine"]
+
+### 初始化索引值,用于定义每一步迭代中
+### systemInfo 和字段标题的索引
+index = 0
+
+### 字段标题变量的初始值
+caption = ""
+
+### 值变量的初始值
+values = ""
+
+### 分隔线变量的初始值
+separators = ""
+
+### 开始循环
+for item in systemInfo:
+    if len(item) < len(headers[index]):
+   	 ### 一个包含横线的字符串,横线长度等于item[index] 或 headers[index]
+   	 ### 要重复一个字符,用引号圈起来并用星号(*)乘以所需的重复次数 
+   	 separators = separators + "-" * len(headers[index]) + " "
+   	 caption = caption + headers[index] + " "
+   	 values = values + systemInfo[index] + " " * (len(headers[index]) - len(item)) + " "
+    else:
+   	 separators = separators + "-" * len(item) + " "
+   	 caption =  caption + headers[index] + " " * (len(item) - len(headers[index]) + 1)
+   	 values = values + item + " "
+    ### 索引加 1
+    index = index + 1
+### 终止循环
+
+### 输出转换为大写的变量(字段标题)名
+print(caption.upper())
+
+### 输出分隔线
+print(separators)
+
+# 输出值(systemInfo 中的项目)
+print(values)
+
+### 步骤:
+### 1) 保持该脚本为 uname.py (或任何你想要的名字)
+### 并通过如下命令给其执行权限:
+### chmod +x uname.py
+### 2) 执行它;
+### ./uname.py
+```
+
+如果你已经按照上述描述将上面的脚本保存到一个文件里,并给文件增加了执行权限,那么运行它:
+
+```
+# chmod +x uname.py
+# ./uname.py
+```
+
+如果试图运行脚本时你得到了如下的错误:
+
+```
+-bash: ./uname.py: /usr/bin/python3: bad interpreter: No such file or directory
+```
+
+这意味着你没有安装 Python3。如果那样的话,你要么安装 Python3 的包,要么替换解释器那行(如果如之前文章里概述的那样,跟着下面的步骤去更新 Python 执行文件的软连接,要特别注意并且非常小心):
+
+```
+#!/usr/bin/python3
+```
+
+为
+
+```
+#!/usr/bin/python
+```
+
+这样会通过使用已经安装好的 Python 2 去执行该脚本。
+
+**注意**:该脚本在 Python 2.x 与 Pyton 3.x 上都测试成功过了。
+
+尽管比较粗糙,你可以认为该脚本就是一个 Python 模块。这意味着你可以在 IDLE 中打开它(File → Open… → Select file):
+
+![](http://www.tecmint.com/wp-content/uploads/2016/05/Open-Python-in-IDLE.png)
+
+*在 IDLE 中打开 Python*
+
+一个包含有文件内容的新窗口就会打开。然后执行 Run → Run module(或者按 F5)。脚本的输出就会在原始的 Shell 里显示出来: 
+
+![](http://www.tecmint.com/wp-content/uploads/2016/05/Run-Python-Script.png)
+
+*执行 Python 脚本*
+
+如果你想纯粹用 bash 写一个脚本,也获得同样的结果,你可能需要结合使用 [awk][3]、[sed][4],并且借助复杂的方法来存储与获得列表中的元素(不要忘了使用 tr 命令将小写字母转为大写)。
+
+另外,在所有的 Linux 系统版本中都至少集成了一个 Python 版本(2.x 或者 3.x,或者两者都有)。你还需要依赖 shell 去完成同样的目标吗?那样你可能需要为不同的 shell 编写不同的版本。
+
+这里演示了面向对象编程的特性,它会成为一个系统管理员得力的助手。
+
+**注意**:你可以在我的 Github 仓库里获得 [这个 python 脚本][5](或者其他的)。
+
+### 总结
+
+这篇文章里,我们讲解了 Python 中控制流、循环/迭代、和模块的概念。我们也演示了如何利用 Python 中面向对象编程的方法和属性来简化复杂的 shell 脚本。
+
+你有任何其他希望去验证的想法吗?开始吧,写出自己的 Python 脚本,如果有任何问题可以咨询我们。不必犹豫,在分割线下面留下评论,我们会尽快回复你。
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/learn-python-programming-to-write-linux-shell-scripts/
+
+作者:[Gabriel Cánepa][a]
+译者:[wi-cuckoo](https://github.com/wi-cuckoo)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/gacanepa/
+[1]: http://www.tecmint.com/learn-python-programming-and-scripting-in-linux/
+[2]: http://please%20note%20that%20the%20if%20/%20else%20statement%20is%20only%20one%20of%20the%20many%20control%20flow%20tools%20available%20in%20Python.%20We%20reviewed%20it%20here%20since%20we%20will%20use%20it%20in%20our%20script%20later.%20You%20can%20learn%20more%20about%20the%20rest%20of%20the%20tools%20in%20the%20official%20docs.
+[3]: http://www.tecmint.com/use-linux-awk-command-to-filter-text-string-in-files/
+[4]: http://www.tecmint.com/sed-command-to-create-edit-and-manipulate-files-in-linux/
+[5]: https://github.com/gacanepa/scripts/blob/master/python/uname.py
+
diff --git a/published/201608/20160602 How to build and deploy a Facebook Messenger bot with Python and Flask.md b/published/201608/20160602 How to build and deploy a Facebook Messenger bot with Python and Flask.md
new file mode 100644
index 0000000000..e2191b88c1
--- /dev/null
+++ b/published/201608/20160602 How to build and deploy a Facebook Messenger bot with Python and Flask.md	
@@ -0,0 +1,392 @@
+如何用 Python 和 Flask 建立部署一个 Facebook Messenger 机器人
+==========================================================================
+
+这是我建立一个简单的 Facebook Messenger 机器人的记录。功能很简单,它是一个回显机器人,只是打印回用户写了什么。
+
+回显服务器类似于服务器的“Hello World”例子。
+
+这个项目的目的不是建立最好的 Messenger 机器人,而是让你了解如何建立一个小型机器人和每个事物是如何整合起来的。
+
+- [技术栈][1]
+- [机器人架构][2]
+- [机器人服务器][3]
+- [部署到 Heroku][4]
+- [创建 Facebook 应用][5]
+- [结论][6]
+
+### 技术栈
+
+使用到的技术栈:
+
+- [Heroku][7] 做后端主机。免费级足够这个等级的教程。回显机器人不需要任何种类的数据持久,所以不需要数据库。
+- [Python][8] 是我们选择的语言。版本选择 2.7,虽然它移植到 Pyhton 3 很容易,只需要很少的改动。
+- [Flask][9] 作为网站开发框架。它是非常轻量的框架,用在小型工程或微服务是非常完美的。
+- 最后 [Git][10] 版本控制系统用来维护代码和部署到 Heroku。
+- 值得一提:[Virtualenv][11]。这个 python 工具是用来创建清洁的 python 库“环境”的,这样你可以只安装必要的需求和最小化应用的大小。
+
+### 机器人架构
+
+Messenger 机器人是由一个响应两种请求的服务器组成的:
+
+- GET 请求被用来认证。他们与你注册的 FaceBook 认证码一同被 Messenger 发出。
+- POST 请求被用来实际的通信。典型的工作流是,机器人将通过用户发送带有消息数据的 POST 请求而建立通信,然后我们将处理这些数据,并发回我们的 POST 请求。如果这个请求完全成功(返回一个 200 OK 状态码),我们也将响应一个 200 OK 状态码给初始的 Messenger请求。
+
+这个教程应用将托管到 Heroku,它提供了一个优雅而简单的部署应用的接口。如前所述,免费级可以满足这个教程。
+
+在应用已经部署并且运行后,我们将创建一个 Facebook 应用然后连接它到我们的应用,以便 Messenger 知道发送请求到哪,这就是我们的机器人。
+
+### 机器人服务器
+
+基本的服务器代码可以在 Github 用户 [hult(Magnus Hult)][13] 的 [Chatbot][12] 项目上获取,做了一些只回显消息的代码修改和修正了一些我遇到的错误。最终版本的服务器代码如下:
+
+```
+from flask import Flask, request
+import json
+import requests
+
+app = Flask(__name__)
+
+### 这需要填写被授予的页面通行令牌(PAT)
+### 它由将要创建的 Facebook 应用提供。
+PAT = ''
+
+@app.route('/', methods=['GET'])
+def handle_verification():
+  print "Handling Verification."
+  if request.args.get('hub.verify_token', '') == 'my_voice_is_my_password_verify_me':
+    print "Verification successful!"
+    return request.args.get('hub.challenge', '')
+  else:
+    print "Verification failed!"
+    return 'Error, wrong validation token'
+
+@app.route('/', methods=['POST'])
+def handle_messages():
+  print "Handling Messages"
+  payload = request.get_data()
+  print payload
+  for sender, message in messaging_events(payload):
+    print "Incoming from %s: %s" % (sender, message)
+    send_message(PAT, sender, message)
+  return "ok"
+
+def messaging_events(payload):
+  """Generate tuples of (sender_id, message_text) from the
+  provided payload.
+  """
+  data = json.loads(payload)
+  messaging_events = data["entry"][0]["messaging"]
+  for event in messaging_events:
+    if "message" in event and "text" in event["message"]:
+      yield event["sender"]["id"], event["message"]["text"].encode('unicode_escape')
+    else:
+      yield event["sender"]["id"], "I can't echo this"
+
+
+def send_message(token, recipient, text):
+  """Send the message text to recipient with id recipient.
+  """
+
+  r = requests.post("https://graph.facebook.com/v2.6/me/messages",
+    params={"access_token": token},
+    data=json.dumps({
+      "recipient": {"id": recipient},
+      "message": {"text": text.decode('unicode_escape')}
+    }),
+    headers={'Content-type': 'application/json'})
+  if r.status_code != requests.codes.ok:
+    print r.text
+
+if __name__ == '__main__':
+  app.run()
+```
+
+让我们分解代码。第一部分是引入所需的依赖:
+
+```
+from flask import Flask, request
+import json
+import requests
+```
+
+接下来我们定义两个函数(使用 Flask 特定的 app.route 装饰器),用来处理到我们的机器人的 GET 和 POST 请求。
+
+```
+@app.route('/', methods=['GET'])
+def handle_verification():
+  print "Handling Verification."
+  if request.args.get('hub.verify_token', '') == 'my_voice_is_my_password_verify_me':
+    print "Verification successful!"
+    return request.args.get('hub.challenge', '')
+  else:
+    print "Verification failed!"
+    return 'Error, wrong validation token'
+```
+
+当我们创建 Facebook 应用时,verify_token 对象将由我们声明的 Messenger 发送。我们必须自己来校验它。最后我们返回“hub.challenge”给 Messenger。
+
+处理 POST 请求的函数更有意思一些:
+
+```
+@app.route('/', methods=['POST'])
+def handle_messages():
+  print "Handling Messages"
+  payload = request.get_data()
+  print payload
+  for sender, message in messaging_events(payload):
+    print "Incoming from %s: %s" % (sender, message)
+    send_message(PAT, sender, message)
+  return "ok"
+```
+
+当被调用时,我们抓取消息载荷,使用函数 messaging_events 来拆解它,并且提取发件人身份和实际发送的消息,生成一个可以循环处理的 python 迭代器。请注意 Messenger 发送的每个请求有可能多于一个消息。
+
+```
+def messaging_events(payload):
+  """Generate tuples of (sender_id, message_text) from the
+  provided payload.
+  """
+  data = json.loads(payload)
+  messaging_events = data["entry"][0]["messaging"]
+  for event in messaging_events:
+    if "message" in event and "text" in event["message"]:
+      yield event["sender"]["id"], event["message"]["text"].encode('unicode_escape')
+    else:
+      yield event["sender"]["id"], "I can't echo this"
+```
+
+对每个消息迭代时,我们会调用 send_message 函数,然后我们使用 Facebook Graph messages API 对 Messenger 发回 POST 请求。在这期间我们一直没有回应我们阻塞的原始 Messenger请求。这会导致超时和 5XX 错误。
+
+上述情况是我在解决遇到错误时发现的,当用户发送表情时实际上是发送的 unicode 标识符,但是被 Python 错误的编码了,最终我们发回了一些乱码。
+
+这个发回 Messenger 的 POST 请求将永远不会完成,这会导致给初始请求返回 5xx 状态码,显示服务不可用。
+
+通过使用 `encode('unicode_escape')` 封装消息,然后在我们发送回消息前用 `decode('unicode_escape')` 解码消息就可以解决。
+
+```
+def send_message(token, recipient, text):
+  """Send the message text to recipient with id recipient.
+  """
+
+  r = requests.post("https://graph.facebook.com/v2.6/me/messages",
+    params={"access_token": token},
+    data=json.dumps({
+      "recipient": {"id": recipient},
+      "message": {"text": text.decode('unicode_escape')}
+    }),
+    headers={'Content-type': 'application/json'})
+  if r.status_code != requests.codes.ok:
+    print r.text
+```
+
+### 部署到 Heroku
+
+一旦代码已经建立成我想要的样子时就可以进行下一步。部署应用。
+
+那么,该怎么做?
+
+我之前在 Heroku 上部署过应用(主要是 Rails),然而我总是遵循某种教程做的,所用的配置是创建好了的。而在本文的情况下,我就需要从头开始。
+
+幸运的是有官方 [Heroku 文档][14]来帮忙。这篇文档很好地说明了运行应用程序所需的最低限度。
+
+长话短说,我们需要的除了我们的代码还有两个文件。第一个文件是“requirements.txt”,它列出了运行应用所依赖的库。
+
+需要的第二个文件是“Procfile”。这个文件通知 Heroku 如何运行我们的服务。此外这个文件只需要一点点内容:
+
+```
+web: gunicorn echoserver:app 
+```
+
+Heroku 对它的解读是,我们的应用通过运行 echoserver.py 启动,并且应用将使用 gunicorn 作为 Web 服务器。我们使用一个额外的网站服务器是因为与性能相关,在上面的 Heroku 文档里对此解释了:
+
+> Web 应用程序并发处理传入的 HTTP 请求比一次只处理一个请求的 Web 应用程序会更有效利地用测试机的资源。由于这个原因,我们建议使用支持并发请求的 Web 服务器来部署和运行产品级服务。
+
+> Django 和 Flask web 框架提供了一个方便的内建 Web 服务器,但是这些阻塞式服务器一个时刻只能处理一个请求。如果你部署这种服务到 Heroku 上,你的测试机就会资源利用率低下,应用会感觉反应迟钝。
+
+> Gunicorn 是一个纯 Python 的 HTTP 服务器,用于 WSGI 应用。允许你在单独一个测试机内通过运行多 Python 进程的方式来并发的运行各种 Python 应用。它在性能、灵活性和配置简易性方面取得了完美的平衡。
+
+回到我们之前提到过的“requirements.txt”文件,让我们看看它如何结合 Virtualenv 工具。
+
+很多情况下,你的开发机器也许已经安装了很多 python 库。当部署应用时你不想全部加载那些库,但是辨认出你实际使用哪些库很困难。
+
+Virtualenv 可以创建一个新的空白虚拟环境,以便你可以只安装你应用所需要的库。
+
+你可以运行如下命令来检查当前安装了哪些库:
+
+```
+kostis@KostisMBP ~ $ pip freeze
+cycler==0.10.0
+Flask==0.10.1
+gunicorn==19.6.0
+itsdangerous==0.24
+Jinja2==2.8
+MarkupSafe==0.23
+matplotlib==1.5.1
+numpy==1.10.4
+pyparsing==2.1.0
+python-dateutil==2.5.0
+pytz==2015.7
+requests==2.10.0
+scipy==0.17.0
+six==1.10.0
+virtualenv==15.0.1
+Werkzeug==0.11.10
+```
+
+注意:pip 工具应该已经与 Python 一起安装在你的机器上。如果没有,查看[官方网站][15]如何安装它。
+
+现在让我们使用 Virtualenv 来创建一个新的空白环境。首先我们给我们的项目创建一个新文件夹,然后进到目录下:
+
+```
+kostis@KostisMBP projects $ mkdir echoserver
+kostis@KostisMBP projects $ cd echoserver/
+kostis@KostisMBP echoserver $
+```
+
+现在来创建一个叫做 echobot 的新环境。运行下面的 source 命令激活它,然后使用 pip freeze 检查,我们能看到现在是空的。
+
+```
+kostis@KostisMBP echoserver $ virtualenv echobot
+kostis@KostisMBP echoserver $ source echobot/bin/activate
+(echobot) kostis@KostisMBP echoserver $ pip freeze
+(echobot) kostis@KostisMBP echoserver $
+```
+
+我们可以安装需要的库。我们需要是 flask、gunicorn 和 requests,它们被安装后我们就创建 requirements.txt 文件:
+
+```
+(echobot) kostis@KostisMBP echoserver $ pip install flask
+(echobot) kostis@KostisMBP echoserver $ pip install gunicorn
+(echobot) kostis@KostisMBP echoserver $ pip install requests
+(echobot) kostis@KostisMBP echoserver $ pip freeze
+click==6.6
+Flask==0.11
+gunicorn==19.6.0
+itsdangerous==0.24
+Jinja2==2.8
+MarkupSafe==0.23
+requests==2.10.0
+Werkzeug==0.11.10
+(echobot) kostis@KostisMBP echoserver $ pip freeze > requirements.txt
+```
+
+上述完成之后,我们用 python 代码创建 echoserver.py 文件,然后用之前提到的命令创建 Procfile,我们最终的文件/文件夹如下:
+
+```
+(echobot) kostis@KostisMBP echoserver $ ls
+Procfile     echobot     echoserver.py   requirements.txt
+```
+
+我们现在准备上传到 Heroku。我们需要做两件事。第一是如果还没有安装 Heroku toolbet,就安装它(详见 [Heroku][16])。第二是通过 Heroku [网页界面][17]创建一个新的 Heroku 应用。
+
+点击右上的大加号然后选择“Create new app”。
+
+![](http://tsaprailis.com/assets/create_app.png)
+
+为你的应用选择一个名字,然后点击“Create App”。
+
+![](http://tsaprailis.com/assets/create.png)
+
+你将会重定向到你的应用的控制面板,在那里你可以找到如何部署你的应用到  Heroku 的细节说明。
+
+```
+(echobot) kostis@KostisMBP echoserver $ heroku login
+(echobot) kostis@KostisMBP echoserver $ git init
+(echobot) kostis@KostisMBP echoserver $ heroku git:remote -a <myappname>
+(echobot) kostis@KostisMBP echoserver $ git add .
+(echobot) kostis@KostisMBP echoserver $ git commit -m "Initial commit"
+(echobot) kostis@KostisMBP echoserver (master) $ git push heroku master
+...
+remote:        https://<myappname>.herokuapp.com/ deployed to Heroku
+...
+(echobot) kostis@KostisMBP echoserver (master) $ heroku config:set WEB_CONCURRENCY=3
+```
+
+如上,当你推送你的修改到 Heroku 之后,你会得到一个用于公开访问你新创建的应用的 URL。保存该 URL,下一步需要它。
+
+### 创建这个 Facebook 应用
+
+让我们的机器人可以工作的最后一步是创建这个我们将连接到其上的 Facebook 应用。Facebook 通常要求每个应用都有一个相关页面,所以我们来[创建一个][18]。
+
+接下来我们去 [Facebook 开发者专页][19],点击右上角的“My Apps”按钮并选择“Add a New App”。不要选择建议的那个,而是点击“basic setup”。填入需要的信息并点击“Create App Id”,然后你会重定向到新的应用页面。
+
+![](http://tsaprailis.com/assets/facebook_app.png)
+
+
+在 “Products” 菜单之下,点击“+ Add Product” ,然后在“Messenger”下点击“Get Started”。跟随这些步骤设置 Messenger,当完成后你就可以设置你的 webhooks 了。Webhooks 简单的来说是你的服务所用的 URL 的名称。点击 “Setup Webhooks” 按钮,并添加该 Heroku 应用的 URL (你之前保存的那个)。在校验元组中写入 ‘my_voice_is_my_password_verify_me’。你可以写入任何你要的内容,但是不管你在这里写入的是什么内容,要确保同时修改代码中 handle_verification 函数。然后勾选 “messages” 选项。
+
+![](http://tsaprailis.com/assets/webhooks.png)
+
+点击“Verify and Save” 就完成了。Facebook 将访问该 Heroku 应用并校验它。如果不工作,可以试试运行:
+
+```
+(echobot) kostis@KostisMBP heroku logs -t
+```
+
+然后看看日志中是否有错误。如果发现错误, Google 搜索一下可能是最快的解决方法。
+
+最后一步是取得页面访问元组(PAT),它可以将该 Facebook 应用于你创建好的页面连接起来。
+
+![](http://tsaprailis.com/assets/PAT.png)
+
+从下拉列表中选择你创建好的页面。这会在“Page Access Token”(PAT)下面生成一个字符串。点击复制它,然后编辑 echoserver.py 文件,将其贴入 PAT 变量中。然后在 Git 中添加、提交并推送该修改。
+
+```
+(echobot) kostis@KostisMBP echoserver (master) $ git add .
+(echobot) kostis@KostisMBP echoserver (master) $ git commit -m "Initial commit"
+(echobot) kostis@KostisMBP echoserver (master) $ git push heroku master
+```
+
+最后,在 Webhooks 菜单下再次选择你的页面并点击“Subscribe”。
+
+![](http://tsaprailis.com/assets/subscribe.png)
+
+现在去访问你的页面并建立会话:
+
+![](http://tsaprailis.com/assets/success.png)
+
+成功了,机器人回显了!
+
+注意:除非你要将这个机器人用在 Messenger 上测试,否则你就是机器人唯一响应的那个人。如果你想让其他人也试试它,到 [Facebook 开发者专页][19]中,选择你的应用、角色,然后添加你要添加的测试者。
+
+###总结
+
+这对于我来说是一个非常有用的项目,希望它可以指引你找到开始的正确方向。[官方的 Facebook 指南][20]有更多的资料可以帮你学到更多。
+
+你可以在 [Github][21] 上找到该项目的代码。
+
+如果你有任何评论、勘误和建议,请随时联系我。
+
+
+--------------------------------------------------------------------------------
+
+via: http://tsaprailis.com/2016/06/02/How-to-build-and-deploy-a-Facebook-Messenger-bot-with-Python-and-Flask-a-tutorial/
+
+作者:[Konstantinos Tsaprailis][a]
+译者:[wyangsun](https://github.com/wyangsun)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://github.com/kostistsaprailis
+[1]: http://tsaprailis.com/2016/06/02/How-to-build-and-deploy-a-Facebook-Messenger-bot-with-Python-and-Flask-a-tutorial/#tech-stack
+[2]: http://tsaprailis.com/2016/06/02/How-to-build-and-deploy-a-Facebook-Messenger-bot-with-Python-and-Flask-a-tutorial/#bot-architecture
+[3]: http://tsaprailis.com/2016/06/02/How-to-build-and-deploy-a-Facebook-Messenger-bot-with-Python-and-Flask-a-tutorial/#the-bot-server
+[4]: http://tsaprailis.com/2016/06/02/How-to-build-and-deploy-a-Facebook-Messenger-bot-with-Python-and-Flask-a-tutorial/#deploying-to-heroku
+[5]: http://tsaprailis.com/2016/06/02/How-to-build-and-deploy-a-Facebook-Messenger-bot-with-Python-and-Flask-a-tutorial/#creating-the-facebook-app
+[6]: http://tsaprailis.com/2016/06/02/How-to-build-and-deploy-a-Facebook-Messenger-bot-with-Python-and-Flask-a-tutorial/#conclusion
+[7]: https://www.heroku.com
+[8]: https://www.python.org
+[9]: http://flask.pocoo.org
+[10]: https://git-scm.com
+[11]: https://virtualenv.pypa.io/en/stable
+[12]: https://github.com/hult/facebook-chatbot-python
+[13]: https://github.com/hult
+[14]: https://devcenter.heroku.com/articles/python-gunicorn
+[15]: https://pip.pypa.io/en/stable/installing
+[16]: https://toolbelt.heroku.com
+[17]: https://dashboard.heroku.com/apps
+[18]: https://www.facebook.com/pages/create
+[19]: https://developers.facebook.com/
+[20]: https://developers.facebook.com/docs/messenger-platform/implementation
+[21]: https://github.com/kostistsaprailis/messenger-bot-tutorial
\ No newline at end of file
diff --git a/translated/tech/20160604 Smem – Reports Memory Consumption Per-Process and Per-User Basis in Linux.md b/published/201608/20160604 Smem – Reports Memory Consumption Per-Process and Per-User Basis in Linux.md
similarity index 86%
rename from translated/tech/20160604 Smem – Reports Memory Consumption Per-Process and Per-User Basis in Linux.md
rename to published/201608/20160604 Smem – Reports Memory Consumption Per-Process and Per-User Basis in Linux.md
index befcdb5d31..1554186fbf 100644
--- a/translated/tech/20160604 Smem – Reports Memory Consumption Per-Process and Per-User Basis in Linux.md	
+++ b/published/201608/20160604 Smem – Reports Memory Consumption Per-Process and Per-User Basis in Linux.md	
@@ -1,45 +1,45 @@
-Smem – Linux 下基于进程和用户的内存占用报告程序
+Smem – Linux 下基于进程和用户的内存占用报告
 ===========================================================================
 
+Linux 系统的内存管理工作中,内存使用情况的监控是十分重要的,在各种 Linux 发行版上你会找到许多这种工具。它们的工作方式多种多样,在这里,我们将会介绍如何安装和使用这样的一个名为 SMEM 的工具软件。
 
-Linux 系统的内存管理工作中,内存使用情况的监控是十分重要的,不同的 Linux 发行版可能会提供不同的工具。但是它们的工作方式多种多样,这里,我们将会介绍如何安装和使用这样的一个名为 SMEM 的工具软件。
-
-Smem 是一款命令行下的内存使用情况报告工具。和其它传统的内存报告工具不同个,它仅做这一件事情——报告 PPS(实际使用的物理内存[比例分配共享库占用的内存]),这种内存使用量表示方法对于那些在虚拟内存中的应用和库更有意义。
+Smem 是一款命令行下的内存使用情况报告工具,它能够给用户提供 Linux 系统下的内存使用的多种报告。和其它传统的内存报告工具不同的是,它有个独特的功能,可以报告 PSS(按比例占用大小:Proportional Set Size),这种内存使用量表示方法对于那些在虚拟内存中的应用和库更有意义。
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/06/Smem-Linux-Memory-Reporting-Tool.png)
->Smem – Linux 内存报告工具
 
-已有的传统工具会将目光主要集中于读取 RSS(实际使用物理内存[包含共享库占用的内存]),这种方法对于恒量那些使用物理内存方案的使用情况来说是标准方法,但是应用程序往往会高估内存的使用情况。
+*Smem – Linux 内存报告工具*
 
-PSS 从另一个侧面,为那些使用虚拟内存方案的应用和库提供了给出了确定内存“公评分担”的合理措施。
+已有的传统工具会将目光主要集中于读取 RSS(实际占用大小:Resident Set Size),这种方法是以物理内存方案来衡量使用情况的标准方法,但是往往高估了应用程序的内存的使用情况。
 
-你可以 [阅读此指南了解 (关于内存的 RSS 和 PSS)][1] Linux 系统中的内存占用。
+PSS 从另一个侧面,通过判定在虚拟内存中的应用和库所使用的“合理分享”的内存,来给出更可信的衡量结果。
+
+你可以阅读此[指南 (关于内存的 RSS 和 PSS)][1]了解 Linux 系统中的内存占用,不过现在让我们继续看看 smem 的特点。
 
 ### Smem 这一工具的特点
 
 - 系统概览列表
-- 以进程,映射和用户来显示或者是过滤
+- 以进程、映射和用户来显示或者是过滤
 - 从 /proc 文件系统中得到数据
-- 从多个数据源配置显示条目
-- 可配置输出单元和百分比
-- 易于配置列表标题和汇总
+- 从多个数据源配置显示的条目
+- 可配置输出单位和百分比
+- 易于配置列表表头和汇总
 - 从镜像文件夹或者是压缩的 tar 文件中获得数据快照
 - 内置的图表生成机制
-- 在嵌入式系统中使用轻量级的捕获工具
+- 轻量级的捕获工具,可用于嵌入式系统
 
 ### 如何安装 Smem - Linux 下的内存使用情况报告工具
 
 安装之前,需要确保满足以下的条件:
 
-- 现代内存 (版本号高于 2.6.27)
+- 现代内核 (版本号高于 2.6.27)
 - 较新的 Python 版本 (2.4 及以后版本)
 - 可选的 [matplotlib][2] 库用于生成图表
 
-对于当今的大多数的 Linux 发行版而言,内核版本和 Python 的版本都能够 满足需要,所以仅需要为生成良好的图表安装 matplotlib 库。
+对于当今的大多数的 Linux 发行版而言,内核版本和 Python 的版本都能够满足需要,所以仅需要为生成良好的图表安装 matplotlib 库。
 
 #### RHEL, CentOS 和 Fedora
 
-首先启用 [EPEL (Extra Packages for Enterprise Linux)][3] 软件源然后按照下列步骤操作:
+首先启用 [EPEL (Extra Packages for Enterprise Linux)][3] 软件源,然后按照下列步骤操作:
 
 ```
 # yum install smem python-matplotlib python-tk
@@ -59,7 +59,7 @@ $ sudo apt-get install smem python-matplotlib python-tk
 
 #### Arch Linux
 
-使用此 [AUR repository][4]。
+使用此 [AUR 仓库][4]。
 
 ### 如何使用 Smem – Linux 下的内存使用情况报告工具
 
@@ -69,7 +69,7 @@ $ sudo apt-get install smem python-matplotlib python-tk
 $ sudo smem
 ```
 
-监视 Linux 系统中的内存使用情况
+*监视 Linux 系统中的内存使用情况*
 
 ```
  PID User     Command                         Swap      USS      PSS      RSS
@@ -108,7 +108,7 @@ $ sudo smem
 ....
 ```
 
-当常规用户运行 smem,将会显示由用户启用的进程的占用情况,其中进程按照 PSS 的值升序排列。
+当普通用户运行 smem,将会显示由该用户启用的进程的占用情况,其中进程按照 PSS 的值升序排列。
 
 下面的输出为用户 “aaronkilik” 启用的进程的使用情况:
 
@@ -116,7 +116,7 @@ $ sudo smem
 $ smem
 ```
 
-监视 Linux 系统中的内存使用情况
+*监视 Linux 系统中的内存使用情况*
 
 ```
  PID User     Command                         Swap      USS      PSS      RSS
@@ -156,12 +156,13 @@ $ smem
 ...
 ```
 
-使用 smem 是还有一些参数可以选用,例如当参看整个系统的内存占用情况,运行以下的命令:
+使用 smem 时还有一些参数可以选用,例如当查看整个系统的内存占用情况,运行以下的命令:
 
 ```
 $ sudo smem -w
 ```
-监视 Linux 系统中的内存使用情况
+
+*监视 Linux 系统中的内存使用情况*
 
 ```
 Area                           Used      Cache   Noncache
@@ -178,7 +179,7 @@ free memory                 4424936    4424936          0
 $ sudo smem -u
 ```
 
-Linux 下以用户为单位监控内存占用情况
+*Linux 下以用户为单位监控内存占用情况*
 
 ```
 User     Count     Swap      USS      PSS      RSS
@@ -201,7 +202,7 @@ tecmint     64        0  1652888  1815699  2763112
 $ sudo smem -m
 ```
 
-Linux 下以映射为单位监控内存占用情况
+*Linux 下以映射为单位监控内存占用情况*
 
 ```
 Map                                       PIDs   AVGPSS      PSS
@@ -231,15 +232,15 @@ Map                                       PIDs   AVGPSS      PSS
 ....
 ```
 
-还有其它的选项用于 smem 的输出,下面将会举两个例子。
+还有其它的选项可以筛选 smem 的输出,下面将会举两个例子。
 
-要按照用户名筛选输出的信息,调用 -u 或者是 --userfilter="regex" 选项,就像下面的命令这样:
+要按照用户名筛选输出的信息,使用 -u 或者是 --userfilter="regex" 选项,就像下面的命令这样:
 
 ```
 $ sudo smem -u
 ```
 
-按照用户报告内存使用情况
+*按照用户报告内存使用情况*
 
 ```
 User     Count     Swap      USS      PSS      RSS
@@ -256,13 +257,13 @@ root        39        0   323804   353374   496552
 tecmint     64        0  1708900  1871766  2819212
 ```
 
-要按照进程名称筛选输出信息,调用 -P 或者是 --processfilter="regex" 选项,就像下面的命令这样:
+要按照进程名称筛选输出信息,使用 -P 或者是 --processfilter="regex" 选项,就像下面的命令这样:
 
 ```
 $ sudo smem --processfilter="firefox"
 ```
 
-按照进程名称报告内存使用情况
+*按照进程名称报告内存使用情况*
 
 ```
 PID User     Command                         Swap      USS      PSS      RSS
@@ -271,7 +272,7 @@ PID User     Command                         Swap      USS      PSS      RSS
  4424 tecmint  /usr/lib/firefox/firefox           0   931732   937590   961504
 ```
 
-输出的格式有时候也很重要,smem 提供了一些参数帮助您格式化内存使用报告,我们将举出几个例子。
+输出的格式有时候也很重要,smem 提供了一些帮助您格式化内存使用报告的参数,我们将举出几个例子。
 
 设置哪些列在报告中,使用 -c 或者是 --columns选项,就像下面的命令这样:
 
@@ -279,7 +280,7 @@ PID User     Command                         Swap      USS      PSS      RSS
 $ sudo smem -c "name user pss rss"
 ```
 
-按列报告内存使用情况
+*按列报告内存使用情况*
 
 ```
 Name                     User          PSS      RSS
@@ -317,7 +318,7 @@ ssh-agent                tecmint       485      992
 $ sudo smem -p
 ```
 
-按百分比报告内存使用情况
+*按百分比报告内存使用情况*
 
 ```
  PID User     Command                         Swap      USS      PSS      RSS
@@ -345,13 +346,13 @@ $ sudo smem -p
 ....
 ```
 
-下面的额命令将会在输出的最后输出一行汇总信息:
+下面的命令将会在输出的最后输出一行汇总信息:
 
 ```
 $ sudo smem -t
 ```
 
-报告内存占用合计
+*报告内存占用合计*
 
 ```
 PID User     Command                         Swap      USS      PSS      RSS
@@ -389,27 +390,29 @@ PID User     Command                         Swap      USS      PSS      RSS
 
 比如,你可以生成一张进程的 PSS 和 RSS 值的条状图。在下面的例子中,我们会生成属于 root 用户的进程的内存占用图。
 
-纵坐标为每一个进程的 PSS 和 RSS 值,横坐标为 root 用户的所有进程:
+纵坐标为每一个进程的 PSS 和 RSS 值,横坐标为 root 用户的所有进程(的 ID):
 
 ```
 $ sudo smem --userfilter="root" --bar pid -c"pss rss"
 ```
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/06/Linux-Memory-Usage-in-PSS-and-RSS-Values.png)
->Linux Memory Usage in PSS and RSS Values
 
-也可以生成进程及其 PSS 和 RSS 占用量的饼状图。以下的命令将会输出一张 root 用户的所有进程的饼状。
+*Linux Memory Usage in PSS and RSS Values*
 
-`--pie` name 意思为以各个进程名字为标签,`-s` 选项帮助以 PSS 的值排序。
+也可以生成进程及其 PSS 和 RSS 占用量的饼状图。以下的命令将会输出一张 root 用户的所有进程的饼状图。
+
+`--pie` name 意思为以各个进程名字为标签,`-s` 选项用来以 PSS 的值排序。
 
 ```
 $ sudo smem --userfilter="root" --pie name -s pss
 ```
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/06/Linux-Memory-Consumption-by-Processes.png)
->Linux Memory Consumption by Processes
 
-它们还提供了一些其它与 PSS 和 RSS 相关的字段用于图表的标签:
+*Linux Memory Consumption by Processes*
+
+除了 PSS 和 RSS ,其它的字段也可以用于图表的标签:
 
 假如需要获得帮助,非常简单,仅需要输入 `smem -h` 或者是浏览帮助页面。
 
@@ -420,16 +423,16 @@ $ sudo smem --userfilter="root" --pie name -s pss
 
 --------------------------------------------------------------------------------
 
-via: http://www.tecmint.com/smem-linux-memory-usage-per-process-per-user/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+tecmint+%28Tecmint%3A+Linux+Howto%27s+Guide%29
+via: http://www.tecmint.com/smem-linux-memory-usage-per-process-per-user/
 
 作者:[Aaron Kili][a]
 译者:[dongfengweixiao](https://github.com/dongfengweixiao)
-校对:[校对者ID](https://github.com/校对者ID)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
 
 [a]: http://www.tecmint.com/author/aaronkili/
 [1]: https://emilics.com/notebook/enblog/p871.html
 [2]: http://matplotlib.org/index.html
-[3]: http://www.tecmint.com/how-to-enable-epel-repository-for-rhel-centos-6-5/
+[3]: https://linux.cn/article-2324-1.html
 [4]: https://www.archlinux.org/packages/community/i686/smem/
diff --git a/published/201608/20160608 Implementing Mandatory Access Control with SELinux or AppArmor in Linux.md b/published/201608/20160608 Implementing Mandatory Access Control with SELinux or AppArmor in Linux.md
new file mode 100644
index 0000000000..2f77f11b9b
--- /dev/null
+++ b/published/201608/20160608 Implementing Mandatory Access Control with SELinux or AppArmor in Linux.md	
@@ -0,0 +1,255 @@
+在 Linux 上用 SELinux 或 AppArmor 实现强制访问控制(MAC)
+===========================================================================
+
+为了解决标准的“用户-组-其他/读-写-执行”权限以及[访问控制列表][1]的限制以及加强安全机制,美国国家安全局(NSA)设计出一个灵活的强制访问控制(Mandatory Access Control:MAC)方法 SELinux(Security Enhanced Linux 的缩写),来限制标准的权限之外的种种权限,在仍然允许对这个控制模型后续修改的情况下,让进程尽可能以最小权限访问或在系统对象(如文件,文件夹,网络端口等)上执行其他操作。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/SELinux-AppArmor-Security-Hardening-Linux.png)
+
+*SELinux 和 AppArmor 加固 Linux 安全*
+
+另一个流行并且被广泛使用的 MAC 是 AppArmor,相比于 SELinux 它提供更多的特性,包括一个学习模式,可以让系统“学习”一个特定应用的行为,以及通过配置文件设置限制实现安全的应用使用。
+
+在 CentOS 7 中,SELinux 合并进了内核并且默认启用强制(Enforcing)模式(下一节会介绍这方面更多的内容),与之不同的是,openSUSE 和 Ubuntu 使用的是 AppArmor 。
+
+在这篇文章中我们会解释 SELinux 和 AppArmor 的本质,以及如何在你选择的发行版上使用这两个工具之一并从中获益。
+
+### SELinux 介绍以及如何在 CentOS 7 中使用
+
+Security Enhanced Linux 可以以两种不同模式运行:
+
+- 强制(Enforcing):这种情况下,SELinux 基于 SELinux 策略规则拒绝访问,策略规则是一套控制安全引擎的规则。
+- 宽容(Permissive):这种情况下,SELinux 不拒绝访问,但如果在强制模式下会被拒绝的操作会被记录下来。
+
+SELinux 也能被禁用。尽管这不是它的一个操作模式,不过也是一种选择。但学习如何使用这个工具强过只是忽略它。时刻牢记这一点!
+
+使用 `getenforce` 命令来显示 SELinux 的当前模式。如果你想要更改模式,使用 `setenforce 0`(设置为宽容模式)或 `setenforce 1`(强制模式)。
+
+因为这些设置重启后就失效了,你需要编辑 `/etc/selinux/config` 配置文件并设置 `SELINUX` 变量为 `enforcing`、`permissive` 或 `disabled` ,保存设置让其重启后也有效:
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Enable-Disable-SELinux-Mode.png)
+
+*如何启用和禁用 SELinux 模式*
+
+还有一点要注意,如果 `getenforce` 返回 Disabled,你得编辑 `/etc/selinux/config` 配置文件为你想要的操作模式并重启。否则你无法利用 `setenforce` 设置(或切换)操作模式。
+
+`setenforce` 的典型用法之一包括在 SELinux 模式之间切换(从强制到宽容或相反)来定位一个应用是否行为不端或没有像预期一样工作。如果它在你将 SELinux 设置为宽容模式正常工作,你就可以确定你遇到的是 SELinux 权限问题。
+
+有两种我们使用 SELinux 可能需要解决的典型案例:
+
+- 改变一个守护进程监听的默认端口。
+- 给一个虚拟主机设置 /var/www/html 以外的文档根路径值。
+
+让我们用以下例子来看看这两种情况。
+
+#### 例 1:更改 sshd 守护进程的默认端口
+
+大部分系统管理员为了加强服务器安全首先要做的事情之一就是更改 SSH 守护进程监听的端口,主要是为了阻止端口扫描和外部攻击。要达到这个目的,我们要更改 `/etc/ssh/sshd_config` 中的 Port 值为以下值(我们在这里使用端口 9999 为例):
+
+```
+Port 9999
+```
+
+在尝试重启服务并检查它的状态之后,我们会看到它启动失败:
+
+```
+# systemctl restart sshd
+# systemctl status sshd
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Check-sshd-Service-Status.png)
+
+*检查 SSH 服务状态*
+
+如果我们看看 `/var/log/audit/audit.log`,就会看到 sshd 被 SELinux 阻止在端口 9999 上启动,因为它是 JBoss 管理服务的保留端口(SELinux 日志信息包含了词语“AVC”,所以应该很容易把它同其他信息区分开来):
+
+```
+# cat /var/log/audit/audit.log | grep AVC | tail -1
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Check-Linux-Audit-Logs.png)
+
+*检查 Linux 审计日志*
+
+在这种情况下大部分人可能会禁用 SELinux,但我们不这么做。我们会看到有个让 SELinux 和监听其他端口的 sshd 和谐共处的方法。首先确保你有 `policycoreutils-python` 这个包,执行:
+
+```
+# yum install policycoreutils-python
+```
+
+查看 SELinux 允许 sshd 监听的端口列表。在接下来的图片中我们还能看到端口 9999 是为其他服务保留的,所以我们暂时无法用它来运行其他服务:
+
+```
+# semanage port -l | grep ssh
+```
+
+当然我们可以给 SSH 选择其他端口,但如果我们确定我们不会使用这台机器跑任何 JBoss 相关的服务,我们就可以修改 SELinux 已存在的规则,转而给 SSH 分配那个端口:
+
+```
+# semanage port -m -t ssh_port_t -p tcp 9999
+```
+
+这之后,我们就可以用前一个 `semanage` 命令检查端口是否正确分配了,即使用 `-lC` 参数(list custom 的简称):
+
+```
+# semanage port -lC
+# semanage port -l | grep ssh
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Assign-Port-to-SSH.png)
+
+*给 SSH 分配端口*
+
+我们现在可以重启 SSH 服务并通过端口 9999 连接了。注意这个更改重启之后依然有效。
+
+#### 例 2:给一个虚拟主机设置 /var/www/html 以外的文档根路径(DocumentRoot)
+
+如果你需要用除 `/var/www/html` 以外目录作为文档根目录(DocumentRoot)[设置一个 Apache 虚拟主机][2](也就是说,比如 `/websrv/sites/gabriel/public_html`):
+
+```
+DocumentRoot “/websrv/sites/gabriel/public_html”
+```
+
+Apache 会拒绝提供内容,因为 `index.html` 已经被标记为了 `default_t SELinux` 类型,Apache 无法访问它:
+
+```
+# wget http://localhost/index.html
+# ls -lZ /websrv/sites/gabriel/public_html/index.html
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Labeled-default_t-SELinux-Type.png)
+
+*被标记为 default_t SELinux 类型*
+
+和之前的例子一样,你可以用以下命令验证这是不是 SELinux 相关的问题:
+
+```
+# cat /var/log/audit/audit.log | grep AVC | tail -1
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Check-Logs-for-SELinux-Issues.png)
+
+*检查日志确定是不是 SELinux 的问题*
+
+要将 `/websrv/sites/gabriel/public_html` 整个目录内容标记为 `httpd_sys_content_t`,执行:
+
+```
+# semanage fcontext -a -t httpd_sys_content_t "/websrv/sites/gabriel/public_html(/.*)?"
+```
+
+上面这个命令会赋予 Apache 对那个目录以及其内容的读取权限。
+
+最后,要应用这条策略(并让更改的标记立即生效),执行:
+
+```
+# restorecon -R -v /websrv/sites/gabriel/public_html
+```
+
+现在你应该可以访问这个目录了:
+
+```
+# wget http://localhost/index.html
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Access-Apache-Directory.png)
+
+*访问 Apache 目录*
+
+要获取关于 SELinux 的更多信息,参阅 Fedora 22 中的 [SELinux 用户及管理员指南][3]。
+
+### AppArmor 介绍以及如何在 OpenSUSE 和 Ubuntu 上使用它
+
+AppArmor 的操作是基于写在纯文本文件中的规则定义,该文件中含有允许权限和访问控制规则。安全配置文件用来限制应用程序如何与系统中的进程和文件进行交互。
+
+系统初始就提供了一系列的配置文件,但其它的也可以由应用程序在安装的时候设置或由系统管理员手动设置。
+
+像 SELinux 一样,AppArmor 以两种模式运行。在 enforce 模式下,应用被赋予它们运行所需要的最小权限,但在 complain 模式下 AppArmor 允许一个应用执行受限的操作并将操作造成的“抱怨”记录到日志里(`/var/log/kern.log`,`/var/log/audit/audit.log`,和其它放在 `/var/log/apparmor` 中的日志)。
+
+日志中会显示配置文件在强制模式下运行时会产生错误的记录,它们中带有 `audit` 这个词。因此,你可以在 AppArmor 的 enforce 模式下运行之前,先在 complain 模式下尝试运行一个应用并调整它的行为。
+
+可以用这个命令显示 AppArmor 的当前状态:
+
+```
+$ sudo apparmor_status
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Check-AppArmor-Status.png)
+
+*查看 AppArmor 的状态*
+
+上面的图片指明配置 `/sbin/dhclient`,`/usr/sbin/`,和 `/usr/sbin/tcpdump` 等处在 enforce 模式下(在 Ubuntu 下默认就是这样的)。
+
+因为不是所有的应用都包含相关的 AppArmor 配置,apparmor-profiles 包给其它没有提供限制的包提供了配置。默认它们配置在 complain 模式下运行,以便系统管理员能够测试并选择一个所需要的配置。
+
+我们将会利用 apparmor-profiles,因为写一份我们自己的配置已经超出了 LFCS [认证][4]的范围了。但是,由于配置都是纯文本文件,你可以查看并学习它们,为以后创建自己的配置做准备。
+
+AppArmor 配置保存在 `/etc/apparmor.d` 中。让我们来看看这个文件夹在安装 apparmor-profiles 之前和之后有什么不同:
+
+```
+$ ls /etc/apparmor.d
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/View-AppArmor-Directory-Content.png)
+
+*查看 AppArmor 文件夹内容*
+
+如果你再次执行 `sudo apparmor_status`,你会在 complain 模式看到更长的配置文件列表。你现在可以执行下列操作。
+
+将当前在 enforce 模式下的配置文件切换到 complain 模式:
+
+```
+$ sudo aa-complain /path/to/file
+```
+
+以及相反的操作(complain –> enforce):
+
+```
+$ sudo aa-enforce /path/to/file
+```
+
+上面这些例子是允许使用通配符的。举个例子:
+
+```
+$ sudo aa-complain /etc/apparmor.d/*
+```
+
+会将 `/etc/apparmor.d` 中的所有配置文件设置为 complain 模式,反之
+
+```
+$ sudo aa-enforce /etc/apparmor.d/*
+```
+
+会将所有配置文件设置为 enforce 模式。
+
+要完全禁用一个配置,在 `/etc/apparmor.d/disabled` 目录中创建一个符号链接:
+
+```
+$ sudo ln -s /etc/apparmor.d/profile.name /etc/apparmor.d/disable/
+```
+
+要获取关于 AppArmor 的更多信息,参阅[官方的 AppArmor wiki][5] 以及 [Ubuntu 提供的][6]文档。
+
+### 总结
+
+在这篇文章中我们学习了一些 SELinux 和 AppArmor 这两个著名的强制访问控制系统的基本知识。什么时候使用两者中的一个或是另一个?为了避免提高难度,你可能需要考虑专注于你选择的发行版自带的那一个。不管怎样,它们会帮助你限制进程和系统资源的访问,以提高你服务器的安全性。
+
+关于本文你有任何的问题,评论,或建议,欢迎在下方发表。不要犹豫,让我们知道你是否有疑问或评论。
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/mandatory-access-control-with-selinux-or-apparmor-linux/
+
+作者:[Gabriel Cánepa][a]
+译者:[alim0x](https://github.com/alim0x)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/gacanepa/
+[1]: http://www.tecmint.com/secure-files-using-acls-in-linux/
+[2]: http://www.tecmint.com/apache-virtual-hosting-in-centos/
+[3]: https://docs.fedoraproject.org/en-US/Fedora/22/html/SELinux_Users_and_Administrators_Guide/index.html
+[4]: http://www.tecmint.com/sed-command-to-create-edit-and-manipulate-files-in-linux/
+[5]: http://wiki.apparmor.net/index.php/Main_Page
+[6]: https://help.ubuntu.com/community/AppArmor
+
+
diff --git a/published/201608/20160614 ​Ubuntu Snap takes charge of Linux desktop and IoT software distribution.md b/published/201608/20160614 ​Ubuntu Snap takes charge of Linux desktop and IoT software distribution.md
new file mode 100644
index 0000000000..e4eae87ab2
--- /dev/null
+++ b/published/201608/20160614 ​Ubuntu Snap takes charge of Linux desktop and IoT software distribution.md	
@@ -0,0 +1,110 @@
+Ubuntu Snap 软件包接管 Linux 桌面和 IoT 软件的发行
+===========================================================================
+
+[Canonical][28] 和 [Ubuntu][29] 创始人 Mark Shuttleworth 在一次采访中说他不准备宣布 Ubuntu 的新 [ Snap 程序包格式][30]。但是就在几个月之后,很多 Linux 发行版的开发者和公司都宣布他们会把 Snap 作为通用 Linux 程序包格式。
+
+![](http://zdnet2.cbsistatic.com/hub/i/r/2016/06/14/a9b2a139-3cd4-41bf-8e10-180cb9450134/resize/770xauto/adc7d16a46167565399ecdb027dd1416/ubuntu-snap.jpg)
+
+*Linux 供应商,独立软件开发商和公司门全都采用 Ubuntu Snap 作为多种 Linux 系统的配置和更新程序包。*
+
+为什么呢?因为 Snap 能使一个单一的二进制程序包可以完美、安全地运行在任何 Linux 台式机、服务器、云或物联网设备上。据 Canonical 的 Ubuntu 客户端产品和版本负责人 Olli Ries 说:
+
+>[Snap 程序包的安全机制][1]让我们在更快的跨发行版的应用更新中打开了新的局面,因为 Snap 应用是与系统的其它部分想隔离的。用户可以安装一个 Snap 而不用担心是否会影响其他的应用程序和操作系统。
+
+当然了,如 Linux 内核的早期开发者和 CoreOS 安全维护者 Matthew Garrett 指出的那样:如果你[将 Snap 用在不安全的程序中,比如 X11 窗口系统][2],实际上您并不会获得安全性。(LCTT 译注:X11 也叫做 X Window 系统,X Window 系统 ( X11 或 X )是一种位图显示的视窗系统 。它是在 Unix 和类 Unix 操作系统 ,以及 OpenVMS 上建立图形用户界面的标准工具包和协议,并可用于几乎所有已有的现代操作系统。)
+
+Shuttleworth 同意 Garrett 的观点,但是他也说你可以控制 Snap 应用是如何与系统的其它部分如何交互的。比如,一个 web 浏览器可以包含在一个安全的 Snap 程序包中,这个 Snap 使用 Ubuntu 打包的 [openssl][3] TLS 和 SSL 库。除此之外,即使有些东西影响到了浏览器实例内部,也不能进入到底层的操作系统。
+
+很多公司也这样认为。戴尔、三星、Mozilla、[krita][7](LCTT 译注:Krita 是一个位图形编辑软件,KOffice 套装的一部份。包含一个绘画程式和照片编辑器,Krita 是自由软件,并根据GNU通用公共许可证发布)、[Mycroft][8](LCTT 译注:Mycroft 是一个开源AI智能家居平台,配置 Raspberry Pi 2 和 Arduino 控制器),以及 [Horizon Computing][9](LCTT 译注:为客户提供优质的硬件架构为其运行云平台)都将使用 Snap。Arch Linux、Debain、Gentoo 和 OpenWrt 开发团队也已经拥抱了 Snap,也会把 Snap 加入到他们各自的发行版中。
+
+Snap 包又叫做“Snaps”,现在已经可以原生的运行在 Arch、Debian、Fedora、Kubuntu、Lubuntu、Ubuntu GNOME、Ubuntu Kylin、Ubuntu MATE、Ubuntu Unity 和 Xubuntu 之上。 Snap 也在 CentOS、Elementary、Gentoo、Mint、OpenSUSE 和 Red Hat Enterprise Linux (RHEL) 上取得了验证,并且也很容易运行在其他 Linux 发行版上。 
+
+这些发行版正在使用 Snaps,Shuttleworth 声称:“Snaps 为每个 Linux 台式机、服务器、设备和云机器带来了很多应用程序,在让用户使用最好的应用的同时也给了用户选择发行版的自由。”
+
+这些发行版共同代表了 Linux 桌面、服务器和云系统发行版的主流。为什么它们从现有的软件包管理系统换了过来呢? Arch Linux 的贡献者 Tim Jester-Pfadt 解释说,“Snaps 最棒的一点是它支持先锐和测试通道,这可以让用户选择使用预发布的开发者版本或跟着最新的稳定版本。”
+
+除过这些 Linux 分支,独立软件开发商也将会因为 Snap 很好的简化了第三方 Linux 应用程序分发和安全维护问题而拥抱 Snap。例如,[文档基金会][14]也将会让流行的开源办公套件 [LibreOffice][15] 支持 Snap 程序包。
+
+文档基金会的联合创始人 Thorsten Behrens 这样说:
+
+> 我们的目标是尽可能的使 LibreOffice 能被大多数人更容易使用。Snap 使我们的用户能够在不同的桌面系统和发行版上更快捷、更容易、持续地获取最新的 LibreOffice 版本。更好的是,它也会帮助我们的发布工程师最终从周而复始的、自产的、陈旧的 Linux 开发解决方案中解放出来,很多东西都可以一同维护了。
+
+Mozilla 的 Firefix 副总裁 Nick Nguyen 在该声明中提到:
+
+> 我们力求为用户提供良好的使用体验,并且使火狐浏览器能够在更多平台、设备和操作系统上运行。随着引入 Snaps ,对火狐浏览器的持续优化成为可能,使它可以为 Linux 用户提供最新的特性。
+
+[Krita 基金会][17] (基于 KDE 的图形程序)项目领导 Boudewijn Rempt 说:
+
+> 在一个私有仓库中维护 DEB 包是复杂而耗时的。Snaps 更容易维护、打包和分发。把 Snap 放进软件商店也特别容易,这是我发布软件用过的最舒服的软件商店了。[Krita 3.0][18] 刚刚作为一个 snap 程序包发行,新版本出现时它会自动更新。
+
+不仅 Linux 桌面系统程序为 Snap 而激动。物联网(IoT)和嵌入式开发者也以双手拥抱了 Snap。 
+
+由于 Snaps 彼此隔离,带来了数据安全性,它们还可以自动更新或回滚,这对于硬件设备是极好的。多个厂商都发布了运行着 snappy 的设备(LCTT 译注:Snap 基于 snappy进行构建),这带来了一种新的带有物联网应用商店的“智能新锐”设备。Snappy 设备能够自动接收系统更新,并且连同安装在设备上的应用程序也会得到更新。
+
+据 Shuttleworth 说,戴尔公司是最早一批认识到 Snap 的巨大潜力的物联网供应商之一,也决定在他们的设备上使用 Snap 了。
+
+戴尔公司的物联网战略和合作伙伴主管 Jason Shepherd 说:“我们认为,Snaps 能解决在单一物联网网关上部署和运行多个第三方应用程序所带来的安全风险和可管理性挑战。这种可信赖的通用的应用程序格式才是戴尔真正需要的,我们的物联网解决方案合作伙伴和商业客户都对构建一个可扩展的、IT 级的、充满活力的物联网应用生态系统有极大的兴趣。”
+
+OpenWrt 的开发者 Matteo Croce 说:“这很简单, Snaps 可以在保持核心系统不变的情况下递送新的应用... Snaps 是为 OpenWrt AP 和路由器提供大量软件的最快方式。”
+
+Shuttleworth 并不认为 Snaps 会取代已经存在的 Linux 程序包比如 [RPM][19] 和 [DEB][20]。相反,他认为它们将会相辅相成。Snaps 将会与现有软件包共存。每个发行版都有其自己提供和更新核心系统及其更新的机制。Snap 为桌面系统带来的是通用的应用程序,这些应用程序不会影响到操作系统的基础。
+
+每个 Snap 都通过使用大量的内核隔离和安全机制而限制,以满足 Snap 应用的需求。谨慎的审核过程可以确保 Snap 仅仅得到其完成请求操作的权限。用户在安装 Snap 的时候也不必考虑复杂的安全问题。
+
+Snap 本质上是一个自包容的 zip 文件,能够快速地在包内执行。流行的[优麒麟][21]团队的负责人 Jack Yu 称:“Snaps 比传统的 Linux 包更容易构建,允许我们独立于操作系统解决依赖性,所以我们很容易地跨发行版为所有用户提供最好、最新的中国 Linux 应用。”
+
+由 Canonical 设计的 Snap 程序包格式由 [snapd][22] 所处理。它的开发工作放在 GitHub 上。将其移植到更多的 Linux 发行版已经被证明是很简单的,社区还在不断增长,吸引了大量具有 Linux 经验的贡献者。 
+
+Snap 程序包使用 snapcraft 工具来构建。项目官网是 [snapcraft.io][24],其上有构建 Snap 的指导和逐步指南,以及给项目开发者和使用者的文档。Snap 能够基于现有的发行版程序包构建,但通常使用源代码来构建,以获得优化和减小软件包大小。
+
+如果你不是 Ubuntu 的忠实粉丝或者一个专业的 Linux 开发者,你可能还不知道 Snap。未来,在任何平台上需要用 Linux 完成工作的任何人都会知道这个软件。它会成为主流,尤其是在 Linux 应用程序的安装和更新机制方面。  
+
+### 相关内容:
+
+- [Linux 专家 Matthew Garrett:Ubuntu 16.04 的新 Snap 程序包格式存在安全风险 ][25]
+- [Ubuntu Linux 16.04 ]
+- [Microsoft 和 Canonical 合作使 Ubuntu 可以在 Windows 10 上运行 ]
+
+--------------------------------------------------------------------------------
+
+via: http://www.zdnet.com/article/ubuntu-snap-takes-charge-of-linux-desktop-and-iot-software-distribution/
+
+作者:[Steven J. Vaughan-Nichols][a]
+译者:[vim-kakali](https://github.com/vim-kakali)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.zdnet.com/meet-the-team/us/steven-j-vaughan-nichols/
+[28]: http://www.canonical.com/
+[29]: http://www.ubuntu.com/
+[30]: https://insights.ubuntu.com/2016/04/13/snaps-for-classic-ubuntu/
+[1]: https://insights.ubuntu.com/2016/04/13/snaps-for-classic-ubuntu/
+[2]: http://www.zdnet.com/article/linux-expert-matthew-garrett-ubuntu-16-04s-new-snap-format-is-a-security-risk/
+[3]: https://www.openssl.org/
+[4]: http://www.dell.com/en-us/
+[5]: http://www.samsung.com/us/
+[6]: http://www.mozilla.com/
+[7]: https://krita.org/en/
+[8]: https://mycroft.ai/
+[9]: http://www.horizon-computing.com/
+[10]: https://www.archlinux.org/
+[11]: https://www.debian.org/
+[12]: https://www.gentoo.org/
+[13]: https://openwrt.org/
+[14]: https://www.documentfoundation.org/
+[15]: https://www.libreoffice.org/download/libreoffice-fresh/
+[16]: https://www.mozilla.org/en-US/firefox/new/
+[17]: https://krita.org/en/about/krita-foundation/
+[18]: https://krita.org/en/item/krita-3-0-released/
+[19]: http://rpm5.org/
+[20]: https://www.debian.org/doc/manuals/debian-faq/ch-pkg_basics.en.html
+[21]: http://www.ubuntu.com/desktop/ubuntu-kylin
+[22]: https://launchpad.net/ubuntu/+source/snapd
+[23]: https://github.com/snapcore/snapd
+[24]: http://snapcraft.io/
+[25]: http://www.zdnet.com/article/linux-expert-matthew-garrett-ubuntu-16-04s-new-snap-format-is-a-security-risk/
+[26]: http://www.zdnet.com/article/ubuntu-linux-16-04-is-here/
+[27]: http://www.zdnet.com/article/microsoft-and-canonical-partner-to-bring-ubuntu-to-windows-10/
+
+
diff --git a/translated/talk/20160615 Explanation of “Everything is a File” and Types of Files in Linux.md b/published/201608/20160615 Explanation of “Everything is a File” and Types of Files in Linux.md
similarity index 75%
rename from translated/talk/20160615 Explanation of “Everything is a File” and Types of Files in Linux.md
rename to published/201608/20160615 Explanation of “Everything is a File” and Types of Files in Linux.md
index c9a1b243ff..62379377cd 100755
--- a/translated/talk/20160615 Explanation of “Everything is a File” and Types of Files in Linux.md	
+++ b/published/201608/20160615 Explanation of “Everything is a File” and Types of Files in Linux.md	
@@ -2,15 +2,16 @@
 ====================================================================
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/05/Everything-is-a-File-in-Linux.png)
->Linux 系统中一切都是文件并有相应的文件类型
+
+*Linux 系统中一切都是文件并有相应的文件类型*
 
 在 Unix 和它衍生的比如 Linux 系统中,一切都可以看做文件。虽然它仅仅只是一个泛泛的概念,但这是事实。如果有不是文件的,那它一定是正运行的进程。
 
-要理解这点,可以举个例子,您的根目录(/) 的空间是由不同类型的 Linux 文件所占据的。当您创建一个文件或向系统传一个文件时,它在物理磁盘上占据的一些空间,可以认为是一个特定的格式(文件类型)。        
+要理解这点,可以举个例子,您的根目录(/)的空间充斥着不同类型的 Linux 文件。当您创建一个文件或向系统传输一个文件时,它会在物理磁盘上占据的一些空间,而且是一个特定的格式(文件类型)。        
 
-虽然 Linux 系统中文件和目录没有什么不同,但目录还有一个重要的功能,那就是有结构性的分组存储其它文件,以方便查找访问。所有的硬件部件都表示为文件,系统使用这些文件来与硬件通信。
+虽然 Linux 系统中文件和目录没有什么不同,但目录还有一个重要的功能,那就是有结构性的分组存储其它文件,以方便查找访问。所有的硬件组件都表示为文件,系统使用这些文件来与硬件通信。
 
-这些思想是对伟大的 Linux 财产的重要阐述,因此像文档、目录(Mac OS X 和 Windows 系统下是文件夹)、键盘、监视器、硬盘、可移动媒体设备、打印机、调制解调器、虚拟终端,还有进程间通信(IPC)和网络通信等输入/输出资源都在定义在文件系统空间下的字节流。
+这些思想是对 Linux 中的各种事物的重要阐述,因此像文档、目录(Mac OS X 和 Windows 系统下称之为文件夹)、键盘、监视器、硬盘、可移动媒体设备、打印机、调制解调器、虚拟终端,还有进程间通信(IPC)和网络通信等输入/输出资源都是定义在文件系统空间下的字节流。
 
 一切都可看作是文件,其最显著的好处是对于上面所列出的输入/输出资源,只需要相同的一套 Linux 工具、实用程序和 API。
 
@@ -28,7 +29,7 @@ Linux 系统中有三种基本的文件类型:
 
 它们是包含文本、数据、程序指令等数据的文件,其在 Linux 系统中是最常见的一种。包括如下:
 
-- 只读文件
+- 可读文件
 - 二进制文件
 - 图像文件
 - 压缩文件等等
@@ -37,7 +38,7 @@ Linux 系统中有三种基本的文件类型:
 
 特殊文件包括以下几种:
 
-块文件:设备文件,对访问系统硬件部件提供了缓存接口。他们提供了一种使用文件系统与设备驱动通信的方法。
+块文件(block):设备文件,对访问系统硬件部件提供了缓存接口。它们提供了一种通过文件系统与设备驱动通信的方法。
 
 有关于块文件一个重要的性能就是它们能在指定时间内传输大块的数据和信息。
 
@@ -73,7 +74,7 @@ brw-rw----  1 root disk        1,   5 May 18 10:26 ram5
 ...
 ```
 
-字符文件: 也是设备文件,对访问系统硬件组件提供了非缓冲串行接口。它们与设备的通信工作方式是一次只传输一个字符的数据。
+字符文件(Character): 也是设备文件,对访问系统硬件组件提供了非缓冲串行接口。它们与设备的通信工作方式是一次只传输一个字符的数据。
 
 列出某目录下的字符文件:
 
@@ -113,7 +114,7 @@ crw-rw-rw-  1 root tty         5,   2 May 18 17:40 ptmx
 crw-rw-rw-  1 root root        1,   8 May 18 10:26 random
 ```
 
-符号链接文件 : 符号链接是指向系统上其他文件的引用。因此,符号链接文件是指向其它文件的文件,也可以是目录或常规文件。
+符号链接文件(Symbolic link) : 符号链接是指向系统上其他文件的引用。因此,符号链接文件是指向其它文件的文件,那些文件可以是目录或常规文件。
 
 列出某目录下的符号链接文件:
 
@@ -142,11 +143,11 @@ Linux 中使用 `ln` 工具就可以创建一个符号链接文件,如下所
 # ls -l /home/tecmint/ | grep "^l"         [列出符号链接文件]
 ```
 
-在上面的例子中,首先我们在 `/tmp`  目录创建了一个名叫  `file1.txt` 的文件,然后创建符号链接文件,所以 `/home/tecmint/file1.txt` 指向 `/tmp/file1.txt` 文件。
+在上面的例子中,首先我们在 `/tmp`  目录创建了一个名叫  `file1.txt` 的文件,然后创建符号链接文件,将 `/home/tecmint/file1.txt` 指向 `/tmp/file1.txt` 文件。
 
-套接字和命令管道 : 连接一个进行的输出和另一个进程的输入,允许进程间通信的文件。
+管道(Pipes)和命令管道(Named pipes) : 将一个进程的输出连接到另一个进程的输入,从而允许进程间通信(IPC)的文件。
 
-命名管道实际上是一个文件,用来使两个进程彼此通信,就像一个 Linux pipe(管道) 命令一样。
+命名管道实际上是一个文件,用来使两个进程彼此通信,就像一个 Linux 管道一样。
 
 列出某目录下的管道文件:
 
@@ -154,7 +155,7 @@ Linux 中使用 `ln` 工具就可以创建一个符号链接文件,如下所
 # ls -l | grep "^p"
 ```
 
-输出例子
+输出例子:
 
 ```
 prw-rw-r-- 1 tecmint tecmint    0 May 18 17:47 pipe1
@@ -171,19 +172,17 @@ prw-rw-r-- 1 tecmint tecmint    0 May 18 17:47 pipe5
 # echo "This is named pipe1" > pipe1
 ```
 
-在上的例子中,我们创建了一个名叫 `pipe1` 的命名管道,然后使用 [echo 命令][2] 加入一些数据,在这操作后,要使用这些输入数据就要用非交互的 shell 了。
+在上的例子中,我们创建了一个名叫 `pipe1` 的命名管道,然后使用 [echo 命令][2] 加入一些数据,这之后在处理输入的数据时 shell 就变成非交互式的了(LCTT 译注:被管道占住了)。
 
-
-
-然后,我们打开另外的 shell 终端,运行另外的命令来打印出刚加入管道的数据。
+然后,我们打开另外一个 shell 终端,运行另外的命令来打印出刚加入管道的数据。
 
 ```
 # while read line ;do echo "This was passed-'$line' "; done<pipe1
 ```
 
-套接字文件 : 提供进程间通信方法的文件,它们能为运行在不同环境中的进程之间的数据和信息提供传输的能力。
+套接字文件(sockets) : 提供进程间通信方法的文件,它们能在运行在不同环境中的进程之间传输数据和信息。
 
-这就是说,套接字 (sockets) 可以对运行网络上不同机器中的进行之间的数据和信息进行传输。
+这就是说,套接字可以为运行网络上不同机器中的进程提供数据和信息传输。
 
 一个 socket 运行的例子就是网页浏览器连接到网站服务器的过程。
 
@@ -191,13 +190,13 @@ prw-rw-r-- 1 tecmint tecmint    0 May 18 17:47 pipe5
 # ls -l /dev/ | grep "^s"
 ```
 
-输出例子
+输出例子:
 
 ```
 srw-rw-rw-  1 root root             0 May 18 10:26 log
 ```
 
-下面是使用 C 语言编写的调用  `socket()` 函数的例子。
+下面是使用 C 语言编写的调用  `socket()` 系统调用的例子。
 
 ```
 int socket_desc= socket(AF_INET, SOCK_STREAM, 0 );
@@ -205,15 +204,15 @@ int socket_desc= socket(AF_INET, SOCK_STREAM, 0 );
 
 上例中:
 
-- `AF_INET` 指的是地址域(IPv4)
-- `SOCK_STREAM` 指的是类型 (默认使用 TCP 协议连接)
-- `0` 指协议(IP 协议)
+- `AF_INET` 指的是地址域(IPv4)
+- `SOCK_STREAM` 指的是类型(默认使用 TCP 协议连接)
+- `0` 指协议(IP 协议)
 
 使用 `socket_desc` 来引用管道文件,它跟文件描述符是一样的,然后再使用系统函数  `read()`  和 `write()`  来分别从这个管道文件读写数据。
 
-### 目录文件
+#### 目录文件
 
-是一些特殊的文件,既可以包含普通文件又可包含特殊文件,它们在 Linux 文件系统中是以根(/)目录为起点分层组织存在的。
+这是一些特殊的文件,既可以包含普通文件又可包含其它的特殊文件,它们在 Linux 文件系统中是以根(/)目录为起点分层组织存在的。
 
 列出某目录下的目录文件:
 
@@ -221,7 +220,7 @@ int socket_desc= socket(AF_INET, SOCK_STREAM, 0 );
 # ls -l / | grep "^d" 
 ```
 
-输出例子
+输出例子:
 
 ```
 drwxr-xr-x   2 root root  4096 May  5 15:49 bin
@@ -257,18 +256,17 @@ drwxr-xr-x  12 root root  4096 Nov 12  2015 var
 
 ### 结论
 
-现在应该有一个清楚的认识为什么 Linux 系统中一切都是文件以及 Linux 系统中可以存在哪些类型的文件了。
+现在应该对为什么 Linux 系统中一切都是文件以及 Linux 系统中可以存在哪些类型的文件有一个清楚的认识了。
 
 您可以通过阅读更多有关各个文件类型的文章和对应的创建过程等来增加更多知识。我希望这篇教程对您有所帮助。有任何疑问或有补充的知识,请留下评论,一起来讨论。
 
-
 --------------------------------------------------------------------------------
 
-via: http://www.tecmint.com/explanation-of-everything-is-a-file-and-types-of-files-in-linux/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+tecmint+%28Tecmint%3A+Linux+Howto%27s+Guide%29
+via: http://www.tecmint.com/explanation-of-everything-is-a-file-and-types-of-files-in-linux/
 
 作者:[Aaron Kili][a]
 译者:[runningwater](https://github.com/runningwater)
-校对:[校对者ID](https://github.com/校对者ID)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
 
diff --git a/published/201608/20160618 An Introduction to Mocking in Python.md b/published/201608/20160618 An Introduction to Mocking in Python.md
new file mode 100644
index 0000000000..56a0c732a2
--- /dev/null
+++ b/published/201608/20160618 An Introduction to Mocking in Python.md	
@@ -0,0 +1,483 @@
+Mock 在 Python 单元测试中的使用
+=====================================
+
+本文讲述的是 Python 中 Mock 的使用。
+
+### 如何执行单元测试而不用考验你的耐心
+
+很多时候,我们编写的软件会直接与那些被标记为“垃圾”的服务交互。用外行人的话说:服务对我们的应用程序很重要,但是我们想要的是交互,而不是那些不想要的副作用,这里的“不想要”是在自动化测试运行的语境中说的。例如:我们正在写一个社交 app,并且想要测试一下 "发布到 Facebook" 的新功能,但是不想每次运行测试集的时候真的发布到 Facebook。
+
+Python 的 `unittest` 库包含了一个名为 `unittest.mock` 或者可以称之为依赖的子包,简称为
+`mock` —— 其提供了极其强大和有用的方法,通过它们可以模拟(mock)并去除那些我们不希望的副作用。
+
+![](https://assets.toptal.io/uploads/blog/image/252/toptal-blog-image-1389090346415.png)
+
+*注意:`mock` [最近被收录][1]到了 Python 3.3 的标准库中;先前发布的版本必须通过 [PyPI][2] 下载 Mock 库。*
+
+### 恐惧系统调用
+
+再举另一个例子,我们在接下来的部分都会用到它,这是就是**系统调用**。不难发现,这些系统调用都是主要的模拟对象:无论你是正在写一个可以弹出 CD 驱动器的脚本,还是一个用来删除 /tmp 下过期的缓存文件的 Web 服务,或者一个绑定到 TCP 端口的 socket 服务器,这些调用都是在你的单元测试上下文中不希望产生的副作用。
+
+作为一个开发者,你需要更关心你的库是否成功地调用了一个可以弹出 CD 的系统函数(使用了正确的参数等等),而不是切身经历 CD 托盘每次在测试执行的时候都打开了。(或者更糟糕的是,弹出了很多次,在一个单元测试运行期间多个测试都引用了弹出代码!)
+
+同样,保持单元测试的效率和性能意味着需要让如此多的“缓慢代码”远离自动测试,比如文件系统和网络访问。
+
+对于第一个例子来说,我们要从原始形式换成使用 `mock` 重构一个标准 Python 测试用例。我们会演示如何使用 mock 写一个测试用例,使我们的测试更加智能、快速,并展示更多关于我们软件的工作原理。
+
+### 一个简单的删除函数
+
+我们都有过需要从文件系统中一遍又一遍的删除文件的时候,因此,让我们在 Python 中写一个可以使我们的脚本更加轻易完成此功能的函数。
+
+```
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import os
+
+def rm(filename):
+    os.remove(filename)
+```
+
+很明显,我们的 `rm` 方法此时无法提供比 `os.remove` 方法更多的相关功能,但我们可以在这里添加更多的功能,使我们的基础代码逐步改善。
+
+让我们写一个传统的测试用例,即,没有使用 `mock`:
+
+```
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from mymodule import rm
+
+import os.path
+import tempfile
+import unittest
+
+class RmTestCase(unittest.TestCase):
+
+    tmpfilepath = os.path.join(tempfile.gettempdir(), "tmp-testfile")
+
+    def setUp(self):
+        with open(self.tmpfilepath, "wb") as f:
+            f.write("Delete me!")
+
+    def test_rm(self):
+        # remove the file
+        rm(self.tmpfilepath)
+        # test that it was actually removed
+        self.assertFalse(os.path.isfile(self.tmpfilepath), "Failed to remove the file.")
+```
+
+我们的测试用例相当简单,但是在它每次运行的时候,它都会创建一个临时文件并且随后删除。此外,我们没有办法测试我们的 `rm` 方法是否正确地将我们的参数向下传递给 `os.remove` 调用。我们可以基于以上的测试*认为*它做到了,但还有很多需要改进的地方。
+
+#### 使用 Mock 重构
+
+让我们使用 mock 重构我们的测试用例:
+
+```
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from mymodule import rm
+
+import mock
+import unittest
+
+class RmTestCase(unittest.TestCase):
+
+    @mock.patch('mymodule.os')
+    def test_rm(self, mock_os):
+        rm("any path")
+        # test that rm called os.remove with the right parameters
+        mock_os.remove.assert_called_with("any path")
+```
+
+使用这些重构,我们从根本上改变了测试用例的操作方式。现在,我们有一个可以用于验证其他功能的内部对象。
+
+##### 潜在陷阱
+
+第一件需要注意的事情就是,我们使用了 `mock.patch` 方法装饰器,用于模拟位于 `mymodule.os` 的对象,并且将 mock 注入到我们的测试用例方法。那么只是模拟 `os` 本身,而不是 `mymodule.os` 下 `os` 的引用(LCTT 译注:注意 `@mock.patch('mymodule.os')` 便是模拟 `mymodule.os` 下的 `os`),会不会更有意义呢?
+
+当然,当涉及到导入和管理模块,Python 的用法就像蛇一样灵活。在运行时,`mymodule` 模块有它自己的被导入到本模块局部作用域的 `os`。因此,如果我们模拟 `os`,我们是看不到 mock 在 `mymodule` 模块中的模仿作用的。
+
+这句话需要深刻地记住:
+
+> 模拟一个东西要看它用在何处,而不是来自哪里。
+
+如果你需要为 `myproject.app.MyElaborateClass` 模拟 `tempfile` 模块,你可能需要将 mock 用于 `myproject.app.tempfile`,而其他模块保持自己的导入。
+
+先将那个陷阱放一边,让我们继续模拟。
+
+#### 向 ‘rm’ 中加入验证
+
+之前定义的 rm 方法相当的简单。在盲目地删除之前,我们倾向于验证一个路径是否存在,并验证其是否是一个文件。让我们重构 rm 使其变得更加智能:
+
+```
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import os
+import os.path
+
+def rm(filename):
+    if os.path.isfile(filename):
+        os.remove(filename)
+```
+
+很好。现在,让我们调整测试用例来保持测试的覆盖率。
+
+```
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from mymodule import rm
+
+import mock
+import unittest
+
+class RmTestCase(unittest.TestCase):
+
+    @mock.patch('mymodule.os.path')
+    @mock.patch('mymodule.os')
+    def test_rm(self, mock_os, mock_path):
+        # set up the mock
+        mock_path.isfile.return_value = False
+
+        rm("any path")
+
+        # test that the remove call was NOT called.
+        self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")
+
+        # make the file 'exist'
+        mock_path.isfile.return_value = True
+
+        rm("any path")
+
+        mock_os.remove.assert_called_with("any path")
+```
+
+我们的测试用例完全改变了。现在我们可以在没有任何副作用的情况下核实并验证方法的内部功能。
+
+#### 将文件删除作为服务
+
+到目前为止,我们只是将 mock 应用在函数上,并没应用在需要传递参数的对象和实例的方法上。我们现在开始涵盖对象的方法。
+
+首先,我们将 `rm` 方法重构成一个服务类。实际上将这样一个简单的函数转换成一个对象,在本质上这不是一个合理的需求,但它能够帮助我们了解 `mock` 的关键概念。让我们开始重构:
+
+```
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import os
+import os.path
+
+class RemovalService(object):
+    """A service for removing objects from the filesystem."""
+
+    def rm(filename):
+        if os.path.isfile(filename):
+            os.remove(filename)
+```
+
+你会注意到我们的测试用例没有太大变化:
+
+```
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from mymodule import RemovalService
+
+import mock
+import unittest
+
+class RemovalServiceTestCase(unittest.TestCase):
+
+    @mock.patch('mymodule.os.path')
+    @mock.patch('mymodule.os')
+    def test_rm(self, mock_os, mock_path):
+        # instantiate our service
+        reference = RemovalService()
+
+        # set up the mock
+        mock_path.isfile.return_value = False
+
+        reference.rm("any path")
+
+        # test that the remove call was NOT called.
+        self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")
+
+        # make the file 'exist'
+        mock_path.isfile.return_value = True
+
+        reference.rm("any path")
+
+        mock_os.remove.assert_called_with("any path")
+```
+
+很好,我们知道 `RemovalService` 会如预期般的工作。接下来让我们创建另一个服务,将 `RemovalService` 声明为它的一个依赖:
+
+```
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import os
+import os.path
+
+class RemovalService(object):
+    """A service for removing objects from the filesystem."""
+
+    def rm(self, filename):
+        if os.path.isfile(filename):
+            os.remove(filename)
+
+
+class UploadService(object):
+
+    def __init__(self, removal_service):
+        self.removal_service = removal_service
+
+    def upload_complete(self, filename):
+        self.removal_service.rm(filename)
+```
+
+因为我们的测试覆盖了 `RemovalService`,因此我们不会对我们测试用例中 `UploadService` 的内部函数 `rm` 进行验证。相反,我们将调用 `UploadService` 的 `RemovalService.rm` 方法来进行简单测试(当然没有其他副作用),我们通过之前的测试用例便能知道它可以正确地工作。
+
+这里有两种方法来实现测试:
+
+1. 模拟 RemovalService.rm 方法本身。
+2. 在 UploadService 的构造函数中提供一个模拟实例。
+
+因为这两种方法都是单元测试中非常重要的方法,所以我们将同时对这两种方法进行回顾。
+
+##### 方法 1:模拟实例的方法
+
+`mock` 库有一个特殊的方法装饰器,可以模拟对象实例的方法和属性,即 `@mock.patch.object decorator` 装饰器:
+
+```
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from mymodule import RemovalService, UploadService
+
+import mock
+import unittest
+
+class RemovalServiceTestCase(unittest.TestCase):
+
+    @mock.patch('mymodule.os.path')
+    @mock.patch('mymodule.os')
+    def test_rm(self, mock_os, mock_path):
+        # instantiate our service
+        reference = RemovalService()
+
+        # set up the mock
+        mock_path.isfile.return_value = False
+
+        reference.rm("any path")
+
+        # test that the remove call was NOT called.
+        self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")
+
+        # make the file 'exist'
+        mock_path.isfile.return_value = True
+
+        reference.rm("any path")
+
+        mock_os.remove.assert_called_with("any path")
+
+
+class UploadServiceTestCase(unittest.TestCase):
+
+    @mock.patch.object(RemovalService, 'rm')
+    def test_upload_complete(self, mock_rm):
+        # build our dependencies
+        removal_service = RemovalService()
+        reference = UploadService(removal_service)
+
+        # call upload_complete, which should, in turn, call `rm`:
+        reference.upload_complete("my uploaded file")
+
+        # check that it called the rm method of any RemovalService
+        mock_rm.assert_called_with("my uploaded file")
+
+        # check that it called the rm method of _our_ removal_service
+        removal_service.rm.assert_called_with("my uploaded file")
+```
+
+非常棒!我们验证了 `UploadService` 成功调用了我们实例的 `rm` 方法。你是否注意到一些有趣的地方?这种修补机制(patching mechanism)实际上替换了我们测试用例中的所有 `RemovalService` 实例的 `rm` 方法。这意味着我们可以检查实例本身。如果你想要了解更多,可以试着在你模拟的代码下断点,以对这种修补机制的原理获得更好的认识。
+
+##### 陷阱:装饰顺序
+
+当我们在测试方法中使用多个装饰器,其顺序是很重要的,并且很容易混乱。基本上,当装饰器被映射到方法参数时,[装饰器的工作顺序是反向的][3]。思考这个例子:
+
+
+```
+    @mock.patch('mymodule.sys')
+    @mock.patch('mymodule.os')
+    @mock.patch('mymodule.os.path')
+    def test_something(self, mock_os_path, mock_os, mock_sys):
+        pass
+```
+
+注意到我们的参数和装饰器的顺序是反向匹配了吗?这部分是由 [Python 的工作方式][4]所导致的。这里是使用多个装饰器的情况下它们执行顺序的伪代码:
+
+```
+patch_sys(patch_os(patch_os_path(test_something)))
+```
+
+因为 `sys` 补丁位于最外层,所以它最晚执行,使得它成为实际测试方法参数的最后一个参数。请特别注意这一点,并且在运行你的测试用例时,使用调试器来保证正确的参数以正确的顺序注入。
+
+##### 方法 2:创建 Mock 实例
+
+我们可以使用构造函数为 `UploadService` 提供一个 Mock 实例,而不是模拟特定的实例方法。我更推荐方法 1,因为它更加精确,但在多数情况,方法 2 或许更加有效和必要。让我们再次重构测试用例:
+
+```
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from mymodule import RemovalService, UploadService
+
+import mock
+import unittest
+
+class RemovalServiceTestCase(unittest.TestCase):
+
+    @mock.patch('mymodule.os.path')
+    @mock.patch('mymodule.os')
+    def test_rm(self, mock_os, mock_path):
+        # instantiate our service
+        reference = RemovalService()
+
+        # set up the mock
+        mock_path.isfile.return_value = False
+
+        reference.rm("any path")
+
+        # test that the remove call was NOT called.
+        self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")
+
+        # make the file 'exist'
+        mock_path.isfile.return_value = True
+
+        reference.rm("any path")
+
+        mock_os.remove.assert_called_with("any path")
+
+
+class UploadServiceTestCase(unittest.TestCase):
+
+    def test_upload_complete(self, mock_rm):
+        # build our dependencies
+        mock_removal_service = mock.create_autospec(RemovalService)
+        reference = UploadService(mock_removal_service)
+
+        # call upload_complete, which should, in turn, call `rm`:
+        reference.upload_complete("my uploaded file")
+
+        # test that it called the rm method
+        mock_removal_service.rm.assert_called_with("my uploaded file")
+```
+
+在这个例子中,我们甚至不需要修补任何功能,只需为 `RemovalService` 类创建一个 auto-spec,然后将实例注入到我们的 `UploadService` 以验证功能。
+
+`mock.create_autospec` 方法为类提供了一个同等功能实例。实际上来说,这意味着在使用返回的实例进行交互的时候,如果使用了非法的方式将会引发异常。更具体地说,如果一个方法被调用时的参数数目不正确,将引发一个异常。这对于重构来说是非常重要。当一个库发生变化的时候,中断测试正是所期望的。如果不使用 auto-spec,尽管底层的实现已经被破坏,我们的测试仍然会通过。
+
+##### 陷阱:mock.Mock 和 mock.MagicMock 类
+
+`mock` 库包含了两个重要的类 [mock.Mock](http://www.voidspace.org.uk/python/mock/mock.html) 和 [mock.MagicMock](http://www.voidspace.org.uk/python/mock/magicmock.html#magic-mock),大多数内部函数都是建立在这两个类之上的。当在选择使用 `mock.Mock` 实例、`mock.MagicMock` 实例还是 auto-spec 的时候,通常倾向于选择使用 auto-spec,因为对于未来的变化,它更能保持测试的健全。这是因为 `mock.Mock` 和 `mock.MagicMock` 会无视底层的 API,接受所有的方法调用和属性赋值。比如下面这个用例:
+
+```
+class Target(object):
+    def apply(value):
+        return value
+
+def method(target, value):
+    return target.apply(value)
+```
+
+我们可以像下面这样使用 mock.Mock 实例进行测试:
+
+```
+class MethodTestCase(unittest.TestCase):
+
+    def test_method(self):
+        target = mock.Mock()
+
+        method(target, "value")
+
+        target.apply.assert_called_with("value")
+```
+
+这个逻辑看似合理,但如果我们修改 `Target.apply` 方法接受更多参数:
+
+```
+class Target(object):
+    def apply(value, are_you_sure):
+        if are_you_sure:
+            return value
+        else:
+            return None
+```
+
+重新运行你的测试,你会发现它仍能通过。这是因为它不是针对你的 API 创建的。这就是为什么你总是应该使用 `create_autospec` 方法,并且在使用 `@patch`和 `@patch.object` 装饰方法时使用 `autospec` 参数。
+
+### 现实例子:模拟 Facebook API 调用
+
+作为这篇文章的结束,我们写一个更加适用的现实例子,一个在介绍中提及的功能:发布消息到 Facebook。我将写一个不错的包装类及其对应的测试用例。
+
+```
+import facebook
+
+class SimpleFacebook(object):
+
+    def __init__(self, oauth_token):
+        self.graph = facebook.GraphAPI(oauth_token)
+
+    def post_message(self, message):
+        """Posts a message to the Facebook wall."""
+        self.graph.put_object("me", "feed", message=message)
+```
+
+这是我们的测试用例,它可以检查我们发布的消息,而不是真正地发布消息:
+
+```
+import facebook
+import simple_facebook
+import mock
+import unittest
+
+class SimpleFacebookTestCase(unittest.TestCase):
+
+    @mock.patch.object(facebook.GraphAPI, 'put_object', autospec=True)
+    def test_post_message(self, mock_put_object):
+        sf = simple_facebook.SimpleFacebook("fake oauth token")
+        sf.post_message("Hello World!")
+
+        # verify
+        mock_put_object.assert_called_with(message="Hello World!")
+```
+
+正如我们所看到的,在 Python 中,通过 mock,我们可以非常容易地动手写一个更加智能的测试用例。
+
+### Python Mock 总结
+
+即使对它的使用还有点不太熟悉,对[单元测试][7]来说,Python 的 `mock` 库可以说是一个规则改变者。我们已经演示了常见的用例来了解了 `mock` 在单元测试中的使用,希望这篇文章能够帮助 [Python 开发者][8]克服初期的障碍,写出优秀、经受过考验的代码。
+
+--------------------------------------------------------------------------------
+
+via: https://www.toptal.com/python/an-introduction-to-mocking-in-python
+
+作者:[NAFTULI TZVI KAY][a]
+译者:[cposture](https://github.com/cposture)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.slviki.com/
+[1]: http://www.python.org/dev/peps/pep-0417/
+[2]: https://pypi.python.org/pypi/mock
+[3]: http://www.voidspace.org.uk/python/mock/patch.html#nesting-patch-decorators
+[4]: http://docs.python.org/2/reference/compound_stmts.html#function-definitions
+[5]: http://www.voidspace.org.uk/python/mock/helpers.html#autospeccing
+[6]: http://www.voidspace.org.uk/python/mock/mock.html
+[7]: http://www.toptal.com/qa/how-to-write-testable-code-and-why-it-matters
+[8]: http://www.toptal.com/python
diff --git a/published/201608/20160620 5 SSH Hardening Tips.md b/published/201608/20160620 5 SSH Hardening Tips.md
new file mode 100644
index 0000000000..22f6edae6e
--- /dev/null
+++ b/published/201608/20160620 5 SSH Hardening Tips.md	
@@ -0,0 +1,119 @@
+五条强化 SSH 安全的建议
+======================
+
+![](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/binary-1188510_1920_0.jpg?itok=ocPCL_9G)
+
+*采用这些简单的建议,使你的 OpenSSH 会话更加安全。*
+
+当你查看你的 SSH 服务日志,可能你会发现充斥着一些不怀好意的尝试性登录。这里有 5 条常规建议(和一些个别特殊策略)可以让你的 OpenSSH 会话更加安全。
+
+### 1. 强化密码登录
+
+密码登录很方便,因为你可以从任何地方的任何机器上登录。但是它们在暴力攻击面前也是脆弱的。尝试以下策略来强化你的密码登录。
+
+- 使用一个密码生成工具,例如 pwgen。pwgen 有几个选项,最有用的就是密码长度的选项(例如,`pwgen 12` 产生一个12位字符的密码)
+- 不要重复使用密码。忽略所有那些不要写下你的密码的建议,然后将你的所有登录信息都记在一个本子上。如果你不相信我的建议,那总可以相信安全权威 [Bruce Schneier][1] 吧。如果你足够细心,没有人能够发现你的笔记本,那么这样能够不受到网络上的那些攻击。
+- 你可以为你的登录记事本增加一些额外的保护措施,例如用字符替换或者增加新的字符来掩盖笔记本上的登录密码。使用一个简单而且好记的规则,比如说给你的密码增加两个额外的随机字符,或者使用单个简单的字符替换,例如 `#` 替换成 `*`。
+- 为你的 SSH 服务开启一个非默认的监听端口。是的,这是很老套的建议,但是它确实很有效。检查你的登录;很有可能 22 端口是被普遍攻击的端口,其他端口则很少被攻击。
+- 使用 [Fail2ban][2] 来动态保护你的服务器,是服务器免于被暴力攻击。
+- 使用不常用的用户名。绝不能让 root 可以远程登录,并避免用户名为“admin”。
+
+### 2. 解决 `Too Many Authentication Failures` 报错
+
+当我的 ssh 登录失败,并显示“Too many authentication failures for carla”的报错信息时,我很难过。我知道我应该不介意,但是这报错确实很碍眼。而且,正如我聪慧的奶奶曾经说过,伤痛之感并不能解决问题。解决办法就是在你的(客户端的) `~/.ssh/config` 文件设置强制密码登录。如果这个文件不存在,首先创个 `~/.ssh/` 目录。
+
+```
+$ mkdir ~/.ssh
+$ chmod 700 ~/.ssh
+```
+然后在一个文本编辑器创建 `~/.ssh/confg` 文件,输入以下行,使用你自己的远程域名替换 HostName。
+
+```
+HostName remote.site.com
+PubkeyAuthentication=no
+```
+
+(LCTT 译注:这种错误发生在你使用一台 Linux 机器使用 ssh 登录另外一台服务器时,你的 .ssh 目录中存储了过多的私钥文件,而 ssh 客户端在你没有指定 -i 选项时,会默认逐一尝试使用这些私钥来登录远程服务器后才会提示密码登录,如果这些私钥并不能匹配远程主机,显然会触发这样的报错,甚至拒绝连接。因此本条是通过禁用本地私钥的方式来强制使用密码登录——显然这并不可取,如果你确实要避免用私钥登录,那你应该用 `-o PubkeyAuthentication=no` 选项登录。显然这条和下两条是互相矛盾的,所以请无视本条即可。)
+
+### 3. 使用公钥认证
+
+公钥认证比密码登录安全多了,因为它不受暴力密码攻击的影响,但是并不方便因为它依赖于 RSA 密钥对。首先,你要创建一个公钥/私钥对。下一步,私钥放于你的客户端电脑,并且复制公钥到你想登录的远程服务器。你只能从拥有私钥的电脑登录才能登录到远程服务器。你的私钥就和你的家门钥匙一样敏感;任何人获取到了私钥就可以获取你的账号。你可以给你的私钥加上密码来增加一些强化保护规则。
+
+使用 RSA 密钥对管理多个用户是一种好的方法。当一个用户离开了,只要从服务器删了他的公钥就能取消他的登录。
+
+以下例子创建一个新的 3072 位长度的密钥对,它比默认的 2048 位更安全,而且为它起一个独一无二的名字,这样你就可以知道它属于哪个服务器。
+
+```
+$ ssh-keygen -t rsa -b 3072 -f id_mailserver
+```
+
+以下创建两个新的密钥, `id_mailserver` 和 `id_mailserver.pub`,`id_mailserver` 是你的私钥--不要传播它!现在用 `ssh-copy-id` 命令安全地复制你的公钥到你的远程服务器。你必须确保在远程服务器上有可用的 SSH 登录方式。
+
+```
+$ ssh-copy-id -i  id_rsa.pub user@remoteserver
+
+/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
+user@remoteserver's password:
+
+Number of key(s) added: 1
+
+Now try logging into the machine, with:   "ssh 'user@remoteserver'"
+and check to make sure that only the key(s) you wanted were added.
+```
+
+`ssh-copy-id` 会确保你不会无意间复制了你的私钥。从上述输出中复制登录命令,记得带上其中的单引号,以测试你的新的密钥登录。
+
+```
+$ ssh 'user@remoteserver'
+```
+
+它将用你的新密钥登录,如果你为你的私钥设置了密码,它会提示你输入。
+
+### 4. 取消密码登录
+
+一旦你已经测试并且验证了你的公钥可以登录,就可以取消密码登录,这样你的远程服务器就不会被暴力密码攻击。如下设置**你的远程服务器**的 `/etc/sshd_config` 文件。
+
+```
+PasswordAuthentication no
+```
+
+然后重启服务器上的 SSH 守护进程。
+
+### 5. 设置别名 -- 这很快捷而且很有 B 格
+
+你可以为你的远程登录设置常用的别名,来替代登录时输入的命令,例如 `ssh -u username -p 2222 remote.site.with.long-name`。你可以使用 `ssh remote1`。你的客户端机器上的 `~/.ssh/config` 文件可以参照如下设置
+
+```
+Host remote1
+HostName remote.site.with.long-name
+Port 2222
+User username
+PubkeyAuthentication no
+```
+
+如果你正在使用公钥登录,可以参照这个:
+
+```
+Host remote1
+HostName remote.site.with.long-name
+Port 2222
+User username
+IdentityFile  ~/.ssh/id_remoteserver
+```
+
+[OpenSSH 文档][3] 很长而且详细,但是当你掌握了基础的 SSH 使用规则之后,你会发现它非常的有用,而且包含很多可以通过 OpenSSH 来实现的炫酷效果。
+
+--------------------------------------------------------------------------------
+
+via: https://www.linux.com/learn/5-ssh-hardening-tips
+
+作者:[CARLA SCHRODER][a]
+译者:[maywanting](https://github.com/maywanting)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.linux.com/users/cschroder
+[1]: https://www.schneier.com/blog/archives/2005/06/write_down_your.html
+[2]: http://www.fail2ban.org/wiki/index.php/Main_Page
+[3]: http://www.openssh.com/
diff --git a/published/201608/20160621 Flatpak brings standalone apps to Linux.md b/published/201608/20160621 Flatpak brings standalone apps to Linux.md
new file mode 100644
index 0000000000..488c404f45
--- /dev/null
+++ b/published/201608/20160621 Flatpak brings standalone apps to Linux.md	
@@ -0,0 +1,35 @@
+Flatpak 为 Linux 带来了独立应用
+================
+
+![](https://cdn.fedoramagazine.org/wp-content/uploads/2016/06/flatpak-945x400.jpg)
+
+[Flatpak][1] 的开发团队[宣布了][2] Flatpak 桌面应用框架已经可用了。 Flatpak (以前在开发时名为 xdg-app)为应用提供了捆绑为一个 Flatpak 软件包的能力,可以让应用在很多 Linux 发行版上都以轻松而一致的体验来安装和运行。将应用程序捆绑成 Flatpak 为其提供了沙盒安全环境,可以将它们与操作系统和彼此之间相互隔离。查看 [Flatpak 网站][3]上的[发布公告][4]来了解关于 Flatpak 框架技术的更多信息。
+
+###  在 Fedora 中安装 Flatpak  
+
+如果用户想要运行以 Flatpak 格式打包的应用,在 Fedora 上安装是很容易的,Flatpak 格式已经可以在官方的 Fedora 23 和 Fedora 24 仓库中获得。Flatpak 网站上有[在 Fedora 上安装的完整细节][5],同时也有如何在 Arch、 Debian、Mageia 和 Ubuntu 中安装的方法。[许多的应用][6]已经使用 Flatpak 打包构建了,这包括 LibreOffice、Inkscape 和 GIMP。
+
+###  对应用开发者
+
+如果你是一个应用开发者,Flatpak 网站也包含许多有关于[使用 Flatpak 打包和分发应用程序][7]的重要资料。这些资料中包括了使用 Flakpak SDK 构建独立的、沙盒化的 Flakpak 应用程序的信息。
+
+--------------------------------------------------------------------------------
+
+via: https://fedoramagazine.org/introducing-flatpak/
+
+作者:[Ryan Lerch][a]
+译者:[zky001](https://github.com/zky001)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://fedoramagazine.org/introducing-flatpak/
+[1]: http://flatpak.org/
+[2]: http://flatpak.org/press/2016-06-21-flatpak-released.html
+[3]: http://flatpak.org/
+[4]: http://flatpak.org/press/2016-06-21-flatpak-released.html
+[5]: http://flatpak.org/getting.html
+[6]: http://flatpak.org/apps.html
+[7]: http://flatpak.org/developer.html
+
+
diff --git a/published/201608/20160623 72% Of The People I Follow On Twitter Are Men.md b/published/201608/20160623 72% Of The People I Follow On Twitter Are Men.md
new file mode 100644
index 0000000000..e0cc100587
--- /dev/null
+++ b/published/201608/20160623 72% Of The People I Follow On Twitter Are Men.md	
@@ -0,0 +1,81 @@
+在推特上我关注的人 72% 都是男性
+===============================================
+
+![](https://emptysqua.re/blog/gender-of-twitter-users-i-follow/abacus.jpg)
+
+至少,这是我的估计。推特并不会询问用户的性别,因此我 [写了一个程序][1] ,根据姓名猜测他们的性别。在那些关注我的人当中,性别分布甚至更糟,83% 的是男性。据我所知,其他的还不全都是女性。
+
+修正第一个数字并不是什么神秘的事:我注意寻找更多支持我兴趣的女性专家,并且关注他们。
+
+另一方面,第二个数字,我只能只能轻微影响一点,但是我也打算改进下。我在推特上的关系网应该代表的是软件行业的多元化未来,而不是不公平的现状。
+
+### 我应该怎么估算呢
+
+我开始估算我关注的人(推特的上的术语是“朋友”)的性别分布,然后发现这格外的难。[推特的分析][2]给我展示了如下的结果, 关于关注我的人的性别估算:
+
+![](https://emptysqua.re/blog/gender-of-twitter-users-i-follow/twitter-analytics.png)
+
+因此,推特的分析将我的关注者分成了三类:男性、女性、未知,并且给我们展示了前面两组的比例。(性别二值化现象在这里并不存在——未知性别的人都集中在组织的推特账号上。)但是我关注的人的性别比例,推特并没有告诉我。 [而这就是可以改进的][3],然后我开始搜索能够帮我估算这个数字的服务,最终发现了 [FollowerWonk][4] 。
+
+FollowerWonk 估算我关注的人里面有 71% 都是男性。这个估算准确吗? 为了评估一下,我把 FollowerWonk 和 Twitter 对我关注的人的进行了估算,结果如下:
+
+**推特分析**
+
+|           | 男性   | 女性   |
+| --------- | ---- | ---- |
+| **我的关注者** | 83%  | 17%  |
+
+**FollowerWonk**
+
+|           | 男性   | 女性   |
+| --------- | ---- | ---- |
+| **我的关注者** | 81%  | 19%  |
+| **我关注的人** | 72%  | 28%  |
+
+FollowerWonk 的分析显示我的关注者中 81% 的人都是男性,很接近推特分析的数字。这个结果还说得过去。如果FollowerWonk 和 Twitter 在我的关注者的性别比例上是一致的,这就表明 FollowerWonk 对我关注的人的性别估算也应当是合理的。使用 FollowerWonk 我就能养成估算这些数字的爱好,并且做出改进。
+
+然而,使用 FollowerWonk 检测我关注的人的性别分布一个月需要 30 美元,这真是一个昂贵的爱好。我并不需要FollowerWonk 的所有的功能。我能很经济的解决只需要性别分布的问题吗?
+
+因为 FollowerWonk 的估算数字看起来比较合理,我试图做一个自己的 FollowerWonk 。使用 Python 和[一些好心的费城人写的 Twitter API 封装类][5](LCTT 译注:Twitter API 封装类是由 Mike Taylor 等一批费城人在 github 上开源的一个项目),我开始下载我所有关注的人和我所有的关注者的简介。我马上就发现推特的速率限制是很低,因此我随机的采样了一部分用户。
+
+我写了一个初步的程序,在所有我关注的人的简介中搜索一个和性别相关的代词。例如,如果简介中包含了“she”或者“her”这样的字眼,可能这就属于一个女性,如果简介中包含了“they”或者“them”,那么可能这就是性别未知的。但是大多数简介中不会出现这些代词。对于这种简介,和性别关联最紧密的信息就是姓名了。例如:@gvanrossum 的姓名那一栏是“Guido van Rossum”,第一姓名是“Guido”,这表明 @gvanrossum 是一个女的。当找不到代词的时候,我就使用名字来评估性别估算数字。
+
+我的脚本把每个名字的一部分传到性别检测机中去检测性别。[性别检测机][6]也有可预见的失败,比如错误的把“Brooklyn Zen Center”当做一个名叫“Brooklyn”的女性,但是它的评估结果与 FollowerWonk 和 Twitter 的相比也是很合理的:
+
+|       | 非男非女 | 男性   | 女性   | 性别未知的 |
+| ----- | ---- | ---- | ---- | ----- |
+| 我关注的人 | 1    | 168  | 66   | 173   |
+|       | 0%   | 72%  | 28%  |       |
+| 我的关注者 | 0    | 459  | 108  | 433   |
+|       | 0%   | 81%  | 19%  |       |
+
+(数据基于我所有的408个关注的人和1000个关注者。)
+
+### 了解你的数字
+
+我想你们也能检测你们推特关系网的性别分布。所以我将“Proportional”应用发布到 PythonAnywhere 这个便利的服务上,每月仅需 10 美元:
+
+> <www.proporti.onl>
+
+这个应用可能会在速率上有限制,超过会失败,因此请温柔的对待它。github 上放了源代码[代码][7] ,也有命令行的工具。
+
+是谁代表了你的推特关系网?你还在忍受那些在过去几十年里一直在谈论的软件行业的不公平的男女分布吗?或者你的关系网看起来像软件行业的未来吗?让我们了解我们的数字并且改善他们。
+
+--------------------------------------------------------------------------------
+
+via: https://emptysqua.re/blog/gender-of-twitter-users-i-follow/
+
+作者:[A. Jesse Jiryu Davis][a]
+译者:[Flowsnow](https://github.com/Flowsnow)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://disqus.com/by/AJesseJiryuDavis/
+[1]: https://www.proporti.onl/
+[2]: https://analytics.twitter.com/
+[3]: http://english.stackexchange.com/questions/14952/that-which-is-measured-improves
+[4]: https://moz.com/followerwonk/
+[5]: https://github.com/bear/python-twitter/graphs/contributors
+[6]: https://pypi.python.org/pypi/SexMachine/
+[7]: https://github.com/ajdavis/twitter-gender-distribution
\ No newline at end of file
diff --git a/published/201608/20160623 Advanced Image Processing with Python.md b/published/201608/20160623 Advanced Image Processing with Python.md
new file mode 100644
index 0000000000..f668a63924
--- /dev/null
+++ b/published/201608/20160623 Advanced Image Processing with Python.md	
@@ -0,0 +1,113 @@
+Python 高级图像处理
+======================================
+
+![](http://www.cuelogic.com/blog/wp-content/uploads/2016/06/Image-Search-Engine.png)
+
+构建图像搜索引擎并不是一件容易的任务。这里有几个概念、工具、想法和技术需要实现。主要的图像处理概念之一是逆图像查询(RIQ:reverse image querying)。Google、Cloudera、Sumo Logic 和 Birst 等公司在使用逆图像搜索中名列前茅。通过分析图像和使用数据挖掘 RIQ 提供了很好的洞察分析能力。
+
+### 顶级公司与逆图像搜索
+
+有很多顶级的技术公司使用 RIQ 来取得了不错的收益。例如:在 2014 年 Pinterest 第一次带来了视觉搜索。随后在 2015 年发布了一份白皮书,披露了其架构。逆图像搜索让 Pinterest 获得了时尚品的视觉特征,并可以显示相似产品的推荐。
+
+众所周知,谷歌图片使用逆图像搜索允许用户上传一张图片然后搜索相关联的图片。通过使用先进的算法对提交的图片进行分析和数学建模,然后和谷歌数据库中无数的其他图片进行比较得到相似的结果。
+
+**这是 OpenCV 2.4.9 特征比较报告一个图表:**
+
+![](http://www.cuelogic.com/blog/wp-content/uploads/2016/06/search-engine-graph.jpg)
+
+### 算法 & Python库
+
+在我们使用它工作之前,让我们过一遍构建图像搜索引擎的 Python 库的主要元素:
+
+### 专利算法
+
+#### 尺度不变特征变换(SIFT - Scale-Invariant Feature Transform)算法
+
+1. 带有非自由功能的一个专利技术,利用图像识别符,以识别相似图像,甚至那些来自不同的角度,大小,深度和尺度的图片,也会被包括在搜索结果中。[点击这里][4]查看 SIFT 详细视频。
+2. SIFT 能与从许多图片中提取了特征的大型数据库正确地匹配搜索条件。
+3. 能匹配不同视角的相同图像和匹配不变特征来获得搜索结果是 SIFT 的另一个特征。了解更多关于尺度不变[关键点][5]。
+
+#### 加速鲁棒特征(SURF - Speeded Up Robust Features)算法
+
+1. [SURF][1] 也是一种带有非自由功能的专利技术,而且还是一种“加速”的 SIFT 版本。不像 SIFT,SURF 接近于带有箱式过滤器(Box Filter)的高斯拉普拉斯算子(Laplacian of Gaussian)。
+2. SURF 依赖于黑塞矩阵(Hessian Matrix)的位置和尺度。
+3. 在许多应用中,旋转不变性不是一个必要条件,所以不按这个方向查找加速了处理。
+4. SURF 包括了几种特性,提升了每一步的速度。SIFT 在旋转和模糊化方面做的很好,比 SIFT 的速度快三倍。然而它不擅长处理照明和变换视角。
+5. OpenCV 程序功能库提供了 SURF 功能,SURF.compute() 和 SURF.Detect() 可以用来找到描述符和要点。阅读更多关于SURF[点击这里][2]
+
+### 开源算法
+
+#### KAZE 算法
+
+1. KAZE是一个开源的非线性尺度空间的二维多尺度和新的特征检测和描述算法。在加性算子分裂(Additive Operator Splitting, AOS)和可变电导扩散中的有效技术被用来建立非线性尺度空间。
+2. 多尺度图像处理的基本原理很简单:创建一个图像的尺度空间,同时用正确的函数过滤原始图像,以提高时间或尺度。
+
+#### 加速的 KAZE(AKAZE - Accelerated-KAZE) 算法
+
+1. 顾名思义,这是一个更快的图像搜索方式,它会在两幅图像之间找到匹配的关键点。AKAZE 使用二进制描述符和非线性尺度空间来平衡精度和速度。
+
+#### 二进制鲁棒性不变尺度可变关键点(BRISK - Binary Robust Invariant Scalable Keypoints) 算法
+
+1. BRISK 非常适合关键点的描述、检测与匹配。
+2. 是一种高度自适应的算法,基于尺度空间 FAST 的快速检测器和一个位字符串描述符,有助于显著加快搜索。
+3. 尺度空间关键点检测与关键点描述帮助优化当前相关任务的性能。
+
+#### 快速视网膜关键点(FREAK - Fast Retina Keypoint)
+
+1. 这个新的关键点描述的灵感来自人的眼睛。通过图像强度比能有效地计算一个二进制串级联。FREAK 算法相比 BRISK、SURF 和 SIFT 算法可以更快的计算与内存负载较低。
+
+#### 定向 FAST 和旋转 BRIEF(ORB - Oriented FAST and Rotated BRIEF)
+
+1. 快速的二进制描述符,ORB 具有抗噪声和旋转不变性。ORB 建立在 FAST 关键点检测器和 BRIEF 描述符之上,有成本低、性能好的元素属性。
+2. 除了快速和精确的定位元件,有效地计算定向的 BRIEF,分析变动和面向 BRIEF 特点相关,是另一个 ORB 的特征。
+
+### Python库
+
+#### OpenCV
+
+1. OpenCV 支持学术和商业用途,它是一个开源的机器学习和计算机视觉库,OpenCV 便于组织利用和修改代码。
+2. 超过 2500 个优化的算法,包括当前最先进的机器学习和计算机视觉算法服务与各种图像搜索--人脸检测、目标识别、摄像机目标跟踪,从图像数据库中寻找类似图像、眼球运动跟随、风景识别等。
+3. 像谷歌,IBM,雅虎,索尼,本田,微软和英特尔这样的大公司广泛的使用 OpenCV。
+4. OpenCV 拥有 python,java,C,C++ 和 MATLAB 接口,同时支持 Windows,Linux,Mac OS 和 Android。
+
+#### Python 图像库 (PIL)
+
+1. Python 图像库(PIL)支持多种文件格式,同时提供图像处理和图形解决方案。开源的 PIL 为你的 Python解释器添加了图像处理能力。
+2. 标准的图像处理能力包括图像增强、透明和遮罩处理、图像过滤、像素操作等。
+
+详细的数据和图表,请看[这里][3]的 OpenCV 2.4.9 特征比较报告。
+
+### 构建图像搜索引擎
+
+图像搜索引擎可以从预置的图像库选择相似的图像。其中最受欢迎的是谷歌的著名的图像搜索引擎。对于初学者来说,有不同的方法来建立这样的系统。提几个如下:
+
+1. 采用图像提取、图像描述提取、元数据提取和搜索结果提取,建立图像搜索引擎。
+2. 定义你的图像描述符,数据集索引,定义你的相似性度量,然后进行搜索和排名。
+3. 选择要搜索的图像,选择用于进行搜索的目录,搜索所有图片的目录,创建图片特征索引,评估搜索图片的相同特征,匹配搜索的图片并获得匹配的图片。
+
+我们的方法基本上从比较灰度版本的图像,逐渐演变到复杂的特征匹配算法如 SIFT 和 SURF,最后采用的是开源的解决方案 BRISK 。所有这些算法都提供了有效的结果,但在性能和延迟有细微变化。建立在这些算法上的引擎有许多应用,如分析流行统计的图形数据,在图形内容中识别对象,等等。
+
+**举例**:一个 IT 公司为其客户建立了一个图像搜索引擎。因此,如果如果搜索一个品牌的标志图像,所有相关的品牌形象也应该显示在搜索结果。所得到的结果也能够被客户用于分析,使他们能够根据地理位置估计品牌知名度。但它还比较年轻,RIQ(反向图像搜索)的潜力尚未被完全挖掘利用。
+
+这就结束了我们的文章,使用 Python 构建图像搜索引擎。浏览我们的博客部分来查看最新的编程技术。
+
+数据来源:OpenCV 2.4.9 特征比较报告(computer-vision-talks.com)
+
+(感谢 Ananthu Nair 的指导与补充)
+
+--------------------------------------------------------------------------------
+
+via: http://www.cuelogic.com/blog/advanced-image-processing-with-python/
+
+作者:[Snehith Kumbla][a]
+译者:[Johnny-Liao](https://github.com/Johnny-Liao)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.cuelogic.com/blog/author/snehith-kumbla/
+[1]: http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_surf_intro/py_surf_intro.html
+[2]: http://www.vision.ee.ethz.ch/~surf/eccv06.pdf
+[3]: https://docs.google.com/spreadsheets/d/1gYJsy2ROtqvIVvOKretfxQG_0OsaiFvb7uFRDu5P8hw/edit#gid=10
+[4]: https://www.youtube.com/watch?v=NPcMS49V5hg
+[5]: https://www.cs.ubc.ca/~lowe/papers/ijcv04.pdf
diff --git a/published/201608/20160628 Python 101 An Intro to urllib.md b/published/201608/20160628 Python 101 An Intro to urllib.md
new file mode 100644
index 0000000000..2ba5ea85b8
--- /dev/null
+++ b/published/201608/20160628 Python 101 An Intro to urllib.md	
@@ -0,0 +1,192 @@
+Python 学习:urllib 简介
+=================================
+
+Python 3 的 urllib 模块是一堆可以处理 URL 的组件集合。如果你有 Python 2 的知识,那么你就会注意到 Python 2 中有 urllib 和 urllib2 两个版本的模块。这些现在都是 Python 3 的 urllib 包的一部分。当前版本的 urllib 包括下面几部分:
+
+- urllib.request
+- urllib.error
+- urllib.parse
+- urllib.rebotparser
+
+接下来我们会分开讨论除了 urllib.error 以外的几部分。官方文档实际推荐你尝试第三方库, requests,一个高级的 HTTP 客户端接口。然而我依然认为知道如何不依赖第三方库打开 URL 并与之进行交互是很有用的,而且这也可以帮助你理解为什么 requests 包是如此的流行。
+
+
+### urllib.request
+
+urllib.request 模块期初是用来打开和获取 URL 的。让我们看看你可以用函数 urlopen 可以做的事:
+
+
+```
+>>> import urllib.request
+>>> url = urllib.request.urlopen('https://www.google.com/')
+>>> url.geturl()
+'https://www.google.com/'
+>>> url.info()
+<http.client.HTTPMessage object at 0x7fddc2de04e0>
+>>> header = url.info()
+>>> header.as_string()
+('Date: Fri, 24 Jun 2016 18:21:19 GMT\n'
+ 'Expires: -1\n'
+ 'Cache-Control: private, max-age=0\n'
+ 'Content-Type: text/html; charset=ISO-8859-1\n'
+ 'P3P: CP="This is not a P3P policy! See '
+ 'https://www.google.com/support/accounts/answer/151657?hl=en for more info."\n'
+ 'Server: gws\n'
+ 'X-XSS-Protection: 1; mode=block\n'
+ 'X-Frame-Options: SAMEORIGIN\n'
+ 'Set-Cookie: '
+ 'NID=80=tYjmy0JY6flsSVj7DPSSZNOuqdvqKfKHDcHsPIGu3xFv41LvH_Jg6LrUsDgkPrtM2hmZ3j9V76pS4K_cBg7pdwueMQfr0DFzw33SwpGex5qzLkXUvUVPfe9g699Qz4cx9ipcbU3HKwrRYA; '
+ 'expires=Sat, 24-Dec-2016 18:21:19 GMT; path=/; domain=.google.com; HttpOnly\n'
+ 'Alternate-Protocol: 443:quic\n'
+ 'Alt-Svc: quic=":443"; ma=2592000; v="34,33,32,31,30,29,28,27,26,25"\n'
+ 'Accept-Ranges: none\n'
+ 'Vary: Accept-Encoding\n'
+ 'Connection: close\n'
+ '\n')
+>>> url.getcode()
+200
+```
+
+在这里我们包含了需要的模块,然后告诉它打开 Google 的 URL。现在我们就有了一个可以交互的 HTTPResponse 对象。我们要做的第一件事是调用方法 geturl ,它会返回根据 URL 获取的资源。这可以让我们发现 URL 是否进行了重定向。
+
+接下来调用 info ,它会返回网页的元数据,比如请求头信息。因此,我们可以将结果赋给我们的 headers 变量,然后调用它的方法 as_string 。就可以打印出我们从 Google 收到的头信息。你也可以通过 getcode 得到网页的 HTTP 响应码,当前情况下就是 200,意思是正常工作。
+
+如果你想看看网页的 HTML 代码,你可以调用变量 url 的方法 read。我不准备再现这个过程,因为输出结果太长了。
+
+请注意 request 对象默认发起 GET 请求,除非你指定了它的 data 参数。如果你给它传递了 data 参数,这样 request 对象将会变成 POST 请求。
+
+---
+
+### 下载文件
+
+urllib 一个典型的应用场景是下载文件。让我们看看几种可以完成这个任务的方法:
+
+```
+>>> import urllib.request
+>>> url = 'http://www.blog.pythonlibrary.org/wp-content/uploads/2012/06/wxDbViewer.zip'
+>>> response = urllib.request.urlopen(url)
+>>> data = response.read()
+>>> with open('/home/mike/Desktop/test.zip', 'wb') as fobj:
+...     fobj.write(data)
+...
+```
+
+这个例子中我们打开一个保存在我的博客上的 zip 压缩文件的 URL。然后我们读出数据并将数据写到磁盘。一个替代此操作的方案是使用 urlretrieve :
+
+```
+>>> import urllib.request
+>>> url = 'http://www.blog.pythonlibrary.org/wp-content/uploads/2012/06/wxDbViewer.zip'
+>>> tmp_file, header = urllib.request.urlretrieve(url)
+>>> with open('/home/mike/Desktop/test.zip', 'wb') as fobj:
+...     with open(tmp_file, 'rb') as tmp:
+...         fobj.write(tmp.read())
+```
+
+方法 urlretrieve 会把网络对象拷贝到本地文件。除非你在使用 urlretrieve 的第二个参数指定你要保存文件的路径,否则这个文件将被拷贝到临时文件夹的随机命名的一个文件中。这个可以为你节省一步操作,并且使代码看起来更简单:
+
+```
+>>> import urllib.request
+>>> url = 'http://www.blog.pythonlibrary.org/wp-content/uploads/2012/06/wxDbViewer.zip'
+>>> urllib.request.urlretrieve(url, '/home/mike/Desktop/blog.zip')
+('/home/mike/Desktop/blog.zip',
+ <http.client.HTTPMessage object at 0x7fddc21c2470>)
+```
+
+如你所见,它返回了文件保存的路径,以及从请求得来的头信息。
+
+### 设置你的用户代理
+
+当你使用浏览器访问网页时,浏览器会告诉网站它是谁。这就是所谓的 user-agent (用户代理)字段。Python 的 urllib 会表示它自己为 Python-urllib/x.y , 其中 x 和 y 是你使用的 Python 的主、次版本号。有一些网站不认识这个用户代理字段,然后网站可能会有奇怪的表现或者根本不能正常工作。辛运的是你可以很轻松的设置你自己的 user-agent 字段。
+
+```
+>>> import urllib.request
+>>> user_agent = ' Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0'
+>>> url = 'http://www.whatsmyua.com/'
+>>> headers = {'User-Agent': user_agent}
+>>> request = urllib.request.Request(url, headers=headers)
+>>> with urllib.request.urlopen(request) as response:
+...     with open('/home/mdriscoll/Desktop/user_agent.html', 'wb') as out:
+...         out.write(response.read())
+```
+
+这里设置我们的用户代理为 Mozilla FireFox ,然后我们访问 <http://www.whatsmyua.com/> , 它会告诉我们它识别出的我们的 user-agent 字段。之后我们将 url 和我们的头信息传给 urlopen 创建一个 Request 实例。最后我们保存这个结果。如果你打开这个结果,你会看到我们成功的修改了自己的 user-agent 字段。使用这段代码尽情的尝试不同的值来看看它是如何改变的。
+
+---
+
+### urllib.parse
+
+urllib.parse 库是用来拆分和组合 URL 字符串的标准接口。比如,你可以使用它来转换一个相对的 URL 为绝对的 URL。让我们试试用它来转换一个包含查询的 URL :
+
+
+```
+>>> from urllib.parse import urlparse
+>>> result = urlparse('https://duckduckgo.com/?q=python+stubbing&t=canonical&ia=qa')
+>>> result
+ParseResult(scheme='https', netloc='duckduckgo.com', path='/', params='', query='q=python+stubbing&t=canonical&ia=qa', fragment='')
+>>> result.netloc
+'duckduckgo.com'
+>>> result.geturl()
+'https://duckduckgo.com/?q=python+stubbing&t=canonical&ia=qa'
+>>> result.port
+None
+```
+
+这里我们导入了函数 urlparse , 并且把一个包含搜索查询字串的 duckduckgo 的 URL 作为参数传给它。我的查询字串是搜索关于 “python stubbing” 的文章。如你所见,它返回了一个 ParseResult 对象,你可以用这个对象了解更多关于 URL 的信息。举个例子,你可以获取到端口信息(本例中没有端口信息)、网络位置、路径和很多其它东西。
+
+### 提交一个 Web 表单
+
+这个模块还有一个方法 urlencode 可以向 URL 传输数据。 urllib.parse 的一个典型使用场景是提交 Web 表单。让我们通过搜索引擎 duckduckgo 搜索 Python 来看看这个功能是怎么工作的。
+
+```
+>>> import urllib.request
+>>> import urllib.parse
+>>> data = urllib.parse.urlencode({'q': 'Python'})
+>>> data
+'q=Python'
+>>> url = 'http://duckduckgo.com/html/'
+>>> full_url = url + '?' + data
+>>> response = urllib.request.urlopen(full_url)
+>>> with open('/home/mike/Desktop/results.html', 'wb') as f:
+...     f.write(response.read())
+```
+
+这个例子很直接。基本上我们是使用 Python 而不是浏览器向 duckduckgo 提交了一个查询。要完成这个我们需要使用 urlencode 构建我们的查询字符串。然后我们把这个字符串和网址拼接成一个完整的正确 URL ,然后使用 urllib.request 提交这个表单。最后我们就获取到了结果然后保存到磁盘上。
+
+---
+
+### urllib.robotparser
+
+robotparser 模块是由一个单独的类 RobotFileParser 构成的。这个类会回答诸如一个特定的用户代理是否获取已经设置了 robot.txt 的网站的 URL。 robot.txt 文件会告诉网络爬虫或者机器人当前网站的那些部分是不允许被访问的。让我们看一个简单的例子:
+
+```
+>>> import urllib.robotparser
+>>> robot = urllib.robotparser.RobotFileParser()
+>>> robot.set_url('http://arstechnica.com/robots.txt')
+None
+>>> robot.read()
+None
+>>> robot.can_fetch('*', 'http://arstechnica.com/')
+True
+>>> robot.can_fetch('*', 'http://arstechnica.com/cgi-bin/')
+False
+```
+
+这里我们导入了 robot 分析器类,然后创建一个实例。然后我们给它传递一个表明网站 robots.txt 位置的 URL 。接下来我们告诉分析器来读取这个文件。完成后,我们给它了一组不同的 URL 让它找出那些我们可以爬取而那些不能爬取。我们很快就看到我们可以访问主站但是不能访问 cgi-bin 路径。
+
+---
+
+### 总结一下
+
+现在你就有能力使用 Python 的 urllib 包了。在这一节里,我们学习了如何下载文件、提交 Web 表单、修改自己的用户代理以及访问 robots.txt。 urllib 还有一大堆附加功能没有在这里提及,比如网站身份认证。你可能会考虑在使用 urllib 进行身份认证之前切换到 requests 库,因为 requests 已经以更易用和易调试的方式实现了这些功能。我同时也希望提醒你 Python 已经通过 http.cookies 模块支持 Cookies 了,虽然在 request 包里也很好的封装了这个功能。你应该可能考虑同时试试两个来决定那个最适合你。
+
+--------------------------------------------------------------------------------
+
+via: http://www.blog.pythonlibrary.org/2016/06/28/python-101-an-intro-to-urllib/
+
+作者:[Mike][a]
+译者:[Ezio](https://github.com/oska874)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.blog.pythonlibrary.org/author/mld/
diff --git a/published/201608/20160630 18 Best IDEs for C+C++ Programming or Source Code Editors on Linux.md b/published/201608/20160630 18 Best IDEs for C+C++ Programming or Source Code Editors on Linux.md
new file mode 100644
index 0000000000..9b0b74e62e
--- /dev/null
+++ b/published/201608/20160630 18 Best IDEs for C+C++ Programming or Source Code Editors on Linux.md	
@@ -0,0 +1,322 @@
+17 个 Linux 下用于 C/C++ 的最好的 IDE /编辑器
+=======================
+
+C++,一个众所周知的 C 语言的扩展,是一个优秀的、强大的、通用编程语言,它能够提供现代化的、通用的编程功能,可以用于开发包括视频游戏、搜索引擎、其他计算机软件乃至操作系统等在内的各种大型应用。
+
+C++,提供高度可靠性的同时还能够允许操作底层内存来满足更高级的编程要求。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Best-Linux-IDE-Editors.png)
+
+虽然已经有了一些供程序员用来写 C/C++ 代码的文本编辑器,但 IDE 可以为轻松、完美的编程提供综合的环境和组件。
+
+在这篇文章里,我们会向你展示一些可以在 Linux 平台上找到的用于 C++ 或者其他编程语言编程的最好的 IDE。 
+
+### 1. 用于 C/C++ 开发的 Netbeans
+
+Netbeans 是一个自由而开源的、流行的跨平台 IDE ,可用于 C/C++ 以及其他编程语言,可以使用由社区开发的插件展现了其完全的扩展性。
+
+它包含了用于 C/C++ 开发的项目类型和模版,并且你可以使用静态和动态函数库来构建应用程序。此外,你可以利用现有的代码去创造你的工程,并且也可以通过拖放的方式导入二进制文件来从头构建应用。
+
+让我们来看看关于它的特性:
+
+- C/C++ 编辑器很好的整合了多线程的 [GNU GDB 调试工具][1]
+- 支持代码协助
+- 支持 C++11 标准
+- 在里面创建和运行 C/C++ 测试程序
+- 支持 QT 工具包
+- 支持将已编译的应用程序自动打包到 .tar,.zip 等归档文件
+- 支持多个编译器,例如: GNU、Clang/LLVM、Cygwin、Oracle Solaris Studio 和 MinGW
+- 支持远程开发
+- 文件导航
+- 源代码检查
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/NetBeans-IDE.png)
+
+主页:<https://netbeans.org/features/cpp/index.html>
+
+### 2. Code::Blocks
+
+Code::Blocks 是一个免费的、具有高度扩展性的、并且可以配置的跨平台 C++ IDE,它为用户提供了必备而典范的功能。它具有一致的界面和体验。
+
+最重要的是,你可以通过用户开发的插件扩展它的功能,一些插件是随同 Code::Blocks 发布的,而另外一些则不是,它们由 Code::Block 开发团队之外的个人用户所编写的。
+
+其功能分为编译器、调试器、界面功能,它们包括:
+
+- 支持多种编译器如 GCC、clang、Borland C++ 5.5、digital mars 等等
+- 非常快,不需要 makefile
+- 支持多个目标平台的项目
+- 支持将项目组合起来的工作空间
+- GNU GDB 接口
+- 支持完整的断点功能,包括代码断点,数据断点,断点条件等等
+- 显示本地函数的符号和参数
+- 用户内存导出和语法高亮显示
+- 可自定义、可扩展的界面以及许多其他的的功能,包括那些用户开发的插件添加功能
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/CodeBlocks-IDE-for-Linux.png)
+
+主页: <http://www.codeblocks.org>
+
+### 3. Eclipse CDT (C/C++ Development Tooling)
+
+Eclipse 在编程界是一款著名的、开源的、跨平台的 IDE。它给用户提供了一个很棒的界面,并支持拖拽功能以方便界面元素的布置。
+
+Eclipse CDT 是一个基于 Eclipse 主平台的项目,它提供了一个完整功能的 C/C++ IDE,并具有以下功能:
+
+- 支持项目创建
+- 管理各种工具链的构建
+- 标准的 make 构建
+- 源代码导航
+- 一些知识工具,如调用图、类型分级结构,内置浏览器,宏定义浏览器
+- 支持语法高亮的代码编辑器
+- 支持代码折叠和超链接导航
+- 代码重构与代码生成
+- 可视化调试存储器、寄存器的工具
+- 反汇编查看器以及更多功能
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Eclipse-IDE-for-Linux.png)
+
+主页: <http://www.eclipse.org/cdt/>
+
+### 4.CodeLite IDE
+
+CodeLite 也是一款为 C/C++、JavaScript(Node.js)和 PHP 编程专门设计打造的自由而开源的、跨平台的 IDE。
+
+它的一些主要特点包括:
+
+- 代码补完,提供了两个代码补完引擎
+- 支持多种编译器,包括 GCC、clang/VC++
+- 以代码词汇的方式显示错误
+- 构建选项卡中的错误消息可点击
+- 支持下一代 LLDB 调试器
+- 支持 GDB
+- 支持重构
+- 代码导航
+- 使用内置的 SFTP 进行远程开发
+- 源代码控制插件
+- 开发基于 wxWidgets 应用的 RAD(快速应用程序开发)工具,以及更多的特性
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Codelite-IDE.png)
+
+主页: <http://codelite.org/>
+
+### 5. Bluefish 编辑器
+
+Bluefish 不仅仅是一个一般的编辑器,它是一个轻量级的、快捷的编辑器,为程序员提供了如开发网站、编写脚本和软件代码的 IDE 特性。它支持多平台,可以在 Linux、Mac OSX、FreeBSD、OpenBSD、Solaris 和 Windows 上运行,同时支持包括 C/C++ 在内的众多编程语言。
+
+下面列出的是它众多功能的一部分:
+
+- 多文档界面
+- 支持递归打开文件,基于文件名通配模式或者内容模式
+- 提供一个非常强大的搜索和替换功能
+- 代码片段边栏
+- 支持整合个人的外部过滤器,可使用命令如 awk,sed,sort 以及自定义构建脚本组成(过滤器的)管道文件
+- 支持全屏编辑
+- 网站上传和下载
+- 支持多种编码等许多其他功能
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/BlueFish-IDE-Editor-for-Linux.png)
+
+主页: <http://bluefish.openoffice.nl>
+
+### 6. Brackets 代码编辑器
+
+Brackets 是一个现代化风格的、开源的文本编辑器,专为 Web 设计与开发打造。它可以通过插件进行高度扩展,因此 C/C++ 程序员通过安装 C/C++/Objective-C 包来使用它来开发,这个包用来在辅助 C/C++ 代码编写的同时提供了 IDE 之类的特性。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Brackets-Code-Editor-for-Linux.png)
+
+主页: <http://brackets.io/>
+
+### 7. Atom 代码编辑器
+
+Atom 也是一个现代化风格、开源的多平台文本编辑器,它能运行在 Linux、Windows 或是 Mac OS X 平台。它的定制可深入底层,用户可以自定义它,以便满足各种编写代码的需求。
+
+它功能完整,主要的功能包括:
+
+- 内置了包管理器
+- 智能的自动补完
+- 内置文件浏览器
+- 查找、替换以及其他更多的功能
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Atom-Code-Editor-for-Linux.png)
+
+主页: <https://atom.io/>
+
+安装指南: <http://www.tecmint.com/atom-text-and-source-code-editor-for-linux/>
+
+### 8. Sublime Text 编辑器
+
+Sublime Text 是一个完善的、跨平台的文本编辑器,可用于代码、标记语言和一般文字。它可以用来编写 C/C++ 代码,并且提供了非常棒的用户界面。
+
+它的功能列表包括:
+
+- 多重选择
+- 按模式搜索命令
+- 抵达任何一处的功能
+- 免打扰模式
+- 窗口分割
+- 支持项目之间快速的切换
+- 高度可定制
+- 支持基于 Python 的 API 插件以及其他特性
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Sublime-Code-Editor-for-Linux.png)
+
+主页: <https://www.sublimetext.com>
+
+安装指南: <http://www.tecmint.com/install-sublime-text-editor-in-linux/>
+
+### 9. JetBrains CLion
+
+JetBrains CLion 是一个收费的、强大的跨平台 C/C++ IDE。它是一个完全整合的 C/C++ 程序开发环境,并提供 Cmake 项目模型、一个嵌入式终端窗口和一个主要以键盘操作的编码环境。
+
+它还提供了一个智能而现代化的编辑器,具有许多令人激动的功能,提供了理想的编码环境,这些功能包括:
+
+- 除了 C/C++ 还支持其他多种语言
+- 在符号声明和上下文中轻松导航
+- 代码生成和重构
+- 可定制的编辑器
+- 即时代码分析
+- 集成的代码调试器
+- 支持 Git、Subversion、Mercurial、CVS、Perforcevia(通过插件)和 TFS
+- 无缝集成了 Google 测试框架
+- 通过 Vim 仿真插件支持 Vim 编辑体验
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/JetBains-CLion-IDE.png)
+
+主页: <https://www.jetbrains.com/clion/>
+
+### 10. 微软的 Visual Studio Code 编辑器
+
+Visual Studio 是一个功能丰富的、完全整合的、跨平台开发环境,运行在 Linux、Windows 和 Mac OS X 上。 最近它向 Linux 用户开源了,它重新定义了代码编辑这件事,为用户提供了在 Windows、Android、iOS 和 Web 等多个平台开发不同应用所需的一切工具。
+
+它功能完备,功能分类为应用程序开发、应用生命周期管理、扩展和集成特性。你可以从 Visual Studio 官网阅读全面的功能列表。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Visual-Studio-Code-Editor.png)
+
+主页: <https://www.visualstudio.com>
+
+### 11. KDevelop
+
+KDevelop 是另一个自由而开源的跨平台 IDE,能够运行在 Linux、Solaris、FreeBSD、Windows、Mac OS X 和其他类 Unix 操作系统上。它基于 KDevPlatform、KDE 和 Qt 库。KDevelop 可以通过插件高度扩展,功能丰富且具有以下显著特色:
+
+- 支持基于 Clang 的 C/C++ 插件
+- 支持 KDE 4 配置迁移
+- 支持调用二进制编辑器 Oketa
+- 支持众多视图插件下的差异行编辑
+- 支持 Grep 视图,使用窗口小部件节省垂直空间等
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/KDevelop-IDE-Editor.png)
+
+主页: <https://www.kdevelop.org>
+
+### 12. Geany IDE
+
+Geany 是一个免费的、快速的、轻量级跨平台 IDE,只需要很少的依赖包就可以工作,独立于流行的 Linux 桌面环境下,比如 GNOME 和 KDE。它需要 GTK2 库实现功能。
+
+它的特性包括以下列出的内容:
+
+- 支持语法高亮显示
+- 代码折叠
+- 调用提示
+- 符号名自动补完
+- 符号列表
+- 代码导航
+- 一个简单的项目管理工具
+- 可以编译并运行用户代码的内置系统
+- 可以通过插件扩展
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Geany-IDE-for-Linux.png)
+
+主页: <http://www.geany.org/>
+
+### 13. Ajunta DeveStudio
+
+Ajunta DevStudio 是一个简单,强大的 GNOME 界面的软件开发工作室,支持包括 C/C++ 在内的几种编程语言。
+
+它提供了先进的编程工具,比如项目管理、GUI 设计、交互式调试器、应用程序向导、源代码编辑器、版本控制等。此外,除了以上特点,Ajunta DeveStudio 也有其他很多不错的 IDE 功能,包括:
+
+- 简单的用户界面
+- 可通过插件扩展
+- 整合了 Glade 用于所见即所得的 UI 开发
+- 项目向导和模板
+- 整合了 GDB 调试器
+- 内置文件管理器
+- 使用 DevHelp 提供上下文敏感的编程辅助
+- 源代码编辑器支持语法高亮显示、智能缩进、自动缩进、代码折叠/隐藏、文本缩放等
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Anjuta-DevStudio-for-Linux.png)
+
+主页: <http://anjuta.org/>
+
+### 14. GNAT Programming Studio
+
+GNAT Programming Studio 是一个免费的、易于使用的 IDE,设计的目的用于统一开发人员与他/她的代码和软件之间的交互。
+
+它通过高亮程序的重要部分和逻辑从而提升源代码导航体验,打造了一个理想的编程环境。它的设计目标是为你带来更舒适的编程体验,使用户能够从头开始开发全面的系统。
+
+它丰富的特性包括以下这些:
+
+- 直观的用户界面
+- 对开发者的友好性
+- 支持多种编程语言,跨平台
+- 灵活的 MDI(多文档界面)
+- 高度可定制
+- 使用喜欢的工具获得全面的可扩展性
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/GNAT-Programming-Studio.jpg)
+
+主页: <http://libre.adacore.com/tools/gps/>
+
+### 15. Qt Creator
+
+这是一款收费的、跨平台的 IDE,用于创建连接设备、用户界面和应用程序。Qt Creator 可以让用户比应用的编码做到更多的创新。
+
+它可以用来创建移动和桌面应用程序,也可以连接到嵌入式设备。
+
+它的优点包含以下几点:
+
+- 复杂的代码编辑器
+- 支持版本控制
+- 项目和构建管理工具
+- 支持多屏幕和多平台,易于构建目标之间的切换等等
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Qt-Creator.png)
+
+主页: <https://www.qt.io/ide/>
+
+### 16. Emacs 编辑器
+
+Emacs 是一个自由的、强大的、可高度扩展的、可定制的、跨平台文本编辑器,你可以在 Linux、Solaris、FreeBSD、NetBSD、OpenBSD、Windows 和 Mac OS X 这些系统中使用该编辑器。
+
+Emacs 的核心也是一个 Emacs Lisp 的解释器,Emacs Lisp 是一种基于 Lisp 的编程语言。在撰写本文时,GNU Emacs 的最新版本是 24.5,Emacs 的基本功能包括:
+
+- 内容识别编辑模式
+- Unicode 的完全支持
+- 可使用 GUI 或 Emacs Lisp 代码高度定制
+- 下载和安装扩展的打包系统
+- 超出了正常文本编辑的功能生态系统,包括项目策划、邮件、日历和新闻阅读器等
+- 完整的内置文档,以及用户指南等等
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Emacs-Editor.png)
+
+主页: https://www.gnu.org/software/emacs/
+
+### 17. VI/VIM 编辑器
+
+Vim,一款 VI 编辑器的改进版本,是一款自由的、强大的、流行的并且高度可配置的文本编辑器。它为有效率地文本编辑而生,并且为 Unix/Linux 使用者提供了激动人心的编辑器特性,因此,它对于撰写和编辑 C/C++ 代码也是一个好的选择。
+
+总的来说,与传统的文本编辑器相比,IDE 为编程提供了更多的便利,因此使用它们是一个很好的选择。它们带有激动人心的特征并且提供了一个综合性的开发环境,有时候程序员不得不陷入对最好的 C/C++ IDE 的选择。
+
+在互联网上你还可以找到许多 IDE 来下载,但不妨试试我们推荐的这几款,可以帮助你尽快找到哪一款是你需要的。
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/best-linux-ide-editors-source-code-editors/
+
+作者:[Aaron Kili][a]
+译者:[ZenMoore](https://github.com/ZenMoore) ,[LiBrad](https://github.com/LiBrad) ,[WangYueScream](https://github.com/WangYueScream) ,[LemonDemo](https://github.com/LemonDemo)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
+[1]: http://www.tecmint.com/debug-source-code-in-linux-using-gdb/
diff --git a/published/201608/20160705 How to Encrypt a Flash Drive Using VeraCrypt.md b/published/201608/20160705 How to Encrypt a Flash Drive Using VeraCrypt.md
new file mode 100644
index 0000000000..71a2e01a24
--- /dev/null
+++ b/published/201608/20160705 How to Encrypt a Flash Drive Using VeraCrypt.md	
@@ -0,0 +1,100 @@
+用 VeraCrypt 加密闪存盘
+============================================
+
+很多安全专家偏好像 VeraCrypt 这类能够用来加密闪存盘的开源软件,是因为可以获取到它的源代码。
+
+保护 USB 闪存盘里的数据,加密是一个聪明的方法,正如我们在使用 Microsoft 的 BitLocker [加密闪存盘][1] 一文中提到的。
+
+但是如果你不想用 BitLocker 呢?
+
+你可能有顾虑,因为你不能够查看 Microsoft 的程序源码,那么它容易被植入用于政府或其它用途的“后门”。而由于开源软件的源码是公开的,很多安全专家认为开源软件很少藏有后门。
+
+还好,有几个开源加密软件能作为 BitLocker 的替代。
+
+要是你需要在 Windows 系统,苹果的 OS X 系统或者 Linux 系统上加密以及访问文件,开源软件 [VeraCrypt][2] 提供绝佳的选择。
+
+VeraCrypt 源于 TrueCrypt。TrueCrypt 是一个备受好评的开源加密软件,尽管它现在已经停止维护了。但是 TrueCrypt 的代码通过了审核,没有发现什么重要的安全漏洞。另外,在 VeraCrypt 中对它进行了改善。
+
+Windows,OS X 和 Linux 系统的版本都有。
+
+用 VeraCrypt 加密 USB 闪存盘不像用 BitLocker 那么简单,但是它也只要几分钟就好了。
+
+### 用 VeraCrypt 加密闪存盘的 8 个步骤
+
+对应你的操作系统 [下载 VeraCrypt][3] 之后:
+
+打开 VeraCrypt,点击 Create Volume,进入 VeraCrypt 的创建卷的向导程序(VeraCrypt Volume Creation Wizard)。
+
+![](http://www.esecurityplanet.com/imagesvr_ce/6246/Vera0.jpg)
+
+VeraCrypt 创建卷向导(VeraCrypt Volume Creation Wizard)允许你在闪存盘里新建一个加密文件容器,这与其它未加密文件是独立的。或者你也可以选择加密整个闪存盘。这个时候你就选加密整个闪存盘就行。
+
+![](http://www.esecurityplanet.com/imagesvr_ce/6703/Vera1.jpg)
+
+然后选择标准模式(Standard VeraCrypt Volume)。
+
+![](http://www.esecurityplanet.com/imagesvr_ce/835/Vera2.jpg)
+
+选择你想加密的闪存盘的驱动器卷标(这里是 O:)。
+
+![](http://www.esecurityplanet.com/imagesvr_ce/9427/Vera3.jpg)
+
+选择创建卷模式(Volume Creation Mode)。如果你的闪存盘是空的,或者你想要删除它里面的所有东西,选第一个。要么你想保持所有现存的文件,选第二个就好了。
+
+![](http://www.esecurityplanet.com/imagesvr_ce/7828/Vera4.jpg)
+
+这一步允许你选择加密选项。要是你不确定选哪个,就用默认的 AES 和 SHA-512 设置。
+
+![](http://www.esecurityplanet.com/imagesvr_ce/5918/Vera5.jpg)
+
+确定了卷容量后,输入并确认你想要用来加密数据密码。
+
+![](http://www.esecurityplanet.com/imagesvr_ce/3850/Vera6.jpg)
+
+要有效工作,VeraCrypt 要从一个熵或者“随机数”池中取出一个随机数。要初始化这个池,你将被要求随机地移动鼠标一分钟。一旦进度条变绿了,或者更方便的是等到进度条到了屏幕右边足够远的时候,点击 “Format” 来结束创建加密盘。
+
+![](http://www.esecurityplanet.com/imagesvr_ce/7468/Vera8.jpg)
+
+### 用 VeraCrypt 使用加密过的闪存盘
+
+当你想要使用一个加密了的闪存盘,先插入闪存盘到电脑上,启动 VeraCrypt。
+
+然后选择一个没有用过的卷标(比如 z:),点击自动挂载设备(Auto-Mount Devices)。
+
+![](http://www.esecurityplanet.com/imagesvr_ce/2016/Vera10.jpg)
+
+输入密码,点击确定。
+
+![](http://www.esecurityplanet.com/imagesvr_ce/8222/Vera11.jpg)
+
+挂载过程需要几分钟,这之后你的解密盘就能通过你先前选择的盘符进行访问了。
+
+### VeraCrypt 移动硬盘安装步骤
+
+如果你设置闪存盘的时候,选择的是加密过的容器而不是加密整个盘,你可以选择创建 VeraCrypt 称为移动盘(Traveler Disk)的设备。这会复制安装一个 VeraCrypt 到 USB 闪存盘。当你在别的 Windows 电脑上插入 U 盘时,就能从 U 盘自动运行 VeraCrypt;也就是说没必要在新电脑上安装 VeraCrypt。
+
+你可以设置闪存盘作为一个移动硬盘(Traveler Disk),在 VeraCrypt 的工具栏(Tools)菜单里选择 Traveler Disk SetUp 就行了。
+
+![](http://www.esecurityplanet.com/imagesvr_ce/5812/Vera12.jpg)
+
+要从移动盘(Traveler Disk)上运行 VeraCrypt,你必须要有那台电脑的管理员权限,这不足为奇。尽管这看起来是个限制,机密文件无法在不受控制的电脑上安全打开,比如在一个商务中心的电脑上。
+
+> 本文作者 Paul Rubens 从事技术行业已经超过 20 年。这期间他为英国和国际主要的出版社,包括 《The Economist》《The Times》《Financial Times》《The BBC》《Computing》和《ServerWatch》等出版社写过文章,
+
+--------------------------------------------------------------------------------
+
+via: http://www.esecurityplanet.com/open-source-security/how-to-encrypt-flash-drive-using-veracrypt.html
+
+作者:[Paul Rubens][a]
+译者:[GitFuture](https://github.com/GitFuture)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.esecurityplanet.com/author/3700/Paul-Rubens
+[1]: http://www.esecurityplanet.com/views/article.php/3880616/How-to-Encrypt-a-USB-Flash-Drive.htm
+[2]: http://www.esecurityplanet.com/open-source-security/veracrypt-a-worthy-truecrypt-alternative.html
+[3]: https://veracrypt.codeplex.com/releases/view/619351
+
+
+
diff --git a/published/201608/20160706 Doing for User Space What We Did for Kernel Space.md b/published/201608/20160706 Doing for User Space What We Did for Kernel Space.md
new file mode 100644
index 0000000000..f9dfb89593
--- /dev/null
+++ b/published/201608/20160706 Doing for User Space What We Did for Kernel Space.md	
@@ -0,0 +1,98 @@
+在用户空间做我们会在内核空间做的事情
+=======================================================
+
+我相信,Linux 最好也是最坏的事情,就是内核空间(kernel space)和用户空间(user space)之间的巨大差别。
+
+如果没有这个区别,Linux 可能也不会成为世界上影响力最大的操作系统。如今,Linux 的使用范围在世界上是最大的,而这些应用又有着世界上最大的用户群——尽管大多数用户并不知道,当他们进行谷歌搜索或者触摸安卓手机的时候,他们其实正在使用 Linux。如果不是 Linux 的巨大成功,Apple 公司也可能并不会成为现在这样(即在他们的电脑产品中使用 BSD 的技术)(LCTT 译注:Linux 获得成功后,Apple 曾与 Linus 协商使用 Linux 核心作为 Apple 电脑的操作系统并帮助开发的事宜,但遭到拒绝。因此,Apple 转向使用许可证更为宽松的 BSD 。)。
+
+不(需要)关注用户空间是 Linux 内核开发中的一个特点而非缺陷。正如 Linus 在 2003 年的[极客巡航(Geek Cruise)][18]中提到的那样,“我只做内核相关的东西……我并不知道内核之外发生的事情,而且我也并不关心。我只关注内核部分发生的事情。” 多年之后的[另一次极客巡航][19]上, Andrew Morton 给我上了另外的一课,这之后我写道:
+
+> 内核空间是Linux 所在的地方,而用户空间是 Linux 与其它的“自然材料”一起使用的地方。内核空间和用户空间的区别,和自然材料与人类用其生产的人造材料的区别很类似。
+
+这个区别是自然而然的结果,就是尽管外面的世界一刻也离不开 Linux, 但是 Linux 社区还是保持相对较小。所以,为了增加哪怕一点我们社区团体的规模,我希望指出两件事情。第一件已经非常火了,另外一件可能会火起来。
+
+第一件事情就是 [区块链(blockchain)][1],出自著名的分布式货币——比特币之手。当你正在阅读这篇文章的同时,人们对区块链的[关注度正在直线上升][2]。
+
+![](http://www.linuxjournal.com/files/linuxjournal.com/ufiles/imagecache/large-550px-centered/u1000009/12042f1.png)
+
+*图1. 区块链的谷歌搜索趋势*
+
+第二件事就是自主身份(self-sovereign identity)。为了解释这个概念,让我先来问你:你是谁,你来自哪里?
+
+如果你从你的老板、你的医生或者车管所,Facebook、Twitter 或者谷歌上得到答案,你就会发现它们都是行政身份(administrative identifiers)——这些机构完全以自己的便利为原因设置这些身份和职位。正如一家区块链技术公司 [Evernym][3] 的 Timothy Ruff 所说,“你并不因组织而存在,但你的身份却因此存在。”身份是个因变量。自变量——即控制着身份的变量——是(你所在的)组织。
+
+如果你的答案出自你自己,我们就有一个广大空间来发展一个新的领域,在这个领域中,我们完全自由。
+
+据我所知,第一个解释这个的人是 [Devon Loffreto][4]。在 2012 年 2 月,他在博客 [Moxy Tongue][5] 中写道:“什么是 'Sovereign Source Authority'?”。在发表于 2016 年 2 月的 “[Self-Sovereign Identity][6]” 一文中,他写道:
+
+> 自主身份必须是独立个人提出的,并且不包含社会因素……自主身份源于每个个体对其自身本源的认识。 一个自主身份可以为个体带来新的社会面貌。每个个体都可能为自己生成一个自主身份,并且这并不会改变固有的人权。使用自主身份机制是所有参与者参与的基石,并且依旧可以同各种形式的人类社会保持联系。
+
+将这个概念放在 Linux 领域中,只有个人才能为他或她设定一个自己的开源社区身份。这在现实实践中,这只是一个非常正常的事件。举个例子,我自己的身份包括:
+
+- David Allen Searls,我父母会这样叫我。
+- David Searls,正式场合下我会这么称呼自己。
+- Dave,我的亲戚和好朋友会这么叫我。
+- Doc,大多数人会这么叫我。
+
+作为承认以上称呼的自主身份来源,我可以在不同的情景中轻易的转换。但是,这只是在现实世界中。在虚拟世界中,这就变得非常困难。除了上述的身份之外,我还可以是 @dsearls (我的 twitter 账号) 和 dsearls (其他的网络账号)。然而为了记住成百上千的不同账号的登录名和密码,我已经不堪重负。
+
+你可以在你的浏览器上感受到这个糟糕的体验。在火狐上,我有成百上千个用户名密码。很多已经废弃(很多都是从 Netscape 时代遗留下来的),但是我想会有大量的工作账号需要处理。对于这些,我只是被动接受者。没有其他的解决方法。甚至一些安全较低的用户认证,已经成为了现实世界中不可缺少的一环。
+
+现在,最简单的方式来联系账号,就是通过 “Log in with Facebook” 或者 “Login in with Twitter” 来进行身份认证。在这种情况下,我们中的每一个甚至并不是真正意义上的自己,甚至(如果我们希望被其他人认识的话)缺乏对其他实体如何认识我们的控制。
+
+我们从一开始就需要的是一个可以实体化我们的自主身份和交流时选择如何保护和展示自身的个人系统。因为缺少这个能力,我们现在陷入混乱。Shoshana Zuboff 称之为 “监视资本主义”,她如此说道:
+
+>...难以想象,在见证了互联网和获得了的巨大成功的谷歌背后。世界因 Apple 和 FBI 的对决而紧密联系在一起。讲道理,热衷于监视的资本家开发的监视系统是每一个国家安全机构都渴望的。
+
+然后,她问道,”我们怎样才能保护自己远离他人的影响?“
+
+我建议使用自主身份。我相信这是我们唯一的既可以保证我们从监视中逃脱、又可以使我们有一个有序的世界的办法。以此为基础,我们才可以完全无顾忌地和社会,政治,商业上的人交流。
+
+我在五月联合国举行的 [ID2020][7] 会议中总结了这个临时的结论。很高兴,Devon Loffreto 也在那,他于 2013 年推动了自主身份的创立。这是[我那时写的一些文章][8],引用了 Devon 的早期博客(比如上面的原文)。
+
+这有三篇这个领域的准则:
+
+- "[Self-Sovereign Identity][9]" - Devon Loffreto.
+- "[System or Human First][10]" - Devon Loffreto.
+- "[The Path to Self-Sovereign Identity][11]" - Christopher Allen.
+
+从 Evernym 的简要说明中,[digi.me][12]、 [iRespond][13] 和 [Respect Network][14] 也被包括在内。自主身份和社会身份 (也被称为“当前模式(current model)”) 的对比结果,显示在图二中。
+
+![](http://www.linuxjournal.com/files/linuxjournal.com/ufiles/imagecache/large-550px-centered/u1000009/12042f2.jpg)
+
+*图 2. 当前模式身份 vs. 自主身份*
+
+Sovrin 就是为此而生的[平台][15],它阐述自己为一个“依托于先进、专用、经授权、分布式平台的,完全开源、基于标识的身份声明图平台”。同时,这也有一本[白皮书][16]。它的代码名为 [plenum][17],并且公开在 Github 上。
+
+在这里——或者其他类似的地方——我们就可以在用户空间中重现我们在过去 25 年中在内核空间做过的事情。
+
+--------------------------------------------------------------------------------
+
+via: https://www.linuxjournal.com/content/doing-user-space-what-we-did-kernel-space
+
+作者:[Doc Searls][a]
+译者:[MikeCoder](https://github.com/MikeCoder)
+校对:[PurlingNayuki](https://github.com/PurlingNayuki)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.linuxjournal.com/users/doc-searls
+[1]: https://en.wikipedia.org/wiki/Block_chain_%28database%29
+[2]: https://www.google.com/trends/explore#q=blockchain
+[3]: http://evernym.com/
+[4]: https://twitter.com/nzn
+[5]: http://www.moxytongue.com/2012/02/what-is-sovereign-source-authority.html
+[6]: http://www.moxytongue.com/2016/02/self-sovereign-identity.html
+[7]: http://www.id2020.org/
+[8]: http://blogs.harvard.edu/doc/2013/10/14/iiw-challenge-1-sovereign-identity-in-the-great-silo-forest
+[9]: http://www.moxytongue.com/2016/02/self-sovereign-identity.html
+[10]: http://www.moxytongue.com/2016/05/system-or-human.html
+[11]: http://www.lifewithalacrity.com/2016/04/the-path-to-self-soverereign-identity.html
+[12]: https://get.digi.me/
+[13]: http://irespond.com/
+[14]: https://www.respectnetwork.com/
+[15]: http://evernym.com/technology
+[16]: http://evernym.com/assets/doc/Identity-System-Essentials.pdf?v=167284fd65
+[17]: https://github.com/evernym/plenum
+[18]: http://www.linuxjournal.com/article/6427
+[19]: http://www.linuxjournal.com/article/8664
\ No newline at end of file
diff --git a/published/201608/20160706 Getting started with Docker Swarm and deploying a replicated Python 3 Application.md b/published/201608/20160706 Getting started with Docker Swarm and deploying a replicated Python 3 Application.md
new file mode 100644
index 0000000000..36b0cbd7c4
--- /dev/null
+++ b/published/201608/20160706 Getting started with Docker Swarm and deploying a replicated Python 3 Application.md	
@@ -0,0 +1,279 @@
+使用 Docker Swarm 部署可扩展的 Python3 应用
+==============
+
+[Ben Firshman][2] 最近在 [Dockercon][1] 做了一个关于使用 Docker 构建无服务应用的演讲,你可以在[这里查看详情][3](还有视频)。之后,我写了一篇关于如何使用 [AWS Lambda][5] 构建微服务系统的[文章][4]。
+
+今天,我想展示给你的就是如何使用 [Docker Swarm][6] 部署一个简单的 Python Falcon REST 应用。这里我不会使用[dockerrun][7] 或者是其他无服务特性,你可能会惊讶,使用 Docker Swarm 部署(复制)一个 Python(Java、Go 都一样)应用是如此的简单。
+
+注意:这展示的部分步骤是截取自 [Swarm Tutorial][8],我已经修改了部分内容,并且[增加了一个 Vagrant Helper 的仓库][9]来启动一个可以让 Docker Swarm 工作起来的本地测试环境。请确保你使用的是 1.12 或以上版本的 Docker Engine。我写这篇文章的时候,使用的是 1.12RC2 版本。注意的是,这只是一个测试版本,可能还会有修改。
+
+你要做的第一件事,就是如果你想本地运行的话,你要保证 [Vagrant][10] 已经正确的安装和运行了。你也可以按如下步骤使用你最喜欢的云服务提供商部署 Docker Swarm 虚拟机系统。
+
+我们将会使用这三台 VM:一个简单的 Docker Swarm 管理平台和两台 worker。
+
+安全注意事项:Vagrantfile 代码中包含了部分位于 Docker 测试服务器上的 shell 脚本。这是一个潜在的安全问题,它会运行你不能控制的脚本,所以请确保你会在运行代码之前[审查过这部分的脚本][11]。
+
+```
+$ git clone https://github.com/chadlung/vagrant-docker-swarm
+$ cd vagrant-docker-swarm
+$ vagrant plugin install vagrant-vbguest
+$ vagrant up
+```
+
+Vagrant up 命令需要一些时间才能完成。
+
+SSH 登陆进入 manager1 虚拟机:
+
+```
+$ vagrant ssh manager1
+```
+
+在 manager1 的 ssh 终端会话中执行如下命令:
+
+```
+$ sudo docker swarm init --listen-addr 192.168.99.100:2377
+```
+
+现在还没有 worker 注册上来:
+
+```
+$ sudo docker node ls
+```
+
+让我们注册两个新的 worker,请打开两个新的终端会话(保持 manager1 会话继续运行):
+
+```
+$ vagrant ssh worker1
+```
+
+在 worker1 的 ssh 终端会话上执行如下命令:
+
+```
+$ sudo docker swarm join 192.168.99.100:2377
+```
+
+在 worker2 的 ssh 终端会话上重复这些命令。
+
+在 manager1 终端上执行如下命令:
+
+```
+$ docker node ls
+```
+
+你将会看到:
+
+![](http://www.giantflyingsaucer.com/blog/wp-content/uploads/2016/06/Screen-Shot-2016-06-28-at-3.15.25-PM.png)
+
+在 manager1 的终端里部署一个简单的服务。
+
+```
+sudo docker service create --replicas 1 --name pinger alpine ping google.com
+```
+
+这个命令将会部署一个服务,它会从 worker 之一 ping google.com。(或者 manager,manager 也可以运行服务,不过如果你只是想 worker 运行容器的话,[也可以禁用这一点][12])。可以使用如下命令,查看哪些节点正在执行服务:
+
+```
+$ sudo docker service tasks pinger
+```
+
+结果会和这个比较类似:
+
+![](http://www.giantflyingsaucer.com/blog/wp-content/uploads/2016/06/Screen-Shot-2016-06-28-at-5.23.05-PM.png)
+
+所以,我们知道了服务正跑在 worker1 上。我们可以回到 worker1 的会话里,然后进入正在运行的容器:
+
+```
+$ sudo docker ps
+```
+
+![](http://www.giantflyingsaucer.com/blog/wp-content/uploads/2016/06/Screen-Shot-2016-06-28-at-5.25.02-PM.png)
+
+你可以看到容器的 id 是: ae56769b9d4d,在我的例子中,我运行如下的代码:
+
+```
+$ sudo docker attach ae56769b9d4d
+```
+
+![](http://www.giantflyingsaucer.com/blog/wp-content/uploads/2016/06/Screen-Shot-2016-06-28-at-5.26.49-PM.png)
+
+你可以按下 CTRL-C 来停止服务。
+
+回到 manager1,然后移除这个 pinger 服务。
+
+```
+$ sudo docker service rm pinger
+```
+
+现在,我们将会部署可复制的 Python 应用。注意,为了保持文章的简洁,而且容易复制,所以部署的是一个简单的应用。
+
+你需要做的第一件事就是将镜像放到 [Docker Hub][13]上,或者使用我[已经上传的一个][14]。这是一个简单的 Python 3 Falcon REST 应用。它有一个简单的入口: /hello 带一个 value 参数。
+
+放在 [chadlung/hello-app][15] 上的 Python 代码看起来像这样:
+
+```
+import json
+from wsgiref import simple_server
+ 
+import falcon
+ 
+ 
+class HelloResource(object):
+    def on_get(self, req, resp):
+        try:
+            value = req.get_param('value')
+ 
+            resp.content_type = 'application/json'
+            resp.status = falcon.HTTP_200
+            resp.body = json.dumps({'message': str(value)})
+        except Exception as ex:
+            resp.status = falcon.HTTP_500
+            resp.body = str(ex)
+ 
+ 
+if __name__ == '__main__':
+    app = falcon.API()
+    hello_resource = HelloResource()
+    app.add_route('/hello', hello_resource)
+    httpd = simple_server.make_server('0.0.0.0', 8080, app)
+    httpd.serve_forever()
+```
+
+Dockerfile 很简单:
+
+```
+FROM python:3.4.4
+ 
+RUN pip install -U pip
+RUN pip install -U falcon
+ 
+EXPOSE 8080
+ 
+COPY . /hello-app
+WORKDIR /hello-app
+ 
+CMD ["python", "app.py"]
+```
+
+上面表示的意思很简单,如果你想,你可以在本地运行该进行来访问这个入口: <http://127.0.0.1:8080/hello?value=Fred>
+
+这将返回如下结果:
+
+```
+{"message": "Fred"}
+```
+
+在 Docker Hub 上构建和部署这个 hello-app(修改成你自己的 Docker Hub 仓库或者[使用这个][15]):
+
+```
+$ sudo docker build . -t chadlung/hello-app:2
+$ sudo docker push chadlung/hello-app:2
+```
+
+现在,我们可以将应用部署到之前的 Docker Swarm 了。登录 manager1 的 ssh 终端会话,并且执行:
+
+```
+$ sudo docker service create -p 8080:8080 --replicas 2 --name hello-app chadlung/hello-app:2
+$ sudo docker service inspect --pretty hello-app
+$ sudo docker service tasks hello-app
+```
+
+现在,我们已经可以测试了。使用任何一个 Swarm 节点的 IP 来访问 /hello 入口。在本例中,我在 manager1 的终端里使用 curl 命令:
+
+注意,Swarm 中的所有的 IP 都可以,不管这个服务是运行在一台还是更多的节点上。
+
+```
+$ curl -v -X GET "http://192.168.99.100:8080/hello?value=Chad"
+$ curl -v -X GET "http://192.168.99.101:8080/hello?value=Test"
+$ curl -v -X GET "http://192.168.99.102:8080/hello?value=Docker"
+```
+
+结果:
+
+```
+* Hostname was NOT found in DNS cache
+*   Trying 192.168.99.101...
+* Connected to 192.168.99.101 (192.168.99.101) port 8080 (#0)
+> GET /hello?value=Chad HTTP/1.1
+> User-Agent: curl/7.35.0
+> Host: 192.168.99.101:8080
+> Accept: */*
+> 
+* HTTP 1.0, assume close after body
+< HTTP/1.0 200 OK
+< Date: Tue, 28 Jun 2016 23:52:55 GMT
+< Server: WSGIServer/0.2 CPython/3.4.4
+< content-type: application/json
+< content-length: 19
+< 
+{"message": "Chad"}
+```
+
+从浏览器中访问其他节点:
+
+![](http://www.giantflyingsaucer.com/blog/wp-content/uploads/2016/06/Screen-Shot-2016-06-28-at-6.54.31-PM.png)
+
+如果你想看运行的所有服务,你可以在 manager1 节点上运行如下代码:
+
+```
+$ sudo docker service ls
+```
+
+如果你想添加可视化控制平台,你可以安装 [Docker Swarm Visualizer][16](这对于展示非常方便)。在 manager1 的终端中执行如下代码:
+
+```
+$ sudo docker run -it -d -p 5000:5000 -e HOST=192.168.99.100 -e PORT=5000 -v /var/run/docker.sock:/var/run/docker.sock manomarks/visualizer)
+```
+
+打开你的浏览器,并且访问: <http://192.168.99.100:5000/>
+
+结果如下(假设已经运行了两个 Docker Swarm 服务):
+
+![](http://www.giantflyingsaucer.com/blog/wp-content/uploads/2016/06/Screen-Shot-2016-06-30-at-2.37.28-PM.png)
+
+要停止运行 hello-app(已经在两个节点上运行了),可以在 manager1 上执行这个代码:
+
+```
+$ sudo docker service rm hello-app
+```
+
+如果想停止 Visualizer, 那么在 manager1 的终端中执行:
+
+```
+$ sudo docker ps
+```
+
+获得容器的 ID,这里是: f71fec0d3ce1,从 manager1 的终端会话中执行这个代码:
+
+```
+$ sudo docker stop f71fec0d3ce1
+```
+
+祝你成功使用 Docker Swarm。这篇文章主要是以 1.12 版本来进行描述的。
+
+--------------------------------------------------------------------------------
+
+via: http://www.giantflyingsaucer.com/blog/?p=5923
+
+作者:[Chad Lung][a]
+译者:[MikeCoder](https://github.com/MikeCoder)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.giantflyingsaucer.com/blog/?author=2
+[1]: http://dockercon.com/
+[2]: https://blog.docker.com/author/bfirshman/
+[3]: https://blog.docker.com/author/bfirshman/
+[4]: http://www.giantflyingsaucer.com/blog/?p=5730
+[5]: https://aws.amazon.com/lambda/
+[6]: https://docs.docker.com/swarm/
+[7]: https://github.com/bfirsh/dockerrun
+[8]: https://docs.docker.com/engine/swarm/swarm-tutorial/
+[9]: https://github.com/chadlung/vagrant-docker-swarm
+[10]: https://www.vagrantup.com/
+[11]: https://test.docker.com/
+[12]: https://docs.docker.com/engine/reference/commandline/swarm_init/
+[13]: https://hub.docker.com/
+[14]: https://hub.docker.com/r/chadlung/hello-app/
+[15]: https://hub.docker.com/r/chadlung/hello-app/
+[16]: https://github.com/ManoMarks/docker-swarm-visualizer
diff --git a/published/201608/20160706 What is Git.md b/published/201608/20160706 What is Git.md
new file mode 100644
index 0000000000..f0fa7dded8
--- /dev/null
+++ b/published/201608/20160706 What is Git.md	
@@ -0,0 +1,118 @@
+Git 系列(一):什么是 Git
+===========
+
+欢迎阅读本系列关于如何使用 Git 版本控制系统的教程!通过本文的介绍,你将会了解到 Git 的用途及谁该使用 Git。
+
+如果你刚步入开源的世界,你很有可能会遇到一些在 Git 上托管代码或者发布使用版本的开源软件。事实上,不管你知道与否,你都在使用基于 Git 进行版本管理的软件:Linux 内核(就算你没有在手机或者电脑上使用 Linux,你正在访问的网站也是运行在 Linux 系统上的),Firefox、Chrome 等其他很多项目都通过 Git 代码库和世界各地开发者共享他们的代码。
+
+换个角度来说,你是否仅仅通过 Git 就可以和其他人共享你的代码?你是否可以在家里或者企业里私有化的使用 Git?你必须要通过一个 GitHub 账号来使用 Git 吗?为什么要使用 Git 呢?Git 的优势又是什么?Git 是我唯一的选择吗?这对 Git 所有的疑问都会把我们搞的一脑浆糊。
+
+因此,忘记你以前所知的 Git,让我们重新走进 Git 世界的大门。
+
+### 什么是版本控制系统?
+
+Git 首先是一个版本控制系统。现在市面上有很多不同的版本控制系统:CVS、SVN、Mercurial、Fossil 当然还有 Git。
+
+很多像 GitHub 和 GitLab 这样的服务是以 Git 为基础的,但是你也可以只使用 Git 而无需使用其他额外的服务。这意味着你可以以私有或者公有的方式来使用 Git。
+
+如果你曾经和其他人有过任何电子文件方面的合作,你就会知道传统版本管理的工作流程。开始是很简单的:你有一个原始的版本,你把这个版本发送给你的同事,他们在接收到的版本上做了些修改,现在你们有两个版本了,然后他们把他们手上修改过的版本发回来给你。你把他们的修改合并到你手上的版本中,现在两个版本又合并成一个最新的版本了。
+
+然后,你修改了你手上最新的版本,同时,你的同事也修改了他们手上合并前的版本。现在你们有 3 个不同的版本了,分别是合并后最新的版本,你修改后的版本,你同事手上继续修改过的版本。至此,你们的版本管理工作开始变得越来越混乱了。
+
+正如 Jason van Gumster 在他的文章中指出 [即使是艺术家也需要版本控制][1],而且已经在个别人那里发现了这种趋势变化。无论是艺术家还是科学家,开发一个某种实验版本是并不鲜见的;在你的项目中,可能有某个版本大获成功,把项目推向一个新的高度,也可能有某个版本惨遭失败。因此,最终你不可避免的会创建出一堆名为project\_justTesting.kdenlive、project\_betterVersion.kdenlive、project\_best\_FINAL.kdenlive、project\_FINAL-alternateVersion.kdenlive 等类似名称的文件。
+
+不管你是修改一个 for 循环,还是一些简单的文本编辑,一个好的版本控制系统都会让我们的生活更加的轻松。
+
+### Git 快照
+
+Git 可以为项目创建快照,并且存储这些快照为唯一的版本。
+
+如果你将项目带领到了一个错误的方向上,你可以回退到上一个正确的版本,并且开始尝试另一个可行的方向。
+
+如果你是和别人合作开发,当有人向你发送他们的修改时,你可以将这些修改合并到你的工作分支中,然后你的同事就可以获取到合并后的最新版本,并在此基础上继续工作。
+
+Git 并不是魔法,因此冲突还是会发生的(“你修改了某文件的最后一行,但是我把这行整行都删除了;我们怎样处理这些冲突呢?”),但是总体而言,Git 会为你保留了所有更改的历史版本,甚至允许并行版本。这为你保留了以任何方式处理冲突的能力。
+
+### 分布式 Git
+
+在不同的机器上为同一个项目工作是一件复杂的事情。因为在你开始工作时,你想要获得项目的最新版本,然后此基础上进行修改,最后向你的同事共享这些改动。传统的方法是通过笨重的在线文件共享服务或者老旧的电邮附件,但是这两种方式都是效率低下且容易出错。
+
+Git 天生是为分布式工作设计的。如果你要参与到某个项目中,你可以克隆(clone)该项目的 Git 仓库,然后就像这个项目只有你本地一个版本一样对项目进行修改。最后使用一些简单的命令你就可以拉取(pull)其他开发者的修改,或者你可以把你的修改推送(push)给别人。现在不用担心谁手上的是最新的版本,或者谁的版本又存放在哪里等这些问题了。全部人都是在本地进行开发,然后向共同的目标推送或者拉取更新。(或者不是共同的目标,这取决于项目的开发方式)。
+
+### Git 界面
+
+最原始的 Git 是运行在 Linux 终端上的应用软件。然而,得益于 Git 是开源的,并且拥有良好的设计,世界各地的开发者都可以为 Git 设计不同的访问界面。
+
+Git 完全是免费的,并且已经打包在 Linux,BSD,Illumos 和其他类 Unix 系统中,Git 命令看起来像这样:
+
+```
+$ git --version
+git version 2.5.3
+```
+
+可能最著名的 Git 访问界面是基于网页的,像 GitHub、开源的 GitLab、Savannah、BitBucket 和 SourceForge 这些网站都是基于网页端的 Git 界面。这些站点为面向公众和面向社会的开源软件提供了最大限度的代码托管服务。在一定程度上,基于浏览器的图形界面(GUI)可以尽量的减缓 Git 的学习曲线。下面的 GitLab 界面的截图:
+
+![](https://opensource.com/sites/default/files/0_gitlab.png)
+
+再者,第三方 Git 服务提供商或者独立开发者甚至可以在 Git 的基础上开发出不是基于 HTML 的定制化前端界面。此类界面让你可以不用打开浏览器就可以方便的使用 Git 进行版本管理。其中对用户最透明的方式是直接集成到文件管理器中。KDE 文件管理器 Dolphin 可以直接在目录中显示 Git 状态,甚至支持提交,推送和拉取更新操作。
+
+![](https://opensource.com/sites/default/files/0_dolphin.jpg)
+
+[Sparkleshare][2] 使用 Git 作为其 Dropbox 式的文件共享界面的基础。
+
+![](https://opensource.com/sites/default/files/0_sparkleshare_1.jpg)
+
+想了解更多的内容,可以查看 [Git wiki][3],这个(长长的)页面中展示了很多 Git 的图形界面项目。
+
+### 谁应该使用 Git?
+
+就是你!我们更应该关心的问题是什么时候使用 Git?和用 Git 来干嘛?
+
+### 我应该在什么时候使用 Git 呢?我要用 Git 来干嘛呢?
+
+想更深入的学习 Git,我们必须比平常考虑更多关于文件格式的问题。
+
+Git 是为了管理源代码而设计的,在大多数编程语言中,源代码就意味者一行行的文本。当然,Git 并不知道你把这些文本当成是源代码还是下一部伟大的美式小说。因此,只要文件内容是以文本构成的,使用 Git 来跟踪和管理其版本就是一个很好的选择了。
+
+但是什么是文本呢?如果你在像 Libre Office 这类办公软件中编辑一些内容,通常并不会产生纯文本内容。因为通常复杂的应用软件都会对原始的文本内容进行一层封装,就如把原始文本内容用 XML 标记语言包装起来,然后封装在 Zip 包中。这种对原始文本内容进行一层封装的做法可以保证当你把文件发送给其他人时,他们可以看到你在办公软件中编辑的内容及特定的文本效果。奇怪的是,虽然,通常你的需求可能会很复杂,就像保存 [Kdenlive][4] 项目文件,或者保存从 [Inkscape][5] 导出的SVG文件,但是,事实上使用 Git 管理像 XML 文本这样的纯文本类容是最简单的。
+
+如果你在使用 Unix 系统,你可以使用 `file` 命令来查看文件内容构成:
+
+```
+$ file ~/path/to/my-file.blah
+my-file.blah: ASCII text
+$ file ~/path/to/different-file.kra: Zip data (MIME type "application/x-krita")
+```
+
+如果还是不确定,你可以使用 `head` 命令来查看文件内容:
+
+```
+$ head ~/path/to/my-file.blah
+```
+
+如果输出的文本你基本能看懂,这个文件就很有可能是文本文件。如果你仅仅在一堆乱码中偶尔看到几个熟悉的字符,那么这个文件就可能不是文本文件了。
+
+准确的说:Git 可以管理其他格式的文件,但是它会把这些文件当成二进制大对象(blob)。两者的区别是,在文本文件中,Git 可以明确的告诉你在这两个快照(或者说提交)间有 3 行是修改过的。但是如果你在两个提交(commit)之间对一张图片进行的编辑操作,Git 会怎么指出这种修改呢?实际上,因为图片并不是以某种可以增加或删除的有意义的文本构成,因此 Git 并不能明确的描述这种变化。当然我个人是非常希望图片的编辑可以像把文本“\<sky>丑陋的蓝绿色\</sky>”修改成“\<sky>漂浮着蓬松白云的天蓝色\</sky>”一样的简单,但是事实上图片的编辑并没有这么简单。
+
+经常有人在 Git 上放入 png 图标、电子表格或者流程图这类二进制大型对象(blob)。尽管,我们知道在 Git 上管理此类大型文件并不直观,但是,如果你需要使用 Git 来管理此类文件,你也并不需要过多的担心。如果你参与的项目同时生成文本文件和二进制大文件对象(如视频游戏中常见的场景,这些和源代码同样重要的图像和音频材料),那么你有两条路可以走:要么开发出你自己的解决方案,就如使用指向共享网络驱动器的引用;要么使用 Git 插件,如 Joey Hess 开发的 [git annex][6],以及 [Git-Media][7] 项目。
+
+你看,Git 真的是一个任何人都可以使用的工具。它是你进行文件版本管理的一个强大而且好用工具,同时它并没有你开始认为的那么可怕。
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/resources/what-is-git
+
+作者:[Seth Kenlon][a]
+译者:[cvsher](https://github.com/cvsher)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/seth
+[1]: https://opensource.com/life/16/2/version-control-isnt-just-programmers
+[2]: http://sparkleshare.org/
+[3]: https://git.wiki.kernel.org/index.php/InterfacesFrontendsAndTools#Graphical_Interfaces
+[4]: https://opensource.com/life/11/11/introduction-kdenlive
+[5]: http://inkscape.org/
+[6]: https://git-annex.branchable.com/
+[7]: https://github.com/alebedev/git-media
diff --git a/published/201608/20160711 Getting started with Git.md b/published/201608/20160711 Getting started with Git.md
new file mode 100644
index 0000000000..a490e59e2d
--- /dev/null
+++ b/published/201608/20160711 Getting started with Git.md	
@@ -0,0 +1,141 @@
+初步了解 Git
+=========================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/life/get_started_lead.jpeg?itok=r22AKc6P)
+
+*图片来源:opensource.com*
+
+在这个系列的[介绍篇][4]中,我们学习到了谁应该使用 Git,以及 Git 是用来做什么的。今天,我们将学习如何克隆公共 Git 仓库,以及如何提取出独立的文件而不用克隆整个仓库。
+
+由于 Git 如此流行,因而如果你能够至少熟悉一些基础的 Git 知识也能为你的生活带来很多便捷。如果你可以掌握 Git 基础(你可以的,我发誓!),那么你将能够下载任何你需要的东西,甚至还可能做一些贡献作为回馈。毕竟,那就是开源的精髓所在:你拥有获取你使用的软件代码的权利,拥有和他人分享的自由,以及只要你愿意就可以修改它的权利。只要你熟悉了 Git,它就可以让这一切都变得很容易。
+
+那么,让我们一起来熟悉 Git 吧。
+
+### 读和写
+
+一般来说,有两种方法可以和 Git 仓库交互:你可以从仓库中读取,或者你也能够向仓库中写入。它就像一个文件:有时候你打开一个文档只是为了阅读它,而其它时候你打开文档是因为你需要做些改动。
+
+本文仅讲解如何从 Git 仓库读取。我们将会在后面的一篇文章中讲解如何向 Git 仓库写回的主题。
+
+### Git 还是 GitHub?
+
+一句话澄清:Git 不同于 GitHub(或 GitLab,或 Bitbucket)。Git 是一个命令行程序,所以它就像下面这样:
+
+```
+$ git
+usage: Git [--version] [--help] [-C <path>] 
+  [-p | --paginate | --no-pager] [--bare]
+  [--Git-dir=<path>] <command> [<args>]
+
+```
+
+由于 Git 是开源的,所以就有许多聪明人围绕它构建了基础软件;这些基础软件,包括在他们自己身边,都已经变得非常流行了。
+
+我的文章系列将首先教你纯粹的 Git 知识,因为一旦你理解了 Git 在做什么,那么你就无需关心正在使用的前端工具是什么了。然而,我的文章系列也将涵盖通过流行的 Git 服务完成每项任务的常用方法,因为那些将可能是你首先会遇到的。
+
+### 安装 Git
+
+在 Linux 系统上,你可以从所使用的发行版软件仓库中获取并安装 Git。BSD 用户应当在 Ports 树的 devel 部分查找 Git。
+
+对于闭源的操作系统,请前往其[项目官网][1],并根据说明安装。一旦安装后,在 Linux、BSD 和 Mac OS X 上的命令应当没有任何差别。Windows 用户需要调整 Git 命令,从而和 Windows 文件系统相匹配,或者安装 Cygwin 以原生的方式运行 Git,而不受 Windows 文件系统转换问题的羁绊。
+
+### Git 下午茶
+
+并非每个人都需要立刻将 Git 加入到我们的日常生活中。有些时候,你和 Git 最多的交互就是访问一个代码库,下载一两个文件,然后就不用它了。以这样的方式看待 Git,它更像是下午茶而非一次正式的宴会。你进行一些礼节性的交谈,获得了需要的信息,然后你就会离开,至少接下来的三个月你不再想这样说话。
+
+当然,那是可以的。
+
+一般来说,有两种方法访问 Git:使用命令行,或者使用一种神奇的因特网技术通过 web 浏览器快速轻松地访问。
+
+假设你想要给终端安装一个回收站,因为你已经被 rm 命令毁掉太多次了。你可能听说过 Trashy ,它称自己为「理智的 rm 命令中间人」,也许你想在安装它之前阅读它的文档。幸运的是,[Trashy 公开地托管在 GitLab.com][2]。
+
+### Landgrab
+
+我们工作的第一步是对这个 Git 仓库使用 landgrab 排序方法:我们会克隆这个完整的仓库,然后会根据内容排序。由于该仓库是托管在公共的 Git 服务平台上,所以有两种方式来完成工作:使用命令行,或者使用 web 界面。
+
+要想使用 Git 获取整个仓库,就要使用 git clone 命令和 Git 仓库的 URL 作为参数。如果你不清楚正确的 URL 是什么,仓库应该会告诉你的。GitLab 为你提供了 [Trashy][3] 仓库的用于拷贝粘贴的 URL。
+
+![](https://opensource.com/sites/default/files/1_gitlab-url.jpg)
+
+你也许注意到了,在某些服务平台上,会同时提供 SSH 和 HTTPS 链接。只有当你拥有仓库的写权限时,你才可以使用 SSH。否则的话,你必须使用 HTTPS URL。
+
+一旦你获得了正确的 URL,克隆仓库是非常容易的。就是 git clone 该 URL 即可,以及一个可选的指定要克隆到的目录。默认情况下会将 git 目录克隆到你当前所在的目录;例如,'trashy.git' 将会克隆到你当前位置的 'trashy' 目录。我使用 .clone 扩展名标记那些只读的仓库,而使用 .git 扩展名标记那些我可以读写的仓库,不过这并不是官方要求的。
+
+```
+$ git clone https://gitlab.com/trashy/trashy.git trashy.clone
+Cloning into 'trashy.clone'...
+remote: Counting objects: 142, done.
+remote: Compressing objects: 100% (91/91), done.
+remote: Total 142 (delta 70), reused 103 (delta 47)
+Receiving objects: 100% (142/142), 25.99 KiB | 0 bytes/s, done.
+Resolving deltas: 100% (70/70), done.
+Checking connectivity... done.
+```
+
+一旦成功地克隆了仓库,你就可以像对待你电脑上任何其它目录那样浏览仓库中的文件。
+
+另外一种获得仓库拷贝的方式是使用 web 界面。GitLab 和 GitHub 都会提供一个 .zip 格式的仓库快照文件。GitHub 有一个大大的绿色下载按钮,但是在 GitLab 中,可以在浏览器的右侧找到并不显眼的下载按钮。
+
+![](https://opensource.com/sites/default/files/1_gitlab-zip.jpg)
+
+### 仔细挑选
+
+另外一种从 Git 仓库中获取文件的方法是找到你想要的文件,然后把它从仓库中拽出来。只有 web 界面才提供这种方法,本质上来说,你看到的是别人的仓库克隆;你可以把它想象成一个 HTTP 共享目录。
+
+使用这种方法的问题是,你也许会发现某些文件并不存在于原始仓库中,因为完整形式的文件可能只有在执行 make 命令后才能构建,那只有你下载了完整的仓库,阅读了 README 或者 INSTALL 文件,然后运行相关命令之后才会产生。不过,假如你确信文件存在,而你只想进入仓库,获取那个文件,然后离开的话,你就可以那样做。
+
+在 GitLab 和 GitHub 中,单击文件链接,并在 Raw 模式下查看,然后使用你的 web 浏览器的保存功能,例如:在 Firefox 中,“文件” \> “保存页面为”。在一个 GitWeb 仓库中(这是一个某些更喜欢自己托管 git 的人使用的私有 git 仓库 web 查看器),Raw 查看链接在文件列表视图中。
+
+![](https://opensource.com/sites/default/files/1_webgit-file.jpg)
+
+### 最佳实践
+
+通常认为,和 Git 交互的正确方式是克隆完整的 Git 仓库。这样认为是有几个原因的。首先,可以使用 git pull 命令轻松地使克隆仓库保持更新,这样你就不必在每次文件改变时就重回 web 站点获得一份全新的拷贝。第二,你碰巧需要做些改进,只要保持仓库整洁,那么你可以非常轻松地向原来的作者提交所做的变更。
+
+现在,可能是时候练习查找感兴趣的 Git 仓库,然后将它们克隆到你的硬盘中了。只要你了解使用终端的基础知识,那就不会太难做到。还不知道基本的终端使用方式吗?那再给多我 5 分钟时间吧。
+
+### 终端使用基础
+
+首先要知道的是,所有的文件都有一个路径。这是有道理的;如果我让你在常规的非终端环境下为我打开一个文件,你就要导航到文件在你硬盘的位置,并且直到你找到那个文件,你要浏览一大堆窗口。例如,你也许要点击你的家目录 > 图片 > InktoberSketches > monkey.kra。
+
+在那样的场景下,文件 monkeysketch.kra 的路径是:$HOME/图片/InktoberSketches/monkey.kra。
+
+在终端中,除非你正在处理一些特殊的系统管理员任务,你的文件路径通常是以 $HOME 开头的(或者,如果你很懒,就使用 ~ 字符),后面紧跟着一些列的文件夹直到文件名自身。
+
+这就和你在 GUI 中点击各种图标直到找到相关的文件或文件夹类似。
+
+如果你想把 Git 仓库克隆到你的文档目录,那么你可以打开一个终端然后运行下面的命令:
+
+```
+$ git clone https://gitlab.com/foo/bar.git 
+$HOME/文档/bar.clone
+```
+
+一旦克隆完成,你可以打开一个文件管理器窗口,导航到你的文档文件夹,然后你就会发现 bar.clone 目录正在等待着你访问。
+
+如果你想要更高级点,你或许会在以后再次访问那个仓库,可以尝试使用 git pull 命令来查看项目有没有更新:
+
+```
+$ cd $HOME/文档/bar.clone
+$ pwd
+bar.clone
+$ git pull
+```
+
+到目前为止,你需要初步了解的所有终端命令就是那些了,那就去探索吧。你实践得越多,Git 掌握得就越好(熟能生巧),这是重点,也是事情的本质。
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/life/16/7/stumbling-git
+
+作者:[Seth Kenlon][a]
+译者:[ChrisLeeGit](https://github.com/chrisleegit)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/seth
+[1]: https://git-scm.com/download
+[2]: https://gitlab.com/trashy/trashy
+[3]: https://gitlab.com/trashy/trashy.git
+[4]: https://linux.cn/article-7639-1.html
diff --git a/published/201608/20160715 bc - Command line calculator.md b/published/201608/20160715 bc - Command line calculator.md
new file mode 100644
index 0000000000..bf96f9c464
--- /dev/null
+++ b/published/201608/20160715 bc - Command line calculator.md	
@@ -0,0 +1,129 @@
+bc : 一个命令行计算器
+============================
+
+![](https://cdn.fedoramagazine.org/wp-content/uploads/2016/07/bc-calculator-945x400.jpg)
+
+假如你在一个图形桌面环境中需要一个计算器时,你可能只需要一路进行点击便可以找到一个计算器。例如,Fedora 工作站中就已经包含了一个名为 `Calculator` 的工具。它有着几种不同的操作模式,例如,你可以进行复杂的数学运算或者金融运算。但是,你知道吗,命令行也提供了一个与之相似的名为 `bc` 的工具?
+
+`bc` 工具可以为你提供的功能可以满足你对科学计算器、金融计算器或者是简单计算器的期望。另外,假如需要的话,它还可以从命令行中被脚本化。这使得当你需要做复杂的数学运算时,你可以在 shell 脚本中使用它。
+
+因为 bc 也被用于其他的系统软件,例如 CUPS 打印服务,所以它可能已经在你的 Fedora 系统中被安装了。你可以使用下面这个命令来进行检查:
+
+```
+dnf list installed bc
+```
+
+假如因为某些原因你没有在上面命令的输出中看到它,你可以使用下面的这个命令来安装它:
+
+```
+sudo dnf install bc
+```
+
+### 用 bc 做一些简单的数学运算
+
+使用 bc 的一种方式是进入它自己的 shell。在那里你可以按行进行许多次计算。当你键入 bc 后,首先出现的是有关这个程序的警告:
+
+```
+$ bc
+bc 1.06.95
+Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
+This is free software with ABSOLUTELY NO WARRANTY.
+For details type `warranty'.
+```
+
+现在你可以按照每行一个输入运算式或者命令了:
+
+```
+1+1
+```
+
+bc 会回答上面计算式的答案是:
+
+```
+2
+```
+
+在这里你还可以执行其他的命令。你可以使用 加(+)、减(-)、乘(*)、除(/)、圆括号、指数符号(\^) 等等。请注意 bc 同样也遵循所有约定俗成的运算规则,例如运算的先后顺序。你可以试试下面的例子:
+
+```
+(4+7)*2
+4+7*2
+```
+
+若要退出 bc 可以通过按键组合 `Ctrl+D` 来发送 “输入结束”信号给 bc 。
+
+使用 bc 的另一种方式是使用 `echo` 命令来传递运算式或命令。下面这个示例就是计算器中的 “Hello, world” 例子,使用 shell 的管道函数(|) 来将 `echo` 的输出传入 `bc` 中:
+
+```
+echo '1+1' | bc
+```
+
+使用 shell 的管道,你可以发送不止一个运算操作,你需要使用分号来分隔不同的运算。结果将在不同的行中返回。
+
+```
+echo '1+1; 2+2' | bc
+```
+
+### 精度
+
+在某些计算中,bc 会使用精度的概念,即小数点后面的数字位数。默认的精度是 0。除法操作总是使用精度的设定。所以,如果你没有设置精度,有可能会带来意想不到的答案:
+
+```
+echo '3/2' | bc
+echo 'scale=3; 3/2' | bc
+```
+
+乘法使用一个更复杂的精度选择机制:
+
+```
+echo '3*2' | bc
+echo '3*2.0' | bc
+```
+
+同时,加法和减法的相关运算则与之相似:
+
+```
+echo '7-4.15' | bc
+```
+
+### 其他进制系统
+
+bc 的另一个有用的功能是可以使用除了十进制以外的其他计数系统。例如,你可以轻松地做十六进制或二进制的数学运算。可以使用 `ibase` 和 `obase` 命令来分别设定输入和输出的进制系统。需要记住的是一旦你使用了 `ibase`,之后你输入的任何数字都将被认为是在新定义的进制系统中。
+
+要做十六进制数到十进制数的转换或运算,你可以使用类似下面的命令。请注意大于 9 的十六进制数必须是大写的(A-F):
+
+```
+echo 'ibase=16; A42F' | bc
+echo 'ibase=16; 5F72+C39B' | bc
+```
+
+若要使得结果是十六进制数,则需要设定 `obase` :
+
+```
+echo 'obase=16; ibase=16; 5F72+C39B' | bc
+```
+
+下面是一个小技巧。假如你在 shell 中做这些十六进制运算,怎样才能使得输入重新为十进制数呢?答案是使用 `ibase` 命令,但你必须设定它为在当前进制中与十进制中的 10 等价的值。例如,假如 `ibase` 被设定为十六进制,你需要输入:
+
+```
+ibase=A
+```
+
+一旦你执行了上面的命令,所有输入的数字都将是十进制的了,接着你便可以输入 `obase=10` 来重置输出的进制系统。
+
+### 结论
+
+上面所提到的只是 bc 所能做到的基础。它还允许你为某些复杂的运算和程序定义函数、变量和循环结构。你可以在你的系统中将这些程序保存为文本文件以便你在需要的时候使用。你还可以在网上找到更多的资源,它们提供了更多的例子以及额外的函数库。快乐地计算吧!
+
+--------------------------------------------------------------------------------
+
+via: https://fedoramagazine.org/bc-command-line-calculator/
+
+作者:[Paul W. Frields][a]
+译者:[FSSlc](https://github.com/FSSlc)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://fedoramagazine.org/author/pfrields/
+[1]: http://phodd.net/gnu-bc/
diff --git a/published/201608/20160717 BEST TEXT EDITORS FOR LINUX COMMAND LINE.md b/published/201608/20160717 BEST TEXT EDITORS FOR LINUX COMMAND LINE.md
new file mode 100644
index 0000000000..f2a76f10bb
--- /dev/null
+++ b/published/201608/20160717 BEST TEXT EDITORS FOR LINUX COMMAND LINE.md	
@@ -0,0 +1,94 @@
+Linux 命令行下的最佳文本编辑器
+==========================================
+
+![](https://itsfoss.com/wp-content/uploads/2016/07/Best-Command-Line-Text-Editors-for-Linux.jpg)
+
+文本编辑软件在任何操作系统上都是必备的软件。我们在 Linux 上不缺乏[非常现代化的编辑软件][1],但是它们都是基于 GUI(图形界面)的编辑软件。
+
+正如你所了解的,Linux 真正的魅力在于命令行。当你正在用命令行工作时,你就需要一个可以在控制台窗口运行的文本编辑器。
+
+正因为这个目的,我们准备了一个基于 Linux 命令行的文本编辑器清单。
+
+### [VIM][2]
+
+如果你已经使用 Linux 有一段时间,那么你肯定听到过 Vim 。Vim 是一个高度可配置的、跨平台的、高效率的文本编辑器。
+
+几乎所有的 Linux 发行版本都已经内置了 Vim ,由于其特性之丰富,它已经变得非常流行了。
+
+![](https://itsfoss.com/wp-content/uploads/2016/07/vim.png)
+
+*Vim 用户界面*
+
+Vim 可能会让第一次使用它的人感到非常痛苦。我记得我第一次尝试使用 Vim 编辑一个文本文件时,我是非常困惑的。我不能用 Vim 输入一个字母,更有趣的是,我甚至不知道该怎么关闭它。如果你准备使用 Vim ,你需要有决心跨过一个陡峭的学习路线。
+
+但是一旦你经历过了那些,通过梳理一些文档,记住它的命令和快捷键,你会发现这段学习经历是非常值得的。你可以将 Vim 按照你的意愿进行改造:配置一个让你看起来舒服的界面,通过使用脚本或者插件等来提高工作效率。Vim 支持格式高亮,宏记录和操作记录。
+
+在Vim官网上,它是这样介绍的:
+
+>**Vim: The power tool for everyone!**
+
+如何使用它完全取决于你。你可以仅仅使用它作为文本编辑器,或者你可以将它打造成一个完善的IDE(Integrated Development Environment:集成开发环境)。
+
+### [GNU EMACS][3]
+
+GNU Emacs 毫无疑问是非常强大的文本编辑器之一。如果你听说过 Vim 和 Emacs ,你应该知道这两个编辑器都拥有非常忠诚的粉丝基础,并且他们对于文本编辑器的选择非常看重。你也可以在互联网上找到大量关于他们的段子:
+
+![](https://itsfoss.com/wp-content/uploads/2016/07/vi-emacs-768x426.png)
+
+*Vim vs Emacs*
+
+Emacs 是一个跨平台的、既有有图形界面也有命令行界面的软件。它也拥有非常多的特性,更重要的是,可扩展!
+
+![](https://itsfoss.com/wp-content/uploads/2016/07/emacs.png)
+
+*Emacs 用户界面*
+
+像 Vim一样,Emacs 也需要经历一个陡峭的学习路线。但是一旦你掌握了它,你就能完全体会到它的强大。Emacs 可以处理几乎所有类型文本文件。它的界面可以定制以适应你的工作流。它也支持宏记录和快捷键。
+
+Emacs 独特的特性是它可以“变形”成和文本编辑器完全不同的的东西。有大量的模块可使它在不同的场景下成为不同的应用,例如:计算器、新闻阅读器、文字处理器等。你甚至都可以在 Emacs 里面玩游戏。
+
+### [NANO][5]
+
+如果说到简易方便的软件,Nano 就是一个。不像 Vim 和 Emacs,nano 的学习曲线是平滑的。
+
+如果你仅仅是想创建和编辑一个文本文件,不想给自己找太多挑战,Nano 估计是最适合你的了。
+
+![](https://itsfoss.com/wp-content/uploads/2016/07/nano.png)
+
+*Nano 用户界面*
+
+Nano 可用的快捷键都在用户界面的下方展示出来了。Nano 仅仅拥有最基础的文本编辑软件的功能。
+
+它是非常小巧的,非常适合编辑系统配置文件。对于那些不需要复杂的命令行编辑功能的人来说,Nano 是完美配备。
+
+###  其它
+
+这里还有一些我想要提及其它编辑器:
+
+[The Nice Editor (ne)][6]: 官网是这样介绍的:
+
+> 如果你有足够的资料,也有使用 Emacs 的耐心或使用 Vim 的良好心态,那么 ne 可能不适合你。
+
+基本上 ne 拥有像 Vim 和 Emacs 一样多的高级功能,包括:脚本和宏记录。但是它有更为直观的操作方式和平滑的学习路线。
+
+### 你认为呢?
+
+我知道,如果你是一个熟练的 Linux 用户,你可以会说还有很多应该被列入 “Linux 最好的命令行编辑器”清单上。因此我想跟你说,如果你还知道其他的 Linux 命令行文本编辑器,你是否愿意跟我们一同分享?
+
+--------------------------------------------------------------------------------
+
+via: https://itsfoss.com/command-line-text-editors-linux/ 
+
+作者:[Munif Tanjim][a]
+译者:[chenzhijun](https://github.com/chenzhijun)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://itsfoss.com/author/munif/
+[1]: https://linux.cn/article-7468-1.html
+[2]: http://www.vim.org/
+[3]: https://www.gnu.org/software/emacs/
+[4]: https://itsfoss.com/download-linux-wallpapers-cheat-sheets/
+[5]: http://www.nano-editor.org/
+[6]: http://ne.di.unimi.it/
diff --git a/published/201608/20160718 Creating your first Git repository.md b/published/201608/20160718 Creating your first Git repository.md
new file mode 100644
index 0000000000..d1a8ca2840
--- /dev/null
+++ b/published/201608/20160718 Creating your first Git repository.md	
@@ -0,0 +1,176 @@
+Git 系列(三):建立你的第一个 Git 仓库
+======================================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/life/open_abstract_pieces.jpg?itok=ZRt0Db00)
+
+现在是时候学习怎样创建你自己的 Git 仓库了,还有怎样增加文件和完成提交。
+
+在本系列[前面的文章][4]中,你已经学习了怎样作为一个最终用户与 Git 进行交互;你就像一个漫无目的的流浪者一样偶然发现了一个开源项目网站,克隆了仓库,然后你就可以继续钻研它了。你知道了和 Git 进行交互并不像你想的那样困难,或许你只是需要被说服现在去使用 Git 完成你的工作罢了。
+
+虽然 Git 确实是被许多重要软件选作版本控制工具,但是并不是仅能用于这些重要软件;它也能管理你购物清单(如果它们对你来说很重要的话,当然可以了!)、你的配置文件、周报或日记、项目进展日志、甚至源代码!
+
+使用 Git 是很有必要的,毕竟,你肯定有过因为一个备份文件不能够辨认出版本信息而抓狂的时候。
+
+Git 无法帮助你,除非你开始使用它,而现在就是开始学习和使用它的最好时机。或者,用 Git 的话来说,“没有其他的 `push` 能像 `origin HEAD` 一样有帮助了”(千里之行始于足下的意思)。我保证,你很快就会理解这一点的。
+
+### 类比于录音
+
+我们经常用名词“快照”来指代计算机上的镜像,因为很多人都能够对插满了不同时光的照片的相册充满了感受。这很有用,不过,我认为 Git 更像是进行一场录音。
+
+也许你不太熟悉传统的录音棚卡座式录音机,它包括几个部件:一个可以正转或反转的转轴、保存声音波形的磁带,可以通过拾音头在磁带上记录声音波形,或者检测到磁带上的声音波形并播放给听众。
+
+除了往前播放磁带,你也可以把磁带倒回到之前的部分,或快进跳过后面的部分。
+
+想象一下上世纪 70 年代乐队录制磁带的情形。你可以想象到他们一遍遍地练习歌曲,直到所有部分都非常完美,然后记录到音轨上。起初,你会录下鼓声,然后是低音,再然后是吉他声,最后是主唱。每次你录音时,录音棚工作人员都会把磁带倒带,然后进入循环模式,这样它就会播放你之前录制的部分。比如说如果你正在录制低音,你就会在背景音乐里听到鼓声,就像你自己在击鼓一样,然后吉他手在录制时会听到鼓声、低音(和牛铃声)等等。在每个循环中,你都会录制一部分,在接下来的循环中,工作人员就会按下录音按钮将其合并记录到磁带中。
+
+你也可以拷贝或换下整个磁带,如果你要对你的作品重新混音的话。
+
+现在我希望对于上述的上世纪 70 年代的录音工作的描述足够生动,这样我们就可以把 Git 的工作想象成一个录音工作了。
+
+### 新建一个 Git 仓库
+
+首先得为我们的虚拟的录音机买一些磁带。用 Git 的话说,这些磁带就是*仓库*;它是完成所有工作的基础,也就是说这里是存放 Git 文件的地方(即 Git 工作区)。
+
+任何目录都可以成为一个 Git 仓库,但是让我们从一个新目录开始。这需要下面三个命令:
+
+- 创建目录(如果你喜欢的话,你可以在你的图形化的文件管理器里面完成。)
+- 在终端里切换到目录。
+- 将其初始化成一个 Git 管理的目录。
+
+也就是运行如下代码:
+
+```
+$ mkdir ~/jupiter  # 创建目录
+$ cd ~/jupiter     # 进入目录
+$ git init .       # 初始化你的新 Git 工作区
+```
+
+在这个例子中,文件夹 jupiter 是一个空的但是合法的 Git 仓库。
+
+有了仓库接下来的事情就可以按部就班进行了。你可以克隆该仓库,你可以在一个历史点前后来回穿梭(前提是你有一个历史点),创建交替的时间线,以及做 Git 能做的其它任何事情。
+
+在 Git 仓库里面工作和在任何目录里面工作都是一样的,可以在仓库中新建文件、复制文件、保存文件。你可以像平常一样做各种事情;Git 并不复杂,除非你把它想复杂了。
+
+在本地的 Git 仓库中,一个文件可以有以下这三种状态:
+
+- 未跟踪文件(Untracked):你在仓库里新建了一个文件,但是你没有把文件加入到 Git 的管理之中。
+- 已跟踪文件(Tracked):已经加入到 Git 管理的文件。
+- 暂存区文件(Staged):被修改了的已跟踪文件,并加入到 Git 的提交队列中。
+
+任何你新加入到 Git 仓库中的文件都是未跟踪文件。这些文件保存在你的电脑硬盘上,但是你没有告诉 Git 这是需要管理的文件,用我们的录音机来类比,就是录音机还没打开;乐队就开始在录音棚里忙碌了,但是录音机并没有准备录音。
+
+不用担心,Git 会在出现这种情况时告诉你:
+
+```
+$ echo "hello world" > foo
+$ git status
+On branch master
+Untracked files:
+(use "git add <file>..." to include in what will be committed)    
+    foo    
+nothing added but untracked files present (use "git add" to track)
+```
+
+你看到了,Git 会提醒你怎样把文件加入到提交任务中。
+
+### 不使用 Git 命令进行 Git 操作
+
+在 GitHub 或 GitLab 上创建一个仓库只需要用鼠标点几下即可。这并不难,你单击“New Repository”这个按钮然后跟着提示做就可以了。
+
+在仓库中包括一个“README”文件是一个好习惯,这样人们在浏览你的仓库的时候就可以知道你的仓库是干什么的,更有用的是可以让你在克隆一个有东西的仓库前知道它有些什么。
+
+克隆仓库通常很简单,但是在 GitHub 上获取仓库改动权限就稍微复杂一些,为了通过 GitHub 验证你必须有一个 SSH 密钥。如果你使用 Linux 系统,可以通过下面的命令生成:
+
+```
+$ ssh-keygen
+```
+
+然后复制你的新密钥的内容,它是纯文本文件,你可以使用一个文本编辑器打开它,也可以使用如下 cat 命令查看:
+
+```
+$ cat ~/.ssh/id_rsa.pub
+```
+
+现在把你的密钥粘贴到 [GitHub SSH 配置文件][1] 中,或者 [GitLab 配置文件][2]。
+
+如果你通过使用 SSH 模式克隆了你的项目,你就可以将修改写回到你的仓库了。
+
+另外,如果你的系统上没有安装 Git 的话也可以使用 GitHub 的文件上传接口来添加文件。
+
+![](https://opensource.com/sites/default/files/2_githubupload.jpg)
+
+### 跟踪文件
+
+正如命令 `git status` 的输出告诉你的那样,如果你想让 git 跟踪一个文件,你必须使用命令 `git add` 把它加入到提交任务中。这个命令把文件存在了暂存区,这里存放的都是等待提交的文件,或者也可以用在快照中。在将文件包括到快照中,和添加要 Git 管理的新的或临时文件时,`git add` 命令的目的是不同的,不过至少现在,你不用为它们之间的不同之处而费神。
+
+类比录音机,这个动作就像打开录音机开始准备录音一样。你可以想象为对已经在录音的录音机按下暂停按钮,或者倒回开头等着记录下个音轨。
+
+当你把文件添加到 Git 管理中,它会标识其为已跟踪文件:
+
+```
+$ git add foo
+$ git status
+On branch master
+Changes to be committed:
+(use "git reset HEAD <file>..." to unstage)
+new file:   foo
+```
+
+加入文件到提交任务中并不是“准备录音”。这仅仅是将该文件置于准备录音的状态。在你添加文件后,你仍然可以修改该文件;它只是被标记为**已跟踪**和**处于暂存区**,所以在它被写到“磁带”前你可以将它撤出或修改它(当然你也可以再次将它加入来做些修改)。但是请注意:你还没有在磁带中记录该文件,所以如果弄坏了一个之前还是好的文件,你是没有办法恢复的,因为你没有在“磁带”中记下那个文件还是好着的时刻。
+
+如果你最后决定不把文件记录到 Git 历史列表中,那么你可以撤销提交任务,在 Git 中是这样做的:
+
+```
+$ git reset HEAD foo
+```
+
+这实际上就是解除了录音机的准备录音状态,你只是在录音棚中转了一圈而已。
+
+### 大型提交
+
+有时候,你想要提交一些内容到仓库;我们以录音机类比,这就好比按下录音键然后记录到磁带中一样。
+
+在一个项目所经历的不同阶段中,你会按下这个“记录键”无数次。比如,如果你尝试了一个新的 Python 工具包并且最终实现了窗口呈现功能,然后你肯定要进行提交,以便你在实验新的显示选项时搞砸了可以回退到这个阶段。但是如果你在 Inkscape 中画了一些图形草样,在提交前你可能需要等到已经有了一些要开发的内容。尽管你可能提交了很多次,但是 Git 并不会浪费很多,也不会占用太多磁盘空间,所以在我看来,提交的越多越好。
+
+`commit` 命令会“记录”仓库中所有的暂存区文件。Git 只“记录”已跟踪的文件,即,在过去某个时间点你使用 `git add` 命令加入到暂存区的所有文件,以及从上次提交后被改动的文件。如果之前没有过提交,那么所有跟踪的文件都包含在这次提交中,以 Git 的角度来看,这是一次非常重要的修改,因为它们从没放到仓库中变成了放进去。
+
+完成一次提交需要运行下面的命令:
+
+```
+$ git commit -m 'My great project, first commit.'
+```
+
+这就保存了所有提交的文件,之后可以用于其它操作(或者,用英国电视剧《神秘博士》中时间领主所讲的 Gallifreyan 语说,它们成为了“固定的时间点” )。这不仅是一个提交事件,也是一个你在 Git 日志中找到该提交的引用指针:
+
+```
+$ git log --oneline
+55df4c2 My great project, first commit.
+```
+
+如果想浏览更多信息,只需要使用不带 `--oneline` 选项的 `git log` 命令。
+
+在这个例子中提交时的引用号码是 55df4c2。它被叫做“提交哈希(commit hash)”(LCTT 译注:这是一个 SHA-1 算法生成的哈希码,用于表示一个 git 提交对象),它代表着刚才你的提交所包含的所有新改动,覆盖到了先前的记录上。如果你想要“倒回”到你的提交历史点上,就可以用这个哈希作为依据。
+
+你可以把这个哈希想象成一个声音磁带上的 [SMPTE 时间码][3],或者再形象一点,这就是好比一个黑胶唱片上两首不同的歌之间的空隙,或是一个 CD 上的音轨编号。
+
+当你改动了文件之后并且把它们加入到提交任务中,最终完成提交,这就会生成新的提交哈希,它们每一个所标示的历史点都代表着你的产品不同的版本。
+
+这就是 Charlie Brown 这样的音乐家们为什么用 Git 作为版本控制系统的原因。
+
+在接下来的文章中,我们将会讨论关于 Git HEAD 的各个方面,我们会真正地向你揭示时间旅行的秘密。不用担心,你只需要继续读下去就行了(或许你已经在读了?)。
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/life/16/7/creating-your-first-git-repository
+
+作者:[Seth Kenlon][a]
+译者:[vim-kakali](https://github.com/vim-kakali)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/seth
+[1]: https://github.com/settings/keys
+[2]: https://gitlab.com/profile/keys
+[3]: http://slackermedia.ml/handbook/doku.php?id=timecode
+[4]: https://linux.cn/article-7641-1.html
diff --git a/published/201608/20160721 5 tricks for getting started with Vim.md b/published/201608/20160721 5 tricks for getting started with Vim.md
new file mode 100644
index 0000000000..684a1d6295
--- /dev/null
+++ b/published/201608/20160721 5 tricks for getting started with Vim.md	
@@ -0,0 +1,60 @@
+Vim 起步的五个技巧
+=====================================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/education/BUSINESS_peloton.png?itok=nuMbW9d3)
+
+多年来,我一直想学 Vim。如今 Vim 是我最喜欢的 Linux 文本编辑器,也是开发者和系统管理者最喜爱的开源工具。我说的学习,指的是真正意义上的学习。想要精通确实很难,所以我只想要达到熟练的水平。我使用了这么多年的 Linux ,我会的也仅仅只是打开一个文件,使用上下左右箭头按键来移动光标,切换到插入模式,更改一些文本,保存,然后退出。
+
+但那只是 Vim 的最最基本的操作。我的技能水平只能让我在终端使用 Vim 修改文本,但是它并没有任何一个我想象中强大的文本处理功能。这样我完全无法用 Vim 发挥出胜出 Pico 和 Nano 的能力。
+
+所以到底为什么要学习 Vim?因为我花费了相当多的时间用于编辑文本,而且我知道还有很大的效率提升空间。为什么不选择 Emacs,或者是更为现代化的编辑器例如 Atom?因为 Vim 适合我,至少我有一丁点的使用经验。而且,很重要的一点就是,在我需要处理的系统上很少碰见没有装 Vim 或者它的弱化版(Vi)。如果你有强烈的欲望想学习对你来说更给力的 Emacs,我希望这些对于 Emacs 同类编辑器的建议能对你有所帮助。
+
+花了几周的时间专注提高我的 Vim 使用技巧之后,我想分享的第一个建议就是必须使用它。虽然这看起来就是明知故问的回答,但事实上它比我所预想的计划要困难一些。我的大多数工作是在网页浏览器上进行的,而且每次我需要在浏览器之外打开并编辑一段文本时,就需要避免下意识地打开 Gedit。Gedit 已经放在了我的快速启动栏中,所以第一步就是移除这个快捷方式,然后替换成 Vim 的。
+
+为了更好的学习 Vim,我尝试了很多。如果你也正想学习,以下列举了一些作为推荐。
+
+### Vimtutor
+
+通常如何开始学习最好就是使用应用本身。我找到一个小的应用叫 Vimtutor,当你在学习编辑一个文本时它能辅导你一些基础知识,它向我展示了很多我这些年都忽视的基础命令。Vimtutor 一般在有 Vim 的地方都能找到它,如果你的系统上没有 Vimtutor,Vimtutor 可以很容易从你的包管理器上安装。
+
+### GVim
+
+我知道并不是每个人都认同这个,但就是它让我从使用终端中的 Vim 转战到使用 GVim 来满足我基本编辑需求。反对者表示 GVim 鼓励使用鼠标,而 Vim 主要是为键盘党设计的。但是我能通过 GVim 的下拉菜单快速找到想找的指令,并且 GVim 可以提醒我正确的指令然后通过敲键盘执行它。努力学习一个新的编辑器然后陷入无法解决的困境,这种感觉并不好受。每隔几分钟读一下 man 出来的文字或者使用搜索引擎来提醒你该用的按键序列也并不是最好的学习新事物的方法。
+
+### 键盘表
+
+当我转战 GVim,我发现有一个键盘的“速查表”来提醒我最基础的按键很是便利。网上有很多这种可用的表,你可以下载、打印,然后贴在你身边的某一处地方。但是为了我的笔记本键盘,我选择买一沓便签纸。这些便签纸在美国不到 10 美元,当我使用键盘编辑文本,尝试新的命令的时候,可以随时提醒我。
+
+### Vimium
+
+上文提到,我工作都在浏览器上进行。其中一条我觉得很有帮助的建议就是,使用 [Vimium][1] 来用增强使用 Vim 的体验。Vimium 是 Chrome 浏览器上的一个开源插件,能用 Vim 的指令快捷操作 Chrome。我发现我只用了几次使用快捷键切换上下文,就好像比之前更熟悉这些快捷键了。同样的扩展 Firefox 上也有,例如 [Vimerator][2]。
+
+### 其它人
+
+毫无疑问,最好的学习方法就是求助于在你之前探索过的人,让他给你建议、反馈和解决方法。
+
+如果你住在一个大城市,那么附近可能会有一个 Vim meetup 小组,或者还有 Freenode IRC 上的 #vim 频道。#vim 频道是 Freenode 上最活跃的频道之一,那上面可以针对你个人的问题来提供帮助。听上面的人发发牢骚或者看看别人尝试解决自己没有遇到过的问题,仅仅是这样我都觉得很有趣。
+
+------
+
+那么,现在怎么样了?到现在为止还不错。为它所花的时间是否值得就在于之后它为你节省了多少时间。但是当我发现一个新的按键序列可以来跳过词,或者一些相似的小技巧,我经常会收获意外的惊喜与快乐。每天我至少可以看见,一点点的回报,正在逐渐配得上当初的付出。
+
+学习 Vim 并不仅仅只有这些建议,还有很多。我很喜欢指引别人去 [Vim Advantures][3],它是一种使用 Vim 按键方式进行移动的在线游戏。而在另外一天我在 [Vimgifts.com][4] 发现了一个非常神奇的虚拟学习工具,那可能就是你真正想要的:用一个小小的 gif 动图来描述 Vim 操作。
+
+你有花时间学习 Vim 吗?或者是任何需要大量键盘操作的程序?那些经过你努力后掌握的工具,你认为这些努力值得吗?效率的提高有没有达到你的预期?分享你们的故事在下面的评论区吧。
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/life/16/7/tips-getting-started-vim
+
+作者:[Jason Baker][a]
+译者:[maywanting](https://github.com/maywanting)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/jason-baker
+[1]: https://github.com/philc/vimium
+[2]: http://www.vimperator.org/
+[3]: http://vim-adventures.com/
+[4]: http://vimgifs.com/
diff --git a/published/201608/20160722 Keeweb A Linux Password Manager.md b/published/201608/20160722 Keeweb A Linux Password Manager.md
new file mode 100644
index 0000000000..6838c3105d
--- /dev/null
+++ b/published/201608/20160722 Keeweb A Linux Password Manager.md	
@@ -0,0 +1,61 @@
+Linux 下的密码管理器:Keeweb
+================================
+
+![](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/keeweb_1.png?608)
+
+如今,我们依赖于越来越多的线上服务。我们每注册一个线上服务,就要设置一个密码;如此,我们就不得不记住数以百计的密码。这样对于每个人来说,都很容易忘记密码。我将在本文中介绍 Keeweb,它是一款 Linux 密码管理器,可以为你离线或在线地安全存储所有的密码。
+
+当谈及 Linux 密码管理器时,我们会发现有很多这样的软件。我们已经在 LinuxAndUbuntu 上讨论过像 [Keepass][1] 和 [Encryptr,一个基于零知识系统的密码管理器][2] 这样的密码管理器。Keeweb 则是另外一款我们将在本文讲解的 Linux 密码管理器。
+
+### Keeweb 可以离线或在线存储密码
+
+Keeweb 是一款跨平台的密码管理器。它可以离线存储你所有的密码,并且能够同步到你自己的云存储服务上,例如 OneDrive、Google Drive、Dropbox 等。Keeweb 并没有它提供它自己的在线数据库来的同步你的密码。
+
+要使用 Keeweb 连接你的线上存储服务,只需要点击界面中的“more”,然后再点击你想要使用的服务即可。
+
+![](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/keeweb.png?685)
+
+现在,Keeweb 会提示你登录到你的云盘。登录成功后,给 Keeweb 授权使用你的账户。
+
+![](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/authenticate-dropbox-with-keeweb_orig.jpg?649)
+
+### 使用 Keeweb 存储密码
+
+使用 Keeweb 存储你的密码是非常容易的。你可以使用一个复杂的密码加密你的密码文件。Keeweb 也允许你使用一个秘钥文件来锁定密码文件,但是我并不推荐这种方式。如果某个家伙拿到了你的秘钥文件,他只需要简单点击一下就可以解锁你的密码文件。
+
+#### 创建密码
+
+想要创建一个新的密码,你只需要简单地点击 `+` 号,然后你就会看到所有需要填充的输入框。根据你的需要创建更多的密码记录。
+
+#### 搜索密码
+
+![](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/search-passwords_orig.png)
+
+Keeweb 拥有一个图标库,这样你就可以轻松地找到各种特定的密码记录。你可以改变图标的颜色、下载更多的图标,甚至可以直接从你的电脑中导入图标。这对于密码搜索来说,异常好使。
+
+相似的服务的密码可以分组,这样你就可以在一个文件夹里找到它们。你也可以给密码打上标签并把它们存放在不同分类中。
+
+![](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/tags-passwords-in-keeweb.png?283)
+
+### 主题
+
+![](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/themes.png?304)
+
+如果你喜欢类似于白色或者高对比度的亮色主题,你可以在“设置 > 通用 > 主题”中修改。(Keeweb)有四款可供选择的主题,其中两款为暗色,另外两款为亮色。
+
+### 不喜欢 Linux 密码管理器?没问题! 
+
+我已经发表过文章介绍了另外两款 Linux 密码管理器,它们分别是 Keepass 和 Encryptr,在 Reddit 和其它社交媒体上有些关于它们的争论。有些人反对使用任何密码管理器,也有人持相反意见。在本文中,我想要澄清的是,存放密码文件是我们自己的责任。我认为像 keepass 和 Keeweb 这样的密码管理器是非常好用的,因为它们并没有自己的云来存放你的密码。这些密码管理器会创建一个文件,然后你可以将它存放在你的硬盘上,或者使用像 VeraCrypt 这样的应用给它加密。我个人不使用也不推荐使用那些将密码存储在它们自己数据库的服务。
+
+--------------------------------------------------------------------------------
+
+via: http://www.linuxandubuntu.com/home/keeweb-a-linux-password-manager
+
+译者:[ChrisLeeGit](https://github.com/chrisleegit)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.linuxandubuntu.com/home/keeweb-a-linux-password-manager
+[1]: http://www.linuxandubuntu.com/home/keepass-password-management-tool-creates-strong-passwords-and-keeps-them-secure
+[2]: http://www.linuxandubuntu.com/home/encryptr-zero-knowledge-system-based-password-manager-for-linux
diff --git a/published/201608/20160726 How to restore older file versions in Git.md b/published/201608/20160726 How to restore older file versions in Git.md
new file mode 100644
index 0000000000..9008d9cf63
--- /dev/null
+++ b/published/201608/20160726 How to restore older file versions in Git.md	
@@ -0,0 +1,182 @@
+Git 系列(四):在 Git 中进行版本回退
+=============================================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/life/file_system.jpg?itok=s2b60oIB)
+
+在这篇文章中,你将学到如何查看项目中的历史版本,如何进行版本回退,以及如何创建 Git 分支以便你可以大胆尝试而不会出现问题。
+
+在你的 Git 项目的历史中,你的位置就像是摇滚专辑中的一个片段,由一个被称为 HEAD 的 标记来确定(如磁带录音机或录音播放器的播放头)。要在你的 Git 时间线上前后移动 HEAD ,需要使用 `git checkout` 命令。
+
+git checkout 命令的使用方式有两种。最常见的用途是从一个以前的提交中恢复文件,你也可以整个倒回磁带,切换到另一个分支。
+
+
+### 恢复一个文件
+
+当你意识到一个本来很好文件被你完全改乱了。我们都这么干过:我们把文件放到一个地方,添加并提交,然后我们发现它还需要做点最后的调整,最后这个文件被搞得面目全非了。
+
+要把它恢复到最后的完好状态,使用 git checkout 从最后的提交(即 HEAD)中恢复:
+
+```
+$ git checkout HEAD filename
+```
+
+如果你碰巧提交了一个错误的版本,你需要找回更早的版本,使用 git log 查看你更早的提交,然后从合适的提交中找回它:
+
+```
+$ git log --oneline
+79a4e5f bad take
+f449007 The second commit
+55df4c2 My great project, first commit.
+
+$ git checkout 55df4c2 filename
+
+```
+
+现在,以前的文件恢复到了你当前的位置。(任何时候你都可以用 git status 命令查看你的当前状态)因为这个文件改变了,你需要添加这个文件,再进行提交:
+
+```
+$ git add filename
+$ git commit -m 'restoring filename from first commit.'
+```
+
+使用 Git log 验证你所提交的:
+
+```
+$ git log --oneline
+d512580 restoring filename from first commit
+79a4e5f bad take
+f449007 The second commit
+55df4c2 My great project, first commit.
+```
+
+从本质上讲,你已经倒好了磁带并修复了坏的地方,所以你需要重新录制正确的。
+
+### 回退时间线
+
+恢复文件的另一种方式是回退整个 Git 项目。这里使用了分支的思想,这是另一种替代方法。
+
+如果你要回到历史提交,你要将 Git HEAD 回退到以前的版本才行。这个例子将回到最初的提交处:
+
+```
+$ git log --oneline
+d512580 restoring filename from first commit
+79a4e5f bad take
+f449007 The second commit
+55df4c2 My great project, first commit.
+
+$ git checkout 55df4c2
+```
+
+当你以这种方式倒回磁带,如果你按下录音键再次开始,就会丢失以前的工作。Git 默认假定你不想这样做,所以将 HEAD 从项目中分离出来,可以让你如所需的那样工作,而不会因为偶尔的记录而影响之后的工作。
+
+如果你想看看以前的版本,想要重新做或者尝试不同的方法,那么安全一点的方式就是创建一个新的分支。可以将这个过程想象为尝试同一首歌曲的不同版本,或者创建一个混音的。原始的依然存在,关闭那个分支做你想做的版本吧。
+
+就像记录到一个空白磁带一样,把你的 Git HEAD 指到一个新的分支处:
+
+```
+$ git checkout -b remix
+Switched to a new branch 'remix'
+```
+
+现在你已经切换到了另一个分支,在你面前的是一个替代的干净工作区,准备开始工作吧。
+
+也可以不用改变时间线来做同样的事情。也许你很想这么做,但切换到一个临时的工作区只是为了尝试一些疯狂的想法。这在工作中完全是可以接受的,请看:
+
+```
+$ git status
+On branch master
+nothing to commit, working directory clean
+
+$ git checkout -b crazy_idea
+Switched to a new branch 'crazy_idea'
+```
+
+现在你有一个干净的工作空间,在这里你可以完成一些奇怪的想法。一旦你完成了,可以保留你的改变,或者丢弃他们,并切换回你的主分支。
+
+若要放弃你的想法,切换到你的主分支,假装新分支不存在:
+
+```
+$ git checkout master
+```
+
+想要继续使用你的疯狂的想法,需要把它们拉回到主分支,切换到主分支然后合并新分支到主分支:
+
+```
+$ git checkout master
+$ git merge crazy_idea
+```
+
+git 的分支功能很强大,开发人员在克隆仓库后马上创建一个新分支是很常见的做法;这样,他们所有的工作都在自己的分支上,可以提交并合并到主分支。Git 是很灵活的,所以没有“正确”或“错误”的方式(甚至一个主分支也可以与其所属的远程仓库分离),但分支易于分离任务和提交贡献。不要太激动,你可以如你所愿的有很多的 Git 分支。完全自由。
+
+### 远程协作
+
+到目前为止你已经在自己舒适而私密的家中维护着一个 Git 仓库,但如何与其他人协同工作呢?
+
+有好几种不同的方式来设置 Git 以便让多人可以同时在一个项目上工作,所以首先我们要克隆仓库,你可能已经从某人的 Git 服务器或 GitHub 主页,或在局域网中的共享存储上克隆了一个仓库。
+
+工作在私人仓库下和共享仓库下唯一不同的是你需要把你的改变 `push` 到别人的仓库。我们把工作的仓库称之为本地(local)仓库,其他仓库称为远程(remote)仓库。
+
+当你以读写的方式克隆一个仓库时,克隆的仓库会继承自被称为 origin 的远程库。你可以看看你的克隆仓库的远程仓库:
+
+```
+$ git remote --verbose
+origin  seth@example.com:~/myproject.Git (fetch)
+origin  seth@example.com:~/myproject.Git (push)
+```
+
+有一个 origin 远程库非常有用,因为它有异地备份的功能,并允许其他人在该项目上工作。
+
+如果克隆没有继承 origin 远程库,或者如果你选择以后再添加,可以使用 `git remote` 命令:
+
+```
+$ git remote add seth@example.com:~/myproject.Git
+```
+
+如果你修改了文件,想把它们发到有读写权限的 origin 远程库,使用 `git push`。第一次推送改变,必须也发送分支信息。不直接在主分支上工作是一个很好的做法,除非你被要求这样做:
+
+```
+$ git checkout -b seth-dev
+$ git add exciting-new-file.txt
+$ git commit -m 'first push to remote'
+$ git push -u origin HEAD
+```
+
+它会推送你当前的位置(HEAD)及其存在的分支到远程。当推送过一次后,以后每次推送可以不使用 -u 选项:
+
+```
+$ git add another-file.txt
+$ git commit -m 'another push to remote'
+$ git push origin HEAD
+```
+
+### 合并分支
+
+当你工作在一个 Git 仓库时,你可以合并任意测试分支到主分支。当团队协作时,你可能想在将它们合并到主分支之前检查他们的改变:
+
+```
+$ git checkout contributor
+$ git pull
+$ less blah.txt  ### 检查改变的文件
+$ git checkout master
+$ git merge contributor
+```
+
+如果你正在使用 GitHub 或 GitLab 以及类似的东西,这个过程是不同的。但克隆项目并把它作为你自己的仓库都是相似的。你可以在本地工作,将改变提交到你的 GitHub 或 GitLab 帐户,而不用其它人的许可,因为这些库是你自己的。
+
+如果你想要让你克隆的仓库接受你的改变,需要创建了一个拉取请求(pull request),它使用 Web 服务的后端发送补丁到真正的拥有者,并允许他们审查和拉取你的改变。
+
+克隆一个项目通常是在 Web 服务端完成的,它和使用 Git 命令来管理项目是类似的,甚至推送的过程也是。然后它返回到 Web 服务打开一个拉取请求,工作就完成了。
+
+下一部分我们将整合一些有用的插件到 Git 中来帮你轻松的完成日常工作。
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/life/16/7/how-restore-older-file-versions-git
+
+作者:[Seth Kenlon][a]
+译者:[strugglingyouth](https://github.com/strugglingyouth)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/seth
diff --git a/published/201608/20160727-How-to-use-multiple-connections-to-speed-up-apt-get on Ubuntu Linux 16.04 LTS server.md b/published/201608/20160727-How-to-use-multiple-connections-to-speed-up-apt-get on Ubuntu Linux 16.04 LTS server.md
new file mode 100644
index 0000000000..d60a04cf8a
--- /dev/null
+++ b/published/201608/20160727-How-to-use-multiple-connections-to-speed-up-apt-get on Ubuntu Linux 16.04 LTS server.md	
@@ -0,0 +1,215 @@
+如何在 Ubuntu Linux 16.04 LTS 中使用多个连接加速 apt-get/apt
+=================================================
+
+我该如何加速在 Ubuntu Linux 16.04 或者 14.04 LTS 上从多个仓库中下载包的 apt-get 或者 apt 命令?
+
+你需要使用到 apt-fast 这个 shell 封装器。它会通过多个连接同时下载一个包来加速 apt-get/apt 和 aptitude 命令。所有的包都会同时下载。它使用 aria2c 作为默认的下载加速器。
+
+### 安装 apt-fast 工具
+
+在 Ubuntu Linux 14.04 或者之后的版本尝试下面的命令:
+
+```
+$ sudo add-apt-repository ppa:saiarcot895/myppa
+```
+
+示例输出:
+
+![](http://s0.cyberciti.org/uploads/faq/2016/07/install-apt-fast-repo.jpg)
+
+更新你的仓库:
+
+```
+$ sudo apt-get update
+```
+
+或者
+
+```
+$ sudo apt update
+```
+
+![](http://s0.cyberciti.org/uploads/faq/2016/07/install-apt-fast-command.jpg)
+
+安装 apt-fast:
+
+```
+$ sudo apt-get -y install apt-fast
+```
+
+或者
+
+```
+$ sudo apt -y install apt-fast
+```
+
+示例输出:
+
+```
+Reading package lists... Done
+Building dependency tree
+Reading state information... Done
+The following additional packages will be installed:
+  aria2 libc-ares2 libssh2-1
+Suggested packages:
+  aptitude
+The following NEW packages will be installed:
+  apt-fast aria2 libc-ares2 libssh2-1
+0 upgraded, 4 newly installed, 0 to remove and 0 not upgraded.
+Need to get 1,282 kB of archives.
+After this operation, 4,786 kB of additional disk space will be used.
+Do you want to continue? [Y/n] y
+Get:1 http://01.archive.ubuntu.com/ubuntu xenial/universe amd64 libssh2-1 amd64 1.5.0-2 [70.3 kB]
+Get:2 http://ppa.launchpad.net/saiarcot895/myppa/ubuntu xenial/main amd64 apt-fast all 1.8.3~137+git7b72bb7-0ubuntu1~ppa3~xenial1 [34.4 kB]
+Get:3 http://01.archive.ubuntu.com/ubuntu xenial/main amd64 libc-ares2 amd64 1.10.0-3 [33.9 kB]
+Get:4 http://01.archive.ubuntu.com/ubuntu xenial/universe amd64 aria2 amd64 1.19.0-1build1 [1,143 kB]
+54% [4 aria2 486 kB/1,143 kB 42%]                                    20.4 kB/s 32s
+```
+
+### 配置 apt-fast
+
+你将会得到下面的提示(必须输入一个5到16的数值):
+
+![](http://s0.cyberciti.org/uploads/faq/2016/07/max-connection-10.jpg)
+
+并且
+
+![](http://s0.cyberciti.org/uploads/faq/2016/07/apt-fast-confirmation-box.jpg)
+
+你也可以直接编辑设置:
+
+```
+$ sudo vi /etc/apt-fast.conf
+```
+
+> **请注意这个工具并不是给慢速网络连接的,它是给快速网络连接的。如果你的网速慢,那么你将无法从这个工具中得到好处。**
+
+### 我该怎么使用 apt-fast 命令?
+
+语法是:
+
+```
+apt-fast command
+apt-fast [options] command
+```
+
+#### 使用 apt-fast 取回新的包列表
+
+```
+sudo apt-fast update
+```
+
+#### 使用 apt-fast 执行升级
+
+```
+sudo apt-fast upgrade
+```
+
+
+#### 执行发行版升级(发布或者强制内核升级),输入:
+
+```
+$ sudo apt-fast dist-upgrade
+```
+
+#### 安装新的包
+
+语法是:
+
+```
+sudo apt-fast install pkg
+```
+
+比如要安装 nginx,输入:
+
+```
+$ sudo apt-fast install nginx
+```
+
+示例输出:
+
+![](http://s0.cyberciti.org/uploads/faq/2016/07/sudo-apt-fast-install.jpg)
+
+#### 删除包
+
+```
+$ sudo apt-fast remove pkg
+$ sudo apt-fast remove nginx
+```
+
+#### 删除包和它的配置文件
+
+```
+$ sudo apt-fast purge pkg
+$ sudo apt-fast purge nginx
+```
+
+#### 删除所有未使用的包
+
+```
+$ sudo apt-fast autoremove
+```
+
+#### 下载源码包
+
+```
+$ sudo apt-fast source pkgNameHere
+```
+
+#### 清理下载的文件
+
+```
+$ sudo apt-fast clean
+```
+
+#### 清理旧的下载文件
+
+```
+$ sudo apt-fast autoclean
+```
+
+#### 验证没有破坏的依赖
+
+```
+$ sudo apt-fast check
+```
+
+#### 下载二进制包到当前目录
+
+```
+$ sudo apt-fast download pkgNameHere
+$ sudo apt-fast download nginx
+```
+
+示例输出:
+
+```
+[#7bee0c 0B/0B CN:1 DL:0B]
+07/26 15:35:42 [NOTICE] Verification finished successfully. file=/home/vivek/nginx_1.10.0-0ubuntu0.16.04.2_all.deb
+07/26 15:35:42 [NOTICE] Download complete: /home/vivek/nginx_1.10.0-0ubuntu0.16.04.2_all.deb
+Download Results:
+gid   |stat|avg speed  |path/URI
+======+====+===========+=======================================================
+7bee0c|OK  |        n/a|/home/vivek/nginx_1.10.0-0ubuntu0.16.04.2_all.deb
+Status Legend:
+(OK):download completed.
+```
+
+#### 下载并显示指定包的 changelog
+
+```
+$ sudo apt-fast changelog pkgNameHere
+$ sudo apt-fast changelog nginx
+```
+
+--------------------------------------------------------------------------------
+
+via: http://www.cyberciti.biz/faq/how-to-speed-up-apt-get-apt-command-ubuntu-linux/
+
+作者:[VIVEK GITE][a]
+译者:[geekpi](https://github.com/geekpi)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.cyberciti.biz/tips/about-us
diff --git a/published/201608/20160801 Linux how long a process has been running.md b/published/201608/20160801 Linux how long a process has been running.md
new file mode 100644
index 0000000000..e7e90948b1
--- /dev/null
+++ b/published/201608/20160801 Linux how long a process has been running.md	
@@ -0,0 +1,75 @@
+在 Linux 下如何查看一个进程的运行时间
+=====================
+
+![](http://s0.cyberciti.org/images/category/old/linux-logo.png)
+
+> 我是一个 Linux 系统的新手。我该如何在我的 Ubuntu 服务器上查看一个进程(或者根据进程 id 查看)已经运行了多久?
+
+你需要使用 ps 命令来查看关于一组正在运行的进程的信息。ps 命令提供了如下的两种格式化选项。
+
+1. etime 显示了自从该进程启动以来,经历过的时间,格式为 `[[DD-]hh:]mm:ss`。
+2. etimes 显示了自该进程启动以来,经历过的时间,以秒的形式。
+
+### 如何查看一个进程已经运行的时间?
+
+你需要在 ps 命令之后添加 -o etimes 或者 -o etime 参数。它的语法如下:
+
+```
+ps -p {PID-HERE} -o etime
+ps -p {PID-HERE} -o etimes
+```
+
+#### 第一步:找到一个进程的 PID (openvpn 为例)
+
+```
+$ pidof openvpn
+6176
+```
+
+#### 第二步:openvpn 进程运行了多长时间?
+
+```
+$ ps -p 6176 -o etime
+```
+
+或者
+
+```
+$ ps -p 6176 -o etimes
+```
+
+隐藏输出头部:
+
+```
+$ ps -p 6176 -o etime=
+$ ps -p 6176 -o etimes=
+```
+
+样例输出:
+
+![](http://s0.cyberciti.org/uploads/faq/2016/08/How-to-check-how-long-a-process-has-been-running.jpg)
+
+这个 6176 就是你想查看的进程的 PID。在这个例子中,我查看的是 openvpn 进程。你可以按照你的需求随意的更换 openvpn 进程名或者是 PID。在下面的例子中,我打印了 PID、执行命令、运行时间、用户 ID、和用户组 ID:
+
+```
+$ ps -p 6176 -o pid,cmd,etime,uid,gid
+```
+
+样例输出:
+
+```
+  PID CMD                             ELAPSED   UID   GID
+ 6176 /usr/sbin/openvpn --daemon        15:25 65534 65534
+```
+ 
+--------------------------------------------------------------------------------
+
+via: http://www.cyberciti.biz/faq/how-to-check-how-long-a-process-has-been-running/
+
+作者:[VIVEK GITE][a]
+译者:[MikeCoder](https://github.com/MikeCoder)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.cyberciti.biz/faq/how-to-check-how-long-a-process-has-been-running/
diff --git a/published/201608/20160802 3 graphical tools for Git.md b/published/201608/20160802 3 graphical tools for Git.md
new file mode 100644
index 0000000000..1cbad11ba6
--- /dev/null
+++ b/published/201608/20160802 3 graphical tools for Git.md	
@@ -0,0 +1,117 @@
+Git 系列(五):三个 Git 图形化工具
+=============================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/government/BUSINESS_meritladder.png?itok=4CAH2wV0)
+
+在本文里,我们来了解几个能帮你在日常工作中舒服地用上 Git 的工具。
+
+我是在这许多漂亮界面出来之前学习的 Git,而且我的日常工作经常是基于字符界面的,所以 Git 本身自带的大部分功能已经足够我用了。在我看来,最好能理解 Git 的工作原理。不过,能有的选也不错,下面这些就是能让你不用终端就可以开始使用 Git 的一些方式。
+
+### KDE Dolphin 里的 Git
+
+我是一个 KDE 用户,如果不在 Plasma 桌面环境下,就是在 Fluxbox 的应用层。Dolphin 是一个非常优秀的文件管理器,有很多配置项以及大量秘密小功能。大家为它开发的插件都特别好用,其中一个几乎就是完整的 Git 界面。是的,你可以直接在自己的桌面上很方便地管理你的 Git 仓库。
+
+但首先,你得先确认已经安装了这个插件。有些发行版带的 KDE 将各种插件都装的满满的,而有些只装了一些最基本的,所以如果你在下面的步骤里没有看到 Git 相关选项,就在你的软件仓库里找找类似 dolphin-extras 或者 dolphin-plugins 的包。
+
+要打开 Git 集成功能,在 Dolphin 的任一窗口里点击 Settings 菜单,并选择 Configure Dolphin。
+
+在弹出的 Configure Dolphin 窗口里,点击左边侧栏里的 Services 图标。
+
+在 Services 面板里,滚动可用的插件列表找到 Git。
+
+![](https://opensource.com/sites/default/files/4_dolphinconfig.jpg)
+
+(勾选上它,)然后保存你的改动并关闭 Dolphin 窗口。重新启动 Dolphin,浏览一个 Git 仓库试试看。你会发现现在所有文件图标都带有标记:绿色方框表示已经提交的文件,绿色实心方块表示文件有改动,没加入库里的文件没有标记,等等。
+
+之后你在 Git 仓库目录下点击鼠标右键弹出的菜单里就会有 Git 选项了。你在 Dolphin 窗口里点击鼠标就可以检出一个版本,推送或提交改动,还可以对文件进行 `git add` 或 `git remove` 操作。
+
+![](https://opensource.com/sites/default/files/4_dolphingit.jpg)
+
+不过 Dolphin 不支持克隆仓库或是改变远端仓库路径,需要到终端窗口操作,按下 F4 就可以很方便地进行切换。
+
+坦白地说,KDE 的这个功能太牛了,这篇文章已经可以到此为止。将 Git 集成到原生文件管理器里可以让 Git 操作非常清晰;不管你在工作流程的哪个阶段,一切都能直接地摆在面前。在终端里 Git,切换到 GUI 后也是一样 Git。完美。
+
+不过别急,还有好多呢!
+
+### Sparkleshare
+
+SparkleShare 来自桌面环境的另一大阵营,由一些 GNOME 开发人员发起,一个使用文件同步模型 (“就像 Dropbox 一样!”) 的项目。不过它并没有集成任何 GNOME 特有的组件,所以你可以在任何平台使用。
+
+如果你在用 Linux,可以从你的软件仓库直接安装 SparkleShare。如果是其它操作系统,可以去 SparkleShare 网站下载。你可以不用看 SparkleShare 网站上的指引,那个是告诉你如何架设 SparkleShare 服务器的,不是我们这里讨论的。当然你想的话也可以架设 SparkleShare 服务器,但是 SparkleShare 能兼容 Git 仓库,所以其实没必要再架一个自己的。
+
+在安装完成后,从应用程序菜单里启动 SparkleShare。走一遍设置向导,只有两个步骤外加一个简单介绍,然后可以选择是否将 SparkleShare 设置为随桌面自动启动。
+
+![](https://opensource.com/sites/default/files/4_sparklesetup.jpg)
+
+之后在你的系统托盘里会出现一个橙色的 SparkleShare 目录。目前,SparkleShare 对你电脑上的任何东西都一无所知,所以你需要添加一个项目。
+
+要添加一个目录给 SparkleShare 追踪,可以点击系统托盘里的 SparkleShare 图标然后选择 Add Hosted Project。
+
+![](https://opensource.com/sites/default/files/4_sparklehost.jpg)
+
+SparkleShare 支持本地 Git 项目,也可以是存放在像 GitHub 和 Bitbucket 这样的公共 Git 服务器上的项目。要获得完整访问权限,你可能会需要使用 SparkleShare 生成的客户端 ID。这是一个 SSH 密钥,作为你所用到服务的授权令牌,包括你自己的 Git 服务器,应该也使用 SSH 公钥认证而不是用户名密码。将客户端 ID 拷贝到你服务器上 Git 用户的 `authorized_hosts` 文件里,或者是你的 Git 主机的 SSH 密钥面板里。
+
+在配置要你要用的主机后,SparkleShare 会下载整个 Git 项目,包括(你可以自己选择)提交历史。可以在 ~/SparkleShare 目录下找到同步完成的文件。
+
+不像 Dolphin 那样的集成方式,SparkleShare 是不透明的,让人心里没底。在你做出改动后,它会悄悄地把改动同步到服务器远端项目中。对大部分人来说,这样做有一个很大的好处:可以用到 Git 的全部威力但是不用维护。对我来说,这样有些乱,因为我想自己管理我的提交以及要用的分支。
+
+SparkleShare 可能不适合所有人,但是它是一个强大而且简单的 Git 解决方案,展示了不同的开源项目完美地协调整合到一起后所创造出的独特项目。
+
+### Git-cola
+
+另一种配合 Git 仓库工作的模型,没那么原生,更多的是监视方式;不是使用一个集成的应用程序和你的 Git 项目直接交互,而是你可以使用一个桌面客户端来监视项目改动,并随意处理每一个改动。这种方式的一个优势就是专注。当你实际只用到项目里的三个文件的时候,你可能不会关心所有的 125 个文件,能将这三个文件挑出来就很方便了。
+
+如果你觉得有好多 Git 托管网站,那只是你还不知道 Git 客户端有多少。[桌面上的 Git 客户端][1] 上有一大把。实际上,Git 默认自带一个图形客户端。它们中最跨平台、最可配置的就是开源的 [Git-cola][2] 客户端,用 Python 和 Qt 写的。
+
+如果你在用 Linux,Git-cola 应该在你的软件仓库里就有。不是的话,可以直接从它的[网站下载][2]并安装:
+
+```
+$ python setup.py install
+```
+
+启动 git-cola 后,会有三个按钮用来打开仓库,创建新仓库,或克隆仓库。
+
+不管选哪个,最终都会停在一个 Git 仓库中。和大多数我用过的客户端一样,Git-cola 不会尝试成为你的仓库的接口;它们一般会让操作系统工具来做这个。换句话说,我可以通过 Git-cola 创建一个仓库,但随后我就在 Thunar 或 Emacs 里打开仓库开始工作。打开 Git-cola 来监视仓库很不错,因为当你创建新文件,或者改动文件的时候,它们都会出现在 Git-cola 的状态面板里。
+
+Git-cola 的默认布局不是线性的。我喜欢从左向右分布,因为 Git-cola 是高度可配置的,所以你可以随便修改布局。我自己设置成最左边是状态面板,显示当前分支的任何改动,然后右边是差异面板,可以浏览当前改动,然后是动作面板,放一些常用任务的快速按钮,最后,最右边是提交面板,可以写提交信息。
+
+![](https://opensource.com/sites/default/files/4_gitcola.jpg)
+
+不管怎么改布局,下面是 Git-cola 的通用流程:
+
+改动会出现在状态面板里。右键点击一个改动或选中一个文件,然后在动作面板里点击 Stage 按钮来将文件加入待提交暂存区。
+
+待提交文件的图标会变成绿色三角形,表示该文件有改动并且正等待提交。你也可以右键点击并选择 Unstage Selected 将改动移出待提交暂存区,或者点击动作面板里的 Unstage 按钮。
+
+在差异面板里检查你的改动。
+
+当准备好提交后,输入提交信息并点击 Commit 按钮。
+
+在动作面板里还有其它按钮用来处理其它普通任务,比如拉取或推送。菜单里有更多的任务列表,比如用于操作分支,改动审查,变基等等的专用操作。
+
+我更愿意将 Git-cola 当作文件管理器的一个浮动面板(在不能用 Dolphin 的时候我只用 Git-cola)。虽然它的交互性没有完全集成 Git 的文件管理器那么强,但另一方面,它几乎提供了原始 Git 命令的所有功能,所以它实际上更为强大。
+
+有很多 Git 图形客户端。有些是不提供源代码的付费软件,有些只是用来查看,有些尝试加入新的特定术语(用 "sync" 替代 "push" ...?) 来重造 Git,也有一些只适合特定的平台。Git-cola 一直是能在任意平台上使用的最简单的客户端,也是最贴近纯粹 Git 命令的,可以让用户在使用过程中学习 Git,即便是高手也会很满意它的界面和术语。
+
+### Git 命令还是图形界面?
+
+我一般不用图形工具来操作 Git;一般我使用上面介绍的工具时,只是帮助其他人找出适合他们的界面。不过,最终归结于怎么适合你的工作。我喜欢基于终端的 Git 命令是因为它可以很好地集成到 Emacs 里,但如果某天我几乎都在用 Inkscape 工作时,我一般会很自然地使用 Dolphin 里带的 Git,因为我在 Dolphin 环境里。
+
+如何使用 Git 你自己可以选择;但要记住 Git 是一种让生活更轻松的方式,也是让你在工作中更安全地尝试一些疯狂点子的方法。熟悉 Git 的工作模式,然后不管以什么方式使用 Git,只要能让你觉得最适合就可以。
+
+在下一期文章里,我们将了解如何架设和管理 Git 服务器,包括用户权限和管理,以及运行定制脚本。
+
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/life/16/8/graphical-tools-git
+
+作者:[Seth Kenlon][a]
+译者:[zpl1025](https://github.com/zpl1025)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/seth
+[1]: https://git-scm.com/downloads/guis
+[2]: https://git-cola.github.io/
diff --git a/published/201608/20160808 How to cache static files on nginx.md b/published/201608/20160808 How to cache static files on nginx.md
new file mode 100644
index 0000000000..4a1d2511b8
--- /dev/null
+++ b/published/201608/20160808 How to cache static files on nginx.md	
@@ -0,0 +1,79 @@
+如何在 nginx 中缓存静态文件
+===========================
+
+这篇教程说明你应该怎样配置 nginx、设置 HTTP 头部过期时间,用 Cache-Control 中的 max-age 标记为静态文件(比如图片、 CSS 和 Javascript 文件)设置一个时间,这样用户的浏览器就会缓存这些文件。这样能节省带宽,并且在访问你的网站时会显得更快些(如果用户第二次访问你的网站,将会使用浏览器缓存中的静态文件)。
+
+### 1、准备事项
+
+我想你需要一个正常工作的 nginx 软件,就像这篇教程里展示的:[在 Ubuntu 16.04 LTS 上安装 Nginx,PHP 7 和 MySQL 5.7 (LEMP)](1)。
+
+### 2 配置 nginx
+
+可以参考 [expires](2) 指令手册来设置 HTTP 头部过期时间,这个标记可以放在 `http {}`、`server {}`、`location {}` 等语句块或者 `location {}` 语句块中的条件语句中。一般会在 `location` 语句块中用 `expires` 指令控制你的静态文件,就像下面一样:
+
+```
+location ~*  \.(jpg|jpeg|png|gif|ico|css|js)$ {
+   expires 365d;
+}
+```
+
+在上面的例子中,所有后缀名是 `.jpg`、 `.jpeg`、 `.png`、 `.gif`、 `.ico`、 `.css` 和 `.js` 的文件会在浏览器访问该文件之后的 365 天后过期。因此你要确保 `location {}` 语句块仅仅包含能被浏览器缓存的静态文件。
+
+然后重启 nginx 进程:
+
+```
+/etc/init.d/nginx reload
+```
+
+你可以在 `expires` 指令中使用以下的时间设置:
+
+- `off` 让 `Expires` 和 `Cache-Control` 头部不能被更改。
+- `epoch` 将 `Expires` 头部设置成 1970 年 1 月 1 日 00:00:01。
+- `max` 设置 `Expires` 头部为 2037 年 12 月 31 日 23:59:59,设置 `Cache-Control` 的最大存活时间为 10 年
+- 没有 `@` 前缀的时间意味着这是一个与浏览器访问时间有关的过期时间。可以指定一个负值的时间,就会把 Cache-Control 头部设置成 no-cache。例如:`expires 10d` 或者 `expires 14w3d`。
+- 有 `@` 前缀的时间指定在一天中的某个时间过期,格式是 Hh 或者 Hh:Mm,H 的范围是 0 到 24,M 的范围是 0 到 59,例如:`expires @15:34`。
+
+你可以用以下的时间单位:
+
+- `ms`: 毫秒
+- `s`: 秒
+- `m`: 分钟
+- `h`: 小时
+- `d`: 天
+- `w`: 星期
+- `M`: 月 (30 天)
+- `y`: 年 (365 天)
+
+例如:`1h30m` 表示一小时三十分钟,`1y6m` 表示一年六个月。
+
+注意,要是你用一个在将来很久才会过期的头部,当组件修改时你就要改变组件的文件名。因此给文件指定版本是一个不错的方法。例如,如果你有个 javascript.js 文件 并且你要修改它,你可以在修改的文件名字后面添加一个版本号。这样浏览器就要下载这个文件,如果你没有更改文件名,浏览器将从缓存里面加载(旧的)文件。
+
+除了把基于浏览器访问时间设置 `Expires` 头部(比如 `expires 10d`)之外,也可以通过在时间前面的 `modified` 关键字,将 `Expires` 头部的基准设为文件修改的时间(请注意这仅仅对存储在硬盘的实际文件有效)。
+
+```
+expires modified 10d;
+```
+
+### 3 测试
+
+要测试你的配置是否有效,可以用火狐浏览器的开发者工具中的网络分析功能,然后用火狐访问一个静态文件(比如一张图片)。在输出的头部信息里,应该能看到 `Expires` 头部和有 `max-age` 标记的 `Cache-Control` 头部(`max-age` 标记包含了一个以秒为单位的值,比如 31536000 就是指今后的一年)
+
+![](https://www.howtoforge.com/images/how-to-cache-static-files-on-nginx/accept_headers.png)
+
+### 4 链接
+
+nginx 的 Http 头部模块(HttpHeadersModule): <http://wiki.nginx.org/HttpHeadersModule>
+
+--------------------------------------------------------------------------------
+
+via: https://www.howtoforge.com/tutorial/how-to-cache-static-files-on-nginx/
+
+作者:[Falko Timme][a]
+译者:[GitFuture](https://github.com/GitFuture)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.howtoforge.com/tutorial/how-to-cache-static-files-on-nginx/
+[1]: https://linux.cn/article-7551-1.html
+[2]:http://nginx.org/en/docs/http/ngx_http_headers_module.html#expires
diff --git a/published/201608/20160809 7 reasons to love Vim.md b/published/201608/20160809 7 reasons to love Vim.md
new file mode 100644
index 0000000000..c15ca99bb5
--- /dev/null
+++ b/published/201608/20160809 7 reasons to love Vim.md	
@@ -0,0 +1,43 @@
+爱 Vim 的七个理由
+====================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/BUS_OpenSourceExperience_520x292_cm.png?itok=APna2N9Y)
+
+当我刚刚开始用 vi 文本编辑器的时候,我憎恨它!我认为这是有史以来设计上最痛苦和反人类的编辑器。但我还是决定我必须学会它,因为如果你使用的是 Unix,vi 无处不在并且是唯一一个保证你可以使用的编辑器。在 1998 年是如此,但是直到今天 vi 也仍然是可用的,现有的几乎每个发行版中,vi 基本上都是基础安装的一部分。
+
+在我学会能使用任何功能前,我已经在 vi 上花费差不多 1 个月的时间,但是我仍然不喜欢它。不过那时我已经意识到有个强大的编辑器隐藏在这个古怪的外表后面。所以我坚持使用它,并且最终发现一旦你知道你在干什么,它就是一个快的令人难以置信的编辑器。
+
+“vi” 这个名称是 “可视(visual)” 的缩写。在 vi 出现的时候,行编辑器是很普遍的,能一次性显示并编辑多个行是非同寻常的。Vim,来自“Vi IMproved”的缩写,最初由 Bram Moolenaar 发布于 1991 年,它成为了主要的仿 vi 软件,并且扩展了这个强大的编辑器已有的功能。Vim 强大的正则表达式和“:”命令行语法开始于行编辑和电传打字机时代。
+
+Vim,有 40 年的历史了,有足够的时间发展出海量而复杂的技巧,即使是懂得最多的用户都不能完全掌握它。这里列出了一些爱 Vim 的理由:
+
+1. 配色方案:你可能知道 Vim 有彩色语法高亮。但你知道可以下载数以百计的配色方案么?[在这找到些更好的][1]。
+2. 你再也不需要让你的手离开键盘或者去碰触鼠标。
+3. Vi 或者 Vim 存在任何地方,甚至在 [OpenWRT][2] 里面也有 vi(好吧,其实是在 [BusyBox][3]中,它挺好用的)。
+4. Vimscript:你可能会想重映射几个键,但是你知道 Vim 有自己的编程语言么?你可以重新定义你的编辑器的行为,或者创造特定语言的编辑器扩展。(最近我在定制 Vim 用于 Ansible 的行为。)学习这个语言最佳的切入点是看 Steve Losh 著名的书《[Learn Vimscript the Hard Way][4]》。
+5. Vim 有插件。使用 [vundle][5](我用的就是它)或者 [Pathogen][6] 来管理你的插件来提升 Vim 的功能。
+6. 插件可以将 git(或者你选择的 VCS)集成到 Vim 中。
+7. 有庞大而活跃的线上社区,如果你在线上提问关于 Vim 的问题,肯定会有人回答。
+
+我一开始讨厌 vi 的可笑之处在于,这 5 年来不断的在尝试新的编辑器中碰壁,总是想找到“一些更好的”。我从来没有像讨厌 vi 一样讨厌过其它的编辑器,现在我已经使用它 17 年了,因为我想象不出一个更好的编辑器。额,或许有稍微好一点的:可以尝试下 Neovim -这是未来的主流。看起来 Bram Moolenaar 将会把 Neovim 的大部分融入到 Vim 第 8 版中,这意味着将会在现有的代码基础上减少 30%、更好的代码补全功能、真正的异步、内置终端、内置鼠标支持、完全兼容。
+
+*在本文作者在多伦多的 [LinuxCon 演讲][7]中(LCTT 译注:LinuxCon 是 Linux 基金会举办的年度会议),他解释了一些在你可能错过的、过去四十年增加的杂乱的扩展和改进。这个内容不适合初学者,所以如果你不知道为什么“hjklia:wq”是很重要的,这就可能不是讲给你听的。它还会涉及一点关于 vi 的历史,因为知道一些历史能帮助我们理解我们的处境。关注他的演讲能让你知道如何使你最喜欢的编辑器更好更快。*
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/business/16/8/7-reasons-love-vim
+
+作者:[Giles Orr][a]
+译者:[hkurj](https://github.com/hkurj)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/gilesorr
+[1]: http://www.gilesorr.com/blog/vim-colours.html
+[2]: https://www.openwrt.org/
+[3]: https://busybox.net/
+[4]: http://learnvimscriptthehardway.stevelosh.com/
+[5]: https://github.com/VundleVim/Vundle.vim
+[6]: https://github.com/tpope/vim-pathogen
+[7]: http://sched.co/7JWz
diff --git a/published/201608/20160810 A brief introduction to Linux containers and image signing.md b/published/201608/20160810 A brief introduction to Linux containers and image signing.md
new file mode 100644
index 0000000000..7e6f6ef2e7
--- /dev/null
+++ b/published/201608/20160810 A brief introduction to Linux containers and image signing.md	
@@ -0,0 +1,59 @@
+浅谈 Linux 容器和镜像签名
+====================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/containers_2015-1-osdc-lead.png?itok=E1imOYe4)
+
+从根本上说,几乎所有的主要软件,即使是开源软件,都是在基于镜像的容器技术出现之前设计的。这意味着把软件放到容器中相当于是一次平台移植。这也意味着一些程序可以很容易就迁移,[而一些就更困难][1]。
+
+我大约在三年半前开展基于镜像的容器相关工作。到目前为止,我已经容器化了大量应用。我了解到什么是现实情况,什么是迷信。今天,我想简要介绍一下 Linux 容器是如何设计的,以及谈谈镜像签名。
+
+### Linux 容器是如何设计的
+
+对于基于镜像的 Linux 容器,让大多数人感到困惑的是,它把操作系统分割成两个部分:[内核空间与用户空间][2]。在传统操作系统中,内核运行在硬件上,你无法直接与其交互。用户空间才是你真正能交互的,这包括所有你可以通过文件浏览器或者运行`ls`命令能看到的文件、类库、程序。当你使用`ifconfig`命令调整 IP 地址时,你实际上正在借助用户空间的程序来使内核根据 TCP 协议栈改变。这点经常让没有研究过 [Linux/Unix 基础][3]的人大吃一惊。
+
+过去,用户空间中的类库支持了与内核交互的程序(比如 ifconfig、sysctl、tuned-adm)以及如网络服务器和数据库之类的面向用户的程序。这些所有的东西都堆积在一个单一的文件系统结构中。用户可以在 /sbin 或者 /lib 文件夹中找到所有操作系统本身支持的程序和类库,或者可以在 /usr/sbin 或 /usr/lib 文件夹中找到所有面向用户的程序或类库(参阅[文件系统层次结构标准][4])。这个模型的问题在于操作系统程序和业务支持程序没有绝对的隔离。/usr/bin 中的程序可能依赖 /lib 中的类库。如果一个应用所有者需要改变一些东西,就很有可能破坏操作系统。相反地,如果负责安全更新的团队需要改变一个类库,就(常常)有可能破坏面向业务的应用。这真是一团糟。
+
+借助基于镜像的容器,比如 Docker、LXD、RKT,应用程序所有者可以打包和调整所有放在 /sbin、/lib、/usr/bin 和 /usr/lib 中的依赖部分,而不用担心破坏底层操作系统。本质上讲,容器技术再次干净地将操作系统隔离为两部分:内核空间与用户空间。现在开发人员和运维人员可以分别独立地更新各自的东西。
+
+然而还是有些令人困扰的地方。通常,每个应用所有者(或开发者)并不想负责更新这些应用依赖:像 openssl、glibc,或很底层的基础组件,比如,XML 解析器、JVM,再或者处理与性能相关的设置。过去,这些问题都委托给运维团队来处理。由于我们在容器中打包了很多依赖,对于很多组织来讲,对容器内的所有东西负责仍是个严峻的问题。
+
+### 迁移现有应用到 Linux 容器
+
+把应用放到容器中算得上是平台移植,我准备突出介绍究竟是什么让移植某些应用到容器当中这么困难。
+
+(通过容器,)开发者现在对 /sbin 、/lib、 /usr/bin、 /usr/lib 中的内容有完全的控制权。但是,他们面临的挑战是,他们仍需要将数据和配置放到 /etc 或者 /var/lib 文件夹中。对于基于镜像的容器来说,这是一个糟糕的想法。我们真正需要的是代码、配置以及数据的隔离。我们希望开发者把代码放在容器当中,而数据和配置通过不同的环境(比如,开发、测试或生产环境)来获得。
+
+这意味着我们(或者说平台)在实例化容器时,需要挂载 /etc 或 /var/lib 中的一些文件或文件夹。这会允许我们到处移动容器并仍能从环境中获得数据和配置。听起来很酷吧?这里有个问题,我们需要能够干净地隔离配置和数据。很多现代开源软件比如 Apache、MySQL、MongoDB、Nginx 默认就这么做了。[但很多自产的、历史遗留的、或专有程序并未默认这么设计][5]。对于很多组织来讲,这是主要的痛点。对于开发者来讲的最佳实践是,开始架构新的应用,移植遗留代码,以完成配置和数据的完全隔离。
+
+### 镜像签名简介
+
+信任机制是容器的重要议题。容器镜像签名允许用户添加数字指纹到镜像中。这个指纹随后可被加密算法测试验证。这使得容器镜像的用户可以验证其来源并信任。
+
+容器社区经常使用“容器镜像”这个词组,但这个命名方法会让人相当困惑。Docker、LXD 和 RKT 推行获取远程文件来当作容器运行这样的概念。这些技术各自通过不同的方式处理容器镜像。LXD 用单独的一层来获取单独一个容器,而 Docker 和 RKT 使用基于开放容器镜像(OCI)格式,可由多层组成。糟糕的是,会出现不同团队和组织对容器镜像中的不同层负责的情况。容器镜像概念下隐含的是容器镜像格式的概念。拥有标准的镜像格式比如 OCI 会让容器生态系统围绕着镜像扫描、签名,和在不同云服务提供商间转移而繁荣发展。
+
+现在谈到签名了。
+
+容器存在一个问题,我们把一堆代码、二进制文件和类库放入其中。一旦我们打包了代码,我们就要把它和必要的文件服务器(注册服务器)共享。代码只要被共享,它基本上就是不具名的,缺少某种密文签名。更糟糕的是,容器镜像经常由不同人或团队控制的各个镜像层组成。每个团队都需要能够检查上一个团队的工作,增加他们自己的工作内容,并在上面添加他们自己的批准印记。然后他们需要继续把工作交给下个团队。
+
+(由很多镜像组成的)容器镜像的最终用户需要检查监管链。他们需要验证每个往其中添加文件的团队的可信度。对于最终用户而言,对容器镜像中的每一层都有信心是极其重要的。
+
+*作者 Scott McCarty 于 8 月 24 日在 ContainerCon 会议上作了题为 [Containers for Grownups: Migrating Traditional & Existing Applications][6] 的报告,更多内容请参阅报告[幻灯片][7]。*
+
+--------------------------------------------------------------------------------
+
+via:  https://opensource.com/bus/16/8/introduction-linux-containers-and-image-signing
+
+作者:[Scott McCarty][a]
+译者:[Tanete](https://github.com/Tanete)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/fatherlinux
+[1]: http://rhelblog.redhat.com/2016/04/21/architecting-containers-part-4-workload-characteristics-and-candidates-for-containerization/
+[2]: http://rhelblog.redhat.com/2015/07/29/architecting-containers-part-1-user-space-vs-kernel-space/
+[3]: http://rhelblog.redhat.com/tag/architecting-containers/
+[4]: https://linux.cn/article-6132-1.html
+[5]: http://rhelblog.redhat.com/2016/04/21/architecting-containers-part-4-workload-characteristics-and-candidates-for-containerization/
+[6]: https://lcccna2016.sched.org/event/7JUc/containers-for-grownups-migrating-traditional-existing-applications-scott-mccarty-red-hat
+[7]: http://schd.ws/hosted_files/lcccna2016/91/Containers%20for%20Grownups_%20Migrating%20Traditional%20%26%20Existing%20Applications.pdf
diff --git a/published/20160803 The revenge of Linux.md b/published/20160803 The revenge of Linux.md
new file mode 100644
index 0000000000..5417b62a05
--- /dev/null
+++ b/published/20160803 The revenge of Linux.md	
@@ -0,0 +1,36 @@
+Linux 的逆袭
+========================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/life/penguin%20swimming.jpg?itok=mfhEdRdM)
+
+Linux 系统在早期的时候被人们嘲笑,它什么也干不了。而现在,Linux 无处不在!
+
+我当时还是个在巴西学习计算机工程的大三学生,并同时在一个全球审计和顾问公司兼职系统管理员。公司决定用 Oracle 数据库开发一些企业资源计划(ERP)软件。因此,我得以在 Digital UNIX OS (DEC Alpha) 进行训练,这个训练颠覆了我的三观。
+
+UNIX 系统非常的强大,而且给予了我们对机器上包括存储系统、网络、应用和其他一切的绝对控制权。
+
+我开始在 ksh 和 Bash 里编写大量的脚本让系统进行自动备份、文件传输、提取转换加载(ETL)操作、自动化 DBA 日常工作,还为各种不同的项目创建了许多服务。此外,调整数据库和操作系统的工作让我更好的理解了如何让服务器以最佳方式运行。在那时,我在自己的个人电脑上使用的是 Windows 95 系统,而我非常想要在我的个人电脑里放进一个 Digital UNIX,或者哪怕是 Solaris 或 HP-UX 也行,但是那些 UNIX 系统都得在特定的硬件才能上运行。我阅读了所有的系统文档,还找过其它的书籍以求获得更多的信息,也在我们的开发环境里对这些疯狂的想法进行了实验。
+
+后来在大学里,我从我的同事那听说了 Linux。我那时非常激动的从还在用拨号方式连接的因特网上下载了它。在我的正宗的个人电脑里装上 UNIX 这类系统的这个想法真是太酷了!
+
+Linux 不同于 UNIX 系统,它设计用来在各种常见个人电脑硬件上运行,在起初,让它开始工作确实有点困难,Linux  针对的用户群只有系统管理员和极客们。我为了让它能运行,甚至用 C 语言修改了驱动软件。我之前使用 UNIX 的经历让我在编译 Linux 内核,排错这些过程中非常的顺手。由于它不同于那些只适合特定硬件配置的封闭系统,所以让 Linux 跟各种意料之外的硬件配置一起工作真的是件非常具有挑战性的事。
+
+我曾见过 Linux 在数据中心获得一席之地。一些具有冒险精神的系统管理员使用它来帮他们完成每天监视和管理基础设施的工作,随后,Linux 作为 DNS 和 DHCP 服务器、打印管理和文件服务器等赢得了更多的使用。企业曾对 Linux 有着很多顾虑(恐惧,不确定性,怀疑(FUD:fear, uncertainty and doubt))和诟病:谁是它的拥有者?由谁来支持它?有适用于它的应用吗?
+
+但现在看来,Linux 在各个地方进行着逆袭!从开发者的个人电脑到大企业的服务器;我们能在智能手机、智能手表以及像树莓派这样的物联网(IoT)设备里找到它。甚至 Mac OS X 有些命令跟我们所熟悉的命令一样。微软在制造它自己的发行版,在 Azure 上运行,然后…… Windows 10 要装备 Bash。
+
+有趣的是 IT 市场会不断地创造并迅速的用新技术替代,但是我们所掌握的 Digital UNIX、HP-UX 和 Solaris 这些旧系统的知识还依然有效并跟 Linux 息息相关,不论是为了工作还是玩。现在我们能完全的掌控我们的系统,并使它发挥最大的效用。此外,Linux 有个充满热情的社区。
+
+我真的建议想在计算机方面发展的年轻人学习 Linux,不论你处于 IT 界里的哪个分支。如果你深入了解了一个普通的家用个人电脑是如何工作的,你就可以以基本相同的方式来面对任何机器。你可以通过 Linux 学习最基本的计算机知识,并通过它建立能在 IT 界任何地方都有用的能力!
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/life/16/8/revenge-linux
+
+作者:[Daniel Carvalho][a]
+译者:[H-mudcup](https://github.com/H-mudcup)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/danielscarvalho
diff --git a/published/20160808 Why measuring IT productivity is so challenging.md b/published/20160808 Why measuring IT productivity is so challenging.md
new file mode 100644
index 0000000000..920b1d689d
--- /dev/null
+++ b/published/20160808 Why measuring IT productivity is so challenging.md	
@@ -0,0 +1,36 @@
+为什么计量 IT 的生产力如此具有挑战性?
+===========================
+
+![](https://enterprisersproject.com/sites/default/files/styles/620x350/public/images/cio_talent_6.png?itok=JV-zSor3)   
+
+在某些行业里,人们可以根据一些测量标准判定一个人的生产力。比如,如果你是一个零件制造商,可以通过一个月你能够制造的零件数量来确定你的生产效率。如果你在客户服务中心工作,你解答了多少个客户来电,你的平均解答时间都会成为评判你的生产效率的依据。这些都是相当简单的案例,但即便你是一位医生,也可以通过你主刀的临床手术次数或者一个月你确诊的患者数量来确定你的生产效率。无论这些评判标准正确与否,但它们提供了一个通用的方法来评断一个人在给定时间内的执行能力。
+
+然而在 IT 这方面,通过上述方法来衡量一个人的生产力是不可能的,因为 IT 有太多的变化性。比如,通过一个开发者编写的代码行数来衡量开发者所用的时间看起来很诱人。但是,编程的语言很大程度上能影响到根据这种方法得到的结论。因为一种编程语言的一行代码比用其他编程语言编写所花费的时间和难度可能会明显的多或少。
+
+它总是这样不可捉摸吗?多年以前,你可能听说过或者经历过根据功能点来衡量 IT 工作人员的生产效率。这些措施是针对开发者们能够创建的关键特征来衡量开发者的生产效率的。但这种方法在今天也变得逐渐难以实施,开发者经常将可能已有的逻辑封装进内部,比如,按供应商来整合功能点。这使得仅仅是基于功能点的数目来估量生产效率难度加大。
+
+这两个例子能够阐述为什么当我们 CIO 之间谈论 IT 生产效率的时候有时会引起争论。考虑以下这个假想中的谈话:
+
+> IT leader:“天啊,我想这些开发者一定很厉害。”  
+> HR:“真的假的,他们做了什么?”   
+> IT leader:“他们做了个相当好的应用。” 
+> HR:“好吧,那他们比那些做了 10 个应用的开发者更好吗”
+> IT leader:“这要看你怎么理解 ‘更好’。”
+
+这个对话比较有代表性。当我们处于上述的这种交谈时,这里面有太多的主观因素导致我们很难回答这个问题。当我们用一种有意义的方法来测试 IT 的效率时,类似上述谈话的这种问题仅仅是冰山一角。这不仅仅使谈话更加困难-它还会使 CIO 们很难展示他们的团队在商业上的价值。
+
+确实这不是一个新出现的问题。我已经花费差不多 30 年的时间来思考这个问题。我得出的结论是我们真的不应该在谈论 IT 的生产效率这件事上面相互烦扰-因为我们永远不可能有结论。
+
+我认为我们需要在改变这种对话同时停止根据生产能力和成本来谈论 IT 的生产效率,将目光集中于衡量 IT 的整体商业价值上。重申一下,这个过程不会很容易。商业价值的实现是一件困难的事情。但如果 CIO 们能够和商业人员合作来解决这个问题,就可以将实际价值变的更加科学而非一种艺术形式。
+
+--------------------------------------------------------------------------------
+
+via: https://enterprisersproject.com/article/2016/8/why-measuring-it-productivity-so-challenging
+
+作者:[Anil Cheriyan][a]
+译者:[LemonDemo](https://github.com/LemonDemo) [WangYueScream](https://github.com/WangYueScream)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://enterprisersproject.com/user/anil-cheriyan
diff --git a/published/20160814 How I Got to be an Android Dev And What I Learned From It.md b/published/20160814 How I Got to be an Android Dev And What I Learned From It.md
new file mode 100644
index 0000000000..0caca5f4df
--- /dev/null
+++ b/published/20160814 How I Got to be an Android Dev And What I Learned From It.md	
@@ -0,0 +1,58 @@
+我的 Android 开发者之路以及我在其中学到了什么
+=============================
+
+大家都说所有的关系都需要经历两年、七年甚至十年的磨砺。我忘了是谁说的这句话,但肯定有人在几年前这么跟我说过。
+
+下周是我来悉尼两周年,所以我想现在正是我写这篇文章的好时候。
+
+去年五月份参加 I/O 年会的时候,我遇到了亚斯曼女士,她十分漂亮。她向我询问我是如何成长为一名安卓开发者的,当我说完我的经历时,她认为我应该写个博客记下来。所以亚斯曼,如你所愿,虽然迟了点,但好过没做。;)
+
+### 故事的开始
+
+如果有件事我可能希望你知道,那就是我发现自己有选择困难症。你最好的朋友是谁?你最喜欢的食物是什么?你应该给你的玩具熊猫命名吗?我连这些问题都不知道该怎么回答才好。所以你可以想象到,16 岁的、即将高中毕业的我对于专业选择根本就没有任何想法。那我最初申请的大学是?在交表给注册员前,我在她面前逐字掂量着写下这个打算申请的专业(商业经济学)。
+
+可我最后去了另外一间学校,就读电子与通信工程。大一时我有一门计算机编程课程。但我很讨厌编程,十分地讨厌。关于编程的一切我都一无所知。我曾发誓再也不要写代码了。
+
+我大学毕业后的第一份工作是在英特尔做产品工程师并在那呆了两年。我很迷茫,无所适从,整天长时间工作。这在我意料之中,身为成年人难道不该努力工作吗?可之后菲律宾的半导体行业开始呈现颓势,大批工厂纷纷倒闭,以前由我们维护一些产品被转移到其他分公司。我便决定去找另一份工作而不是等着被裁员,因为被裁员后我都不知道自己多久才能找到另一份工作。
+
+### 现在呢?
+
+我想留在城市里找到一份工作,但我不想呆在正在没落的半导体行业里了。但话说回来,我又不知道该做什么好。对了,我可是拿了毕业证书的工程师,所以从技术上来说我可以在电信运营商或电视台找到工作。可这种时候,如果想入职电信运营商,我应该在大学毕业之际就去他们那实习,这样更容易被录用。可惜我没有,所以我放弃了这个想法。虽然有很多软件开发人员的招聘信息,但我讨厌编程,所以我真的不知道怎么做才好。
+
+接下来是我第一个幸运的机遇,我很幸运地遇到了信任我的上司,我也和她坦诚了我什么都不会。之后我不得不边工作边学习,一开始这个过程很漫长。无需多言,我在这份工作上学到了很多,也结识了很多很好的人,与我一起的是一群很厉害的同事(我们曾开发出安装在 SIM 卡上的 APP)。但更重要的是我开始踏上了软件开发的征途。
+
+最后我做得更多是一些公司的琐事(十分无聊)直到项目完结。换句话说,我总在是在办公室里闲逛并坐等发薪。之后我发现这确实是在浪费时间,2009 年的时候,我不停地接触到关于谷歌的新系统 Android 的消息,并得知它的 SDK 已经公布!是时候尝试一波了。于是我安装了所有相关软件并着手 Android 开发。
+
+### 事情变得有趣了
+
+所以现在我能够构建一个在运行在仿真器的 Hello World 应用,在我看来意味着我有胜任安卓开发工作的能力。我加入了一个创业公司,并且再次坦诚我不知道该怎么做,我只是接触过一些;但如果你们愿意付薪水给我继续尝试,我们就可以成为朋友。然后我很幸运地遇到另一个机遇。
+
+那时成为开发者是一件令人欣喜的事。StackOverflow 上的 Android 开发社区非常小,我们都在相互交流学习,说真的,我认为里面的所有人都很友好、很豪迈(注 1)!
+
+我最后去了一家企业,这家企业的移动开发团队在马尼拉、悉尼、纽约都设有办公地点。而我是马尼拉办公地点的第一个安卓开发人员,但那时我很习惯,并没有在意。
+
+在那里我认识了最后令我永远感激的引荐我参与 Domain 项目的人。Domain 项目不管在个人或职业上对我来说都意味深重。我和一支很有才华的团队一起共事,也从没见过一个公司能如此执着于一款产品。Domain 让我实现了参加 I/O 年会的梦。与他们共事后我懂得了很多之前没想到的可爱特性(注 2)。这是又一个幸运的机遇,我是说最大限度地利用它。
+
+### 然后呢?
+
+我想说的是,虽然这些年都在晃荡,但至少我很诚实,对吧?如上就是我所学到的全部东西。说一句「我不懂」没什么可怕的。有时候我们是该装懂,但更多时候我们需要坦诚地接受这样一个事实:我们还不懂。
+
+别害怕尝试新事物,不管它让你感觉多害怕。我知道说比做简单。但总有一些东西能让你鼓起勇气动手去尝试的(注3)。Lundagin mo, baby!(LCTT 译注:一首歌名)
+
+
+注 1: 我翻阅着以前在 StackOverflow 提的问题,认真想想,如果现在我问他们这些,估计会收到很多「你是谁啊,傻瓜」的评论。我不知道是不是因为我老了,而且有些愤世妒俗。但关键是,我们有缘在同一个社区中,所以大家相互之间友善些,好吗?
+注 2: 这一点写在另一篇文章里了。
+注 3: 我还清晰地记得第一次申请 Android 开发职位的情形:我写完求职信后又通读了一遍,提交前鼠标在发送按钮上不断徘徊,深呼吸之后我趁改变主意之前把它发出去了。
+
+
+--------------------------------------------------------------------------------
+
+via: http://www.zdominguez.com/2016/08/winging-it-how-i-got-to-be-android-dev.html
+
+作者:[Zarah Dominguez][a]
+译者:[JianhuanZhuo](https://github.com/JianhuanZhuo)
+校对:[PurlingNayuki](https://github.com/PurlingNayuki)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://plus.google.com/102371834744366149197
diff --git a/published/20160815 Alternative System Monitor Applet For LXDE  Xfce  And MATE - Multiload-ng.md b/published/20160815 Alternative System Monitor Applet For LXDE  Xfce  And MATE - Multiload-ng.md
new file mode 100644
index 0000000000..55e867dd46
--- /dev/null
+++ b/published/20160815 Alternative System Monitor Applet For LXDE  Xfce  And MATE - Multiload-ng.md	
@@ -0,0 +1,102 @@
+LXDE、Xfce 及 MATE 桌面环境下的又一系统监视器应用:Multiload-ng
+======
+
+[Multiload-ng][1] 是一个 GTK2 图形化系统监视器应用,可集成到 Xfce、LXDE 及 MATE 的桌面面板中, 它 fork 自原来的 GNOME Multiload 应用。它也可以运行在一个独立的窗口中。
+
+![](https://2.bp.blogspot.com/-U8CFzhSPJho/V7GigDbcLWI/AAAAAAAAYS0/pJMM6Rt5-HkbKljmxzP4-v0oGGxjvH8AgCLcB/s1600/multiload-ng-lxde.png)
+
+Multiload-ng 的特点有:
+
+- 支持以下资源的图形块: CPU,内存,网络,交换空间,平均负载,磁盘以及温度;
+- 高度可定制;
+- 支持配色方案;
+- 自动适应容器(面板或窗口)的改变;
+- 极低的 CPU 和内存占用;
+- 提供基本或详细的提示信息;
+- 可自定义双击触发的动作。
+
+相比于原来的 Multiload 应用,Multiload-ng 含有一个额外的图形块(温度),以及更多独立的图形自定义选项,例如独立的边框颜色,支持配色方案,可根据自定义的动作对鼠标的点击做出反应,图形块的方向可以被设定为与面板的方向无关。
+
+它也可以运行在一个独立的窗口中,而不需要面板:
+
+![](https://1.bp.blogspot.com/-hHoipwFlHrg/V7Gw2s107zI/AAAAAAAAYTQ/fS5OtiL7VvwDEzr6qO_gdEA_qB9YvJa5gCLcB/s400/multiload-ng-standalone.png)
+
+另外,它的 GitHub page 上说还会带来更多的图形块支持。
+
+下图展示的是在带有一个垂直面板的 Xubuntu 16.04 中,该应用分别处于水平和垂直方向的效果:
+
+![](https://3.bp.blogspot.com/-xa0OML8T-lg/V7Gixksbt8I/AAAAAAAAYS4/Jxo-MukDh3sYlOOk9A1YGtARmte490g8ACLcB/s400/multiload-ng-xfce-horizontal.png)
+
+![](https://1.bp.blogspot.com/-WAD5MdDObD8/V7GixgVU0DI/AAAAAAAAYS8/uMhHJri1GJccRWvmf_tZkYeenVdxiENQwCLcB/s400/multiload-ng-xfce-vertical.png)
+
+这个应用的偏好设置窗口虽然不是非常好看,但是有计划去改进它:
+
+![](https://2.bp.blogspot.com/-P-ophDpc-gI/V7Gi_54b7JI/AAAAAAAAYTA/AHQck_JF_RcwZ1KbgHbaO2JRt24ZZdO3gCLcB/s320/multiload-ng-preferences.png)
+
+Multiload-ng 当前使用的是 GTK2,所以它不能在构建自 GTK3 下的 Xfce 或 MATE 桌面环境(面板)下工作。
+
+对于 Ubuntu 系统而言,只有 Ubuntu MATE 16.10 使用 GTK3。但是鉴于 MATE 的系统监视器应用也是 Multiload GNOME 的一个分支,所以它们大多数的功能相同(除了 Multiload-ng 提供的额外自定义选项和温度图形块)。
+
+该应用的[愿望清单][2] 中提及到了计划支持 GTK3 的集成以及各种各样的改进,例如温度块资料的更多来源,能够显示十进制(KB、MB、GB……)或二进制(KiB、MiB、GiB……)单位等等。
+
+### 安装 Multiload-ng
+
+请注意因为依赖的关系, Multiload-ng 不能在 Lubuntu 14.04 上构建。
+
+Multiload-ng 可在  WebUpd8 的主 PPA (针对 Ubuntu 14.04 - 16.04 / Linux Mint 17.x 和 18)中获取到。可以使用下面的命令来添加 PPA 并更新软件源:
+
+```
+sudo add-apt-repository ppa:nilarimogard/webupd8
+sudo apt update
+```
+
+然后可以使用下面的命令来安装这个应用:
+
+- 对于 LXDE (Lubuntu):
+
+```
+sudo apt install lxpanel-multiload-ng-plugin
+```
+
+- 对于 Xfce (Xubuntu,Linux Mint Xfce):
+
+```
+sudo apt install xfce4-multiload-ng-plugin
+```
+
+- 对于 MATE (Ubuntu MATE,Linux Mint MATE):
+
+```
+sudo apt install mate-multiload-ng-applet
+```
+
+- 独立安装 (不需要集成到面板):
+
+```
+sudo apt install multiload-ng-standalone
+```
+
+一旦安装完毕,便可以像其他应用那样添加到桌面面板中了。需要注意的是在 LXDE 中,Multiload-ng 不能马上出现在面板清单中,除非重新启动面板。你可以通过重启会话(登出后再登录)或者使用下面的命令来重启面板:
+
+```
+lxpanelctl restart
+```
+
+独立的 Multiload-ng 应用可以像其他正常应用那样从菜单中启动。
+
+如果要下载源码或报告 bug 等,请看 Multiload-ng 的 [GitHub page][3]。
+
+--------------------------------------------------------------------------------
+
+via: http://www.webupd8.org/2016/08/alternative-system-monitor-applet-for.html
+
+作者:[Andrew][a]
+译者:[FSSlc](https://github.com/FSSlc)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.webupd8.org/p/about.html
+[1]: https://github.com/udda/multiload-ng
+[2]: https://github.com/udda/multiload-ng/wiki/Wishlist
+[3]: https://github.com/udda/multiload-ng
diff --git a/published/20160816 Accelerating Node.js applications with HTTP2 Server Push.md b/published/20160816 Accelerating Node.js applications with HTTP2 Server Push.md
new file mode 100644
index 0000000000..d2f33e24f0
--- /dev/null
+++ b/published/20160816 Accelerating Node.js applications with HTTP2 Server Push.md	
@@ -0,0 +1,111 @@
+使用 HTTP/2 服务端推送技术加速 Node.js 应用
+=========================================================
+
+四月份,我们宣布了对 [HTTP/2 服务端推送技术][3]的支持,我们是通过 HTTP 的 [Link 头部](https://www.w3.org/wiki/LinkHeader)来实现这项支持的。我的同事 John 曾经通过一个例子演示了[在 PHP 里支持服务端推送功能][4]是多么的简单。
+
+![](https://blog.cloudflare.com/content/images/2016/08/489477622_594bf9e3d9_z.jpg)
+
+我们想让现今使用 Node.js 构建的网站能够更加轻松的获得性能提升。为此,我们开发了 [netjet][1] 中间件,它可以解析应用生成的 HTML 并自动添加 Link 头部。当在一个示例的 Express 应用中使用这个中间件时,我们可以看到应用程序的输出多了如下 HTTP 头:
+
+![](https://blog.cloudflare.com/content/images/2016/08/2016-08-11_13-32-45.png)
+
+[本博客][5]是使用 [Ghost](https://ghost.org/)(LCTT 译注:一个博客发布平台)进行发布的,因此如果你的浏览器支持 HTTP/2,你已经在不知不觉中享受了服务端推送技术带来的好处了。接下来,我们将进行更详细的说明。
+
+netjet 使用了带有定制插件的 [PostHTML](https://github.com/posthtml/posthtml) 来解析 HTML。目前,netjet 用它来查找图片、脚本和外部 CSS 样式表。你也可以用其它的技术来实现这个。
+
+在响应过程中增加 HTML 解析器有个明显的缺点:这将增加页面加载的延时(到加载第一个字节所花的时间)。大多数情况下,所新增的延时被应用里的其他耗时掩盖掉了,比如数据库访问。为了解决这个问题,netjet 包含了一个可调节的 LRU 缓存,该缓存以 HTTP 的 ETag 头部作为索引,这使得 netjet 可以非常快的为已经解析过的页面插入 Link 头部。
+
+不过,如果我们现在从头设计一款全新的应用,我们就应该考虑把页面内容和页面中的元数据分开存放,从而整体地减少 HTML 解析和其它可能增加的延时了。
+
+任意的 Node.js HTML 框架,只要它支持类似 Express 这样的中间件,netjet 都是能够兼容的。只要把 netjet 像下面这样加到中间件加载链里就可以了。
+
+```javascript
+var express = require('express');
+var netjet = require('netjet');
+var root = '/path/to/static/folder';
+
+express()
+  .use(netjet({
+    cache: {
+      max: 100
+    }
+  }))
+  .use(express.static(root))
+  .listen(1337);
+```
+
+稍微加点代码,netjet 也可以摆脱 HTML 框架,独立工作:
+
+```javascript
+var http = require('http');
+var netjet = require('netjet');
+
+var port = 1337;
+var hostname = 'localhost';
+var preload = netjet({
+  cache: {
+    max: 100
+  }
+});
+
+var server = http.createServer(function (req, res) {
+  preload(req, res, function () {
+      res.statusCode = 200;
+      res.setHeader('Content-Type', 'text/html');
+      res.end('<!doctype html><h1>Hello World</h1>');
+  });
+});
+
+server.listen(port, hostname, function () {
+  console.log('Server running at http://' + hostname + ':' + port+ '/');
+});
+```
+
+[netjet 文档里][1]有更多选项的信息。
+
+### 查看推送了什么数据
+
+![](https://blog.cloudflare.com/content/images/2016/08/2016-08-02_10-49-33.png)
+
+访问[本文][5]时,通过 Chrome 的开发者工具,我们可以轻松的验证网站是否正在使用服务器推送技术(LCTT 译注: Chrome 版本至少为 53)。在“Network”选项卡中,我们可以看到有些资源的“Initiator”这一列中包含了`Push`字样,这些资源就是服务器端推送的。
+
+不过,目前 Firefox 的开发者工具还不能直观的展示被推送的资源。不过我们可以通过页面响应头部里的`cf-h2-pushed`头部看到一个列表,这个列表包含了本页面主动推送给浏览器的资源。
+
+希望大家能够踊跃为 netjet 添砖加瓦,我也乐于看到有人正在使用 netjet。
+
+### Ghost 和服务端推送技术
+
+Ghost 真是包罗万象。在 Ghost 团队的帮助下,我把 netjet 也集成到里面了,而且作为测试版内容可以在 Ghost 的 0.8.0 版本中用上它。
+
+如果你正在使用 Ghost,你可以通过修改 config.js、并在`production`配置块中增加 `preloadHeaders` 选项来启用服务端推送。
+
+```javascript
+production: {
+  url: 'https://my-ghost-blog.com',
+  preloadHeaders: 100,
+  // ...
+}
+```
+
+Ghost 已经为其用户整理了[一篇支持文档][2]。
+
+### 总结
+
+使用 netjet,你的 Node.js 应用也可以使用浏览器预加载技术。并且 [CloudFlare][5] 已经使用它在提供了 HTTP/2 服务端推送了。
+
+--------------------------------------------------------------------------------
+
+via: https://blog.cloudflare.com/accelerating-node-js-applications-with-http-2-server-push/
+
+作者:[Terin Stock][a]
+译者:[echoma](https://github.com/echoma)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://blog.cloudflare.com/author/terin-stock/
+[1]: https://www.npmjs.com/package/netjet
+[2]: http://support.ghost.org/preload-headers/
+[3]: https://www.cloudflare.com/http2/server-push/
+[4]: https://blog.cloudflare.com/using-http-2-server-push-with-php/
+[5]: https://blog.cloudflare.com/accelerating-node-js-applications-with-http-2-server-push/
\ No newline at end of file
diff --git a/published/20160817 Build an integration for GitHub.md b/published/20160817 Build an integration for GitHub.md
new file mode 100644
index 0000000000..8d2212e714
--- /dev/null
+++ b/published/20160817 Build an integration for GitHub.md	
@@ -0,0 +1,54 @@
+为 Github 创造集成件(Integration)
+=============
+
+![](https://cloud.githubusercontent.com/assets/4432363/17537572/934e2f9c-5e52-11e6-9c24-50c7be5b5ae3.png)
+
+现在你可以从我们的 [集成件目录][1]里面找到更多工具。这个目录目前有超过 15 个分类 — 从 [API 管理][2] 到 [应用监控][3], Github 的集成件可以支持您的开发周期的每一个阶段。
+
+我们邀请了具有不同层面的专长的开发人员,来创造有助于开发者更好的工作的集成件。如果你曾经为 Github 构建过一个很棒的集成件,我们希望来让更多人知道它! [Gitter][4]、[AppVeyor][5] 和 [ZenHub][6] 都做到了,你也可以!
+
+### 我们在寻找什么?
+
+良好的软件开发依赖于上乘的工具,开发人员如今有了更多的选择,无论是语言、框架、工作流程,还是包含了其他因素的环境。我们正在寻找能够创造更好的整体开发体验的开发工具。
+
+#### 进入集成件目录清单的指南:
+
+- 稳步增多( 你的 Github OAuth 接口当前可以支持超过 500 个用户 )
+- 查看我们的 [技术要求][7]
+- 遵从[服务条款][8] 和[隐私政策][9]
+- 专注于软件开发生命周期
+
+### 有帮助的资源
+
+如果想要被列在目录里,请按照[列出需求页][10]中概述的步骤。
+
+你也应该阅读我们的[营销指南][11]和[已有目录清单][12]来更好的了解如何把它们全都放在一起。请把你的列表的内容记录在一个[私密 gist][13] 中(markdown 格式),并且通过邮件联系我们 <partnerships@github.com>。 如果你有任何问题,不要犹疑,请联系 <partnerships@github.com>。
+
+--------------------------------------------------------------------------------
+
+via: https://github.com/blog/2226-build-an-integration-for-github
+
+作者:[chobberoni][a]
+译者:[Bestony](https://github.com/Bestony)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://github.com/chobberoni
+
+[1]: https://github.com/integrations
+[2]: https://github.com/integrations/feature/api-management
+[3]: https://github.com/integrations/feature/monitoring
+[4]: https://github.com/integrations/feature/monitoring
+[5]: https://github.com/integrations/appveyor
+[6]: https://github.com/integrations/zenhub
+[7]: https://developer.github.com/integrations-directory/getting-listed/#technical-requirements
+[8]: https://help.github.com/articles/github-terms-of-service/
+[9]: https://help.github.com/articles/github-privacy-policy/
+[10]: https://developer.github.com/integrations-directory/getting-listed/
+[11]: https://developer.github.com/integrations-directory/marketing-guidelines/
+[12]: https://github.com/integrations
+[13]: https://gist.github.com/
+
+
+
diff --git a/published/20160817 Turtl - Secure, Open Source Evernote Alternative.md b/published/20160817 Turtl - Secure, Open Source Evernote Alternative.md
new file mode 100644
index 0000000000..fec3a36be5
--- /dev/null
+++ b/published/20160817 Turtl - Secure, Open Source Evernote Alternative.md	
@@ -0,0 +1,79 @@
+Turtl:安全、开源的 Evernote 替代品
+=============================
+
+Turtl 是一个安全、开源的 Evernote 替代品,在Linux、Windows、Mac 和 Android 等系统上都能使用。iOS版本仍在开发当中,Firefox 和 Chrome 也有扩展程序可以使用。
+
+![](https://3.bp.blogspot.com/-cNoUUjaU4A0/V7MFKCasZJI/AAAAAAAAYTk/r7oWe-z_HB87hDvlKLViiiHUMfagnC6LQCLcB/s400/turtl-desktop-linux.png)
+
+这个产品仍在测试阶段,它能够让你把你的笔记(便签编辑器支持 Markdown)、网站书签、密码、文档、图片等单独放在一个隐秘地方。
+
+笔记可以按模块组织起来,支持嵌套,也可以和其他 Turtl 用户分享。
+
+![](https://2.bp.blogspot.com/-G-Ln3T1c2QA/V7MFmrqkukI/AAAAAAAAYTs/dXMPEB9MPREicixlEJlQVqg9SFjBX1pwgCLcB/s400/turtl-boards.png)
+
+你可以给你的笔记打上标签。Turtl 通过创建时间、最后修改时间或者标签来找你的笔记。
+
+这个是便签编辑器(文件便签):
+
+![](https://1.bp.blogspot.com/-8cNHV69iCWM/V7MFX7sBlMI/AAAAAAAAYTo/ZUVTYwiCSy8uzrVKdf6NcsQZlHtylIyvgCEw/s400/turtl-edit-note.png)
+
+那么安全性如何呢?Turtl 会在保存数据之前加密,使用的是一个加密密钥,而密码并不保存在服务器上。只有你和你想要分享的人能获取数据。你可以从[这里][1]获得更多关于 Turtl 安全和加密的信息。
+
+更新(感谢 Dimitry!):根据[错误反馈][2],Turtl 有个严重的安全性问题。Turtl 允许创建多个相同用户名的账号,却只使用密码来区分它们。希望能马上修复这个问题。
+
+Turtl 团队提供了一个托管服务来同步你的记录,它是完全免费的,”除非你的文件足够大,或者你需要更好的服务”,在我写这篇文章的时候这个高级服务还不能用。
+
+并且你也不一定要用这个托管服务,因为就像其桌面应用和手机应用一样,这个自托管服务器也是一个自由、开源的软件,所以你可以自己搭建一个 [Turtl 服务器][3]。
+
+Turtl 没有像 Evernote 那么多的功能,但它在它的[计划][4]中也有一些新的功能,比如:支持导入/导出文本和Evernote 格式的数据、原生支持 PDF 阅读器、界面锁定等。
+
+不得不提醒的是,每次启动都要输入密码,虽然安全,但有时候实在是麻烦。
+
+###下载 Turtl
+
+[下载 Turtl 应用][5](二进制文件支持 Linux (32位/64位)、Windows 64 位、Mac 64位、Android,以及 Chrome 和Firefox 浏览器插件)
+
+**更新**:Turtl 用了一个新的服务器,注销然后在登录框的下面选择高级设置,把 Turtl 服务器设置为 "https://api.turtlapp.com/v2"(没有引号)。
+
+下载源代码(桌面应用、移动应用和服务器)、反馈问题等,参见 Turtl 的 [GitHub][6] 项目站点。
+
+Arch Linux 用户可以通过 [AUR][7] 来安装 Turtl。
+
+要在 Linux 上安装,把安装包解压后运行 install.sh,安装之前请确保 ~/.local/share/applications 目录存在,若不存在请自行创建:
+
+```
+mkdir -p ~/.local/share/applications
+```
+
+注意:如果使用 sudo 命令安装,那么只有 root 用户才能使用。所以,要么不用 sudo 命令安装,要么在安装完成后修改权限。你可以参考[AUR 软件包的设置][8]来了解如何修改权限。
+
+使用如下命令把 Turtl 安装到 ~/turtl 文件夹下(假定你已经把安装包解压在你的家目录下了):
+
+````
+~/turtl-*/install.sh ~/turtl
+```
+
+可以使用 ~/.turtl 代替 ~/turtl 把 Turtl 安装到你的家目录的隐藏文件夹下。你也可以用些小技巧把它隐藏起来。
+
+如果 Turtl 没有在你的 Unity Dash 上显示出来,请注销/登录以重启会话。
+
+--------------------------------------------------------------------------------
+
+via: http://www.webupd8.org/2016/08/turtl-secure-open-source-evernote.html
+
+作者:[Andrew][a]
+译者:[chisper](https://github.com/chisper)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.webupd8.org/p/about.html
+[1]: https://turtl.it/docs/security/
+[2]: https://github.com/turtl/api/issues/20
+[3]: https://turtl.it/docs/server/
+[4]: https://trello.com/b/yIQGkHia/turtl-product-dev
+[5]: https://turtl.it/download/
+[6]: https://github.com/turtl
+[7]: https://aur.archlinux.org/packages/turtl/
+[8]: https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=turtl
+[9]: https://turtlapp.com/
diff --git a/published/20160823  What Datatype Should You Use to Represent Time in MySQL.md b/published/20160823  What Datatype Should You Use to Represent Time in MySQL.md
new file mode 100644
index 0000000000..3baf1eab27
--- /dev/null
+++ b/published/20160823  What Datatype Should You Use to Represent Time in MySQL.md	
@@ -0,0 +1,554 @@
+MySQL 中你应该使用什么数据类型表示时间?
+==========================================================
+
+![](http://www.vertabelo.com/_file/blog/what-datatype-should-you-use-to-represent-time-in-mysql-we-compare-datetime-timestamp-and-int/clock.jpg)
+
+_当你需要保存日期时间数据时,一个问题来了:你应该使用 MySQL 中的什么类型?使用 MySQL 原生的 DATE 类型还是使用 INT 字段把日期和时间保存为一个纯数字呢?_
+
+在这篇文章中,我将解释 MySQL 原生的方案,并给出一个最常用数据类型的对比表。我们也将对一些典型的查询做基准测试,然后得出在给定场景下应该使用什么数据类型的结论。
+
+## 原生的 MySQL Datetime 数据类型
+
+Datetime 数据表示一个时间点。这可以用作日志记录、物联网时间戳、日历事件数据,等等。MySQL 有两种原生的类型可以将这种信息保存在单个字段中:Datetime 和 Timestamp。MySQL 文档中是这么介绍这些数据类型的:
+
+> DATETIME 类型用于保存同时包含日期和时间两部分的值。MySQL 以 'YYYY-MM-DD HH:MM:SS' 形式接收和显示 DATETIME 类型的值。
+
+> TIMESTAMP 类型用于保存同时包含日期和时间两部分的值。
+
+> DATETIME 或 TIMESTAMP 类型的值可以在尾部包含一个毫秒部分,精确度最高到微秒(6 位数)。
+
+> TIMESTAMP 和 DATETIME 数据类型提供自动初始化和更新到当前的日期和时间的功能,只需在列的定义中设置 DEFAULT CURRENT_TIMESTAMP 和 ON UPDATE CURRENT_TIMESTAMP。
+
+作为一个例子:
+
+```SQL
+CREATE TABLE `datetime_example` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `measured_on` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `measured_on` (`measured_on`)
+) ENGINE=InnoDB;
+```
+
+```SQL
+CREATE TABLE `timestamp_example` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `measured_on` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `measured_on` (`measured_on`)
+) ENGINE=InnoDB;
+```
+
+除了原生的日期时间表示方法,还有另一种常用的存储日期和时间信息的方法。即使用 INT 字段保存 Unix 时间(从1970 年 1 月 1 日协调世界时(UTC)建立所经过的秒数)。
+
+MySQL 也提供了只保存时间信息中的一部分的方式,通过使用 Date、Year 或 Time 类型。由于这篇文章是关于保存准确时间点的最佳方式的,我们没有讨论这些不那么精确的局部类型。
+
+## 使用 INT 类型保存 Unix 时间
+
+使用一个简单的 INT 列保存 Unix 时间是最普通的方法。使用 INT,你可以确保你要保存的数字可以快速、可靠地插入到表中,就像这样:
+
+```SQL
+INSERT INTO `vertabelo`.`sampletable`
+(
+ `id`,
+ `measured_on` ### INT 类型的列
+)
+VALUES
+(
+ 1,
+ 946684801
+ ### 至 01/01/2000 @ 12:00am (UTC) 的 UNIX 时间戳 http://unixtimestamp.com
+);
+```
+
+这就是关于它的所有内容了。它仅仅是个简单的 INT 列,MySQL 的处理方式是这样的:在内部使用 4 个字节保存那些数据。所以如果你在这个列上使用 SELECT 你将会得到一个数字。如果你想把这个列用作日期进行比较,下面的查询并不能正确工作:
+
+```SQL
+SELECT
+    id, measured_on, FROM_UNIXTIME(measured_on)
+FROM
+    vertabelo.inttimestampmeasures
+WHERE
+    measured_on > '2016-01-01' ### measured_on 会被作为字符串比较以进行查询
+LIMIT 5;
+```
+
+这是因为 MySQL 把 INT 视为数字,而非日期。为了进行日期比较,你必须要么获取( LCTT 译注:从 1970-01-01 00:00:00)到 2016-01-01 经过的秒数,要么使用 MySQL 的 FROM\_UNIXTIME() 函数把 INT 列转为 Date 类型。下面的查询展示了 FROM\_UNIXTIME() 函数的用法:
+
+```SQL
+SELECT
+    id, measured_on, FROM_UNIXTIME(measured_on)
+FROM
+    vertabelo.inttimestampmeasures
+WHERE
+    FROM_UNIXTIME(measured_on) > '2016-01-01'
+LIMIT 5;
+```
+
+这会正确地获取到日期在 2016-01-01 之后的记录。你也可以直接比较数字和 2016-01-01 的 Unix 时间戳表示形式,即 1451606400。这样做意味着不用使用任何特殊的函数,因为你是在直接比较数字。查询如下:
+
+```SQL
+SELECT
+    id, measured_on, FROM_UNIXTIME(measured_on)
+FROM
+    vertabelo.inttimestampmeasures
+WHERE
+   measured_on > 1451606400
+LIMIT 5;
+```
+
+假如这种方式不够高效甚至提前做这种转换是不可行的话,那该怎么办?例如,你想获取 2016 年所有星期三的记录。要做到这样而不使用任何 MySQL 日期函数,你就不得不查出 2016 年每个星期三的开始和结束时间的 Unix 时间戳。然后你不得不写很大的查询,至少要在 WHERE 中包含 104 个比较。(2016 年有 52 个星期三,你不得不考虑一天的开始(0:00 am)和结束(11:59:59 pm)...)
+
+结果是你很可能最终会使用 FROM\_UNIXTIME() 转换函数。既然如此,为什么不试下真正的日期类型呢?
+
+## 使用 Datetime 和 Timestamp
+
+Datetime 和 Timestamp 几乎以同样的方式工作。两种都保存日期和时间信息,毫秒部分最高精确度都是 6 位数。同时,使用人类可读的日期形式如 "2016-01-01" (为了便于比较)都能工作。查询时两种类型都支持“宽松格式”。宽松的语法允许任何标点符号作为分隔符。例如,"YYYY-MM-DD HH:MM:SS" 和 "YY-MM-DD HH:MM:SS" 两种形式都可以。在宽松格式情况下以下任何一种形式都能工作:
+
+```
+2012-12-31 11:30:45
+2012^12^31 11+30+45
+2012/12/31 11*30*45
+2012@12@31 11^30^45
+```
+
+其它宽松格式也是允许的;你可以在 [MySQL 参考手册](https://dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html) 找到所有的格式。
+
+默认情况下,Datetime 和 Timestamp 两种类型查询结果都以标准输出格式显示 —— 年-月-日 时:分:秒 (如 2016-01-01 23:59:59)。如果使用了毫秒部分,它们应该以小数值出现在秒后面 (如 2016-01-01 23:59:59.5)。
+
+Timestamp 和 Datetime 的核心不同点主要在于 MySQL 在内部如何表示这些信息:两种都以二进制而非字符串形式存储,但在表示日期/时间部分时 Timestamp (4 字节) 比 Datetime (5 字节) 少使用 1 字节。当保存毫秒部分时两种都使用额外的空间 (1-3 字节)。如果你存储 150 万条记录,这种 1 字节的差异是微不足道的:
+
+> 150 万条记录 * 每条记录 1 字节 / (1048576 字节/MB) = __1.43 MB__
+
+Timestamp 节省的 1 字节是有代价的:你只能存储从 '1970-01-01 00:00:01.000000' 到 '2038-01-19 03:14:07.999999' 之间的时间。而 Datetime 允许你存储从 '1000-01-01 00:00:00.000000' 到 '9999-12-31 23:59:59.999999' 之间的任何时间。
+
+另一个重要的差别 —— 很多 MySQL 开发者没意识到的 —— 是 MySQL 使用__服务器的时区__转换 Timestamp 值到它的 UTC 等价值再保存。当获取值是它会再次进行时区转换,所以你得回了你“原始的”日期/时间值。有可能,下面这些情况会发生。
+
+理想情况下,如果你一直使用同一个时区,MySQL 会获取到和你存储的同样的值。以我的经验,如果你的数据库涉及时区变换,你可能会遇到问题。例如,服务器变化(比如,你把数据库从都柏林的一台服务器迁移到加利福尼亚的一台服务器上,或者你只是修改了一下服务器的时区)时可能会发生这种情况。不管哪种方式,如果你获取数据时的时区是不同的,数据就会受影响。
+
+Datetime 列不会被数据库改变。无论时区怎样配置,每次都会保存和获取到同样的值。就我而言,我认为这是一个更可靠的选择。
+
+> __MySQL 文档:__
+
+> MySQL 把 TIMESTAMP 值从当前的时区转换到 UTC 再存储,获取时再从 UTC 转回当前的时区。(其它类型如 DATETIME 不会这样,它们会“原样”保存。) 默认情况下,每个连接的当前时区都是服务器的时区。时区可以基于连接设置。只要时区设置保持一致,你就能得到和保存的相同的值。如果你保存了一个 TIMESTAMP 值,然后改变了时区再获取这个值,获取到的值和你存储的是不同的。这是因为在写入和查询的会话上没有使用同一个时区。当前时区可以通过系统变量 [time_zone](https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_time_zone) 的值得到。更多信息,请查看 [MySQL Server Time Zone Support](https://dev.mysql.com/doc/refman/5.7/en/time-zone-support.html)。
+
+## 对比总结
+
+在深入探讨使用各数据类型的性能差异之前,让我们先看一个总结表格以给你更多了解。每种类型的弱点以红色显示。
+
+特性 | Datetime | Timestamp | Int (保存 Unix 时间)
+:--|:--|:--|:--
+原生时间表示 | 是 | 是 | 否,所以大多数操作需要先使用转换函数,如 FROM_UNIXTIME()
+能保存毫秒 | 是,最高 6 位精度 | 是,最高 6 位精度 | **否**
+合法范围 | '1000-01-01 00:00:00.000000' 到 '9999-12-31 23:59:59.999999 | **'1970-01-01 00:00:01.000000' 到 '2038-01-19 03:14:07.999999'** | 若使用 unsigned, '1970-01-01 00:00:01.000000; 理论上最大到 '2106-2-07 06:28:15'
+自动初始化(MySQL 5.6.5+) | 是 | 是 | **否**
+宽松解释 ([MySQL docs](https://dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html)) | 是 | 是 | **否,必须使用正确的格式**
+值被转换到 UTC 存储 | 否 | **是** | 否
+可转换到其它类型 | 是,如果值在合法的 Timestamp 范围中 | 是,总是 | 是,如果值在合法的范围中并使用转换函数
+存储需求([MySQL 5.6.4+](https://dev.mysql.com/doc/refman/5.7/en/storage-requirements.html)) | **5 字节**(如果使用了毫秒部分,再加最多 3 字节) | 4 字节 (如果使用了毫秒部分,再加最多 3 字节) | 4 字节 (不允许毫秒部分)
+无需使用函数即可作为真实日期可读 | 是 | 是 | **否,你必须格式化输出**
+数据分区 | 是 | 是,使用 [UNIX_TIMESTAMP()](https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_unix-timestamp);在 MySQL 5.7 中其它表达式是不允许包含 [TIMESTAMP](https://dev.mysql.com/doc/refman/5.7/en/datetime.html) 值的。同时,注意[分区裁剪时的这些考虑](https://dev.mysql.com/doc/refman/5.7/en/partitioning-pruning.html) | 是,使用 INT 上的任何合法操作
+
+## 基准测试 INT、Timestamp 和 Datetime 的性能
+
+为了比较这些类型的性能,我会使用我创建的一个天气预报网络的 150 万记录(准确说是 1,497,421)。这个网络每分钟都收集数据。为了让这些测试可复现,我已经删除了一些私有列,所以你可以使用这些数据运行你自己的测试。
+
+基于我原始的表格,我创建了三个版本:
+
+- `datetimemeasures` 表在 `measured_on` 列使用 Datetime 类型,表示天气预报记录的测量时间
+- `timestampmeasures` 表在 `measured_on` 列使用 Timestamp 类型
+- `inttimestampmeasures` 表在 `measured_on` 列使用 INT (unsigned) 类型
+
+这三个表拥有完全相同的数据;唯一的差别就是 `measured_on` 字段的类型。所有表都在 `measured_on` 列上设置了一个索引。
+
+
+
+### 基准测试工具
+
+为了评估这些数据类型的性能,我使用了两种方法。一种基于 [Sysbench](https://github.com/akopytov/sysbench),它的官网是这么描述的:
+
+_“... 一个模块化、跨平台和多线程的基准测试工具,用以评估那些对运行高负载数据库的系统非常重要的系统参数。”_
+
+这个工具是 [MySQL 文档](https://dev.mysql.com/downloads/benchmarks.html)中推荐的。
+
+如果你使用 Windows (就像我),你可以下载一个包含可执行文件和我使用的测试查询的 zip 文件。它们基于 [一种推荐的基准测试方法](https://dba.stackexchange.com/questions/39221/stress-test-mysql-with-queries-captured-with-general-log-in-mysql)。
+
+为了执行一个给定的测试,你可以使用下面的命令(插入你自己的连接参数):
+
+```bash
+sysbench --MySQL-table-engine=innodb --MySQL-db=vertabelo --MySQL-user=root --MySQL-host=localhost --MySQL-password= --test=sysbench_test_file.lua --num-threads=8 --max-requests=100 run
+```
+
+这会正常工作,这里 `sysbench_test_file.lua` 是测试文件,并包含了各个测试中指向各个表的 SQL 查询。
+
+为了进一步验证结果,我也运行了 [mysqlslap](https://dev.mysql.com/doc/refman/5.7/en/mysqlslap.html)。它的官网是这么描述的:
+
+_“[mysqlslap](https://dev.mysql.com/doc/refman/5.7/en/mysqlslap.html) 是一个诊断程序,为模拟 MySQL 服务器的客户端负载并报告各个阶段的用时而设计。它工作起来就像是很多客户端在同时访问服务器。_
+
+记得这些测试中最重要的不是所需的_绝对_时间。而是在不同数据类型上执行相同查询时的_相对_时间。这两个基准测试工具的测试时间不一定相同,因为不同工具的工作方式不同。重要的是数据类型的比较,随着我们深入到测试中,这将会变得清楚。
+
+### 基准测试
+
+我将使用三种可以评估几个性能方面的查询:
+
+* 时间范围选择
+    * 在 Datetime 和 Timestamp 数据类型上这允许我们直接比较而不需要使用任何特殊的日期函数。
+    * 同时,我们可以评估在 INT 类型的列上使用日期函数相对于使用简单的数值比较的影响。为了做到这些我们需要把范围转换为 Unix 时间戳数值。
+* 日期函数选择
+    * 与前个测试中比较操作针对一个简单的 DATE 值相反,这个测试使得我们可以评估使用日期函数作为 “WHERE” 子句的一部分的性能。
+    * 我们还可以测试一个场景,即我们必须使用一个函数将 INT 列转换为一个合法的 DATE 类型然后执行查询。
+* count() 查询
+    * 作为对前面测试的补充,这将评估在三种不同的表示类型上进行典型的统计查询的性能。
+
+我们将在这些测试中覆盖一些常见的场景,并看到三种类型上的性能表现。
+
+### 关于 SQL\_NO\_CACHE
+
+当在查询中使用 SQL\_NO\_CACHE 时,服务器不使用查询缓存。它既不检查查询缓存以确认结果是不是已经在那儿了,也不会保存查询结果。因此,每个查询将反映真实的性能影响,就像每次查询都是第一次被调用。
+
+### 测试 1:选择一个日期范围中的值
+
+这个查询返回总计 1,497,421 行记录中的 75,706 行。
+
+#### 查询 1 和 Datetime:
+
+```SQL
+SELECT SQL_NO_CACHE
+    measured_on
+FROM
+    vertabelo.datetimemeasures m
+WHERE
+    m.measured_on > '2016-01-01 00:00:00.0'
+      AND m.measured_on < '2016-02-01 00:00:00.0';
+```
+
+#### 性能
+
+| 响应时间 (ms) | Sysbench | mysqlslap |
+|-------------|----------|-----------|
+| 最小 | 152 | 296 |
+| 最大 | 1261 | 3203 |
+| 平均 | 362 | 809 |
+
+```bash
+Sysbench cmd> sysbench --MySQL-table-engine=innodb --MySQL-db=vertabelo --MySQL-user=root --MySQL-host=localhost --MySQL-password= --test=datetime.lua --num-threads=8 --max-requests=100 run
+```
+
+```bash
+mysqlslap cmd> mysqlslap --query="SELECT SQL_NO_CACHE measured_on FROM vertabelo.datetimemeasures m WHERE m.measured_on > '2016-01-01 00:00:00.0' AND m.measured_on < '2016-02-01 00:00:00.0'" --host=localhost --user=root --concurrency=8 --iterations=100  --no-drop --create-schema=vertabelo
+```
+
+#### 查询 1 和 Timestamp:
+
+```SQL
+SELECT SQL_NO_CACHE
+    measured_on
+FROM
+    vertabelo.timestampmeasures m
+WHERE
+    m.measured_on > '2016-01-01 00:00:00.0'
+        AND m.measured_on < '2016-02-01 00:00:00.0';
+```
+
+#### 性能
+
+| 响应时间 (ms) | Sysbench | mysqlslap |
+|-------------|----------|-----------|
+| 最小 | 214 | 359 |
+| 最大 | 1389 | 3313 |
+| 平均 | 431 | 1004 |
+
+```bash
+Sysbench cmd> sysbench --MySQL-table-engine=innodb --MySQL-db=vertabelo --MySQL-user=root --MySQL-host=localhost --MySQL-password= --test=timestamp.lua --num-threads=8 --max-requests=100 run
+```
+
+```bash
+mysqlslap cmd> mysqlslap --query="SELECT SQL_NO_CACHE measured_on FROM vertabelo.timestampmeasures m WHERE m.measured_on > '2016-01-01 00:00:00.0' AND m.measured_on < '2016-02-01 00:00:00.0'" --host=localhost --user=root --concurrency=8 --iterations=100  --no-drop --create-schema=vertabelo
+```
+
+#### 查询 1 和 INT:
+
+```SQL
+SELECT SQL_NO_CACHE
+    measured_on
+FROM
+    vertabelo.inttimestampmeasures m
+WHERE
+    FROM_UNIXTIME(m.measured_on) > '2016-01-01 00:00:00.0'
+        AND FROM_UNIXTIME(m.measured_on) < '2016-02-01 00:00:00.0';
+```
+
+#### 性能
+
+| 响应时间 (ms) | Sysbench | mysqlslap |
+|-------------|----------|-----------|
+| 最小 | 2472 | 7968 |
+| 最大 | 6554 | 10312 |
+| 平均 | 4107 | 8527 |
+
+```bash
+Sysbench cmd> sysbench --MySQL-table-engine=innodb --MySQL-db=vertabelo --MySQL-user=root --MySQL-host=localhost --MySQL-password= --test=int.lua --num-threads=8 --max-requests=100 run
+```
+
+```bash
+mysqlslap cmd> mysqlslap --query="SELECT SQL_NO_CACHE measured_on FROM vertabelo.inttimestampmeasures m WHERE FROM_UNIXTIME(m.measured_on) > '2016-01-01 00:00:00.0' AND FROM_UNIXTIME(m.measured_on) < '2016-02-01 00:00:00.0'" --host=localhost --user=root --concurrency=8 --iterations=100  --no-drop --create-schema=vertabelo
+```
+
+#### 另一种 INT 上的查询 1:
+
+由于这是个相当直接的范围搜索,而且查询中的日期可以轻易地转为简单的数值比较,我将它包含在了这个测试中。结果证明这是最快的方法 (你大概已经预料到了),因为它仅仅是比较数字而没有使用任何日期转换函数:
+
+```SQL
+SELECT SQL_NO_CACHE
+    measured_on
+FROM
+    vertabelo.inttimestampmeasures m
+WHERE
+    m.measured_on > 1451617200
+        AND m.measured_on < 1454295600;
+```
+
+#### 性能
+
+| 响应时间 (ms) | Sysbench | mysqlslap |
+|-------------|----------|-----------|
+| 最小 | 88 | 171 |
+| 最大 | 275 | 2157 |
+| 平均 | 165 | 514 |
+
+```bash
+Sysbench cmd> sysbench --MySQL-table-engine=innodb --MySQL-db=vertabelo --MySQL-user=root --MySQL-host=localhost --MySQL-password= --test=basic_int.lua --num-threads=8 --max-requests=100 run
+```
+
+```bash
+mysqlslap cmd> mysqlslap --query="SELECT SQL_NO_CACHE measured_on FROM vertabelo.inttimestampmeasures m WHERE m.measured_on > 1451617200 AND m.measured_on < 1454295600" --host=localhost --user=root --concurrency=8 --iterations=100  --no-drop --create-schema=vertabelo
+```
+
+#### 测试 1 总结
+
+| 平均响应时间 (ms) | Sysbench | 相对于 Datetime 的速度 | mysqlslap | 相对于 Datetime 的速度 |
+|-----------------|----------|----------------------|-----------|----------------------|
+| Datetime | 362 | - | 809 | - |
+| Timestamp | 431 | 慢 19% | 1004 | 慢 24% |
+| INT | 4107 | 慢 1134% | 8527 | 慢 1054% |
+| 另一种 INT 查询 | 165 | 快 55% | 514 | 快 36% |
+
+两种基准测试工具都显示 Datetime 比 Timestamp 和 INT 更快。但 Datetime 没有我们在另一种 INT 查询中使用的简单数值比较快。
+
+### 测试 2:选择星期一产生的记录
+
+这个查询返回总计 1,497,421 行记录中的 221,850 行。
+
+#### 查询 2 和 Datetime:
+
+```SQL
+SELECT SQL_NO_CACHE measured_on
+FROM
+    vertabelo.datetimemeasures m
+WHERE
+    WEEKDAY(m.measured_on) = 0; # MONDAY
+```
+
+#### 性能
+
+| 响应时间 (ms) | Sysbench | mysqlslap |
+|-------------|----------|-----------|
+| 最小 | 1874 | 4343 |
+| 最大 | 6168 | 7797 |
+| 平均 | 3127 | 6103 |
+
+```bash
+Sysbench cmd> sysbench --MySQL-table-engine=innodb --MySQL-db=vertabelo --MySQL-user=root --MySQL-host=localhost --MySQL-password= --test=datetime_1.lua --num-threads=8 --max-requests=100 run
+```
+
+```bash
+mysqlslap cmd> mysqlslap --query="SELECT SQL_NO_CACHE measured_on FROM vertabelo.datetimemeasures m WHERE WEEKDAY(m.measured_on) = 0" --host=localhost --user=root --concurrency=8 --iterations=25 --no-drop --create-schema=vertabelo
+```
+
+#### 查询 2 和 Timestamp:
+
+```SQL
+SELECT SQL_NO_CACHE
+    measured_on
+FROM
+    vertabelo.timestampmeasures m
+WHERE
+    WEEKDAY(m.measured_on) = 0; # MONDAY
+```
+
+#### 性能
+
+| 响应时间 (ms) | Sysbench | mysqlslap |
+|-------------|----------|-----------|
+| 最小 | 2688 | 5953 |
+| 最大 | 6666 | 13531 |
+| 平均 | 3653 | 8412 |
+
+```bash
+Sysbench cmd> sysbench --MySQL-table-engine=innodb --MySQL-db=vertabelo --MySQL-user=root --MySQL-host=localhost --MySQL-password= --test=timestamp_1.lua --num-threads=8 --max-requests=100 run
+```
+
+```bash
+mysqlslap cmd> mysqlslap --query="SELECT SQL_NO_CACHE measured_on FROM vertabelo.timestampmeasures m WHERE WEEKDAY(m.measured_on) = 0" --host=localhost --user=root --concurrency=8 --iterations=25 --no-drop --create-schema=vertabelo
+```
+
+#### 查询 2 和 INT:
+
+```SQL
+SELECT SQL_NO_CACHE
+    measured_on
+FROM
+    vertabelo.inttimestampmeasures m
+WHERE
+    WEEKDAY(FROM_UNIXTIME(m.measured_on)) = 0; # MONDAY
+```
+
+#### 性能
+
+| 响应时间 (ms) | Sysbench | mysqlslap |
+|-------------|----------|-----------|
+| 最小 | 2051 | 5844 |
+| 最大 | 7007 | 10469 |
+| 平均 | 3486 | 8088 |
+
+
+```bash
+Sysbench cmd> sysbench --MySQL-table-engine=innodb --MySQL-db=vertabelo --MySQL-user=root --MySQL-host=localhost --MySQL-password= --test=int_1.lua --num-threads=8 --max-requests=100 run
+```
+
+```bash
+mysqlslap cmd> mysqlslap --query="SELECT SQL_NO_CACHE measured_on FROM vertabelo.inttimestampmeasures m WHERE WEEKDAY(FROM_UNIXTIME(m.measured_on)) = 0" --host=localhost --user=root --concurrency=8 --iterations=25  --no-drop --create-schema=vertabelo
+```
+
+#### 测试 2 总结
+
+| 平均响应时间 (ms) | Sysbench | 相对于 Datetime 的速度 | mysqlslap | 相对于 Datetime 的速度 |
+|-----------------|----------|----------------------|-----------|----------------------|
+| Datetime | 3127 | - | 6103 | - |
+| Timestamp | 3653 | 慢 17% | 8412 | 慢 38% |
+| INT | 3486 | 慢 11% | 8088 | 慢 32% |
+
+再次,在两个基准测试工具中 Datetime 比 Timestamp 和 INT 快。但在这个测试中,INT 查询 —— 即使它使用了一个函数以转换日期 —— 比 Timestamp 查询更快得到结果。
+
+### 测试 3:选择星期一产生的记录总数
+
+这个查询返回一行,包含产生于星期一的所有记录的总数(从总共 1,497,421 行可用记录中)。
+
+#### 查询 3 和 Datetime:
+
+```SQL
+SELECT SQL_NO_CACHE
+    COUNT(measured_on)
+FROM
+    vertabelo.datetimemeasures m
+WHERE
+    WEEKDAY(m.measured_on) = 0; # MONDAY
+```
+
+#### 性能
+
+| 响应时间 (ms) | Sysbench | mysqlslap |
+|-------------|----------|-----------|
+| 最小 | 1720 | 4063 |
+| 最大 | 4594 | 7812 |
+| 平均 | 2797 | 5540 |
+
+```bash
+Sysbench cmd> sysbench --MySQL-table-engine=innodb --MySQL-db=vertabelo --MySQL-user=root --MySQL-host=localhost --MySQL-password= --test=datetime_1_count.lua --num-threads=8 --max-requests=100 run
+```
+
+```bash
+mysqlslap cmd> mysqlslap --query="SELECT SQL_NO_CACHE COUNT(measured_on) FROM vertabelo.datetimemeasures m WHERE WEEKDAY(m.measured_on) = 0" --host=localhost --user=root --concurrency=8 --iterations=25 --no-drop --create-schema=vertabelo
+```
+
+#### 查询 3 和 Timestamp:
+
+```SQL
+SELECT SQL_NO_CACHE
+    COUNT(measured_on)
+FROM
+    vertabelo.timestampmeasures m
+WHERE
+    WEEKDAY(m.measured_on) = 0; # MONDAY
+```
+
+#### 性能
+
+| 响应时间 (ms) | Sysbench | mysqlslap |
+|-------------|----------|-----------|
+| 最小 | 1907 | 4578 |
+| 最大 | 5437 | 10235 |
+| 平均 | 3408 | 7102 |
+
+```bash
+Sysbench cmd> sysbench --MySQL-table-engine=innodb --MySQL-db=vertabelo --MySQL-user=root --MySQL-host=localhost --MySQL-password= --test=timestamp_1_count.lua --num-threads=8 --max-requests=100 run
+```
+
+```bash
+mysqlslap cmd> mysqlslap --query="SELECT SQL_NO_CACHE COUNT(measured_on) FROM vertabelo.timestampmeasures m WHERE WEEKDAY(m.measured_on) = 0" --host=localhost --user=root --concurrency=8 --iterations=25 --no-drop --create-schema=vertabelo
+```
+
+#### 查询 3 和 INT:
+
+```SQL
+SELECT SQL_NO_CACHE
+    COUNT(measured_on)
+FROM
+    vertabelo.inttimestampmeasures m
+WHERE
+    WEEKDAY(FROM_UNIXTIME(m.measured_on)) = 0; # MONDAY
+```
+
+#### 性能
+
+| 响应时间 (ms) | Sysbench | mysqlslap |
+|-------------|----------|-----------|
+| 最小 | 2108 | 5609 |
+| 最大 | 4764 | 9735 |
+| 平均 | 3307 | 7416 |
+
+```bash
+Sysbench cmd> sysbench --MySQL-table-engine=innodb --MySQL-db=vertabelo --MySQL-user=root --MySQL-host=localhost --MySQL-password= --test=int_1_count.lua --num-threads=8 --max-requests=100 run
+```
+
+```bash
+mysqlslap cmd> mysqlslap --query="SELECT SQL_NO_CACHE COUNT(measured_on) FROM vertabelo.inttimestampmeasures m WHERE WEEKDAY(FROM_UNIXTIME(m.measured_on)) = 0" --host=localhost --user=root --concurrency=8 --iterations=25  --no-drop --create-schema=vertabelo
+```
+
+#### 测试 3 总结
+
+| 平均响应时间 (ms) | Sysbench | 相对于 Datetime 的速度 | mysqlslap | 相对于 Datetime 的速度 |
+|-----------------|----------|----------------------|-----------|----------------------|
+| Datetime | 2797 | - | 5540 | - |
+| Timestamp | 3408 | 慢 22% | 7102 | 慢 28% |
+| INT | 3307 | 慢 18% | 7416 | 慢 33% |
+
+再一次,两个基准测试工具都显示 Datetime 比 Timestamp 和 INT 快。不能判断 INT 是否比 Timestamp 快,因为 mysqlslap 显示 INT 比 Timestamp 略快而 Sysbench 却相反。
+
+_注意:_ 所有测试都是在一台 Windows 10 机器上本地运行的,这台机器拥有一个双核 i7 CPU,16GB 内存,运行 MariaDB v10.1.9,使用 innoDB 引擎。
+
+## 结论
+
+基于这些数据,我确信 Datetime 是大多数场景下的最佳选择。原因是:
+
+* 更快(根据我们的三个基准测试)。
+* 无需任何转换即是人类可读的。
+* 不会因为时区变换产生问题。
+* 只比它的对手们多用 1 字节
+* 支持更大的日期范围(从 1000 年到 9999 年)
+
+如果你只是存储 Unix 时间戳(并且在它的合法日期范围内),而且你真的不打算在它上面使用任何基于日期的查询,我觉得使用 INT 是可以的。我们已经看到,它执行简单数值比较查询时非常快,因为只是在处理简单的数字。
+
+Timestamp 怎么样呢?如果 Datetime 相对于 Timestamp 的优势不适用于你特殊的场景,你最好使用时间戳。阅读这篇文章后,你对三种类型间的区别应该有了更好的理解,可以根据你的需要做出最佳的选择。
+
+--------------------------------------------------------------------------------
+
+via: http://www.vertabelo.com/blog/technical-articles/what-datatype-should-you-use-to-represent-time-in-mysql-we-compare-datetime-timestamp-and-int
+
+作者:[Francisco Claria][a]
+译者:[bianjp](https://github.com/bianjp)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.axones.com.ar/
diff --git a/published/20160823 Publish Your Project Documentation with GitHub Pages.md b/published/20160823 Publish Your Project Documentation with GitHub Pages.md
new file mode 100644
index 0000000000..762597195e
--- /dev/null
+++ b/published/20160823 Publish Your Project Documentation with GitHub Pages.md	
@@ -0,0 +1,49 @@
+使用 Github Pages 发布你的项目文档
+=====
+
+你可能比较熟悉[如何用 Github Pages 来分享你的工作][3],又或许你看过[一堂][4]教你建立你的第一个 Github Pages 网站的教程。近期 Github Pages 的改进使得[从不同的数据源来发布您的网站][5]更加的方便,其中的来源之一就是你的仓库的 /docs 目录。
+
+文档的质量是一个软件项目健康发展的标志。对于开源项目来说,维护一个可靠而不出错的知识库、详细说明所有的细节是至关重要的。精心策划的文档可以让增加项目的亲切感,提供一步步的指导并促进各种方式的合作可以推动开源软件开发的协作进程。
+
+在 Web 上托管你的文档是一个消耗时间的挑战,而且对于它的发布和维护也没有省事的办法,然而这是并非不可避免的。面对多种不同的发布工具,又是 FTP 服务器,又是数据库,文件以各种不同的方式存放在不同的位置下,而这些都需要你手动来调整。需要说明的是,传统的 Web 发布方式提供了无与伦比的灵活性和性能,但是在许多情况下,这是以牺牲简单易用为代价的。
+
+当作为文档使用时,麻烦更少的方式显然更容易去维护。
+
+[GitHub Pages][2] 可以以指定的方式为你的项目创建网站,这使得它天然地适合发布和维护文档。因为 Github Pages 支持 Jekyll,所以你可以使用纯文本或 Markdown 来书写你的文档,从而降低你维护的成本、减少维护时的障碍。Jekyll 还支持许多有用的工具比如变量、模板、以及自动代码高亮等等,它会给你更多的灵活性而不会增加复杂性,这些你在一些笨重的平台是见不到的。
+
+最重要的是,在 Github 上使用 GitHub Pages 意味着你的文档和代码可以使用诸如 Issues 和 Pull Requests 来确保它得到应有的高水平维护,而且因为 GitHub Pages 允许您发布代码库主分支上的 /docs 目录,这样您就可以在同一分支同时维护你的代码库及其文档。
+
+### 现在开始!
+
+发布你的第一个文档页面只需要短短几分钟。
+
+1. 在你的仓库的主分支里创建一个 /docs/index.md 文件。
+
+2. 把你的内容以 Jekyll 格式添加进去,并提交你的修改。
+
+	![](https://cloud.githubusercontent.com/assets/3477155/17778793/47c5a586-6533-11e6-982c-ebd41ec6968c.gif)
+
+3. 查看你的仓库的设置分支然后选择主分支 /docs 目录,将其设置为 GitHub Pages 的源 ,点击保存,你就搞定了。
+
+![](https://cloud.githubusercontent.com/assets/3477155/17778792/47c2ecc4-6533-11e6-828a-91980daa7297.gif)
+
+GitHub Pages 将会从你的 /docs 目录中读取内容,转换 index.md 为 HTML。然后把它发布到你的 GitHub Pages 的 URL 上。
+
+这样将会创建并输出一个最基础的 HTML ,而且你可以使用 Jekyll 的自定义模板、CSS 和其他特性。如果想要看所有的可能,你可以看看 [GitHub Pages Showcase][1]。
+
+--------------------------------------------------------------------------------
+
+via: https://github.com/blog/2233-publish-your-project-documentation-with-github-pages
+
+作者:[loranallensmith][a]
+译者:[Bestony](https://github.com/bestony)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://github.com/loranallensmith
+[1]: https://github.com/showcases/github-pages-examples
+[2]: https://pages.github.com/
+[3]: https://www.youtube.com/watch?v=2MsN8gpT6jY
+[4]: https://www.youtube.com/watch?v=RaKX4A5EiQo
+[5]: https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/
diff --git a/published/20160824 Baidu Takes FPGA Approach to Accelerating SQL at Scale.md b/published/20160824 Baidu Takes FPGA Approach to Accelerating SQL at Scale.md
new file mode 100644
index 0000000000..c53cf1f982
--- /dev/null
+++ b/published/20160824 Baidu Takes FPGA Approach to Accelerating SQL at Scale.md	
@@ -0,0 +1,48 @@
+百度运用 FPGA 方法大规模加速 SQL 查询
+===================================================================
+
+尽管我们对百度今年工作焦点的关注集中在这个中国搜索巨头在深度学习方面的举措上,许多其他的关键的,尽管不那么前沿的应用表现出了大数据带来的挑战。
+
+正如百度的欧阳剑在本周 Hot Chips 大会上谈论的,百度坐拥超过 1 EB 的数据,每天处理大约 100 PB 的数据,每天更新 100 亿的网页,每 24 小时更新处理超过 1 PB 的日志更新,这些数字和 Google 不分上下,正如人们所想象的。百度采用了类似 Google 的方法去大规模地解决潜在的瓶颈。
+
+正如刚刚我们谈到的,Google 寻找一切可能的方法去打败摩尔定律,百度也在进行相同的探索,而令人激动的、使人着迷的机器学习工作是迷人的,业务的核心关键任务的加速同样也是,因为必须如此。欧阳提到,公司基于自身的数据提供高端服务的需求和 CPU 可以承载的能力之间的差距将会逐渐增大。
+
+![](http://www.nextplatform.com/wp-content/uploads/2016/08/BaiduFPGA1.png)
+
+对于百度的百亿亿级问题,在所有数据的接受端是一系列用于数据分析的框架和平台,从该公司的海量知识图谱,多媒体工具,自然语言处理框架,推荐引擎,和点击流分析都是这样。简而言之,大数据的首要问题就是这样的:一系列各种应用和与之匹配的具有压倒性规模的数据。
+
+当谈到加速百度的大数据分析,所面临的几个挑战,欧阳谈到抽象化运算核心去寻找一个普适的方法是困难的。“大数据应用的多样性和变化的计算类型使得这成为一个挑战,把所有这些整合成为一个分布式系统是困难的,因为有多变的平台和编程模型(MapReduce,Spark,streaming,user defined,等等)。将来还会有更多的数据类型和存储格式。”
+
+尽管存在这些障碍,欧阳讲到他们团队找到了(它们之间的)共同线索。如他所指出的那样,那些把他们的许多数据密集型的任务相连系在一起的就是传统的 SQL。“我们的数据分析任务大约有  40% 是用 SQL 写的,而其他的用 SQL 重写也是可用做到的。” 更进一步,他讲道他们可以享受到现有的 SQL 系统的好处,并可以和已有的框架相匹配,比如 Hive,Spark SQL,和 Impala 。下一步要做的事情就是 SQL 查询加速,百度发现 FPGA 是最好的硬件。
+
+![](http://www.nextplatform.com/wp-content/uploads/2016/08/BaiduFPGA2.png)
+
+这些主板,被称为处理单元( 下图中的 PE ),当执行 SQL 时会自动地处理关键的 SQL 功能。这里所说的都是来自演讲,我们不承担责任。确切的说,这里提到的 FPGA 有点神秘,或许是故意如此。如果百度在基准测试中得到了如下图中的提升,那这可是一个有竞争力的信息。后面我们还会继续介绍这里所描述的东西。简单来说,FPGA 运行在数据库中,当其收到 SQL 查询的时候,该团队设计的软件就会与之紧密结合起来。
+
+![](http://www.nextplatform.com/wp-content/uploads/2016/08/BaiduFPGA3.png)
+
+欧阳提到了一件事,他们的加速器受限于 FPGA 的带宽,不然性能表现本可以更高,在下面的评价中,百度安装了 2 块12 核心,主频 2.0 GHz 的 intl E26230 CPU,运行在 128G 内存。SDA 具有 5 个处理单元,(上图中的 300MHz FPGA 主板)每个分别处理不同的核心功能(筛选(filter),排序(sort),聚合(aggregate),联合(join)和分组(group by)) 
+
+为了实现 SQL 查询加速,百度针对 TPC-DS 的基准测试进行了研究,并且创建了称做处理单元(PE)的特殊引擎,用于在基准测试中加速 5 个关键功能,这包括筛选(filter),排序(sort),聚合(aggregate),联合(join)和分组(group by),(我们并没有把这些单词都像 SQL 那样大写)。SDA 设备使用卸载模型,具有多个不同种类的处理单元的加速卡在 FPGA 中组成逻辑,SQL 功能的类型和每张卡的数量由特定的工作量决定。由于这些查询在百度的系统中执行,用来查询的数据被以列格式推送到加速卡中(这会使得查询非常快速),而且通过一个统一的 SDA API 和驱动程序,SQL 查询工作被分发到正确的处理单元而且 SQL 操作实现了加速。
+
+SDA 架构采用一种数据流模型,加速单元不支持的操作被退回到数据库系统然后在那里本地运行,比其他任何因素,百度开发的 SQL 加速卡的性能被 FPGA 卡的内存带宽所限制。加速卡跨整个集群机器工作,顺便提一下,但是数据和 SQL 操作如何分发到多个机器的准确原理没有被百度披露。
+
+![](http://www.nextplatform.com/wp-content/uploads/2016/08/BaiduFPGA4.png)
+
+![](http://www.nextplatform.com/wp-content/uploads/2016/08/BaiduFPGA5.png)
+
+我们受限与百度所愿意披露的细节,但是这些基准测试结果是十分令人鼓舞的,尤其是 Terasort 方面,我们将在 Hot Chips 大会之后跟随百度的脚步去看看我们是否能得到关于这是如何连接到一起的和如何解决内存带宽瓶颈的细节。
+
+--------------------------------------------------------------------------------
+
+via: http://www.nextplatform.com/2016/08/24/baidu-takes-fpga-approach-accelerating-big-sql/
+
+作者:[Nicole Hemsoth][a]
+译者:[LinuxBars](https://github.com/LinuxBars)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.nextplatform.com/author/nicole/
+[1]: http://www.nextplatform.com/?s=baidu+deep+learning
+[2]: http://www.hotchips.org/wp-content/uploads/hc_archives/hc26/HC26-12-day2-epub/HC26.12-5-FPGAs-epub/HC26.12.545-Soft-Def-Acc-Ouyang-baidu-v3--baidu-v4.pdf
diff --git a/published/20160824 JSON support is generally available in Azure SQL Database.md b/published/20160824 JSON support is generally available in Azure SQL Database.md
new file mode 100644
index 0000000000..42c38958fc
--- /dev/null
+++ b/published/20160824 JSON support is generally available in Azure SQL Database.md	
@@ -0,0 +1,53 @@
+Azure SQL 数据库已经支持 JSON
+===========
+
+我们很高兴地宣布你现在可以在 Azure SQL 中查询及存储关系型数据或者 JSON 了、Azure SQL 数据库提供了读取 JSON 文本数据的简单的内置函数,将 JSON 文本转化成表,以及将表的数据转化成 JSON。
+
+![](https://azurecomcdn.azureedge.net/mediahandler/acomblog/media/Default/blog/1cc536a5-a822-467b-a4a2-4557746f70cc.png)
+
+你可以使用 JSON 函数来从 JSON 文本中提取值([JSON_VALUE][4])、提取对象([JSON_QUERY][5]), 更新JSON 中的值([JSON_MODIFY][6]),并且验证 JSON 文本的正确性([ISJSON][7])。[OPENJSON][8] 函数让你可以将 JSON 文本转化成表结构。最后,JSON 功能函数可以让你很简单地从 SQL 查询中使用 [FOR JSON][9] 从句来获得 JSON 文本结果。
+
+### 你可以用 JSON 做什么?
+
+Azure SQL 数据库中的 JSON 可以让您构建并与现代 web、移动设备和 HTML5/单页应用、诸如 Azure DocumentDB 等包含 JSON 格式化数据的 NoSQL 存储等交换数据,分析来自不同系统和服务的日志和消息。现在你可以轻易地将 Azure SQL 数据库与任何使用使用 JSON 的服务集成。
+
+#### 轻易地开放数据给现代框架和服务
+
+你有没有在使用诸如 REST 或者 Azure App 使用 JSON 来交换数据的服务?你有使用诸如 AngularJS、ReactJS、D3 或者 JQuery 等使用 JSON 的组件或框架么?使用新的 JSON 功能函数,你可以轻易地格式化存储在 Azure SQL 数据库中的数据,并将它用在任何现代服务或者应用中。
+
+#### 轻松采集 JSON 数据
+
+你有在使用移动设备、传感器、如 Azure Stream Analytics 或者 Insight 这样产生 JSON 的服务、如 Azure DocumentDB 或者 MongoDB 这样存储 JSON 的系统么?你需要在 Azure SQL 数据中使用熟悉的 SQL 语句来查询并分析 JSON 数据么?现在你可以轻松采集 JSON 数据并存储到 Azure SQL 数据库中,并且可以使用任何 Azure SQL 数据库支持的语言或者工具来查询和分析加载的数据。
+
+#### 简化你的数据模型
+
+你需要同时存储及查询数据库中关系型及半结构化的数据么?你需简化像 NoSQL 平台下的数据模型么?现在你可以在一张表中同时存储结构化数据及非结构化数据了。在 Azure SQL 数据库中,你可以同时从关系型及 NoSQL 的世界中使用最好的方法来调整你的数据模型。Azure SQL 数据库让你可以使用 Transact-SQL 语言来查询关系及 JSON 数据。程序和工具将不会在从表中取出的值及 JSON 文本中提取的值看出差别。
+
+### 下一步
+
+要学习如何在你的应用中集成 JSON,查看我们的[开始学习][1]页面或者 [Channel 9的视频][2]。要了解不同的情景下如何集成 JSON,观看 Channel 9 的视频或者在这些 [JSON 分类文章][3]中查找你感兴趣的使用情景。
+
+我们将持续增加新的 JSON 特性并让 JSON 支持的更好。请敬请关注。
+
+
+--------------------------------------------------------------------------------
+
+via: https://azure.microsoft.com/en-us/blog/json-support-is-generally-available-in-azure-sql-database/
+
+作者:[Jovan Popovic][a]
+译者:[geekpi](https://github.com/geekpi)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://azure.microsoft.com/en-us/blog/author/jovanpop/
+[1]: https://azure.microsoft.com/en-us/documentation/articles/sql-database-json-features/
+[2]: https://channel9.msdn.com/Shows/Data-Exposed/SQL-Server-2016-and-JSON-Support
+[3]: http://blogs.msdn.com/b/sqlserverstorageengine/archive/tags/json/
+[4]: https://msdn.microsoft.com/en-us/library/dn921898.aspx
+[5]: https://msdn.microsoft.com/en-us/library/dn921884.aspx
+[6]: https://msdn.microsoft.com/en-us/library/dn921892.aspx
+[7]: https://msdn.microsoft.com/en-us/library/dn921896.aspx
+[8]: https://msdn.microsoft.com/en-us/library/dn921885.aspx
+[9]: https://msdn.microsoft.com/en-us/library/dn921882.aspx
+
diff --git a/published/20160830 The State Of JavaScript - JavaScript Flavors.md b/published/20160830 The State Of JavaScript - JavaScript Flavors.md
new file mode 100644
index 0000000000..2261426046
--- /dev/null
+++ b/published/20160830 The State Of JavaScript - JavaScript Flavors.md	
@@ -0,0 +1,99 @@
+JavaScript 现状:方言篇
+===========
+
+JavaScript 和其他编程语言有一个很大的不同,它不像单纯的一个语言,而像一个由众多方言组成大家族。
+
+从 2009 年 CoffeeScript 出现开始,近几年出现了大量基于 JavaScript 语言,或者叫方言,例如 ES6、TypeScript、Elm 等等。它们都有自己的优势,且都可以被完美编译成标准 JavaScript。
+
+所以,继上周的前端框架篇,今天带来 JavaScript 现状之方言篇,看一下大家对于 JavaScript 的方言是怎么选择的。
+
+> 声明:下面的部分结论来自部分数据,这是在我想要展示完整数据时找到的最好的办法,这便于我分享我的一些想法。
+
+> 注意:如果你还没有参与[这个调查][3],现在就来参加吧,可以花十分钟完成调查然后再回来看这篇文章。
+
+### 认知度
+
+首先,我想看一下参与问卷调查的人是否**知道**下面六种语言:
+
+- 经典的 JavaScript: 97%
+- ES6: 98%
+- CoffeeScript: 99%
+- TypeScript: 98%
+- Elm: 66%
+- ClojureScript: 77%
+
+你可能觉得 100% 的人都应该知道『经典的 JavaScript 』,我想是有人无法抵抗在一个 JavaScript 调查中投『我从来没有听说过 JavaScript 』这个选项的强大诱惑吧……
+
+几乎所有人都知道 ES6、CoffeeScript 和 TypeScript 这三种语言,比较令我惊讶的是 TypeScript 竟然会稍微落后于 ES6 和 CoffeeScript。
+
+另一方面,Elm 和 ClojureScript 得分就要低得多,当然这也有道理,因为它们跟自己的生态环境绑定的比较紧密,也很难在已有的 App 中进行使用。
+
+### 兴趣度
+
+接下来,让我们一起看一下,哪一种方言吸引新开发者的能力更强一些:
+
+![](https://d3ugvbs94d921r.cloudfront.net/57c4dc599973d2525fee820a.png?t=3efc9491eba2ce2)
+
+要注意,该表是统计该语言对从未使用过它们的用户的吸引度,因为只有很少人没有用过经典 JavaScript,所以『经典 JavaScript 』这一列的数值很低。
+
+ES6的数值很有趣:已有很大比例的用户在使用 ES6 了,没有用过的人中的绝大部分(89%)也很想学习它。
+
+TypeScript 和 Elm 的状态差不多:用过的人不多,但感兴趣的比例表现不错,分别是 53% 和 58%。
+
+如果让我预测一下,那我觉得 TypeScript 和 Elm 都很难向普通的 JavaScript 开发者讲明自己的优势。毕竟如果开发者只懂 JavaScript 的话,你很难解释清楚静态类型的好处。
+
+另外,只有很少开发者用过 CoffeeScript,而且很明显几乎没人想去学。我觉得我该写一本 12 卷厚的 CoffeeScript 百科全书了……
+
+### 满意度
+
+现在是最重要的问题的时间了:有多少开发者用过这些语言,有多少人还想继续使用这些方言呢?
+
+![](https://d3ugvbs94d921r.cloudfront.net/57c4e5f79973d29461ee820a.png?t=1061d2ab8fc9838)
+
+虽然经典 JavaScript 拥有最多的用户量,但就满意度来说 ES6 才是大赢家,而且我想现在已经能安全的说,ES6 可以作为开发 JavaScript App 默认的语言。
+
+TypeScript 和 Elm 有相似的高满意度,都在 85% 上下。然后,只有可怜的 17% 的开发者会考虑继续使用 CoffeeScript。
+
+### 快乐度
+
+最后一个问题,我问大家在用现在的方式写 JavaScript 时是否感到快乐:
+
+![](https://d3ugvbs94d921r.cloudfront.net/57c4cd8c9973d2d95bee820a.png?t=f53efb029ea4456)
+
+这个问题的答案和上一个问题的满意度想匹配:平均分达到 3.96 分(1 - 5 分),大家在使用 JavaScript 时候确实是快乐的。
+
+不过很难说高分是因为 JavaScript 最近的一些改进造成的呢,还是发现 JavaScript 可能(仅仅是可能)没有大家认为的那么讨厌。总之,JavaScript 令人满意。
+
+### 总结
+
+如果说上次的赢家是 React 和 Vue,那此次调查的冠军毫无争议是 ES6 了。 ES6 并带来没有开天辟地的变化,但整个社区都还是很认可当前 JavaScript 演进方向的。
+
+我觉得一年之后我们再来一次这样的调查,结果会很有趣。同时也可以关注一下 TypeScript、Elm 还有ClojureScript 有没有继续进步。
+
+个人认为,当前 JavaScript 大家庭百花齐放的现象还只是一个开始,或许几年之后 JavaScript 就会变得非常不同了。
+
+### 结语 & 敬请期待
+
+对于我这样的调查来说数据越多就意味着数据越准确!越多人参加这个调查,那就越能代表整个 JavaScript 社区。
+
+所以,我十分希望你能帮忙分享这个调查问卷:
+
+- [在 Twitter 上][1]
+- [在 Facebook 上][2]
+
+另外,如果你想收到我下一个调查结果分析,前往 [调查问卷主页][3] 并留下自己的邮箱吧。
+
+--------------------------------------------------------------------------------
+
+via: https://medium.com/@sachagreif/the-state-of-javascript-javascript-flavors-1e02b0bfefb6
+
+作者:[Sacha Greif][a]
+译者:[eriwoon](https://github.com/eriwoon)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://medium.com/@sachagreif
+[1]: https://twitter.com/intent/tweet/?text=The%20State%20Of%20JavaScript%3A%20take%20a%20short%20survey%20about%20popular%20JavaScript%20technologies%20http%3A%2F%2Fstateofjs.com%20%23stateofjs
+[2]: https://facebook.com/sharer/sharer.php?u=http%3A%2F%2Fstateofjs.com
+[3]: http://stateofjs.com/
diff --git a/published/20160905 QOWNNOTES IS A NOTE TAKING AND TODO LIST APP THAT INTEGRATES WITH OWNCLOUD.md b/published/20160905 QOWNNOTES IS A NOTE TAKING AND TODO LIST APP THAT INTEGRATES WITH OWNCLOUD.md
new file mode 100755
index 0000000000..ccef2057ef
--- /dev/null
+++ b/published/20160905 QOWNNOTES IS A NOTE TAKING AND TODO LIST APP THAT INTEGRATES WITH OWNCLOUD.md	
@@ -0,0 +1,77 @@
+QOwnNotes:一款记录笔记和待办事项的应用,集成 ownCloud 云服务
+===============
+
+[QOwnNotes][1] 是一款自由而开源的笔记记录和待办事项的应用,可以运行在 Linux、Windows 和 mac 上。 
+
+这款程序将你的笔记保存为纯文本文件,它支持 Markdown 支持,并与 ownCloud 云服务紧密集成。
+
+![](https://2.bp.blogspot.com/-a2vsrOG0zFk/V81gyHWlaaI/AAAAAAAAYZs/uzY16JtNcT8bnje1rTKJx1135WueY6V9gCLcB/s400/qownnotes.png)
+
+QOwnNotes 的亮点就是它集成了 ownCloud 云服务(当然是可选的)。在 ownCloud 上用这款 APP,你就可以在网路上记录和搜索你的笔记,也可以在移动设备上使用(比如一款像 CloudNotes 的软件[2])。 
+
+不久以后,用你的 ownCloud 账户连接上 QOwnNotes,你就可以从你 ownCloud 服务器上分享笔记和查看或恢复之前版本记录的笔记(或者丢到垃圾箱的笔记)。
+
+同样,QOwnNotes 也可以与 ownCloud 任务或者 Tasks Plus 应用程序相集成。
+
+	如果你不熟悉 [ownCloud][3] 的话,这是一款替代 Dropbox、Google Drive 和其他类似商业性的网络服务的自由软件,它可以安装在你自己的服务器上。它有一个网络界面,提供了文件管理、日历、照片、音乐、文档浏览等等功能。开发者同样提供桌面同步客户端以及移动 APP。
+
+因为笔记被保存为纯文本,它们可以在不同的设备之间通过云存储服务进行同步,比如 Dropbox,Google Drive 等等,但是在这些应用中不能完全替代 ownCloud 的作用。 
+
+我提到的上述特点,比如恢复之前的笔记,只能在 ownCloud 下可用(尽管 Dropbox 和其他类似的也提供恢复以前的文件的服务,但是你不能在 QOwnnotes 中直接访问到)。
+
+鉴于 QOwnNotes 有这么多优点,它支持 Markdown 语言(内置了 Markdown 预览模式),可以标记笔记,对标记和笔记进行搜索,在笔记中加入超链接,也可以插入图片:
+
+![](https://4.bp.blogspot.com/-SuBhC43gzkY/V81oV7-zLBI/AAAAAAAAYZ8/l6nLQQSUv34Y7op_Xrma8XYm6EdWrhbIACLcB/s400/qownnotes_2.png)
+
+标记嵌套和笔记文件夹同样支持。
+
+代办事项管理功能比较基本还可以做一些改进,它现在打开在一个单独的窗口里,它也不用和笔记一样的编辑器,也不允许添加图片或者使用 Markdown 语言。 
+
+![](https://3.bp.blogspot.com/-AUeyZS3s_ck/V81opialKtI/AAAAAAAAYaA/xukIiZZUdNYBVZ92xgKEsEFew7q961CDwCLcB/s400/qownnotes-tasks.png)
+
+它可以让你搜索你代办事项,设置事项优先级,添加提醒和显示完成的事项。此外,待办事项可以加入笔记中。
+
+这款软件的界面是可定制的,允许你放大或缩小字体,切换窗格等等,也支持无干扰模式。
+
+![](https://4.bp.blogspot.com/-Pnzw1wZde50/V81rrE6mTWI/AAAAAAAAYaM/0UZnH9ktbAgClkuAk1g6fgXK87kB_Bh0wCLcB/s400/qownnotes-distraction-free.png)
+
+从程序的设置里,你可以开启黑夜模式(这里有个 bug,在 Ubuntu 16.04 里有些工具条图标消失了),改变状态条大小,字体和颜色方案(白天和黑夜):
+
+![](https://1.bp.blogspot.com/-K1MGlXA8sxs/V81rv3fwL6I/AAAAAAAAYaQ/YDhhhnbJ9gY38B6Vz1Na_pHLCjLHhPWiwCLcB/s400/qownnotes-settings.png)
+
+其他的特点有支持加密(笔记只能在 QOwnNotes 中加密),自定义键盘快捷键,输出笔记为 pdf 或者 Markdown,自定义笔记自动保存间隔等等。
+
+访问 [QOwnNotes][11] 主页查看完整的特性。
+
+### 下载 QOwnNotes
+
+如何安装,请查看安装页(支持 Debian、Ubuntu、Linux Mint、openSUSE、Fedora、Arch Linux、KaOS、Gentoo、Slackware、CentOS 以及 Mac OSX 和 Windows)。
+
+QOwnNotes 的 [snap][5] 包也是可用的,在 Ubuntu 16.04 或更新版本中,你可以通过 Ubuntu 的软件管理器直接安装它。
+
+为了集成 QOwnNotes 到 ownCloud,你需要有 [ownCloud 服务器][6],同样也需要 [Notes][7]、[QOwnNotesAPI][8]、[Tasks][9]、[Tasks Plus][10] 等 ownColud 应用。这些可以从 ownCloud 的 Web 界面上安装,不需要手动下载。
+
+请注意 QOenNotesAPI 和 Notes ownCloud 应用是实验性的,你需要“启用实验程序”来发现并安装他们,可以从 ownCloud 的 Web 界面上进行设置,在 Apps 菜单下,在左下角点击设置按钮。
+
+--------------------------------------------------------------------------------
+
+via: http://www.webupd8.org/2016/09/qownnotes-is-note-taking-and-todo-list.html
+
+作者:[Andrew][a]
+译者:[jiajia9linuxer](https://github.com/jiajia9linuxer)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.webupd8.org/p/about.html
+[1]: http://www.qownnotes.org/
+[2]: http://peterandlinda.com/cloudnotes/
+[3]: https://owncloud.org/
+[4]: http://www.qownnotes.org/installation
+[5]: https://uappexplorer.com/app/qownnotes.pbek
+[6]: https://download.owncloud.org/download/repositories/stable/owncloud/
+[7]: https://github.com/owncloud/notes
+[8]: https://github.com/pbek/qownnotesapi
+[9]: https://apps.owncloud.com/content/show.php/Tasks?content=164356
+[10]: https://apps.owncloud.com/content/show.php/Tasks+Plus?content=170561
+[11]: http://www.qownnotes.org/
diff --git a/translated/tech/LFCS/Part 11 - How to Manage and Create LVM Using vgcreate, lvcreate and lvextend Commands.md b/published/LFCS/Part 11 - How to Manage and Create LVM Using vgcreate, lvcreate and lvextend Commands.md
similarity index 88%
rename from translated/tech/LFCS/Part 11 - How to Manage and Create LVM Using vgcreate, lvcreate and lvextend Commands.md
rename to published/LFCS/Part 11 - How to Manage and Create LVM Using vgcreate, lvcreate and lvextend Commands.md
index 84b9db7968..49ac0e34f4 100644
--- a/translated/tech/LFCS/Part 11 - How to Manage and Create LVM Using vgcreate, lvcreate and lvextend Commands.md	
+++ b/published/LFCS/Part 11 - How to Manage and Create LVM Using vgcreate, lvcreate and lvextend Commands.md	
@@ -1,14 +1,15 @@
 LFCS 系列第十一讲:如何使用命令 vgcreate、lvcreate 和 lvextend 管理和创建 LVM
-============================================================================================
+========================================================================================
 
 由于 LFCS 考试中的一些改变已在 2016 年 2 月 2 日生效,我们添加了一些必要的专题到 [LFCS 系列][1]。我们也非常推荐备考的同学,同时阅读 [LFCE 系列][2]。
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/03/Manage-LVM-and-Create-LVM-Partition-in-Linux.png)
->LFCS:管理 LVM 和创建 LVM 分区
 
-在安装 Linux 系统的时候要做的最重要的决定之一便是给系统文件,home 目录等分配空间。在这个地方犯了错,再要增长空间不足的分区,那样既麻烦又有风险。
+*LFCS:管理 LVM 和创建 LVM 分区*
 
-**逻辑卷管理** (即 **LVM**)相较于传统的分区管理有许多优点,已经成为大多数(如果不能说全部的话) Linux 发行版安装时的默认选择。LVM 最大的优点应该是能方便的按照你的意愿调整(减小或增大)逻辑分区的大小。
+在安装 Linux 系统的时候要做的最重要的决定之一便是给系统文件、home 目录等分配空间。在这个地方犯了错,再要扩大空间不足的分区,那样既麻烦又有风险。
+
+**逻辑卷管理** (**LVM**)相较于传统的分区管理有许多优点,已经成为大多数(如果不能说全部的话) Linux 发行版安装时的默认选择。LVM 最大的优点应该是能方便的按照你的意愿调整(减小或增大)逻辑分区的大小。
 
 LVM 的组成结构:
 
@@ -16,7 +17,7 @@ LVM 的组成结构:
 * 一个用一个或多个物理卷创建出的卷组(**VG**)。可以把一个卷组想象成一个单独的存储单元。
 * 在一个卷组上可以创建多个逻辑卷。每个逻辑卷相当于一个传统意义上的分区 —— 优点是它的大小可以根据需求重新调整大小,正如之前提到的那样。
 
-本文,我们将使用三块 **8 GB** 的磁盘(**/dev/sdb**、**/dev/sdc** 和 **/dev/sdd**)分别创建三个物理卷。你既可以直接在设备上创建 PV,也可以先分区在创建。
+本文,我们将使用三块 **8 GB** 的磁盘(**/dev/sdb**、**/dev/sdc** 和 **/dev/sdd**)分别创建三个物理卷。你既可以直接在整个设备上创建 PV,也可以先分区在创建。
 
 在这里我们选择第一种方式,如果你决定使用第二种(可以参考本系列[第四讲:创建分区和文件系统][3])确保每个分区的类型都是 `8e`。
 
@@ -59,7 +60,8 @@ LVM 的组成结构:
 由于 `vg00` 是由两个 **8 GB** 的磁盘组成的,所以它将会显示成一个 **16 GB** 的硬盘:
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/03/List-LVM-Volume-Groups.png)
->LVM 卷组列表
+
+*LVM 卷组列表*
 
 当谈到创建逻辑卷,空间的分配必须考虑到当下和以后的需求。根据每个逻辑卷的用途来命名是一个好的做法。
 
@@ -78,7 +80,7 @@ LVM 的组成结构:
 # lvs
 ```
 
-或是详细信息,通过:
+或是查看详细信息,通过:
 
 ```
 # lvdisplay
@@ -91,9 +93,10 @@ LVM 的组成结构:
 ```
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/03/List-Logical-Volume.png)
->逻辑卷列表
 
-如上图,我们看到 LV 已经被创建成存储设备了(参考  LV Path line)。在使用每个逻辑卷之前,需要先在上面创建文件系统。
+*逻辑卷列表*
+
+如上图,我们看到 LV 已经被创建成存储设备了(参考  LV Path 那一行)。在使用每个逻辑卷之前,需要先在上面创建文件系统。
 
 这里我们拿 ext4 来做举例,因为对于每个 LV 的大小, ext4 既可以增大又可以减小(相对的 xfs 就只允许增大):
 
@@ -116,7 +119,8 @@ LVM 的组成结构:
 ```
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/03/Resize-Reduce-Logical-Volume-and-Volume-Group.png)
->减小逻辑卷和卷组
+
+*减小逻辑卷和卷组*
 
 在调整逻辑卷的时候,其中包含的减号 `(-)` 或加号 `(+)` 是十分重要的。否则 LV 将会被设置成指定的大小,而非调整指定大小。
 
@@ -135,7 +139,8 @@ LVM 的组成结构:
 ```
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/03/List-Volume-Group-Size.png)
->查看卷组磁盘大小
+
+*查看卷组磁盘大小*
 
 现在,你可以使用新加的空间,按照你的需求调整现有 LV 的大小,或者创建一个新的 LV。
 
@@ -151,7 +156,8 @@ LVM 的组成结构:
 ```
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/03/Find-Logical-Volume-UUID.png)
->寻找逻辑卷的 UUID
+
+*寻找逻辑卷的 UUID*
 
 为每个 LV 创建挂载点:
 
@@ -175,7 +181,8 @@ UUID=e1929239-5087-44b1-9396-53e09db6eb9e /home/backups ext4    defaults 0 0
 ```
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/03/Mount-Logical-Volumes-on-Linux-1.png)
->挂载逻辑卷
+
+*挂载逻辑卷*
 
 在涉及到 LV 的实际使用时,你还需要按照曾在本系列[第八讲:管理用户和用户组][4]中讲解的那样,为其设置合适的 `ugo+rwx`。
 
@@ -193,7 +200,7 @@ via: http://www.tecmint.com/manage-and-create-lvm-parition-using-vgcreate-lvcrea
 
 作者:[Gabriel Cánepa][a]
 译者:[martin2011qi](https://github.com/martin2011qi)
-校对:[校对者ID](https://github.com/校对者ID)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
 
@@ -202,5 +209,5 @@ via: http://www.tecmint.com/manage-and-create-lvm-parition-using-vgcreate-lvcrea
 [2]: http://www.tecmint.com/installing-network-services-and-configuring-services-at-system-boot/
 [3]: https://linux.cn/article-7187-1.html
 [4]: https://linux.cn/article-7418-1.html
-[5]: http://www.tecmint.com/create-lvm-storage-in-linux/
+[5]: https://linux.cn/article-3965-1.html
 [6]: https://linux.cn/article-7229-1.html
diff --git a/published/LFCS/Part 12 - How to Explore Linux with Installed Help Documentations and Tools.md b/published/LFCS/Part 12 - How to Explore Linux with Installed Help Documentations and Tools.md
new file mode 100644
index 0000000000..bc5e764637
--- /dev/null
+++ b/published/LFCS/Part 12 - How to Explore Linux with Installed Help Documentations and Tools.md	
@@ -0,0 +1,184 @@
+LFCS 系列第十二讲:如何使用 Linux 的帮助文档和工具
+==================================================================================
+
+由于 2016 年 2 月 2 号开始启用了新的 LFCS 考试要求,  我们在 [LFCS 系列][1]系列添加了一些必要的内容。为了考试的需要,我们强烈建议你看一下[LFCE 系列][2]。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/03/Explore-Linux-with-Documentation-and-Tools.png)
+
+*LFCS: 了解 Linux 的帮助文档和工具*
+
+当你习惯了在命令行下进行工作,你会发现 Linux 已经有了许多使用和配置 Linux 系统所需要的文档。
+
+另一个你必须熟悉命令行帮助工具的理由是,在[LFCS][3] 和 [LFCE][4] 考试中,它们是你唯一能够使用的信息来源,没有互联网也没有百度。你只能依靠你自己和命令行。
+
+基于上面的理由,在这一章里我们将给你一些建议来可以让你有效的使用这些安装的文档和工具,以帮助你通过**Linux 基金会认证**考试。
+
+### Linux 帮助手册(man)
+
+man 手册是  manual 手册的缩写,就是其名字所揭示的那样:一个给定工具的帮助手册。它包含了命令所支持的选项列表(以及解释),有些工具甚至还提供一些使用范例。
+
+我们用 **man 命令** 跟上你想要了解的工具名称来打开一个帮助手册。例如:
+
+```
+# man diff
+```
+
+这将打开`diff`的手册页,这个工具将逐行对比文本文件(如你想退出只需要轻轻的点一下 q 键)。
+
+下面我来比较两个文本文件 `file1` 和 `file2`。这两个文本文件包含了使用同一个 Linux 发行版相同版本安装的两台机器上的的安装包列表。
+
+输入`diff` 命令它将告诉我们 `file1` 和`file2` 有什么不同:
+
+```
+# diff file1 file2
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/03/Compare-Two-Text-Files-in-Linux.png)
+
+*在Linux中比较两个文本文件*
+
+`<` 这个符号是说`file2`缺失的行。如果是 `file1`缺失,我们将用 `>` 符号来替代指示。
+
+另外,**7d6** 意思是说`file1`的第**7**行要删除了才能和`file2`一致(**24d22** 和 **41d38** 也是同样的意思) **65,67d61** 告诉需要删除从第 **65** 行到 **67** 行。我们完成了以上步骤,那么这两个文件将完全一致。
+
+此外,根据 man 手册说明,你还可以通过 `-y` 选项来以两路的方式显示文件。你可以发现这对于你找到两个文件间的不同根据方便容易。
+
+```
+# diff -y file1 file2
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/03/Compare-and-List-Difference-of-Two-Files.png)
+
+*比较并列出两个文件的不同*
+
+此外,你也可以用`diff`来比较两个二进制文件。如果它们完全一样,`diff` 将什么也不会输出。否则,它将会返回如下信息:“**Binary files X and Y differ**”。
+
+### –help 选项
+
+`--help`选项,大多数命令都支持它(并不是所有), 它可以理解为一个命令的简短帮助手册。尽管它没有提供工具的详细介绍,但是确实是一个能够快速列出程序的所支持的选项的不错的方法。
+
+例如,
+
+```
+# sed --help
+```
+
+将显示 sed (流编辑器)的每个支持的选项。
+
+`sed`命令的一个典型用法是替换文件中的字符。用 `-i` 选项(意思是 “**原地编辑编辑文件**”),你可以编辑一个文件而且并不需要打开它。 如果你想要同时备份一个原始文件,用 `-i` 选项加后缀来创建一个原始文件的副本。
+
+例如,替换 `lorem.txt` 中的`Lorem` 为 `Tecmint`(忽略大小写),并且创建一个原文件的备份副本,命令如下:
+
+```
+# less lorem.txt | grep -i lorem
+# sed -i.orig 's/Lorem/Tecmint/gI' lorem.txt
+# less lorem.txt | grep -i lorem
+# less lorem.txt.orig | grep -i lorem
+```
+
+请注意`lorem.txt`文件中`Lorem` 都已经替换为 `Tecmint`,并且原文件 `lorem.txt` 被保存为`lorem.txt.orig`。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/03/Replace-A-String-in-File.png)
+
+*替换文件中的文本*
+
+### /usr/share/doc 内的文档
+
+这可能是我最喜欢的方法。如果你进入 `/usr/share/doc` 目录,并列出该目录,你可以看到许多以安装在你的 Linux 上的工具为名称的文件夹。
+
+根据 [文件系统层级标准][5],这些文件夹包含了许多帮助手册没有的信息,还有一些可以使配置更方便的模板和配置文件。
+
+例如,让我们来看一下 `squid-3.3.8` (不同发行版的版本可能会不同),这还是一个非常受欢迎的 HTTP 代理和 [squid 缓存服务器][6]。
+
+让我们用`cd`命令进入目录:
+
+```
+# cd /usr/share/doc/squid-3.3.8
+```
+
+列出当前文件夹列表:
+
+```
+# ls
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/03/List-Files-in-Linux.png)
+
+*使用 ls 列出目录*
+
+你应该特别注意 `QUICKSTART` 和 `squid.conf.documented`。这些文件分别包含了 Squid 详细文档及其经过详细备注的配置文件。对于别的安装包来说,具体的名字可能不同(有可能是 **QuickRef** 或者**00QUICKSTART**),但意思是一样的。
+
+对于另外一些安装包,比如 Apache web 服务器,在`/usr/share/doc`目录提供了配置模板,当你配置独立服务器或者虚拟主机的时候会非常有用。
+
+### GNU 信息文档
+
+你可以把它看做帮助手册的“开挂版”。它不仅仅提供工具的帮助信息,而且还是超级链接的形式(没错,在命令行中的超级链接),你可以通过箭头按钮从一个章节导航到另外章节,并按下回车按钮来确认。
+
+一个典型的例子是:
+
+```
+# info coreutils
+```
+
+因为 coreutils 包含了每个系统中都有的基本文件、shell 和文本处理工具,你自然可以从 coreutils 的 info 文档中得到它们的详细介绍。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/03/Info-Coreutils.png)
+
+*Info Coreutils*
+
+和帮助手册一样,你可以按 q 键退出。
+
+此外,GNU info 还可以显示标准的帮助手册。 例如:
+
+```
+# info tune2fs
+```
+
+它将显示 **tune2fs**的帮助手册, 这是一个 ext2/3/4 文件系统管理工具。
+
+我们现在看到了,让我们来试试怎么用**tune2fs**:
+
+显示 **/dev/mapper/vg00-vol_backups** 文件系统信息:
+
+```
+# tune2fs -l /dev/mapper/vg00-vol_backups
+```
+
+修改文件系统标签(修改为 Backups):
+
+```
+# tune2fs -L Backups /dev/mapper/vg00-vol_backups
+```
+
+设置文件系统的自检间隔及挂载计数(用`-c` 选项设置挂载计数间隔, 用  `-i` 选项设置自检时间间隔,这里 **d 表示天,w 表示周,m 表示月**)。
+
+```
+# tune2fs -c 150 /dev/mapper/vg00-vol_backups # 每 150 次挂载检查一次
+# tune2fs -i 6w /dev/mapper/vg00-vol_backups # 每 6 周检查一次
+```
+
+以上这些内容也可以通过 `--help` 选项找到,或者查看帮助手册。
+
+### 摘要
+
+不管你选择哪种方法,知道并且会使用它们在考试中对你是非常有用的。你知道其它的一些方法吗? 欢迎给我们留言。
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/explore-linux-installed-help-documentation-and-tools/
+
+作者:[Gabriel Cánepa][a]
+译者:[kokialoves](https://github.com/kokialoves)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]:http://www.tecmint.com/author/gacanepa/
+[1]: https://linux.cn/article-7161-1.html
+[2]: http://www.tecmint.com/installing-network-services-and-configuring-services-at-system-boot/
+[3]: https://linux.cn/article-7161-1.html
+[4]: http://www.tecmint.com/installing-network-services-and-configuring-services-at-system-boot/
+[5]: https://linux.cn/article-6132-1.html
+[6]: http://www.tecmint.com/configure-squid-server-in-linux/
+[7]: http://www.tecmint.com/sed-command-to-create-edit-and-manipulate-files-in-linux/
+
diff --git a/published/LFCS/Part 13 - How to Configure and Troubleshoot Grand Unified Bootloader (GRUB).md b/published/LFCS/Part 13 - How to Configure and Troubleshoot Grand Unified Bootloader (GRUB).md
new file mode 100644
index 0000000000..f8e7fb1834
--- /dev/null
+++ b/published/LFCS/Part 13 - How to Configure and Troubleshoot Grand Unified Bootloader (GRUB).md	
@@ -0,0 +1,191 @@
+LFCS 系列第十三讲:如何配置并排除 GNU 引导加载程序(GRUB)故障
+=====================================================================================
+
+由于 LFCS 考试需求的变动已于 2016 年 2 月 2 日生效,因此我们向 [LFCS 系列][1] 添加了一些必要的话题。为了准备认证考试,我们也强烈推荐你去看看 [LFCE 系列][2]。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/03/Configure-Troubleshoot-Grub-Boot-Loader.png)
+
+*LFCS 系列第十三讲:配置并排除 Grub 引导加载程序故障。*
+
+本文将会向你介绍 GRUB 的知识,并会说明你为什么需要一个引导加载程序,以及它是如何给系统增加功能的。
+
+[Linux 引导过程][3] 是从你按下你的电脑电源键开始,直到你拥有一个全功能的系统为止,整个过程遵循着这样的主要步骤:
+
+* 1. 一个叫做 **POST**(**上电自检**)的过程会对你的电脑硬件组件做全面的检查。
+* 2. 当 **POST** 完成后,它会把控制权转交给引导加载程序,接下来引导加载程序会将 Linux 内核(以及 **initramfs**)加载到内存中并执行。
+* 3. 内核首先检查并访问硬件,然后运行初始化进程(主要以它的通用名 **init** 而为人熟知),接下来初始化进程会启动一些服务,最后完成系统启动过程。
+
+在该系列的第七讲(“[SysVinit、Upstart 和 Systemd][4]”)中,我们介绍了现代 Linux 发行版使用的一些服务管理系统和工具。在继续学习之前,你可能想要回顾一下那一讲的知识。
+
+### GRUB 引导装载程序介绍
+
+在现代系统中,你会发现有两种主要的 **GRUB** 版本(一种是有时被称为 **GRUB Legacy** 的 **v1** 版本,另一种则是 **v2** 版本),虽说多数最新版本的发行版系统都默认使用了 **v2** 版本。如今,只有 **红帽企业版 Linux 6** 及其衍生系统仍在使用 **v1** 版本。
+
+因此,在本指南中,我们将着重关注 **v2** 版本的功能。
+
+不管 **GRUB** 的版本是什么,一个引导加载程序都允许用户:
+
+1. 通过指定使用不同的内核来修改系统的行为;
+2. 从多个操作系统中选择一个启动;
+3. 添加或编辑配置区块来改变启动选项等。
+
+如今,**GNU** 项目负责维护 **GRUB**,并在它们的网站上提供了丰富的文档。当你在阅读这篇指南时,我们强烈建议你看下 [GNU 官方文档][6]。
+
+当系统引导时,你会在主控制台看到如下的 **GRUB** 画面。最开始,你可以根据提示在多个内核版本中选择一个内核(默认情况下,系统将会使用最新的内核启动),并且可以进入 **GRUB** 命令行模式(使用 `c` 键),或者编辑启动项(按下 `e` 键)。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/03/GRUB-Boot-Screen.png)
+
+*GRUB 启动画面*
+
+你会考虑使用一个旧版内核启动的原因之一是之前工作正常的某个硬件设备在一次升级后出现了“怪毛病(acting up)”(例如,你可以参考 AskUbuntu 论坛中的[这条链接][7])。
+
+在启动时会从 `/boot/grub/grub.cfg` 或 `/boot/grub2/grub.cfg` 文件中读取**GRUB v2** 的配置文件,而 **GRUB v1** 使用的配置文件则来自 `/boot/grub/grub.conf` 或 `/boot/grub/menu.lst`。这些文件**不应该**直接手动编辑,而应通过 `/etc/default/grub` 的内容和 `/etc/grub.d` 目录中的文件来更新。
+
+在 **CentOS 7** 上,当系统最初完成安装后,会生成如下的配置文件:
+
+```
+GRUB_TIMEOUT=5
+GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
+GRUB_DEFAULT=saved
+GRUB_DISABLE_SUBMENU=true
+GRUB_TERMINAL_OUTPUT="console"
+GRUB_CMDLINE_LINUX="vconsole.keymap=la-latin1 rd.lvm.lv=centos_centos7-2/swap crashkernel=auto  vconsole.font=latarcyrheb-sun16 rd.lvm.lv=centos_centos7-2/root rhgb quiet"
+GRUB_DISABLE_RECOVERY="true"
+```
+
+除了在线文档外,你也可以使用下面的命令查阅 GNU GRUB 手册:
+
+```
+# info grub
+```
+
+如果你对 `/etc/default/grub` 文件中的可用选项特别感兴趣的话,你可以直接查阅配置一节的帮助文档:
+
+```
+# info -f grub -n 'Simple configuration'
+```
+
+使用上述命令,你会发现 `GRUB_TIMEOUT` 用于设置启动画面出现和系统自动开始启动(除非被用户中断)之间的时间。当该变量值为 `-1` 时,除非用户主动做出选择,否则不会开始启动。
+
+当同一台机器上安装了多个操作系统或内核后,`GRUB_DEFAULT` 就需要用一个整数来指定 GRUB 启动画面默认选择启动的操作系统或内核条目。我们既可以通过上述启动画面查看启动条目列表,也可以使用下面的命令:
+
+### 在 CentOS 和 openSUSE 系统上
+
+```
+# awk -F\' '$1=="menuentry " {print $2}' /boot/grub2/grub.cfg
+```
+
+### 在 Ubuntu 系统上
+
+```
+# awk -F\' '$1=="menuentry " {print $2}' /boot/grub/grub.cfg
+```
+
+如下图所示的例子中,如果我们想要使用版本为 `3.10.0-123.el7.x86_64` 的内核(第四个条目),我们需要将 `GRUB_DEFAULT` 设置为 `3`(条目从零开始编号),如下所示:
+
+```
+GRUB_DEFAULT=3
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/03/Boot-System-with-Old-Kernel-Version.png)
+
+*使用旧版内核启动系统*
+
+最后一个需要特别关注的 GRUB 配置变量是 `GRUB_CMDLINE_LINUX`,它是用来给内核传递选项的。我们可以在 [内核变量文件][8] 和 [`man 7 bootparam`][9] 中找到能够通过 GRUB 传递给内核的选项的详细文档。
+
+我的 **CentOS 7** 服务器上当前的选项是:
+
+```
+GRUB_CMDLINE_LINUX="vconsole.keymap=la-latin1 rd.lvm.lv=centos_centos7-2/swap crashkernel=auto  vconsole.font=latarcyrheb-sun16 rd.lvm.lv=centos_centos7-2/root rhgb quiet"
+```
+
+为什么你希望修改默认的内核参数或者传递额外的选项呢?简单来说,在很多情况下,你需要告诉内核某些由内核自身无法判断的硬件参数,或者是覆盖一些内核检测的值。
+
+不久之前,就在我身上发生过这样的事情,当时我在自己已用了 10 年的老笔记本上尝试了衍生自 **Slackware** 的 **Vector Linux**。完成安装后,内核并没有检测出我的显卡的正确配置,所以我不得不通过 GRUB 传递修改过的内核选项来让它工作。
+
+另外一个例子是当你需要将系统切换到单用户模式以执行维护工作时。为此,你可以直接在 `GRUB_CMDLINE_LINUX` 变量中直接追加 `single` 并重启即可:
+
+```
+GRUB_CMDLINE_LINUX="vconsole.keymap=la-latin1 rd.lvm.lv=centos_centos7-2/swap crashkernel=auto  vconsole.font=latarcyrheb-sun16 rd.lvm.lv=centos_centos7-2/root rhgb quiet single"
+```
+
+编辑完 `/etc/default/grub` 之后,你需要运行 `update-grub` (在 Ubuntu 上)或者 `grub2-mkconfig -o /boot/grub2/grub.cfg` (在 **CentOS** 和 **openSUSE** 上)命令来更新 `grub.cfg` 文件(否则,改动会在系统启动时丢失)。
+
+这条命令会处理早先提到的那些启动配置文件来更新 `grub.cfg` 文件。这种方法可以确保改动持久化,而在启动时刻通过 GRUB 传递的选项仅在当前会话期间有效。
+
+### 修复 Linux GRUB 问题
+
+如果你安装了第二个操作系统,或者由于人为失误而导致你的 GRUB 配置文件损坏了,依然有一些方法可以让你恢复并能够再次启动系统。
+
+在启动画面中按下 `c` 键进入 GRUB 命令行模式(记住,你也可以按下 `e` 键编辑默认启动选项),并可以在 GRUB 提示中输入 `help` 命令获得可用命令:
+
+![](http://www.tecmint.com/wp-content/uploads/2016/03/Fix-Grub-Issues-in-Linux.png)
+
+*修复 Linux 的 Grub 配置问题*
+
+我们将会着重关注 **ls** 命令,它会列出已安装的设备和文件系统,并且我们将会看看它查找到的东西。在下面的图片中,我们可以看到有 4 块硬盘(`hd0` 到 `hd3`)。
+
+貌似只有 `hd0` 已经分区了(msdos1 和 msdos2 可以证明,这里的 1 和 2 是分区号,msdos 则是分区方案)。
+
+现在我们来看看能否在第一个分区 `hd0`(**msdos1**)上找到 GRUB。这种方法允许我们启动 Linux,并且使用高级工具修复配置文件,或者如果有必要的话,干脆重新安装 GRUB:
+
+```
+# ls (hd0,msdos1)/
+```
+
+从高亮区域可以发现,`grub2` 目录就在这个分区:
+
+![](http://www.tecmint.com/wp-content/uploads/2016/03/Find-Grub-Configuration.png)
+
+*查找 Grub 配置*
+ 
+一旦我们确信了 GRUB 位于 (**hd0, msdos1**),那就让我们告诉  GRUB 该去哪儿查找它的配置文件并指示它去尝试启动它的菜单:
+
+```
+set prefix=(hd0,msdos1)/grub2
+set root=(hd0,msdos1)
+insmod normal
+normal
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/03/Find-and-Launch-Grub-Menu.png)
+
+*查找并启动 Grub 菜单*
+
+然后,在 GRUB 菜单中,选择一个条目并按下回车键以使用它启动。一旦系统成功启动后,你就可以运行 `grub2-install /dev/sdX` 命令修复问题了(将 `sdX` 改成你想要安装 GRUB 的设备)。然后启动信息将会更新,并且所有相关文件都会得到恢复。
+
+```
+# grub2-install /dev/sdX
+```
+
+其它更加复杂的情景及其修复建议都记录在 [Ubuntu GRUB2 故障排除指南][10] 中。该指南中阐述的概念对于其它发行版也是有效的。
+
+### 总结
+
+本文向你介绍了 GRUB,并指导你可以在何处找到线上和线下的文档,同时说明了如何面对由于引导加载相关的问题而导致系统无法正常启动的情况。
+
+幸运的是,GRUB 是文档支持非常丰富的工具之一,你可以使用我们在文中分享的资源非常轻松地获取已安装的文档或在线文档。
+
+你有什么问题或建议吗?请不要犹豫,使用下面的评论框告诉我们吧。我们期待着来自你的回复!
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/configure-and-troubleshoot-grub-boot-loader-linux/
+
+作者:[Gabriel Cánepa][a]
+译者:[ChrisLeeGit](https://github.com/chrisleegit)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/gacanepa/
+[1]: http://www.tecmint.com/sed-command-to-create-edit-and-manipulate-files-in-linux/
+[2]: http://www.tecmint.com/installing-network-services-and-configuring-services-at-system-boot/
+[3]: http://www.tecmint.com/linux-boot-process/
+[4]: https://linux.cn/article-7365-1.html
+[5]: http://www.tecmint.com/best-linux-log-monitoring-and-management-tools/
+[6]: http://www.gnu.org/software/grub/manual/
+[7]: http://askubuntu.com/questions/82140/how-can-i-boot-with-an-older-kernel-version
+[8]: https://www.kernel.org/doc/Documentation/kernel-parameters.txt
+[9]: http://man7.org/linux/man-pages/man7/bootparam.7.html
+[10]: https://help.ubuntu.com/community/Grub2/Troubleshooting
diff --git a/translated/tech/LFCS/Part 14 - Monitor Linux Processes Resource Usage and Set Process Limits on a Per-User Basis.md b/published/LFCS/Part 14 - Monitor Linux Processes Resource Usage and Set Process Limits on a Per-User Basis.md
similarity index 78%
rename from translated/tech/LFCS/Part 14 - Monitor Linux Processes Resource Usage and Set Process Limits on a Per-User Basis.md
rename to published/LFCS/Part 14 - Monitor Linux Processes Resource Usage and Set Process Limits on a Per-User Basis.md
index c6db6be7cc..0ba5df1b34 100644
--- a/translated/tech/LFCS/Part 14 - Monitor Linux Processes Resource Usage and Set Process Limits on a Per-User Basis.md	
+++ b/published/LFCS/Part 14 - Monitor Linux Processes Resource Usage and Set Process Limits on a Per-User Basis.md	
@@ -1,10 +1,11 @@
-LFCS 系列第十四讲: Linux 进程资源使用监控和基于每个用户的进程限制设置
-=============================================================================================
+LFCS 系列第十四讲: Linux 进程资源用量监控和按用户设置进程限制
+============================================================
 
 由于 2016 年 2 月 2 号开始启用了新的 LFCS 考试要求,我们在已经发表的 [LFCS 系列][1] 基础上增加了一些必要的主题。为了准备考试,同时也建议你看看 [LFCE 系列][2] 文章。
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/03/Linux-Process-Monitoring-Set-Process-Limits-Per-User.png)
->第十四讲: 监控 Linux 进程并为每个用户设置进程限制
+
+*第十四讲: 监控 Linux 进程并为每个用户设置进程限制*
 
 每个 Linux 系统管理员都应该知道如何验证硬件、资源和主要进程的完整性和可用性。另外,基于每个用户设置资源限制也是其中一项必备技能。
 
@@ -26,13 +27,13 @@ LFCS 系列第十四讲: Linux 进程资源使用监控和基于每个用户
 
 安装完 **mpstat** 之后,就可以使用它生成处理器统计信息的报告。
 
-你可以使用下面的命令每隔 2 秒显示所有 CPU(用 `-P` ALL 表示) 的 CPU 利用率(`-u`),共显示 **3** 次。
+你可以使用下面的命令每隔 2 秒显示所有 CPU(用 `-P` ALL 表示)的 CPU 利用率(`-u`),共显示 **3** 次。
 
 ```
 # mpstat -P ALL -u 2 3
 ```
 
-### 事例输出
+**示例输出:**
 
 ```
 Linux 3.19.0-32-generic (tecmint.com) 	Wednesday 30 March 2016 	_x86_64_	(4 CPU)
@@ -72,7 +73,7 @@ Average:       3   12.25    0.00    1.16    0.00    0.00    0.00    0.00    0.00
 # mpstat -P 0 -u 2 3
 ```
 
-### 事例输出
+**示例输出:**
 
 ```
 Linux 3.19.0-32-generic (tecmint.com) 	Wednesday 30 March 2016 	_x86_64_	(4 CPU)
@@ -86,16 +87,16 @@ Average:       0    5.58    0.00    0.34    0.85    0.00    0.00    0.00    0.00
 
 上面命令的输出包括这些列:
 
-* `CPU`: 整数表示的处理器号或者 all 表示所有处理器的平局值。
+* `CPU`: 整数表示的处理器号或者 all 表示所有处理器的平均值。
 * `%usr`: 运行在用户级别的应用的 CPU 利用率百分数。
 * `%nice`: 和 `%usr` 相同,但有 nice 优先级。
 * `%sys`: 执行内核应用的 CPU 利用率百分比。这不包括用于处理中断或者硬件请求的时间。
-* `%iowait`: 指定(或所有) CPU 的空闲时间百分比,这表示当前 CPU 处于 I/O 操作密集的状态。更详细的解释(附带示例)可以查看[这里][4]。
+* `%iowait`: 指定(或所有)CPU 的空闲时间百分比,这表示当前 CPU 处于 I/O 操作密集的状态。更详细的解释(附带示例)可以查看[这里][4]。
 * `%irq`: 用于处理硬件中断的时间所占百分比。
-* `%soft`: 和 `%irq` 相同,但是软中断。
-* `%steal`: 当一个客户虚拟机在竞争 CPU 时,非自主等待(时间片窃取)所占时间的百分比。应该保持这个值尽可能小。如果这个值很大,意味着虚拟机正在或者将要停止运转。
+* `%soft`: 和 `%irq` 相同,但是是软中断。
+* `%steal`: 虚拟机非自主等待(时间片窃取)所占时间的百分比,即当虚拟机在竞争 CPU 时所从虚拟机管理程序那里“赢得”的时间。应该保持这个值尽可能小。如果这个值很大,意味着虚拟机正在或者将要停止运转。
 * `%guest`: 运行虚拟处理器所用的时间百分比。
-* `%idle`: CPU(s) 没有运行任何任务所占时间的百分比。如果你观察到这个值很小,意味着系统负载很重。在这种情况下,你需要查看详细的进程列表、以及下面将要讨论的内容来确定这是什么原因导致的。
+* `%idle`: CPU 没有运行任何任务所占时间的百分比。如果你观察到这个值很小,意味着系统负载很重。在这种情况下,你需要查看详细的进程列表、以及下面将要讨论的内容来确定这是什么原因导致的。
 
 运行下面的命令使处理器处于极高负载,然后在另一个终端执行 mpstat 命令:
 
@@ -109,7 +110,8 @@ Average:       0    5.58    0.00    0.34    0.85    0.00    0.00    0.00    0.00
 最后,和 “正常” 情况下 **mpstat** 的输出作比较:
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/03/Report-Processors-Related-Statistics.png)
-> Linux 处理器相关统计信息报告
+
+*Linux 处理器相关统计信息报告*
 
 正如你在上面图示中看到的,在前面两个例子中,根据 `%idle` 的值可以判断 **CPU 0** 负载很高。
 
@@ -126,7 +128,8 @@ Average:       0    5.58    0.00    0.34    0.85    0.00    0.00    0.00    0.00
 上面的命令只会显示 `PID`、`PPID`、和进程相关的命令、 CPU 使用率以及 RAM 使用率,并按照 CPU 使用率降序排序。创建 .iso 文件的时候运行上面的命令,下面是输出的前面几行:
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/03/Find-Linux-Processes-By-CPU-Usage.png)
->根据 CPU 使用率查找进程
+
+*根据 CPU 使用率查找进程*
 
 一旦我们找到了感兴趣的进程(例如 `PID=2822` 的进程),我们就可以进入 `/proc/PID`(本例中是 `/proc/2822`) 列出目录内容。
 
@@ -134,10 +137,9 @@ Average:       0    5.58    0.00    0.34    0.85    0.00    0.00    0.00    0.00
 
 #### 例如:
 
-* `/proc/2822/io` 包括该进程的 IO 统计信息( IO 操作时的读写字符数)。
+* `/proc/2822/io` 包括该进程的 IO 统计信息(IO 操作时的读写字符数)。
 * `/proc/2822/attr/current` 显示了进程当前的 SELinux 安全属性。
-* `/proc/2822/attr/current` shows the current SELinux security attributes of the process.
-* `/proc/2822/cgroup` 如果启用了 CONFIG_CGROUPS 内核设置选项,这会显示该进程所属的控制组(简称 cgroups),你可以使用下面命令验证是否启用了 CONFIG_CGROUPS:
+* `/proc/2822/cgroup` 如果启用了 CONFIG_CGROUPS 内核设置选项,这会显示该进程所属的控制组(简称 cgroups),你可以使用下面命令验证是否启用了 CONFIG_CGROUPS:
 
 ```
 # cat /boot/config-$(uname -r) | grep -i cgroups
@@ -154,7 +156,8 @@ CONFIG_CGROUPS=y
 `/proc/2822/fd` 这个目录包含每个打开的描述进程的文件的符号链接。下面的截图显示了 tty1(第一个终端) 中创建 **.iso** 镜像进程的相关信息:
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/03/Find-Linux-Process-Information.png)
->查找 Linux 进程信息
+
+*查找 Linux 进程信息*
 
 上面的截图显示 **stdin**(文件描述符 **0**)、**stdout**(文件描述符 **1**)、**stderr**(文件描述符 **2**) 相应地被映射到 **/dev/zero**、 **/root/test.iso** 和   **/dev/tty1**。
 
@@ -170,14 +173,15 @@ CONFIG_CGROUPS=y
 *   	hard	nproc   10
 ```
 
-第一个字段可以用来表示一个用户、组或者所有`(*)`, 第二个字段强制限制可以使用的进程数目(nproc) 为 **10**。退出并重新登录就可以使设置生效。
+第一个字段可以用来表示一个用户、组或者所有人`(*)`, 第二个字段强制限制可以使用的进程数目(nproc) 为 **10**。退出并重新登录就可以使设置生效。
 
-然后,让我们来看看非 root 用户(合法用户或非法用户) 试图引起 shell fork bomb[WiKi][12] 时会发生什么。如果我们没有设置限制, shell fork bomb 会无限制地启动函数的两个实例,然后无限循环地复制任意一个实例。最终导致你的系统卡死。
+然后,让我们来看看非 root 用户(合法用户或非法用户) 试图引起 shell fork 炸弹 (参见 [WiKi][12]) 时会发生什么。如果我们没有设置限制, shell fork 炸弹会无限制地启动函数的两个实例,然后无限循环地复制任意一个实例。最终导致你的系统卡死。
 
-但是,如果使用了上面的限制,fort bomb 就不会成功,但用户仍然会被锁在外面直到系统管理员杀死相关的进程。
+但是,如果使用了上面的限制,fort 炸弹就不会成功,但用户仍然会被锁在外面直到系统管理员杀死相关的进程。
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/03/Shell-Fork-Bomb.png)
->运行 Shell Fork Bomb
+
+*运行 Shell Fork 炸弹*
 
 **提示**: `limits.conf` 文件中可以查看其它 **ulimit** 可以更改的限制。 
 
@@ -185,7 +189,7 @@ CONFIG_CGROUPS=y
 
 除了上面讨论的工具, 一个系统管理员还可能需要:
 
-**a)** 通过使用 **renice** 调整执行优先级(系统资源使用)。这意味着内核会根据分配的优先级(众所周知的 “**niceness**”,它是一个范围从 `-20` 到 `19` 的整数)给进程分配更多或更少的系统资源。
+**a)** 通过使用 **renice** 调整执行优先级(系统资源的使用)。这意味着内核会根据分配的优先级(众所周知的 “**niceness**”,它是一个范围从 `-20` 到 `19` 的整数)给进程分配更多或更少的系统资源。
 
 这个值越小,执行优先级越高。普通用户(而非 root)只能调高他们所有的进程的 niceness 值(意味着更低的优先级),而 root 用户可以调高或调低任何进程的 niceness 值。
 
@@ -195,9 +199,9 @@ renice 命令的基本语法如下:
 # renice [-n] <new priority> <UID, GID, PGID, or empty> identifier
 ```
 
-如果没有 new priority 后面的参数(为空),默认就是 PID。在这种情况下,  **PID=identifier** 的进程的 niceness 值会被设置为 `<new priority>`。
+如果 new priority 后面的参数没有(为空),默认就是 PID。在这种情况下,**PID=identifier** 的进程的 niceness 值会被设置为 `<new priority>`。
 
-**b)** 需要的时候中断一个进程的正常执行。这也就是通常所说的 [“杀死”进程][9]。实质上,这意味着给进程发送一个信号使它恰当地结束运行并以有序的方式释放任何占用的资源。
+**b)** 需要的时候中断一个进程的正常执行。这也就是通常所说的[“杀死”进程][9]。实质上,这意味着给进程发送一个信号使它恰当地结束运行并以有序的方式释放任何占用的资源。
 
 按照下面的方式使用 **kill** 命令[杀死进程][10]:
 
@@ -205,7 +209,7 @@ renice 命令的基本语法如下:
 # kill PID
 ```
 
-另外,你也可以使用 [pkill][11] 结束指定用户`(-u)`、指定组`(-G)` 甚至有共同 PPID`(-P)` 的所有进程。这些选项后面可以使用数字或者名称表示的标识符。
+另外,你也可以使用 [pkill][11] 结束指定用户`(-u)`、指定组`(-G)` 甚至有共同的父进程 ID `(-P)` 的所有进程。这些选项后面可以使用数字或者名称表示的标识符。
 
 ```
 # pkill [options] identifier
@@ -217,9 +221,7 @@ renice 命令的基本语法如下:
 # pkill -G 1000
 ```
 
-会杀死组 `GID=1000` 的所有进程。
-
-而
+会杀死组 `GID=1000` 的所有进程。而
 
 ```
 # pkill -P 4993 
@@ -236,7 +238,8 @@ renice 命令的基本语法如下:
 用下面的图片说明:
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/03/List-User-Running-Processes.png)
->在 Linux 中查找用户运行的进程
+
+*在 Linux 中查找用户运行的进程*
 
 ### 总结
 
@@ -246,17 +249,13 @@ renice 命令的基本语法如下:
 
 我们希望本篇中介绍的概念能对你有所帮助。如果你有任何疑问或者评论,可以使用下面的联系方式联系我们。
 
-
-
-
-
 --------------------------------------------------------------------------------
 
-via: http://www.tecmint.com/linux-basic-shell-scripting-and-linux-filesystem-troubleshooting/
+via: http://www.tecmint.com/monitor-linux-processes-and-set-process-limits-per-user/
 
 作者:[Gabriel Cánepa][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
+译者:[ictlyh](https://github.com/ictlyh)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
 
diff --git a/translated/tech/LXD/Part 1 - LXD 2.0--Introduction to LXD.md b/published/LXD/Part 1 - LXD 2.0--Introduction to LXD.md
similarity index 61%
rename from translated/tech/LXD/Part 1 - LXD 2.0--Introduction to LXD.md
rename to published/LXD/Part 1 - LXD 2.0--Introduction to LXD.md
index 011f385881..11c7220964 100644
--- a/translated/tech/LXD/Part 1 - LXD 2.0--Introduction to LXD.md	
+++ b/published/LXD/Part 1 - LXD 2.0--Introduction to LXD.md	
@@ -1,4 +1,4 @@
-Part 1 - LXD 2.0: LXD 入门
+LXD 2.0 系列(一):LXD 入门
 ======================================
 
 这是 [LXD 2.0 系列介绍文章][1]的第一篇。
@@ -20,12 +20,11 @@ LXD 最主要的目标就是使用 Linux 容器而不是硬件虚拟化向用户
 
 LXD 聚焦于系统容器,通常也被称为架构容器。这就是说 LXD 容器实际上如在裸机或虚拟机上运行一般运行了一个完整的 Linux 操作系统。
 
-这些容器一般基于一个干净的发布镜像并会长时间运行。传统的配置管理工具和部署工具可以如在虚拟机、云和物理机器上一样与 LXD 一起使用。
+这些容器一般基于一个干净的发布镜像并会长时间运行。传统的配置管理工具和部署工具可以如在虚拟机、云实例和物理机器上一样与 LXD 一起使用。
 
-相对的, Docker 关注于短期的、无状态的最小容器,这些容器通常并不会升级或者重新配置,而是作为一个整体被替换掉。这就使得 Docker 及类似项目更像是一种软件发布机制,而不是一个机器管理工具。
-
-这两种模型并不是完全互斥的。你完全可以使用 LXD 为你的用户提供一个完整的 Linux 系统,而他们可以在 LXD 内安装 Docker 来运行他们想要的软件。
+相对的, Docker 关注于短期的、无状态的、最小化的容器,这些容器通常并不会升级或者重新配置,而是作为一个整体被替换掉。这就使得 Docker 及类似项目更像是一种软件发布机制,而不是一个机器管理工具。
 
+这两种模型并不是完全互斥的。你完全可以使用 LXD 为你的用户提供一个完整的 Linux 系统,然后他们可以在 LXD 内安装 Docker 来运行他们想要的软件。
 
 #### 为什么要用 LXD?
 
@@ -35,56 +34,55 @@ LXD 聚焦于系统容器,通常也被称为架构容器。这就是说 LXD 
 
 我们把 LXD 作为解决这些缺陷的一个很好的机会。作为一个长时间运行的守护进程, LXD 可以绕开 LXC 的许多限制,比如动态资源限制、无法进行容器迁移和高效的在线迁移;同时,它也为创造新的默认体验提供了机会:默认开启安全特性,对用户更加友好。
 
-
 ### LXD 的主要组件
 
-LXD 是由几个主要组件构成的,这些组件都是 LXD 目录结构、命令行客户端和 API 结构体里下可见的。
+LXD 是由几个主要组件构成的,这些组件都出现在 LXD 目录结构、命令行客户端和 API 结构体里。
 
 #### 容器
 
 LXD 中的容器包括以下及部分:
 
-- 根文件系统
+- 根文件系统(rootfs)
+- 配置选项列表,包括资源限制、环境、安全选项等等
 - 设备:包括磁盘、unix 字符/块设备、网络接口
 - 一组继承而来的容器配置文件
-- 属性(容器架构,暂时的或持久的,容器名)
-- 运行时状态(当时为了记录检查点、恢复时用到了 CRIU时)
+- 属性(容器架构、暂时的还是持久的、容器名)
+- 运行时状态(当用 CRIU 来中断/恢复时)
 
 #### 快照
 
 容器快照和容器是一回事,只不过快照是不可修改的,只能被重命名,销毁或者用来恢复系统,但是无论如何都不能被修改。
 
-值得注意的是,因为我们允许用户保存容器的运行时状态,这就有效的为我们提供了“有状态”的快照的功能。这就是说我们可以使用快照回滚容器的 CPU 和内存。
+值得注意的是,因为我们允许用户保存容器的运行时状态,这就有效的为我们提供了“有状态”的快照的功能。这就是说我们可以使用快照回滚容器的状态,包括快照当时的 CPU 和内存状态。
 
 #### 镜像
 
 LXD 是基于镜像实现的,所有的 LXD 容器都是来自于镜像。容器镜像通常是一些纯净的 Linux 发行版的镜像,类似于你们在虚拟机和云实例上使用的镜像。
 
-所以就可以「发布」容器:使用容器制作一个镜像并在本地或者远程 LXD 主机上使用。
+所以可以「发布」一个容器:使用容器制作一个镜像并在本地或者远程 LXD 主机上使用。
 
-镜像通常使用全部或部分 sha256 哈希码来区分。因为输入长长的哈希码对用户来说不好,所以镜像可以使用几个自身的属性来区分,这就使得用户在镜像商店里方便搜索镜像。别名也可以用来 1 对 1 地把对用户友好的名字映射到某个镜像的哈希码。
+镜像通常使用全部或部分 sha256 哈希码来区分。因为输入长长的哈希码对用户来说不方便,所以镜像可以使用几个自身的属性来区分,这就使得用户在镜像商店里方便搜索镜像。也可以使用别名来一对一地将一个用户好记的名字映射到某个镜像的哈希码上。
 
 LXD 安装时已经配置好了三个远程镜像服务器(参见下面的远程一节):
 
-- “ubuntu:” 提供稳定版的 Ubuntu 镜像
-- “ubuntu-daily:” 提供每天构建出来的 Ubuntu
-- “images:” 社区维护的镜像服务器,提供一系列的 Linux 发布版,使用的是上游 LXC 的模板
+- “ubuntu”:提供稳定版的 Ubuntu 镜像
+- “ubuntu-daily”:提供 Ubuntu 的每日构建镜像
+- “images”: 社区维护的镜像服务器,提供一系列的其它 Linux 发布版,使用的是上游 LXC 的模板
 
 LXD 守护进程会从镜像上次被使用开始自动缓存远程镜像一段时间(默认是 10 天),超过时限后这些镜像才会失效。
 
 此外, LXD 还会自动更新远程镜像(除非指明不更新),所以本地的镜像会一直是最新版的。
 
-
 #### 配置
 
-配置文件是一种在一处定义容器配置和容器设备,然后应用到一系列容器的方法。
+配置文件是一种在一个地方定义容器配置和容器设备,然后将其应用到一系列容器的方法。
 
-一个容器可以被应用多个配置文件。当构建最终容器配置时(即通常的扩展配置),这些配置文件都会按照他们定义顺序被应用到容器上,当有重名的配置时,新的会覆盖掉旧的。然后本地容器设置会在这些基础上应用,覆盖所有来自配置文件的选项。
+一个容器可以被应用多个配置文件。当构建最终容器配置时(即通常的扩展配置),这些配置文件都会按照他们定义顺序被应用到容器上,当有重名的配置键或设备时,新的会覆盖掉旧的。然后本地容器设置会在这些基础上应用,覆盖所有来自配置文件的选项。
 
 LXD 自带两种预配置的配置文件:
 
-- 「 default 」配置是自动应用在所有容器之上,除非用户提供了一系列替代的配置文件。目前这个配置文件只做一件事,为容器定义 eth0 网络设备。
-- 「 docker” 」配置是一个允许你在容器里运行 Docker 容器的配置文件。它会要求 LXD 加载一些需要的内核模块以支持容器嵌套并创建一些设备入口。
+- “default”配置是自动应用在所有容器之上,除非用户提供了一系列替代的配置文件。目前这个配置文件只做一件事,为容器定义 eth0 网络设备。
+- “docker”配置是一个允许你在容器里运行 Docker 容器的配置文件。它会要求 LXD 加载一些需要的内核模块以支持容器嵌套并创建一些设备。
 
 #### 远程
 
@@ -92,14 +90,14 @@ LXD 自带两种预配置的配置文件:
 
 默认情况下,我们的命令行客户端会与下面几个预定义的远程服务器通信:
 
-- local:(默认的远程服务器,使用 UNIX socket 和本地的 LXD 守护进程通信)
-- ubuntu:( Ubuntu 镜像服务器,提供稳定版的 Ubuntu 镜像)
-- ubuntu-daily:( Ubuntu 镜像服务器,提供每天构建出来的 Ubuntu )
-- images:( images.linuxcontainers.org 镜像服务器)
+- local:默认的远程服务器,使用 UNIX socket 和本地的 LXD 守护进程通信
+- ubuntu:Ubuntu 镜像服务器,提供稳定版的 Ubuntu 镜像
+- ubuntu-daily:Ubuntu 镜像服务器,提供 Ubuntu 的每日构建版
+- images:images.linuxcontainers.org 的镜像服务器
 
 所有这些远程服务器的组合都可以在命令行客户端里使用。
 
-你也可以添加任意数量的远程 LXD 主机来监听网络。匿名的开放镜像服务器,或者通过认证可以管理远程容器的镜像服务器,都可以添加进来。
+你也可以添加任意数量的远程 LXD 主机,并配置它们监听网络。匿名的开放镜像服务器,或者通过认证可以管理远程容器的镜像服务器,都可以添加进来。
 
 正是这种远程机制使得与远程镜像服务器交互及在主机间复制、移动容器成为可能。
 
@@ -107,30 +105,29 @@ LXD 自带两种预配置的配置文件:
 
 我们设计 LXD 时的一个核心要求,就是在不修改现代 Linux 发行版的前提下,使容器尽可能的安全。
 
-LXD 使用的、通过使用 LXC 库实现的主要安全特性有:
+LXD 通过使用 LXC 库实现的主要安全特性有:
 
-- 内核名字空间。尤其是用户名字空间,它让容器和系统剩余部分完全分离。LXD 默认使用用户名字空间(和 LXC 相反),并允许用户在需要的时候以容器为单位打开或关闭。
+- 内核名字空间。尤其是用户名字空间,它让容器和系统剩余部分完全分离。LXD 默认使用用户名字空间(和 LXC 相反),并允许用户在需要的时候以容器为单位关闭(将容器标为“特权的”)。
 - Seccomp 系统调用。用来隔离潜在危险的系统调用。
-- AppArmor:对 mount、socket、ptrace 和文件访问提供额外的限制。特别是限制跨容器通信。
+- AppArmor。对 mount、socket、ptrace 和文件访问提供额外的限制。特别是限制跨容器通信。
 - Capabilities。阻止容器加载内核模块,修改主机系统时间,等等。
-- CGroups。限制资源使用,防止对主机的 DoS 攻击。
+- CGroups。限制资源使用,防止针对主机的 DoS 攻击。
 
+为了对用户友好,LXD 构建了一个新的配置语言把大部分的这些特性都抽象封装起来,而不是如 LXC 一般直接将这些特性暴露出来。举了例子,一个用户可以告诉 LXD 把主机设备放进容器而不需要手动检查他们的主/次设备号来手动更新 CGroup 策略。
 
-为了对用户友好 , LXD 构建了一个新的配置语言把大部分的这些特性都抽象封装起来,而不是如 LXC 一般直接将这些特性暴露出来。举了例子,一个用户可以告诉 LXD 把主机设备放进容器而不需要手动检查他们的主/次设备号来更新 CGroup 策略。
-
-和 LXD 本身通信是基于使用 TLS 1.2 保护的链路,这些链路只允许使用有限的几个被允许的密钥。当和那些经过系统证书认证之外的主机通信时, LXD 会提示用户验证主机的远程足迹(SSH 方式),然后把足迹缓存起来以供以后使用。
+和 LXD 本身通信是基于使用 TLS 1.2 保护的链路,只允许使用有限的几个被允许的密钥算法。当和那些经过系统证书认证之外的主机通信时, LXD 会提示用户验证主机的远程指纹(SSH 方式),然后把指纹缓存起来以供以后使用。
 
 ### REST 接口
 
-LXD 的工作都是通过 REST 接口实现的。在客户端和守护进程之间并没有其他的通讯手段。
+LXD 的工作都是通过 REST 接口实现的。在客户端和守护进程之间并没有其他的通讯渠道。
 
-REST 接口可以通过本地的 unix socket 访问,这只需要经过组认证,或者经过 HTTP 套接字使用客户端认证进行通信。
+REST 接口可以通过本地的 unix socket 访问,这只需要经过用户组认证,或者经过 HTTP 套接字使用客户端认证进行通信。
 
 REST 接口的结构能够和上文所说的不同的组件匹配,是一种简单、直观的使用方法。
 
 当需要一种复杂的通信机制时, LXD 将会进行 websocket 协商完成剩余的通信工作。这主要用于交互式终端会话、容器迁移和事件通知。
 
-LXD 2.0 附带了 1.0 版的稳定 API。虽然我们在 1.0 版 API 添加了额外的特性,但是这不会在 1.0 版 API 的端点里破坏向后兼容性,因为我们会声明额外的 API 扩展使得客户端可以找到新的接口。
+LXD 2.0 附带了 1.0 版的稳定 API。虽然我们在 1.0 版 API 添加了额外的特性,但是这不会在 1.0 版 API 端点里破坏向后兼容性,因为我们会声明额外的 API 扩展使得客户端可以找到新的接口。
 
 ### 容器规模化
 
diff --git a/translated/tech/LXD/Part 2 - LXD 2.0--Installing and configuring LXD.md b/published/LXD/Part 2 - LXD 2.0--Installing and configuring LXD.md
similarity index 78%
rename from translated/tech/LXD/Part 2 - LXD 2.0--Installing and configuring LXD.md
rename to published/LXD/Part 2 - LXD 2.0--Installing and configuring LXD.md
index fe386f3229..a523ffa1e6 100644
--- a/translated/tech/LXD/Part 2 - LXD 2.0--Installing and configuring LXD.md	
+++ b/published/LXD/Part 2 - LXD 2.0--Installing and configuring LXD.md	
@@ -1,4 +1,4 @@
-Part 2 - LXD 2.0: 安装与配置
+LXD 2.0 系列(二):安装与配置
 =================================================
 
 这是 LXD 2.0 [系列介绍文章][2]的第二篇。
@@ -11,7 +11,7 @@ Part 2 - LXD 2.0: 安装与配置
 
 #### Ubuntu 标准版
 
-所有新发布的 LXD 都会在发布几分钟后上传到 Ubuntu 开发版的安装源里。这个安装包然后就会当作种子给全部其他的安装包源,供 Ubuntu 用户使用。
+所有新发布的 LXD 都会在发布几分钟后上传到 Ubuntu 开发版的安装源里。这个安装包然后就会作为 Ubuntu 用户的其他安装包源的种子。
 
 如果使用 Ubuntu 16.04,可以直接安装:
 
@@ -54,7 +54,7 @@ sudo emerge --ask lxd
 
 #### 使用源代码安装
 
-如果你曾经编译过 Go 语言的项目,那么从源代码编译 LXD 并不是十分困难。然而注意,你需要 LXC 的开发头文件。为了运行 LXD, 你的发布版需也要使用比较新的内核(最起码是 3.13)、比较新的 LXC (1.1.4 或更高版本)、LXCFS 以及支持用户子 uid/gid 分配的 shadow。
+如果你曾经编译过 Go 语言的项目,那么从源代码编译 LXD 并不是十分困难。然而注意,你需要 LXC 的开发头文件。为了运行 LXD, 你的发布版需也要使用比较新的内核(最起码是 3.13)、比较新的 LXC (1.1.4 或更高版本)、LXCFS 以及支持用户子 uid/gid 分配的 shadow 文件。
 
 从源代码编译 LXD 的最新教程可以在[上游 README][2]里找到。
 
@@ -76,13 +76,13 @@ sudo lxd init
 
 ### 存储后端
 
-LXD 提供了许多集中存储后端。在开始使用 LXD 之前,你应该决定将要使用的后端,因为我们不支持在后端之间迁移已经生成的容器。
+LXD 提供了几种存储后端。在开始使用 LXD 之前,你应该决定将要使用的后端,因为我们不支持在后端之间迁移已经生成的容器。
 
 各个[后端特性比较表][3]可以在[这里][3]找到。
 
 #### ZFS
 
-我们的推荐是 ZFS, 因为它能支持 LXD 的全部特性,同时提供最快和最可靠的容器体验。它包括了以容器为单位的磁盘配额,即时快照和恢复,优化了的迁移(发送/接收),以及快速从镜像创建容器的能力。它同时也被认为要比 btrfs 更成熟。
+我们的推荐是 ZFS, 因为它能支持 LXD 的全部特性,同时提供最快和最可靠的容器体验。它包括了以容器为单位的磁盘配额、即时快照和恢复、优化后的迁移(发送/接收),以及快速从镜像创建容器的能力。它同时也被认为要比 btrfs 更成熟。
 
 要和 LXD 一起使用 ZFS ,你需要首先在你的系统上安装 ZFS。
 
@@ -112,11 +112,11 @@ sudo apt install ubuntu-zfs
 sudo lxd init
 ```
 
-这条命令接下来会向你提问一下一些 ZFS 的配置细节,然后为你配置好 ZFS。
+这条命令接下来会向你提问一些 ZFS 的配置细节,然后为你配置好 ZFS。
 
 #### btrfs
 
-如果 ZFS 不可用,那么 btrfs 可以提供相同级别的集成,但不会合理地报告容器内的磁盘使用情况(虽然配额仍然可用)。
+如果 ZFS 不可用,那么 btrfs 可以提供相同级别的集成,但不能正确地报告容器内的磁盘使用情况(虽然配额仍然可用)。
 
 btrfs 同时拥有很好的嵌套属性,而这是 ZFS 所不具有的。也就是说如果你计划在 LXD 中再使用 LXD,那么 btrfs 就很值得你考虑。
 
@@ -126,14 +126,13 @@ btrfs 同时拥有很好的嵌套属性,而这是 ZFS 所不具有的。也就
 
 如果 ZFS 和 btrfs 都不是你想要的,你还可以考虑使用 LVM 以获得部分特性。 LXD 会以自动精简配置的方式使用 LVM,为每个镜像和容器创建 LV,如果需要的话也会使用 LVM 的快照功能。
 
-要配置 LXD 使用 LVM,需要创建一个 LVM VG,然后运行:
-
+要配置 LXD 使用 LVM,需要创建一个 LVM 卷组,然后运行:
 
 ```
 lxc config set storage.lvm_vg_name "THE-NAME-OF-YOUR-VG"
 ```
 
-默认情况下 LXD 使用 ext4 作为全部 LV 的文件系统。如果你喜欢的话可以改成 XFS:
+默认情况下 LXD 使用 ext4 作为全部逻辑卷的文件系统。如果你喜欢的话可以改成 XFS:
 
 ```
 lxc config set storage.lvm_fstype xfs
@@ -151,7 +150,7 @@ LXD 守护进程的完整配置项列表可以在[这里找到][4]。
 
 #### 网络配置
 
-默认情况下 LXD 不会监听网络。和它通信的唯一办法是通过 `/var/lib/lxd/unix.socket` 使用本地 unix socket 进行通信。
+默认情况下 LXD 不会监听网络。和它通信的唯一办法是通过 `/var/lib/lxd/unix.socket` 使用本地 unix 套接字进行通信。
 
 要让 LXD 监听网络,下面有两个有用的命令:
 
@@ -160,11 +159,11 @@ lxc config set core.https_address [::]
 lxc config set core.trust_password some-secret-string
 ```
 
-第一条命令将 LXD 绑定到 IPv6 地址 “::”,也就是监听机器的所有 IPv6 地址。你可以显式的使用一个特定的 IPv4 或者 IPv6 地址替代默认地址,如果你想绑定 TCP 端口(默认是 8443)的话可以在地址后面添加端口号即可。
+第一条命令将 LXD 绑定到 IPv6 地址 “::”,也就是监听机器的所有 IPv6 地址。你可以显式的使用一个特定的 IPv4 或者 IPv6 地址替代默认地址,如果你想绑定某个 TCP 端口(默认是 8443)的话可以在地址后面添加端口号即可。
 
-第二条命令设置了密码,用于让远程客户端用来把自己添加到 LXD 可信证书中心。如果已经给主机设置了密码,当添加 LXD 主机时会提示输入密码,LXD 守护进程会保存他们的客户端的证书以确保客户端是可信的,这样就不需要再次输入密码(可以随时设置和取消)。
+第二条命令设置了密码,用于让远程客户端把自己添加到 LXD 可信证书中心。如果已经给主机设置了密码,当添加 LXD 主机时会提示输入密码,LXD 守护进程会保存他们的客户端证书以确保客户端是可信的,这样就不需要再次输入密码(可以随时设置和取消)。
 
-你也可以选择不设置密码,然后通过给每个客户端发送“client.crt”(来自于 `~/.config/lxc`)文件,然后把它添加到你自己的可信中信来实现人工验证每个新客户端是否可信,可以使用下面的命令:
+你也可以选择不设置密码,而是人工验证每个新客户端是否可信——让每个客户端发送“client.crt”(来自于 `~/.config/lxc`)文件,然后把它添加到你自己的可信证书中心:
 
 ```
 lxc config trust add client.crt
@@ -186,7 +185,7 @@ lxc config set core.proxy_ignore_hosts image-server.local
 
 #### 镜像管理
 
-LXD 使用动态镜像缓存。当从远程镜像创建容器的时候,它会自动把镜像下载到本地镜像商店,同时标志为已缓存并记录来源。几天后(默认 10 天)如果某个镜像没有被使用过,那么它就会自动地被删除。每个几小时(默认是 6 小时)LXD 还会检查一下这个镜像是否有新版本,然后更新镜像的本地拷贝。
+LXD 使用动态镜像缓存。当从远程镜像创建容器的时候,它会自动把镜像下载到本地镜像商店,同时标志为已缓存并记录来源。几天后(默认 10 天)如果某个镜像没有被使用过,那么它就会自动地被删除。每隔几小时(默认是 6 小时)LXD 还会检查一下这个镜像是否有新版本,然后更新镜像的本地拷贝。
 
 所有这些都可以通过下面的配置选项进行配置:
 
@@ -196,8 +195,7 @@ lxc config set images.auto_update_interval 24
 lxc config set images.auto_update_cached false
 ```
 
-这些命令让 LXD 修改了它的默认属性,缓存期替换为 5 天,更新间隔为 24 小时,而且只更新那些标记为自动更新的镜像(lxc 镜像拷贝被标记为 `–auto-update`)而不是 LXD 自动缓存的镜像。
-
+这些命令让 LXD 修改了它的默认属性,缓存期替换为 5 天,更新间隔为 24 小时,而且只更新那些标记为自动更新(–auto-update)的镜像(lxc 镜像拷贝被标记为 `–auto-update`)而不是 LXD 自动缓存的镜像。
 
 ### 总结
 
@@ -205,13 +203,10 @@ lxc config set images.auto_update_cached false
 
 ### 额外信息
 
-LXD 的主站在: <https://linuxcontainers.org/lxd>
-
-LXD 的 GitHub 仓库: <https://github.com/lxc/lxd>
-
-LXD 的邮件列表: <https://lists.linuxcontainers.org>
-
-LXD 的 IRC 频道: #lxcontainers on irc.freenode.net
+- LXD 的主站在: <https://linuxcontainers.org/lxd>
+- LXD 的 GitHub 仓库: <https://github.com/lxc/lxd>
+- LXD 的邮件列表: <https://lists.linuxcontainers.org>
+- LXD 的 IRC 频道: #lxcontainers on irc.freenode.net
 
 如果你不想或者不能在你的机器上安装 LXD ,你可以[试试在线版的 LXD][1]。
 
diff --git a/published/LXD/Part 3 - LXD 2.0--Your first LXD container.md b/published/LXD/Part 3 - LXD 2.0--Your first LXD container.md
new file mode 100644
index 0000000000..dd961f347b
--- /dev/null
+++ b/published/LXD/Part 3 - LXD 2.0--Your first LXD container.md	
@@ -0,0 +1,559 @@
+LXD 2.0 系列( 三):你的第一个 LXD 容器
+==========================================
+
+这是 [LXD 2.0 系列][0]的第三篇博客。
+
+由于在管理 LXD 容器时涉及到大量的命令,所以这篇文章的篇幅是比较长的,如果你更喜欢使用同样的命令来快速的一步步实现整个过程,你可以[尝试我们的在线示例][1]!
+
+![](https://linuxcontainers.org/static/img/containers.png)
+
+### 创建并启动一个新的容器
+
+正如我在先前的文章中提到的一样,LXD 命令行客户端预配置了几个镜像源。Ubuntu 的所有发行版和架构平台全都提供了官方镜像,但是对于其他的发行版也有大量的非官方镜像,那些镜像都是由社区制作并且被 LXC 上游贡献者所维护。
+
+#### Ubuntu
+
+如果你想要支持最为完善的 Ubuntu 版本,你可以按照下面的去做:
+
+```
+lxc launch ubuntu:
+```
+
+注意,这里意味着会随着 Ubuntu LTS 的发布而变化。因此,如果用于脚本,你需要指明你具体安装的版本(参见下面)。
+
+#### Ubuntu14.04 LTS
+
+得到最新更新的、已经测试过的、稳定的 Ubuntu 14.04 LTS 镜像,你可以简单的执行:
+
+```
+lxc launch ubuntu:14.04
+```
+
+在该模式下,会指定一个随机的容器名。
+
+如果你更喜欢指定一个你自己的名字,你可以这样做:
+
+```
+lxc launch ubuntu:14.04 c1
+```
+
+如果你想要指定一个特定的体系架构(非主流平台),比如 32 位 Intel 镜像,你可以这样做:
+
+```
+lxc launch ubuntu:14.04/i386 c2
+```
+
+#### 当前的 Ubuntu 开发版本
+
+上面使用的“ubuntu:”远程仓库只会给你提供官方的并经过测试的 Ubuntu 镜像。但是如果你想要未经测试过的日常构建版本,开发版可能对你来说是合适的,你需要使用“ubuntu-daily:”远程仓库。
+
+```
+lxc launch ubuntu-daily:devel c3
+```
+
+在这个例子中,将会自动选中最新的 Ubuntu 开发版本。
+
+你也可以更加精确,比如你可以使用代号名:
+
+```
+lxc launch ubuntu-daily:xenial c4
+```
+
+#### 最新的Alpine Linux
+
+Alpine 镜像可以在“Images:”远程仓库中找到,通过如下命令执行:
+
+```
+lxc launch images:alpine/3.3/amd64 c5
+```
+
+#### 其他
+
+全部的 Ubuntu 镜像列表可以这样获得:
+
+```
+lxc image list ubuntu:
+lxc image list ubuntu-daily:
+```
+
+全部的非官方镜像:
+
+```
+lxc image list images:
+```
+
+某个给定的原程仓库的全部别名(易记名称)可以这样获得(比如对于“ubuntu:”远程仓库):
+
+```
+lxc image alias list ubuntu:
+```
+
+### 创建但不启动一个容器
+
+如果你想创建一个容器或者一批容器,但是你不想马上启动它们,你可以使用`lxc init`替换掉`lxc launch`。所有的选项都是相同的,唯一的不同就是它并不会在你创建完成之后启动容器。
+
+```
+lxc init ubuntu:
+```
+
+### 关于你的容器的信息
+
+#### 列出所有的容器
+
+要列出你的所有容器,你可以这样这做:
+
+```
+lxc list
+```
+ 
+有大量的选项供你选择来改变被显示出来的列。在一个拥有大量容器的系统上,默认显示的列可能会有点慢(因为必须获取容器中的网络信息),你可以这样做来避免这种情况:
+
+```
+lxc list --fast
+```
+
+上面的命令显示了另外一套列的组合,这个组合在服务器端需要处理的信息更少。
+
+你也可以基于名字或者属性来过滤掉一些东西:
+
+```
+stgraber@dakara:~$ lxc list security.privileged=true
++------+---------+---------------------+-----------------------------------------------+------------+-----------+
+| NAME |  STATE  |        IPV4         |                       IPV6                    |    TYPE    | SNAPSHOTS |
++------+---------+---------------------+-----------------------------------------------+------------+-----------+
+| suse | RUNNING | 172.17.0.105 (eth0) | 2607:f2c0:f00f:2700:216:3eff:fef2:aff4 (eth0) | PERSISTENT | 0         |
++------+---------+---------------------+-----------------------------------------------+------------+-----------+
+```
+
+在这个例子中,只有那些特权容器(禁用了用户命名空间)才会被列出来。
+
+```
+stgraber@dakara:~$ lxc list --fast alpine
++-------------+---------+--------------+----------------------+----------+------------+
+|    NAME     |  STATE  | ARCHITECTURE |      CREATED AT      | PROFILES |    TYPE    |
++-------------+---------+--------------+----------------------+----------+------------+
+| alpine      | RUNNING | x86_64       | 2016/03/20 02:11 UTC | default  | PERSISTENT |
++-------------+---------+--------------+----------------------+----------+------------+
+| alpine-edge | RUNNING | x86_64       | 2016/03/20 02:19 UTC | default  | PERSISTENT |
++-------------+---------+--------------+----------------------+----------+------------+
+```
+
+在这个例子中,只有在名字中带有“alpine”的容器才会被列出来(也支持复杂的正则表达式)。
+
+####  获取容器的详细信息
+
+由于 list 命令显然不能以一种友好的可读方式显示容器的所有信息,因此你可以使用如下方式来查询单个容器的信息:
+
+```
+lxc info <container>
+```
+
+例如:
+
+```
+stgraber@dakara:~$ lxc info zerotier
+Name: zerotier
+Architecture: x86_64
+Created: 2016/02/20 20:01 UTC
+Status: Running
+Type: persistent
+Profiles: default
+Pid: 31715
+Processes: 32
+Ips:
+ eth0: inet 172.17.0.101
+ eth0: inet6 2607:f2c0:f00f:2700:216:3eff:feec:65a8
+ eth0: inet6 fe80::216:3eff:feec:65a8
+ lo: inet 127.0.0.1
+ lo: inet6 ::1
+ lxcbr0: inet 10.0.3.1
+ lxcbr0: inet6 fe80::c0a4:ceff:fe52:4d51
+ zt0: inet 29.17.181.59
+ zt0: inet6 fd80:56c2:e21c:0:199:9379:e711:b3e1
+ zt0: inet6 fe80::79:e7ff:fe0d:5123
+Snapshots:
+ zerotier/blah (taken at 2016/03/08 23:55 UTC) (stateless)
+ ```
+ 
+### 生命周期管理命令
+
+这些命令对于任何容器或者虚拟机管理器或许都是最普通的命令,但是它们仍然需要讲到。
+
+所有的这些命令在批量操作时都能接受多个容器名。
+
+#### 启动
+
+启动一个容器就向下面一样简单:
+
+```
+lxc start <container>
+```
+
+#### 停止
+
+停止一个容器可以这样来完成:
+
+```
+lxc stop <container>
+```
+
+如果容器不合作(即没有对发出的 SIGPWR 信号产生回应),这时候,你可以使用下面的方式强制执行:
+
+```
+lxc stop <container> --force
+```
+
+#### 重启
+
+通过下面的命令来重启一个容器:
+
+```
+lxc restart <container>
+```
+
+如果容器不合作(即没有对发出的 SIGINT 信号产生回应),你可以使用下面的方式强制执行:
+
+```
+lxc restart <container> --force
+```
+
+#### 暂停
+
+你也可以“暂停”一个容器,在这种模式下,所有的容器任务将会被发送相同的 SIGSTOP 信号,这也意味着它们将仍然是可见的,并且仍然会占用内存,但是它们不会从调度程序中得到任何的 CPU 时间片。
+
+如果你有一个很占用 CPU 的容器,而这个容器需要一点时间来启动,但是你却并不会经常用到它。这时候,你可以先启动它,然后将它暂停,并在你需要它的时候再启动它。
+
+```
+lxc pause <container>
+```
+
+#### 删除
+
+最后,如果你不需要这个容器了,你可以用下面的命令删除它:
+
+```
+lxc delete <container>
+```
+
+注意,如果容器还处于运行状态时你将必须使用“-force”。
+
+### 容器的配置
+
+LXD 拥有大量的容器配置设定,包括资源限制,容器启动控制以及对各种设备是否允许访问的配置选项。完整的清单因为太长所以并没有在本文中列出,但是,你可以从[这里]获取它。
+
+就设备而言,LXD 当前支持下面列出的这些设备类型:
+
+- 磁盘 
+	既可以是一块物理磁盘,也可以只是一个被挂挂载到容器上的分区,还可以是一个来自主机的绑定挂载路径。
+- 网络接口卡
+	一块网卡。它可以是一块桥接的虚拟网卡,或者是一块点对点设备,还可以是一块以太局域网设备或者一块已经被连接到容器的真实物理接口。
+- unix 块设备
+	一个 UNIX 块设备,比如 /dev/sda
+- unix 字符设备
+	一个 UNIX 字符设备,比如 /dev/kvm
+- none
+	这种特殊类型被用来隐藏那种可以通过配置文件被继承的设备。
+
+#### 配置 profile 文件
+
+所有可用的配置文件列表可以这样获取:
+
+```
+lxc profile list
+```
+
+为了看到给定配置文件的内容,最简单的方式是这样做:
+
+```
+lxc profile show <profile>
+```
+
+你可能想要改变文件里面的内容,可以这样做:
+
+```
+lxc profile edit <profile>
+```
+
+你可以使用如下命令来改变应用到给定容器的配置文件列表:
+
+```
+lxc profile apply <container> <profile1>,<profile2>,<profile3>,...
+```
+
+####  本地配置
+
+有些配置是某个容器特定的,你并不想将它放到配置文件中,你可直接对容器设置它们:
+
+```
+lxc config edit <container>
+```
+
+上面的命令做的和“profile edit”命令是一样。
+
+如果不想在文本编辑器中打开整个文件的内容,你也可以像这样修改单独的配置:
+
+```
+lxc config set <container> <key> <value>
+```
+
+或者添加设备,例如:
+
+```
+lxc config device add my-container kvm unix-char path=/dev/kvm
+```
+
+上面的命令将会为名为“my-container”的容器设置一个 /dev/kvm 项。
+
+对一个配置文件使用`lxc profile set`和`lxc profile device add`命令也能实现上面的功能。
+
+#### 读取配置
+
+你可以使用如下命令来读取容器的本地配置:
+
+```
+lxc config show <container>
+```
+
+或者得到已经被展开了的配置(包含了所有的配置值):
+
+```
+lxc config show --expanded <container>
+```
+
+例如:
+
+```
+stgraber@dakara:~$ lxc config show --expanded zerotier
+name: zerotier
+profiles:
+- default
+config:
+ security.nesting: "true"
+ user.a: b
+ volatile.base_image: a49d26ce5808075f5175bf31f5cb90561f5023dcd408da8ac5e834096d46b2d8
+ volatile.eth0.hwaddr: 00:16:3e:ec:65:a8
+ volatile.last_state.idmap: '[{"Isuid":true,"Isgid":false,"Hostid":100000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":100000,"Nsid":0,"Maprange":65536}]'
+devices:
+ eth0:
+  name: eth0
+  nictype: macvlan
+  parent: eth0
+  type: nic
+  limits.ingress: 10Mbit
+  limits.egress: 10Mbit
+ root:
+  path: /
+  size: 30GB
+  type: disk
+ tun:
+  path: /dev/net/tun
+  type: unix-char
+ephemeral: false
+```
+
+这样做可以很方便的检查有哪些配置属性被应用到了给定的容器。
+
+####  实时配置更新
+
+注意,除非在文档中已经被明确指出,否则所有的配置值和设备项的设置都会对容器实时发生影响。这意味着在不重启正在运行的容器的情况下,你可以添加和移除某些设备或者修改安全配置文件。
+
+### 获得一个 shell
+
+LXD 允许你直接在容器中执行任务。最常用的做法是在容器中得到一个 shell 或者执行一些管理员任务。
+
+和 SSH 相比,这样做的好处是你不需要容器是网络可达的,也不需要任何软件和特定的配置。
+
+#### 执行环境
+
+与 LXD 在容器内执行命令的方式相比,有一点是不同的,那就是 shell 并不是在容器中运行。这也意味着容器不知道使用的是什么样的 shell,以及设置了什么样的环境变量和你的家目录在哪里。
+
+通过 LXD 来执行命令总是使用最小的路径环境变量设置,并且 HOME 环境变量必定为 /root,以容器的超级用户身份来执行(即 uid 为 0,gid 为 0)。
+
+其他的环境变量可以通过命令行来设置,或者在“environment.<key>”配置中设置成永久环境变量。
+
+####  执行命令
+
+在容器中获得一个 shell 可以简单的执行下列命令得到:
+
+```
+lxc exec <container> bash
+```
+
+当然,这样做的前提是容器内已经安装了 bash。
+
+更复杂的命令要求使用分隔符来合理分隔参数。
+
+```
+lxc exec <container> -- ls -lh /
+```
+
+如果想要设置或者重写变量,你可以使用“-env”参数,例如:
+
+```
+stgraber@dakara:~$ lxc exec zerotier --env mykey=myvalue env | grep mykey
+mykey=myvalue
+```
+
+### 管理文件
+
+因为 LXD 可以直接访问容器的文件系统,因此,它可以直接读取和写入容器中的任意文件。当我们需要提取日志文件或者与容器传递文件时,这个特性是很有用的。
+
+#### 从容器中取回一个文件
+
+想要从容器中获得一个文件,简单的执行下列命令:
+
+```
+lxc file pull <container>/<path> <dest>
+```
+
+例如:
+
+```
+stgraber@dakara:~$ lxc file pull zerotier/etc/hosts hosts
+```
+
+或者将它读取到标准输出:
+
+```
+stgraber@dakara:~$ lxc file pull zerotier/etc/hosts -
+127.0.0.1 localhost
+
+# The following lines are desirable for IPv6 capable hosts
+::1 ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
+ff02::3 ip6-allhosts
+```
+
+#### 向容器发送一个文件  
+
+发送以另一种简单的方式完成:
+
+```
+lxc file push <source> <container>/<path>
+```
+
+#### 直接编辑一个文件
+
+编辑是一个方便的功能,其实就是简单的提取一个给定的路径,在你的默认文本编辑器中打开它,在你关闭编辑器时会自动将编辑的内容保存到容器。
+
+```
+lxc file edit <container>/<path>
+```
+
+### 快照管理
+
+LXD 允许你对容器执行快照功能并恢复它。快照包括了容器在某一时刻的完整状态(如果`-stateful`被使用的话将会包括运行状态),这也意味着所有的容器配置,容器设备和容器文件系统也会被保存。
+
+#### 创建一个快照
+
+你可以使用下面的命令来执行快照功能:
+
+```
+lxc snapshot <container>
+```
+
+命令执行完成之后将会生成名为snapX(X 为一个自动增长的数)的记录。
+
+除此之外,你还可以使用如下命令命名你的快照:
+
+```
+lxc snapshot <container> <snapshot name>
+```
+
+#### 列出所有的快照
+
+一个容器的所有快照的数量可以使用`lxc list`来得到,但是具体的快照列表只能执行`lxc info`命令才能看到。
+
+```
+lxc info <container>
+```
+
+#### 恢复快照
+
+为了恢复快照,你可以简单的执行下面的命令:
+
+```
+lxc restore <container> <snapshot name>
+```
+
+#### 给快照重命名
+
+可以使用如下命令来给快照重命名:
+
+```
+lxc move <container>/<snapshot name> <container>/<new snapshot name>
+```
+
+#### 从快照中创建一个新的容器
+
+你可以使用快照来创建一个新的容器,而这个新的容器除了一些可变的信息将会被重置之外(例如 MAC 地址)其余所有信息都将和快照完全相同。
+
+```
+lxc copy <source container>/<snapshot name> <destination container>
+```
+
+#### 删除一个快照
+
+最后,你可以执行下面的命令来删除一个快照:
+
+```
+lxc delete <container>/<snapshot name>
+```
+
+### 克隆并重命名
+
+得到一个纯净的发行版镜像总是让人感到愉悦,但是,有时候你想要安装一系列的软件到你的容器中,这时,你需要配置它然后将它分支成多个其他的容器。
+
+#### 复制一个容器
+
+为了复制一个容器并有效的将它克隆到一个新的容器中,你可以执行下面的命令:
+
+```
+lxc copy <source container> <destination container>
+```
+
+目标容器在所有方面将会完全和源容器等同。除了新的容器没有任何源容器的快照以及一些可变值将会被重置之外(例如 MAC 地址)。
+
+#### 移动一个快照
+
+LXD 允许你复制容器并在主机之间移动它。但是,关于这一点将在后面的文章中介绍。
+
+现在,“move”命令将会被用作给容器重命名。
+
+```
+lxc move <old name> <new name>
+```
+
+唯一的要求就是当容器应该被停止,容器内的任何事情都会被保存成它本来的样子,包括可变化的信息(类似 MAC 地址等)。
+
+### 结论
+
+这篇如此长的文章介绍了大多数你可能会在日常操作中使用到的命令。
+
+很显然,这些如此之多的命令都会有不少选项,可以让你的命令更加有效率或者可以让你指定你的 LXD 容器的某个具体方面的参数。最好的学习这些命令的方式就是深入学习它们的帮助文档( --help)。
+
+### 更多信息
+
+- LXD 的主要网站是:<https://linuxcontainers.org/lxd>
+- Github 上的开发动态: <https://github.com/lxc/lxd>
+- 邮件列表支持:<https://lists.linuxcontainers.org>
+- IRC 支持: #lxcontainers on irc.freenode.net
+
+如果你不想或者不能在你的机器上安装 LXD,你可以[试试在线版本][1]!
+
+--------------------------------------------------------------------------------
+
+via:https://www.stgraber.org/2016/03/19/lxd-2-0-your-first-lxd-container-312/
+作者:[Stéphane Graber][a]
+译者:[kylepeng93](https://github.com/kylepeng93)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.stgraber.org/author/stgraber/
+[0]: https://www.stgraber.org/2016/03/11/lxd-2-0-blog-post-series-012/
+[1]: https://linuxcontainers.org/lxd/try-it
+[2]: https://github.com/lxc/lxd/blob/master/doc/configuration.md
diff --git a/published/Part 1 - How to Use Awk and Regular Expressions to Filter Text or String in Files.md b/published/awk/Part 1 - How to Use Awk and Regular Expressions to Filter Text or String in Files.md
similarity index 100%
rename from published/Part 1 - How to Use Awk and Regular Expressions to Filter Text or String in Files.md
rename to published/awk/Part 1 - How to Use Awk and Regular Expressions to Filter Text or String in Files.md
diff --git a/published/awk/Part 10 - Learn How to Use Awk Built-in Variables.md b/published/awk/Part 10 - Learn How to Use Awk Built-in Variables.md
new file mode 100644
index 0000000000..70f494fb90
--- /dev/null
+++ b/published/awk/Part 10 - Learn How to Use Awk Built-in Variables.md	
@@ -0,0 +1,129 @@
+awk 系列:如何使用 awk 内置变量
+=================================================
+
+我们将逐渐揭开 awk 功能的神秘面纱,在本节中,我们将介绍 awk 内置(built-in)变量的概念。你可以在 awk 中使用两种类型的变量,它们是:用户自定义(user-defined)变量(我们在[第八节][1]中已经介绍了)和内置变量。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Awk-Built-in-Variables-Examples.png)
+
+*awk 内置变量示例*
+
+awk 内置变量已经有预先定义的值了,但我们也可以谨慎地修改这些值,awk 内置变量包括:
+
+- `FILENAME` : 当前输入文件名称
+- `NR` : 当前输入行编号(是指输入行 1,2,3……等)
+- `NF` : 当前输入行的字段编号
+- `OFS` : 输出字段分隔符
+- `FS` : 输入字段分隔符
+- `ORS` : 输出记录分隔符
+- `RS` : 输入记录分隔符
+
+让我们继续演示一些使用上述 awk 内置变量的方法:
+
+想要读取当前输入文件的名称,你可以使用 `FILENAME` 内置变量,如下:
+
+```
+$ awk ' { print FILENAME } ' ~/domains.txt 
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Awk-FILENAME-Variable.png)
+
+*awk FILENAME 变量*
+
+你会看到,每一行都会对应输出一次文件名,那是你使用 `FILENAME` 内置变量时 awk 默认的行为。
+
+我们可以使用 `NR` 来统计一个输入文件的行数(记录),谨记,它也会计算空行,正如我们将要在下面的例子中看到的那样。
+
+当我们使用 cat 命令查看文件 domains.txt 时,会发现它有 14 行文本和 2 个空行:
+
+```
+$ cat ~/domains.txt
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Print-Contents-of-File.png)
+
+*输出文件内容*
+
+
+```
+$ awk ' END { print "Number of records in file is: ", NR } ' ~/domains.txt 
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Awk-Count-Number-of-Lines.png)
+
+*awk 统计行数*
+
+想要统计一条记录或一行中的字段数,我们可以像下面那样使用 NR 内置变量:
+
+```
+$ cat ~/names.txt
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/List-File-Contents.png)
+
+*列出文件内容*
+
+```
+$ awk '{ "Record:",NR,"has",NF,"fields" ; }' ~/names.txt
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Awk-Count-Number-of-Fields-in-File.png)
+
+*awk 统计文件中的字段数*
+
+接下来,你也可以使用 FS 内置变量指定一个输入文件分隔符,它会定义 awk 如何将输入行划分成字段。
+
+FS 默认值为“空格”和“制表符”,但我们也能将 FS 值修改为任何字符来让 awk 根据情况切分输入行。
+
+有两种方法可以达到目的:
+
+- 第一种方法是使用 FS 内置变量
+- 第二种方法是使用 awk 的 -F 选项
+
+来看 Linux 系统上的 `/etc/passwd` 文件,该文件中的各字段是使用 `:` 分隔的,因此,当我们想要过滤出某些字段时,可以将 `:` 指定为新的输入字段分隔符,示例如下:
+
+我们可以使用 `-F` 选项,如下:
+
+```
+$ awk -F':' '{ print $1, $4 ;}' /etc/passwd
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Awk-Filter-Fields-in-Password-File.png)
+
+*awk 过滤密码文件中的各字段*
+
+此外,我们也可以利用 FS 内置变量,如下:
+
+```
+$ awk ' BEGIN {  FS=“:” ; }  { print $1, $4  ; } ' /etc/passwd
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Filter-Fields-in-File-Using-Awk.png)
+
+*使用 awk 过滤文件中的各字段*
+
+使用 OFS 内置变量来指定一个用于输出的字段分隔符,它会定义如何使用指定的字符分隔输出字段,示例如下:
+
+```
+$ awk -F':' ' BEGIN { OFS="==>" ;} { print $1, $4 ;}' /etc/passwd
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Add-Separator-to-Field-in-File.png)
+
+*向文件中的字段添加分隔符*
+
+在本节中,我们已经学习了使用含有预定义值的 awk 内置变量的理念。但我们也能够修改这些值,虽然并不推荐这样做,除非你明白自己在做什么,并且充分理解(这些变量值)。
+
+此后,我们将继续学习如何在 awk 命令操作中使用 shell 变量,所以,请继续关注我们。
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/awk-built-in-variables-examples/
+
+作者:[Aaron Kili][a]
+译者:[ChrisLeeGit](https://github.com/chrisleegit)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
+[1]: https://linux.cn/article-7650-1.html
diff --git a/published/awk/Part 11 - How to Allow Awk to Use Shell Variables.md b/published/awk/Part 11 - How to Allow Awk to Use Shell Variables.md
new file mode 100644
index 0000000000..7caf10ec28
--- /dev/null
+++ b/published/awk/Part 11 - How to Allow Awk to Use Shell Variables.md	
@@ -0,0 +1,100 @@
+awk 系列:如何让 awk 使用 Shell 变量
+==================================================
+
+当我们编写 shell 脚本时,我们通常会在脚本中包含其它小程序或命令,例如 awk 操作。对于 awk 而言,我们需要找一些将某些值从 shell 传递到 awk 操作中的方法。
+
+我们可以通过在 awk 命令中使用 shell 变量达到目的,在 awk 系列的这一节中,我们将学习如何让 awk 使用 shell 变量,这些变量可能包含我们希望传递给 awk 命令的值。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/08/Use-Shell-Variables-in-Awk.png)
+
+有两种可能的方法可以让 awk 使用 shell 变量:
+
+### 1. 使用 Shell 引用
+
+让我们用一个示例来演示如何在一条 awk 命令中使用 shell 引用来替代一个 shell 变量。在该示例中,我们希望在文件 /etc/passwd 中搜索一个用户名,过滤并输出用户的账户信息。
+
+因此,我们可以编写一个 `test.sh` 脚本,内容如下:
+
+```
+#!/bin/bash
+
+### 读取用户名
+read -p "请输入用户名:" username
+
+### 在 /etc/passwd 中搜索用户名,然后在屏幕上输出详细信息
+cat /etc/passwd | awk "/$username/ "' { print $0 }'
+```
+
+然后,保存文件并退出。
+
+上述 `test.sh` 脚本中 awk 命令的说明:
+
+```
+cat /etc/passwd | awk "/$username/ "' { print $0 }'
+```
+
+`"/$username/ "`:该 shell 引用用于在 awk 命令中替换 shell 变量 `username` 的值。`username` 的值就是要在文件 /etc/passwd 中搜索的模式。
+
+注意,双引号位于 awk 脚本 `'{ print $0 }'` 之外。
+
+接下来给脚本添加可执行权限并运行它,操作如下:
+
+```
+$ chmod  +x  test.sh
+$ ./text.sh 
+```
+
+运行脚本后,它会提示你输入一个用户名,然后你输入一个合法的用户名并回车。你将会看到来自 /etc/passwd 文件中详细的用户账户信息,如下图所示:
+
+![](http://www.tecmint.com/wp-content/uploads/2016/08/Shell-Script-to-Find-Username-in-Passwd-File.png)
+
+*在 Password 文件中查找用户名的 shell 脚本*
+
+### 2. 使用 awk 进行变量赋值
+
+和上面介绍的方法相比,该方法更加单,并且更好。考虑上面的示例,我们可以运行一条简单的命令来完成同样的任务。
+在该方法中,我们使用 `-v` 选项将一个 shell 变量的值赋给一个 awk 变量。
+
+首先,创建一个 shell 变量 `username`,然后给它赋予一个我们希望在 /etc/passwd 文件中搜索的名称。
+
+```
+username="aaronkilik"
+```
+然后输入下面的命令并回车:
+
+```
+# cat /etc/passwd | awk -v name="$username" ' $0 ~ name {print $0}'
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/08/Find-Username-in-Password-File-Using-Awk.png)
+
+*使用 awk 在 Password 文件中查找用户名*
+
+上述命令的说明:
+
+- `-v`:awk 选项之一,用于声明一个变量
+- `username`:是 shell 变量
+- `name`:是 awk 变量
+
+让我们仔细瞧瞧 awk 脚本 `' $0 ~ name {print $0}'` 中的 `$0 ~ name`。还记得么,当我们在 awk 系列第四节中介绍 awk 比较运算符时,`value ~ pattern` 便是比较运算符之一,它是指:如果 `value` 匹配了 `pattern` 则返回 `true`。
+
+cat 命令通过管道传给 awk 的 `output($0)` 与模式 `(aaronkilik)` 匹配,该模式即为我们在 /etc/passwd 中搜索的名称,最后,比较操作返回 `true`。接下来会在屏幕上输出包含用户账户信息的行。
+
+### 结论
+
+我们已经介绍了 awk 功能的一个重要部分,它能帮助我们在 awk 命令中使用 shell 变量。很多时候,你都会在 shell 脚本中编写小的 awk 程序或命令,因此,你需要清晰地理解如何在 awk 命令中使用 shell 变量。
+
+在 awk 系列的下一个部分,我们将会深入学习 awk 功能的另外一个关键部分,即流程控制语句。所以请继续保持关注,并让我们坚持学习与分享。
+
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/use-shell-script-variable-in-awk/
+
+作者:[Aaron Kili][a]
+译者:[ChrisLeeGit](https://github.com/chrisleegit)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
diff --git a/published/awk/Part 12 -  How to Use Flow Control Statements in Awk - Part 12.md b/published/awk/Part 12 -  How to Use Flow Control Statements in Awk - Part 12.md
new file mode 100644
index 0000000000..96e0f4337d
--- /dev/null
+++ b/published/awk/Part 12 -  How to Use Flow Control Statements in Awk - Part 12.md	
@@ -0,0 +1,246 @@
+awk 系列:在 awk 中如何使用流程控制语句
+===========================================
+
+当你回顾所有到目前为止我们已经覆盖的 awk 实例,从 awk 系列的开始,你会注意到各种实例的所有指令是顺序执行的,即一个接一个地执行。但在某些情况下,我们可能希望基于一些条件进行文本过滤操作,即流程控制语句允许的那些语句。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/08/Use-Flow-Control-Statements-in-Awk.png)
+
+在 awk 编程中有各种各样的流程控制语句,其中包括:
+
+- if-else 语句
+- for 语句
+- while 语句
+- do-while 语句
+- break 语句
+- continue 语句
+- next 语句
+- nextfile 语句
+- exit 语句
+
+然而,对于本系列的这一部分,我们将阐述:`if-else`、`for`、`while` 和 `do while` 语句。请记住,我们已经在这个 [awk 系列的第 6 部分][1]介绍过如何使用 awk 的 `next` 语句。
+
+### 1. if-else 语句
+
+如你想的那样。if 语句的语法类似于 shell 中的 if 语句:
+
+```
+if  (条件 1) {
+     动作 1
+}
+else {
+      动作 2
+}
+```
+
+在上述语法中,`条件 1` 和`条件 2` 是 awk 表达式,而`动作 1` 和`动作 2` 是当各自的条件得到满足时所执行的 awk 命令。
+
+当`条件 1` 满足时,意味着它为真,那么`动作 1` 被执行并退出 `if 语句`,否则`动作 2` 被执行。
+
+if 语句还能扩展为如下的 `if-else_if-else` 语句:
+
+```
+if (条件 1){
+     动作 1
+}
+else if (条件 2){
+      动作 2
+}
+else{
+     动作 3
+}
+```
+
+对于上面的形式,如果`条件 1` 为真,那么`动作 1` 被执行并退出 `if 语句`,否则`条件 2` 被求值且如果值为真,那么`动作 2` 被执行并退出 `if 语句`。然而,当`条件 2` 为假时,那么`动作 3` 被执行并退出 `if 语句`。
+
+这是在使用 if 语句的一个实例,我们有一个用户和他们年龄的列表,存储在文件 users.txt 中。
+
+我们要打印一个清单,显示用户的名称和用户的年龄是否小于或超过 25 岁。
+
+```
+aaronkilik@tecMint ~ $ cat users.txt
+Sarah L			35    	F
+Aaron Kili		40    	M
+John  Doo		20    	M
+Kili  Seth		49    	M
+```
+
+我们可以写一个简短的 shell 脚本来执行上文中我们的工作,这是脚本的内容:
+
+```
+#!/bin/bash
+awk ' {
+        if ( $3 <= 25 ){
+           print "User",$1,$2,"is less than 25 years old." ;
+        }
+        else {
+           print "User",$1,$2,"is more than 25 years old" ;
+        }
+}'    ~/users.txt
+```
+
+然后保存文件并退出,按如下方式使脚本可执行并运行它:
+
+```
+$ chmod +x test.sh
+$ ./test.sh
+```
+
+输出样例
+
+```
+User Sarah L is more than 25 years old
+User Aaron Kili is more than 25 years old
+User John Doo is less than 25 years old.
+User Kili Seth is more than 25 years old
+```
+
+### 2. for 语句
+
+如果你想在一个循环中执行一些 awk 命令,那么 `for 语句`为你提供一个做这个的合适方式,格式如下:
+
+```
+for ( 计数器的初始化 ; 测试条件 ; 计数器增加 ){
+      动作
+}
+```
+
+这里,该方法是通过一个计数器来控制循环执行来定义的,首先你需要初始化这个计数器,然后针对测试条件运行它,如果它为真,执行这些动作并最终增加这个计数器。当计数器不满足条件时,循环终止。
+
+在我们想要打印数字 0 到 10 时,以下 awk 命令显示 for 语句是如何工作的:
+
+```
+$ awk 'BEGIN{ for(counter=0;counter<=10;counter++){ print counter} }'
+```
+
+输出样例
+
+```
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+```
+
+### 3. while 语句
+
+while 语句的传统语法如下:
+
+```
+while ( 条件 ) {
+          动作
+}
+```
+
+这个`条件`是一个 awk 表达式而`动作`是当条件为真时被执行的 awk 命令。
+
+下面是一个说明使用 `while` 语句来打印数字 0 到 10 的脚本:
+
+```
+#!/bin/bash
+awk ' BEGIN{ counter=0;
+
+        while(counter<=10){
+              print counter;
+              counter+=1;
+
+        }
+}'
+```
+
+保存文件并使脚本可执行,然后运行它:
+
+```
+$ chmod +x test.sh
+$ ./test.sh
+```
+
+输出样例
+
+```
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+```
+
+### 4. do while 语句
+
+它是上文中 `while` 语句的一个变型,具有以下语法:
+
+```
+do {
+     动作
+}
+ while (条件)
+```
+
+这轻微的区别在于,在 `do while` 语句下,awk 的命令在求值条件之前执行。使用上文 `while` 语句的例子,我们可以通过按如下所述修改 test.sh 脚本中的 awk 命令来说明 `do while` 语句的用法:
+
+```
+#!/bin/bash
+
+awk ' BEGIN{ counter=0;
+        do{
+            print counter;
+            counter+=1;
+        }
+        while (counter<=10)
+}'
+```
+
+修改脚本之后,保存文件并退出。按如下方式使脚本可执行并执行它:
+
+```
+$ chmod +x test.sh
+$ ./test.sh
+```
+
+输出样例
+
+```
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+```
+
+### 总结
+
+这不是关于 awk 的流程控制语句的一个全面的指南,正如我早先提到的,在 awk 里还有其他几个流程控制语句。
+
+尽管如此,awk 系列的这一部分使应该你明白了一个明确的基于某些条件控制的 awk 命令是如何执行的基本概念。
+
+你还可以了解其余更多的流程控制语句以获得更多关于该主题的理解。最后,在 awk 的系列下一节,我们将进入编写 awk 脚本。
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/use-flow-control-statements-with-awk-command/
+作者:[Aaron Kili][a]
+译者:[robot527](https://github.com/robot527)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux 中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
+[1]: https://linux.cn/article-7609-1.html
\ No newline at end of file
diff --git a/published/Part 2 - How to Use Awk to Print Fields and Columns in File.md b/published/awk/Part 2 - How to Use Awk to Print Fields and Columns in File.md
similarity index 100%
rename from published/Part 2 - How to Use Awk to Print Fields and Columns in File.md
rename to published/awk/Part 2 - How to Use Awk to Print Fields and Columns in File.md
diff --git a/translated/tech/awk/Part 3 - How to Use Awk to Filter Text or Strings Using Pattern Specific Actions.md b/published/awk/Part 3 - How to Use Awk to Filter Text or Strings Using Pattern Specific Actions.md
similarity index 72%
rename from translated/tech/awk/Part 3 - How to Use Awk to Filter Text or Strings Using Pattern Specific Actions.md
rename to published/awk/Part 3 - How to Use Awk to Filter Text or Strings Using Pattern Specific Actions.md
index ef91e93575..53e9aabd68 100644
--- a/translated/tech/awk/Part 3 - How to Use Awk to Filter Text or Strings Using Pattern Specific Actions.md	
+++ b/published/awk/Part 3 - How to Use Awk to Filter Text or Strings Using Pattern Specific Actions.md	
@@ -1,11 +1,11 @@
-如何使用 Awk 来筛选文本或字符串
+awk 系列:如何使用 awk 按模式筛选文本或字符串
 =========================================================================
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/04/Use-Awk-to-Filter-Text-or-Strings-Using-Pattern.png)
 
-作为 Awk 命令系列的第三部分,这次我们将看一看如何基于用户定义的特定模式来筛选文本或字符串。
+作为 awk 命令系列的第三部分,这次我们将看一看如何基于用户定义的特定模式来筛选文本或字符串。
 
-在筛选文本时,有时你可能想根据某个给定的条件或使用一个特定的可被匹配的模式,去标记某个文件或数行字符串中的某几行。使用 Awk 来完成这个任务是非常容易的,这也正是 Awk 中可能对你有所帮助的几个特色之一。
+在筛选文本时,有时你可能想根据某个给定的条件或使用一个可被匹配的特定模式,去标记某个文件或数行字符串中的某几行。使用 awk 来完成这个任务是非常容易的,这也正是 awk 中可能对你有所帮助的几个功能之一。
 
 让我们看一看下面这个例子,比方说你有一个写有你想要购买的食物的购物清单,其名称为 food_prices.list,它所含有的食物名称及相应的价格如下所示:
 
@@ -28,9 +28,10 @@ $ awk '/ *\$[2-9]\.[0-9][0-9] */ { print $1, $2, $3, $4, "*" ; } / *\$[0-1]\.[0-
 ```
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/04/Filter-and-Print-Text-Using-Awk.gif)
->打印出单价大于 $2 的项目
 
-从上面的输出你可以看到在含有 芒果(mangoes) 和 菠萝(pineapples) 的那行末尾都已经有了一个 `(*)` 标记。假如你检查它们的单价,你可以看到它们的单价的确超过了 $2 。
+*打印出单价大于 $2 的项目*
+
+从上面的输出你可以看到在含有 芒果(mangoes) 和菠萝(pineapples)的那行末尾都已经有了一个 `(*)` 标记。假如你检查它们的单价,你可以看到它们的单价的确超过了 $2 。
 
 在这个例子中,我们已经使用了两个模式:
 
@@ -39,33 +40,35 @@ $ awk '/ *\$[2-9]\.[0-9][0-9] */ { print $1, $2, $3, $4, "*" ; } / *\$[0-1]\.[0-
 
 上面的命令具体做了什么呢?这个文件有四个字段,当模式一匹配到含有食物单价大于 $2 的行时,它便会输出所有的四个字段并在该行末尾加上一个 `(*)` 符号来作为标记。
 
-第二个模式只是简单地输出其他含有食物单价小于 $2 的行,因为它们出现在输入文件 food_prices.list 中。
+第二个模式只是简单地输出其他含有食物单价小于 $2 的行,按照它们出现在输入文件 food_prices.list 中的样子。
 
 这样你就可以使用模式来筛选出那些价格超过 $2 的食物项目,尽管上面的输出还有些问题,带有 `(*)` 符号的那些行并没有像其他行那样被格式化输出,这使得输出显得不够清晰。
 
-我们在 Awk 系列的第二部分中也看到了同样的问题,但我们可以使用下面的两种方式来解决:
+我们在 awk 系列的第二部分中也看到了同样的问题,但我们可以使用下面的两种方式来解决:
 
-1. 可以像下面这样使用 printf 命令,但这样使用又长又无聊:
+1、可以像下面这样使用 printf 命令,但这样使用又长又无聊:
 
 ```
 $ awk '/ *\$[2-9]\.[0-9][0-9] */ { printf "%-10s %-10s %-10s %-10s\n", $1, $2, $3, $4 "*" ; } / *\$[0-1]\.[0-9][0-9] */ { printf "%-10s %-10s %-10s %-10s\n", $1, $2, $3, $4; }' food_prices.list 
 ```
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/04/Filter-and-Print-Items-Using-Awk-and-Printf.gif)
->使用 Awk 和 Printf 来筛选和输出项目
 
-2. 使用 `$0` 字段。Awk 使用变量 **0** 来存储整个输入行。对于上面的问题,这种方式非常方便,并且它还简单、快速:
+*使用 Awk 和 Printf 来筛选和输出项目*
+
+2、 使用 `$0` 字段。Awk 使用变量 **0** 来存储整个输入行。对于上面的问题,这种方式非常方便,并且它还简单、快速:
 
 ```
 $ awk '/ *\$[2-9]\.[0-9][0-9] */ { print $0 "*" ; } / *\$[0-1]\.[0-9][0-9] */ { print ; }' food_prices.list 
 ```
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/04/Filter-and-Print-Items-Using-Awk-and-Variable.gif)
->使用 Awk 和变量来筛选和输出项目
+
+*使用 Awk 和变量来筛选和输出项目*
 
 ### 结论
 
-这就是全部内容了,使用 Awk 命令你便可以通过几种简单的方法去利用模式匹配来筛选文本,帮助你在一个文件中对文本或字符串的某些行做标记。
+这就是全部内容了,使用 awk 命令你便可以通过几种简单的方法去利用模式匹配来筛选文本,帮助你在一个文件中对文本或字符串的某些行做标记。
 
 希望这篇文章对你有所帮助。记得阅读这个系列的下一部分,我们将关注在 awk 工具中使用比较运算符。
 
@@ -75,7 +78,7 @@ via: http://www.tecmint.com/awk-filter-text-or-string-using-patterns/
 
 作者:[Aaron Kili][a]
 译者:[FSSlc](https://github.com/FSSlc)
-校对:[校对者ID](https://github.com/校对者ID)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
 
diff --git a/translated/tech/awk/Part 4 - How to Use Comparison Operators with Awk in Linux.md b/published/awk/Part 4 - How to Use Comparison Operators with Awk in Linux.md
similarity index 61%
rename from translated/tech/awk/Part 4 - How to Use Comparison Operators with Awk in Linux.md
rename to published/awk/Part 4 - How to Use Comparison Operators with Awk in Linux.md
index 86649a52d5..4ed7d8ed02 100644
--- a/translated/tech/awk/Part 4 - How to Use Comparison Operators with Awk in Linux.md	
+++ b/published/awk/Part 4 - How to Use Comparison Operators with Awk in Linux.md	
@@ -1,15 +1,15 @@
-在 Linux 下如何使用 Awk 比较操作符
+awk 系列:如何使用 awk 比较操作符
 ===================================================
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/05/Use-Comparison-Operators-with-AWK.png)
 
-对于 Awk 命令的用户来说,处理一行文本中的数字或者字符串时,使用比较运算符来过滤文本和字符串是十分方便的。
+对于 使用 awk 命令的用户来说,处理一行文本中的数字或者字符串时,使用比较运算符来过滤文本和字符串是十分方便的。
 
-在 Awk 系列的此部分中,我们将探讨一下如何使用比较运算符来过滤文本或者字符串。如果你是程序员,那么你应该已经熟悉比较运算符;对于其它人,下面的部分将介绍比较运算符。
+在  awk 系列的此部分中,我们将探讨一下如何使用比较运算符来过滤文本或者字符串。如果你是程序员,那么你应该已经熟悉了比较运算符;对于其它人,下面的部分将介绍比较运算符。
 
-### Awk 中的比较运算符是什么?
+### awk 中的比较运算符是什么?
 
-Awk 中的比较运算符用于比较字符串和或者数值,包括以下类型:
+awk 中的比较运算符用于比较字符串和或者数值,包括以下类型:
 
 - `>` – 大于
 - `<` – 小于
@@ -17,12 +17,12 @@ Awk 中的比较运算符用于比较字符串和或者数值,包括以下类
 - `<=` – 小于等于
 - `==` – 等于
 - `!=` – 不等于
-- `some_value ~ / pattern/` – 如果some_value匹配模式pattern,则返回true
-- `some_value !~ / pattern/` – 如果some_value不匹配模式pattern,则返回true
+- `some_value ~ / pattern/` – 如果 some_value 匹配模式 pattern,则返回 true
+- `some_value !~ / pattern/` – 如果 some_value 不匹配模式 pattern,则返回 true
 
-现在我们通过例子来熟悉 Awk 中各种不同的比较运算符。
+现在我们通过例子来熟悉 awk 中各种不同的比较运算符。
 
-在这个例子中,我们有一个文件名为 food_list.txt 的文件,里面包括不同食物的购买列表。我想给食物数量小于或等于30的物品所在行的后面加上`(**)`
+在这个例子中,我们有一个文件名为 food_list.txt 的文件,里面包括不同食物的购买列表。我想给食物数量小于或等于 30 的物品所在行的后面加上`(**)`
 
 ```
 File – food_list.txt
@@ -38,7 +38,7 @@ No      Item_Name               Quantity        Price
 Awk 中使用比较运算符的通用语法如下:
 
 ```
-# expression { actions; }
+# 表达式 { 动作; }
 ```
 
 为了实现刚才的目的,执行下面的命令:
@@ -57,8 +57,8 @@ No	Item_Name`		Quantity	Price
 
 在刚才的例子中,发生如下两件重要的事情:
 
-- 第一个表达式 `{ action ; }` 组合, `$3 <= 30 { printf “%s\t%s\n”, $0,”**” ; }` 打印出数量小于等于30的行,并且在后面增加`(**)`。物品的数量是通过 `$3`这个域变量获得的。
-- 第二个表达式 `{ action ; }` 组合, `$3 > 30 { print $0 ;}` 原样输出数量小于等于 `30` 的行。
+- 第一个“表达式 {动作;}”组合中, `$3 <= 30 { printf “%s\t%s\n”, $0,”**” ; }` 打印出数量小于等于30的行,并且在后面增加`(**)`。物品的数量是通过 `$3` 这个域变量获得的。
+- 第二个“表达式 {动作;}”组合中, `$3 > 30 { print $0 ;}` 原样输出数量小于等于 `30` 的行。
 
 再举一个例子:
 
@@ -78,9 +78,9 @@ No	  Item_Name		Quantity	Price
 
 ### 总结
 
-这是一篇对 Awk 中的比较运算符介绍性的指引,因此你需要尝试其他选项,发现更多使用方法。
+这是一篇对 awk 中的比较运算符介绍性的指引,因此你需要尝试其他选项,发现更多使用方法。
 
-如果你遇到或者想到任何问题,请在下面评论区留下评论。请记得阅读 Awk 系列下一部分的文章,那里我将介绍组合表达式。
+如果你遇到或者想到任何问题,请在下面评论区留下评论。请记得阅读  awk 系列下一部分的文章,那里我将介绍组合表达式。
 
 --------------------------------------------------------------------------------
 
@@ -88,7 +88,7 @@ via: http://www.tecmint.com/comparison-operators-in-awk/
 
 作者:[Aaron Kili][a]
 译者:[chunyang-wen](https://github.com/chunyang-wen)
-校对:[校对者ID](https://github.com/校对者ID)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
 
diff --git a/translated/tech/awk/Part 5 - How to Use Compound Expressions with Awk in Linux.md b/published/awk/Part 5 - How to Use Compound Expressions with Awk in Linux.md
similarity index 65%
rename from translated/tech/awk/Part 5 - How to Use Compound Expressions with Awk in Linux.md
rename to published/awk/Part 5 - How to Use Compound Expressions with Awk in Linux.md
index ed1ba4aa7c..ebe288b020 100644
--- a/translated/tech/awk/Part 5 - How to Use Compound Expressions with Awk in Linux.md	
+++ b/published/awk/Part 5 - How to Use Compound Expressions with Awk in Linux.md	
@@ -1,31 +1,31 @@
-如何使用 Awk 复合表达式
+awk 系列:如何使用 awk 复合表达式
 ====================================================
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/05/Use-Compound-Expressions-with-Awk.png)
 
-一直以来在查对条件是否匹配时,我们寻求的都是简单的表达式。那如果你想用超过一个表达式,来查对特定的条件呢?
+一直以来在查对条件是否匹配时,我们使用的都是简单的表达式。那如果你想用超过一个表达式来查对特定的条件呢?
 
 本文,我们将看看如何在过滤文本和字符串时,结合多个表达式,即复合表达式,用以查对条件。
 
-Awk 的复合表达式可由表示`与`的组合操作符 `&&`  和表示`或`的 `||` 构成。
+awk 的复合表达式可由表示“与”的组合操作符 `&&`  和表示“或”的 `||` 构成。
 
 复合表达式的常规写法如下:
 
 ```
-( first_expression ) && ( second_expression )
+( 第一个表达式 ) && ( 第二个表达式 )
 ```
 
-为了保证整个表达式的正确,在这里必须确保 `first_expression` 和 `second_expression` 是正确的。
+这里只有当“第一个表达式” 和“第二个表达式”都是真值时整个表达式才为真。
 
 ```
-( first_expression ) || ( second_expression) 
+( 第一个表达式 ) || ( 第二个表达式) 
 ```
 
-为了保证整个表达式的正确,在这里必须确保 `first_expression` 或 `second_expression` 是正确的。
+这里只要“第一个表达式” 为真或“第二个表达式”为真,整个表达式就为真。
 
 **注意**:切记要加括号。
 
-表达式可以由比较操作符构成,具体可查看 awk 系列的第四部分。
+表达式可以由比较操作符构成,具体可查看[ awk 系列的第四节][1]。
 
 现在让我们通过一个例子来加深理解:
 
@@ -43,7 +43,7 @@ No      Name                                    Price           Type
 7       Nano_Prowler_Mini_Drone                 $36.99          Tech 
 ```
 
-我们只想打印出价格超过 $20 的物品,并在其中种类为 “Tech” 的物品的行末用 (**) 打上标记。
+我们只想打印出价格超过 $20 且其种类为 “Tech” 的物品,在其行末用 (*) 打上标记。
 
 我们将要执行以下命令。
 
@@ -56,13 +56,13 @@ No      Name                                    Price           Type
 
 此例,在复合表达式中我们使用了两个表达式:
 
-- 表达式 1:`($3 ~ /^\$[2-9][0-9]*\.[0-9][0-9]$/)` ;查找交易价格超过 `$20` 的行,即只有当 `$3` 也就是价格满足 `/^\$[2-9][0-9]*\.[0-9][0-9]$/` 时值才为 true。
-- 表达式 2:`($4 == “Tech”)` ;查找是否有种类为 “`Tech`”的交易,即只有当 `$4` 等于 “`Tech`” 时值才为 true。
-切记,只有当 `&&` 操作符的两端状态,也就是两个表达式都是 true 的情况下,这一行才会被打上 `(**)` 标志。
+- 表达式 1:`($3 ~ /^\$[2-9][0-9]*\.[0-9][0-9]$/)` ;查找交易价格超过 `$20` 的行,即只有当 `$3` 也就是价格满足 `/^\$[2-9][0-9]*\.[0-9][0-9]$/` 时值才为真值。
+- 表达式 2:`($4 == “Tech”)` ;查找是否有种类为 “`Tech`”的交易,即只有当 `$4` 等于 “`Tech`” 时值才为真值。
+切记,只有当 `&&` 操作符的两端状态,也就是两个表达式都是真值的情况下,这一行才会被打上 `(*)` 标志。
 
 ### 总结
 
-有些时候为了匹配你的真实想法,就不得不用到复合表达式。当你掌握了比较和复合表达式操作符的用法之后,在难的文本或字符串过滤条件也能轻松解决。
+有些时候为了真正符合你的需求,就不得不用到复合表达式。当你掌握了比较和复合表达式操作符的用法之后,复杂的文本或字符串过滤条件也能轻松解决。
 
 希望本向导对你有所帮助,如果你有任何问题或者补充,可以在下方发表评论,你的问题将会得到相应的解释。
 
@@ -71,9 +71,10 @@ No      Name                                    Price           Type
 via: http://www.tecmint.com/combine-multiple-expressions-in-awk/
 
 作者:[Aaron Kili][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
+译者:[martin2011qi](https://github.com/martin2011qi)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
 
 [a]: http://www.tecmint.com/author/aaronkili/
+[1]: https://linux.cn/article-7602-1.html
\ No newline at end of file
diff --git a/translated/tech/awk/Part 6 - How to Use ‘next’ Command with Awk in Linux.md b/published/awk/Part 6 - How to Use ‘next’ Command with Awk in Linux.md
similarity index 59%
rename from translated/tech/awk/Part 6 - How to Use ‘next’ Command with Awk in Linux.md
rename to published/awk/Part 6 - How to Use ‘next’ Command with Awk in Linux.md
index 8ead60bd44..c2d277fc17 100644
--- a/translated/tech/awk/Part 6 - How to Use ‘next’ Command with Awk in Linux.md	
+++ b/published/awk/Part 6 - How to Use ‘next’ Command with Awk in Linux.md	
@@ -1,13 +1,13 @@
-
-如何使用AWK的‘next’命令
+awk 系列:如何使用 awk 的 ‘next’ 命令
 =============================================
 
 ![](http://www.tecmint.com/wp-content/uploads/2016/06/Use-next-Command-with-Awk-in-Linux.png)
 
-在Awk 系列的第六章, 我们来看一下`next`命令 ,它告诉 Awk 跳过你所提供的表达式而是读取下一个输入行.
-`next` 命令帮助你阻止运行多余的步骤.
+在 awk 系列的第六节,我们来看一下`next`命令 ,它告诉 awk 跳过你所提供的所有剩下的模式和表达式,直接处理下一个输入行。
 
-要明白它是如何工作的,  让我们来分析一下food_list.txt它看起来像这样 :
+`next` 命令帮助你阻止运行命令执行过程中多余的步骤。
+
+要明白它是如何工作的,  让我们来分析一下 food_list.txt 它看起来像这样:
 
 ```
 Food List Items
@@ -20,7 +20,7 @@ No      Item_Name               Price           Quantity
 6       Bananas                 $3.45              30
 ```
 
-运行下面的命令,它将在每个食物数量小于或者等于20的行后面标一个星号:
+运行下面的命令,它将在每个食物数量小于或者等于 20 的行后面标一个星号:
 
 ```
 # awk '$4 <= 20 { printf "%s\t%s\n", $0,"*" ; } $4 > 20 { print $0 ;} ' food_list.txt 
@@ -36,14 +36,14 @@ No	Item_Name		Price		Quantity
 
 上面的命令实际运行如下:
 
-- 首先, 它用`$4 <= 20`表达式检查每个输入行的第四列是否小于或者等于20,如果满足条件, 它将在末尾打一个星号 `(*)` .
-- 接着, 它用`$4 > 20`表达式检查每个输入行的第四列是否大于20,如果满足条件,显示出来.
+- 首先,它用`$4 <= 20`表达式检查每个输入行的第四列(数量(Quantity))是否小于或者等于 20,如果满足条件,它将在末尾打一个星号 `(*)`。
+- 接着,它用`$4 > 20`表达式检查每个输入行的第四列是否大于20,如果满足条件,显示出来。
 
 但是这里有一个问题, 当第一个表达式用`{ printf "%s\t%s\n", $0,"**" ; }`命令进行标注的时候在同样的步骤第二个表达式也进行了判断这样就浪费了时间.
 
-因此当我们已经用第一个表达式打印标志行的时候就不在需要用第二个表达式`$4 > 20`再次打印.
+因此当我们已经用第一个表达式打印标志行的时候就不再需要用第二个表达式`$4 > 20`再次打印。
 
-要处理这个问题, 我们需要用到`next` 命令:
+要处理这个问题, 我们需要用到`next` 命令:
 
 ```
 # awk '$4 <= 20 { printf "%s\t%s\n", $0,"*" ; next; } $4 > 20 { print $0 ;} ' food_list.txt
@@ -57,11 +57,11 @@ No	Item_Name		Price		           Quantity
 6	  Bananas	    $3.45              30
 ```
 
-当输入行用`$4 <= 20` `{ printf "%s\t%s\n", $0,"*" ; next ; }`命令打印以后,`next`命令 将跳过第二个`$4 > 20` `{ print $0 ;}`表达式, 继续判断下一个输入行,而不是浪费时间继续判断一下是不是当前输入行还大于20.
+当输入行用`$4 <= 20` `{ printf "%s\t%s\n", $0,"*" ; next ; }`命令打印以后,`next`命令将跳过第二个`$4 > 20` `{ print $0 ;}`表达式,继续判断下一个输入行,而不是浪费时间继续判断一下是不是当前输入行还大于 20。
 
-next命令在编写高效的命令脚本时候是非常重要的, 它可以很大的提高脚本速度. 下面我们准备来学习Awk的下一个系列了.
+`next`命令在编写高效的命令脚本时候是非常重要的,它可以提高脚本速度。本系列的下一部分我们将来学习如何使用 awk 来处理标准输入(STDIN)。
 
-希望这篇文章对你有帮助,你可以给我们留言.
+希望这篇文章对你有帮助,你可以给我们留言。
 
 --------------------------------------------------------------------------------
 
@@ -69,7 +69,7 @@ via: http://www.tecmint.com/use-next-command-with-awk-in-linux/
 
 作者:[Aaron Kili][a]
 译者:[kokialoves](https://github.com/kokialoves)
-校对:[校对者ID](https://github.com/校对者ID)
+校对:[wxy](https://github.com/wxy)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
 
diff --git a/published/awk/Part 7 - How to Read Awk Input from STDIN in Linux.md b/published/awk/Part 7 - How to Read Awk Input from STDIN in Linux.md
new file mode 100644
index 0000000000..2730691459
--- /dev/null
+++ b/published/awk/Part 7 - How to Read Awk Input from STDIN in Linux.md	
@@ -0,0 +1,75 @@
+awk 系列:awk 怎么从标准输入(STDIN)读取输入
+============================================
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Read-Awk-Input-from-STDIN.png)
+
+在 awk 系列的前几节,我们看到大多数操作都是从一个文件或多个文件读取输入,或者你想要把标准输入作为 awk 的输入。
+
+在 awk 系列的第七节中,我们将会看到几个例子,你可以筛选其他命令的输出代替从一个文件读取输入作为 awk 的输入。
+
+我们首先从使用 [dir 命令][1]开始,它类似于 [ls 命令][2],在第一个例子下面,我们使用 `dir -l` 命令的输出作为 awk 命令的输入,这样就可以打印出文件拥有者的用户名,所属组组名以及在当前路径下他/她拥有的文件。
+
+```
+# dir -l | awk '{print $3, $4, $9;}'
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/List-Files-Owned-By-User-in-Directory.png)
+
+*列出当前路径下的用户文件*
+
+
+再来看另一个例子,我们[使用 awk 表达式][3] ,在这里,我们想要在 awk 命令里使用一个表达式筛选出字符串来打印出属于 root 用户的文件。命令如下:
+
+```
+# dir -l | awk '$3=="root" {print $1,$3,$4, $9;} '
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/List-Files-Owned-by-Root-User.png)
+
+*列出 root 用户的文件*
+
+上面的命令包含了 `(==)` 来进行比较操作,这帮助我们在当前路径下筛选出 root 用户的文件。这是通过使用 `$3=="root"` 表达式实现的。
+
+让我们再看另一个例子,我们使用一个 [awk 比较运算符][4] 来匹配一个确定的字符串。
+
+这里,我们使用了 [cat 命令][5] 来浏览文件名为 tecmint_deals.txt 的文件内容,并且我们想要仅仅查看有字符串 Tech 的部分,所以我们会运行下列命令:
+
+```
+# cat tecmint_deals.txt
+# cat tecmint_deals.txt | awk '$4 ~ /tech/{print}'
+# cat tecmint_deals.txt | awk '$4 ~ /Tech/{print}'
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/06/Use-Comparison-Operator-to-Match-String.png)
+
+*用 Awk 比较运算符匹配字符串*
+
+在上面的例子中,我们已经用了参数为 `~ /匹配字符/` 的比较操作,但是上面的两个命令给我们展示了一些很重要的问题。
+
+当你运行带有 tech 字符串的命令时终端没有输出,因为在文件中没有 tech 这种字符串,但是运行带有 Tech 字符串的命令,你却会得到包含 Tech 的输出。
+
+所以你应该在进行这种比较操作的时候时刻注意这种问题,正如我们在上面看到的那样,awk 对大小写很敏感。
+
+你总是可以使用另一个命令的输出作为 awk 命令的输入来代替从一个文件中读取输入,这就像我们在上面看到的那样简单。
+
+希望这些例子足够简单到可以使你理解 awk 的用法,如果你有任何问题,你可以在下面的评论区提问,记得查看 awk 系列接下来的章节内容,我们将关注 awk 的一些功能,比如变量,数字表达式以及赋值运算符。
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/read-awk-input-from-stdin-in-linux/
+
+作者:[Aaron Kili][a]
+译者:[vim-kakali](https://github.com/vim-kakali)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
+[1]: http://www.tecmint.com/linux-dir-command-usage-with-examples/
+[2]: http://www.tecmint.com/15-basic-ls-command-examples-in-linux/
+[3]: https://linux.cn/article-7599-1.html
+[4]: https://linux.cn/article-7602-1.html
+[5]: http://www.tecmint.com/13-basic-cat-command-examples-in-linux/
+
+
+
diff --git a/published/awk/Part 8 - Learn How to Use Awk Variables, Numeric Expressions and Assignment Operators.md b/published/awk/Part 8 - Learn How to Use Awk Variables, Numeric Expressions and Assignment Operators.md
new file mode 100644
index 0000000000..1f44f3c37a
--- /dev/null
+++ b/published/awk/Part 8 - Learn How to Use Awk Variables, Numeric Expressions and Assignment Operators.md	
@@ -0,0 +1,264 @@
+awk 系列:怎样使用 awk 变量、数值表达式以及赋值运算符
+=======================================================================================
+
+我觉得 [awk 系列][1] 将会越来越好,在本系列的前七节我们讨论了在 Linux 中处理文件和筛选字符串所需要的一些 awk 命令基础。
+
+在这一部分,我们将会进入 awk 更高级的部分,使用 awk 处理更复杂的文本和进行字符串过滤操作。因此,我们将会讲到 Awk 的一些特性,诸如变量、数值表达式和赋值运算符。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Learn-Awk-Variables-Numeric-Expressions-Assignment-Operators.png)
+
+*学习 Awk 变量,数值表达式和赋值运算符*
+
+你可能已经在很多编程语言中接触过它们,比如 shell,C,Python 等;这些概念在理解上和这些语言没有什么不同,所以在这一小节中你不用担心很难理解,我们将会简短的提及常用的一些 awk 特性。
+
+这一小节可能是 awk 命令里最容易理解的部分,所以放松点,我们开始吧。
+
+### 1. Awk 变量
+
+在很多编程语言中,变量就是一个存储了值的占位符,当你在程序中新建一个变量的时候,程序一运行就会在内存中创建一些空间,你为变量赋的值会存储在这些内存空间上。
+
+你可以像下面这样定义 shell 变量一样定义 Awk 变量:
+
+```
+variable_name=value 
+```
+
+上面的语法:
+
+- `variable_name`: 为定义的变量的名字
+- `value`: 为变量赋的值
+
+再看下面的一些例子:
+
+```
+computer_name=”tecmint.com”
+port_no=”22”
+email=”admin@tecmint.com”
+server=computer_name
+```
+
+观察上面的简单的例子,在定义第一个变量的时候,值 'tecmint.com' 被赋给了 'computer_name' 变量。
+
+此外,值 22 也被赋给了 port\_no 变量,把一个变量的值赋给另一个变量也是可以的,在最后的例子中我们把变量 computer\_name 的值赋给了变量 server。
+
+你可以看看[本系列的第 2 节][2]中提到的字段编辑,我们讨论了 awk 怎样将输入的行分隔为若干字段并且使用标准字段访问操作符 `$` 来访问拆分出来的不同字段。我们也可以像下面这样使用变量为字段赋值。
+
+```
+first_name=$2
+second_name=$3
+```
+
+在上面的例子中,变量 first\_name 的值设置为第二个字段,second\_name 的值设置为第三个字段。
+
+再举个例子,有一个名为 names.txt 的文件,这个文件包含了一个应用程序的用户列表,这个用户列表包含了用户的名和姓以及性别。可以使用 [cat 命令][3] 查看文件内容:
+
+```
+$ cat names.txt
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/List-File-Content-Using-cat-Command.png)
+
+*使用 cat 命令查看列表文件内容*
+
+然后,我们也可以使用下面的 awk 命令把列表中第一个用户的第一个和第二个名字分别存储到变量 first\_name 和 second\_name 上:
+
+```
+$ awk '/Aaron/{ first_name=$2 ; second_name=$3 ; print first_name, second_name ; }' names.txt
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Store-Variables-Using-Awk-Command.png)
+
+*使用 Awk 命令为变量赋值*
+
+再看一个例子,当你在终端运行 'uname -a' 时,它可以打印出所有的系统信息。
+
+第二个字段包含了你的主机名,因此,我们可以像下面这样把它赋给一个叫做 hostname 的变量并且用 awk 打印出来。
+
+```
+$ uname -a
+$ uname -a | awk '{hostname=$2 ; print hostname ; }' 
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Store-Command-Output-to-Variable-Using-Awk.png)
+
+*使用 Awk 把命令的输出赋给变量*
+
+### 2. 数值表达式
+
+在 Awk 中,数值表达式使用下面的数值运算符组成:
+
+- `*` : 乘法运算符
+- `+` : 加法运算符
+- `/` : 除法运算符
+- `-` : 减法运算符
+- `%` : 取模运算符
+- `^` : 指数运算符
+
+数值表达式的语法是:
+
+```
+$ operand1 operator operand2
+```
+
+上面的 operand1 和 operand2 可以是数值和变量,运算符可以是上面列出的任意一种。
+
+下面是一些展示怎样使用数值表达式的例子:
+
+```
+counter=0
+num1=5
+num2=10
+num3=num2-num1
+counter=counter+1
+```
+
+
+要理解 Awk 中数值表达式的用法,我们可以看看下面的例子,文件 domians.txt 里包括了所有属于 Tecmint 的域名。
+
+```
+news.tecmint.com
+tecmint.com
+linuxsay.com
+windows.tecmint.com
+tecmint.com
+news.tecmint.com
+tecmint.com
+linuxsay.com
+tecmint.com
+news.tecmint.com
+tecmint.com
+linuxsay.com
+windows.tecmint.com
+tecmint.com
+```
+
+可以使用下面的命令查看文件的内容:
+
+```
+$ cat domains.txt
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/View-Contents-of-File.png)
+
+*查看文件内容*
+
+
+如果想要计算出域名 tecmint.com 在文件中出现的次数,我们就可以通过写一个简单的脚本实现这个功能:
+
+```
+#!/bin/bash
+for file in $@; do
+if [ -f $file ] ; then
+#print out filename
+echo "File is: $file"
+#print a number incrementally for every line containing tecmint.com 
+awk  '/^tecmint.com/ { counter=counter+1 ; printf "%s\n", counter ; }'   $file
+else
+#print error info incase input is not a file
+echo "$file is not a file, please specify a file." >&2 && exit 1
+fi
+done
+#terminate script with exit code 0 in case of successful execution 
+exit 0
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Shell-Script-to-Count-a-String-in-File.png)
+
+*计算一个字符串或文本在文件中出现次数的 shell 脚本*
+
+
+写完脚本后保存并赋予执行权限,当我们使用文件运行脚本的时候,文件 domains.txt 作为脚本的输入,我们会得到下面的输出:
+
+```
+$ ./script.sh  ~/domains.txt
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Script-To-Count-String.png)
+
+*计算字符串或文本出现次数的脚本*
+
+从脚本执行后的输出中,可以看到在文件 domains.txt 中包含域名 tecmint.com 的地方有 6 行,你可以自己计算进行验证。
+
+### 3. 赋值操作符
+
+我们要说的最后的 Awk 特性是赋值操作符,下面列出的只是 awk 中的部分赋值运算符:
+
+- `*=` : 乘法赋值操作符
+- `+=` : 加法赋值操作符
+- `/=` : 除法赋值操作符
+- `-=` : 减法赋值操作符
+- `%=` : 取模赋值操作符
+- `^=` : 指数赋值操作符
+
+下面是 Awk 中最简单的一个赋值操作的语法:
+
+```
+$ variable_name=variable_name operator operand
+```
+
+
+例子:
+
+```
+counter=0
+counter=counter+1
+num=20
+num=num-1
+```
+
+你可以使用在 awk 中使用上面的赋值操作符使命令更简短,从先前的例子中,我们可以使用下面这种格式进行赋值操作:
+
+```
+variable_name operator=operand
+counter=0
+counter+=1
+num=20
+num-=1
+```
+
+因此,我们可以在 shell 脚本中改变 awk 命令,使用上面提到的 += 操作符:
+
+```
+#!/bin/bash
+for file in $@; do
+if [ -f $file ] ; then
+#print out filename
+echo "File is: $file"
+#print a number incrementally for every line containing tecmint.com 
+awk  '/^tecmint.com/ { counter+=1 ; printf  "%s\n",  counter ; }'   $file
+else
+#print error info incase input is not a file
+echo "$file is not a file, please specify a file." >&2 && exit 1
+fi
+done
+#terminate script with exit code 0 in case of successful execution 
+exit 0
+```
+
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Alter-Shell-Script.png)
+
+*修改了的 shell 脚本*
+
+
+在 [awk 系列][4] 的这一部分,我们讨论了一些有用的 awk 特性,有变量,使用数值表达式和赋值运算符,还有一些使用它们的实例。
+
+这些概念和其他的编程语言没有任何不同,但是可能在 awk 中有一些意义上的区别。
+
+在本系列的第 9 节,我们会学习更多的 awk 特性,比如特殊格式: BEGIN 和 END。请继续关注。
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/learn-awk-variables-numeric-expressions-and-assignment-operators/
+
+作者:[Aaron Kili][a]
+译者:[vim-kakali](https://github.com/vim-kakali)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
+[1]: https://linux.cn/article-7586-1.html
+[2]: https://linux.cn/article-7587-1.html
+[3]: http://www.tecmint.com/13-basic-cat-command-examples-in-linux/
+[4]: https://linux.cn/article-7586-1.html
diff --git a/published/awk/Part 9 - Learn How to Use Awk Special Patterns begin and end.md b/published/awk/Part 9 - Learn How to Use Awk Special Patterns begin and end.md
new file mode 100644
index 0000000000..5791ffb0f9
--- /dev/null
+++ b/published/awk/Part 9 - Learn How to Use Awk Special Patterns begin and end.md	
@@ -0,0 +1,168 @@
+awk 系列:如何使用 awk 的特殊模式 BEGIN 和 END
+===============================================================
+在 awk 系列的第八节,我们介绍了一些强大的 awk 命令功能,它们是变量、数字表达式和赋值运算符。
+
+本节我们将学习更多的 awk 功能,即 awk 的特殊模式:`BEGIN` 和 `END`。
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Learn-Awk-Patterns-BEGIN-and-END.png)
+> 学习 awk 的模式 BEGIN 和 END
+
+随着我们逐渐展开,并探索出更多构建复杂 awk 操作的方法,将会证明 awk 的这些特殊功能的是多么强大。
+
+开始前,先让我们回顾一下 awk 系列的介绍,记得当我们开始这个系列时,我就指出 awk 指令的通用语法是这样的:
+
+```
+# awk 'script' filenames  
+```
+
+在上述语法中,awk 脚本拥有这样的形式:
+
+```
+/pattern/ { actions } 
+```
+
+你通常会发现脚本中的模式(`/pattern/`)是一个正则表达式,不过你也可以将模式使用特殊模式 `BEGIN` 和 `END`。因此,我们也能按照下面的形式编写一条 awk 命令:
+
+```
+awk '
+BEGIN { actions } 
+/pattern/ { actions }
+/pattern/ { actions }
+……….
+END { actions } 
+' filenames  
+```
+
+假如你在 awk 脚本中使用了特殊模式:`BEGIN` 和 `END`,以下则是它们对应的含义:
+
+- `BEGIN` 模式:是指 awk 将在读取任何输入行之前立即执行 `BEGIN` 中指定的动作。
+- `END` 模式:是指 awk 将在它正式退出前执行 `END` 中指定的动作。
+
+含有这些特殊模式的 awk 命令脚本的执行流程如下:
+
+1. 当在脚本中使用了 `BEGIN` 模式,则 `BEGIN` 中所有的动作都会在读取任何输入行之前执行。
+2. 然后,读入一个输入行并解析成不同的段。
+3. 接下来,每一条指定的非特殊模式都会和输入行进行比较匹配,当匹配成功后,就会执行模式对应的动作。对所有你指定的模式重复此执行该步骤。
+4. 再接下来,对于所有输入行重复执行步骤 2 和 步骤 3。
+5. 当读取并处理完所有输入行后,假如你指定了 `END` 模式,那么将会执行相应的动作。
+
+当你使用特殊模式时,想要在 awk 操作中获得最好的结果,你应当记住上面的执行顺序。
+
+为了便于理解,让我们使用第八节的例子进行演示,那个例子是关于 Tecmint 拥有的域名列表,并保存在一个叫做 domains.txt 的文件中。
+
+```
+news.tecmint.com
+tecmint.com
+linuxsay.com
+windows.tecmint.com
+tecmint.com
+news.tecmint.com
+tecmint.com
+linuxsay.com
+tecmint.com
+news.tecmint.com
+tecmint.com
+linuxsay.com
+windows.tecmint.com
+tecmint.com
+```
+
+```
+$ cat ~/domains.txt
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/View-Contents-of-File.png)
+
+*查看文件内容*
+
+在这个例子中,我们希望统计出 domains.txt 文件中域名 `tecmint.com` 出现的次数。所以,我们编写了一个简单的 shell 脚本帮助我们完成任务,它使用了变量、数学表达式和赋值运算符的思想,脚本内容如下:
+
+```
+#!/bin/bash
+for file in $@; do
+if [ -f $file ] ; then
+### 输出文件名
+echo "File is: $file"
+### 输出一个递增的数字记录包含 tecmint.com 的行数
+awk '/^tecmint.com/ { counter+=1 ; printf "%s\n", counter ; }' $file
+else
+### 若输入不是文件,则输出错误信息
+echo "$file 不是一个文件,请指定一个文件。" >&2 && exit 1
+fi
+done
+### 成功执行后使用退出代码 0 终止脚本
+exit 0
+```
+
+现在让我们像下面这样在上述脚本的 awk 命令中应用这两个特殊模式:`BEGIN` 和 `END`:
+
+我们应当把脚本:
+
+```
+awk '/^tecmint.com/ { counter+=1 ; printf "%s\n", counter ; }' $file
+```
+
+改成:
+
+```
+awk ' BEGIN {  print "文件中出现 tecmint.com 的次数是:" ; }
+/^tecmint.com/ {  counter+=1  ;  }
+END {  printf "%s\n",  counter  ; } 
+'  $file
+```
+
+在修改了 awk 命令之后,现在完整的 shell 脚本就像下面这样:
+
+```
+#!/bin/bash
+for file in $@; do
+if [ -f $file ] ; then
+### 输出文件名
+echo "File is: $file"
+### 输出文件中 tecmint.com 出现的总次数
+awk ' BEGIN {  print "文件中出现 tecmint.com 的次数是:" ; }
+/^tecmint.com/ {  counter+=1  ;  }
+END {  printf "%s\n",  counter  ; } 
+'  $file
+else
+### 若输入不是文件,则输出错误信息
+echo "$file 不是一个文件,请指定一个文件。" >&2 && exit 1
+fi
+done
+### 成功执行后使用退出代码 0 终止脚本
+exit 0
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Awk-BEGIN-and-END-Patterns.png)
+
+*awk 模式 BEGIN 和 END*
+
+当我们运行上面的脚本时,它会首先输出 domains.txt 文件的位置,然后执行 awk 命令脚本,该命令脚本中的特殊模式 `BEGIN` 将会在从文件读取任何行之前帮助我们输出这样的消息“`文件中出现 tecmint.com 的次数是:`”。
+
+接下来,我们的模式 `/^tecmint.com/` 会在每个输入行中进行比较,对应的动作 `{ counter+=1 ; }` 会在每个匹配成功的行上执行,它会统计出 `tecmint.com` 在文件中出现的次数。
+
+最终,`END` 模式将会输出域名 `tecmint.com` 在文件中出现的总次数。
+
+```
+$ ./script.sh ~/domains.txt 
+```
+![](http://www.tecmint.com/wp-content/uploads/2016/07/Script-to-Count-Number-of-Times-String-Appears.png)
+
+*用于统计字符串出现次数的脚本*
+
+最后总结一下,我们在本节中演示了更多的 awk 功能,并学习了特殊模式 `BEGIN` 和 `END` 的概念。
+
+正如我之前所言,这些 awk 功能将会帮助我们构建出更复杂的文本过滤操作。第十节将会给出更多的 awk 功能,我们将会学习 awk 内置变量的思想,所以,请继续保持关注。
+
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/learn-use-awk-special-patterns-begin-and-end/
+
+作者:[Aaron Kili][a]
+译者:[ChrisLeeGit](https://github.com/chrisleegit)
+校对:[wxy](https://github.com/wxy)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
diff --git a/sources/talk/20150806 Torvalds 2.0--Patricia Torvalds on computing college feminism and increasing diversity in tech.md b/sources/talk/20150806 Torvalds 2.0--Patricia Torvalds on computing college feminism and increasing diversity in tech.md
deleted file mode 100644
index 57faa7998f..0000000000
--- a/sources/talk/20150806 Torvalds 2.0--Patricia Torvalds on computing college feminism and increasing diversity in tech.md	
+++ /dev/null
@@ -1,82 +0,0 @@
-Torvalds 2.0: Patricia Torvalds on computing, college, feminism, and increasing diversity in tech
-================================================================================
-
-![Image by : Photo by Becky Svartström. Modified by Opensource.com. CC BY-SA 4.0](http://opensource.com/sites/default/files/styles/image-full-size/public/images/life/osdc-lead-patriciatorvalds.png)
-Image by : Photo by Becky Svartström. Modified by Opensource.com. [CC BY-SA 4.0][1]
-
-Patricia Torvalds isn't the Torvalds name that pops up in Linux and open source circles. Yet.
-
-![](http://opensource.com/sites/default/files/images/life-uploads/ptorvalds.png)
-
-At 18, Patricia is a feminist with a growing list of tech achievements, open source industry experience, and her sights set on diving into her freshman year of college at Duke University's Pratt School of Engineering. She works for [Puppet Labs][2] in Portland, Oregon, as an intern, but soon she'll head to Durham, North Carolina, to start the fall semester of college.
-
-In this exclusive interview, Patricia explains what got her interested in computer science and engineering (spoiler alert: it wasn't her father), what her high school did "right" with teaching tech, the important role feminism plays in her life, and her thoughts on the lack of diversity in technology.
-
-![](http://opensource.com/sites/default/files/images/life/Interview%20banner%20Q%26A.png)
-
-### What made you interested in studying computer science and engineering? ###
-
-My interest in tech really grew throughout high school. I wanted to go into biology for a while, until around my sophomore year. I had a web design internship at the Portland VA after my sophomore year. And I took an engineering class called Exploratory Ventures, which sent an ROV into the Pacific ocean late in my sophomore year, but the turning point was probably when I was named a regional winner and national runner up for the [NCWIT Aspirations in Computing][3] award halfway through my junior year.
-
-The award made me feel validated in my interest, of course, but I think the most important part of it was getting to join a Facebook group for all the award winners. The girls who have won the award are absolutely incredible and so supportive of each other. I was definitely interested in computer science before I won the award, because of my work in XV and at the VA, but having these girls to talk to solidified my interest and has kept it really strong. Teaching XV—more on that later—my junior and senior year, also, made engineering and computer science really fun for me.
-
-### What do you plan to study? And do you already know what you want to do after college? ###
-
-I hope to major in either Mechanical or Electrical and Computer Engineering as well as Computer Science, and minor in Women's Studies. After college, I hope to work for a company that supports or creates technology for social good, or start my own company.
-
-### My daughter had one high school programming class—Visual Basic. She was the only girl in her class, and she ended up getting harassed and having a miserable experience. What was your experience like? ###
-
-My high school began offering computer science classes my senior year, and I took Visual Basic as well! The class wasn't bad, but I was definitely one of three or four girls in the class of 20 or so students. Other computing classes seemed to have similar gender breakdowns. However, my high school was extremely small and the teacher was supportive of inclusivity in tech, so there was no harassment that I noticed. Hopefully the classes become more diverse in future years.
-
-### What did your schools do right technology-wise? And how could they have been better? ###
-
-My high school gave us consistent access to computers, and teachers occasionally assigned technology-based assignments in unrelated classes—we had to create a website for a social studies class a few times—which I think is great because it exposes everyone to tech. The robotics club was also pretty active and well-funded, but fairly small; I was not a member. One very strong component of the school's technology/engineering program is actually a student-taught engineering class called Exploratory Ventures, which is a hands-on class that tackles a new engineering or computer science problem every year. I taught it for two years with a classmate of mine, and have had students come up to me and tell me they're interested in pursuing engineering or computer science as a result of the class.
-
-However, my high school was not particularly focused on deliberately including young women in these programs, and it isn't very racially diverse. The computing-based classes and clubs were, by a vast majority, filled with white male students. This could definitely be improved on.
-
-### Growing up, how did you use technology at home? ###
-
-Honestly, when I was younger I used my computer time (my dad created a tracker, which logged us off after an hour of Internet use) to play Neopets or similar games. I guess I could have tried to mess with the tracker or played on the computer without Internet use, but I just didn't. I sometimes did little science projects with my dad, and I remember once printing "Hello world" in the terminal with him a thousand times, but mostly I just played online games with my sisters and didn't get my start in computing until high school.
-
-### You were active in the Feminism Club at your high school. What did you learn from that experience? What feminist issues are most important to you now? ###
-
-My friend and I co-founded Feminism Club at our high school late in our sophomore year. We did receive lots of resistance to the club at first, and while that never entirely went away, by the time we graduated feminist ideals were absolutely a part of the school's culture. The feminist work we did at my high school was generally on a more immediate scale and focused on issues like the dress code.
-
-Personally, I'm very focused on intersectional feminism, which is feminism as it applies to other aspects of oppression like racism and classism. The Facebook page [Guerrilla Feminism][4] is a great example of an intersectional feminism and has done so much to educate me. I currently run the Portland branch.
-
-Feminism is also important to me in terms of diversity in tech, although as an upper-class white woman with strong connections in the tech world, the problems here affect me much less than they do other people. The same goes for my involvement in intersectional feminism. Publications like [Model View Culture][5] are very inspiring to me, and I admire Shanley Kane so much for what she does.
-
-### What advice would you give parents who want to teach their children how to program? ###
-
-Honestly, nobody ever pushed me into computer science or engineering. Like I said, for a long time I wanted to be a geneticist. I got a summer internship doing web design for the VA the summer after my sophomore year and totally changed my mind. So I don't know if I can fully answer that question.
-
-I do think genuine interest is important, though. If my dad had sat me down in front of the computer and told me to configure a webserver when I was 12, I don't think I'd be interested in computer science. Instead, my parents gave me a lot of free reign to do what I wanted, which was mostly coding terrible little HTML sites for my Neopets. Neither of my younger sisters are interested in engineering or computer science, and my parents don't care. I'm really lucky my parents have given me and my sisters the encouragement and resources to explore our interests.
-
-Still, I grew up saying my future career would be "like my dad's"—even when I didn't know what he did. He has a pretty cool job. Also, one time when I was in middle school, I told him that and he got a little choked up and said I wouldn't think that in high school. So I guess that motivated me a bit.
-
-### What suggestions do you have for leaders in open source communities to help them attract and maintain a more diverse mix of contributors? ###
-
-I'm actually not active in particular open source communities. I feel much more comfortable discussing computing with other women; I'm a member of the [NCWIT Aspirations in Computing][6] network and it's been one of the most important aspects of my continued interest in technology, as well as the Facebook group [Ladies Storm Hackathons][7].
-
-I think this applies well to attracting and maintaining a talented and diverse mix of contributors: Safe spaces are important. I have seen the misogynistic and racist comments made in some open source communities, and subsequent dismissals when people point out the issues. I think that in maintaining a professional community there have to be strong standards on what constitutes harassment or inappropriate conduct. Of course, people can—and will—have a variety of opinions on what they should be able to express in open source communities, or any community. However, if community leaders actually want to attract and maintain diverse talent, they need to create a safe space and hold community members to high standards.
-
-I also think that some community leaders just don't value diversity. It's really easy to argue that tech is a meritocracy, and the reason there are so few marginalized people in tech is just that they aren't interested, and that the problem comes from earlier on in the pipeline. They argue that if someone is good enough at their job, their gender or race or sexual orientation doesn't matter. That's the easy argument. But I was raised not to make excuses for mistakes. And I think the lack of diversity is a mistake, and that we should be taking responsibility for it and actively trying to make it better.
-
---------------------------------------------------------------------------------
-
-via: http://opensource.com/life/15/8/patricia-torvalds-interview
-
-作者:[Rikki Endsley][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]:http://opensource.com/users/rikki-endsley
-[1]:https://creativecommons.org/licenses/by-sa/4.0/
-[2]:https://puppetlabs.com/
-[3]:https://www.aspirations.org/
-[4]:https://www.facebook.com/guerrillafeminism
-[5]:https://modelviewculture.com/
-[6]:https://www.aspirations.org/
-[7]:https://www.facebook.com/groups/LadiesStormHackathons/
diff --git a/sources/talk/20160516 Linux will be the major operating system of 21st century cars.md b/sources/talk/20160516 Linux will be the major operating system of 21st century cars.md
deleted file mode 100644
index dbde68bda8..0000000000
--- a/sources/talk/20160516 Linux will be the major operating system of 21st century cars.md	
+++ /dev/null
@@ -1,39 +0,0 @@
-KevinSJ Translating
-Linux will be the major operating system of 21st century cars
-===============================================================
-
->Cars are more than engines and good looking bodies. They're also complex computing devices so, of course, Linux runs inside them.
-
-Linux doesn't just run your servers and, via Android, your phones. It also runs your cars. Of course, no one has ever bought a car for its operating system. But Linux is already powering the infotainment, heads-up display and connected car 4G and Wi-Fi systems for such major car manufacturers as Toyota, Nissan, and Jaguar Land Rover and [Linux is on its way to Ford][1], Mazda, Mitsubishi, and Subaru cars.
-
-![](http://zdnet4.cbsistatic.com/hub/i/2016/05/10/743f0c14-6458-4d1e-8723-d2d94d0d0e69/c297b7d52e27e97d8721d4cb46bb371b/agl-logo.jpg)
->All the Linux and open-source car software efforts have now been unified under the Automotive Grade Linux project.
-
-Software companies are also getting into this Internet of mobile things act. Movimento, Oracle, Qualcomm, Texas Instruments, UIEvolution and VeriSilicon have all [joined the Automotive Grade Linux (AGL)][2] project. The [AGL][3] is a collaborative open-source project devoted to creating a common, Linux-based software stack for the connected car.
-
-AGL has seen tremendous growth over the past year as demand for connected car technology and infotainment are rapidly increasing," said Dan Cauchy, the Linux Foundation's General Manager of Automotive, in a statement.
-
-Cauchy continued, "Our membership base is not only growing rapidly, but it is also diversifying across various business interests, from semiconductors and in-vehicle software to IoT and connected cloud services. This is a clear indication that the connected car revolution has broad implications across many industry verticals."
-
-These companies have joined after AGL's recent announcement of a new AGL Unified Code Base (UCB). This new Linux distribution is based on AGL and two other car open-source projects: [Tizen][4] and the [GENIVI Alliance][5]. UCB is a second-generation car Linux. It was built from the ground up to address automotive specific applications. It handles navigation, communications, safety, security and infotainment functionality,
-
-"The automotive industry needs a standard open operating system and framework to enable automakers and suppliers to quickly bring smartphone-like capabilities to the car," said Cauchy. "This new distribution integrates the best components from AGL, Tizen, GENIVI and related open-source code into a single AGL Unified Code Base, allowing car-makers to leverage a common platform for rapid innovation. The AGL UCB distribution will play a huge role in the adoption of Linux-based systems for all functions in the vehicle."
-
-He's right. Since its release in January 2016, four car companies and ten new software businesses have joined AGL. Esso, now Exxon, made the advertising slogan, "Put a tiger in your tank!" famous. I doubt that "Put a penguin under your hood" will ever become well-known, but that's exactly what's happening. Linux is well on its way to becoming the major operating system of 21st century cars.
-
-------------------------------------------------------------------------------
-
-via: http://www.zdnet.com/article/the-linux-in-your-car-movement-gains-momentum/
-
-作者:[Steven J. Vaughan-Nichols][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]:http://www.zdnet.com/meet-the-team/us/steven-j-vaughan-nichols/
-[1]: https://www.automotivelinux.org/news/announcement/2016/01/ford-mazda-mitsubishi-motors-and-subaru-join-linux-foundation-and
-[2]: https://www.automotivelinux.org/news/announcement/2016/05/oracle-qualcomm-innovation-center-texas-instruments-and-others-support
-[3]: https://www.automotivelinux.org/
-[4]: https://www.tizen.org/
-[5]: http://www.genivi.org/
diff --git a/sources/talk/20160525 What containers and unikernels can learn from Arduino and Raspberry Pi.md b/sources/talk/20160525 What containers and unikernels can learn from Arduino and Raspberry Pi.md
deleted file mode 100644
index a1d6257d4d..0000000000
--- a/sources/talk/20160525 What containers and unikernels can learn from Arduino and Raspberry Pi.md	
+++ /dev/null
@@ -1,51 +0,0 @@
-What containers and unikernels can learn from Arduino and Raspberry Pi
-==========================================================================
-
-![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/bus-containers.png?itok=vM7_7vs0)
-
-
-Just the other day, I was speaking with a friend who is a mechanical engineer. He works on computer assisted braking systems for semi trucks and mentioned that his company has [Arduinos][1] all over the office. The idea is to encourage people to quickly experiment with new ideas. He also mentioned that Arduinos are more expensive than printed circuits. I was surprised by his comment about price, because coming from the software side of things, my perceptions of Arduinos was that they cost less than designing a specialized circuit.
-
-I had always viewed [Arduinos][2] and [Raspberry Pi][3] as these cool, little, specialized devices that can be used to make all kinds of fun gadgets. I came from the software side of the world and have always considered Linux on x86 and x86-64 "general purpose." The truth is, Arduinos are not specialized. In fact, they are very general purpose. They are fairly small, fairly cheap, and extremely flexible—that's why they caught on like wildfire. They have all kinds of I/O ports and expansion cards. They allow a maker to go out and build something cool really quickly. They even allow companies to build new products quickly.
-
-The unit price for an Arduino is much higher than a printed circuit, but time to a minimum viable idea is much lower. With a printed circuit, the unit price can be driven much lower but the upfront capital investment is much higher. So, long story short, the answer is—it depends.
-
-### Unikernels, rump kernels, and container hosts
-
-Enter unikernels, rump kernels, and minimal Linux distributions—these operating systems are purpose-built for specific use cases. These specialized operating systems are kind of like printed circuits. They require some up-front investment in planning and design to utilize, but could provide a great performance increase when deploying a specific workload at scale.
-
-Minimal operating systems such as Red Hat Enterprise Linux Atomic or CoreOS are purpose-built to run containers. They are small, quick, easily configured at boot time, and run containers quite well. The downside is that it requires extra engineering to add third-party extensions such as monitoring agents or tools for virtualization. Some side-loaded tooling needs redesigned as super-privileged containers. This extra engineering could be worth it if you are building a big enough container environment, but might not be necessary to just try out containers.
-
-Containers provide the ability to run standard workloads (things built on [glibc][4], etc.). The advantage is that the workload artifact (Docker image) can be built and tested on your desktop and deployed in production on completely different hardware or in the cloud with confidence that it will run with the same characteristics. In the production environment, container hosts are still configured by the operations teams, but the application is controlled by the developer. This is a sort of a best of both worlds.
-
-Unikernels and rump kernels are also purpose-built, but go a step further. The entire operating system is configured at build time by the developer or architect. This has benefits and challenges.
-
-One benefit is that the developer can control a lot about how the workload will run. Theoretically, a developer could try out [different TCP stacks][5] for different performance characteristics and choose the best one. The developer can configure the IP address ahead of time or have the system configure itself at boot with DHCP. The developer can also cut out anything that is not necessary for their application. There is also the promise of increased performance because of less [context switching][6].
-
-There are also challenges with unikernels. Currently, there is a lot of tooling missing. It's much like a printed circuit world right now. A developer has to invest a lot of time and energy discerning if all of the right libraries exist, or they have to change the way their application works. There may also be challenges with how the "embedded" operating system is configured at runtime. Finally, every time a major change is made to the OS, it requires [going back to the developer][7] to change it. This is not a clean separation between development and operations, so I envision some organizational changes being necessary to truly adopt this model.
-
-### Conclusion
-
-There is a lot of interesting buzz around specialized container hosts, rump kernels, and unikernels because they hold the potential to revolutionize certain workloads (embedded, cloud, etc.). Keep your eye on this exciting, fast moving space, but cautiously.
-
-Currently, unikernels seem quite similar to building printed circuits. They require a lot of upfront investment to utilize and are very specialized, providing benefits for certain workloads. In the meantime containers are quite interesting even for conventional workloads and don't require as much investment. Typically an operations team should be able to port an application to containers, whereas it takes real re-engineering to port an application to unikernels and the industry is still not quite sure what workloads can be ported to unikernels.
-
-Here's to an exciting future of containers, rump kernels, and unikernels!
-
---------------------------------------
-via: https://opensource.com/business/16/5/containers-unikernels-learn-arduino-raspberry-pi 
-
-作者:[Scott McCarty][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: https://opensource.com/users/fatherlinux
-[1]: https://opensource.com/resources/what-arduino
-[2]: https://opensource.com/life/16/4/arduino-day-3-projects
-[3]: https://opensource.com/resources/what-raspberry-pi
-[4]: https://en.wikipedia.org/wiki/GNU_C_Library
-[5]: http://www.eetasia.com/ARTICLES/2001JUN/2001JUN18_NTEK_CT_AN5.PDF
-[6]: https://en.wikipedia.org/wiki/Context_switch
-[7]: http://developers.redhat.com/blog/2016/05/18/3-reasons-i-should-build-my-containerized-applications-on-rhel-and-openshift/
diff --git a/sources/talk/20160606 Who needs a GUI - How to live in a Linux terminal.md b/sources/talk/20160606 Who needs a GUI - How to live in a Linux terminal.md
new file mode 100644
index 0000000000..148d758216
--- /dev/null
+++ b/sources/talk/20160606 Who needs a GUI - How to live in a Linux terminal.md	
@@ -0,0 +1,109 @@
+chenxinlong translating
+Who needs a GUI? How to live in a Linux terminal
+=================================================
+
+![](http://core0.staticworld.net/images/article/2016/07/linux-terminal-1-100669790-orig.jpg)
+
+### The best Linux shell apps for handling common functions
+
+Ever consider the idea of living entirely in a Linux terminal? No graphical desktop. No modern GUI software. Just text—and nothing but text—inside a Linux shell. It may not be easy, but it’s absolutely doable. [I recently tried living completely in a Linux shell for 30 days][1]. What follows are my favorite shell applications for handling some of the most common bits of computer functionality (web browsing, word processing, etc.). With a few obvious holes. Because being text-only is hard.
+
+![](http://core0.staticworld.net/images/article/2016/07/linux-terminal-2-100669791-orig.png)
+
+### Emailing within a Linux terminal
+
+For emailing in a terminal, we are spoiled for choice. Many people recommend mutt and notmuch. Both of those are powerful and excellent, but I prefer alpine. Why? Not only does it work well, but it’s also much more of a familiar interface if you are used to GUI email software like Thunderbird.
+
+![](http://core0.staticworld.net/images/article/2016/07/linux-terminal-3-100669837-orig.jpg)
+
+### Web browsing within a Linux terminal
+
+I have one word for you: [w3m][5]. Well, I suppose that’s not even really a word. But w3m is definitely my terminal web browser of choice. It tenders things fairly well and is powerful enough to even let you post to sites such as Google Plus (albeit, not in a terribly fun way). Lynx may be the de facto text-based web browser, but w3m is my favorite.
+
+![](http://core0.staticworld.net/images/article/2016/07/linux-terminal-4-100669838-orig.jpg)
+
+### Text editing within a Linux terminal
+
+For editing simple text files, I have one application that I straight-up love. No, not emacs. Also, definitely not vim. For editing of a text file or jotting down some notes, I like nano. Yes, nano. It’s simple, easy to learn and pleasant to use. Are there pieces of software with more features? Sure. But nano is just delightful.
+
+![](http://core0.staticworld.net/images/article/2016/07/linux-terminal-5-100669839-orig.jpg)
+
+### Word processing within a Linux terminal
+
+In a shell—with nothing but text—there really isn’t a huge difference between a “text editor” and a “word processor.” But being as I do a lot of writing, having a piece of software built specifically for long-form writing is a definite must. My favorite is wordgrinder. It has just enough tools to make me happy, a nice menu-driven interface (with hot-keys), and it supports multiple file types, including OpenDocument, HTML and a bunch of other ones.
+
+![](http://core0.staticworld.net/images/article/2016/07/linux-terminal-6-100669795-orig.jpg)
+
+### Music playing within a Linux terminal
+
+When it comes to playing music (mp3, Ogg, etc.) from a shell, one piece of software is king: [cmus][7]. It supports every conceivable file format. It’s super easy to use and incredibly fast and light on system resource usage. So clean. So streamlined. This is what a good music player should be like.
+
+![](http://core0.staticworld.net/images/article/2016/07/linux-terminal-7-100669796-orig.jpg)
+
+### Instant messaging within a Linux terminal
+
+When I realized how will I could instant message from the terminal, my head exploded. You know Pidgin, the multi-protocol IM client? Well, it has a version for the terminal, called “[finch][8],” that allows you to connect to multiple networks and chat with multiple people at once. The interface is even similar to Pidgin. Just amazing. Use Google Hangouts? Try [hangups][9]. It has a nice tabbed interface and works amazingly well. Seriously. Other than needing perhaps some emoji and inline pictures, instant messaging from the shell is a great experience.
+
+![](http://core0.staticworld.net/images/article/2016/07/linux-terminal-8-100669797-orig.jpg)
+
+### Tweeting within a Linux terminal
+
+No joke. Twitter, in your terminal, thanks to [rainbowstream][10]. I hit a few bugs here and there, but overall, it works rather well. Not as well as the website itself—and not as well as the official mobile clients—but, come on, this is Twitter in a shell. Even if it has one or two rough edges, this is pretty stinkin’ cool.
+
+![](http://core0.staticworld.net/images/article/2016/07/linux-terminal-9-100669798-orig.jpg)
+
+### Reddit-ing within a Linux terminal
+
+Spending time on Reddit from the comforts of the command line feels right somehow. And with rtv, it’s a rather pleasant experience. Reading. Commenting. Voting. It all works. The experience isn’t actually all that different than the website itself.
+
+![](http://core0.staticworld.net/images/article/2016/07/linux-terminal-10-100669799-orig.jpg)
+
+### Process managing within a Linux terminal
+
+Use [htop][12]. It’s like top—only better and prettier. Sometimes I just leave htop up and running all the time. Just because. In that regard, it’s like a music visualizer—only for RAM and CPU usage.
+
+![](http://core0.staticworld.net/images/article/2016/07/linux-terminal-11-100669800-orig.png)
+
+### File managing within a Linux terminal
+
+Just because you’re in a text-based shell doesn’t mean you don’t enjoy the finer things in life. Like having a nice file browser and manager. In that regard, [Midnight Commander][13] is a pretty doggone great one.
+
+![](http://core0.staticworld.net/images/article/2016/07/linux-terminal-12-100669801-orig.png)
+
+### Terminal managing within a Linux terminal
+
+If you spend much time in the shell, you’re going to need a terminal multiplexer. Basically it’s a piece of software that lets you split up your terminal session into a customizable grid, allowing you to use and see multiple terminal applications at the same time. It’s a tiled window manager for your shell. My favorite is [tmux][14]. But [GNU Screen][15] is also quite nice. It might take a few minutes to learn how to use it, but once you do, you’ll be glad you did.
+
+![](http://core0.staticworld.net/images/article/2016/07/linux-terminal-13-100669802-orig.jpg)
+
+### Presentation-ing within a Linux terminal
+
+LibreOffice, Google Slides or, gasp, PowerPoint. I spend a lot of time in presentation software. The fact that one exists for the shell pleases me greatly. It’s called, appropriately, “[text presentation program][16].” There are no images (obviously), just a simple program for displaying slides put together in a simple markup language. It may not let you embed pictures of cats, but you’ll earn some serious nerd-cred for doing an entire presentation from the terminal.
+
+--------------------------------------------------------------------------------
+
+via: http://www.networkworld.com/article/3091139/linux/who-needs-a-gui-how-to-live-in-a-linux-terminal.html#slide1
+
+作者:[Bryan Lunduke][a]
+译者:[译者ID](https://github.com/chenxinlong)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.networkworld.com/author/Bryan-Lunduke/
+[1]: http://www.networkworld.com/article/3083268/linux/30-days-in-a-terminal-day-0-the-adventure-begins.html
+[2]: https://en.wikipedia.org/wiki/Mutt_(email_client)
+[3]: https://notmuchmail.org/
+[4]: https://en.wikipedia.org/wiki/Alpine_(email_client)
+[5]: https://en.wikipedia.org/wiki/W3m
+[6]: http://cowlark.com/wordgrinder/index.html
+[7]: https://en.wikipedia.org/wiki/Cmus
+[8]: https://developer.pidgin.im/wiki/Using%20Finch
+[9]: https://github.com/tdryer/hangups
+[10]: http://www.rainbowstream.org/
+[11]: https://github.com/michael-lazar/rtv
+[12]: http://hisham.hm/htop/
+[13]: https://en.wikipedia.org/wiki/Midnight_Commander
+[14]: https://tmux.github.io/
+[15]: https://en.wikipedia.org/wiki/GNU_Screen
+[16]: http://www.ngolde.de/tpp.html
diff --git a/sources/talk/20160614 ​Ubuntu Snap takes charge of Linux desktop and IoT software distribution.md b/sources/talk/20160614 ​Ubuntu Snap takes charge of Linux desktop and IoT software distribution.md
deleted file mode 100644
index 667c49b056..0000000000
--- a/sources/talk/20160614 ​Ubuntu Snap takes charge of Linux desktop and IoT software distribution.md	
+++ /dev/null
@@ -1,113 +0,0 @@
-vim-kakali translating
-
-
-Ubuntu Snap takes charge of Linux desktop and IoT software distribution
-===========================================================================
-
-[Canonical][28] and [Ubuntu][29] founder Mark Shuttleworth said in an interview that he hadn't planned on an announcement about Ubuntu's new [Snap app package format][30]. But then in a matter of a few months, developers from multiple Linux distributions and companies announced they would use Snap as a universal Linux package format.
-
-![](http://zdnet2.cbsistatic.com/hub/i/r/2016/06/14/a9b2a139-3cd4-41bf-8e10-180cb9450134/resize/770xauto/adc7d16a46167565399ecdb027dd1416/ubuntu-snap.jpg)
->Linux distributors, ISVs, and companies are all adopting Ubuntu Snap to distribute and update programs across all Linux varieties.
-
-Why? Because Snap enables a single binary package to work perfectly and securely on any Linux desktop, server, cloud or device. According to Olli Ries, head of Canonical's Ubuntu client platform products and releases:
-
->The [security mechanisms in Snap packages][1] allow us to open up the platform for much faster iteration across all our flavors as Snap applications are isolated from the rest of the system. Users can install a Snap without having to worry whether it will have an impact on their other apps or their system.
-
-Of course, as Matthew Garrett, a former Linux kernel developer and CoreOS security developer, has pointed out: If you [use Snap with an insecure program, such as the X11][2] window system, you don't actually gain any security.
-
-Shuttleworth agrees with Garrett but points out that you can control how Snap applications interact with the rest of this system. So, for example, a web browser can be contained within a secure Snap, which uses the Ubuntu packaged [openssl][3] Transport Layer Security (TLS) and Secure Sockets Layer (SSL) library. In addition, even if something does break into the browser instance, it still can't get to the underlying operating system.
-
-Many companies agree. [Dell][4], [Samsung][5], [Mozilla][6], [Krita][7], [Mycroft][8], and [Horizon Computing][9] are adopting Snap. [Arch Linux][10], [Debian][11], [Gentoo][12], and [OpenWrt][13] developers have also embraced Snaps and are adding it to their Linux distributions
-
-Snap packages, aka "Snaps", now work natively on Arch, Debian, Fedora, Kubuntu, Lubuntu, Ubuntu GNOME, Ubuntu Kylin, Ubuntu MATE, Ubuntu Unity, and Xubuntu. Snap is being validated on CentOS, Elementary, Gentoo, Mint, OpenSUSE, and Red Hat Enterprise Linux (RHEL), and are easy to enable on other Linux distributions.
-
-These distributions are adopting Snaps, Shuttleworth explained, because "Snaps bring those apps to every Linux desktop, server, device or cloud machine, giving users freedom to choose any Linux distribution while retaining access to the best apps."
-
-Taken together these distributions represent the vast majority of common Linux desktop, server and cloud distributions. Why would they switch from their existing package management systems? "One nice feature of Snaps is support for edge and beta channels, which allow users to opt-in to the pre-release developer versions of software or stick with the latest stable versions." explained Tim Jester-Pfadt, an Arch Linux contributor.
-
-In addition to the Linux distributors, independent software vendors (ISVs) are embracing Snap since it greatly simplifies third-party Linux app distribution and security maintenance. For example, [The Document Foundation][14] will be making the popular open-source office suite [LibreOffice][15] available as a Snap.
-
-Thorsten Behrens, co-founder of The Document Foundation explained:
-
->Our objective is to make LibreOffice easily available to as many users as possible. Snaps enable our users to get the freshest LibreOffice releases across different desktops and distributions quickly, easily and consistently. As a bonus, it should help our release engineers to eventually move away from bespoke, home-grown and ancient Linux build solutions, towards something that is collectively maintained.
-
-In a statement, Nick Nguyen, Mozilla's [Firefox][16] VP, added:
-
->We strive to offer users a great experience and make Firefox available across many platforms, devices and operating systems. With the introduction of Snaps, continually optimizing Firefox will become possible, providing Linux users the most up-to-date features.
-
-Boudewijn Rempt, project lead at the [Krita Foundation][17], a KDE-based graphics program, said:
-
->Maintaining DEB packages in a private repository was complex and time consuming, snaps are much easier to maintain, package and distribute. Putting the snap in the store was particularly simple, this is the most streamlined app store I have published software in. [Krita 3.0][18] has just been released as a snap which will be updated automatically as newer versions become available.
-
-It's not just Linux desktop programmers who are excited by Snap. Internet of Things (IoT) and embedded developers are also grabbing on to Snap with both hands.
-
-Because Snaps are isolated from one another to help with data security, and can be updated or rolled back automatically, they are ideal for devices. Multiple vendors have launched snappy IoT devices, enabling a new class of "smart edge" device with IoT app store. Snappy devices receive automatic updates for the base OS, together with updates to the apps installed on the device.
-
-Dell, which according to Shuttleworth was one of the first IoT vendors to see the power of Snap, will be using Snap in its devices.
-
-"We believe Snaps address the security risks and manageability challenges associated with deploying and running multiple third party applications on a single IoT Gateway," said Jason Shepherd, Dell's Director of IoT Strategy and Partnerships. "This trusted and universal app format is essential for Dell, our IoT Solutions Partners and commercial customers to build a scalable, IT-ready, and vibrant ecosystem of IoT applications."
-
-It's simple, explained OpenWrt developer Matteo Croce. "Snaps deliver new applications to OpenWrt while leaving the core OS unchanged.... Snaps are a faster way to deliver a wider range of software to supported OpenWrt access points and routers."
-
-Shuttleworth doesn't see Snaps replacing existing Linux package systems such as [RPM][19] and [DEB][20]. Instead he sees it as being complementary to them. Snaps will sit alongside the native package. Each distribution has its own mechanisms to provide and update the core operating system and its updates. What Snap brings to the table is universal apps that cannot interfere with the base operating system
-
-Each Snap is confined using a range of kernel isolation and security mechanisms, tailored to the Snap application's needs. A careful review process ensures that snaps only receive the permissions they require to operate. Users will not have to make complex security decisions when installing the snap.
-
-Since Snaps are essentially self-contained zip files that can be quickly executed in place, "Snaps are much easier to create than traditional Linux packages, and allow us to evolve dependencies independent of the base operating system, so we can easily provide the very best and latest Chinese Linux apps to users across all distributions," explained Jack Yu, leader of the popular [Chinese Ubuntu Kylin][21] team.
-
-The snap format, designed by Canonical, is handled by [snapd][22]. Its development work is done on [GitHub][23]. Porting snapd to a wide range of Linux distributions has proven straightforward, and the community has grown to include contributors from a wide range of Linux backgrounds.
-
-Snap packages are created with the snapcrafttool. The home of the project is [snapcraft.io][24], which includes a tour and step-by-step guides to Snap creation, along with documentation for users and contributors to the project. Snaps can be built from existing distribution packages, but are more commonly built from source for optimization and size efficiency.
-
-Unless you're an Ubuntu power-user or serious Linux developer you may not have heard of Snap. In the future, anyone who does work with Linux on any platform will know the program. It's well on its way to becoming a major -- perhaps the most important of all -- Linux application installation and upgrade mechanism.
-
-#### Related Stories:
-
-- [Linux expert Matthew Garrett: Ubuntu 16.04's new Snap format is a security risk][25]
-- [Ubuntu Linux 16.04 is here][26]
-- [Microsoft and Canonical partner to bring Ubuntu to Windows 10][27]
-
-
---------------------------------------------------------------------------------
-
-via: http://www.zdnet.com/article/ubuntu-snap-takes-charge-of-linux-desktop-and-iot-software-distribution/
-
-作者:[Steven J. Vaughan-Nichols][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.zdnet.com/meet-the-team/us/steven-j-vaughan-nichols/
-[28]: http://www.canonical.com/
-[29]: http://www.ubuntu.com/
-[30]: https://insights.ubuntu.com/2016/04/13/snaps-for-classic-ubuntu/
-[1]: https://insights.ubuntu.com/2016/04/13/snaps-for-classic-ubuntu/
-[2]: http://www.zdnet.com/article/linux-expert-matthew-garrett-ubuntu-16-04s-new-snap-format-is-a-security-risk/
-[3]: https://www.openssl.org/
-[4]: http://www.dell.com/en-us/
-[5]: http://www.samsung.com/us/
-[6]: http://www.mozilla.com/
-[7]: https://krita.org/en/
-[8]: https://mycroft.ai/
-[9]: http://www.horizon-computing.com/
-[10]: https://www.archlinux.org/
-[11]: https://www.debian.org/
-[12]: https://www.gentoo.org/
-[13]: https://openwrt.org/
-[14]: https://www.documentfoundation.org/
-[15]: https://www.libreoffice.org/download/libreoffice-fresh/
-[16]: https://www.mozilla.org/en-US/firefox/new/
-[17]: https://krita.org/en/about/krita-foundation/
-[18]: https://krita.org/en/item/krita-3-0-released/
-[19]: http://rpm5.org/
-[20]: https://www.debian.org/doc/manuals/debian-faq/ch-pkg_basics.en.html
-[21]: http://www.ubuntu.com/desktop/ubuntu-kylin
-[22]: https://launchpad.net/ubuntu/+source/snapd
-[23]: https://github.com/snapcore/snapd
-[24]: http://snapcraft.io/
-[25]: http://www.zdnet.com/article/linux-expert-matthew-garrett-ubuntu-16-04s-new-snap-format-is-a-security-risk/
-[26]: http://www.zdnet.com/article/ubuntu-linux-16-04-is-here/
-[27]: http://www.zdnet.com/article/microsoft-and-canonical-partner-to-bring-ubuntu-to-windows-10/
-
-
diff --git a/sources/talk/20160620 5 Best Linux Package Managers for Linux Newbies.md b/sources/talk/20160620 5 Best Linux Package Managers for Linux Newbies.md
deleted file mode 100644
index a92c5265e5..0000000000
--- a/sources/talk/20160620 5 Best Linux Package Managers for Linux Newbies.md	
+++ /dev/null
@@ -1,118 +0,0 @@
-transalting by ynmlml
-5 Best Linux Package Managers for Linux Newbies
-=====================================================
-
-
-One thing a new Linux user will get to know as he/she progresses in using it is the existence of several Linux distributions and the different ways they manage packages.
-
-Package management is very important in Linux, and knowing how to use multiple package managers can proof life saving for a power user, since downloading or installing software from repositories, plus updating, handling dependencies and uninstalling software is very vital and a critical section in Linux system Administration.
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/Best-Linux-Package-Managers.png)
->Best Linux Package Managers
-
-Therefore to become a Linux power user, it is significant to understand how the major Linux distributions actually handle packages and in this article, we shall take a look at some of the best package managers you can find in Linux.
-
-Here, our main focus is on relevant information about some of the best package managers, but not how to use them, that is left to you to discover more. But I will provide meaningful links that point out usage guides and many more.
-
-### 1. DPKG – Debian Package Management System
-
-Dpkg is a base package management system for the Debian Linux family, it is used to install, remove, store and provide information about `.deb` packages.
-
-It is a low-level tool and there are front-end tools that help users to obtain packages from remote repositories and/or handle complex package relations and these include:
-
-Don’t Miss: [15 Practical Examples of “dpkg commands” for Debian Based Distros][1]
-
-#### APT (Advanced Packaging Tool)
-
-It is a very popular, free, powerful and more so, useful command line package management system that is a front end for dpkg package management system.
-
-Users of Debian or its derivatives such as Ubuntu and Linux Mint should be familiar with this package management tool.
-
-To understand how it actually works, you can go over these how to guides:
-
-Don’t Miss: [15 Examples of How to Use New Advanced Package Tool (APT) in Ubuntu/Debian][2]
-
-Don’t Miss: [25 Useful Basic Commands of APT-GET and APT-CACHE for Package Management][3]
-
-#### Aptitude Package Manager
-
-This is also a popular command line front-end package management tool for Debian Linux family, it works similar to APT and there have been a lot of comparisons between the two, but above all, testing out both can make you understand which one actually works better.
-
-It was initially built for Debian and its derivatives but now its functionality stretches to RHEL family as well. You can refer to this guide for more understanding of APT and Aptitude:
-
-Don’t Miss: [What is APT and Aptitude? and What’s real Difference Between Them?][4]
-
-#### Synaptic Package Manager
-
-Synaptic is a GUI package management tool for APT based on GTK+ and it works fine for users who may not want to get their hands dirty on a command line. It implements the same features as apt-get command line tool.
-
-### 2. RPM (Red Hat Package Manager)
-
-This is the Linux Standard Base packing format and a base package management system created by RedHat. Being the underlying system, there several front-end package management tools that you can use with it and but we shall only look at the best and that is:
-
-#### YUM (Yellowdog Updater, Modified)
-
-It is an open source and popular command line package manager that works as a interface for users to RPM. You can compare it to APT under Debian Linux systems, it incorporates the common functionalities that APT has. You can get a clear understanding of YUM with examples from this how to guide:
-
-Don’t Miss: [20 Linux YUM Commands for Package Management][5]
-
-#### DNF – Dandified Yum
-
-It is also a package manager for the RPM-based distributions, introduced in Fedora 18 and it is the next generation of version of YUM.
-
-If you have been using Fedora 22 onwards, you must have realized that it is the default package manager. Here are some links that will provide you more information about DNF and how to use it:
-
-Don’t Miss: [DNF – The Next Generation Package Management for RPM Based Distributions][6]
-
-Don’t Miss: [27 ‘DNF’ Commands Examples to Manage Fedora Package Management][7]
-
-### 3. Pacman Package Manager – Arch Linux
-
-It is a popular and powerful yet simple package manager for Arch Linux and some little known Linux distributions, it provides some of the fundamental functionalities that other common package managers provide including installing, automatic dependency resolution, upgrading, uninstalling and also downgrading software.
-
-But most effectively, it is built to be simple for easy package management by Arch users. You can read this [Pacman overview][8] which explains into details some of its functions mentioned above.
-
-### 4. Zypper Package Manager – openSUSE
-
-It is a command line package manager on OpenSUSE Linux and makes use of the libzypp library, its common functionalities include repository access, package installation, resolution of dependencies issues and many more.
-
-Importantly, it can also handle repository extensions such as patterns, patches, and products. New OpenSUSE user can refer to this following guide to master it.
-
-Don’t Miss: [45 Zypper Commands to Master OpenSUSE Package Management][9]
-
-### 5. Portage Package Manager – Gentoo
-
-It is a package manager for Gentoo, a less popular Linux distribution as of now, but this won’t limit it as one of the best package managers in Linux.
-
-The main aim of the Portage project is to make a simple and trouble free package management system to include functionalities such as backwards compatibility, automation plus many more.
-
-For better understanding, try reading [Portage project page][10].
-
-### Concluding Remarks
-
-As I already hinted at the beginning, the main purpose of this guide was to provide Linux users a list of the best package managers but knowing how to use them can be done by following the necessary links provided and trying to test them out.
-
-Users of the different Linux distributions will have to learn more on their own to better understand the different package managers mentioned above.
-
-
---------------------------------------------------------------------------------
-
-via: http://www.tecmint.com/linux-package-managers/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+tecmint+%28Tecmint%3A+Linux+Howto%27s+Guide%29
-
-作者:[Ravi Saive][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.tecmint.com/author/admin/
-[1]: http://www.tecmint.com/dpkg-command-examples/
-[2]: http://www.tecmint.com/apt-advanced-package-command-examples-in-ubuntu/
-[3]: http://www.tecmint.com/useful-basic-commands-of-apt-get-and-apt-cache-for-package-management/
-[4]: http://www.tecmint.com/difference-between-apt-and-aptitude/
-[5]: http://www.tecmint.com/20-linux-yum-yellowdog-updater-modified-commands-for-package-mangement/
-[6]: http://www.tecmint.com/dnf-next-generation-package-management-utility-for-linux/
-[7]: http://www.tecmint.com/dnf-commands-for-fedora-rpm-package-management/
-[8]: https://wiki.archlinux.org/index.php/Pacman
-[9]: http://www.tecmint.com/zypper-commands-to-manage-suse-linux-package-management/
-[10]: https://wiki.gentoo.org/wiki/Project:Portage
diff --git a/sources/talk/20160625 Ubuntu’s Snap, Red Hat’s Flatpak And Is ‘One Fits All’ Linux Packages Useful.md b/sources/talk/20160625 Ubuntu’s Snap, Red Hat’s Flatpak And Is ‘One Fits All’ Linux Packages Useful.md
deleted file mode 100644
index 35088e4be1..0000000000
--- a/sources/talk/20160625 Ubuntu’s Snap, Red Hat’s Flatpak And Is ‘One Fits All’ Linux Packages Useful.md	
+++ /dev/null
@@ -1,110 +0,0 @@
-Ubuntu’s Snap, Red Hat’s Flatpak And Is ‘One Fits All’ Linux Packages Useful?
-=================================================================================
-
-![](http://www.iwillfolo.com/wordpress/wp-content/uploads/2016/06/Flatpak-and-Snap-Packages.jpg)
-
-An in-depth look into the new generation of packages starting to permeate the Linux ecosystem.
-
-
-Lately we’ve been hearing more and more about Ubuntu’s Snap packages and Flatpak (formerly referred to as xdg-app) created by Red Hat’s employee Alexander Larsson.
-
-These 2 types of next generation packages are in essence having the same goal and characteristics which are: being standalone packages that doesn’t rely on 3rd-party system libraries in order to function.
-
-This new technology direction which Linux seems to be headed is automatically giving rise to questions such as, what are the advantages / disadvantages of standalone packages? does this lead us to a better Linux overall? what are the motives behind it?
-
-To answer these questions and more, let us explore the things we know about Snap and Flatpak so far.
-
-### The Motive
-
-According to both [Flatpak][1] and [Snap][2] statements, the main motive behind them is to be able to bring one and the same version of application to run across multiple Linux distributions.
-
->“From the very start its primary goal has been to allow the same application to run across a myriad of Linux distributions and operating systems.” Flatpak
-
->“… ‘snap’ universal Linux package format, enabling a single binary package to work perfectly and securely on any Linux desktop, server, cloud or device.” Snap
-
-To be more specific, the guys behind Snap and Flatpak (S&F) believe that there’s a barrier of fragmentation on the Linux platform.
-
-A barrier which holds back the platform advancement by burdening developers with more, perhaps unnecessary, work to get their software run on the many distributions out there.
-
-Therefore, as leading Linux distributions (Ubuntu & Red Hat), they wish to eliminate the barrier and strengthen the platform in general.
-
-But what are the more personal gains which motivate the development of S&F?
-
-#### Personal Gains?
-
-Although not officially stated anywhere, it may be assumed that by leading the efforts of creating a unified package that could potentially be adopted by the vast majority of Linux distros (if not all of them), the captains of these projects could assume a key position in determining where the Linux ship sails.
-
-### The Advantages
-
-The benefits of standalone packages are diverse and can depend on different factors.
-
-Basically however, these factors can be categorized under 2 distinct criteria:
-
-#### User Perspective
-
-+ From a Linux user point of view, Snap and Flatpak both bring the possibility of installing any package (software / app) on any distribution the user is using.
-
-That is, for instance, if you’re using a not so popular distribution which has only a scarce supply of packages available in their repo, due to workforce limitations probably, you’ll now be able to easily and significantly increase the amount of packages available to you – which is a great thing.
-
-+ Also, users of popular distributions that do have many packages available in their repos, will enjoy the ability of installing packages that might not have behaved with their current set of installed libraries.
-
-For example, a Debian user who wants to install a package from ‘testing branch’ will not have to convert his entire system into ‘testing’ (in order for the package to run against newer libraries), rather, that user will simply be able to install only the package he wants from whichever branch he likes and on whatever branch he’s on.
-
-The latter point, was already basically possible for users who were compiling their packages straight from source, however, unless using a source based distribution such as Gentoo, most users will see this as just an unworthily hassle.
-
-+ The advanced user, or perhaps better put, the security aware user might feel more comfortable with this type of packages as long as they come from a reliable source as they tend to provide another layer of isolation since they are generally isolated from system packages.
-
-* Both S&F are being developed with enhanced security in mind, which generally makes use of “sandboxing” i.e isolation in order to prevent cases where they carry a virus which can infect the entire system, similar to the way .exe files on MS Windows may. (More on MS and S&F later)
-
-#### Developer Perspective
-
-For developers, the advantages of developing S&F packages will probably be a lot clearer than they are to the average user, some of these were already hinted in a previous section of this post.
-
-Nonetheless, here they are:
-
-+ S&F will make it easier on devs who want to develop for more than one Linux distribution by unifying the process of development, therefore minimizing the amount of work a developer needs to do in order to get his app running on multiple distributions.
-
-++ Developers could therefore gain easier access to a wider range of distributions.
-
-+ S&F allow devs to privately distribute their packages without being dependent on distribution maintainers to stabilize their package for each and every distro.
-
-++ Through the above, devs may gain access to direct statistics of user adoption / engagement for their software.
-
-++ Also through the above, devs could get more directly involved with users, rather than having to do so through a middleman, in this case, the distribution.
-
-### The Downsides
-
-– Bloat. Simple as that. Flatpak and Snap aren’t just magic making dependencies evaporate into thin air. Rather, instead of relying on the target system to provide the required dependencies, S&F comes with the dependencies prebuilt into them.
-
-As the saying goes “if the mountain won’t come to Muhammad, Muhammad must go to the mountain…”
-
-– Just as the security-aware user might enjoy S&F packages extra layer of isolation, as long they come from a trusted source. The less knowledgeable user on the hand, might be prone to the other side of the coin hazard which is using a package from an unknown source which may contain malicious software.
-
-The above point can be said to be valid even with today’s popular methods, as PPAs, overlays, etc might also be maintained by untrusted sources.
-
-However, with S&F packages the risk increases since malicious software developers need to create only one version of their program in order to infect a large number of distributions, whereas, without it they’d needed to create multiple versions in order to adjust their malware to other distributions.
-
-Was Microsoft Right All Along?
-
-With all that’s mentioned above in mind, it’s pretty clear that for the most part, the advantages of using S&F packages outweighs the drawbacks.
-
-At the least for users of binary-based distributions, or, non lightweight focused distros.
-
-Which eventually lead me to asking the above question – could it be that Microsoft was right all along? if so and S&F becomes the Linux standard, would you still consider Linux a Unix-like variant?
-
-Well apparently, the best one to answer those questions is probably time.
-
-Nevertheless, I’d argue that even if not entirely right, MS certainly has a good point to their credit, and having all these methods available here on Linux out of the box is certainly a plus in my book.
-
-
---------------------------------------------------------------------------------
-
-via: http://www.iwillfolo.com/ubuntus-snap-red-hats-flatpack-and-is-one-fits-all-linux-packages-useful/
-
-作者:[Editorials][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.iwillfolo.com/category/editorials/
diff --git a/sources/talk/20160627 Linux Applications That Works On All Distributions – Are They Any Good.md b/sources/talk/20160627 Linux Applications That Works On All Distributions – Are They Any Good.md
deleted file mode 100644
index 98967d866b..0000000000
--- a/sources/talk/20160627 Linux Applications That Works On All Distributions – Are They Any Good.md	
+++ /dev/null
@@ -1,90 +0,0 @@
-Linux Applications That Works On All Distributions – Are They Any Good?
-============================================================================
-
-![](http://www.iwillfolo.com/wordpress/wp-content/uploads/2016/06/Bundled-applications.jpg)
-
-
-A revisit of Linux community’s latest ambitions – promoting decentralized applications in order to tackle distribution fragmentation.
-
-Following last week’s article: [Ubuntu’s Snap, Red Hat’s Flatpak And Is ‘One Fits All’ Linux Packages Useful][1]?, a couple of new opinions rose to the surface which may contain crucial information about the usefulness of such apps.
-
-### The Con Side
-
-Commenting on the subject [here][2], a [Gentoo][3] user who goes by the name Till, gave rise to a few points which hasn’t been addressed to the fullest the last time we covered the issue.
-
-While previously we settled on merely calling it bloat, Till here on the other hand, is dissecting that bloat further so to help us better understand both its components and its consequences.
-
-Referring to such apps as “bundled applications” – for the way they work on all distributions is by containing dependencies together with the apps themselves, Till says:
-
->“bundles ship a lot of software that now needs to be maintained by the application developer. If library X has a security problem and needs an update, you rely on every single applications to ship correct updates in order to make your system save.”
-
-Essentially, Till raises an important security point. However, it isn’t necessarily has to be tied to security alone, but can also be linked to other aspects such as system maintenance, atomic updates, etc…
-
-Furthermore, if we take that notion one step further and assume that dependencies developers may cooperate, therefore releasing their software in correlation with the apps who use them (an utopic situation), we shall then get an overall slowdown of the entire platform development.
-
-Another problem that arises from the same point made above is dependencies-transparency becomes obscure, that is, if you’d want to know which libraries are bundled with a certain app, you’ll have to rely on the developer to publish such data.
-
-Or, as Till puts it: “Questions like, did package XY already include the updated library Z, will be your daily bread”.
-
-For comparison, with the standard methods available on Linux nowadays (both binary and source distributions), you can easily notice which libraries are being updated upon a system update.
-
-And you can also rest assure that all other apps on the system will use it, freeing you from the need to check each app individually.
-
-Other cons that may be deduced from the term bloat include: bigger package size (each app is bundled with dependencies), higher memory usage (no more library sharing) and also –
-
-One less filter mechanism to prevent malicious software – distributions package maintainers also serve as a filter between developers and users, helping to assure users get quality software.
-
-With bundled apps this may no longer be the case.
-
-As a finalizing general point, Till asserts that although useful in some cases, for the most part, bundled apps weakens the free software position in distributions (as proprietary vendors will now be able to deliver software without sharing it on public repositories).
-
-And apart from that, it introduces many other issues. Many problems are simply moved towards the developers.
-
-### The Pro Side
-
-In contrast, another comment by a person named Sven tries to contradict common claims that basically go against the use of bundled applications, hence justifying and promoting the use of it.
-
-“waste of space” – Sven claims that in today’s world we have many other things that wastes disk space, such as movies stored on hard drive, installed locals, etc…
-
-Ultimately, these things are infinitely more wasteful than a mere “100 MB to run a program you use all day … Don’t be ridiculous.”
-
-“waste of RAM” – the major points in favor are:
-
-- Shared libraries waste significantly less RAM compared to application runtime data.
-- RAM is cheap today.
-
-“security nightmare” – not every application you run is actually security-critical.
-
-Also, many applications never even see any security updates, unless on a ‘rolling distro’.
-
-In addition to Sven’s opinions, who try to stick to the pragmatic side, a few advantages were also pointed by Till who admits that bundled apps has their merits in certain cases:
-
-- Proprietary vendors who want to keep their code out of the public repositories will be able to do so more easily.
-- Niche applications, which are not packaged by your distribution, will now be more readily available.
-- Testing on binary distributions which do not have beta packages will become easier.
-- Freeing users from solving dependencies problems.
-
-### Final Thoughts
-
-Although shedding new light onto the matter, it seems that one conclusion still stands and accepted by all parties – bundled apps has their niche to fill in the Linux ecosystem.
-
-Nevertheless, the role that niche should take, whether main or marginal one, appears to be a lot clearer now, at least from a theoretical point of view.
-
-Users who are looking to make their system optimized as possible, should, in the majority of cases, avoid using bundled apps.
-
-Whereas, users that are after ease-of-use, meaning – doing the least of work in order to maintain their systems, should and probably would feel very comfortable adopting the new method.
-
---------------------------------------------------------------------------------
-
-via: http://www.iwillfolo.com/linux-applications-that-works-on-all-distributions-are-they-any-good/
-
-作者:[Editorials][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.iwillfolo.com/category/editorials/
-[1]: http://www.iwillfolo.com/ubuntus-snap-red-hats-flatpack-and-is-one-fits-all-linux-packages-useful/
-[2]: http://www.proli.net/2016/06/25/gnulinux-bundled-application-ramblings/
-[3]: http://www.iwillfolo.com/5-reasons-use-gentoo-linux/
diff --git a/sources/talk/20160627 Linux Practicality vs Activism.md b/sources/talk/20160627 Linux Practicality vs Activism.md
deleted file mode 100644
index fc4f5eff26..0000000000
--- a/sources/talk/20160627 Linux Practicality vs Activism.md	
+++ /dev/null
@@ -1,72 +0,0 @@
-Linux Practicality vs Activism
-==================================
-
->Is Linux actually more practical than other OSes, or is there some higher minded reason to use it?
-
-One of the greatest things about running Linux is the freedom it provides. Where the division among the Linux community appears is in how we value this freedom.
-
-For some, the freedom enjoyed by using Linux is the freedom from vendor lock-in or high software costs. Most would call this a practical consideration. Others users would tell you the freedom they enjoy is software freedom. This means embracing Linux distributions that support the [Free Software Movement][1], avoiding proprietary software completely and all things related.
-
-In this article, I'll walk you through some of the differences between these two freedoms and how they affect Linux usage.
-
-### The problem with proprietary
-
-One thing most Linux users have in common is their preference for avoiding proprietary software. For practical enthusiasts like myself, it's a matter of how I spend my money, the ability to control my software and avoiding vendor lock-in. Granted, I'm not a coder...so my tweaks to my installed software are pretty mild. But there are instances where a minor tweak to an application can mean the difference between it working and it not working.
-
-Then there are Linux enthusiasts who opt to avoid proprietary software because they feel it's unethical to use it. Usually the main concern here is that using proprietary software takes away or simply obstructs your personal freedom. Users in this corner prefer to use Linux distributions and software that support the [Free Software philosophy][2]. While it's similar to and often directly confused with Open Source concepts, [there are differences][3].
-
-So here's the issue: Users such as myself tend to put convenience over the ideals of pure software freedom. Don't get me wrong, folks like me prefer to use software that meets the ideals behind Free Software, but we also are more likely to make concessions in order to accomplish specific tasks.
-
-Both types of Linux enthusiasts prefer using non-proprietary solutions. But Free Software advocates won't use proprietary at all, where as the practical user will rely on the best tool with the best performance. This means there are instances where the practical user is willing to run a proprietary application or code on their non-proprietary operating system.
-
-In the end, both user types enjoy using what Linux has to offer. But our reasons for doing so tend to vary. Some have argued that this is a matter of ignorance with those who don't support Free Software. I disagree and believe it's a matter of practical convenience. Users who prefer practical convenience simply aren't concerned about the politics of their software.
-
-### Practical Convenience
-
-When you ask most people why they use the operating system they use, it's usually tied in with practical convenience. Examples of this convenience might include "it's what I've always used" down to "it runs the software I need." Other folks might take this a step further and explain it's not so much the software that drives their OS preference, as the familiarity of the OS in question. And finally, there are specialty "niche tasks" or hardware compatibility issues that also provide good reasons for using one OS over another.
-
-This might surprise many of you, but the single biggest reason I run desktop Linux today is due to familiarity. Even though I provide support for Windows and OS X for others, it's actually quite frustrating to use these operating systems as they're simply not what my muscle memory is used to. I like to believe this allows me to empathize with Linux newcomers, as I too know how off-putting it can be to step into the realm of the unfamiliar. My point here is this – familiarity has value. And familiarity also powers practical convenience as well.
-
-Now if we compare this to the needs of a Free Software advocate, you'll find those folks are willing to learn something new and perhaps even more challenging if it translates into them avoiding using non-free software. It's actually something I've always admired about this type of user. Their willingness to take the path less followed to stick to their principles is, in my opinion, admirable.
-
-### The price of freedom
-
-One area I don't envy is the extra work involved in making sure a Free Software advocate is always using Linux distros and hardware that respect their digital freedom according to the standards set forth by the [Free Software Foundation][4]. This means the Linux kernel needs to be free from proprietary blobs for driver support and the hardware in question doesn't require any proprietary code whatsoever. Certainly not impossible, but it's pretty close.
-
-The absolute best scenario a Free Software advocate can shoot for is hardware that is "freedom-compatible." There are vendors out there that can meet this need, however most of them are offering hardware that relies on Linux compatible proprietary firmware. Great for the practical user, a show-stopper for the Free Software advocate.
-
-What all of this translates into is that the advocate must be far more vigilant than the practical Linux enthusiast. This isn't necessarily a negative thing per se, however it's a consideration if one is planning on jumping onto the Free Software approach to computing. Practical users, by contrast, can use any software or hardware that happens to be Linux compatible without a second thought. I don't know about you, but in my eyes this seems a bit easier to me.
-
-### Defining software freedom
-
-This part is going to get some folks upset as I personally don't subscribe to the belief that there's only one flavor of software freedom. From where I stand, I think true freedom is being able to soak in all the available data on a given issue and then come to terms with the approach that best suits that person's lifestyle.
-
-So for me, I prefer using Linux distributions that provide me with the desktop that meets all of my needs. This includes the use of non-proprietary software and proprietary software. Even though it's fair to suggest that the proprietary software restricts my personal freedom, I must counter this by pointing out that I had the freedom to use it in the first place. One might even call this freedom of choice.
-
-Perhaps this too, is why I find myself identifying more with the ideals of Open Source Software instead of sticking with the ideals behind the Free Software movement. I prefer to stand with the group that doesn't spend their time telling me how I'm wrong for using what works best for me. It's been my experience that the Open Source crowd is merely interested in sharing the merits of software freedom without the passion for Free Software idealism.
-
-I think the concept of Free Software is great. And to those who need to be active in software politics and point out the flaws of using proprietary software to folks, then I think Linux ([GNU/Linux][5]) activism is a good fit. Where practical users such as myself tend to change course from Free Software Linux advocates is in our presentation.
-
-When I present Linux on the desktop, I share my passion for its practical merits. And if I'm successful and they enjoy the experience, I allow the user to discover the Free Software perspective on their own. I've found most people use Linux on their computers not because they want to embrace software freedom, rather because they simply want the best user experience possible. Perhaps I'm alone in this, it's hard to say.
-
-What say you? Are you a Free Software Advocate? Perhaps you're a fan of using proprietary software/code on your desktop Linux distribution? Hit the Comments and share your Linux desktop experiences.
-
-
---------------------------------------------------------------------------------
-
-via: http://www.datamation.com/open-source/linux-practicality-vs-activism.html
-
-作者:[Matt Hartley][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.datamation.com/author/Matt-Hartley-3080.html
-[1]: https://en.wikipedia.org/wiki/Free_software_movement
-[2]: https://www.gnu.org/philosophy/free-sw.en.html
-[3]: https://www.gnu.org/philosophy/free-software-for-freedom.en.html
-[4]: https://en.wikipedia.org/wiki/Free_Software_Foundation
-[5]: https://en.wikipedia.org/wiki/GNU/Linux_naming_controversy
-
-
diff --git a/sources/talk/20160808 What is open source.md b/sources/talk/20160808 What is open source.md
new file mode 100644
index 0000000000..b82747b426
--- /dev/null
+++ b/sources/talk/20160808 What is open source.md	
@@ -0,0 +1,123 @@
+rusking 翻译中
+
+What is open source
+===========================
+
+The term "open source" refers to something people can modify and share because its design is publicly accessible.
+
+The term originated in the context of software development to designate a specific approach to creating computer programs. Today, however, "open source" designates a broader set of values—what we call "[the open source way][1]." Open source projects, products, or initiatives embrace and celebrate principles of open exchange, collaborative participation, rapid prototyping, transparency, meritocracy, and community-oriented development.
+
+### What is open source software?
+
+Open source software is software with source code that anyone can inspect, modify, and enhance.
+
+"Source code" is the part of software that most computer users don't ever see; it's the code computer programmers can manipulate to change how a piece of software—a "program" or "application"—works. Programmers who have access to a computer program's source code can improve that program by adding features to it or fixing parts that don't always work correctly.
+
+### What's the difference between open source software and other types of software?
+
+Some software has source code that only the person, team, or organization who created it—and maintains exclusive control over it—can modify. People call this kind of software "proprietary" or "closed source" software.
+
+Only the original authors of proprietary software can legally copy, inspect, and alter that software. And in order to use proprietary software, computer users must agree (usually by signing a license displayed the first time they run this software) that they will not do anything with the software that the software's authors have not expressly permitted. Microsoft Office and Adobe Photoshop are examples of proprietary software.
+
+Open source software is different. Its authors [make its source code available][2] to others who would like to view that code, copy it, learn from it, alter it, or share it. [LibreOffice][3] and the [GNU Image Manipulation Program][4] are examples of open source software.
+
+As they do with proprietary software, users must accept the terms of a [license][5] when they use open source software—but the legal terms of open source licenses differ dramatically from those of proprietary licenses.
+
+Open source licenses affect the way people can [use, study, modify, and distribute][6] software. In general, open source licenses grant computer users [permission to use open source software for any purpose they wish][7]. Some open source licenses—what some people call "copyleft" licenses—stipulate that anyone who releases a modified open source program must also release the source code for that program alongside it. Moreover, [some open source licenses][8] stipulate that anyone who alters and shares a program with others must also share that program's source code without charging a licensing fee for it.
+
+By design, open source software licenses promote collaboration and sharing because they permit other people to make modifications to source code and incorporate those changes into their own projects. They encourage computer programmers to access, view, and modify open source software whenever they like, as long as they let others do the same when they share their work.
+
+### Is open source software only important to computer programmers?
+
+No. Open source technology and open source thinking both benefit programmers and non-programmers.
+
+Because early inventors built much of the Internet itself on open source technologies—like [the Linux operating system][9] and the[ Apache Web server][10] application—anyone using the Internet today benefits from open source software.
+
+Every time computer users view web pages, check email, chat with friends, stream music online, or play multiplayer video games, their computers, mobile phones, or gaming consoles connect to a global network of computers using open source software to route and transmit their data to the "local" devices they have in front of them. The computers that do all this important work are typically located in faraway places that users don't actually see or can't physically access—which is why some people call these computers "remote computers."
+
+More and more, people rely on remote computers when performing tasks they might otherwise perform on their local devices. For example, they may use online word processing, email management, and image editing software that they don't install and run on their personal computers. Instead, they simply access these programs on remote computers by using a Web browser or mobile phone application. When they do this, they're engaged in "remote computing."
+
+Some people call remote computing "cloud computing," because it involves activities (like storing files, sharing photos, or watching videos) that incorporate not only local devices but also a global network of remote computers that form an "atmosphere" around them.
+
+Cloud computing is an increasingly important aspect of everyday life with Internet-connected devices. Some cloud computing applications, like Google Apps, are proprietary. Others, like ownCloud and Nextcloud, are open source.
+
+Cloud computing applications run "on top" of additional software that helps them operate smoothly and efficiently, so people will often say that software running "underneath" cloud computing applications acts as a "platform" for those applications. Cloud computing platforms can be open source or closed source. OpenStack is an example of an open source cloud computing platform.
+
+### Why do people prefer using open source software?
+
+People prefer open source software to proprietary software for a number of reasons, including:
+
+Control. Many people prefer open source software because they [have more control][11] over that kind of software. They can examine the code to make sure it's not doing anything they don't want it to do, and they can change parts of it they don't like. Users who aren't programmers also benefit from open source software, because they can use this software for any purpose they wish—not merely the way someone else thinks they should.
+
+Training. Other people like open source software because it helps them [become better programmers][12]. Because open source code is publicly accessible, students can easily study it as they learn to make better software. Students can also share their work with others, inviting comment and critique, as they develop their skills. When people discover mistakes in programs' source code, they can share those mistakes with others to help them avoid making those same mistakes themselves.
+
+Security. Some people prefer open source software because they consider it more [secure][13] and stable than proprietary software. Because anyone can view and modify open source software, someone might spot and correct errors or omissions that a program's original authors might have missed. And because so many programmers can work on a piece of open source software without asking for permission from original authors, they can fix, update, and upgrade open source software more [quickly][14] than they can proprietary software.
+
+Stability. Many users prefer open source software to proprietary software for important, long-term projects. Because programmers [publicly distribute][15] the source code for open source software, users relying on that software for critical tasks can be sure their tools won't disappear or fall into disrepair if their original creators stop working on them. Additionally, open source software tends to both incorporate and operate according to open standards.
+
+### Doesn't "open source" just mean something is free of charge?
+
+No. This is a [common misconception][16] about what "open source" implies, and the concept's implications are [not only economic][17].
+
+Open source software programmers can charge money for the open source software they create or to which they contribute. But in some cases, because an open source license might require them to release their source code when they sell software to others, some programmers find that charging users money for software services and support (rather than for the software itself) is more lucrative. This way, their software remains free of charge, and they [make money helping others][18] install, use, and troubleshoot it.
+
+While some open source software may be free of charge, skill in programming and troubleshooting open source software can be [quite valuable][19]. Many employers specifically seek to [hire programmers with experience][20] working on open source software.
+
+### What is open source "beyond software"?
+
+At Opensource.com, we like to say that we're interested in the ways open source values and principles apply to the world beyond software. We like to think of open source as not only a way to develop and license computer software, but also an attitude.
+
+Approaching all aspects of life "[the open source way][21]" means expressing a willingness to share, collaborating with others in ways that are transparent (so that others can watch and join too), embracing failure as a means of improving, and expecting—even encouraging—everyone else to do the same.
+
+It also means committing to playing an active role in improving the world, which is possible only when [everyone has access][22] to the way that world is designed.
+
+The world is full of "source code"—[blueprints][23], [recipes][24], [rules][25]—that guide and shape the way we think and act in it. We believe this underlying code (whatever its form) should be open, accessible, and shared—so many people can have a hand in altering it for the better.
+
+Here, we tell stories about the impact of open source values on all areas of life—[science][26], [education][27], [government][28], [manufacturing][29], health, law, and [organizational dynamics][30]. We're a community committed to telling others how the open source way is the best way, because a love of open source is just like anything else: it's better when it's shared.
+
+Where can I learn more about open source?
+
+We've compiled several resources designed to help you learn more about [open source. We recommend you read our open source FAQs, how-to guides, and tutorials][31] to get started.
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/resources/what-open-source
+
+作者:[opensource.com][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: opensource.com
+[1]:https://opensource.com/open-source-way
+[2]:https://opensource.com/business/13/5/open-source-your-code
+[3]:https://www.libreoffice.org/
+[4]:http://www.gimp.org/
+[5]:https://opensource.com/law/13/1/which-open-source-software-license-should-i-use
+[6]:https://opensource.com/law/10/10/license-compliance-not-problem-open-source-users
+[7]:https://opensource.org/docs/osd
+[8]:https://opensource.com/law/13/5/does-your-code-need-license
+[9]:https://opensource.com/resources/what-is-linux
+[10]:http://httpd.apache.org/
+[11]:https://opensource.com/life/13/5/tumblr-open-publishing
+[12]:https://opensource.com/life/13/6/learning-program-open-source-way
+[13]:https://opensource.com/government/10/9/scap-computer-security-rest-us
+[14]:https://opensource.com/government/13/2/bug-fix-day
+[15]:https://opensource.com/life/12/9/should-we-develop-open-source-openly
+[16]:https://opensource.com/education/12/7/clearing-open-source-misconceptions
+[17]:https://opensource.com/open-organization/16/5/appreciating-full-power-open
+[18]:https://opensource.com/business/14/7/making-your-product-free-and-open-source-crazy-talk
+[19]:https://opensource.com/business/16/2/add-open-source-to-your-resume
+[20]:https://opensource.com/business/16/5/2016-open-source-jobs-report
+[21]:https://opensource.com/open-source-way
+[22]:https://opensource.com/resources/what-open-access
+[23]:https://opensource.com/life/11/6/architecture-open-source-applications-learn-those-you
+[24]:https://opensource.com/life/12/6/open-source-like-sharing-recipe
+[25]:https://opensource.com/life/12/4/day-my-mind-became-open-sourced
+[26]:https://opensource.com/resources/open-science
+[27]:https://opensource.com/resources/what-open-education
+[28]:https://opensource.com/resources/open-government
+[29]:https://opensource.com/resources/what-open-hardware
+[30]:https://opensource.com/resources/what-open-organization
+[31]:https://opensource.com/resources
diff --git a/sources/talk/20160912 Adobe's new CIO shares leadership advice for starting a new role.md b/sources/talk/20160912 Adobe's new CIO shares leadership advice for starting a new role.md
new file mode 100644
index 0000000000..a4a962e8d2
--- /dev/null
+++ b/sources/talk/20160912 Adobe's new CIO shares leadership advice for starting a new role.md	
@@ -0,0 +1,50 @@
+Adobe's new CIO shares leadership advice for starting a new role
+====
+
+![](https://enterprisersproject.com/sites/default/files/styles/620x350/public/images/CIO_Leadership_3.png?itok=QWUGMw-V)
+
+I’m currently a few months into a new CIO role at a highly-admired, cloud-based technology company. One of my first tasks was to get to know the organization’s people, culture, and priorities.
+
+As part of that goal, I am visiting all the major IT sites. While In India, less than two months into the job, I was asked directly: “What are you going to do? What is your plan?” My response, which will not surprise seasoned CIOs, was that I was still in discovery mode, and I was there to listen and learn.
+
+I’ve never gone into an organization with a set blueprint for what I’ll do. I know some CIOs have a playbook for how they will operate. They’ll come in and blow the whole organization up and put their set plan in motion.
+
+Yes, there may be situations where things are massively broken and not working, so that course of action makes sense. Once I’m inside a company, however, my strategy is to go through a discovery process. I don’t want to have any preconceived notions about the way things should be or what’s working versus what’s not.
+
+Here are my guiding principles as a newly-appointed leader:
+
+### Get to know your people
+
+This means building relationships, and it includes your IT staff as well as your business users and your top salespeople. What are the top things on their lists? What do they want you to focus on? What’s working well? What’s not? How is the customer experience? Knowing how you can help everyone be more successful will help you shape the way you deliver services to them.
+
+If your department is spread across several floors, as mine is, consider meet-and-greet lunches or mini-tech fairs so people can introduce themselves, discuss what they’re working on, and share stories about their family, if they feel comfortable doing that. If you have an open-door office policy, make sure they know that as well. If your staff spreads across countries or continents, get out there and visit as soon as you reasonably can.
+
+### Get to know your products and company culture
+
+One of the things that surprised me coming into to Adobe was how broad our product portfolio is. We have a platform of solutions and services across three clouds – Adobe Creative Cloud, Document Cloud and Marketing Cloud – and a vast portfolio of products within each. You’ll never know how much opportunity your new company presents until you get to know your products and learn how to support all of them. At Adobe we use many of our digital media and digital marketing solutions as Customer Zero, so we have first-hand experiences to share with our customers
+
+### Get to know customers
+
+Very early on, I started getting requests to meet with customers. Meeting with customers is a great way to jump-start your thinking into the future of the IT organization, which includes the different types of technologies, customers, and consumers we could have going forward.
+
+### Plan for the future
+
+As a new leader, I have a fresh perspective and can think about the future of the organization without getting distracted by challenges or obstacles.
+
+What CIOs need to do is jump-start IT into its next generation. When I meet my staff, I’m asking them what we want to be three to five years out so we can start positioning ourselves for that future. That means discussing the initiatives and priorities.
+
+After that, it makes sense to bring the leadership team together so you can work to co-create the next generation of the organization – its mission, vision, modes of alignment, and operating norms.  If you start changing IT from the inside out, it will percolate into business and everything else you do.
+
+Through this whole process, I’ve been very open with people that this is not going to be a top-down directive. I have ideas on priorities and what we need to focus on, but we have to be in lockstep, working as a team and figuring out what we want to do jointly.
+
+--------------------------------------------------------------------------------
+
+via: https://enterprisersproject.com/article/2016/9/adobes-new-cio-shares-leadership-advice-starting-new-role   
+
+作者:[Cynthia Stoddard][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://enterprisersproject.com/user/cynthia-stoddard
diff --git a/sources/talk/20160913 Linus Torvalds reveals his favorite programming laptop.md b/sources/talk/20160913 Linus Torvalds reveals his favorite programming laptop.md
new file mode 100644
index 0000000000..9741a58fcc
--- /dev/null
+++ b/sources/talk/20160913 Linus Torvalds reveals his favorite programming laptop.md	
@@ -0,0 +1,62 @@
+Linus Torvalds reveals his favorite programming laptop
+====
+
+>It's the Dell XPS 13 Developer Edition. Here's why.
+
+I recently talked with some Linux developers about what the best laptop is for serious programmers. As a result I checked out several laptops from a programmer's viewpoint. The winner in my book? The 2016 Dell XPS 13 Developer Edition. I'm in good company. Linus Torvalds, Linux's creator, agrees. The Dell XPS 13 Developer Edition, for him, is the best laptop around.
+
+![](http://zdnet3.cbsistatic.com/hub/i/r/2016/07/18/702609c3-db38-4603-9f5f-4dcc3d71b140/resize/770xauto/50a8ba1c2acb1f0994aec2115d2e55ce/2016-dell-xps-13.jpg)
+
+Torvald's requirements may not be yours though.
+
+On Google+, Torvalds explained, "First off: [I don't use my laptop as a desktop replacement][1], and I only travel for a small handful of events each year. So for me, the laptop is a fairly specialized thing that doesn't get daily (or even weekly) use, so the main criteria are not some kind of "average daily use", but very much "travel use".
+
+Therefore, for Torvalds, "I end up caring a lot about it being fairly small and light, because I may end up carrying it around all day at a conference. I also want it to have a good screen, because by now I'm just used to it at my main desktop, and I want my text to be legible but small."
+
+The Dell's display is powered by Intel's Iris 540 GPU. In my experience it works really well.
+
+The Iris powers a 13.3 inch display with a 3,200×1,800 touchscreen. That's 280 pixels per inch, 40 more than my beloved [2015 Chromebook Pixel][2] and 60 more than a [MacBook Pro with Retina][3].
+
+However, getting that hardware to work and play well with the [Gnome][4] desktop isn't easy. As Torvalds explained in another post, it "has the [same resolution as my desktop][5], but apparently because the laptop screen is smaller, Gnome seems to decide on its own that I need an automatic scaling factor of 2, which blows up all the stupid things (window decorations, icons etc) to a ridiculous degree".
+
+The solution? You can forget about looking to the user interface. You need to go to the shell and run: gsettings set org.gnome.desktop.interface scaling-factor 1.
+
+Torvalds may use Gnome, but he's [never liked the Gnome 3.x family much][6]. I can't argue with him. That's why I use [Cinnamon][7] instead.
+
+He also wants "a reasonably powerful CPU, because when I'm traveling I still build the kernel a lot. I don't do my normal full 'make allmodconfig' build between each pull request like I do at home, but I'd like to do it more often than I did with my previous laptop, which is actually (along with the screen) the main reason I wanted to upgrade."
+
+Linus doesn't describe the features of his XPS 13, but my review unit was a high-end model. It came with dual-core, 2.2GHz 6th Generation Intel Core i7-6560U Skylake processor and 16GBs of DDR3 RAM with a half a terabyte, PCIe solid state drive (SSD). I'm sure Torvalds' system is at least that well-equipped.
+
+Some features you may care about aren't on Torvalds' list.
+
+>"What I don't tend to care about is touch-screens, because my fingers are big and clumsy compared to the text I'm looking at (I also can't handle the smudges: maybe I just have particularly oily fingers, but I really don't want to touch that screen).
+
+I also don't care deeply about some 'all day battery life', because quite frankly, I can't recall the last time I didn't have access to power. I might not want to bother to plug it in for some quick check, but it's just not a big overwhelming issue. By the time battery life is in 'more than a couple of hours', I just don't care very much any more."
+Dell claims the XPS 13, with its 56wHR, 4-Cell Battery, has about a 12-hour battery life. It has well over 10 in my experience. I haven't tried to run it down to the dregs.
+
+Torvalds also didn't have any trouble with the Intel Wi-Fi set. The non Developer Edition uses a Broadcom chip set and that has proven troublesome for both Windows and Linux users. Dell technical support was extremely helpful to me in getting this problem under control.
+
+Some people have trouble with the XPS 13 touchpad. Neither I nor Torvalds have any worries. Torvalds wrote, the "XPS13 touchpad works very well for me. That may be a personal preference thing, but it seems to be both smooth and responsive."
+
+Still, while Torvalds likes the XPS 13, he's also fond of the latest Lenovo X1 Carbon, HP Spectre 13 x360, and last year's Lenovo Yoga 900. Me? I like the XPS 13 Developer Editor. The price tag, which for the model I reviewed was $1949.99, may keep you from reaching for your credit card.
+
+Still, if you want to develop like one of the world's top programmers, the Dell XPS 13 Developer Edition is worth the money.
+
+--------------------------------------------------------------------------------
+
+via: http://www.zdnet.com/article/linus-torvalds-reveals-his-favorite-programming-laptop/
+
+作者:[Steven J. Vaughan-Nichols ][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.zdnet.com/meet-the-team/us/steven-j-vaughan-nichols/
+[1]: https://plus.google.com/+LinusTorvalds/posts/VZj8vxXdtfe
+[2]: http://www.zdnet.com/article/the-best-chromebook-ever-the-chromebook-pixel-2015/
+[3]: http://www.zdnet.com/product/apple-15-inch-macbook-pro-with-retina-display-mid-2015/
+[4]: https://www.gnome.org/
+[5]: https://plus.google.com/+LinusTorvalds/posts/d7nfnWSXjfD
+[6]: http://www.zdnet.com/article/linus-torvalds-finds-gnome-3-4-to-be-a-total-user-experience-design-failure/
+[7]: http://www.zdnet.com/article/how-to-customise-your-linux-desktop-cinnamon/
diff --git a/sources/talk/20160915 Should Smartphones Do Away with the Headphone Jack Here Are Our Thoughts.md b/sources/talk/20160915 Should Smartphones Do Away with the Headphone Jack Here Are Our Thoughts.md
new file mode 100644
index 0000000000..0bf4b144f9
--- /dev/null
+++ b/sources/talk/20160915 Should Smartphones Do Away with the Headphone Jack Here Are Our Thoughts.md	
@@ -0,0 +1,54 @@
+Should Smartphones Do Away with the Headphone Jack? Here Are Our Thoughts
+====
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/09/Writers-Opinion-Headphone-Featured.jpg)
+
+Even though Apple removing the headphone jack from the iPhone 7 has been long-rumored, after the official announcement last week confirming the news, it has still become a hot topic.
+
+For those not in the know on this latest news, Apple has removed the headphone jack from the phone, and the headphones will now plug into the lightning port. Those that want to still use their existing headphones may, as there is an adapter that ships with the phone along with the lightning headphones. They are also selling a new product: AirPods. These are wireless and are inserted into your ear. The biggest advantage is that by eliminating the jack they were able to make the phone dust and water-resistant.
+
+Being it’s such a big story right now, we asked our writers, “What are your thoughts on Smartphones doing away with the headphone jack?”
+
+### Our Opinion
+
+Derrik believes that “Apple’s way of doing it is a play to push more expensive peripherals that do not comply to an open standard.” He also doesn’t want to have to charge something every five hours, meaning the AirPods. While he understands that the 3.5mm jack is aging, as an “audiophile” he would love a new, open standard, but “proprietary pushes” worry him about device freedom.
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/09/headphone-jacks.jpg)
+
+Damien doesn’t really even use the headphone jack these days as he has Bluetooth headphones. He hates that wire anyway, so feels “this is a good move.” Yet he also understands Derrik’s point about the wireless headphones running out of battery, leaving him with “nothing to fall back on.”
+
+Trevor is very upfront in saying he thought it was “dumb” until he heard you couldn’t charge the phone and use the headphones at the same time and realized it was “dumb X 2.” He uses the headphones/headphone jack all the time in a work van without Bluetooth and listens to audio or podcasts. He uses the plug-in style as Bluetooth drains his battery.
+
+Simon is not a big fan. He hasn’t seen much reasoning past it leaving more room within the device. He figures “it will then come down to whether or not consumers favor wireless headphones, an adapter, and water-resistance over not being locked into AirPods, lightning, or an adapter”. He fears it might be “too early to jump into removing ports” and likes a “one pair fits all” standard.
+
+James believes that wireless technology is progressive, so he sees it as a good move “especially for Apple in terms of hardware sales.” He happens to use expensive headphones, so personally he’s “yet to be convinced,” noting his Xperia is waterproof and has a jack.
+
+Jeffry points out that “almost every transition attempt in the tech world always starts with strong opposition from those who won’t benefit from the changes.” He remembers the flak Apple received when they removed the floppy disk drive and decided not to support Flash, and now both are industry standards. He believes everything is evolving for the better, removing the audio jack is “just the first step toward the future,” and Apple is just the one who is “brave enough to lead the way (and make a few bucks in doing so).”
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/09/Writers-Opinion-Headphone-Headset.jpg)
+
+Vamsi doesn’t mind the removal of the headphone jack as long as there is a “better solution applicable to all the users that use different headphones and other gadgets.” He doesn’t feel using headphones via a lightning port is a good solution as it renders nearly all other headphones obsolete. Regarding Bluetooth headphones, he just doesn’t want to deal with another gadget. Additionally, he doesn’t get the argument of it being good for water resistance since there are existing devices with headphone jacks that are water resistant.
+
+Mahesh prefers a phone with a jack because many times he is charging his phone and listening to music simultaneously. He believes we’ll get to see how it affects the public in the next few months.
+
+Derrik chimed back in to say that by “removing open standard ports and using a proprietary connection too.,” you can be technical and say there are adapters, but Thunderbolt is also closed, and Apple can stop selling those adapters at any time. He also notes that the AirPods won’t be Bluetooth.
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/09/Writers-Opinion-Headphone-AirPods.jpg)
+
+As for me, I’m always up for two things: New technology and anything Apple. I’ve been using iPhones since a few weeks past the very first model being introduced, yet I haven’t updated since 2012 and the iPhone 5, so I was overdue. I’ll be among the first to get my hands on the iPhone 7. I hate that stupid white wire being in my face, so I just might be opting for AirPods at some point. I am very appreciative of the phone becoming water-resistant. As for charging vs. listening, the charge on new iPhones lasts so long that I don’t expect it to be much of a problem. Even my old iPhone 5 usually lasts about twenty hours on a good day and twelve hours on a bad day. So I don’t expect that to be a problem.
+
+### Your Opinion
+
+Our writers have given you a lot to think about. What are your thoughts on Smartphones doing away with the headphone jack? Will you miss it? Is it a deal breaker for you? Or do you relish the upgrade in technology? Will you be trying the iPhone 5 or the AirPods? Let us know in the comments below.
+
+--------------------------------------------------------------------------------
+
+via: https://www.maketecheasier.com/should-smartphones-do-away-with-the-headphone-jack/?utm_medium=feed&utm_source=feedpress.me&utm_campaign=Feed%3A+maketecheasier
+
+作者:[Laura Tucker][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.maketecheasier.com/author/lauratucker/
diff --git a/sources/talk/20160915 What the rise of permissive open source licenses means.md b/sources/talk/20160915 What the rise of permissive open source licenses means.md
new file mode 100644
index 0000000000..9a17fec1e0
--- /dev/null
+++ b/sources/talk/20160915 What the rise of permissive open source licenses means.md	
@@ -0,0 +1,57 @@
+What the rise of permissive open source licenses means
+====
+
+Why restrictive licenses such as the GNU GPL are steadily falling out of favor.
+
+"If you use any open source software, you have to make the rest of your software open source." That's what former Microsoft CEO Steve Ballmer said back in 2001, and while his statement was never true, it must have spread some FUD (fear, uncertainty and doubt) about free software. Probably that was the intention.
+
+This FUD about open source software is mainly about open source licensing. There are many different licenses, some more restrictive (some people use the term "protective") than others. Restrictive licenses such as the GNU General Public License (GPL) use the concept of copyleft, which grants people the right to freely distribute copies and modified versions of a piece of software as long as the same rights are preserved in derivative works. The GPL (v3) is used by open source projects such as bash and GIMP. There's also the Affero GPL, which provides copyleft to software that is offered over a network (for example as a web service.)
+
+What this means is that if you take code that is licensed in this way and you modify it by adding some of your own proprietary code, then in some circumstances the whole new body of code, including your code, becomes subject to the restrictive open source license. It was this type of license that Ballmer was probably referring to when he made his statement.
+
+But permissive licenses are a different animal. The MIT License, for example, lets anyone take open source code and do what they want with it — including modifying and selling it — as long as they provide attribution and don't hold the developer liable. Another popular permissive open source license, the Apache License 2.0, also provides an express grant of patent rights from contributors to users. JQuery, the .NET Core and Rails are licensed using the MIT license, while the Apache 2.0 license is used by software including Android, Apache and Swift.
+
+Ultimately both license types are intended to make software more useful. Restrictive licenses aim to foster the open source ideals of participation and sharing so everyone gets the maximum benefit from software. And permissive licenses aim to ensure that people can get the maximum benefit from software by allowing them to do what they want with it — even if that means they take the code, modify it and keep it for themselves or even sell the resulting work as proprietary software without contributing anything back.
+
+Figures compiled by open source license management company Black Duck Software show that the restrictive GPL 2.0 was the most commonly used open source license last year with about 25 percent of the market. The permissive MIT and Apache 2.0 licenses were next with about 18 percent and 16 percent respectively, followed by the GPL 3.0 with about 10 percent. That's almost evenly split at 35 percent restrictive and 34 percent permissive.
+
+But this snapshot misses the trend. Black Duck's data shows that in the six years from 2009 to 2015 the MIT license's share of the market has gone up 15.7 percent and Apache's share has gone up 12.4 percent. GPL v2 and v3's share during the same period has dropped by a staggering 21.4 percent. In other words there was a significant move away from restrictive licenses and towards permissive ones during that period.
+
+And the trend is continuing. Black Duck's [latest figures][1] show that MIT is now at 26 percent, GPL v2 21 percent, Apache 2 16 percent, and GPL v3 9 percent. That's 30 percent restrictive, 42 percent permissive — a huge swing from last year’s 35 percent restrictive and 34 percent permissive. Separate [research][2] of the licenses used on GitHub appears to confirm this shift. It shows that MIT is overwhelmingly the most popular license with a 45 percent share, compared to GLP v2 with just 13 percent and Apache with 11 percent.
+
+![](http://images.techhive.com/images/article/2016/09/open-source-licenses.jpg-100682571-large.idge.jpeg)
+
+### Driving the trend
+
+What’s behind this mass move from restrictive to permissive licenses? Do companies fear that if they let restrictive software into the house they will lose control of their proprietary software, as Ballmer warned? In fact, that may well be the case. Google, for example, has [banned Affero GPL software][3] from its operations.
+
+Jim Farmer, chairman of [Instructional Media + Magic][4], a developer of open source technology for education, believes that many companies avoid restrictive licenses to avoid legal difficulties. "The problem is really about complexity. The more complexity in a license, the more chance there is that someone has a cause of action to bring you to court. Complexity makes litigation more likely," he says.
+
+He adds that fear of restrictive licenses is being driven by lawyers, many of whom recommend that clients use software that is licensed with the MIT or Apache 2.0 licenses, and who specifically warn against the Affero license.
+
+This has a knock-on effect with software developers, he says, because if companies avoid software with restrictive licenses then developers have more incentive to license their new software with permissive ones if they want it to get used.
+
+But Greg Soper, CEO of SalesAgility, the company behind the open source SuiteCRM, believes that the move towards permissive licenses is also being driven by some developers. "Look at an application like Rocket.Chat. The developers could have licensed that with GPL 2.0 or Affero but they chose a permissive license," he says. "That gives the app the widest possible opportunity, because a proprietary vendor can take it and not harm their product or expose it to an open source license. So if a developer wants an application to be used inside a third-party application it makes sense to use a permissive license."
+
+Soper points out that restrictive licenses are designed to help an open source project succeed by stopping developers from taking other people's code, working on it, and then not sharing the results back with the community. "The Affero license is critical to the health of our product because if people could make a fork that was better than ours and not give the code back that would kill our product," he says. "For Rocket.Chat it's different because if it used Affero then it would pollute companies' IP and so it wouldn't get used. Different licenses have different use cases."
+
+Michael Meeks, an open source developer who has worked on Gnome, OpenOffice and now LibreOffice, agrees with Jim Farmer that many companies do choose to use software with permissive licenses for fear of legal action. "There are risks with copyleft licenses, but there are also huge benefits. Unfortunately people listen to lawyers, and lawyers talk about risk but they never tell you that something is safe."
+
+Fifteen years after Ballmer made his inaccurate statement it seems that the FUD it generated it is still having an effect — even if the move from restrictive licenses to permissive ones is not quite the effect he intended.
+
+--------------------------------------------------------------------------------
+
+via: http://www.cio.com/article/3120235/open-source-tools/what-the-rise-of-permissive-open-source-licenses-means.html
+
+作者:[Paul Rubens  ][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.cio.com/author/Paul-Rubens/
+[1]: https://www.blackducksoftware.com/top-open-source-licenses
+[2]: https://github.com/blog/1964-open-source-license-usage-on-github-com
+[3]: http://www.theregister.co.uk/2011/03/31/google_on_open_source_licenses/
+[4]: http://immagic.com/
+
diff --git a/sources/team_test/ Building a data science portfolio - Machine learning project.md b/sources/team_test/ Building a data science portfolio - Machine learning project.md
new file mode 100644
index 0000000000..5213042360
--- /dev/null
+++ b/sources/team_test/ Building a data science portfolio - Machine learning project.md	
@@ -0,0 +1,837 @@
+Building a data science portfolio: Machine learning project
+===========================================================
+
+>这是这个系列的第三次发布关于如何建立科学的投资数据. 如果你喜欢这个系列并且想继续关注, 你可以在订阅页面的底部找到链接[subscribe at the bottom of the page][1].
+
+数据科学公司越来越关注投资组合问题. 这其中的一个原因是,投资组合是最好的判断人们生活技能的方法. 好消息是投资组合是完全在你的控制之下的. 只要你做些投资方面的工作,就可以做出很棒的投资组合.
+
+高质量投资组合的第一步就是知道需要什么技能. 客户想要将这些初级技能应用到数据科学, 因此这些投资技能显示如下:
+
+- 沟通能力
+- 协作能力
+- 技术能力
+- 数据推理能力
+- 主动性
+
+任何好的投资组合都由多个能力组成,其中必然包含以上一到两点. 这里我们主要讲第三点如何做好科学的数据投资组合. 在这一节, 我们主要讲第二项以及如何创建一个端对端的机器学习项目. 在最后, 在最后我们将拥有一个项目它将显示你的能力和技术水平. [Here’s][2]如果你想看一下这里有一个完整的例子.
+
+### 一个端到端的项目
+
+作为一个数据科学家, 有时候你会拿到一个数据集并被问到是 [如何产生的][3]. 在这个时候, 交流是非常重要的, 走一遍流程. 用用Jupyter notebook, 看一看以前的例子,这对你非常有帮助. 在这里你能找到一些可以用的报告或者文档.
+
+不管怎样, 有时候你会被要求创建一个具有操作价值的项目. 一个直接影响公司业务的项目, 不止一次的, 许多人用的项目. 这个任务可能像这样 “创建一个算法来预测波动率”或者 “创建一个模型来自动标签我们的文章”. 在这种情况下, 技术能力比说评书更重要. 你必须能够创建一个数据集, 并且理解它, 然后创建脚本处理该数据. 还有很重要的脚本要运行的很快, 占用系统资源很小. 它可能要运行很多次, 脚本的可使用性也很重要,并不仅仅是一个演示版. 可使用性是指整合操作流程, 因为他很有可能是面向用户的.
+
+端对端项目的主要组成部分:
+
+- 理解背景
+- 浏览数据并找出细微差别
+- 创建结构化项目, 那样比较容易整合操作流程
+- 运行速度快占用系统资源小的代码
+- 写好文档以便其他人用
+
+为类有效的创建这种类型的项目, 我们可能需要处理多个文件. 强烈推荐使用 [Atom][4]或者[PyCharm][5] . 这些工具允许你在文件间跳转, 编辑不同类型的文件, 例如 markdown 文件, Python 文件, 和csv 文件. 结构化你的项目还利于版本控制 [Github][6] 也很有用.
+
+![](https://www.dataquest.io/blog/images/end_to_end/github.png)
+>Github上的这个项目.
+
+在这一节中我们将使用  [Pandas][7] 和 [scikit-learn][8]扩展包 . 我们还将用到Pandas [DataFrames][9], 它使得python读取和处理表格数据更加方便.
+
+### 找到好的数据集
+
+找到一个好的端到端投资项目数据集很难. [The dataset][10]数据集需要足够大但是内存和性能限制了它. 它还需要实际有用的. 例如, 这个数据集, 它包含有美国院校的录取标准, 毕业率以及毕业以后的收入是个很好的数据集了. 不管怎样, 不管你如何想这个数据, 很显然它不适合创建端到端的项目. 比如, 你能告诉人们他们去了这些大学以后的未来收益, 但是却没有足够的细微差别. 你还能找出院校招生标准收入更高, 但是却没有告诉你如何实际操作.
+
+这里还有内存和性能约束的问题比如你有几千兆的数据或者有一些细微差别需要你去预测或者运行什么样的算法数据集等.
+
+一个好的数据集包括你可以动态的转换数组, 并且回答动态的问题. 一个很好的例子是股票价格数据集. 你可以预测明天的价格, 持续的添加数据在算法中. 它将有助于你预测利润. 这不是讲故事这是真实的.
+
+一些找到数据集的好地方:
+
+- [/r/datasets][11] – subreddit(Reddit是国外一个社交新闻站点,subreddit指该论坛下的各不同板块).
+- [Google Public Datasets][12] – 通过Google BigQuery发布的可用数据集.
+- [Awesome datasets][13] – Github上的数据集.
+
+当你查看这些数据集, 想一下人们想要在这些数据集中得到什么答案, 哪怕这些问题只想过一次 (“放假是如何S&P 500关联的?”), 或者更进一步(“你能预测股市吗?”). 这里的关键是更进一步找出问题, 并且多次运行不同的数据相同的代码.
+
+为了这个目标, 我们来看一下[Fannie Mae 贷款数据][14]. Fannie Mae 是一家政府赞助的企业抵押贷款公司它从其他银行购买按揭贷款. 然后捆绑这些贷款为抵押贷款来倒卖证券. 这使得贷款机构可以提供更多的抵押贷款, 在市场上创造更多的流动性. 这在理论上会导致更多的住房和更好的贷款条件. 从借款人的角度来说,他们大体上差不多, 话虽这样说.
+
+Fannie Mae 发布了两种类型的数据 – 它获得的贷款, 随着时间的推移这些贷款是否被偿还.在理想的情况下, 有人向贷款人借钱, 然后还清贷款. 不管怎样, 有些人没还的起钱, 丧失了抵押品赎回权. Foreclosure 是说没钱还了被银行把房子给回收了. Fannie Mae 追踪谁没还钱, 并且需要收回房屋抵押权. 每个季度会发布此数据, 并滞后一年. 当前可用是2015年第一季度数据.
+
+采集数据是由Fannie Mae发布的贷款数据, 它包含借款人的信息, 信用评分, 和他们的家庭贷款信息. 性能数据, 贷款回收后的每一个季度公布, 包含借贷人所支付款项信息和丧失抵押品赎回状态, 收回贷款的性能数据可能有十几行.一个很好的思路是这样的采集数据告诉你Fannie Mae所控制的贷款, 性能数据包含几个属性来更新贷款. 其中一个属性告诉我们每个季度的贷款赎回权.
+
+![](https://www.dataquest.io/blog/images/end_to_end/foreclosure.jpg)
+>一个没有及时还贷的房子就这样的被卖了.
+
+### 选择一个角度
+
+这里有几个方向我们可以去分析 Fannie Mae 数据集. 我们可以:
+
+- 预测房屋的销售价格.
+- 预测借款人还款历史.
+- 在收购时为每一笔贷款打分.
+
+最重要的事情是坚持单一的角度. 关注太多的事情很难做出效果. 选择一个有着足够细节的角度也很重要. 下面的理解就没有太多差别:
+
+- 找出哪些银行将贷款出售给Fannie Mae.
+- 计算贷款人的信用评分趋势.
+- 搜索哪些类型的家庭没有偿还贷款的能力.
+- 搜索贷款金额和抵押品价格之间的关系
+
+上面的想法非常有趣, 它会告诉我们很多有意思的事情, 但是不是一个很适合操作的项目。
+
+在Fannie Mae数据集中, 我们将预测贷款是否能被偿还. 实际上, 我们将建立一个抵押贷款的分数来告诉 Fannie Mae买还是不买. 这将给我提供很好的基础.
+
+------------
+
+### 理解数据
+我们来简单看一下原始数据文件。下面是2012年1季度前几行的采集数据。
+```
+100000853384|R|OTHER|4.625|280000|360|02/2012|04/2012|31|31|1|23|801|N|C|SF|1|I|CA|945||FRM|
+100003735682|R|SUNTRUST MORTGAGE INC.|3.99|466000|360|01/2012|03/2012|80|80|2|30|794|N|P|SF|1|P|MD|208||FRM|788
+100006367485|C|PHH MORTGAGE CORPORATION|4|229000|360|02/2012|04/2012|67|67|2|36|802|N|R|SF|1|P|CA|959||FRM|794
+```
+
+下面是2012年1季度的前几行执行数据
+```
+100000853384|03/01/2012|OTHER|4.625||0|360|359|03/2042|41860|0|N||||||||||||||||
+100000853384|04/01/2012||4.625||1|359|358|03/2042|41860|0|N||||||||||||||||
+100000853384|05/01/2012||4.625||2|358|357|03/2042|41860|0|N||||||||||||||||
+```
+在开始编码之前,花些时间真正理解数据是值得的。这对于操作项目优为重要,因为我们没有交互式探索数据,将很难察觉到细微的差别除非我们在前期发现他们。在这种情况下,第一个步骤是阅读房利美站点的资料:
+- [概述][15]
+- [有用的术语表][16]
+- [问答][17]
+- [采集和执行文件中的列][18]
+- [采集数据文件样本][19]
+- [执行数据文件样本][20]
+
+在看完这些文件后后,我们了解到一些能帮助我们的关键点:
+- 从2000年到现在,每季度都有一个采集和执行文件,因数据是滞后一年的,所以到目前为止最新数据是2015年的。
+- 这些文件是文本格式的,采用管道符号“|”进行分割。
+- 这些文件是没有表头的,但我们有文件列明各列的名称。
+- 所有一起,文件包含2200万个贷款的数据。
+由于执行文件包含过去几年获得的贷款的信息,在早些年获得的贷款将有更多的执行数据(即在2014获得的贷款没有多少历史执行数据)。
+这些小小的信息将会为我们节省很多时间,因为我们知道如何构造我们的项目和利用这些数据。
+
+### 构造项目
+在我们开始下载和探索数据之前,先想一想将如何构造项目是很重要的。当建立端到端项目时,我们的主要目标是:
+- 创建一个可行解决方案
+- 有一个快速运行且占用最小资源的解决方案
+- 容易可扩展
+- 写容易理解的代码
+- 写尽量少的代码
+
+为了实现这些目标,需要对我们的项目进行良好的构造。一个结构良好的项目遵循几个原则:
+- 分离数据文件和代码文件
+- 从原始数据中分离生成的数据。
+- 有一个README.md文件帮助人们安装和使用该项目。
+- 有一个requirements.txt文件列明项目运行所需的所有包。
+- 有一个单独的settings.py 文件列明其它文件中使用的所有的设置
+    - 例如,如果从多个Python脚本读取相同的文件,把它们全部import设置和从一个集中的地方获得文件名是有用的。
+- 有一个.gitignore文件,防止大的或秘密文件被提交。
+- 分解任务中每一步可以单独执行的步骤到单独的文件中。
+    - 例如,我们将有一个文件用于读取数据,一个用于创建特征,一个用于做出预测。
+- 保存中间结果,例如,一个脚本可输出下一个脚本可读取的文件。
+
+    - 这使我们无需重新计算就可以在数据处理流程中进行更改。
+    
+
+我们的文件结构大体如下:
+
+```
+loan-prediction
+├── data
+├── processed
+├── .gitignore
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
+### 创建初始文件
+首先,我们需要创建一个loan-prediction文件夹,在此文件夹下面,再创建一个data文件夹和一个processed文件夹。data文件夹存放原始数据,processed文件夹存放所有的中间计算结果。
+其次,创建.gitignore文件,.gitignore文件将保证某些文件被git忽略而不会被推送至github。关于这个文件的一个好的例子是由OSX在每一个文件夹都会创建的.DS_Store文件,.gitignore文件一个很好的起点就是在这了。我们还想忽略数据文件因为他们实在是太大了,同时房利美的条文禁止我们重新分发该数据文件,所以我们应该在我们的文件后面添加以下2行:
+```
+data
+processed
+```
+
+这是该项目的一个关于.gitignore文件的例子。
+再次,我们需要创建README.md文件,它将帮助人们理解该项目。后缀.md表示这个文件采用markdown格式。Markdown使你能够写纯文本文件,同时还可以添加你想要的梦幻格式。这是关于markdown的导引。如果你上传一个叫README.md的文件至Github,Github会自动处理该markdown,同时展示给浏览该项目的人。
+至此,我们仅需在README.md文件中添加简单的描述:
+```
+Loan Prediction
+-----------------------
+
+Predict whether or not loans acquired by Fannie Mae will go into foreclosure.  Fannie Mae acquires loans from other lenders as a way of inducing them to lend more.  Fannie Mae releases data on the loans it has acquired and their performance afterwards [here](http://www.fanniemae.com/portal/funding-the-market/data/loan-performance-data.html).
+```
+
+现在,我们可以创建requirements.txt文件了。这会唯其它人可以很方便地安装我们的项目。我们还不知道我们将会具体用到哪些库,但是以下几个库是一个很好的开始:
+```
+pandas
+matplotlib
+scikit-learn
+numpy
+ipython
+scipy
+```
+
+以上几个是在python数据分析任务中最常用到的库。可以认为我们将会用到大部分这些库。这里是【24】该项目requirements文件的一个例子。
+ 创建requirements.txt文件之后,你应该安装包了。我们将会使用python3.如果你没有安装python,你应该考虑使用 [Anaconda][25],一个python安装程序,同时安装了上面列出的所有包。
+最后,我们可以建立一个空白的settings.py文件,因为我们的项目还没有任何设置。
+
+
+----------------
+
+### 获取数据
+
+一旦我们拥有项目的基本架构,我们就可以去获得原始数据
+
+Fannie Mae 对获取数据有一些限制,所有你需要去注册一个账户。在创建完账户之后,你可以找到下载页面[在这里][26],你可以按照你所需要的下载非常少量或者很多的借款数据文件。文件格式是zip,在解压后当然是非常大的。
+
+为了达到我们这个博客文章的目的,我们将要下载从2012年1季度到2015
+年1季度的所有数据。接着我们需要解压所有的文件。解压过后,删掉原来的.zip格式的文件。最后,借款预测文件夹看起来应该像下面的一样:
+```
+loan-prediction
+├── data
+│   ├── Acquisition_2012Q1.txt
+│   ├── Acquisition_2012Q2.txt
+│   ├── Performance_2012Q1.txt
+│   ├── Performance_2012Q2.txt
+│   └── ...
+├── processed
+├── .gitignore
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
+在下载完数据后,你可以在shell命令行中使用head和tail命令去查看文件中的行数据,你看到任何的不需要的列数据了吗?在做这件事的同时查阅[列名称的pdf文件][27]可能是有用的事情。
+### 读入数据
+
+有两个问题让我们的数据难以现在就使用:
+- 收购数据和业绩数据被分割在多个文件中
+- 每个文件都缺少标题
+
+在我们开始使用数据之前,我们需要首先明白我们要在哪里去存一个收购数据的文件,同时到哪里去存储一个业绩数据的文件。每个文件仅仅需要包括我们关注的那些数据列,同时拥有正确的标题。这里有一个小问题是业绩数据非常大,因此我们需要尝试去修剪一些数据列。
+
+第一步是向settings.py文件中增加一些变量,这个文件中同时也包括了我们原始数据的存放路径和处理出的数据存放路径。我们同时也将添加其他一些可能在接下来会用到的设置数据:
+```
+DATA_DIR = "data"
+PROCESSED_DIR = "processed"
+MINIMUM_TRACKING_QUARTERS = 4
+TARGET = "foreclosure_status"
+NON_PREDICTORS = [TARGET, "id"]
+CV_FOLDS = 3
+```
+
+把路径设置在settings.py中将使它们放在一个集中的地方,同时使其修改更加的容易。当提到在多个文件中的相同的变量时,你想改变它的话,把他们放在一个地方比分散放在每一个文件时更加容易。[这里的][28]是一个这个工程的示例settings.py文件
+
+第二步是创建一个文件名为assemble.py,它将所有的数据分为2个文件。当我们运行Python assemble.py,我们在处理数据文件的目录会获得2个数据文件。
+
+接下来我们开始写assemble.py文件中的代码。首先我们需要为每个文件定义相应的标题,因此我们需要查看[列名称的pdf文件][29]同时创建在每一个收购数据和业绩数据的文件的列数据的列表:
+```
+HEADERS = {
+    "Acquisition": [
+        "id",
+        "channel",
+        "seller",
+        "interest_rate",
+        "balance",
+        "loan_term",
+        "origination_date",
+        "first_payment_date",
+        "ltv",
+        "cltv",
+        "borrower_count",
+        "dti",
+        "borrower_credit_score",
+        "first_time_homebuyer",
+        "loan_purpose",
+        "property_type",
+        "unit_count",
+        "occupancy_status",
+        "property_state",
+        "zip",
+        "insurance_percentage",
+        "product_type",
+        "co_borrower_credit_score"
+    ],
+    "Performance": [
+        "id",
+        "reporting_period",
+        "servicer_name",
+        "interest_rate",
+        "balance",
+        "loan_age",
+        "months_to_maturity",
+        "maturity_date",
+        "msa",
+        "delinquency_status",
+        "modification_flag",
+        "zero_balance_code",
+        "zero_balance_date",
+        "last_paid_installment_date",
+        "foreclosure_date",
+        "disposition_date",
+        "foreclosure_costs",
+        "property_repair_costs",
+        "recovery_costs",
+        "misc_costs",
+        "tax_costs",
+        "sale_proceeds",
+        "credit_enhancement_proceeds",
+        "repurchase_proceeds",
+        "other_foreclosure_proceeds",
+        "non_interest_bearing_balance",
+        "principal_forgiveness_balance"
+    ]
+}
+```
+
+
+接下来一步是定义我们想要保持的数据列。我们将需要保留收购数据中的所有列数据,丢弃列数据将会使我们节省下内存和硬盘空间,同时也会加速我们的代码。
+```
+SELECT = {
+    "Acquisition": HEADERS["Acquisition"],
+    "Performance": [
+        "id",
+        "foreclosure_date"
+    ]
+}
+```
+
+下一步,我们将编写一个函数来连接数据集。下面的代码将:
+- 引用一些需要的库,包括设置。
+- 定义一个函数concatenate, 目的是:
+    - 获取到所有数据目录中的文件名.
+    - 在每个文件中循环.
+        - 如果文件不是正确的格式 (不是以我们需要的格式作为开头), 我们将忽略它.
+        - 把文件读入一个[数据帧][30] 伴随着正确的设置通过使用Pandas [读取csv][31]函数.
+            - 在|处设置分隔符以便所有的字段能被正确读出.
+            - 数据没有标题行,因此设置标题为None来进行标示.
+            - 从HEADERS字典中设置正确的标题名称 – 这将会是我们数据帧中的数据列名称.
+            - 通过SELECT来选择我们加入数据的数据帧中的列.
+- 把所有的数据帧共同连接在一起.
+- 把已经连接好的数据帧写回一个文件.
+
+```
+import os
+import settings
+import pandas as pd
+
+def concatenate(prefix="Acquisition"):
+    files = os.listdir(settings.DATA_DIR)
+    full = []
+    for f in files:
+        if not f.startswith(prefix):
+            continue
+
+        data = pd.read_csv(os.path.join(settings.DATA_DIR, f), sep="|", header=None, names=HEADERS[prefix], index_col=False)
+        data = data[SELECT[prefix]]
+        full.append(data)
+
+    full = pd.concat(full, axis=0)
+
+    full.to_csv(os.path.join(settings.PROCESSED_DIR, "{}.txt".format(prefix)), sep="|", header=SELECT[prefix], index=False)
+```
+
+我们可以通过调用上面的函数,通过传递的参数收购和业绩两次以将所有收购和业绩文件连接在一起。下面的代码将:
+- 仅仅在脚本被在命令行中通过python assemble.py被唤起而执行.
+- 将所有的数据连接在一起,并且产生2个文件:
+    - `processed/Acquisition.txt`
+    - `processed/Performance.txt`
+
+```
+if __name__ == "__main__":
+    concatenate("Acquisition")
+    concatenate("Performance")
+```
+
+我们现在拥有了一个漂亮的,划分过的assemble.py文件,它很容易执行,也容易被建立。通过像这样把问题分解为一块一块的,我们构建工程就会变的容易许多。不用一个可以做所有的凌乱的脚本,我们定义的数据将会在多个脚本间传递,同时使脚本间完全的0耦合。当你正在一个大的项目中工作,这样做是一个好的想法,因为这样可以更佳容易的修改其中的某一部分而不会引起其他项目中不关联部分产生超出预期的结果。
+
+一旦我们完成assemble.py脚本文件, 我们可以运行python assemble.py命令. 你可以查看完整的assemble.py文件[在这里][32].
+
+这将会在处理结果目录下产生2个文件:
+
+```
+loan-prediction
+├── data
+│   ├── Acquisition_2012Q1.txt
+│   ├── Acquisition_2012Q2.txt
+│   ├── Performance_2012Q1.txt
+│   ├── Performance_2012Q2.txt
+│   └── ...
+├── processed
+│   ├── Acquisition.txt
+│   ├── Performance.txt
+├── .gitignore
+├── assemble.py
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
+
+--------------
+
+
+### 计算运用数据中的值
+
+接下来我们会计算过程数据或运用数据中的值。我们要做的就是推测这些数据代表的贷款是否被收回。如果能够计算出来,我们只要看一下包含贷款的运用数据的参数 foreclosure_date 就可以了。如果这个参数的值是 None ,那么这些贷款肯定没有收回。为了避免我们的样例中存在少量的运用数据,我们会计算出运用数据中有贷款数据的行的行数。这样我们就能够从我们的训练数据中筛选出贷款数据,排除了一些运用数据。
+
+下面是一种区分贷款数据和运用数据的方法:
+
+![](https://github.com/LCTT/wiki-images/blob/master/TranslateProject/ref_img/001.png)
+
+
+在上面的表格中,采集数据中的每一行数据都与运用数据中的多行数据有联系。在运用数据中,在收回贷款的时候 foreclosure_date 就会以季度的的形式显示出收回时间,而且它会在该行数据的最前面显示一个空格。一些贷款没有收回,所以与运用数据中的贷款数据有关的行都会在前面出现一个表示 foreclosure_date 的空格。
+
+我们需要计算 foreclosure_status 的值,它的值是布尔类型,可以表示一个特殊的贷款数据 id 是否被收回过,还有一个参数 performance_count ,它记录了运用数据中每个贷款 id 出现的行数。 
+
+计算这些行数有多种不同的方法:
+
+- 我们能够读取所有的运用数据,然后我们用 Pandas 的 groupby 方法在数据框中计算出与每个贷款 id 有关的行的行数,然后就可以查看贷款 id 的 foreclosure_date 值是否为 None 。
+    - 这种方法的优点是从语法上来说容易执行。
+    - 它的缺点需要读取所有的 129236094 行数据,这样就会占用大量内存,并且运行起来极慢。
+- 我们可以读取所有的运用数据,然后使用采集到的数据框去计算每个贷款 id 出现的次数。
+    - 这种方法的优点是容易理解。
+    - 缺点是需要读取所有的 129236094 行数据。这样会占用大量内存,并且运行起来极慢。
+- 我们可以在迭代访问运用数据中的每一行数据,而且会建立一个区分开的计数字典。
+    - 这种方法的优点是数据不需要被加载到内存中,所以运行起来会很快且不需要占用内存。
+    - 缺点是这样的话理解和执行上可能有点耗费时间,我们需要对每一行数据进行语法分析。
+
+加载所有的数据会非常耗费内存,所以我们采用第三种方法。我们要做的就是迭代运用数据中的每一行数据,然后为每一个贷款 id 生成一个字典值。在这个字典中,我们会计算出贷款 id 在运用数据中出现的次数,而且如果 foreclosure_date 不是 Nnoe 。我们可以查看 foreclosure_status 和 performance_count 的值 。
+
+我们会新建一个 annotate.py 文件,文件中的代码可以计算这些值。我们会使用下面的代码:
+
+- 导入需要的库
+- 定义一个函数 count_performance_rows 。
+    - 打开 processed/Performance.txt 文件。这不是在内存中读取文件而是打开了一个文件标识符,这个标识符可以用来以行为单位读取文件。 
+    - 迭代文件的每一行数据。
+    - 使用分隔符(|)分开每行的不同数据。
+    - 检查 loan_id 是否在计数字典中。
+        - 如果不存在,进行一次计数。
+    - loan_id 的 performance_count 参数自增 1 次,因为我们这次迭代也包含其中。
+    - 如果日期是 None ,我们就会知道贷款被收回了,然后为foreclosure_status 设置合适的值。
+
+```
+import os
+import settings
+import pandas as pd
+
+def count_performance_rows():
+    counts = {}
+    with open(os.path.join(settings.PROCESSED_DIR, "Performance.txt"), 'r') as f:
+        for i, line in enumerate(f):
+            if i == 0:
+                # Skip header row
+                continue
+            loan_id, date = line.split("|")
+            loan_id = int(loan_id)
+            if loan_id not in counts:
+                counts[loan_id] = {
+                    "foreclosure_status": False,
+                    "performance_count": 0
+                }
+            counts[loan_id]["performance_count"] += 1
+            if len(date.strip()) > 0:
+                counts[loan_id]["foreclosure_status"] = True
+    return counts
+```
+
+### 获取值
+
+只要我们创建了计数字典,我们就可以使用一个函数通过一个 loan_id 和一个 key 从字典中提取到需要的参数的值:
+
+```
+def get_performance_summary_value(loan_id, key, counts):
+    value = counts.get(loan_id, {
+        "foreclosure_status": False,
+        "performance_count": 0
+    })
+    return value[key]
+```
+
+
+上面的函数会从计数字典中返回合适的值,我们也能够为采集数据中的每一行赋一个 foreclosure_status 值和一个 performance_count 值。如果键不存在,字典的 [get][33] 方法会返回一个默认值,所以在字典中不存在键的时候我们就可以得到一个可知的默认值。
+
+
+
+------------------
+
+
+### 注解数据
+
+我们已经在annotate.py中添加了一些功能, 现在我们来看一看数据文件. 我们需要将采集到的数据转换到训练数据表来进行机器学习的训练. 这涉及到以下几件事情:
+
+- 转换所以列数字.
+- 填充缺失值.
+- 分配 performance_count 和 foreclosure_status.
+- 移除出现次数很少的行(performance_count 计数低).
+
+我们有几个列是文本类型的, 看起来对于机器学习算法来说并不是很有用. 然而, 他们实际上是分类变量, 其中有很多不同的类别代码, 例如R,S等等. 我们可以把这些类别标签转换为数值:
+
+![](https://github.com/LCTT/wiki-images/blob/master/TranslateProject/ref_img/002.png)
+
+通过这种方法转换的列我们可以应用到机器学习算法中.
+
+还有一些包含日期的列 (first_payment_date 和 origination_date). 我们可以将这些日期放到两个列中:
+
+![](https://github.com/LCTT/wiki-images/blob/master/TranslateProject/ref_img/003.png)
+在下面的代码中, 我们将转换采集到的数据. 我们将定义一个函数如下:
+
+- 在采集到的数据中创建foreclosure_status列 .
+- 在采集到的数据中创建performance_count列.
+- 将下面的string列转换为integer列:
+    - channel
+    - seller
+    - first_time_homebuyer
+    - loan_purpose
+    - property_type
+    - occupancy_status
+    - property_state
+    - product_type
+- 转换first_payment_date 和 origination_date 为两列:
+    - 通过斜杠分离列.
+    - 将第一部分分离成月清单.
+    - 将第二部分分离成年清单.
+    - 删除这一列.
+    - 最后, 我们得到 first_payment_month, first_payment_year, origination_month, and origination_year.
+- 所有缺失值填充为-1.
+
+```
+def annotate(acquisition, counts):
+    acquisition["foreclosure_status"] = acquisition["id"].apply(lambda x: get_performance_summary_value(x, "foreclosure_status", counts))
+    acquisition["performance_count"] = acquisition["id"].apply(lambda x: get_performance_summary_value(x, "performance_count", counts))
+    for column in [
+        "channel",
+        "seller",
+        "first_time_homebuyer",
+        "loan_purpose",
+        "property_type",
+        "occupancy_status",
+        "property_state",
+        "product_type"
+    ]:
+        acquisition[column] = acquisition[column].astype('category').cat.codes
+
+    for start in ["first_payment", "origination"]:
+        column = "{}_date".format(start)
+        acquisition["{}_year".format(start)] = pd.to_numeric(acquisition[column].str.split('/').str.get(1))
+        acquisition["{}_month".format(start)] = pd.to_numeric(acquisition[column].str.split('/').str.get(0))
+        del acquisition[column]
+
+    acquisition = acquisition.fillna(-1)
+    acquisition = acquisition[acquisition["performance_count"] > settings.MINIMUM_TRACKING_QUARTERS]
+    return acquisition
+```
+
+### 聚合到一起
+
+我们差不多准备就绪了, 我们只需要再在annotate.py添加一点点代码. 在下面代码中, 我们将:
+
+- 定义一个函数来读取采集的数据.
+- 定义一个函数来写入数据到/train.csv
+- 如果我们在命令行运行annotate.py来读取更新过的数据文件,它将做如下事情:
+    - 读取采集到的数据.
+    - 计算数据性能.
+    - 注解数据.
+    - 将注解数据写入到train.csv.
+
+```
+def read():
+    acquisition = pd.read_csv(os.path.join(settings.PROCESSED_DIR, "Acquisition.txt"), sep="|")
+    return acquisition
+    
+def write(acquisition):
+    acquisition.to_csv(os.path.join(settings.PROCESSED_DIR, "train.csv"), index=False)
+
+if __name__ == "__main__":
+    acquisition = read()
+    counts = count_performance_rows()
+    acquisition = annotate(acquisition, counts)
+    write(acquisition)
+```
+
+修改完成以后为了确保annotate.py能够生成train.csv文件. 你可以在这里找到完整的 annotate.py file [here][34].
+
+文件夹结果应该像这样:
+
+```
+loan-prediction
+├── data
+│   ├── Acquisition_2012Q1.txt
+│   ├── Acquisition_2012Q2.txt
+│   ├── Performance_2012Q1.txt
+│   ├── Performance_2012Q2.txt
+│   └── ...
+├── processed
+│   ├── Acquisition.txt
+│   ├── Performance.txt
+│   ├── train.csv
+├── .gitignore
+├── annotate.py
+├── assemble.py
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
+### 找到标准
+
+我们已经完成了训练数据表的生成, 现在我们需要最后一步, 生成预测. 我们需要找到错误的标准, 以及该如何评估我们的数据. 在这种情况下, 因为有很多的贷款没有收回, 所以根本不可能做到精确的计算.
+
+我们需要读取数据, 并且计算foreclosure_status列, 我们将得到如下信息:
+
+```
+import pandas as pd
+import settings
+
+train = pd.read_csv(os.path.join(settings.PROCESSED_DIR, "train.csv"))
+train["foreclosure_status"].value_counts()
+```
+
+```
+False    4635982
+True        1585
+Name: foreclosure_status, dtype: int64
+```
+
+因为只有一点点贷款收回, 通过百分比标签来建立的机器学习模型会把每行都设置为Fasle, 所以我们在这里要考虑每个样本的不平衡性,确保我们做出的预测是准确的. 我们不想要这么多假的false, 我们将预计贷款收回但是它并没有收回, 我们预计贷款不会回收但是却回收了. 通过以上两点, Fannie Mae的false太多了, 因此显示他们可能无法收回投资.
+
+所以我们将定义一个百分比,就是模型预测没有收回但是实际上收回了, 这个数除以总的负债回收总数. 这个负债回收百分比模型实际上是“没有的”. 下面看这个图表:
+
+![](https://github.com/LCTT/wiki-images/blob/master/TranslateProject/ref_img/004.png)
+
+通过上面的图表, 1个负债预计不会回收, 也确实没有回收. 如果我们将这个数除以总数, 2, 我们将得到false的概率为50%. 我们将使用这个标准, 因此我们可以评估一下模型的性能.
+
+### 设置机器学习分类器
+
+我们使用交叉验证预测. 通过交叉验证法, 我们将数据分为3组. 按照下面的方法来做:
+
+- Train a model on groups 1 and 2, and use the model to make predictions for group 3.
+- Train a model on groups 1 and 3, and use the model to make predictions for group 2.
+- Train a model on groups 2 and 3, and use the model to make predictions for group 1.
+
+将它们分割到不同的组 ,这意味着我们永远不会用相同的数据来为预测训练模型. 这样就避免了overfitting(过拟合). 如果我们overfit(过拟合), 我们将得到很低的false概率, 这使得我们难以改进算法或者应用到现实生活中.
+
+[Scikit-learn][35] 有一个叫做 [cross_val_predict][36] 他可以帮助我们理解交叉算法.
+
+我们还需要一种算法来帮我们预测. 我们还需要一个分类器 [binary classification][37](二元分类). 目标变量foreclosure_status 只有两个值, True 和 False.
+
+我们用[logistic regression][38](回归算法), 因为它能很好的进行binary classification(二元分类), 并且运行很快, 占用内存很小. 我们来说一下它是如何工作的 – 取代许多树状结构, 更像随机森林, 进行转换, 更像一个向量机, 逻辑回归涉及更少的步骤和更少的矩阵.
+
+我们可以使用[logistic regression classifier][39](逻辑回归分类器)算法 来实现scikit-learn. 我们唯一需要注意的是每个类的标准. 如果我们使用同样标准的类, 算法将会预测每行都为false, 因为它总是试图最小化误差.不管怎样, 我们关注有多少贷款能够回收而不是有多少不能回收. 因此, 我们通过 [LogisticRegression][40](逻辑回归)来平衡标准参数, 并计算回收贷款的标准. 这将使我们的算法不会认为每一行都为false.
+
+
+----------------------
+
+### 做出预测
+
+既然完成了前期准备,我们可以开始准备做出预测了。我将创建一个名为 predict.py 的新文件,它会使用我们在最后一步创建的 train.csv 文件。下面的代码:
+
+- 导入所需的库
+- 创建一个名为 `cross_validate` 的函数:
+    - 使用正确的关键词参数创建逻辑回归分类器(logistic regression classifier)
+    - 创建移除了 `id` 和 `foreclosure_status` 属性的用于训练模型的列
+    - 跨 `train` 数据帧使用交叉验证
+    - 返回预测结果
+
+```python
+import os
+import settings
+import pandas as pd
+from sklearn import cross_validation
+from sklearn.linear_model import LogisticRegression
+from sklearn import metrics
+
+def cross_validate(train):
+    clf = LogisticRegression(random_state=1, class_weight="balanced")
+
+    predictors = train.columns.tolist()
+    predictors = [p for p in predictors if p not in settings.NON_PREDICTORS]
+
+    predictions = cross_validation.cross_val_predict(clf, train[predictors], train[settings.TARGET], cv=settings.CV_FOLDS)
+    return predictions
+```
+
+### 预测误差
+
+现在,我们仅仅需要写一些函数来计算误差。下面的代码:
+
+- 创建函数 `compute_error`:
+    - 使用 scikit-learn 计算一个简单的精确分数(与实际 `foreclosure_status` 值匹配的预测百分比)
+- 创建函数 `compute_false_negatives`:
+    - 为了方便,将目标和预测结果合并到一个数据帧
+    - 查找漏报率
+        - 找到原本应被预测模型取消但没有取消的贷款数目
+        - 除以没被取消的贷款总数目
+
+```python
+def compute_error(target, predictions):
+    return metrics.accuracy_score(target, predictions)
+
+def compute_false_negatives(target, predictions):
+    df = pd.DataFrame({"target": target, "predictions": predictions})
+    return df[(df["target"] == 1) & (df["predictions"] == 0)].shape[0] / (df[(df["target"] == 1)].shape[0] + 1)
+
+def compute_false_positives(target, predictions):
+    df = pd.DataFrame({"target": target, "predictions": predictions})
+    return df[(df["target"] == 0) & (df["predictions"] == 1)].shape[0] / (df[(df["target"] == 0)].shape[0] + 1)
+```
+
+### 聚合到一起
+
+现在,我们可以把函数都放在 `predict.py`。下面的代码:
+
+- 读取数据集
+- 计算交叉验证预测
+- 计算上面的 3 个误差
+- 打印误差
+
+```python
+def read():
+    train = pd.read_csv(os.path.join(settings.PROCESSED_DIR, "train.csv"))
+    return train
+
+if __name__ == "__main__":
+    train = read()
+    predictions = cross_validate(train)
+    error = compute_error(train[settings.TARGET], predictions)
+    fn = compute_false_negatives(train[settings.TARGET], predictions)
+    fp = compute_false_positives(train[settings.TARGET], predictions)
+    print("Accuracy Score: {}".format(error))
+    print("False Negatives: {}".format(fn))
+    print("False Positives: {}".format(fp))
+```
+
+一旦你添加完代码,你可以运行 `python predict.py` 来产生预测结果。运行结果向我们展示漏报率为 `.26`,这意味着我们没能预测 `26%` 的取消贷款。这是一个好的开始,但仍有很多改善的地方!
+
+你可以在[这里][41]找到完整的 `predict.py` 文件
+
+你的文件树现在看起来像下面这样:
+
+```
+loan-prediction
+├── data
+│   ├── Acquisition_2012Q1.txt
+│   ├── Acquisition_2012Q2.txt
+│   ├── Performance_2012Q1.txt
+│   ├── Performance_2012Q2.txt
+│   └── ...
+├── processed
+│   ├── Acquisition.txt
+│   ├── Performance.txt
+│   ├── train.csv
+├── .gitignore
+├── annotate.py
+├── assemble.py
+├── predict.py
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
+### 撰写 README
+
+既然我们完成了端到端的项目,那么我们可以撰写 README.md 文件了,这样其他人便可以知道我们做的事,以及如何复制它。一个项目典型的 README.md 应该包括这些部分:
+
+- 一个高水准的项目概览,并介绍项目目的
+- 任何必需的数据和材料的下载地址
+- 安装命令
+    - 如何安装要求依赖
+- 使用命令
+    - 如何运行项目
+    - 每一步之后会看到的结果
+- 如何为这个项目作贡献
+    - 扩展项目的下一步计划
+
+[这里][42] 是这个项目的一个 README.md 样例。
+
+### 下一步
+
+恭喜你完成了端到端的机器学习项目!你可以在[这里][43]找到一个完整的示例项目。一旦你完成了项目,把它上传到 [Github][44] 是一个不错的主意,这样其他人也可以看到你的文件夹的部分项目。
+
+这里仍有一些留待探索数据的角度。总的来说,我们可以把它们分割为 3 类 - 扩展这个项目并使它更加精确,发现预测其他列并探索数据。这是其中一些想法:
+
+- 在 `annotate.py` 中生成更多的特性
+- 切换 `predict.py` 中的算法
+- 尝试使用比我们发表在这里的更多的来自 `Fannie Mae` 的数据
+- 添加对未来数据进行预测的方法。如果我们添加更多数据,我们所写的代码仍然可以起作用,这样我们可以添加更多过去和未来的数据。
+- 尝试看看是否你能预测一个银行是否应该发放贷款(相对地,`Fannie Mae` 是否应该获得贷款)
+    - 移除 train 中银行不知道发放贷款的时间的任何列
+        - 当 Fannie Mae 购买贷款时,一些列是已知的,但不是之前
+    - 做出预测
+- 探索是否你可以预测除了 foreclosure_status 的其他列
+    - 你可以预测在销售时财产值多少?
+- 探索探索性能更新之间的细微差别
+    - 你能否预测借款人会逾期还款多少次?
+    - 你能否标出的典型贷款周期?
+- 标出一个州到州或邮政编码到邮政级水平的数据
+    - 你看到一些有趣的模式了吗?
+
+如果你建立了任何有趣的东西,请在评论中让我们知道!
+
+如果你喜欢这个,你可能会喜欢阅读 ‘Build a Data Science Porfolio’ 系列的其他文章:
+
+- [Storytelling with data][45].
+- [How to setup up a data science blog][46].
+
+
+--------------------------------------------------------------------------------
+
+via: https://www.dataquest.io/blog/data-science-portfolio-machine-learning/
+
+作者:[Vik Paruchuri][a]
+译者:[kokialoves](https://github.com/译者ID),[zky001](https://github.com/译者ID),[vim-kakali](https://github.com/译者ID),[cposture](https://github.com/译者ID),[ideas4u](https://github.com/译者ID)
+校对:[校对ID](https://github.com/校对ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.dataquest.io/blog
+[1]: https://www.dataquest.io/blog/data-science-portfolio-machine-learning/#email-signup
+[2]: https://github.com/dataquestio/loan-prediction
+[3]: https://www.dataquest.io/blog/data-science-portfolio-project/
+[4]: https://atom.io/
+[5]: https://www.jetbrains.com/pycharm/
+[6]: https://github.com/
+[7]: http://pandas.pydata.org/
+[8]: http://scikit-learn.org/
+[9]: http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html
+[10]: https://collegescorecard.ed.gov/data/
+[11]: https://reddit.com/r/datasets
+[12]: https://cloud.google.com/bigquery/public-data/#usa-names
+[13]: https://github.com/caesar0301/awesome-public-datasets
+[14]: http://www.fanniemae.com/portal/funding-the-market/data/loan-performance-data.html
+[15]: http://www.fanniemae.com/portal/funding-the-market/data/loan-performance-data.html
+[16]: https://loanperformancedata.fanniemae.com/lppub-docs/lppub_glossary.pdf
+[17]: https://loanperformancedata.fanniemae.com/lppub-docs/lppub_faq.pdf
+[18]: https://loanperformancedata.fanniemae.com/lppub-docs/lppub_file_layout.pdf
+[19]: https://loanperformancedata.fanniemae.com/lppub-docs/acquisition-sample-file.txt
+[20]: https://loanperformancedata.fanniemae.com/lppub-docs/performance-sample-file.txt
+[21]: https://github.com/dataquestio/loan-prediction/blob/master/.gitignore
+[22]: https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet
+[23]: https://github.com/dataquestio/loan-prediction
+[24]: https://github.com/dataquestio/loan-prediction/blob/master/requirements.txt
+[25]: https://www.continuum.io/downloads
+[26]: https://loanperformancedata.fanniemae.com/lppub/index.html
+[27]: https://loanperformancedata.fanniemae.com/lppub-docs/lppub_file_layout.pdf
+[28]: https://github.com/dataquestio/loan-prediction/blob/master/settings.py
+[29]: https://loanperformancedata.fanniemae.com/lppub-docs/lppub_file_layout.pdf
+[30]: http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html
+[31]: http://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_csv.html
+[32]: https://github.com/dataquestio/loan-prediction/blob/master/assemble.py
+[33]: https://docs.python.org/3/library/stdtypes.html#dict.get
+[34]: https://github.com/dataquestio/loan-prediction/blob/master/annotate.py
+[35]: http://scikit-learn.org/
+[36]: http://scikit-learn.org/stable/modules/generated/sklearn.cross_validation.cross_val_predict.html
+[37]: https://en.wikipedia.org/wiki/Binary_classification
+[38]: https://en.wikipedia.org/wiki/Logistic_regression
+[39]: http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html
+[40]: http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html
+[41]: https://github.com/dataquestio/loan-prediction/blob/master/predict.py
+[42]: https://github.com/dataquestio/loan-prediction/blob/master/README.md
+[43]: https://github.com/dataquestio/loan-prediction
+[44]: https://www.github.com/
+[45]: https://www.dataquest.io/blog/data-science-portfolio-project/
+[46]: https://www.dataquest.io/blog/how-to-setup-a-data-science-blog/
diff --git a/sources/team_test/README.md b/sources/team_test/README.md
new file mode 100644
index 0000000000..0798a592ef
--- /dev/null
+++ b/sources/team_test/README.md
@@ -0,0 +1,7 @@
+组队翻译    :  《Building a data science portfolio: Machine learning project》
+
+本次组织者  :  @选题-oska874
+
+参加译者    :  @译者-vim-kakali @译者-Noobfish @译者-zky001 @译者-kokialoves @译者-ideas4u @译者-cposture 
+
+分配方式    :  原文按大致长度分成 6 部分,参与者自由选择,先到先选,如有疑问联系 @选题-oska874
diff --git a/sources/team_test/part 1 - Building a data science portfolio - Machine learning project.md b/sources/team_test/part 1 - Building a data science portfolio - Machine learning project.md
new file mode 100644
index 0000000000..9acb80676b
--- /dev/null
+++ b/sources/team_test/part 1 - Building a data science portfolio - Machine learning project.md	
@@ -0,0 +1,79 @@
+
+>这是这个系列的第三次发布关于如何建立科学的投资数据. 如果你喜欢这个系列并且想继续关注, 你可以在订阅页面的底部找到链接[subscribe at the bottom of the page][1].
+
+数据科学公司越来越关注投资组合问题. 这其中的一个原因是,投资组合是最好的判断人们生活技能的方法. 好消息是投资组合是完全在你的控制之下的. 只要你做些投资方面的工作,就可以做出很棒的投资组合.
+
+高质量投资组合的第一步就是知道需要什么技能. 客户想要将这些初级技能应用到数据科学, 因此这些投资技能显示如下:
+
+- 沟通能力
+- 协作能力
+- 技术能力
+- 数据推理能力
+- 主动性
+
+任何好的投资组合都由多个能力组成,其中必然包含以上一到两点. 这里我们主要讲第三点如何做好科学的数据投资组合. 在这一节, 我们主要讲第二项以及如何创建一个端对端的机器学习项目. 在最后, 在最后我们将拥有一个项目它将显示你的能力和技术水平. [Here’s][2]如果你想看一下这里有一个完整的例子.
+
+### 一个端到端的项目
+
+作为一个数据科学家, 有时候你会拿到一个数据集并被问到是 [如何产生的][3]. 在这个时候, 交流是非常重要的, 走一遍流程. 用用Jupyter notebook, 看一看以前的例子,这对你非常有帮助. 在这里你能找到一些可以用的报告或者文档.
+
+不管怎样, 有时候你会被要求创建一个具有操作价值的项目. 一个直接影响公司业务的项目, 不止一次的, 许多人用的项目. 这个任务可能像这样 “创建一个算法来预测波动率”或者 “创建一个模型来自动标签我们的文章”. 在这种情况下, 技术能力比说评书更重要. 你必须能够创建一个数据集, 并且理解它, 然后创建脚本处理该数据. 还有很重要的脚本要运行的很快, 占用系统资源很小. 它可能要运行很多次, 脚本的可使用性也很重要,并不仅仅是一个演示版. 可使用性是指整合操作流程, 因为他很有可能是面向用户的.
+
+端对端项目的主要组成部分:
+
+- 理解背景
+- 浏览数据并找出细微差别
+- 创建结构化项目, 那样比较容易整合操作流程
+- 运行速度快占用系统资源小的代码
+- 写好文档以便其他人用
+
+为类有效的创建这种类型的项目, 我们可能需要处理多个文件. 强烈推荐使用 [Atom][4]或者[PyCharm][5] . 这些工具允许你在文件间跳转, 编辑不同类型的文件, 例如 markdown 文件, Python 文件, 和csv 文件. 结构化你的项目还利于版本控制 [Github][6] 也很有用.
+
+![](https://www.dataquest.io/blog/images/end_to_end/github.png)
+>Github上的这个项目.
+
+在这一节中我们将使用  [Pandas][7] 和 [scikit-learn][8]扩展包 . 我们还将用到Pandas [DataFrames][9], 它使得python读取和处理表格数据更加方便.
+
+### 找到好的数据集
+
+找到一个好的端到端投资项目数据集很难. [The dataset][10]数据集需要足够大但是内存和性能限制了它. 它还需要实际有用的. 例如, 这个数据集, 它包含有美国院校的录取标准, 毕业率以及毕业以后的收入是个很好的数据集了. 不管怎样, 不管你如何想这个数据, 很显然它不适合创建端到端的项目. 比如, 你能告诉人们他们去了这些大学以后的未来收益, 但是却没有足够的细微差别. 你还能找出院校招生标准收入更高, 但是却没有告诉你如何实际操作.
+
+这里还有内存和性能约束的问题比如你有几千兆的数据或者有一些细微差别需要你去预测或者运行什么样的算法数据集等.
+
+一个好的数据集包括你可以动态的转换数组, 并且回答动态的问题. 一个很好的例子是股票价格数据集. 你可以预测明天的价格, 持续的添加数据在算法中. 它将有助于你预测利润. 这不是讲故事这是真实的.
+
+一些找到数据集的好地方:
+
+- [/r/datasets][11] – subreddit(Reddit是国外一个社交新闻站点,subreddit指该论坛下的各不同板块).
+- [Google Public Datasets][12] – 通过Google BigQuery发布的可用数据集.
+- [Awesome datasets][13] – Github上的数据集.
+
+当你查看这些数据集, 想一下人们想要在这些数据集中得到什么答案, 哪怕这些问题只想过一次 (“放假是如何S&P 500关联的?”), 或者更进一步(“你能预测股市吗?”). 这里的关键是更进一步找出问题, 并且多次运行不同的数据相同的代码.
+
+为了这个目标, 我们来看一下[Fannie Mae 贷款数据][14]. Fannie Mae 是一家政府赞助的企业抵押贷款公司它从其他银行购买按揭贷款. 然后捆绑这些贷款为抵押贷款来倒卖证券. 这使得贷款机构可以提供更多的抵押贷款, 在市场上创造更多的流动性. 这在理论上会导致更多的住房和更好的贷款条件. 从借款人的角度来说,他们大体上差不多, 话虽这样说.
+
+Fannie Mae 发布了两种类型的数据 – 它获得的贷款, 随着时间的推移这些贷款是否被偿还.在理想的情况下, 有人向贷款人借钱, 然后还清贷款. 不管怎样, 有些人没还的起钱, 丧失了抵押品赎回权. Foreclosure 是说没钱还了被银行把房子给回收了. Fannie Mae 追踪谁没还钱, 并且需要收回房屋抵押权. 每个季度会发布此数据, 并滞后一年. 当前可用是2015年第一季度数据.
+
+采集数据是由Fannie Mae发布的贷款数据, 它包含借款人的信息, 信用评分, 和他们的家庭贷款信息. 性能数据, 贷款回收后的每一个季度公布, 包含借贷人所支付款项信息和丧失抵押品赎回状态, 收回贷款的性能数据可能有十几行.一个很好的思路是这样的采集数据告诉你Fannie Mae所控制的贷款, 性能数据包含几个属性来更新贷款. 其中一个属性告诉我们每个季度的贷款赎回权.
+
+![](https://www.dataquest.io/blog/images/end_to_end/foreclosure.jpg)
+>一个没有及时还贷的房子就这样的被卖了.
+
+### 选择一个角度
+
+这里有几个方向我们可以去分析 Fannie Mae 数据集. 我们可以:
+
+- 预测房屋的销售价格.
+- 预测借款人还款历史.
+- 在收购时为每一笔贷款打分.
+
+最重要的事情是坚持单一的角度. 关注太多的事情很难做出效果. 选择一个有着足够细节的角度也很重要. 下面的理解就没有太多差别:
+
+- 找出哪些银行将贷款出售给Fannie Mae.
+- 计算贷款人的信用评分趋势.
+- 搜索哪些类型的家庭没有偿还贷款的能力.
+- 搜索贷款金额和抵押品价格之间的关系
+
+上面的想法非常有趣, 它会告诉我们很多有意思的事情, 但是不是一个很适合操作的项目。
+
+在Fannie Mae数据集中, 我们将预测贷款是否能被偿还. 实际上, 我们将建立一个抵押贷款的分数来告诉 Fannie Mae买还是不买. 这将给我提供很好的基础.
diff --git a/sources/team_test/part 2 - Building a data science portfolio - Machine learning project.md b/sources/team_test/part 2 - Building a data science portfolio - Machine learning project.md
new file mode 100644
index 0000000000..ff979de34f
--- /dev/null
+++ b/sources/team_test/part 2 - Building a data science portfolio - Machine learning project.md	
@@ -0,0 +1,98 @@
+
+翻译中 by ideas4u
+### 理解数据
+我们来简单看一下原始数据文件。下面是2012年1季度前几行的采集数据。
+```
+100000853384|R|OTHER|4.625|280000|360|02/2012|04/2012|31|31|1|23|801|N|C|SF|1|I|CA|945||FRM|
+100003735682|R|SUNTRUST MORTGAGE INC.|3.99|466000|360|01/2012|03/2012|80|80|2|30|794|N|P|SF|1|P|MD|208||FRM|788
+100006367485|C|PHH MORTGAGE CORPORATION|4|229000|360|02/2012|04/2012|67|67|2|36|802|N|R|SF|1|P|CA|959||FRM|794
+```
+
+下面是2012年1季度的前几行执行数据
+```
+100000853384|03/01/2012|OTHER|4.625||0|360|359|03/2042|41860|0|N||||||||||||||||
+100000853384|04/01/2012||4.625||1|359|358|03/2042|41860|0|N||||||||||||||||
+100000853384|05/01/2012||4.625||2|358|357|03/2042|41860|0|N||||||||||||||||
+```
+在开始编码之前,花些时间真正理解数据是值得的。这对于操作项目优为重要,因为我们没有交互式探索数据,将很难察觉到细微的差别除非我们在前期发现他们。在这种情况下,第一个步骤是阅读房利美站点的资料:
+- [概述][15]
+- [有用的术语表][16]
+- [问答][17]
+- [采集和执行文件中的列][18]
+- [采集数据文件样本][19]
+- [执行数据文件样本][20]
+
+在看完这些文件后后,我们了解到一些能帮助我们的关键点:
+- 从2000年到现在,每季度都有一个采集和执行文件,因数据是滞后一年的,所以到目前为止最新数据是2015年的。
+- 这些文件是文本格式的,采用管道符号“|”进行分割。
+- 这些文件是没有表头的,但我们有文件列明各列的名称。
+- 所有一起,文件包含2200万个贷款的数据。
+由于执行文件包含过去几年获得的贷款的信息,在早些年获得的贷款将有更多的执行数据(即在2014获得的贷款没有多少历史执行数据)。
+这些小小的信息将会为我们节省很多时间,因为我们知道如何构造我们的项目和利用这些数据。
+
+### 构造项目
+在我们开始下载和探索数据之前,先想一想将如何构造项目是很重要的。当建立端到端项目时,我们的主要目标是:
+- 创建一个可行解决方案
+- 有一个快速运行且占用最小资源的解决方案
+- 容易可扩展
+- 写容易理解的代码
+- 写尽量少的代码
+
+为了实现这些目标,需要对我们的项目进行良好的构造。一个结构良好的项目遵循几个原则:
+- 分离数据文件和代码文件
+- 从原始数据中分离生成的数据。
+- 有一个README.md文件帮助人们安装和使用该项目。
+- 有一个requirements.txt文件列明项目运行所需的所有包。
+- 有一个单独的settings.py 文件列明其它文件中使用的所有的设置
+    - 例如,如果从多个Python脚本读取相同的文件,把它们全部import设置和从一个集中的地方获得文件名是有用的。
+- 有一个.gitignore文件,防止大的或秘密文件被提交。
+- 分解任务中每一步可以单独执行的步骤到单独的文件中。
+    - 例如,我们将有一个文件用于读取数据,一个用于创建特征,一个用于做出预测。
+- 保存中间结果,例如,一个脚本可输出下一个脚本可读取的文件。
+
+    - 这使我们无需重新计算就可以在数据处理流程中进行更改。
+    
+
+我们的文件结构大体如下:
+
+```
+loan-prediction
+├── data
+├── processed
+├── .gitignore
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
+### 创建初始文件
+首先,我们需要创建一个loan-prediction文件夹,在此文件夹下面,再创建一个data文件夹和一个processed文件夹。data文件夹存放原始数据,processed文件夹存放所有的中间计算结果。
+其次,创建.gitignore文件,.gitignore文件将保证某些文件被git忽略而不会被推送至github。关于这个文件的一个好的例子是由OSX在每一个文件夹都会创建的.DS_Store文件,.gitignore文件一个很好的起点就是在这了。我们还想忽略数据文件因为他们实在是太大了,同时房利美的条文禁止我们重新分发该数据文件,所以我们应该在我们的文件后面添加以下2行:
+```
+data
+processed
+```
+
+这是该项目的一个关于.gitignore文件的例子。
+再次,我们需要创建README.md文件,它将帮助人们理解该项目。后缀.md表示这个文件采用markdown格式。Markdown使你能够写纯文本文件,同时还可以添加你想要的梦幻格式。这是关于markdown的导引。如果你上传一个叫README.md的文件至Github,Github会自动处理该markdown,同时展示给浏览该项目的人。
+至此,我们仅需在README.md文件中添加简单的描述:
+```
+Loan Prediction
+-----------------------
+
+Predict whether or not loans acquired by Fannie Mae will go into foreclosure.  Fannie Mae acquires loans from other lenders as a way of inducing them to lend more.  Fannie Mae releases data on the loans it has acquired and their performance afterwards [here](http://www.fanniemae.com/portal/funding-the-market/data/loan-performance-data.html).
+```
+
+现在,我们可以创建requirements.txt文件了。这会唯其它人可以很方便地安装我们的项目。我们还不知道我们将会具体用到哪些库,但是以下几个库是一个很好的开始:
+```
+pandas
+matplotlib
+scikit-learn
+numpy
+ipython
+scipy
+```
+
+以上几个是在python数据分析任务中最常用到的库。可以认为我们将会用到大部分这些库。这里是【24】该项目requirements文件的一个例子。
+ 创建requirements.txt文件之后,你应该安装包了。我们将会使用python3.如果你没有安装python,你应该考虑使用 [Anaconda][25],一个python安装程序,同时安装了上面列出的所有包。
+最后,我们可以建立一个空白的settings.py文件,因为我们的项目还没有任何设置。
diff --git a/sources/team_test/part 3Building a data science portfolio - Machine learning project.md b/sources/team_test/part 3Building a data science portfolio - Machine learning project.md
new file mode 100644
index 0000000000..8160ef18b6
--- /dev/null
+++ b/sources/team_test/part 3Building a data science portfolio - Machine learning project.md	
@@ -0,0 +1,189 @@
+### 获取数据
+
+一旦我们拥有项目的基本架构,我们就可以去获得原始数据
+
+Fannie Mae 对获取数据有一些限制,所有你需要去注册一个账户。在创建完账户之后,你可以找到下载页面[在这里][26],你可以按照你所需要的下载非常少量或者很多的借款数据文件。文件格式是zip,在解压后当然是非常大的。
+
+为了达到我们这个博客文章的目的,我们将要下载从2012年1季度到2015
+年1季度的所有数据。接着我们需要解压所有的文件。解压过后,删掉原来的.zip格式的文件。最后,借款预测文件夹看起来应该像下面的一样:
+```
+loan-prediction
+├── data
+│   ├── Acquisition_2012Q1.txt
+│   ├── Acquisition_2012Q2.txt
+│   ├── Performance_2012Q1.txt
+│   ├── Performance_2012Q2.txt
+│   └── ...
+├── processed
+├── .gitignore
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
+在下载完数据后,你可以在shell命令行中使用head和tail命令去查看文件中的行数据,你看到任何的不需要的列数据了吗?在做这件事的同时查阅[列名称的pdf文件][27]可能是有用的事情。
+### 读入数据
+
+有两个问题让我们的数据难以现在就使用:
+- 收购数据和业绩数据被分割在多个文件中
+- 每个文件都缺少标题
+
+在我们开始使用数据之前,我们需要首先明白我们要在哪里去存一个收购数据的文件,同时到哪里去存储一个业绩数据的文件。每个文件仅仅需要包括我们关注的那些数据列,同时拥有正确的标题。这里有一个小问题是业绩数据非常大,因此我们需要尝试去修剪一些数据列。
+
+第一步是向settings.py文件中增加一些变量,这个文件中同时也包括了我们原始数据的存放路径和处理出的数据存放路径。我们同时也将添加其他一些可能在接下来会用到的设置数据:
+```
+DATA_DIR = "data"
+PROCESSED_DIR = "processed"
+MINIMUM_TRACKING_QUARTERS = 4
+TARGET = "foreclosure_status"
+NON_PREDICTORS = [TARGET, "id"]
+CV_FOLDS = 3
+```
+
+把路径设置在settings.py中将使它们放在一个集中的地方,同时使其修改更加的容易。当提到在多个文件中的相同的变量时,你想改变它的话,把他们放在一个地方比分散放在每一个文件时更加容易。[这里的][28]是一个这个工程的示例settings.py文件
+
+第二步是创建一个文件名为assemble.py,它将所有的数据分为2个文件。当我们运行Python assemble.py,我们在处理数据文件的目录会获得2个数据文件。
+
+接下来我们开始写assemble.py文件中的代码。首先我们需要为每个文件定义相应的标题,因此我们需要查看[列名称的pdf文件][29]同时创建在每一个收购数据和业绩数据的文件的列数据的列表:
+```
+HEADERS = {
+    "Acquisition": [
+        "id",
+        "channel",
+        "seller",
+        "interest_rate",
+        "balance",
+        "loan_term",
+        "origination_date",
+        "first_payment_date",
+        "ltv",
+        "cltv",
+        "borrower_count",
+        "dti",
+        "borrower_credit_score",
+        "first_time_homebuyer",
+        "loan_purpose",
+        "property_type",
+        "unit_count",
+        "occupancy_status",
+        "property_state",
+        "zip",
+        "insurance_percentage",
+        "product_type",
+        "co_borrower_credit_score"
+    ],
+    "Performance": [
+        "id",
+        "reporting_period",
+        "servicer_name",
+        "interest_rate",
+        "balance",
+        "loan_age",
+        "months_to_maturity",
+        "maturity_date",
+        "msa",
+        "delinquency_status",
+        "modification_flag",
+        "zero_balance_code",
+        "zero_balance_date",
+        "last_paid_installment_date",
+        "foreclosure_date",
+        "disposition_date",
+        "foreclosure_costs",
+        "property_repair_costs",
+        "recovery_costs",
+        "misc_costs",
+        "tax_costs",
+        "sale_proceeds",
+        "credit_enhancement_proceeds",
+        "repurchase_proceeds",
+        "other_foreclosure_proceeds",
+        "non_interest_bearing_balance",
+        "principal_forgiveness_balance"
+    ]
+}
+```
+
+
+接下来一步是定义我们想要保持的数据列。我们将需要保留收购数据中的所有列数据,丢弃列数据将会使我们节省下内存和硬盘空间,同时也会加速我们的代码。
+```
+SELECT = {
+    "Acquisition": HEADERS["Acquisition"],
+    "Performance": [
+        "id",
+        "foreclosure_date"
+    ]
+}
+```
+
+下一步,我们将编写一个函数来连接数据集。下面的代码将:
+- 引用一些需要的库,包括设置。
+- 定义一个函数concatenate, 目的是:
+    - 获取到所有数据目录中的文件名.
+    - 在每个文件中循环.
+        - 如果文件不是正确的格式 (不是以我们需要的格式作为开头), 我们将忽略它.
+        - 把文件读入一个[数据帧][30] 伴随着正确的设置通过使用Pandas [读取csv][31]函数.
+            - 在|处设置分隔符以便所有的字段能被正确读出.
+            - 数据没有标题行,因此设置标题为None来进行标示.
+            - 从HEADERS字典中设置正确的标题名称 – 这将会是我们数据帧中的数据列名称.
+            - 通过SELECT来选择我们加入数据的数据帧中的列.
+- 把所有的数据帧共同连接在一起.
+- 把已经连接好的数据帧写回一个文件.
+
+```
+import os
+import settings
+import pandas as pd
+
+def concatenate(prefix="Acquisition"):
+    files = os.listdir(settings.DATA_DIR)
+    full = []
+    for f in files:
+        if not f.startswith(prefix):
+            continue
+
+        data = pd.read_csv(os.path.join(settings.DATA_DIR, f), sep="|", header=None, names=HEADERS[prefix], index_col=False)
+        data = data[SELECT[prefix]]
+        full.append(data)
+
+    full = pd.concat(full, axis=0)
+
+    full.to_csv(os.path.join(settings.PROCESSED_DIR, "{}.txt".format(prefix)), sep="|", header=SELECT[prefix], index=False)
+```
+
+我们可以通过调用上面的函数,通过传递的参数收购和业绩两次以将所有收购和业绩文件连接在一起。下面的代码将:
+- 仅仅在脚本被在命令行中通过python assemble.py被唤起而执行.
+- 将所有的数据连接在一起,并且产生2个文件:
+    - `processed/Acquisition.txt`
+    - `processed/Performance.txt`
+
+```
+if __name__ == "__main__":
+    concatenate("Acquisition")
+    concatenate("Performance")
+```
+
+我们现在拥有了一个漂亮的,划分过的assemble.py文件,它很容易执行,也容易被建立。通过像这样把问题分解为一块一块的,我们构建工程就会变的容易许多。不用一个可以做所有的凌乱的脚本,我们定义的数据将会在多个脚本间传递,同时使脚本间完全的0耦合。当你正在一个大的项目中工作,这样做是一个好的想法,因为这样可以更佳容易的修改其中的某一部分而不会引起其他项目中不关联部分产生超出预期的结果。
+
+一旦我们完成assemble.py脚本文件, 我们可以运行python assemble.py命令. 你可以查看完整的assemble.py文件[在这里][32].
+
+这将会在处理结果目录下产生2个文件:
+
+```
+loan-prediction
+├── data
+│   ├── Acquisition_2012Q1.txt
+│   ├── Acquisition_2012Q2.txt
+│   ├── Performance_2012Q1.txt
+│   ├── Performance_2012Q2.txt
+│   └── ...
+├── processed
+│   ├── Acquisition.txt
+│   ├── Performance.txt
+├── .gitignore
+├── assemble.py
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
diff --git a/sources/team_test/part 4 - Building a data science portfolio - Machine learning project.md b/sources/team_test/part 4 - Building a data science portfolio - Machine learning project.md
new file mode 100644
index 0000000000..9e6aa7c1fe
--- /dev/null
+++ b/sources/team_test/part 4 - Building a data science portfolio - Machine learning project.md	
@@ -0,0 +1,82 @@
+
+### 计算运用数据中的值
+
+接下来我们会计算过程数据或运用数据中的值。我们要做的就是推测这些数据代表的贷款是否被收回。如果能够计算出来,我们只要看一下包含贷款的运用数据的参数 foreclosure_date 就可以了。如果这个参数的值是 None ,那么这些贷款肯定没有收回。为了避免我们的样例中存在少量的运用数据,我们会计算出运用数据中有贷款数据的行的行数。这样我们就能够从我们的训练数据中筛选出贷款数据,排除了一些运用数据。
+
+下面是一种区分贷款数据和运用数据的方法:
+
+![](https://github.com/LCTT/wiki-images/blob/master/TranslateProject/ref_img/001.png)
+
+
+在上面的表格中,采集数据中的每一行数据都与运用数据中的多行数据有联系。在运用数据中,在收回贷款的时候 foreclosure_date 就会以季度的的形式显示出收回时间,而且它会在该行数据的最前面显示一个空格。一些贷款没有收回,所以与运用数据中的贷款数据有关的行都会在前面出现一个表示 foreclosure_date 的空格。
+
+我们需要计算 foreclosure_status 的值,它的值是布尔类型,可以表示一个特殊的贷款数据 id 是否被收回过,还有一个参数 performance_count ,它记录了运用数据中每个贷款 id 出现的行数。 
+
+计算这些行数有多种不同的方法:
+
+- 我们能够读取所有的运用数据,然后我们用 Pandas 的 groupby 方法在数据框中计算出与每个贷款 id 有关的行的行数,然后就可以查看贷款 id 的 foreclosure_date 值是否为 None 。
+    - 这种方法的优点是从语法上来说容易执行。
+    - 它的缺点需要读取所有的 129236094 行数据,这样就会占用大量内存,并且运行起来极慢。
+- 我们可以读取所有的运用数据,然后使用采集到的数据框去计算每个贷款 id 出现的次数。
+    - 这种方法的优点是容易理解。
+    - 缺点是需要读取所有的 129236094 行数据。这样会占用大量内存,并且运行起来极慢。
+- 我们可以在迭代访问运用数据中的每一行数据,而且会建立一个区分开的计数字典。
+    - 这种方法的优点是数据不需要被加载到内存中,所以运行起来会很快且不需要占用内存。
+    - 缺点是这样的话理解和执行上可能有点耗费时间,我们需要对每一行数据进行语法分析。
+
+加载所有的数据会非常耗费内存,所以我们采用第三种方法。我们要做的就是迭代运用数据中的每一行数据,然后为每一个贷款 id 生成一个字典值。在这个字典中,我们会计算出贷款 id 在运用数据中出现的次数,而且如果 foreclosure_date 不是 Nnoe 。我们可以查看 foreclosure_status 和 performance_count 的值 。
+
+我们会新建一个 annotate.py 文件,文件中的代码可以计算这些值。我们会使用下面的代码:
+
+- 导入需要的库
+- 定义一个函数 count_performance_rows 。
+    - 打开 processed/Performance.txt 文件。这不是在内存中读取文件而是打开了一个文件标识符,这个标识符可以用来以行为单位读取文件。 
+    - 迭代文件的每一行数据。
+    - 使用分隔符(|)分开每行的不同数据。
+    - 检查 loan_id 是否在计数字典中。
+        - 如果不存在,进行一次计数。
+    - loan_id 的 performance_count 参数自增 1 次,因为我们这次迭代也包含其中。
+    - 如果日期是 None ,我们就会知道贷款被收回了,然后为foreclosure_status 设置合适的值。
+
+```
+import os
+import settings
+import pandas as pd
+
+def count_performance_rows():
+    counts = {}
+    with open(os.path.join(settings.PROCESSED_DIR, "Performance.txt"), 'r') as f:
+        for i, line in enumerate(f):
+            if i == 0:
+                # Skip header row
+                continue
+            loan_id, date = line.split("|")
+            loan_id = int(loan_id)
+            if loan_id not in counts:
+                counts[loan_id] = {
+                    "foreclosure_status": False,
+                    "performance_count": 0
+                }
+            counts[loan_id]["performance_count"] += 1
+            if len(date.strip()) > 0:
+                counts[loan_id]["foreclosure_status"] = True
+    return counts
+```
+
+### 获取值
+
+只要我们创建了计数字典,我们就可以使用一个函数通过一个 loan_id 和一个 key 从字典中提取到需要的参数的值:
+
+```
+def get_performance_summary_value(loan_id, key, counts):
+    value = counts.get(loan_id, {
+        "foreclosure_status": False,
+        "performance_count": 0
+    })
+    return value[key]
+```
+
+
+上面的函数会从计数字典中返回合适的值,我们也能够为采集数据中的每一行赋一个 foreclosure_status 值和一个 performance_count 值。如果键不存在,字典的 [get][33] 方法会返回一个默认值,所以在字典中不存在键的时候我们就可以得到一个可知的默认值。
+
+
diff --git a/sources/team_test/part 5 - Building a data science portfolio - Machine learning project.md b/sources/team_test/part 5 - Building a data science portfolio - Machine learning project.md
new file mode 100644
index 0000000000..a92bb01900
--- /dev/null
+++ b/sources/team_test/part 5 - Building a data science portfolio - Machine learning project.md	
@@ -0,0 +1,163 @@
+
+### 注解数据
+
+我们已经在annotate.py中添加了一些功能, 现在我们来看一看数据文件. 我们需要将采集到的数据转换到训练数据表来进行机器学习的训练. 这涉及到以下几件事情:
+
+- 转换所以列数字.
+- 填充缺失值.
+- 分配 performance_count 和 foreclosure_status.
+- 移除出现次数很少的行(performance_count 计数低).
+
+我们有几个列是文本类型的, 看起来对于机器学习算法来说并不是很有用. 然而, 他们实际上是分类变量, 其中有很多不同的类别代码, 例如R,S等等. 我们可以把这些类别标签转换为数值:
+
+![](https://github.com/LCTT/wiki-images/blob/master/TranslateProject/ref_img/002.png)
+
+通过这种方法转换的列我们可以应用到机器学习算法中.
+
+还有一些包含日期的列 (first_payment_date 和 origination_date). 我们可以将这些日期放到两个列中:
+
+![](https://github.com/LCTT/wiki-images/blob/master/TranslateProject/ref_img/003.png)
+在下面的代码中, 我们将转换采集到的数据. 我们将定义一个函数如下:
+
+- 在采集到的数据中创建foreclosure_status列 .
+- 在采集到的数据中创建performance_count列.
+- 将下面的string列转换为integer列:
+    - channel
+    - seller
+    - first_time_homebuyer
+    - loan_purpose
+    - property_type
+    - occupancy_status
+    - property_state
+    - product_type
+- 转换first_payment_date 和 origination_date 为两列:
+    - 通过斜杠分离列.
+    - 将第一部分分离成月清单.
+    - 将第二部分分离成年清单.
+    - 删除这一列.
+    - 最后, 我们得到 first_payment_month, first_payment_year, origination_month, and origination_year.
+- 所有缺失值填充为-1.
+
+```
+def annotate(acquisition, counts):
+    acquisition["foreclosure_status"] = acquisition["id"].apply(lambda x: get_performance_summary_value(x, "foreclosure_status", counts))
+    acquisition["performance_count"] = acquisition["id"].apply(lambda x: get_performance_summary_value(x, "performance_count", counts))
+    for column in [
+        "channel",
+        "seller",
+        "first_time_homebuyer",
+        "loan_purpose",
+        "property_type",
+        "occupancy_status",
+        "property_state",
+        "product_type"
+    ]:
+        acquisition[column] = acquisition[column].astype('category').cat.codes
+
+    for start in ["first_payment", "origination"]:
+        column = "{}_date".format(start)
+        acquisition["{}_year".format(start)] = pd.to_numeric(acquisition[column].str.split('/').str.get(1))
+        acquisition["{}_month".format(start)] = pd.to_numeric(acquisition[column].str.split('/').str.get(0))
+        del acquisition[column]
+
+    acquisition = acquisition.fillna(-1)
+    acquisition = acquisition[acquisition["performance_count"] > settings.MINIMUM_TRACKING_QUARTERS]
+    return acquisition
+```
+
+### 聚合到一起
+
+我们差不多准备就绪了, 我们只需要再在annotate.py添加一点点代码. 在下面代码中, 我们将:
+
+- 定义一个函数来读取采集的数据.
+- 定义一个函数来写入数据到/train.csv
+- 如果我们在命令行运行annotate.py来读取更新过的数据文件,它将做如下事情:
+    - 读取采集到的数据.
+    - 计算数据性能.
+    - 注解数据.
+    - 将注解数据写入到train.csv.
+
+```
+def read():
+    acquisition = pd.read_csv(os.path.join(settings.PROCESSED_DIR, "Acquisition.txt"), sep="|")
+    return acquisition
+    
+def write(acquisition):
+    acquisition.to_csv(os.path.join(settings.PROCESSED_DIR, "train.csv"), index=False)
+
+if __name__ == "__main__":
+    acquisition = read()
+    counts = count_performance_rows()
+    acquisition = annotate(acquisition, counts)
+    write(acquisition)
+```
+
+修改完成以后为了确保annotate.py能够生成train.csv文件. 你可以在这里找到完整的 annotate.py file [here][34].
+
+文件夹结果应该像这样:
+
+```
+loan-prediction
+├── data
+│   ├── Acquisition_2012Q1.txt
+│   ├── Acquisition_2012Q2.txt
+│   ├── Performance_2012Q1.txt
+│   ├── Performance_2012Q2.txt
+│   └── ...
+├── processed
+│   ├── Acquisition.txt
+│   ├── Performance.txt
+│   ├── train.csv
+├── .gitignore
+├── annotate.py
+├── assemble.py
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
+### 找到标准
+
+我们已经完成了训练数据表的生成, 现在我们需要最后一步, 生成预测. 我们需要找到错误的标准, 以及该如何评估我们的数据. 在这种情况下, 因为有很多的贷款没有收回, 所以根本不可能做到精确的计算.
+
+我们需要读取数据, 并且计算foreclosure_status列, 我们将得到如下信息:
+
+```
+import pandas as pd
+import settings
+
+train = pd.read_csv(os.path.join(settings.PROCESSED_DIR, "train.csv"))
+train["foreclosure_status"].value_counts()
+```
+
+```
+False    4635982
+True        1585
+Name: foreclosure_status, dtype: int64
+```
+
+因为只有一点点贷款收回, 通过百分比标签来建立的机器学习模型会把每行都设置为Fasle, 所以我们在这里要考虑每个样本的不平衡性,确保我们做出的预测是准确的. 我们不想要这么多假的false, 我们将预计贷款收回但是它并没有收回, 我们预计贷款不会回收但是却回收了. 通过以上两点, Fannie Mae的false太多了, 因此显示他们可能无法收回投资.
+
+所以我们将定义一个百分比,就是模型预测没有收回但是实际上收回了, 这个数除以总的负债回收总数. 这个负债回收百分比模型实际上是“没有的”. 下面看这个图表:
+
+![](https://github.com/LCTT/wiki-images/blob/master/TranslateProject/ref_img/004.png)
+
+通过上面的图表, 1个负债预计不会回收, 也确实没有回收. 如果我们将这个数除以总数, 2, 我们将得到false的概率为50%. 我们将使用这个标准, 因此我们可以评估一下模型的性能.
+
+### 设置机器学习分类器
+
+我们使用交叉验证预测. 通过交叉验证法, 我们将数据分为3组. 按照下面的方法来做:
+
+- Train a model on groups 1 and 2, and use the model to make predictions for group 3.
+- Train a model on groups 1 and 3, and use the model to make predictions for group 2.
+- Train a model on groups 2 and 3, and use the model to make predictions for group 1.
+
+将它们分割到不同的组 ,这意味着我们永远不会用相同的数据来为预测训练模型. 这样就避免了overfitting(过拟合). 如果我们overfit(过拟合), 我们将得到很低的false概率, 这使得我们难以改进算法或者应用到现实生活中.
+
+[Scikit-learn][35] 有一个叫做 [cross_val_predict][36] 他可以帮助我们理解交叉算法.
+
+我们还需要一种算法来帮我们预测. 我们还需要一个分类器 [binary classification][37](二元分类). 目标变量foreclosure_status 只有两个值, True 和 False.
+
+我们用[logistic regression][38](回归算法), 因为它能很好的进行binary classification(二元分类), 并且运行很快, 占用内存很小. 我们来说一下它是如何工作的 – 取代许多树状结构, 更像随机森林, 进行转换, 更像一个向量机, 逻辑回归涉及更少的步骤和更少的矩阵.
+
+我们可以使用[logistic regression classifier][39](逻辑回归分类器)算法 来实现scikit-learn. 我们唯一需要注意的是每个类的标准. 如果我们使用同样标准的类, 算法将会预测每行都为false, 因为它总是试图最小化误差.不管怎样, 我们关注有多少贷款能够回收而不是有多少不能回收. 因此, 我们通过 [LogisticRegression][40](逻辑回归)来平衡标准参数, 并计算回收贷款的标准. 这将使我们的算法不会认为每一行都为false.
diff --git a/sources/team_test/part 6 - Building a data science portfolio - Machine learning project.md b/sources/team_test/part 6 - Building a data science portfolio - Machine learning project.md
new file mode 100644
index 0000000000..2ea994c119
--- /dev/null
+++ b/sources/team_test/part 6 - Building a data science portfolio - Machine learning project.md	
@@ -0,0 +1,150 @@
+### 做出预测
+
+既然完成了前期准备,我们可以开始准备做出预测了。我将创建一个名为 predict.py 的新文件,它会使用我们在最后一步创建的 train.csv 文件。下面的代码:
+
+- 导入所需的库
+- 创建一个名为 `cross_validate` 的函数:
+    - 使用正确的关键词参数创建逻辑回归分类器(logistic regression classifier)
+    - 创建移除了 `id` 和 `foreclosure_status` 属性的用于训练模型的列
+    - 跨 `train` 数据帧使用交叉验证
+    - 返回预测结果
+
+```python
+import os
+import settings
+import pandas as pd
+from sklearn import cross_validation
+from sklearn.linear_model import LogisticRegression
+from sklearn import metrics
+
+def cross_validate(train):
+    clf = LogisticRegression(random_state=1, class_weight="balanced")
+
+    predictors = train.columns.tolist()
+    predictors = [p for p in predictors if p not in settings.NON_PREDICTORS]
+
+    predictions = cross_validation.cross_val_predict(clf, train[predictors], train[settings.TARGET], cv=settings.CV_FOLDS)
+    return predictions
+```
+
+### 预测误差
+
+现在,我们仅仅需要写一些函数来计算误差。下面的代码:
+
+- 创建函数 `compute_error`:
+    - 使用 scikit-learn 计算一个简单的精确分数(与实际 `foreclosure_status` 值匹配的预测百分比)
+- 创建函数 `compute_false_negatives`:
+    - 为了方便,将目标和预测结果合并到一个数据帧
+    - 查找漏报率
+        - 找到原本应被预测模型取消但没有取消的贷款数目
+        - 除以没被取消的贷款总数目
+
+```python
+def compute_error(target, predictions):
+    return metrics.accuracy_score(target, predictions)
+
+def compute_false_negatives(target, predictions):
+    df = pd.DataFrame({"target": target, "predictions": predictions})
+    return df[(df["target"] == 1) & (df["predictions"] == 0)].shape[0] / (df[(df["target"] == 1)].shape[0] + 1)
+
+def compute_false_positives(target, predictions):
+    df = pd.DataFrame({"target": target, "predictions": predictions})
+    return df[(df["target"] == 0) & (df["predictions"] == 1)].shape[0] / (df[(df["target"] == 0)].shape[0] + 1)
+```
+
+### 聚合到一起
+
+现在,我们可以把函数都放在 `predict.py`。下面的代码:
+
+- 读取数据集
+- 计算交叉验证预测
+- 计算上面的 3 个误差
+- 打印误差
+
+```python
+def read():
+    train = pd.read_csv(os.path.join(settings.PROCESSED_DIR, "train.csv"))
+    return train
+
+if __name__ == "__main__":
+    train = read()
+    predictions = cross_validate(train)
+    error = compute_error(train[settings.TARGET], predictions)
+    fn = compute_false_negatives(train[settings.TARGET], predictions)
+    fp = compute_false_positives(train[settings.TARGET], predictions)
+    print("Accuracy Score: {}".format(error))
+    print("False Negatives: {}".format(fn))
+    print("False Positives: {}".format(fp))
+```
+
+一旦你添加完代码,你可以运行 `python predict.py` 来产生预测结果。运行结果向我们展示漏报率为 `.26`,这意味着我们没能预测 `26%` 的取消贷款。这是一个好的开始,但仍有很多改善的地方!
+
+你可以在[这里][41]找到完整的 `predict.py` 文件
+
+你的文件树现在看起来像下面这样:
+
+```
+loan-prediction
+├── data
+│   ├── Acquisition_2012Q1.txt
+│   ├── Acquisition_2012Q2.txt
+│   ├── Performance_2012Q1.txt
+│   ├── Performance_2012Q2.txt
+│   └── ...
+├── processed
+│   ├── Acquisition.txt
+│   ├── Performance.txt
+│   ├── train.csv
+├── .gitignore
+├── annotate.py
+├── assemble.py
+├── predict.py
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
+### 撰写 README
+
+既然我们完成了端到端的项目,那么我们可以撰写 README.md 文件了,这样其他人便可以知道我们做的事,以及如何复制它。一个项目典型的 README.md 应该包括这些部分:
+
+- 一个高水准的项目概览,并介绍项目目的
+- 任何必需的数据和材料的下载地址
+- 安装命令
+    - 如何安装要求依赖
+- 使用命令
+    - 如何运行项目
+    - 每一步之后会看到的结果
+- 如何为这个项目作贡献
+    - 扩展项目的下一步计划
+
+[这里][42] 是这个项目的一个 README.md 样例。
+
+### 下一步
+
+恭喜你完成了端到端的机器学习项目!你可以在[这里][43]找到一个完整的示例项目。一旦你完成了项目,把它上传到 [Github][44] 是一个不错的主意,这样其他人也可以看到你的文件夹的部分项目。
+
+这里仍有一些留待探索数据的角度。总的来说,我们可以把它们分割为 3 类 - 扩展这个项目并使它更加精确,发现预测其他列并探索数据。这是其中一些想法:
+
+- 在 `annotate.py` 中生成更多的特性
+- 切换 `predict.py` 中的算法
+- 尝试使用比我们发表在这里的更多的来自 `Fannie Mae` 的数据
+- 添加对未来数据进行预测的方法。如果我们添加更多数据,我们所写的代码仍然可以起作用,这样我们可以添加更多过去和未来的数据。
+- 尝试看看是否你能预测一个银行是否应该发放贷款(相对地,`Fannie Mae` 是否应该获得贷款)
+    - 移除 train 中银行不知道发放贷款的时间的任何列
+        - 当 Fannie Mae 购买贷款时,一些列是已知的,但不是之前
+    - 做出预测
+- 探索是否你可以预测除了 foreclosure_status 的其他列
+    - 你可以预测在销售时财产值多少?
+- 探索探索性能更新之间的细微差别
+    - 你能否预测借款人会逾期还款多少次?
+    - 你能否标出的典型贷款周期?
+- 标出一个州到州或邮政编码到邮政级水平的数据
+    - 你看到一些有趣的模式了吗?
+
+如果你建立了任何有趣的东西,请在评论中让我们知道!
+
+如果你喜欢这个,你可能会喜欢阅读 ‘Build a Data Science Porfolio’ 系列的其他文章:
+
+- [Storytelling with data][45].
+- [How to setup up a data science blog][46].
diff --git a/sources/tech/20160506 Setup honeypot in Kali Linux.md b/sources/tech/20160506 Setup honeypot in Kali Linux.md
new file mode 100644
index 0000000000..cb85992389
--- /dev/null
+++ b/sources/tech/20160506 Setup honeypot in Kali Linux.md	
@@ -0,0 +1,84 @@
+Setup honeypot in Kali Linux
+====
+
+The Pentbox is a safety kit containing various tools for streamlining PenTest conducting a job easily. It is programmed in Ruby and oriented to GNU / Linux, with support for Windows, MacOS and every systems where Ruby is installed. In this small article we will explain how to set up a honeypot in Kali Linux. If you don’t know what is a honeypot, “a honeypot is a computer security mechanism set to detect, deflect, or, in some manner, counteract attempts at unauthorized use of information systems.”
+
+### Download Pentbox:
+
+Simply type in the following command in your terminal to download pentbox-1.8.
+
+```
+root@kali:~# wget http://downloads.sourceforge.net/project/pentbox18realised/pentbox-1.8.tar.gz
+```
+
+![](https://www.blackmoreops.com/wp-content/uploads/2016/05/Set-up-a-honeypot-in-Kali-Linux-blackMORE-Ops-1.jpg)
+
+### Uncompress pentbox files
+
+Decompressing the file with the following command:
+
+```
+root@kali:~# tar -zxvf pentbox-1.8.tar.gz
+```
+
+![](https://www.blackmoreops.com/wp-content/uploads/2016/05/Set-up-a-honeypot-in-Kali-Linux-blackMORE-Ops-2.jpg)
+
+### Run pentbox ruby script
+
+Change directory into pentbox folder
+
+```
+root@kali:~# cd pentbox-1.8/
+```
+
+![](https://www.blackmoreops.com/wp-content/uploads/2016/05/Set-up-a-honeypot-in-Kali-Linux-blackMORE-Ops-3.jpg)
+
+Run pentbox using the following command
+
+```
+root@kali:~# ./pentbox.rb
+```
+
+![](https://www.blackmoreops.com/wp-content/uploads/2016/05/Set-up-a-honeypot-in-Kali-Linux-blackMORE-Ops-4.jpg)
+
+### Setup a honeypot
+
+Use option 2 (Network Tools) and then option 3 (Honeypot).
+
+![](https://www.blackmoreops.com/wp-content/uploads/2016/05/Set-up-a-honeypot-in-Kali-Linux-blackMORE-Ops-5.jpg)
+
+Finally for first test, choose option 1 (Fast Auto Configuration)
+
+![](https://www.blackmoreops.com/wp-content/uploads/2016/05/Set-up-a-honeypot-in-Kali-Linux-blackMORE-Ops-6.jpg)
+
+This opens up a honeypot in port 80. Simply open browser and browse to http://192.168.160.128 (where 192.168.160.128 is your IP Address. You should see an Access denied error.
+
+![](https://www.blackmoreops.com/wp-content/uploads/2016/05/Set-up-a-honeypot-in-Kali-Linux-blackMORE-Ops-7.jpg)
+
+and in the terminal you should see “HONEYPOT ACTIVATED ON PORT 80” followed by “INTRUSION ATTEMPT DETECTED”.
+
+![](https://www.blackmoreops.com/wp-content/uploads/2016/05/Set-up-a-honeypot-in-Kali-Linux-blackMORE-Ops-8.jpg)
+
+Now, if you do the same steps but this time select Option 2 (Manual Configuration), you should see more extra options
+
+![](https://www.blackmoreops.com/wp-content/uploads/2016/05/Set-up-a-honeypot-in-Kali-Linux-blackMORE-Ops-9.jpg)
+
+Do the same steps but select port 22 this time (SSH Port). Then do a port forwarding in your home router to forward port external port 22 to this machines’ port 22. Alternatively, set it up in a VPS in your cloud server.
+
+You’d be amazed how many bots out there scanning port SSH continuously. You know what you do then? You try to hack them back for the lulz!
+
+Here’s a video of setting up honeypot if video is your thing:
+
+<https://youtu.be/NufOMiktplA>
+
+--------------------------------------------------------------------------------
+
+via: https://www.blackmoreops.com/2016/05/06/setup-honeypot-in-kali-linux/
+
+作者:[blackmoreops.com][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: blackmoreops.com
diff --git a/translated/tech/20160512  Rapid prototyping with docker-compose.md b/sources/tech/20160512  Rapid prototyping with docker-compose.md
similarity index 66%
rename from translated/tech/20160512  Rapid prototyping with docker-compose.md
rename to sources/tech/20160512  Rapid prototyping with docker-compose.md
index a32f894d4b..0c67223697 100644
--- a/translated/tech/20160512  Rapid prototyping with docker-compose.md	
+++ b/sources/tech/20160512  Rapid prototyping with docker-compose.md	
@@ -1,36 +1,36 @@
-使用docker快速组成样品机
+
+Rapid prototyping with docker-compose
 ========================================
 
-在写前,我们将看看 Node.js 样机 ** 找寻树莓派 PI Zero ** 的供应在英国三个主要销售.
+In this write-up we'll look at a Node.js prototype for **finding stock of the Raspberry PI Zero** from three major outlets in the UK. 
 
-我写的代码,黑客部署到 Azure Ubuntu 虚拟机一个晚上就可以到位。Docker 和 docker-compose 工具做出调配和更新过程非常快。
+I wrote the code and deployed it to an Ubuntu VM in Azure within a single evening of hacking. Docker and the docker-compose tool made the deployment and update process extremely quick.
 
-### 建立链接?
+### Remember linking?
 
-
-如果您已经通过 [动手 Docker 教程指南] [1] 那么你已有在命令行建立 Docker 容器的经验。链接一个Redis 服务器计数器节点在命令行上可能是这样:
+If you've already been through the [Hands-On Docker tutorial][1] then you will have experience linking Docker containers on the command line. Linking a Node hit counter to a Redis server on the command line may look like this:
 
 ```
 $ docker run -d -P --name redis1
 $ docker run -d hit_counter -p 3000:3000 --link redis1:redis
 ```
 
-现在,假设应用程序中有三个等级
+Now imagine your application has three tiers
 
-- Web 前端
-- 批次层处理长时间运行的任务
-- Redis 或 MongoDB 数据库
+- Web front-end
+- Batch tier for processing long running tasks
+- Redis or mongo database
 
-通过 `--link` 管理几个容器,但可能失效,可以添加多层级或容器到应用程序。
+Explicit linking through `--link` is just about manageable with a couple of containers, but can get out of hand as we add more tiers or containers to the application.
 
-### 键入 docker 撰写
+### Enter docker-compose
 
 ![](http://blog.alexellis.io/content/images/2016/05/docker-compose-logo-01.png)
->Docker 撰写图标
+>Docker Compose logo
 
-docker-compose 工具是标准的 docker工具箱的一部分,也可以单独下载。它提供了丰富功能,通过一个纯文本YAML文件配置所有应用程序组件。
+The docker-compose tool is part of the standard Docker Toolbox and can also be downloaded separately. It provides a rich set of features to configure all of an application's parts through a plain-text YAML file.
 
-上述提供了一个例子:
+The above example would look like this:
 
 ```
 version: "2.0"  
@@ -43,18 +43,18 @@ services:
      - 3000:3000
 ```
 
-从Docker 1.10起,我们可以充分利用网络来帮助我们在多个主机进行扩展覆盖。在此之前,仅通过单个主机工作。“docker-compose scale” 命令可用于更多计算能力有需要时。
+From Docker 1.10 onwards we can take advantage of network overlays to help us scale out across multiple hosts. Prior to this linking only worked across a single host. The `docker-compose scale` command can be used to bring on more computing power as the need arises.
 
->参考docker.com上关于"docker-compose"
+>View the [docker-compose][2] reference on docker.com
 
-### 真实例子:树莓派 PI 到货通知
+### Real-world example: Raspberry PI Stock Alert
 
 ![](http://blog.alexellis.io/content/images/2016/05/Raspberry_Pi_Zero_ver_1-3_1_of_3_large.JPG)
->新版树莓派 PI Zero V1.3 图片提供来自Pimoroni
+>The new Raspberry PI Zero v1.3 image courtesy of Pimoroni
 
-树莓派 PI Zero - 巨大的轰动一个微型计算机具有一个1GHz 处理器 和 512MB 内存能够运行完整 Linux,Docker,Node.js,Ruby 和许多流行的开源工具。一个关于 PI Zero 的好消息是,成本只有5美元。这也意味着,存量迅速抢购一空。
+There is a huge buzz around the Raspberry PI Zero - a tiny microcomputer with a 1GHz CPU and 512MB RAM capable of running full Linux, Docker, Node.js, Ruby and many other popular open-source tools. One of the best things about the PI Zero is that costs only 5 USD. That also means that stock gets snapped up really quickly.
 
-*如果您想尝试Docker 或集群在PI看看下面的教程。*
+*If you want to try Docker or Swarm on the PI check out the tutorial below.*
 
 >[Docker Swarm on the PI Zero][3]
 
@@ -127,7 +127,7 @@ Preview as of 16th of May 2016
 via: http://blog.alexellis.io/rapid-prototype-docker-compose/
 
 作者:[Alex Ellis][a]
-译者:[erlinux](https://github.com/erlinux)
+译者:[译者ID](https://github.com/译者ID)
 校对:[校对者ID](https://github.com/校对者ID)
 
 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
@@ -139,3 +139,4 @@ via: http://blog.alexellis.io/rapid-prototype-docker-compose/
 [4]: https://github.com/alexellis/pi_zero_stock
 [5]: https://github.com/alexellis/pi_zero_stock
 [6]: http://stockalert.alexellis.io/
+
diff --git a/sources/tech/20160512 Python unittest - assertTrue is truthy - assertFalse is falsy.md b/sources/tech/20160512 Python unittest - assertTrue is truthy - assertFalse is falsy.md
new file mode 100644
index 0000000000..30589a9d63
--- /dev/null
+++ b/sources/tech/20160512 Python unittest - assertTrue is truthy - assertFalse is falsy.md	
@@ -0,0 +1,177 @@
+Translating by cposture
+Python unittest: assertTrue is truthy, assertFalse is falsy
+===========================
+
+In this post, I explore the differences between the unittest boolean assert methods assertTrue and assertFalse and the assertIs identity assertion.
+
+### Definitions
+
+Here’s what the [unittest module documentation][1] currently notes about assertTrue and assertFalse, with the appropriate code highlighted:
+
+
+>assertTrue(expr, msg=None)
+
+>assertFalse(expr, msg=None)
+
+>>Test that expr is true (or false).
+
+>>Note that this is equivalent to
+
+>>bool(expr) is True
+
+>>and not to
+
+>>expr is True
+
+>>(use assertIs(expr, True) for the latter).
+
+[Mozilla Developer Network defines truthy][2] as:
+
+> A value that translates to true when evaluated in a Boolean context.
+In Python this is equivalent to:
+
+```
+bool(expr) is True
+```
+
+Which exactly matches what assertTrue is testing for.
+
+Therefore the documentation already indicates assertTrue is truthy and assertFalse is falsy. These assertion methods are creating a bool from the received value and then evaluating it. It also suggests that we really shouldn’t use assertTrue or assertFalse for very much at all.
+
+### What does this mean in practice?
+
+Let’s use a very simple example - a function called always_true that returns True. We’ll write the tests for it and then make changes to the code and see how the tests perform.
+
+Starting with the tests, we’ll have two tests. One is “loose”, using assertTrue to test for a truthy value. The other is “strict” using assertIs as recommended by the documentation:
+
+```
+import unittest
+
+from func import always_true
+
+
+class TestAlwaysTrue(unittest.TestCase):
+
+    def test_assertTrue(self):
+        """
+        always_true returns a truthy value
+        """
+        result = always_true()
+
+        self.assertTrue(result)
+
+    def test_assertIs(self):
+        """
+        always_true returns True
+        """
+        result = always_true()
+
+        self.assertIs(result, True)
+```
+
+Here’s the code for our simple function in func.py:
+
+```
+def always_true():
+    """
+    I'm always True.
+
+    Returns:
+        bool: True
+    """
+    return True
+```
+
+When run, everything passes:
+
+```
+always_true returns True ... ok
+always_true returns a truthy value ... ok
+
+----------------------------------------------------------------------
+Ran 2 tests in 0.004s
+
+OK
+```
+
+Happy days!
+
+Now, “someone” changes always_true to the following:
+
+```
+def always_true():
+    """
+    I'm always True.
+
+    Returns:
+        bool: True
+    """
+    return 'True'
+```
+
+Instead of returning True (boolean), it’s now returning string 'True'. (Of course this “someone” hasn’t updated the docstring - we’ll raise a ticket later.)
+
+This time the result is not so happy:
+
+```
+always_true returns True ... FAIL
+always_true returns a truthy value ... ok
+
+======================================================================
+FAIL: always_true returns True
+----------------------------------------------------------------------
+Traceback (most recent call last):
+  File "/tmp/assertttt/test.py", line 22, in test_is_true
+    self.assertIs(result, True)
+AssertionError: 'True' is not True
+
+----------------------------------------------------------------------
+Ran 2 tests in 0.004s
+
+FAILED (failures=1)
+```
+
+Only one test failed! This means assertTrue gave us a false-positive. It passed when it shouldn’t have. It’s lucky we wrote the second test with assertIs.
+
+Therefore, just as we learned from the manual, to keep the functionality of always_true pinned tightly the stricter assertIs should be used rather than assertTrue.
+
+### Use assertion helpers
+
+Writing out assertIs to test for True and False values is not too lengthy. However, if you have a project in which you often need to check that values are exactly True or exactly False, then you can make yourself the assertIsTrue and assertIsFalse assertion helpers.
+
+This doesn’t save a particularly large amount of code, but it does improve readability in my opinion.
+
+```
+def assertIsTrue(self, value):
+    self.assertIs(value, True)
+
+def assertIsFalse(self, value):
+    self.assertIs(value, False)
+```
+
+### Summary
+
+In general, my recommendation is to keep tests as tight as possible. If you mean to test for the exact value True or False, then follow the [documentation][3] and use assertIs. Do not use assertTrue or assertFalse unless you really have to.
+
+If you are looking at a function that can return various types, for example, sometimes bool sometimes int, then consider refactoring. This is a code smell and in Python, that False value for an error would probably be better raised as an exception.
+
+In addition, if you really need to assert the return value from a function under test is truthy, there might be a second code smell - is your code correctly encapsulated? If assertTrue and assertFalse are asserting that function return values will trigger if statements correctly, then it might be worth sense-checking you’ve encapsulated everything you intended in the appropriate place. Maybe those if statements should be encapsulated within the function under test.
+
+Happy testing!
+
+
+
+--------------------------------------------------------------------------------
+
+via: http://jamescooke.info/python-unittest-asserttrue-is-truthy-assertfalse-is-falsy.html
+
+作者:[James Cooke][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://jamescooke.info/pages/hello-my-name-is-james.html
+[1]:https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertTrue
+[2]:https://developer.mozilla.org/en-US/docs/Glossary/Truthy
+[3]:https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertTrue
diff --git a/sources/tech/20160602 How to build and deploy a Facebook Messenger bot with Python and Flask.md b/sources/tech/20160602 How to build and deploy a Facebook Messenger bot with Python and Flask.md
deleted file mode 100644
index 6ab74e3527..0000000000
--- a/sources/tech/20160602 How to build and deploy a Facebook Messenger bot with Python and Flask.md	
+++ /dev/null
@@ -1,320 +0,0 @@
-wyangsun translating
-How to build and deploy a Facebook Messenger bot with Python and Flask, a tutorial
-==========================================================================
-
-This is my log of how I built a simple Facebook Messenger bot. The functionality is really simple, it’s an echo bot that will just print back to the user what they write.
-
-This is something akin to the Hello World example for servers, the echo server.
-
-The goal of the project is not to build the best Messenger bot, but rather to get a feel for what it takes to build a minimal bot and how everything comes together.
-
-- [Tech Stack][1]
-- [Bot Architecture][2]
-- [The Bot Server][3]
-- [Deploying to Heroku][4]
-- [Creating the Facebook App][5]
-- [Conclusion][6]
-
-### Tech Stack
-
-The tech stack that was used is:
-
-- [Heroku][7] for back end hosting. The free-tier is more than enough for a tutorial of this level. The echo bot does not require any sort of data persistence so a database was not used.
-- [Python][8] was the language of choice. The version that was used is 2.7 however it can easily be ported to Python 3 with minor alterations.
-- [Flask][9] as the web development framework. It’s a very lightweight framework that’s perfect for small scale projects/microservices.
-- Finally the [Git][10] version control system was used for code maintenance and to deploy to Heroku.
-- Worth mentioning: [Virtualenv][11]. This python tool is used to create “environments” clean of python libraries so you can only install the necessary requirements and minimize the app footprint.
-
-### Bot Architecture
-
-Messenger bots are constituted by a server that responds to two types of requests:
-
-- GET requests are being used for authentication. They are sent by Messenger with an authentication code that you register on FB.
-- POST requests are being used for the actual communication. The typical workflow is that the bot will initiate the communication by sending the POST request with the data of the message sent by the user, we will handle it, send a POST request of our own back. If that one is completed successfully (a 200 OK status is returned) we also respond with a 200 OK code to the initial Messenger request.
-For this tutorial the app will be hosted on Heroku, which provides a nice and easy interface to deploy apps. As mentioned the free tier will suffice for this tutorial.
-
-After the app has been deployed and is running, we’ll create a Facebook app and link it to our app so that messenger knows where to send the requests that are meant for our bot.
-
-### The Bot Server
-The basic server code was taken from the following [Chatbot][12] project by Github user [hult (Magnus Hult)][13], with a few modifications to the code to only echo messages and a couple bugfixes I came across. This is the final version of the server code:
-
-```
-from flask import Flask, request
-import json
-import requests
-
-app = Flask(__name__)
-
-# This needs to be filled with the Page Access Token that will be provided
-# by the Facebook App that will be created.
-PAT = ''
-
-@app.route('/', methods=['GET'])
-def handle_verification():
-  print "Handling Verification."
-  if request.args.get('hub.verify_token', '') == 'my_voice_is_my_password_verify_me':
-    print "Verification successful!"
-    return request.args.get('hub.challenge', '')
-  else:
-    print "Verification failed!"
-    return 'Error, wrong validation token'
-
-@app.route('/', methods=['POST'])
-def handle_messages():
-  print "Handling Messages"
-  payload = request.get_data()
-  print payload
-  for sender, message in messaging_events(payload):
-    print "Incoming from %s: %s" % (sender, message)
-    send_message(PAT, sender, message)
-  return "ok"
-
-def messaging_events(payload):
-  """Generate tuples of (sender_id, message_text) from the
-  provided payload.
-  """
-  data = json.loads(payload)
-  messaging_events = data["entry"][0]["messaging"]
-  for event in messaging_events:
-    if "message" in event and "text" in event["message"]:
-      yield event["sender"]["id"], event["message"]["text"].encode('unicode_escape')
-    else:
-      yield event["sender"]["id"], "I can't echo this"
-
-
-def send_message(token, recipient, text):
-  """Send the message text to recipient with id recipient.
-  """
-
-  r = requests.post("https://graph.facebook.com/v2.6/me/messages",
-    params={"access_token": token},
-    data=json.dumps({
-      "recipient": {"id": recipient},
-      "message": {"text": text.decode('unicode_escape')}
-    }),
-    headers={'Content-type': 'application/json'})
-  if r.status_code != requests.codes.ok:
-    print r.text
-
-if __name__ == '__main__':
-  app.run()
-```
-
-Let’s break down the code. The first part is the imports that will be needed:
-
-```
-from flask import Flask, request
-import json
-import requests
-```
-
-Next we define the two functions (using the Flask specific app.route decorators) that will handle the GET and POST requests to our bot.
-
-```
-@app.route('/', methods=['GET'])
-def handle_verification():
-  print "Handling Verification."
-  if request.args.get('hub.verify_token', '') == 'my_voice_is_my_password_verify_me':
-    print "Verification successful!"
-    return request.args.get('hub.challenge', '')
-  else:
-    print "Verification failed!"
-    return 'Error, wrong validation token'
-```
-
-The verify_token object that is being sent by Messenger will be declared by us when we create the Facebook app. We have to validate the one we are being have against itself. Finally we return the “hub.challenge” back to Messenger.
-
-The function that handles the POST requests is a bit more interesting.
-
-```
-@app.route('/', methods=['POST'])
-def handle_messages():
-  print "Handling Messages"
-  payload = request.get_data()
-  print payload
-  for sender, message in messaging_events(payload):
-    print "Incoming from %s: %s" % (sender, message)
-    send_message(PAT, sender, message)
-  return "ok"
-```
-
-When called we grab the massage payload, use function messaging_events to break it down and extract the sender user id and the actual message sent, generating a python iterator that we can loop over. Notice that in each request sent by Messenger it is possible to have more than one messages.
-
-```
-def messaging_events(payload):
-  """Generate tuples of (sender_id, message_text) from the
-  provided payload.
-  """
-  data = json.loads(payload)
-  messaging_events = data["entry"][0]["messaging"]
-  for event in messaging_events:
-    if "message" in event and "text" in event["message"]:
-      yield event["sender"]["id"], event["message"]["text"].encode('unicode_escape')
-    else:
-      yield event["sender"]["id"], "I can't echo this"
-```
-
-While iterating over each message we call the send_message function and we perform the POST request back to Messnger using the Facebook Graph messages API. During this time we still have not responded to the original Messenger request which we are blocking. This can lead to timeouts and 5XX errors.
-
-The above was spotted during an outage due to a bug I came across, which was occurred when the user was sending emojis which are actual unicode ids, however Python was miss-encoding. We ended up sending back garbage.
-
-This POST request back to Messenger would never finish, and that in turn would cause 5XX status codes to be returned to the original request, rendering the service unusable.
-
-This was fixed by escaping the messages with `encode('unicode_escape')` and then just before we sent back the message decode it with `decode('unicode_escape')`.
-
-```
-def send_message(token, recipient, text):
-  """Send the message text to recipient with id recipient.
-  """
-
-  r = requests.post("https://graph.facebook.com/v2.6/me/messages",
-    params={"access_token": token},
-    data=json.dumps({
-      "recipient": {"id": recipient},
-      "message": {"text": text.decode('unicode_escape')}
-    }),
-    headers={'Content-type': 'application/json'})
-  if r.status_code != requests.codes.ok:
-    print r.text
-```
-
-### Deploying to Heroku
-
-Once the code was built to my liking it was time for the next step.
-Deploy the app.
-
-Sure, but how?
-
-I have deployed apps before to Heroku (mainly Rails) however I was always following a tutorial of some sort, so the configuration has already been created. In this case though I had to start from scratch.
-
-Fortunately it was the official [Heroku documentation][14] to the rescue. The article explains nicely the bare minimum required for running an app.
-
-Long story short, what we need besides our code are two files. The first file is the “requirements.txt” file which is a list of of the library dependencies required to run the application.
-
-The second file required is the “Procfile”. This file is there to inform the Heroku how to run our service. Again the bare minimum needed for this file is the following:
-
->web: gunicorn echoserver:app 
-
-The way this will be interpreted by heroku is that our app is started by running the echoserver.py file and the app will be using gunicorn as the web server. The reason we are using an additional webserver is performance related and is explained in the above Heroku documentation:
-
->Web applications that process incoming HTTP requests concurrently make much more efficient use of dyno resources than web applications that only process one request at a time. Because of this, we recommend using web servers that support concurrent request processing whenever developing and running production services.
-
->The Django and Flask web frameworks feature convenient built-in web servers, but these blocking servers only process a single request at a time. If you deploy with one of these servers on Heroku, your dyno resources will be underutilized and your application will feel unresponsive.
-
->Gunicorn is a pure-Python HTTP server for WSGI applications. It allows you to run any Python application concurrently by running multiple Python processes within a single dyno. It provides a perfect balance of performance, flexibility, and configuration simplicity.
-
-Going back to our “requirements.txt” file let’s see how it binds with the Virtualenv tool that was mentioned.
-
-At anytime, your developement machine may have a number of python libraries installed. When deploying applications you don’t want to have these libraries loaded as it makes it hard to make out which ones you actually use.
-
-What Virtualenv does is create a new blank virtual enviroment so that you can only install the libraries that your app requires.
-
-You can check which libraries are currently installed by running the following command:
-
-```
-kostis@KostisMBP ~ $ pip freeze
-cycler==0.10.0
-Flask==0.10.1
-gunicorn==19.6.0
-itsdangerous==0.24
-Jinja2==2.8
-MarkupSafe==0.23
-matplotlib==1.5.1
-numpy==1.10.4
-pyparsing==2.1.0
-python-dateutil==2.5.0
-pytz==2015.7
-requests==2.10.0
-scipy==0.17.0
-six==1.10.0
-virtualenv==15.0.1
-Werkzeug==0.11.10
-```
-
-Note: The pip tool should already be installed on your machine along with Python.
-
-If not check the [official site][15] for how to install it.
-
-Now let’s use Virtualenv to create a new blank enviroment. First we create a new folder for our project, and change dir into it:
-
-```
-kostis@KostisMBP projects $ mkdir echoserver
-kostis@KostisMBP projects $ cd echoserver/
-kostis@KostisMBP echoserver $
-```
-
-Now let’s create a new enviroment called echobot. To activate it you run the following source command, and checking with pip freeze we can see that it’s now empty.
-
-```
-kostis@KostisMBP echoserver $ virtualenv echobot
-kostis@KostisMBP echoserver $ source echobot/bin/activate
-(echobot) kostis@KostisMBP echoserver $ pip freeze
-(echobot) kostis@KostisMBP echoserver $
-```
-
-We can start installing the libraries required. The ones we’ll need are flask, gunicorn, and requests and with them installed we create the requirements.txt file:
-
-```
-(echobot) kostis@KostisMBP echoserver $ pip install flask
-(echobot) kostis@KostisMBP echoserver $ pip install gunicorn
-(echobot) kostis@KostisMBP echoserver $ pip install requests
-(echobot) kostis@KostisMBP echoserver $ pip freeze
-click==6.6
-Flask==0.11
-gunicorn==19.6.0
-itsdangerous==0.24
-Jinja2==2.8
-MarkupSafe==0.23
-requests==2.10.0
-Werkzeug==0.11.10
-(echobot) kostis@KostisMBP echoserver $ pip freeze > requirements.txt
-```
-
-After all the above have been run, we create the echoserver.py file with the python code and the Procfile with the command that was mentioned, and we should end up with the following files/folders:
-
-```
-(echobot) kostis@KostisMBP echoserver $ ls
-Procfile     echobot     echoserver.py   requirements.txt
-```
-
-We are now ready to upload to Heroku. We need to do two things. The first is to install the Heroku toolbet if it’s not already installed on your system (go to [Heroku][16] for details). The second is to create a new Heroku app through the [web interface][17].
-
-Click on the big plus sign on the top right and select “Create new app”.    
-
-
-
-
-
-
-
-
---------------------------------------------------------------------------------
-
-via: http://tsaprailis.com/2016/06/02/How-to-build-and-deploy-a-Facebook-Messenger-bot-with-Python-and-Flask-a-tutorial/
-
-作者:[Konstantinos Tsaprailis][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: https://github.com/kostistsaprailis
-[1]: http://tsaprailis.com/2016/06/02/How-to-build-and-deploy-a-Facebook-Messenger-bot-with-Python-and-Flask-a-tutorial/#tech-stack
-[2]: http://tsaprailis.com/2016/06/02/How-to-build-and-deploy-a-Facebook-Messenger-bot-with-Python-and-Flask-a-tutorial/#bot-architecture
-[3]: http://tsaprailis.com/2016/06/02/How-to-build-and-deploy-a-Facebook-Messenger-bot-with-Python-and-Flask-a-tutorial/#the-bot-server
-[4]: http://tsaprailis.com/2016/06/02/How-to-build-and-deploy-a-Facebook-Messenger-bot-with-Python-and-Flask-a-tutorial/#deploying-to-heroku
-[5]: http://tsaprailis.com/2016/06/02/How-to-build-and-deploy-a-Facebook-Messenger-bot-with-Python-and-Flask-a-tutorial/#creating-the-facebook-app
-[6]: http://tsaprailis.com/2016/06/02/How-to-build-and-deploy-a-Facebook-Messenger-bot-with-Python-and-Flask-a-tutorial/#conclusion
-[7]: https://www.heroku.com
-[8]: https://www.python.org
-[9]: http://flask.pocoo.org
-[10]: https://git-scm.com
-[11]: https://virtualenv.pypa.io/en/stable
-[12]: https://github.com/hult/facebook-chatbot-python
-[13]: https://github.com/hult
-[14]: https://devcenter.heroku.com/articles/python-gunicorn
-[15]: https://pip.pypa.io/en/stable/installing
-[16]: https://toolbelt.heroku.com
-[17]: https://dashboard.heroku.com/apps
-
-
diff --git a/sources/tech/20160608 Implementing Mandatory Access Control with SELinux or AppArmor in Linux.md b/sources/tech/20160608 Implementing Mandatory Access Control with SELinux or AppArmor in Linux.md
deleted file mode 100644
index 65c8657dff..0000000000
--- a/sources/tech/20160608 Implementing Mandatory Access Control with SELinux or AppArmor in Linux.md	
+++ /dev/null
@@ -1,248 +0,0 @@
-Implementing Mandatory Access Control with SELinux or AppArmor in Linux
-===========================================================================
-
-To overcome the limitations of and to increase the security mechanisms provided by standard ugo/rwx permissions and [access control lists][1], the United States National Security Agency (NSA) devised a flexible Mandatory Access Control (MAC) method known as SELinux (short for Security Enhanced Linux) in order to restrict among other things, the ability of processes to access or perform other operations on system objects (such as files, directories, network ports, etc) to the least permission possible, while still allowing for later modifications to this model.
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/SELinux-AppArmor-Security-Hardening-Linux.png)
->SELinux and AppArmor Security Hardening Linux
-
-Another popular and widely-used MAC is AppArmor, which in addition to the features provided by SELinux, includes a learning mode that allows the system to “learn” how a specific application behaves, and to set limits by configuring profiles for safe application usage.
-
-In CentOS 7, SELinux is incorporated into the kernel itself and is enabled in Enforcing mode by default (more on this in the next section), as opposed to openSUSE and Ubuntu which use AppArmor.
-
-In this article we will explain the essentials of SELinux and AppArmor and how to use one of these tools for your benefit depending on your chosen distribution.
-
-### Introduction to SELinux and How to Use it on CentOS 7
-
-Security Enhanced Linux can operate in two different ways:
-
-- Enforcing: SELinux denies access based on SELinux policy rules, a set of guidelines that control the security engine.
-- Permissive: SELinux does not deny access, but denials are logged for actions that would have been denied if running in enforcing mode.
-
-SELinux can also be disabled. Although it is not an operation mode itself, it is still an option. However, learning how to use this tool is better than just ignoring it. Keep it in mind!
-
-To display the current mode of SELinux, use getenforce. If you want to toggle the operation mode, use setenforce 0 (to set it to Permissive) or setenforce 1 (Enforcing).
-
-Since this change will not survive a reboot, you will need to edit the /etc/selinux/config file and set the SELINUX variable to either enforcing, permissive, or disabled in order to achieve persistence across reboots:
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/Enable-Disable-SELinux-Mode.png)
->How to Enable and Disable SELinux Mode
-
-On a side note, if getenforce returns Disabled, you will have to edit /etc/selinux/config with the desired operation mode and reboot. Otherwise, you will not be able to set (or toggle) the operation mode with setenforce.
-
-One of the typical uses of setenforce consists of toggling between SELinux modes (from enforcing to permissive or the other way around) to troubleshoot an application that is misbehaving or not working as expected. If it works after you set SELinux to Permissive mode, you can be confident you’re looking at a SELinux permissions issue.
-
-Two classic cases where we will most likely have to deal with SELinux are:
-
-- Changing the default port where a daemon listens on.
-- Setting the DocumentRoot directive for a virtual host outside of /var/www/html.
-
-Let’s take a look at these two cases using the following examples.
-
-#### EXAMPLE 1: Changing the default port for the sshd daemon
-
-One of the first thing most system administrators do in order to secure their servers is change the port where the SSH daemon listens on, mostly to discourage port scanners and external attackers. To do this, we use the Port directive in `/etc/ssh/sshd_config` followed by the new port number as follows (we will use port 9999 in this case):
-
-```
-Port 9999
-```
-
-After attempting to restart the service and checking its status we will see that it failed to start:
-
-```
-# systemctl restart sshd
-# systemctl status sshd
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/Check-sshd-Service-Status.png)
->Check SSH Service Status
-
-If we take a look at /var/log/audit/audit.log, we will see that sshd was prevented from starting on port 9999 by SELinux because that is a reserved port for the JBoss Management service (SELinux log messages include the word “AVC” so that they might be easily identified from other messages):
-
-```
-# cat /var/log/audit/audit.log | grep AVC | tail -1
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/Check-Linux-Audit-Logs.png)
->Check Linux Audit Logs
-
-At this point most people would probably disable SELinux but we won’t. We will see that there’s a way for SELinux, and sshd listening on a different port, to live in harmony together. Make sure you have the policycoreutils-python package installed and run:
-
-```
-# yum install policycoreutils-python
-```
-
-To view a list of the ports where SELinux allows sshd to listen on. In the following image we can also see that port 9999 was reserved for another service and thus we can’t use it to run another service for the time being:
-
-```
-# semanage port -l | grep ssh
-```
-
-Of course we could choose another port for SSH, but if we are certain that we will not need to use this specific machine for any JBoss-related services, we can then modify the existing SELinux rule and assign that port to SSH instead:
-
-```
-# semanage port -m -t ssh_port_t -p tcp 9999
-```
-
-After that, we can use the first semanage command to check if the port was correctly assigned, or the -lC options (short for list custom):
-
-```
-# semanage port -lC
-# semanage port -l | grep ssh
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/Assign-Port-to-SSH.png)
->Assign Port to SSH
-
-We can now restart SSH and connect to the service using port 9999. Note that this change WILL survive a reboot.
-
-#### EXAMPLE 2: Choosing a DocumentRoot outside /var/www/html for a virtual host
-
-If you need to [set up a Apache virtual host][2] using a directory other than /var/www/html as DocumentRoot (say, for example, `/websrv/sites/gabriel/public_html`):
-
-```
-DocumentRoot “/websrv/sites/gabriel/public_html”
-```
-
-Apache will refuse to serve the content because the index.html has been labeled with the default_t SELinux type, which Apache can’t access:
-
-```
-# wget http://localhost/index.html
-# ls -lZ /websrv/sites/gabriel/public_html/index.html
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/Labeled-default_t-SELinux-Type.png)
->Labeled as default_t SELinux Type
-
-As with the previous example, you can use the following command to verify that this is indeed a SELinux-related issue:
-
-```
-# cat /var/log/audit/audit.log | grep AVC | tail -1
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/Check-Logs-for-SELinux-Issues.png)
->Check Logs for SELinux Issues
-
-To change the label of /websrv/sites/gabriel/public_html recursively to httpd_sys_content_t, do:
-
-```
-# semanage fcontext -a -t httpd_sys_content_t "/websrv/sites/gabriel/public_html(/.*)?"
-```
-
-The above command will grant Apache read-only access to that directory and its contents.
-
-Finally, to apply the policy (and make the label change effective immediately), do:
-
-```
-# restorecon -R -v /websrv/sites/gabriel/public_html
-```
-
-Now you should be able to access the directory:
-
-```
-# wget http://localhost/index.html
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/Access-Apache-Directory.png)
->Access Apache Directory
-
-For more information on SELinux, refer to the Fedora 22 [SELinux and Administrator guide][3].
-
-
-### Introduction to AppArmor and How to Use it on OpenSUSE and Ubuntu
-
-The operation of AppArmor is based on profiles defined in plain text files where the allowed permissions and access control rules are set. Profiles are then used to place limits on how applications interact with processes and files in the system.
-
-A set of profiles is provided out-of-the-box with the operating system, whereas others can be put in place either automatically by applications when they are installed or manually by the system administrator.
-
-Like SELinux, AppArmor runs profiles in two modes. In enforce mode, applications are given the minimum permissions that are necessary for them to run, whereas in complain mode AppArmor allows an application to take restricted actions and saves the “complaints” resulting from that operation to a log (/var/log/kern.log, /var/log/audit/audit.log, and other logs inside /var/log/apparmor).
-
-These logs will show through lines with the word audit in them errors that would occur should the profile be run in enforce mode. Thus, you can try out an application in complain mode and adjust its behavior before running it under AppArmor in enforce mode.
-
-The current status of AppArmor can be shown using:
-
-```
-$ sudo apparmor_status
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/Check-AppArmor-Status.png)
->Check AppArmor Status
-
-The image above indicates that the profiles /sbin/dhclient, /usr/sbin/, and /usr/sbin/tcpdump are in enforce mode (that is true by default in Ubuntu).
-
-Since not all applications include the associated AppArmor profiles, the apparmor-profiles package, which provides other profiles that have not been shipped by the packages they provide confinement for. By default, they are configured to run in complain mode so that system administrators can test them and choose which ones are desired.
-
-We will make use of apparmor-profiles since writing our own profiles is out of the scope of the LFCS [certification][4]. However, since profiles are plain text files, you can view them and study them in preparation to create your own profiles in the future.
-
-AppArmor profiles are stored inside /etc/apparmor.d. Let’s take a look at the contents of that directory before and after installing apparmor-profiles:
-
-```
-$ ls /etc/apparmor.d
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/View-AppArmor-Directory-Content.png)
->View AppArmor Directory Content
-
-If you execute sudo apparmor_status again, you will see a longer list of profiles in complain mode. You can now perform the following operations:
-
-To switch a profile currently in enforce mode to complain mode:
-
-```
-$ sudo aa-complain /path/to/file
-```
-
-and the other way around (complain –> enforce):
-
-```
-$ sudo aa-enforce /path/to/file
-```
-
-Wildcards are allowed in the above cases. For example,
-
-```
-$ sudo aa-complain /etc/apparmor.d/*
-```
-
-will place all profiles inside /etc/apparmor.d into complain mode, whereas
-
-```
-$ sudo aa-enforce /etc/apparmor.d/*
-```
-
-will switch all profiles to enforce mode.
-
-To entirely disable a profile, create a symbolic link in the /etc/apparmor.d/disabled directory:
-
-```
-$ sudo ln -s /etc/apparmor.d/profile.name /etc/apparmor.d/disable/
-```
-
-For more information on AppArmor, please refer to the [official AppArmor wiki][5] and to the documentation [provided by Ubuntu][6].
-
-### Summary
-
-In this article we have gone through the basics of SELinux and AppArmor, two well-known MACs. When to use one or the other? To avoid difficulties, you may want to consider sticking with the one that comes with your chosen distribution. In any event, they will help you place restrictions on processes and access to system resources to increase the security in your servers.
-
-Do you have any questions, comments, or suggestions about this article? Feel free to let us know using the form below. Don’t hesitate to let us know if you have any questions or comments.
-
-
---------------------------------------------------------------------------------
-
-via: http://www.tecmint.com/mandatory-access-control-with-selinux-or-apparmor-linux/
-
-作者:[Gabriel Cánepa][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.tecmint.com/author/gacanepa/
-[1]: http://www.tecmint.com/secure-files-using-acls-in-linux/
-[2]: http://www.tecmint.com/apache-virtual-hosting-in-centos/
-[3]: https://docs.fedoraproject.org/en-US/Fedora/22/html/SELinux_Users_and_Administrators_Guide/index.html
-[4]: http://www.tecmint.com/sed-command-to-create-edit-and-manipulate-files-in-linux/
-[5]: http://wiki.apparmor.net/index.php/Main_Page
-[6]: https://help.ubuntu.com/community/AppArmor
-
-
-
diff --git a/sources/tech/20160618 An Introduction to Mocking in Python.md b/sources/tech/20160618 An Introduction to Mocking in Python.md
deleted file mode 100644
index e9c5c847cb..0000000000
--- a/sources/tech/20160618 An Introduction to Mocking in Python.md	
+++ /dev/null
@@ -1,490 +0,0 @@
-An Introduction to Mocking in Python
-=====================================
-
-This article is about mocking in python,
-
-**How to Run Unit Tests Without Testing Your Patience**
-
-More often than not, the software we write directly interacts with what we would label as “dirty” services. In layman’s terms: services that are crucial to our application, but whose interactions have intended but undesired side-effects—that is, undesired in the context of an autonomous test run.For example: perhaps we’re writing a social app and want to test out our new ‘Post to Facebook feature’, but don’t want to actually post to Facebook every time we run our test suite.
-
-The Python unittest library includes a subpackage named unittest.mock—or if you declare it as a dependency, simply mock—which provides extremely powerful and useful means by which to mock and stub out these undesired side-effects.
-
->Source | <http://www.toptal.com/python/an-introduction-to-mocking-in-python>
-
-Note: mock is [newly included][1] in the standard library as of Python 3.3; prior distributions will have to use the Mock library downloadable via [PyPI][2].
-
-### Fear System Calls
-
-To give you another example, and one that we’ll run with for the rest of the article, consider system calls. It’s not difficult to see that these are prime candidates for mocking: whether you’re writing a script to eject a CD drive, a web server which removes antiquated cache files from /tmp, or a socket server which binds to a TCP port, these calls all feature undesired side-effects in the context of your unit-tests.
-
->As a developer, you care more that your library successfully called the system function for ejecting a CD as opposed to experiencing your CD tray open every time a test is run.
-
-As a developer, you care more that your library successfully called the system function for ejecting a CD (with the correct arguments, etc.) as opposed to actually experiencing your CD tray open every time a test is run. (Or worse, multiple times, as multiple tests reference the eject code during a single unit-test run!)
-
-Likewise, keeping your unit-tests efficient and performant means keeping as much “slow code” out of the automated test runs, namely filesystem and network access.
-
-For our first example, we’ll refactor a standard Python test case from original form to one using mock. We’ll demonstrate how writing a test case with mocks will make our tests smarter, faster, and able to reveal more about how the software works.
-
-### A Simple Delete Function
-
-We all need to delete files from our filesystem from time to time, so let’s write a function in Python which will make it a bit easier for our scripts to do so.
-
-```
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import os
-
-def rm(filename):
-    os.remove(filename)
-```
-
-Obviously, our rm method at this point in time doesn’t provide much more than the underlying os.remove method, but our codebase will improve, allowing us to add more functionality here.
-
-Let’s write a traditional test case, i.e., without mocks:
-
-```
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from mymodule import rm
-
-import os.path
-import tempfile
-import unittest
-
-class RmTestCase(unittest.TestCase):
-
-    tmpfilepath = os.path.join(tempfile.gettempdir(), "tmp-testfile")
-
-    def setUp(self):
-        with open(self.tmpfilepath, "wb") as f:
-            f.write("Delete me!")
-        
-    def test_rm(self):
-        # remove the file
-        rm(self.tmpfilepath)
-        # test that it was actually removed
-        self.assertFalse(os.path.isfile(self.tmpfilepath), "Failed to remove the file.")
-```
-
-Our test case is pretty simple, but every time it is run, a temporary file is created and then deleted. Additionally, we have no way of testing whether our rm method properly passes the argument down to the os.remove call. We can assume that it does based on the test above, but much is left to be desired.
-
-Refactoring with MocksLet’s refactor our test case using mock:
-
-```
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from mymodule import rm
-
-import mock
-import unittest
-
-class RmTestCase(unittest.TestCase):
-    
-    @mock.patch('mymodule.os')
-    def test_rm(self, mock_os):
-        rm("any path")
-        # test that rm called os.remove with the right parameters
-        mock_os.remove.assert_called_with("any path")
-```
-
-With these refactors, we have fundamentally changed the way that the test operates. Now, we have an insider, an object we can use to verify the functionality of another.
-
-### Potential Pitfalls
-
-One of the first things that should stick out is that we’re using the mock.patch method decorator to mock an object located at mymodule.os, and injecting that mock into our test case method. Wouldn’t it make more sense to just mock os itself, rather than the reference to it at mymodule.os?
-
-Well, Python is somewhat of a sneaky snake when it comes to imports and managing modules. At runtime, the mymodule module has its own os which is imported into its own local scope in the module. Thus, if we mock os, we won’t see the effects of the mock in the mymodule module.
-
-The mantra to keep repeating is this:
-
-> Mock an item where it is used, not where it came from.
-
-If you need to mock the tempfile module for myproject.app.MyElaborateClass, you probably need to apply the mock to myproject.app.tempfile, as each module keeps its own imports.
-
-With that pitfall out of the way, let’s keep mocking.
-
-### Adding Validation to ‘rm’
-
-The rm method defined earlier is quite oversimplified. We’d like to have it validate that a path exists and is a file before just blindly attempting to remove it. Let’s refactor rm to be a bit smarter:
-
-```
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import os
-import os.path
-
-def rm(filename):
-    if os.path.isfile(filename):
-        os.remove(filename)
-```
-
-Great. Now, let’s adjust our test case to keep coverage up.
-
-```
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from mymodule import rm
-
-import mock
-import unittest
-
-class RmTestCase(unittest.TestCase):
-    
-    @mock.patch('mymodule.os.path')
-    @mock.patch('mymodule.os')
-    def test_rm(self, mock_os, mock_path):
-        # set up the mock
-        mock_path.isfile.return_value = False
-        
-        rm("any path")
-        
-        # test that the remove call was NOT called.
-        self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")
-        
-        # make the file 'exist'
-        mock_path.isfile.return_value = True
-        
-        rm("any path")
-        
-        mock_os.remove.assert_called_with("any path")
-```
-
-Our testing paradigm has completely changed. We now can verify and validate internal functionality of methods without any side-effects.
-
-### File-Removal as a Service
-
-So far, we’ve only been working with supplying mocks for functions, but not for methods on objects or cases where mocking is necessary for sending parameters. Let’s cover object methods first.
-
-We’ll begin with a refactor of the rm method into a service class. There really isn’t a justifiable need, per se, to encapsulate such a simple function into an object, but it will at the very least help us demonstrate key concepts in mock. Let’s refactor:
-
-```
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import os
-import os.path
-
-class RemovalService(object):
-    """A service for removing objects from the filesystem."""
-
-    def rm(filename):
-        if os.path.isfile(filename):
-            os.remove(filename)
-```
-
-### You’ll notice that not much has changed in our test case:
-
-```
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from mymodule import RemovalService
-
-import mock
-import unittest
-
-class RemovalServiceTestCase(unittest.TestCase):
-    
-    @mock.patch('mymodule.os.path')
-    @mock.patch('mymodule.os')
-    def test_rm(self, mock_os, mock_path):
-        # instantiate our service
-        reference = RemovalService()
-        
-        # set up the mock
-        mock_path.isfile.return_value = False
-        
-        reference.rm("any path")
-        
-        # test that the remove call was NOT called.
-        self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")
-        
-        # make the file 'exist'
-        mock_path.isfile.return_value = True
-        
-        reference.rm("any path")
-        
-        mock_os.remove.assert_called_with("any path")
-```
-
-Great, so we now know that the RemovalService works as planned. Let’s create another service which declares it as a dependency:
-
-```
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import os
-import os.path
-
-class RemovalService(object):
-    """A service for removing objects from the filesystem."""
-
-    def rm(self, filename):
-        if os.path.isfile(filename):
-            os.remove(filename)
-            
-
-class UploadService(object):
-
-    def __init__(self, removal_service):
-        self.removal_service = removal_service
-        
-    def upload_complete(self, filename):
-        self.removal_service.rm(filename)
-```
-
-Since we already have test coverage on the RemovalService, we’re not going to validate internal functionality of the rm method in our tests of UploadService. Rather, we’ll simply test (without side-effects, of course) that UploadService calls the RemovalService.rm method, which we know “just works™” from our previous test case.
-
-There are two ways to go about this:
-
-1. Mock out the RemovalService.rm method itself.
-2. Supply a mocked instance in the constructor of UploadService.
-
-As both methods are often important in unit-testing, we’ll review both.
-
-### Option 1: Mocking Instance Methods
-
-The mock library has a special method decorator for mocking object instance methods and properties, the @mock.patch.object decorator:
-
-```
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from mymodule import RemovalService, UploadService
-
-import mock
-import unittest
-
-class RemovalServiceTestCase(unittest.TestCase):
-    
-    @mock.patch('mymodule.os.path')
-    @mock.patch('mymodule.os')
-    def test_rm(self, mock_os, mock_path):
-        # instantiate our service
-        reference = RemovalService()
-        
-        # set up the mock
-        mock_path.isfile.return_value = False
-        
-        reference.rm("any path")
-        
-        # test that the remove call was NOT called.
-        self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")
-        
-        # make the file 'exist'
-        mock_path.isfile.return_value = True
-        
-        reference.rm("any path")
-        
-        mock_os.remove.assert_called_with("any path")
-      
-      
-class UploadServiceTestCase(unittest.TestCase):
-
-    @mock.patch.object(RemovalService, 'rm')
-    def test_upload_complete(self, mock_rm):
-        # build our dependencies
-        removal_service = RemovalService()
-        reference = UploadService(removal_service)
-        
-        # call upload_complete, which should, in turn, call `rm`:
-        reference.upload_complete("my uploaded file")
-        
-        # check that it called the rm method of any RemovalService
-        mock_rm.assert_called_with("my uploaded file")
-        
-        # check that it called the rm method of _our_ removal_service
-        removal_service.rm.assert_called_with("my uploaded file")
-```
-
-Great! We’ve validated that the UploadService successfully calls our instance’s rm method. Notice anything interesting in there? The patching mechanism actually replaced the rm method of all RemovalService instances in our test method. That means that we can actually inspect the instances themselves. If you want to see more, try dropping in a breakpoint in your mocking code to get a good feel for how the patching mechanism works.
-
-### Pitfall: Decorator Order
-
-When using multiple decorators on your test methods, order is important, and it’s kind of confusing. Basically, when mapping decorators to method parameters, [work backwards][3]. Consider this example:
-
-```
-@mock.patch('mymodule.sys')
-    @mock.patch('mymodule.os')
-    @mock.patch('mymodule.os.path')
-    def test_something(self, mock_os_path, mock_os, mock_sys):
-        pass
-```
-
-Notice how our parameters are matched to the reverse order of the decorators? That’s partly because of [the way that Python works][4]. With multiple method decorators, here’s the order of execution in pseudocode:
-
-```
-patch_sys(patch_os(patch_os_path(test_something)))
-```
-
-Since the patch to sys is the outermost patch, it will be executed last, making it the last parameter in the actual test method arguments. Take note of this well and use a debugger when running your tests to make sure that the right parameters are being injected in the right order.
-
-### Option 2: Creating Mock Instances
-
-Instead of mocking the specific instance method, we could instead just supply a mocked instance to UploadService with its constructor. I prefer option 1 above, as it’s a lot more precise, but there are many cases where option 2 might be efficient or necessary. Let’s refactor our test again:
-
-```
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-from mymodule import RemovalService, UploadService
-
-import mock
-import unittest
-
-class RemovalServiceTestCase(unittest.TestCase):
-    
-    @mock.patch('mymodule.os.path')
-    @mock.patch('mymodule.os')
-    def test_rm(self, mock_os, mock_path):
-        # instantiate our service
-        reference = RemovalService()
-        
-        # set up the mock
-        mock_path.isfile.return_value = False
-        
-        reference.rm("any path")
-        
-        # test that the remove call was NOT called.
-        self.assertFalse(mock_os.remove.called, "Failed to not remove the file if not present.")
-        
-        # make the file 'exist'
-        mock_path.isfile.return_value = True
-        
-        reference.rm("any path")
-        
-        mock_os.remove.assert_called_with("any path")
-      
-      
-class UploadServiceTestCase(unittest.TestCase):
-
-    def test_upload_complete(self, mock_rm):
-        # build our dependencies
-        mock_removal_service = mock.create_autospec(RemovalService)
-        reference = UploadService(mock_removal_service)
-        
-        # call upload_complete, which should, in turn, call `rm`:
-        reference.upload_complete("my uploaded file")
-        
-        # test that it called the rm method
-        mock_removal_service.rm.assert_called_with("my uploaded file")
-```
-
-In this example, we haven’t even had to patch any functionality, we simply create an auto-spec for the RemovalService class, and then inject this instance into our UploadService to validate the functionality.
-
-The [mock.create_autospec][5] method creates a functionally equivalent instance to the provided class. What this means, practically speaking, is that when the returned instance is interacted with, it will raise exceptions if used in illegal ways. More specifically, if a method is called with the wrong number of arguments, an exception will be raised. This is extremely important as refactors happen. As a library changes, tests break and that is expected. Without using an auto-spec, our tests will still pass even though the underlying implementation is broken.
-
-### Pitfall: The mock.Mock and mock.MagicMock Classes
-
-The mock library also includes two important classes upon which most of the internal functionality is built upon: [mock.Mock][6] and mock.MagicMock. When given a choice to use a mock.Mock instance, a mock.MagicMock instance, or an auto-spec, always favor using an auto-spec, as it helps keep your tests sane for future changes. This is because mock.Mock and mock.MagicMock accept all method calls and property assignments regardless of the underlying API. Consider the following use case:
-
-```
-class Target(object):
-    def apply(value):
-        return value
-
-def method(target, value):
-    return target.apply(value)
-```
-
-We can test this with a mock.Mock instance like this:
-
-```
-class MethodTestCase(unittest.TestCase):
-
-    def test_method(self):
-        target = mock.Mock()
-
-        method(target, "value")
-
-        target.apply.assert_called_with("value")
-```
-
-This logic seems sane, but let’s modify the Target.apply method to take more parameters:
-
-```
-class Target(object):
-    def apply(value, are_you_sure):
-        if are_you_sure:
-            return value
-        else:
-            return None
-```
-
-Re-run your test, and you’ll find that it still passes. That’s because it isn’t built against your actual API. This is why you should always use the create_autospec method and the autospec parameter with the @patch and @patch.object decorators.
-
-### Real-World Example: Mocking a Facebook API Call
-
-To finish up, let’s write a more applicable real-world example, one which we mentioned in the introduction: posting a message to Facebook. We’ll write a nice wrapper class and a corresponding test case.
-
-```
-import facebook
-
-class SimpleFacebook(object):
-    
-    def __init__(self, oauth_token):
-        self.graph = facebook.GraphAPI(oauth_token)
-
-    def post_message(self, message):
-        """Posts a message to the Facebook wall."""
-        self.graph.put_object("me", "feed", message=message)
-```
-
-Here’s our test case, which checks that we post the message without actually posting the message:
-
-```
-import facebook
-import simple_facebook
-import mock
-import unittest
-
-class SimpleFacebookTestCase(unittest.TestCase):
-    
-    @mock.patch.object(facebook.GraphAPI, 'put_object', autospec=True)
-    def test_post_message(self, mock_put_object):
-        sf = simple_facebook.SimpleFacebook("fake oauth token")
-        sf.post_message("Hello World!")
-
-        # verify
-        mock_put_object.assert_called_with(message="Hello World!")
-```
-
-As we’ve seen so far, it’s really simple to start writing smarter tests with mock in Python.
-
-### Mocking in python Conclusion
-
-Python’s mock library, if a little confusing to work with, is a game-changer for [unit-testing][7]. We’ve demonstrated common use-cases for getting started using mock in unit-testing, and hopefully this article will help [Python developers][8] overcome the initial hurdles and write excellent, tested code.
-
---------------------------------------------------------------------------------
-
-via: http://slviki.com/index.php/2016/06/18/introduction-to-mocking-in-python/
-
-作者:[Dasun Sucharith][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.slviki.com/
-[1]: http://www.python.org/dev/peps/pep-0417/
-[2]: https://pypi.python.org/pypi/mock
-[3]: http://www.voidspace.org.uk/python/mock/patch.html#nesting-patch-decorators
-[4]: http://docs.python.org/2/reference/compound_stmts.html#function-definitions
-[5]: http://www.voidspace.org.uk/python/mock/helpers.html#autospeccing
-[6]: http://www.voidspace.org.uk/python/mock/mock.html
-[7]: http://www.toptal.com/qa/how-to-write-testable-code-and-why-it-matters
-[8]: http://www.toptal.com/python
-
-
-
-
-
-
-
-
-
diff --git a/sources/tech/20160621 Container technologies in Fedora - systemd-nspawn.md b/sources/tech/20160621 Container technologies in Fedora - systemd-nspawn.md
deleted file mode 100644
index 1ec72a3c90..0000000000
--- a/sources/tech/20160621 Container technologies in Fedora - systemd-nspawn.md	
+++ /dev/null
@@ -1,106 +0,0 @@
-Container technologies in Fedora: systemd-nspawn
-===
-
-Welcome to the “Container technologies in Fedora” series! This is the first article in a series of articles that will explain how you can use the various container technologies available in Fedora. This first article will deal with `systemd-nspawn`.
-
-### What is a container?
-
-A container is a user-space instance which can be used to run a program or an operating system in isolation from the system hosting the container (called the host system). The idea is very similar to a `chroot` or a [virtual machine][1]. The processes running in a container are managed by the same kernel as the host operating system, but they are isolated from the host file system, and from the other processes.
-
-
-### What is systemd-nspawn?
-
-The systemd project considers container technologies as something that should fundamentally be part of the desktop and that should integrate with the rest of the user’s systems. To this end, systemd provides `systemd-nspawn`, a tool which is able to create containers using various Linux technologies. It also provides some container management tools.
-
-In many ways, `systemd-nspawn` is similar to `chroot`, but is much more powerful. It virtualizes the file system, process tree, and inter-process communication of the guest system. Much of its appeal lies in the fact that it provides a number of tools, such as `machinectl`, for managing containers. Containers run by `systemd-nspawn` will integrate with the systemd components running on the host system. As an example, journal entries can be logged from a container in the host system’s journal.
-
-In Fedora 24, `systemd-nspawn` has been split out from the systemd package, so you’ll need to install the `systemd-container` package. As usual, you can do that with a `dnf install systemd-container`.
-
-### Creating the container
-
-Creating a container with `systemd-nspawn` is easy. Let’s say you have an application made for Debian, and it doesn’t run well anywhere else. That’s not a problem, we can make a container! To set up a container with the latest version of Debian (at this point in time, Jessie), you need to pick a directory to set up your system in. I’ll be using `~/DebianJessie` for now.
-
-Once the directory has been created, you need to run `debootstrap`, which you can install from the Fedora repositories. For Debian Jessie, you run the following command to initialize a Debian file system.
-
-```
-$ debootstrap --arch=amd64 stable ~/DebianJessie
-```
-
-This assumes your architecture is x86_64. If it isn’t, you must change `amd64` to the name of your architecture. You can find your machine’s architecture with `uname -m`.
-
-Once your root directory is set up, you will start your container with the following command.
-
-```
-$ systemd-nspawn -bD ~/DebianJessie
-```
-
-You’ll be up and running within seconds. You’ll notice something as soon as you try to log in: you can’t use any accounts on your system. This is because systemd-nspawn virtualizes users. The fix is simple: remove -b from the previous command. You’ll boot directly to the root shell in the container. From there, you can just use passwd to set a password for root, or you can use adduser to add a new user. As soon as you’re done with that, go ahead and put the -b flag back. You’ll boot to the familiar login console and you log in with the credentials you set.
-
-All of this applies for any distribution you would want to run in the container, but you need to create the system using the correct package manager. For Fedora, you would use DNF instead of debootstrap. To set up a minimal Fedora system, you can run the following command, replacing the absolute path with wherever you want the container to be.
-
-```
-$ sudo dnf --releasever=24 --installroot=/absolute/path/ install systemd passwd dnf fedora-release
-```
-
-![](https://cdn.fedoramagazine.org/wp-content/uploads/2016/06/Screenshot-from-2016-06-17-15-04-14.png)
-
-### Setting up the network
-
-You’ll notice an issue if you attempt to start a service that binds to a port currently in use on your host system. Your container is using the same network interface. Luckily, `systemd-nspawn` provides several ways to achieve separate networking from the host machine.
-
-#### Local networking
-
-The first method uses the `--private-network` flag, which only creates a loopback device by default. This is ideal for environments where you don’t need networking, such as build systems and other continuous integration systems.
-
-#### Multiple networking interfaces
-
-If you have multiple network devices, you can give one to the container with the `--network-interface` flag. To give `eno1` to my container, I would add the flag `--network-interface=eno1`. While an interface is assigned to a container, the host can’t use it at the same time. When the container is completely shut down, it will be available to the host again.
-
-#### Sharing network interfaces
-
-For those of us who don’t have spare network devices, there are other options for providing access to the container. One of those is the `--port` flag. This forwards a port on the container to the host. The format is `protocol:host:container`, where protocol is either `tcp` or `udp`, `host` is a valid port number on the host, and `container` is a valid port on the container. You can omit the protocol and specify only `host:container`. I often use something similar to `--port=2222:22`.
-
-You can enable complete, host-only networking with the `--network-veth` flag, which creates a virtual Ethernet interface between the host and the container. You can also bridge two connections with `--network-bridge`.
-
-### Using systemd components
-
-If the system in your container has D-Bus, you can use systemd’s provided utilities to control and monitor your container. Debian doesn’t include dbus in the base install. If you want to use it with Debian Jessie, you’ll want to run `apt install dbus`.
-
-#### machinectl
-
-To easily manage containers, systemd provides the machinectl utility. Using machinectl, you can log in to a container with machinectl login name, check the status with machinectl status name, reboot with machinectl reboot name, or power it off with machinectl poweroff name.
-
-### Other systemd commands
-
-Most systemd commands, such as journalctl, systemd-analyze, and systemctl, support containers with the `--machine` option. For example, if you want to see the journals of a container named “foobar”, you can use journalctl `--machine=foobar`. You can also see the status of a service running in this container with `systemctl --machine=foobar` status service.
-
-![](https://cdn.fedoramagazine.org/wp-content/uploads/2016/06/Screenshot-from-2016-06-17-15-09-25.png)
-
-### Working with SELinux
-
-If you’re running with SELinux enforcing (the default in Fedora), you’ll need to set the SELinux context for your container. To do that, you need to run the following two commands on the host system.
-
-```
-$ semanage fcontext -a -t svirt_sandbox_file_t "/path/to/container(/.*)?"
-$ restorecon -R /path/to/container/
-```
-
-Make sure you replace “/path/to/container” with the path to your container. For my container, “DebianJessie”, I would run the following:
-
-```
-$ semanage fcontext -a -t svirt_sandbox_file_t "/home/johnmh/DebianJessie(/.*)?"
-$ restorecon -R /home/johnmh/DebianJessie/
-```
-
---------------------------------------------------------------------------------
-
-via: http://linoxide.com/linux-how-to/set-nginx-reverse-proxy-centos-7-cpanel/
-
-作者:[John M. Harris, Jr.][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://linoxide.com/linux-how-to/set-nginx-reverse-proxy-centos-7-cpanel/
-[1]: https://en.wikipedia.org/wiki/Virtual_machine
diff --git a/sources/tech/20160621 Flatpak brings standalone apps to Linux.md b/sources/tech/20160621 Flatpak brings standalone apps to Linux.md
deleted file mode 100644
index c2c1b51e7b..0000000000
--- a/sources/tech/20160621 Flatpak brings standalone apps to Linux.md	
+++ /dev/null
@@ -1,35 +0,0 @@
-翻译中:by zky001
-Flatpak brings standalone apps to Linux
-===
-
-![](https://cdn.fedoramagazine.org/wp-content/uploads/2016/06/flatpak-945x400.jpg)
-
-The development team behind [Flatpak][1] has [just announced the general availability][2] of the Flatpak desktop application framework. Flatpak (which was also known during development as xdg-app) provides the ability for an application — bundled as a Flatpak — to be installed and run easily and consistently on many different Linux distributions. Applications bundled as Flatpaks also have the ability to be sandboxed for security, isolating them from your operating system, and other applications. Check out the [Flatpak website][3], and the [press release][4] for more information on the tech that makes up the Flatpak framework.
-
-###  Installing Flatpak on Fedora
-
-For users wanting to run applications bundled as Flatpaks, installation on Fedora is easy, with Flatpak already available in the official Fedora 23 and Fedora 24 repositories. The Flatpak website has [full details on installation on Fedora][5], as well as how to install on Arch, Debian, Mageia, and Ubuntu. [Many applications][6] have builds already bundled with Flatpak — including LibreOffice, and nightly builds of popular graphics applications Inkscape and GIMP.
-
-###  For Application Developers
-
-If you are an application developer, the Flatpak website also contains some great resources on getting started [bundling and distributing your applications with Flatpak][7]. These resources contain information on using Flakpak SDKs to build standalone, sandboxed Flatpak applications.
-
-
---------------------------------------------------------------------------------
-
-via: https://fedoramagazine.org/introducing-flatpak/
-
-作者:[Ryan Lerch][a]
-译者:[zky001](https://github.com/zky001)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: https://fedoramagazine.org/introducing-flatpak/
-[1]: http://flatpak.org/
-[2]: http://flatpak.org/press/2016-06-21-flatpak-released.html
-[3]: http://flatpak.org/
-[4]: http://flatpak.org/press/2016-06-21-flatpak-released.html
-[5]: http://flatpak.org/getting.html
-[6]: http://flatpak.org/apps.html
-[7]: http://flatpak.org/developer.html
diff --git a/sources/tech/20160623 72% Of The People I Follow On Twitter Are Men.md b/sources/tech/20160623 72% Of The People I Follow On Twitter Are Men.md
deleted file mode 100644
index 71a23511e9..0000000000
--- a/sources/tech/20160623 72% Of The People I Follow On Twitter Are Men.md	
+++ /dev/null
@@ -1,88 +0,0 @@
-Translating by Flowsnow!
-72% Of The People I Follow On Twitter Are Men
-===============================================
-
-![](https://emptysqua.re/blog/gender-of-twitter-users-i-follow/abacus.jpg)
-
-At least, that's my estimate. Twitter does not ask users their gender, so I [have written a program that guesses][1] based on their names. Among those who follow me, the distribution is even worse: 83% are men. None are gender-nonbinary as far as I can tell.
-
-The way to fix the first number is not mysterious: I should notice and seek more women experts tweeting about my interests, and follow them.
-
-The second number, on the other hand, I can merely influence, but I intend to improve it as well. My network on Twitter should represent of the software industry's diverse future, not its unfair present.
-
-### How Did I Measure It?
-
-I set out to estimate the gender distribution of the people I follow—my "friends" in Twitter's jargon—and found it surprisingly hard. [Twitter analytics][2] readily shows me the converse, an estimate of my followers' gender:
-
-![](https://emptysqua.re/blog/gender-of-twitter-users-i-follow/twitter-analytics.png)
-
-So, Twitter analytics divides my followers' accounts among male, female, and unknown, and tells me the ratio of the first two groups. (Gender-nonbinary folk are absent here—they're lumped in with the Twitter accounts of organizations, and those whose gender is simply unknown.) But Twitter doesn't tell me the ratio of my friends. That [which is measured improves][3], so I searched for a service that would measure this number for me, and found [FollowerWonk][4].
-
-FollowerWonk guesses my friends are 71% men. Is this a good guess? For the sake of validation, I compare FollowerWonk's estimate of my followers to Twitter's estimate:
-
-**Twitter analytics**
-
- 	          |men	    |women
-:--         |:--      |:-- 	          
-Followers	  |83%	    |17%
-
-**FollowerWonk**
-
- 	                |men	    |women
-:--               |:--      |:-- 	                
-Followers	        |81%	    |19%
-Friends I follow	|72%	    |28%
-
-My followers show up 81% male here, close to the Twitter analytics number. So far so good. If FollowerWonk and Twitter agree on the gender ratio of my followers, that suggests FollowerWonk's estimate of the people I follow (which Twitter doesn't analyze) is reasonably good. With it, I can make a habit of measuring my numbers, and improve them.
-
-At $30 a month, however, checking my friends' gender distribution with FollowerWonk is a pricey habit. I don't need all its features anyhow. Can I solve only the gender-distribution problem economically?
-
-Since FollowerWonk's numbers seem reasonable, I tried to reproduce them. Using Python and [some nice Philadelphians' Twitter][5] API wrapper, I began downloading the profiles of all my friends and followers. I immediately found that Twitter's rate limits are miserly, so I randomly sampled only a subset of users instead.
-
-I wrote a rudimentary program that searches for a pronoun announcement in each of my friends' profiles. For example, a profile description that includes "she/her" probably belongs to a woman, a description with "they/them" is probably nonbinary. But most don't state their pronouns: for these, the best gender-correlated information is the "name" field: for example, @gvanrossum's name field is "Guido van Rossum", and the first name "Guido" suggests that @gvanrossum is male. Where pronouns were not announced, I decided to use first names to estimate my numbers.
-
-My script passes parts of each name to the SexMachine library to guess gender. [SexMachine][6] has predictable downfalls, like mistaking "Brooklyn Zen Center" for a woman named "Brooklyn", but its estimates are as good as FollowerWonk's and Twitter's:
-
-
-
- 	                |nonbinary	    |men	    |women	    |no gender,unknown
-:--               |:--            |:--      |:--        |:--
-Friends I follow	|1	            |168	    |66	        |173
- 	                |0%	            |72%	    |28%	      |
-Followers	        |0	            |459	    |108	      |433
- 	                |0%	            |81%	    |19%	      |
-
-(Based on all 408 friends and a sample of 1000 followers.)
-
-### Know Your Number
-
-I want you to check your Twitter network's gender distribution, too. So I've deployed "Proportional" to PythonAnywhere's handy service for $10 a month:
-
-><www.proporti.onl>
-
-The application may rate-limit you or otherwise fail, so use it gently. The [code is on GitHub][7]. It includes a command-line tool, as well.
-
-Who is represented in your network on Twitter? Are you speaking and listening to the same unfairly distributed group who have been talking about software for the last few decades, or does your network look like the software industry of the future? Let's know our numbers and improve them.
-
-
-
-
-
---------------------------------------------------------------------------------
-
-via: https://emptysqua.re/blog/gender-of-twitter-users-i-follow/
-
-作者:[A. Jesse Jiryu Davis][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: https://disqus.com/by/AJesseJiryuDavis/
-[1]: https://www.proporti.onl/
-[2]: https://analytics.twitter.com/
-[3]: http://english.stackexchange.com/questions/14952/that-which-is-measured-improves
-[4]: https://moz.com/followerwonk/
-[5]: https://github.com/bear/python-twitter/graphs/contributors
-[6]: https://pypi.python.org/pypi/SexMachine/
-[7]: https://github.com/ajdavis/twitter-gender-distribution
diff --git a/sources/tech/20160624 DAISY A Linux-compatible text format for the visually impaired.md b/sources/tech/20160624 DAISY A Linux-compatible text format for the visually impaired.md
index 4bf5ccaf42..87c84eb644 100644
--- a/sources/tech/20160624 DAISY A Linux-compatible text format for the visually impaired.md	
+++ b/sources/tech/20160624 DAISY A Linux-compatible text format for the visually impaired.md	
@@ -1,3 +1,4 @@
+Translating by 19761332
 DAISY : A Linux-compatible text format for the visually impaired
 =================================================================
 
diff --git a/sources/tech/20160626 Backup Photos While Traveling With an Ipad Pro and a Raspberry Pi.md b/sources/tech/20160626 Backup Photos While Traveling With an Ipad Pro and a Raspberry Pi.md
deleted file mode 100644
index b7b3e8d5f1..0000000000
--- a/sources/tech/20160626 Backup Photos While Traveling With an Ipad Pro and a Raspberry Pi.md	
+++ /dev/null
@@ -1,407 +0,0 @@
-Backup Photos While Traveling With an Ipad Pro and a Raspberry Pi
-===================================================================
-
-![](http://www.movingelectrons.net/images/bkup_photos_main.jpg)
->Backup Photos While Traveling - Gear.
-
-### Introduction
-
-I’ve been on a quest to finding the ideal travel photo backup solution for a long time. Relying on just tossing your SD cards in your camera bag after they are full during a trip is a risky move that leaves you too exposed: SD cards can be lost or stolen, data can get corrupted or cards can get damaged in transit. Backing up to another medium - even if it’s just another SD card - and leaving that in a safe(r) place while traveling is the best practice. Ideally, backing up to a remote location would be the way to go, but that may not be practical depending on where you are traveling to and Internet availability in the region.
-
-My requirements for the ideal backup procedure are:
-
-1. Use an iPad to manage the process instead of a laptop. I like to travel light and since most of my trips are business related (i.e. non-photography related), I’d hate to bring my personal laptop along with my business laptop. My iPad, however; is always with me, so using it as a tool just makes sense.
-2. Use as few hardware devices as practically possible.
-3. Connection between devices should be secure. I’ll be using this setup in hotels and airports, so closed and encrypted connection between devices is ideal.
-4. The whole process should be sturdy and reliable. I’ve tried other options using router/combo devices and [it didn’t end up well][1].
-
-### The Setup
-
-I came up with a setup that meets the above criteria and is also flexible enough to expand on it in the future. It involves the use of the following gear:
-
-1. [iPad Pro 9.7][2] inches. It’s the most powerful, small and lightweight iOS device at the time of writing. Apple pencil is not really needed, but it’s part of my gear as I so some editing on the iPad Pro while on the road. All the heavy lifting will be done by the Raspberry Pi, so any other device capable of connecting through SSH would fit the bill.
-2. [Raspberry Pi 3][3] with Raspbian installed.
-3. [Micro SD card][4] for Raspberry Pi and a Raspberry Pi [box/case][5].
-5. [128 GB Pen Drive][6]. You can go bigger, but 128 GB is enough for my user case. You can also get a portable external hard drive like [this one][7], but the Raspberry Pi may not provide enough power through its USB port, which means you would have to get a [powered USB hub][8], along with the needed cables, defeating the purpose of having a lightweight and minimalistic setup.
-6. [SD card reader][9]
-7. [SD Cards][10]. I use several as I don’t wait for one to fill up before using a different one. That allows me to spread photos I take on a single trip amongst several cards.
-
-The following diagram shows how these devices will be interacting with each other.
-
-![](http://www.movingelectrons.net/images/bkup_photos_diag.jpg)
->Backup Photos While Traveling - Process Diagram.
-
-The Raspberry Pi will be configured to act as a secured Hot Spot. It will create its own WPA2-encrypted WiFi network to which the iPad Pro will connect. Although there are many online tutorials to create an Ad Hoc (i.e. computer-to-computer) connection with the Raspberry Pi, which is easier to setup; that connection is not encrypted and it’s relatively easy for other devices near you to connect to it. Therefore, I decided to go with the WiFi option.
-
-The camera’s SD card will be connected to one of the Raspberry Pi’s USB ports through an SD card reader. Additionally, a high capacity Pen Drive (128 GB in my case) will be permanently inserted in one of the USB ports on the Raspberry Pi. I picked the [Sandisk Ultra Fit][11] because of its tiny size. The main idea is to have the Raspberry Pi backup the photos from the SD Card to the Pen Drive with the help of a Python script. The backup process will be incremental, meaning that only changes (i.e. new photos taken) will be added to the backup folder each time the script runs, making the process really fast. This is a huge advantage if you take a lot of photos or if you shoot in RAW format. The iPad will be used to trigger the Python script and to browse the SD Card and Pen Drive as needed.
-
-As an added benefit, if the Raspberry Pi is connected to Internet through a wired connection (i.e. through the Ethernet port), it will be able to share the Internet connection with the devices connected to its WiFi network.
-
-### 1. Raspberry Pi Configuration
-
-This is the part where we roll up our sleeves and get busy as we’ll be using Raspbian’s command-line interface (CLI) . I’ll try to be as descriptive as possible so it’s easy to go through the process.
-
-#### Install and Configure Raspbian
-
-Connect a keyboard, mouse and an LCD monitor to the Raspberry Pi. Insert the Micro SD in the Raspberry Pi’s slot and proceed to install Raspbian per the instructions in the [official site][12].
-
-After the installation is done, go to the CLI (Terminal in Raspbian) and type:
-
-```
-sudo apt-get update
-sudo apt-get upgrade
-```
-
-This will upgrade all software on the machine. I configured the Raspberry Pi to connect to the local network and changed the default password as a safety measure.
-
-By default SSH is enabled on Raspbian, so all sections below can be done from a remote machine. I also configured RSA authentication, but that’s optional. More info about it [here][13].
-
-This is a screenshot of the SSH connection to the Raspberry Pi from [iTerm][14] on Mac:
-
-##### Creating Encrypted (WPA2) Access Point
-
-The installation was made based on [this][15] article, it was optimized for my user case.
-
-##### 1. Install Packages
-
-We need to type the following to install the required packages:
-
-```
-sudo apt-get install hostapd
-sudo apt-get install dnsmasq
-```
-
-hostapd allows to use the built-in WiFi as an access point. dnsmasq is a combined DHCP and DNS server that’s easy to configure.
-
-##### 2. Edit dhcpcd.conf
-
-Connect to the Raspberry Pi through Ethernet. Interface configuration on the Raspbery Pi is handled by dhcpcd, so first we tell it to ignore wlan0 as it will be configured with a static IP address.
-
-Open up the dhcpcd configuration file with sudo nano `/etc/dhcpcd.conf` and add the following line to the bottom of the file:
-
-```
-denyinterfaces wlan0
-```
-
-Note: This must be above any interface lines that may have been added.
-
-##### 3. Edit interfaces
-
-Now we need to configure our static IP. To do this, open up the interface configuration file with sudo nano `/etc/network/interfaces` and edit the wlan0 section so that it looks like this:
-
-```
-allow-hotplug wlan0
-iface wlan0 inet static
-    address 192.168.1.1
-    netmask 255.255.255.0
-    network 192.168.1.0
-    broadcast 192.168.1.255
-#    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
-```
-
-Also, the wlan1 section was edited to be:
-
-```
-#allow-hotplug wlan1
-#iface wlan1 inet manual
-#    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
-```
-
-Important: Restart dhcpcd with sudo service dhcpcd restart and then reload the configuration for wlan0 with `sudo ifdown eth0; sudo ifup wlan0`.
-
-##### 4. Configure Hostapd
-
-Next, we need to configure hostapd. Create a new configuration file with `sudo nano /etc/hostapd/hostapd.conf with the following contents:
-
-```
-interface=wlan0
-
-# Use the nl80211 driver with the brcmfmac driver
-driver=nl80211
-
-# This is the name of the network
-ssid=YOUR_NETWORK_NAME_HERE
-
-# Use the 2.4GHz band
-hw_mode=g
-
-# Use channel 6
-channel=6
-
-# Enable 802.11n
-ieee80211n=1
-
-# Enable QoS Support
-wmm_enabled=1
-
-# Enable 40MHz channels with 20ns guard interval
-ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40]
-
-# Accept all MAC addresses
-macaddr_acl=0
-
-# Use WPA authentication
-auth_algs=1
-
-# Require clients to know the network name
-ignore_broadcast_ssid=0
-
-# Use WPA2
-wpa=2
-
-# Use a pre-shared key
-wpa_key_mgmt=WPA-PSK
-
-# The network passphrase
-wpa_passphrase=YOUR_NEW_WIFI_PASSWORD_HERE
-
-# Use AES, instead of TKIP
-rsn_pairwise=CCMP
-```
-
-Now, we also need to tell hostapd where to look for the config file when it starts up on boot. Open up the default configuration file with `sudo nano /etc/default/hostapd` and find the line `#DAEMON_CONF=""` and replace it with `DAEMON_CONF="/etc/hostapd/hostapd.conf"`.
-
-##### 5. Configure Dnsmasq
-
-The shipped dnsmasq config file contains tons of information on how to use it, but we won’t be using all the options. I’d recommend moving it (rather than deleting it), and creating a new one with
-
-```
-sudo mv /etc/dnsmasq.conf /etc/dnsmasq.conf.orig  
-sudo nano /etc/dnsmasq.conf  
-```
-
-Paste the following into the new file:
-
-```
-interface=wlan0      # Use interface wlan0
-listen-address=192.168.1.1 # Explicitly specify the address to listen on
-bind-interfaces      # Bind to the interface to make sure we aren't sending things elsewhere
-server=8.8.8.8       # Forward DNS requests to Google DNS
-domain-needed        # Don't forward short names
-bogus-priv           # Never forward addresses in the non-routed address spaces.
-dhcp-range=192.168.1.50,192.168.1.100,12h # Assign IP addresses in that range  with a 12 hour lease time
-```
-
-##### 6. Set up IPV4 forwarding
-
-One of the last things that we need to do is to enable packet forwarding. To do this, open up the sysctl.conf file with `sudo nano /etc/sysctl.conf`, and remove the # from the beginning of the line containing `net.ipv4.ip_forward=1`. This will enable it on the next reboot.
-
-We also need to share our Raspberry Pi’s internet connection to our devices connected over WiFi by the configuring a NAT between our wlan0 interface and our eth0 interface. We can do this by writing a script with the following lines.
-
-```
-sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE  
-sudo iptables -A FORWARD -i eth0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT  
-sudo iptables -A FORWARD -i wlan0 -o eth0 -j ACCEPT  
-```
-
-I named the script hotspot-boot.sh and made it executable with:
-
-```
-sudo chmod 755 hotspot-boot.sh
-```
-
-The script should be executed when the Raspberry Pi boots. There are many ways to accomplish this, and this is the way I went with:
-
-1. Put the file in `/home/pi/scripts`.
-2. Edit the rc.local file by typing `sudo nano /etc/rc.local` and place the call to the script before the line that reads exit 0 (more information [here][16]).
-
-This is how the rc.local file looks like after editing it.
-
-```
-#!/bin/sh -e
-#
-# rc.local
-#
-# This script is executed at the end of each multiuser runlevel.
-# Make sure that the script will "exit 0" on success or any other
-# value on error.
-#
-# In order to enable or disable this script just change the execution
-# bits.
-#
-# By default this script does nothing.
-
-# Print the IP address
-_IP=$(hostname -I) || true
-if [ "$_IP" ]; then
-  printf "My IP address is %s\n" "$_IP"
-fi
-
-sudo /home/pi/scripts/hotspot-boot.sh &
-
-exit 0
-
-```
-
-#### Installing Samba and NTFS Compatibility.
-
-We also need to install the following packages to enable the Samba protocol and allow the File Browser App to see the connected devices to the Raspberry Pi as shared folders. Also, ntfs-3g provides NTFS compatibility in case we decide to connect a portable hard drive to the Raspberry Pi.
-
-```
-sudo apt-get install ntfs-3g
-sudo apt-get install samba samba-common-bin
-```
-
-You can follow [this][17] article for details on how to configure Samba.
-
-Important Note: The referenced article also goes through the process of mounting external hard drives on the Raspberry Pi. We won’t be doing that because, at the time of writing, the current version of Raspbian (Jessie) auto-mounts both the SD Card and the Pendrive to `/media/pi/` when the device is turned on. The article also goes over some redundancy features that we won’t be using.
-
-### 2. Python Script
-
-Now that the Raspberry Pi has been configured, we need to work on the script that will actually backup/copy our photos. Note that this script just provides certain degree of automation to the backup process. If you have a basic knowledge of using the Linux/Raspbian CLI, you can just SSH into the Raspberry Pi and copy yourself all photos from one device to the other by creating the needed folders and using either the cp or the rsync command. We’ll be using the rsync method on the script as it’s very reliable and allows for incremental backups.
-
-This process relies on two files: the script itself and the configuration file `backup_photos.conf`. The latter just have a couple of lines indicating where the destination drive (Pendrive) is mounted and what folder has been mounted to. This is what it looks like:
-
-```
-mount folder=/media/pi/
-destination folder=PDRIVE128GB
-```
-
-Important: Do not add any additional spaces between the `=` symbol and the words to both sides of it as the script will break (definitely an opportunity for improvement).
-
-Below is the Python script, which I named `backup_photos.py` and placed in `/home/pi/scripts/`. I included comments in between the lines of code to make it easier to follow.
-
-```
-#!/usr/bin/python3
-
-import os
-import sys
-from sh import rsync
-
-'''
-The script copies an SD Card mounted on /media/pi/ to a folder with the same name 
-created in the destination drive. The destination drive's name is defined in 
-the .conf file.
-
-
-Argument:  label/name of the mounted SD Card.
-'''
-
-CONFIG_FILE = '/home/pi/scripts/backup_photos.conf'
-ORIGIN_DEV = sys.argv[1]
-
-def create_folder(path):
-
-    print ('attempting to create destination folder: ',path)
-    if not os.path.exists(path):
-        try: 
-            os.mkdir(path)
-            print ('Folder created.')
-        except:
-            print ('Folder could not be created. Stopping.')
-            return
-    else:
-        print ('Folder already in path. Using that instead.')
-
-
-
-confFile = open(CONFIG_FILE,'rU') 
-#IMPORTANT: rU Opens the file with Universal Newline Support, 
-#so \n and/or \r is recognized as a new line.
-
-confList = confFile.readlines()
-confFile.close()
-
-
-for line in confList:
-    line = line.strip('\n')
-
-    try:
-        name , value = line.split('=')
-
-        if name == 'mount folder':
-            mountFolder = value
-        elif name == 'destination folder':
-            destDevice = value
-
-
-    except ValueError:
-        print ('Incorrect line format. Passing.')
-        pass
-
-
-destFolder = mountFolder+destDevice+'/'+ORIGIN_DEV
-create_folder(destFolder)
-
-print ('Copying files...')
-
-# Comment out to delete files that are not in the origin:
-# rsync("-av", "--delete", mountFolder+ORIGIN_DEV, destFolder)
-rsync("-av", mountFolder+ORIGIN_DEV+'/', destFolder)
-
-print ('Done.')
-```
-
-### 3. iPad Pro Configuration
-
-Since all the heavy-lifting will be done on the Raspberry Pi and no files will be transferred through the iPad Pro, which was a huge disadvantage in [one of the workflows I tried before][18]; we just need to install [Prompt 2][19] on the iPad to access the Raspeberry Pi through SSH. Once connected, you can either run the Python script or copy the files manually.
-
-![](http://www.movingelectrons.net/images/bkup_photos_ipad&rpi_prompt.jpg)
->SSH Connection to Raspberry Pi From iPad Using Prompt.
-
-Since we installed Samba, we can access USB devices connected to the Raspberry Pi in a more graphical way. You can stream videos, copy and move files between devices. [File Browser][20] is perfect for that.
-
-### 4. Putting it All Together
-
-Let’s suppose that `SD32GB-03` is the label of an SD card connected to one of the USB ports on the Raspberry Pi. Also, let’s suppose that `PDRIVE128GB` is the label of the Pendrive, also connected to the device and defined on the `.conf` file as indicated above. If we wanted to backup the photos on the SD Card, we would need to go through the following steps:
-
-1. Turn on Raspberry Pi so that drives are mounted automatically.
-2. Connect to the WiFi network generated by the Raspberry Pi.
-3. Connect to the Raspberry Pi through SSH using the [Prompt][21] App.
-4. Type the following once you are connected:
-
-```
-python3 backup_photos.py SD32GB-03
-```
-
-The first backup my take some minutes depending on how much of the card is used. That means you need to keep the connection alive to the Raspberry Pi from the iPad. You can get around this by using the [nohup][22] command before running the script.
-
-```
-nohup python3 backup_photos.py SD32GB-03 &
-```
-
-![](http://www.movingelectrons.net/images/bkup_photos_ipad&rpi_finished.png)
->iTerm Screenshot After Running Python Script.
-
-### Further Customization
-
-I installed a VNC server to access Raspbian’s graphical interface from another computer or the iPad through [Remoter App][23]. I’m looking into installing [BitTorrent Sync][24] for backing up photos to a remote location while on the road, which would be the ideal setup. I’ll expand this post once I have a workable solution.
-
-Feel free to either include your comments/questions below or reach out to me. My contact info is at the footer of this page.
-
-
---------------------------------------------------------------------------------
-
-via: http://www.movingelectrons.net/blog/2016/06/26/backup-photos-while-traveling-with-a-raspberry-pi.html
-
-作者:[Editor][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.movingelectrons.net/blog/2016/06/26/backup-photos-while-traveling-with-a-raspberry-pi.html
-[1]: http://bit.ly/1MVVtZi
-[2]: http://www.amazon.com/dp/B01D3NZIMA/?tag=movinelect0e-20
-[3]: http://www.amazon.com/dp/B01CD5VC92/?tag=movinelect0e-20
-[4]: http://www.amazon.com/dp/B010Q57T02/?tag=movinelect0e-20
-[5]: http://www.amazon.com/dp/B01F1PSFY6/?tag=movinelect0e-20
-[6]: http://amzn.to/293kPqX
-[7]: http://amzn.to/290syFY
-[8]: http://amzn.to/290syFY
-[9]: http://amzn.to/290syFY
-[10]: http://amzn.to/290syFY
-[11]: http://amzn.to/293kPqX
-[12]: https://www.raspberrypi.org/downloads/noobs/
-[13]: https://www.raspberrypi.org/documentation/remote-access/ssh/passwordless.md
-[14]: https://www.iterm2.com/
-[15]: https://frillip.com/using-your-raspberry-pi-3-as-a-wifi-access-point-with-hostapd/
-[16]: https://www.raspberrypi.org/documentation/linux/usage/rc-local.md
-[17]: http://www.howtogeek.com/139433/how-to-turn-a-raspberry-pi-into-a-low-power-network-storage-device/
-[18]: http://bit.ly/1MVVtZi
-[19]: https://itunes.apple.com/us/app/prompt-2/id917437289?mt=8&uo=4&at=11lqkH
-[20]: https://itunes.apple.com/us/app/filebrowser-access-files-on/id364738545?mt=8&uo=4&at=11lqkH
-[21]: https://itunes.apple.com/us/app/prompt-2/id917437289?mt=8&uo=4&at=11lqkH
-[22]: https://en.m.wikipedia.org/wiki/Nohup
-[23]: https://itunes.apple.com/us/app/remoter-pro-vnc-ssh-rdp/id519768191?mt=8&uo=4&at=11lqkH
-[24]: https://getsync.com/
diff --git a/sources/tech/20160627 TOP 5 BEST VIDEO EDITING SOFTWARE FOR LINUX IN 2016.md b/sources/tech/20160627 TOP 5 BEST VIDEO EDITING SOFTWARE FOR LINUX IN 2016.md
new file mode 100644
index 0000000000..e0f69aca84
--- /dev/null
+++ b/sources/tech/20160627 TOP 5 BEST VIDEO EDITING SOFTWARE FOR LINUX IN 2016.md	
@@ -0,0 +1,151 @@
+vim-kakali translating
+
+
+TOP 5 BEST VIDEO EDITING SOFTWARE FOR LINUX IN 2016
+=====================================================
+
+![](https://itsfoss.com/wp-content/uploads/2016/06/linux-video-ditor-software.jpg)
+
+Brief: Tiwo discusses the best video editors for Linux, their pros and cons and the installation method for Ubuntu-based distros in this article.
+
+We have discussed [best photo management applications for Linux][1], [best code editors for Linux][2] in similar articles in the past. Today we shall see the best video editing software for Linux.
+
+When asked about free video editing software, Windows Movie Maker and iMovie is what most people often suggest.
+
+Unfortunately, both of them are not available for GNU/Linux. But you don’t need to worry about it, we have pooled together a list of best free video editors for you.
+
+### BEST VIDEO EDITOR APPS FOR LINUX
+
+Let’s have a look at the top 5 best free video editing software for Linux below :
+
+#### 1. KDENLIVE
+
+![](https://itsfoss.com/wp-content/uploads/2016/06/kdenlive-free-video-editor-on-ubuntu.jpg)
+
+[Kdenlive][3] is a free and [open source][4] video editing software from KDE that provides dual video monitors, a multi-track timeline, clip list, customizable layout support, basic effects, and basic transitions.
+It supports wide variety of file formats and a wide range of camcorders and cameras including Low resolution camcorder (Raw and AVI DV editing), Mpeg2, mpeg4 and h264 AVCHD (small cameras and camcorders), High resolution camcorder files, including HDV and AVCHD camcorders, Professional camcorders, including XDCAM-HD™ streams, IMX™ (D10) streams, DVCAM (D10) , DVCAM, DVCPRO™, DVCPRO50™ streams and DNxHD™ streams.
+
+You can install it from terminal by running the following command :
+
+```
+sudo apt-get install kdenlive
+```
+
+Or, open Ubuntu Software Center then search Kdenlive.
+
+#### 2. OPENSHOT
+
+![](https://itsfoss.com/wp-content/uploads/2016/06/openshot-free-video-editor-on-ubuntu.jpg)
+
+[OpenShot][5] is the second choice in our list of Linux video editing software. OpenShot can help you create the film that supports for transitions, effects, adjusting audio levels, and of course, it support of most formats and codecs.
+
+You can also export your film to DVD, upload to YouTube, Vimeo, Xbox 360, and many other common formats. OpenShot is simpler than kdenlive. So if you need a video editor with a simple UI OpenShot is a good choice.
+
+The latest version is 2.0.7. You can install OpenShot video editor by run the following command from terminal window :
+
+```
+sudo apt-get install openshot
+```
+
+It needs to download 25 MB, and 70 MB disk space after installed.
+
+#### 3. FLOWBLADE MOVIE EDITOR
+
+![](https://itsfoss.com/wp-content/uploads/2016/06/flowblade-movie-editor-on-ubuntu.jpg)
+
+[Flowblade Movie Editor][6] is a multitrack non-linear video editor for Linux. It is free and open source. It comes with a stylish and modern user interface.
+
+Written in Python, it is designed to provide a fast, and precise. Flowblade has focused on providing the best possible experience on Linux and other free platforms. So there’s no Windows and OS X version for now.
+
+To install Flowblade in Ubuntu and other Ubuntu based systems, use the command below:
+
+```
+sudo apt-get install flowblade
+```
+
+#### 4. LIGHTWORKS
+
+![](https://itsfoss.com/wp-content/uploads/2016/06/lightworks-running-on-ubuntu-16.04.jpg)
+
+If you looking for a video editor software that has more feature, this is the answer. [Lightworks][7] is a cross-platform professional video editor, available for Linux, Mac OS X and Windows.
+
+It is an award winning professional [non-linear editing][8] (NLE) software that supports resolutions up to 4K as well as video in SD and HD formats.
+
+This application has two versions: Lightworks Free and Lightworks Pro. While free version doesn’t support Vimeo (H.264 / MPEG-4) and YouTube (H.264 / MPEG-4)- Up to 2160p (4K UHD), Blu-ray, and H.264/MP4 export option with configurable bitrate setting, then pro version is.
+
+- Lightworks Free
+- Lightworks Pro
+
+Pro version has more features such as higher resolution support, 4K and Blue Ray support etc.
+
+##### HOW TO INSTALL LIGHTWORKS?
+
+Unlike the other video editors, installing Lightwork is not as straight forward as running a single command. Don’t worry, it’s not that complicated either.
+
+- Step 1 –  You can get the package from [Lightworks Downloads Page][9]. The package’s size about 79,5 MB.
+
+>Please note: There’s no Linux 32-bit support.
+
+- Step 2 – Once downloaded, you can install it using [Gdebi package installer][10]. Gdebi automatically downloads the dependency :
+
+![](https://itsfoss.com/wp-content/uploads/2016/06/Installing-lightworks-on-ubuntu.jpg)
+
+- Step 3 – Now you can open it from Ubuntu dashboard, or your Linux distro’s menu.
+
+- Step 4 – It needs an account when you use it for first time. Click at Not Registerd? button to register. Don’t worry, it’s free!
+
+- Step 5 – After your account has been verified, now login.
+
+Now the Lightworks is ready to use.
+
+Need Lightworks video tutorial? Get them at [Lightworks video tutorials Page][11].
+
+#### 5. BLENDER
+
+![](https://itsfoss.com/wp-content/uploads/2016/06/blender-running-on-ubuntu-16.04.jpg)
+
+Blender is a professional, industry-grade open source, cross platform video editor. It is popular for 3D works. Blender has been used in several Hollywood movies including Spider Man series.
+
+Although originally designed for produce 3D modeling, but it can also be used for video editing and input capabilities with a variety of formats. The Video Editor includes:
+
+- Live preview, luma waveform, chroma vectorscope and histogram displays
+- Audio mixing, syncing, scrubbing and waveform visualization
+- Up to 32 slots for adding video, images, audio, scenes, masks and effects
+- Speed control, adjustment layers, transitions, keyframes, filters and more.
+
+The latest version can be downloaded from [Blender Download Page][12].
+
+### WHICH IS THE BEST VIDEO EDITING SOFTWARE?
+
+If you need a simple video editor, OpenShot, Kdenlive or Flowblade is a good choice. These are suitable for beginners and a system with standard specification.
+
+Then if you have a high-end computer, and need advanced features you can go out with Lightworks. If you are looking for more advanced features, Blender has got your back.
+
+So that’s all I can write about 5 best video editing software for Linux such as Ubuntu, Linux Mint, Elementary, and other Linux distributions. Share with us which video editor you like the most.
+
+--------------------------------------------------------------------------------
+
+via: https://itsfoss.com/best-video-editing-software-linux/
+
+作者:[Tiwo Satriatama][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://itsfoss.com/author/tiwo/
+[1]: https://itsfoss.com/linux-photo-management-software/
+[2]: https://itsfoss.com/best-modern-open-source-code-editors-for-linux/
+[3]: https://kdenlive.org/
+[4]: https://itsfoss.com/tag/open-source/
+[5]: http://www.openshot.org/
+[6]: http://jliljebl.github.io/flowblade/
+[7]: https://www.lwks.com/
+[8]: https://en.wikipedia.org/wiki/Non-linear_editing_system
+[9]: https://www.lwks.com/index.php?option=com_lwks&view=download&Itemid=206
+[10]: https://itsfoss.com/gdebi-default-ubuntu-software-center/
+[11]: https://www.lwks.com/videotutorials
+[12]: https://www.blender.org/download/
+
+
+
diff --git a/sources/tech/20160628 How To Setup Open Source Discussion Platform Discourse On Ubuntu Linux 16.04.md b/sources/tech/20160628 How To Setup Open Source Discussion Platform Discourse On Ubuntu Linux 16.04.md
deleted file mode 100644
index a2521673c9..0000000000
--- a/sources/tech/20160628 How To Setup Open Source Discussion Platform Discourse On Ubuntu Linux 16.04.md	
+++ /dev/null
@@ -1,101 +0,0 @@
-How To Setup Open Source Discussion Platform Discourse On Ubuntu Linux 16.04
-===============================================================================
-
-Discourse is an open source discussion platform, that can work as a mailing list, a chat room and a forum as well. It is a popular tool and modern day implementation of a successful discussion platform. On server side, it is built using Ruby on Rails and uses Postgres on the backend, it also makes use of Redis caching to reduce the loading times, while on client’s side end, it runs in browser using Java Script. It is a pretty well optimized and well structured tool. It also offers converter plugins to migrate your existing discussion boards / forums like  vBulletin, phpBB, Drupal, SMF etc to Discourse. In this article, we will be learning how to install Discourse on Ubuntu operating system.
-
-It is developed by keeping security in mind, so spammers and hackers might not be lucky with this application. It works well with all modern devices, and adjusts its display setting accordingly for mobile devices and tablets.
-
-### Installing Discourse on Ubuntu 16.04
-
-Let’s get started ! the minimum system RAM to run Discourse is 1 GB and the officially supported installation process for Discourse requires dockers to be installed on our Linux system. Besides dockers, it also requires Git. We can fulfill these two requirements by simply running the following command on our system’s terminal.
-
-```
-wget -qO- https://get.docker.com/ | sh
-```
-
-![](http://linuxpitstop.com/wp-content/uploads/2016/06/124.png)
-
-It shouldn’t take longer to complete the installation for Docker and Git, as soon its installation process is complete, create a directory for Discourse inside /var partition of your system (You can choose any other partition here too).
-
-```
-mkdir /var/discourse 
-```
-
-Now clone the Discourse’s Github repository to this newly created directory.
-
-```
-git clone https://github.com/discourse/discourse_docker.git /var/discourse
-```
-
-Go into the cloned directory.
-
-```
-cd /var/discourse
-```
-
-![](http://linuxpitstop.com/wp-content/uploads/2016/06/314.png)
-
-You should be able to locate “discourse-setup” script file here, simply run this script to initiate the installation wizard for Discourse.
-
-```
-./discourse-setup
-```
-
-**Side note: Please make sure  you have a ready email server setup before attempting install for discourse.**
-
-Installation wizard will ask you following six questions.
-
-```
-Hostname for your Discourse? 
-Email address for admin account? 
-SMTP server address? 
-SMTP user name? 
-SMTP port [587]:
-SMTP password? []:
-```
-
-![](http://linuxpitstop.com/wp-content/uploads/2016/06/411.png)
-
-Once you supply these information, it will ask for the confirmation, if everything is fine, hit “Enter” and installation process will take off.
-
-![](http://linuxpitstop.com/wp-content/uploads/2016/06/511.png)
-
-Sit back and relax! it will take sweet amount of time to complete the installation,  grab a cup of coffee, and keep an eye for any error messages.
-
-![](http://linuxpitstop.com/wp-content/uploads/2016/06/610.png)
-
-Here is how the successful completion of the installation process should look alike.
-
-![](http://linuxpitstop.com/wp-content/uploads/2016/06/710.png)
-
-Now launch your web browser, if the hostname for discourse installation resolves properly to IP, then you can use your hostname in browser , otherwise use your IP address to launch the Discourse page. Here is what you should see:
-
-![](http://linuxpitstop.com/wp-content/uploads/2016/06/85.png)
-
-That’s it, create new account by using “Sign Up” option and you should be good to go with your Discourse setup.
-
-![](http://linuxpitstop.com/wp-content/uploads/2016/06/106.png)
-
-### Conclusion
-
-It is an easy to setup application and works flawlessly. It is equipped with all required features of modern day discussion board. It is available under General Public License and is 100% open source product. The simplicity, easy of use, powerful and long feature list are the most important feathers of this tool. Hope you enjoyed this article, Question? do let us know in comments please.
-
---------------------------------------------------------------------------------
-
-via: http://linuxpitstop.com/install-discourse-on-ubuntu-linux-16-04/
-
-作者:[Aun][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://linuxpitstop.com/author/aun/
-
-
-
-
-
-
-
-
diff --git a/sources/tech/20160630 What makes up the Fedora kernel.md b/sources/tech/20160630 What makes up the Fedora kernel.md
deleted file mode 100644
index 95b61a201a..0000000000
--- a/sources/tech/20160630 What makes up the Fedora kernel.md	
+++ /dev/null
@@ -1,33 +0,0 @@
-What makes up the Fedora kernel?
-====================================
-
-![](https://cdn.fedoramagazine.org/wp-content/uploads/2016/06/kernel-945x400.png)
-
-Every Fedora system runs a kernel. Many pieces of code come together to make this a reality.
-
-Each release of the Fedora kernel starts with a baseline release from the [upstream community][1]. This is often called a ‘vanilla’ kernel. The upstream kernel is the standard. The goal is to have as much code upstream as possible. This makes it easier for bug fixes and API updates to happen as well as having more people review the code. In an ideal world, Fedora would be able to to take the kernel straight from kernel.org and send that out to all users.
-
-Realistically, using the vanilla kernel isn’t complete enough for Fedora. Some features Fedora users want may not be available. The [Fedora kernel][2] that users actually receive contains a number of patches on top of the vanilla kernel. These patches are considered ‘out of tree’. Many of these patches will not exist out of tree patches very long. If patches are available to fix an issue, the patches may be pulled in to the Fedora tree so the fix can go out to users faster. When the kernel is rebased to a new version, the patches will be removed if they are in the new version.
-
-Some patches remain in the Fedora kernel tree for an extended period of time. A good example of patches that fall into this category are the secure boot patches. These patches provide a feature Fedora wants to support even though the upstream community has not yet accepted them. It takes effort to keep these patches up to date so Fedora tries to minimize the number of patches that are carried without being accepted by an upstream kernel maintainer.
-
-Generally, the best way to get a patch included in the Fedora kernel is to send it to the ]Linux Kernel Mailing List (LKML)][3] first and then ask for it to be included in Fedora. If a patch has been accepted by a maintainer it stands a very high chance of being included in the Fedora kernel tree. Patches that come from places like github which have not been submitted to LKML are unlikely to be taken into the tree. It’s important to send the patches to LKML first to ensure Fedora is carrying the correct patches in its tree. Without the community review, Fedora could end up carrying patches which are buggy and cause problems.
-
-The Fedora kernel contains code from many places. All of it is necessary to give the best experience possible.
-
-
-
---------------------------------------------------------------------------------
-
-via: https://fedoramagazine.org/makes-fedora-kernel/
-
-作者:[Laura Abbott][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: https://fedoramagazine.org/makes-fedora-kernel/
-[1]: http://www.kernel.org/
-[2]: http://pkgs.fedoraproject.org/cgit/rpms/kernel.git/
-[3]: http://www.labbott.name/blog/2015/10/02/the-art-of-communicating-with-lkml/
diff --git a/sources/tech/20160705 Building a data science portfolio - Machine learning project.md b/sources/tech/20160705 Building a data science portfolio - Machine learning project.md
new file mode 100644
index 0000000000..ca57b2e83c
--- /dev/null
+++ b/sources/tech/20160705 Building a data science portfolio - Machine learning project.md	
@@ -0,0 +1,845 @@
+Building a data science portfolio: Machine learning project
+===========================================================
+
+>This is the third in a series of posts on how to build a Data Science Portfolio. If you like this and want to know when the next post in the series is released, you can [subscribe at the bottom of the page][1].
+
+Data science companies are increasingly looking at portfolios when making hiring decisions. One of the reasons for this is that a portfolio is the best way to judge someone’s real-world skills. The good news for you is that a portfolio is entirely within your control. If you put some work in, you can make a great portfolio that companies are impressed by.
+
+The first step in making a high-quality portfolio is to know what skills to demonstrate. The primary skills that companies want in data scientists, and thus the primary skills they want a portfolio to demonstrate, are:
+
+- Ability to communicate
+- Ability to collaborate with others
+- Technical competence
+- Ability to reason about data
+- Motivation and ability to take initiative
+
+Any good portfolio will be composed of multiple projects, each of which may demonstrate 1-2 of the above points. This is the third post in a series that will cover how to make a well-rounded data science portfolio. In this post, we’ll cover how to make the second project in your portfolio, and how to build an end to end machine learning project. At the end, you’ll have a project that shows your ability to reason about data, and your technical competence. [Here’s][2] the completed project if you want to take a look.
+
+### An end to end project
+
+As a data scientist, there are times when you’ll be asked to take a dataset and figure out how to [tell a story with it][3]. In times like this, it’s important to communicate very well, and walk through your process. Tools like Jupyter notebook, which we used in a previous post, are very good at helping you do this. The expectation here is that the deliverable is a presentation or document summarizing your findings.
+
+However, there are other times when you’ll be asked to create a project that has operational value. A project with operational value directly impacts the day-to-day operations of a company, and will be used more than once, and often by multiple people. A task like this might be “create an algorithm to forecast our churn rate”, or “create a model that can automatically tag our articles”. In cases like this, storytelling is less important than technical competence. You need to be able to take a dataset, understand it, then create a set of scripts that can process that data. It’s often important that these scripts run quickly, and use minimal system resources like memory. It’s very common that these scripts will be run several times, so the deliverable becomes the scripts themselves, not a presentation. The deliverable is often integrated into operational flows, and may even be user-facing.
+
+The main components of building an end to end project are:
+
+- Understanding the context
+- Exploring the data and figuring out the nuances
+- Creating a well-structured project, so its easy to integrate into operational flows
+- Writing high-performance code that runs quickly and uses minimal system resources
+- Documenting the installation and usage of your code well, so others can use it
+
+In order to effectively create a project of this kind, we’ll need to work with multiple files. Using a text editor like [Atom][4], or an IDE like [PyCharm][5] is highly recommended. These tools will allow you to jump between files, and edit files of different types, like markdown files, Python files, and csv files. Structuring your project so it’s easy to version control and upload to collaborative coding tools like [Github][6] is also useful.
+
+![](https://www.dataquest.io/blog/images/end_to_end/github.png)
+>This project on Github.
+
+We’ll use our editing tools along with libraries like [Pandas][7] and [scikit-learn][8] in this post. We’ll make extensive use of Pandas [DataFrames][9], which make it easy to read in and work with tabular data in Python.
+
+### Finding good datasets
+
+A good dataset for an end to end portfolio project can be hard to find. [The dataset][10] needs to be sufficiently large that memory and performance constraints come into play. It also needs to potentially be operationally useful. For instance, this dataset, which contains data on the admission criteria, graduation rates, and graduate future earnings for US colleges would be a great dataset to use to tell a story. However, as you think about the dataset, it becomes clear that there isn’t enough nuance to build a good end to end project with it. For example, you could tell someone their potential future earnings if they went to a specific college, but that would be a quick lookup without enough nuance to demonstrate technical competence. You could also figure out if colleges with higher admissions standards tend to have graduates who earn more, but that would be more storytelling than operational.
+
+These memory and performance constraints tend to come into play when you have more than a gigabyte of data, and when you have some nuance to what you want to predict, which involves running algorithms over the dataset.
+
+A good operational dataset enables you to build a set of scripts that transform the data, and answer dynamic questions. A good example would be a dataset of stock prices. You would be able to predict the prices for the next day, and keep feeding new data to the algorithm as the markets closed. This would enable you to make trades, and potentially even profit. This wouldn’t be telling a story – it would be adding direct value.
+
+Some good places to find datasets like this are:
+
+- [/r/datasets][11] – a subreddit that has hundreds of interesting datasets.
+- [Google Public Datasets][12] – public datasets available through Google BigQuery.
+- [Awesome datasets][13] – a list of datasets, hosted on Github.
+
+As you look through these datasets, think about what questions someone might want answered with the dataset, and think if those questions are one-time (“how did housing prices correlate with the S&P 500?”), or ongoing (“can you predict the stock market?”). The key here is to find questions that are ongoing, and require the same code to be run multiple times with different inputs (different data).
+
+For the purposes of this post, we’ll look at [Fannie Mae Loan Data][14]. Fannie Mae is a government sponsored enterprise in the US that buys mortgage loans from other lenders. It then bundles these loans up into mortgage-backed securities and resells them. This enables lenders to make more mortgage loans, and creates more liquidity in the market. This theoretically leads to more homeownership, and better loan terms. From a borrowers perspective, things stay largely the same, though.
+
+Fannie Mae releases two types of data – data on loans it acquires, and data on how those loans perform over time. In the ideal case, someone borrows money from a lender, then repays the loan until the balance is zero. However, some borrowers miss multiple payments, which can cause foreclosure. Foreclosure is when the house is seized by the bank because mortgage payments cannot be made. Fannie Mae tracks which loans have missed payments on them, and which loans needed to be foreclosed on. This data is published quarterly, and lags the current date by 1 year. As of this writing, the most recent dataset that’s available is from the first quarter of 2015.
+
+Acquisition data, which is published when the loan is acquired by Fannie Mae, contains information on the borrower, including credit score, and information on their loan and home. Performance data, which is published every quarter after the loan is acquired, contains information on the payments being made by the borrower, and the foreclosure status, if any. A loan that is acquired may have dozens of rows in the performance data. A good way to think of this is that the acquisition data tells you that Fannie Mae now controls the loan, and the performance data contains a series of status updates on the loan. One of the status updates may tell us that the loan was foreclosed on during a certain quarter.
+
+![](https://www.dataquest.io/blog/images/end_to_end/foreclosure.jpg)
+>A foreclosed home being sold.
+
+### Picking an angle
+
+There are a few directions we could go in with the Fannie Mae dataset. We could:
+
+- Try to predict the sale price of a house after it’s foreclosed on.
+- Predict the payment history of a borrower.
+- Figure out a score for each loan at acquisition time.
+
+The important thing is to stick to a single angle. Trying to focus on too many things at once will make it hard to make an effective project. It’s also important to pick an angle that has sufficient nuance. Here are examples of angles without much nuance:
+
+- Figuring out which banks sold loans to Fannie Mae that were foreclosed on the most.
+- Figuring out trends in borrower credit scores.
+- Exploring which types of homes are foreclosed on most often.
+- Exploring the relationship between loan amounts and foreclosure sale prices
+
+All of the above angles are interesting, and would be great if we were focused on storytelling, but aren’t great fits for an operational project.
+
+With the Fannie Mae dataset, we’ll try to predict whether a loan will be foreclosed on in the future by only using information that was available when the loan was acquired. In effect, we’ll create a “score” for any mortgage that will tell us if Fannie Mae should buy it or not. This will give us a nice foundation to build on, and will be a great portfolio piece.
+
+### Understanding the data
+
+Let’s take a quick look at the raw data files. Here are the first few rows of the acquisition data from quarter 1 of 2012:
+
+```
+100000853384|R|OTHER|4.625|280000|360|02/2012|04/2012|31|31|1|23|801|N|C|SF|1|I|CA|945||FRM|
+100003735682|R|SUNTRUST MORTGAGE INC.|3.99|466000|360|01/2012|03/2012|80|80|2|30|794|N|P|SF|1|P|MD|208||FRM|788
+100006367485|C|PHH MORTGAGE CORPORATION|4|229000|360|02/2012|04/2012|67|67|2|36|802|N|R|SF|1|P|CA|959||FRM|794
+```
+
+Here are the first few rows of the performance data from quarter 1 of 2012:
+
+```
+100000853384|03/01/2012|OTHER|4.625||0|360|359|03/2042|41860|0|N||||||||||||||||
+100000853384|04/01/2012||4.625||1|359|358|03/2042|41860|0|N||||||||||||||||
+100000853384|05/01/2012||4.625||2|358|357|03/2042|41860|0|N||||||||||||||||
+```
+
+Before proceeding too far into coding, it’s useful to take some time and really understand the data. This is more critical in operational projects – because we aren’t interactively exploring the data, it can be harder to spot certain nuances unless we find them upfront. In this case, the first step is to read the materials on the Fannie Mae site:
+
+- [Overview][15]
+- [Glossary of useful terms][16]
+- [FAQs][17]
+- [Columns in the Acquisition and Performance files][18]
+- [Sample Acquisition data file][19]
+- [Sample Performance data file][20]
+
+After reading through these files, we know some key facts that will help us:
+
+- There’s an Acquisition file and a Performance file for each quarter, starting from the year 2000 to present. There’s a 1 year lag in the data, so the most recent data is from 2015 as of this writing.
+- The files are in text format, with a pipe (|) as a delimiter.
+- The files don’t have headers, but we have a list of what each column is.
+- All together, the files contain data on 22 million loans.
+- Because the Performance files contain information on loans acquired in previous years, there will be more performance data for loans acquired in earlier years (ie loans acquired in 2014 won’t have much performance history).
+
+These small bits of information will save us a ton of time as we figure out how to structure our project and work with the data.
+
+### Structuring the project
+
+Before we start downloading and exploring the data, it’s important to think about how we’ll structure the project. When building an end-to-end project, our primary goals are:
+
+- Creating a solution that works
+- Having a solution that runs quickly and uses minimal resources
+- Enabling others to easily extend our work
+- Making it easy for others to understand our code
+- Writing as little code as possible
+
+In order to achieve these goals, we’ll need to structure our project well. A well structured project follows a few principles:
+
+- Separates data files and code files.
+- Separates raw data from generated data.
+- Has a README.md file that walks people through installing and using the project.
+- Has a requirements.txt file that contains all the packages needed to run the project.
+- Has a single settings.py file that contains any settings that are used in other files.
+    - For example, if you are reading the same file from multiple Python scripts, it’s useful to have them all import settings and get the file name from a centralized place.
+- Has a .gitignore file that prevents large or secret files from being committed.
+- Breaks each step in our task into a separate file that can be executed separately.
+    - For example, we may have one file for reading in the data, one for creating features, and one for making predictions.
+- Stores intermediate values. For example, one script may output a file that the next script can read.
+    - This enables us to make changes in our data processing flow without recalculating everything.
+
+Our file structure will look something like this shortly:
+
+```
+loan-prediction
+├── data
+├── processed
+├── .gitignore
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
+### Creating the initial files
+
+To start with, we’ll need to create a loan-prediction folder. Inside that folder, we’ll need to make a data folder and a processed folder. The first will store our raw data, and the second will store any intermediate calculated values.
+
+Next, we’ll make a .gitignore file. A .gitignore file will make sure certain files are ignored by git and not pushed to Github. One good example of such a file is the .DS_Store file created by OSX in every folder. A good starting point for a .gitignore file is here. We’ll also want to ignore the data files because they are very large, and the Fannie Mae terms prevent us from redistributing them, so we should add two lines to the end of our file:
+
+```
+data
+processed
+```
+
+[Here’s][21] an example .gitignore file for this project.
+
+Next, we’ll need to create README.md, which will help people understand the project.  .md indicates that the file is in markdown format. Markdown enables you write plain text, but also add some fancy formatting if you want. [Here’s][22] a guide on markdown. If you upload a file called README.md to Github, Github will automatically process the markdown, and show it to anyone who views the project. [Here’s][23] an example.
+
+For now, we just need to put a simple description in README.md:
+
+```
+Loan Prediction
+-----------------------
+
+Predict whether or not loans acquired by Fannie Mae will go into foreclosure.  Fannie Mae acquires loans from other lenders as a way of inducing them to lend more.  Fannie Mae releases data on the loans it has acquired and their performance afterwards [here](http://www.fanniemae.com/portal/funding-the-market/data/loan-performance-data.html).
+```
+
+Now, we can create a requirements.txt file. This will make it easy for other people to install our project. We don’t know exactly what libraries we’ll be using yet, but here’s a good starting point:
+
+```
+pandas
+matplotlib
+scikit-learn
+numpy
+ipython
+scipy
+```
+
+The above libraries are the most commonly used for data analysis tasks in Python, and its fair to assume that we’ll be using most of them. [Here’s][24] an example requirements file for this project.
+
+After creating requirements.txt, you should install the packages. For this post, we’ll be using Python 3. If you don’t have Python installed, you should look into using [Anaconda][25], a Python installer that also installs all the packages listed above.
+
+Finally, we can just make a blank settings.py file, since we don’t have any settings for our project yet.
+
+### Acquiring the data
+
+Once we have the skeleton of our project, we can get the raw data.
+
+Fannie Mae has some restrictions around acquiring the data, so you’ll need to sign up for an account. You can find the download page [here][26]. After creating an account, you’ll be able to download as few or as many loan data files as you want. The files are in zip format, and are reasonably large after decompression.
+
+For the purposes of this blog post, we’ll download everything from Q1 2012 to Q1 2015, inclusive. We’ll then need to unzip all of the files. After unzipping the files, remove the original .zip files. At the end, the loan-prediction folder should look something like this:
+
+```
+loan-prediction
+├── data
+│   ├── Acquisition_2012Q1.txt
+│   ├── Acquisition_2012Q2.txt
+│   ├── Performance_2012Q1.txt
+│   ├── Performance_2012Q2.txt
+│   └── ...
+├── processed
+├── .gitignore
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
+After downloading the data, you can use the head and tail shell commands to look at the lines in the files. Do you see any columns that aren’t needed? It might be useful to consult the [pdf of column names][27] while doing this.
+
+### Reading in the data
+
+There are two issues that make our data hard to work with right now:
+
+- The acquisition and performance datasets are segmented across multiple files.
+- Each file is missing headers.
+
+Before we can get started on working with the data, we’ll need to get to the point where we have one file for the acquisition data, and one file for the performance data. Each of the files will need to contain only the columns we care about, and have the proper headers. One wrinkle here is that the performance data is quite large, so we should try to trim some of the columns if we can.
+
+The first step is to add some variables to settings.py, which will contain the paths to our raw data and our processed data. We’ll also add a few other settings that will be useful later on:
+
+```
+DATA_DIR = "data"
+PROCESSED_DIR = "processed"
+MINIMUM_TRACKING_QUARTERS = 4
+TARGET = "foreclosure_status"
+NON_PREDICTORS = [TARGET, "id"]
+CV_FOLDS = 3
+```
+
+Putting the paths in settings.py will put them in a centralized place and make them easy to change down the line. When referring to the same variables in multiple files, it’s easier to put them in a central place than edit them in every file when you want to change them. [Here’s][28] an example settings.py file for this project.
+
+The second step is to create a file called assemble.py that will assemble all the pieces into 2 files. When we run python assemble.py, we’ll get 2 data files in the processed directory.
+
+We’ll then start writing code in assemble.py. We’ll first need to define the headers for each file, so we’ll need to look at [pdf of column names][29] and create lists of the columns in each Acquisition and Performance file:
+
+```
+HEADERS = {
+    "Acquisition": [
+        "id",
+        "channel",
+        "seller",
+        "interest_rate",
+        "balance",
+        "loan_term",
+        "origination_date",
+        "first_payment_date",
+        "ltv",
+        "cltv",
+        "borrower_count",
+        "dti",
+        "borrower_credit_score",
+        "first_time_homebuyer",
+        "loan_purpose",
+        "property_type",
+        "unit_count",
+        "occupancy_status",
+        "property_state",
+        "zip",
+        "insurance_percentage",
+        "product_type",
+        "co_borrower_credit_score"
+    ],
+    "Performance": [
+        "id",
+        "reporting_period",
+        "servicer_name",
+        "interest_rate",
+        "balance",
+        "loan_age",
+        "months_to_maturity",
+        "maturity_date",
+        "msa",
+        "delinquency_status",
+        "modification_flag",
+        "zero_balance_code",
+        "zero_balance_date",
+        "last_paid_installment_date",
+        "foreclosure_date",
+        "disposition_date",
+        "foreclosure_costs",
+        "property_repair_costs",
+        "recovery_costs",
+        "misc_costs",
+        "tax_costs",
+        "sale_proceeds",
+        "credit_enhancement_proceeds",
+        "repurchase_proceeds",
+        "other_foreclosure_proceeds",
+        "non_interest_bearing_balance",
+        "principal_forgiveness_balance"
+    ]
+}
+```
+
+The next step is to define the columns we want to keep. Since all we’re measuring on an ongoing basis about the loan is whether or not it was ever foreclosed on, we can discard many of the columns in the performance data. We’ll need to keep all the columns in the acquisition data, though, because we want to maximize the information we have about when the loan was acquired (after all, we’re predicting if the loan will ever be foreclosed or not at the point it’s acquired). Discarding columns will enable us to save disk space and memory, while also speeding up our code.
+
+```
+SELECT = {
+    "Acquisition": HEADERS["Acquisition"],
+    "Performance": [
+        "id",
+        "foreclosure_date"
+    ]
+}
+```
+
+Next, we’ll write a function to concatenate the data sets. The below code will:
+
+- Import a few needed libraries, including settings.
+- Define a function concatenate, that:
+    - Gets the names of all the files in the data directory.
+    - Loops through each file.
+        - If the file isn’t the right type (doesn’t start with the prefix we want), we ignore it.
+        - Reads the file into a [DataFrame][30] with the right settings using the Pandas [read_csv][31] function.
+            - Sets the separator to | so the fields are read in correctly.
+            - The data has no header row, so sets header to None to indicate this.
+            - Sets names to the right value from the HEADERS dictionary – these will be the column names of our DataFrame.
+            - Picks only the columns from the DataFrame that we added in SELECT.
+- Concatenates all the DataFrames together.
+- Writes the concatenated DataFrame back to a file.
+
+```
+import os
+import settings
+import pandas as pd
+
+def concatenate(prefix="Acquisition"):
+    files = os.listdir(settings.DATA_DIR)
+    full = []
+    for f in files:
+        if not f.startswith(prefix):
+            continue
+
+        data = pd.read_csv(os.path.join(settings.DATA_DIR, f), sep="|", header=None, names=HEADERS[prefix], index_col=False)
+        data = data[SELECT[prefix]]
+        full.append(data)
+
+    full = pd.concat(full, axis=0)
+
+    full.to_csv(os.path.join(settings.PROCESSED_DIR, "{}.txt".format(prefix)), sep="|", header=SELECT[prefix], index=False)
+```
+
+We can call the above function twice with the arguments Acquisition and Performance to concatenate all the acquisition and performance files together. The below code will:
+
+- Only execute if the script is called from the command line with python assemble.py.
+- Concatenate all the files, and result in two files:
+    - `processed/Acquisition.txt`
+    - `processed/Performance.txt`
+
+```
+if __name__ == "__main__":
+    concatenate("Acquisition")
+    concatenate("Performance")
+```
+
+We now have a nice, compartmentalized assemble.py that’s easy to execute, and easy to build off of. By decomposing the problem into pieces like this, we make it easy to build our project. Instead of one messy script that does everything, we define the data that will pass between the scripts, and make them completely separate from each other. When you’re working on larger projects, it’s a good idea to do this, because it makes it much easier to change individual pieces without having unexpected consequences on unrelated pieces of the project.
+
+Once we finish the assemble.py script, we can run python assemble.py. You can find the complete assemble.py file [here][32].
+
+This will result in two files in the processed directory:
+
+```
+loan-prediction
+├── data
+│   ├── Acquisition_2012Q1.txt
+│   ├── Acquisition_2012Q2.txt
+│   ├── Performance_2012Q1.txt
+│   ├── Performance_2012Q2.txt
+│   └── ...
+├── processed
+│   ├── Acquisition.txt
+│   ├── Performance.txt
+├── .gitignore
+├── assemble.py
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
+### Computing values from the performance data
+
+The next step we’ll take is to calculate some values from processed/Performance.txt. All we want to do is to predict whether or not a property is foreclosed on. To figure this out, we just need to check if the performance data associated with a loan ever has a foreclosure_date. If foreclosure_date is None, then the property was never foreclosed on. In order to avoid including loans with little performance history in our sample, we’ll also want to count up how many rows exist in the performance file for each loan. This will let us filter loans without much performance history from our training data.
+
+One way to think of the loan data and the performance data is like this:
+
+![](https://github.com/LCTT/wiki-images/blob/master/TranslateProject/ref_img/001.png)
+
+As you can see above, each row in the Acquisition data can be related to multiple rows in the Performance data. In the Performance data, foreclosure_date will appear in the quarter when the foreclosure happened, so it should be blank prior to that. Some loans are never foreclosed on, so all the rows related to them in the Performance data have foreclosure_date blank.
+
+We need to compute foreclosure_status, which is a Boolean that indicates whether a particular loan id was ever foreclosed on, and performance_count, which is the number of rows in the performance data for each loan id.
+
+There are a few different ways to compute the counts we want:
+
+- We could read in all the performance data, then use the Pandas groupby method on the DataFrame to figure out the number of rows associated with each loan id, and also if the foreclosure_date is ever not None for the id.
+    - The upside of this method is that it’s easy to implement from a syntax perspective.
+    - The downside is that reading in all 129236094 lines in the data will take a lot of memory, and be extremely slow.
+- We could read in all the performance data, then use apply on the acquisition DataFrame to find the counts for each id.
+    - The upside is that it’s easy to conceptualize.
+    - The downside is that reading in all 129236094 lines in the data will take a lot of memory, and be extremely slow.
+- We could iterate over each row in the performance dataset, and keep a separate dictionary of counts.
+    - The upside is that the dataset doesn’t need to be loaded into memory, so it’s extremely fast and memory-efficient.
+    - The downside is that it will take slightly longer to conceptualize and implement, and we need to parse the rows manually.
+
+Loading in all the data will take quite a bit of memory, so let’s go with the third option above. All we need to do is to iterate through all the rows in the Performance data, while keeping a dictionary of counts per loan id. In the dictionary, we’ll keep track of how many times the id appears in the performance data, as well as if foreclosure_date is ever not None. This will give us foreclosure_status and performance_count.
+
+We’ll create a new file called annotate.py, and add in code that will enable us to compute these values. In the below code, we’ll:
+
+- Import needed libraries.
+- Define a function called count_performance_rows.
+    - Open processed/Performance.txt. This doesn’t read the file into memory, but instead opens a file handler that can be used to read in the file line by line.
+    - Loop through each line in the file.
+    - Split the line on the delimiter (|)
+    - Check if the loan_id is not in the counts dictionary.
+        - If not, add it to counts.
+    - Increment performance_count for the given loan_id because we’re on a row that contains it.
+    - If date is not None, then we know that the loan was foreclosed on, so set foreclosure_status appropriately.
+
+```
+import os
+import settings
+import pandas as pd
+
+def count_performance_rows():
+    counts = {}
+    with open(os.path.join(settings.PROCESSED_DIR, "Performance.txt"), 'r') as f:
+        for i, line in enumerate(f):
+            if i == 0:
+                # Skip header row
+                continue
+            loan_id, date = line.split("|")
+            loan_id = int(loan_id)
+            if loan_id not in counts:
+                counts[loan_id] = {
+                    "foreclosure_status": False,
+                    "performance_count": 0
+                }
+            counts[loan_id]["performance_count"] += 1
+            if len(date.strip()) > 0:
+                counts[loan_id]["foreclosure_status"] = True
+    return counts
+```
+
+### Getting the values
+
+Once we create our counts dictionary, we can make a function that will extract values from the dictionary if a loan_id and a key are passed in:
+
+```
+def get_performance_summary_value(loan_id, key, counts):
+    value = counts.get(loan_id, {
+        "foreclosure_status": False,
+        "performance_count": 0
+    })
+    return value[key]
+```
+
+The above function will return the appropriate value from the counts dictionary, and will enable us to assign a foreclosure_status value and a performance_count value to each row in the Acquisition data. The [get][33] method on dictionaries returns a default value if a key isn’t found, so this enables us to return sensible default values if a key isn’t found in the counts dictionary.
+
+### Annotating the data
+
+We’ve already added a few functions to annotate.py, but now we can get into the meat of the file. We’ll need to convert the acquisition data into a training dataset that can be used in a machine learning algorithm. This involves a few things:
+
+- Converting all columns to numeric.
+- Filling in any missing values.
+- Assigning a performance_count and a foreclosure_status to each row.
+- Removing any rows that don’t have a lot of performance history (where performance_count is low).
+
+Several of our columns are strings, which aren’t useful to a machine learning algorithm. However, they are actually categorical variables, where there are a few different category codes, like R, S, and so on. We can convert these columns to numeric by assigning a number to each category label:
+
+![](https://github.com/LCTT/wiki-images/blob/master/TranslateProject/ref_img/002.png)
+
+Converting the columns this way will allow us to use them in our machine learning algorithm.
+
+Some of the columns also contain dates (first_payment_date and origination_date). We can split these dates into 2 columns each:
+
+![](https://github.com/LCTT/wiki-images/blob/master/TranslateProject/ref_img/003.png)
+In the below code, we’ll transform the Acquisition data. We’ll define a function that:
+
+- Creates a foreclosure_status column in acquisition by getting the values from the counts dictionary.
+- Creates a performance_count column in acquisition by getting the values from the counts dictionary.
+- Converts each of the following columns from a string column to an integer column:
+    - channel
+    - seller
+    - first_time_homebuyer
+    - loan_purpose
+    - property_type
+    - occupancy_status
+    - property_state
+    - product_type
+- Converts first_payment_date and origination_date to 2 columns each:
+    - Splits the column on the forward slash.
+    - Assigns the first part of the split list to a month column.
+    - Assigns the second part of the split list to a year column.
+    - Deletes the column.
+    - At the end, we’ll have first_payment_month, first_payment_year, origination_month, and origination_year.
+- Fills any missing values in acquisition with -1.
+
+```
+def annotate(acquisition, counts):
+    acquisition["foreclosure_status"] = acquisition["id"].apply(lambda x: get_performance_summary_value(x, "foreclosure_status", counts))
+    acquisition["performance_count"] = acquisition["id"].apply(lambda x: get_performance_summary_value(x, "performance_count", counts))
+    for column in [
+        "channel",
+        "seller",
+        "first_time_homebuyer",
+        "loan_purpose",
+        "property_type",
+        "occupancy_status",
+        "property_state",
+        "product_type"
+    ]:
+        acquisition[column] = acquisition[column].astype('category').cat.codes
+
+    for start in ["first_payment", "origination"]:
+        column = "{}_date".format(start)
+        acquisition["{}_year".format(start)] = pd.to_numeric(acquisition[column].str.split('/').str.get(1))
+        acquisition["{}_month".format(start)] = pd.to_numeric(acquisition[column].str.split('/').str.get(0))
+        del acquisition[column]
+
+    acquisition = acquisition.fillna(-1)
+    acquisition = acquisition[acquisition["performance_count"] > settings.MINIMUM_TRACKING_QUARTERS]
+    return acquisition
+```
+
+### Pulling everything together
+
+We’re almost ready to pull everything together, we just need to add a bit more code to annotate.py. In the below code, we:
+
+- Define a function to read in the acquisition data.
+- Define a function to write the processed data to processed/train.csv
+- If this file is called from the command line, like python annotate.py:
+    - Read in the acquisition data.
+    - Compute the counts for the performance data, and assign them to counts.
+    - Annotate the acquisition DataFrame.
+    - Write the acquisition DataFrame to train.csv.
+
+```
+def read():
+    acquisition = pd.read_csv(os.path.join(settings.PROCESSED_DIR, "Acquisition.txt"), sep="|")
+    return acquisition
+    
+def write(acquisition):
+    acquisition.to_csv(os.path.join(settings.PROCESSED_DIR, "train.csv"), index=False)
+
+if __name__ == "__main__":
+    acquisition = read()
+    counts = count_performance_rows()
+    acquisition = annotate(acquisition, counts)
+    write(acquisition)
+```
+
+Once you’re done updating the file, make sure to run it with python annotate.py, to generate the train.csv file. You can find the complete annotate.py file [here][34].
+
+The folder should now look like this:
+
+```
+loan-prediction
+├── data
+│   ├── Acquisition_2012Q1.txt
+│   ├── Acquisition_2012Q2.txt
+│   ├── Performance_2012Q1.txt
+│   ├── Performance_2012Q2.txt
+│   └── ...
+├── processed
+│   ├── Acquisition.txt
+│   ├── Performance.txt
+│   ├── train.csv
+├── .gitignore
+├── annotate.py
+├── assemble.py
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
+### Finding an error metric
+
+We’re done with generating our training dataset, and now we’ll just need to do the final step, generating predictions. We’ll need to figure out an error metric, as well as how we want to evaluate our data. In this case, there are many more loans that aren’t foreclosed on than are, so typical accuracy measures don’t make much sense.
+
+If we read in the training data, and check the counts in the foreclosure_status column, here’s what we get:
+
+```
+import pandas as pd
+import settings
+
+train = pd.read_csv(os.path.join(settings.PROCESSED_DIR, "train.csv"))
+train["foreclosure_status"].value_counts()
+```
+
+```
+False    4635982
+True        1585
+Name: foreclosure_status, dtype: int64
+```
+
+Since so few of the loans were foreclosed on, just checking the percentage of labels that were correctly predicted will mean that we can make a machine learning model that predicts False for every row, and still gets a very high accuracy. Instead, we’ll want to use a metric that takes the class imbalance into account, and ensures that we predict foreclosures accurately. We don’t want too many false positives, where we make predict that a loan will be foreclosed on even though it won’t, or too many false negatives, where we predict that a loan won’t be foreclosed on, but it is. Of these two, false negatives are more costly for Fannie Mae, because they’re buying loans where they may not be able to recoup their investment.
+
+We’ll define false negative rate as the number of loans where the model predicts no foreclosure but the the loan was actually foreclosed on, divided by the number of total loans that were actually foreclosed on. This is the percentage of actual foreclosures that the model “Missed”. Here’s a diagram:
+
+![](https://github.com/LCTT/wiki-images/blob/master/TranslateProject/ref_img/004.png)
+
+In the diagram above, 1 loan was predicted as not being foreclosed on, but it actually was. If we divide this by the number of loans that were actually foreclosed on, 2, we get the false negative rate, 50%. We’ll use this as our error metric, so we can evaluate our model’s performance.
+
+### Setting up the classifier for machine learning
+
+We’ll use cross validation to make predictions. With cross validation, we’ll divide our data into 3 groups. Then we’ll do the following:
+
+- Train a model on groups 1 and 2, and use the model to make predictions for group 3.
+- Train a model on groups 1 and 3, and use the model to make predictions for group 2.
+- Train a model on groups 2 and 3, and use the model to make predictions for group 1.
+
+Splitting it up into groups this way means that we never train a model using the same data we’re making predictions for. This avoids overfitting. If we overfit, we’ll get a falsely low false negative rate, which makes it hard to improve our algorithm or use it in the real world.
+
+[Scikit-learn][35] has a function called [cross_val_predict][36] which will make it easy to perform cross validation.
+
+We’ll also need to pick an algorithm to use to make predictions. We need a classifier that can do [binary classification][37]. The target variable, foreclosure_status only has two values, True and False.
+
+We’ll use [logistic regression][38], because it works well for binary classification, runs extremely quickly, and uses little memory. This is due to how the algorithm works – instead of constructing dozens of trees, like a random forest, or doing expensive transformations, like a support vector machine, logistic regression has far fewer steps involving fewer matrix operations.
+
+We can use the [logistic regression classifier][39] algorithm that’s implemented in scikit-learn. The only thing we need to pay attention to is the weights of each class. If we weight the classes equally, the algorithm will predict False for every row, because it is trying to minimize errors. However, we care much more about foreclosures than we do about loans that aren’t foreclosed on. Thus, we’ll pass balanced to the class_weight keyword argument of the [LogisticRegression][40] class, to get the algorithm to weight the foreclosures more to account for the difference in the counts of each class. This will ensure that the algorithm doesn’t predict False for every row, and instead is penalized equally for making errors in predicting either class.
+
+### Making predictions
+
+Now that we have the preliminaries out of the way, we’re ready to make predictions. We’ll create a new file called predict.py that will use the train.csv file we created in the last step. The below code will:
+
+- Import needed libraries.
+- Create a function called cross_validate that:
+    - Creates a logistic regression classifier with the right keyword arguments.
+    - Creates a list of columns that we want to use to train the model, removing id and foreclosure_status.
+    - Run cross validation across the train DataFrame.
+    - Return the predictions.
+
+
+```
+import os
+import settings
+import pandas as pd
+from sklearn import cross_validation
+from sklearn.linear_model import LogisticRegression
+from sklearn import metrics
+
+def cross_validate(train):
+    clf = LogisticRegression(random_state=1, class_weight="balanced")
+
+    predictors = train.columns.tolist()
+    predictors = [p for p in predictors if p not in settings.NON_PREDICTORS]
+
+    predictions = cross_validation.cross_val_predict(clf, train[predictors], train[settings.TARGET], cv=settings.CV_FOLDS)
+    return predictions
+```
+
+### Predicting error
+
+Now, we just need to write a few functions to compute error. The below code will:
+
+- Create a function called compute_error that:
+    - Uses scikit-learn to compute a simple accuracy score (the percentage of predictions that matched the actual foreclosure_status values).
+- Create a function called compute_false_negatives that:
+    - Combines the target and the predictions into a DataFrame for convenience.
+    - Finds the false negative rate.
+- Create a function called compute_false_positives that:
+    - Combines the target and the predictions into a DataFrame for convenience.
+    - Finds the false positive rate.
+        - Finds the number of loans that weren’t foreclosed on that the model predicted would be foreclosed on.
+        - Divide by the total number of loans that weren’t foreclosed on.
+
+```
+def compute_error(target, predictions):
+    return metrics.accuracy_score(target, predictions)
+
+def compute_false_negatives(target, predictions):
+    df = pd.DataFrame({"target": target, "predictions": predictions})
+    return df[(df["target"] == 1) & (df["predictions"] == 0)].shape[0] / (df[(df["target"] == 1)].shape[0] + 1)
+
+def compute_false_positives(target, predictions):
+    df = pd.DataFrame({"target": target, "predictions": predictions})
+    return df[(df["target"] == 0) & (df["predictions"] == 1)].shape[0] / (df[(df["target"] == 0)].shape[0] + 1)
+```
+
+### Putting it all together
+
+Now, we just have to put the functions together in predict.py. The below code will:
+
+- Read in the dataset.
+- Compute cross validated predictions.
+- Compute the 3 error metrics above.
+- Print the error metrics.
+
+```
+def read():
+    train = pd.read_csv(os.path.join(settings.PROCESSED_DIR, "train.csv"))
+    return train
+    
+if __name__ == "__main__":
+    train = read()
+    predictions = cross_validate(train)
+    error = compute_error(train[settings.TARGET], predictions)
+    fn = compute_false_negatives(train[settings.TARGET], predictions)
+    fp = compute_false_positives(train[settings.TARGET], predictions)
+    print("Accuracy Score: {}".format(error))
+    print("False Negatives: {}".format(fn))
+    print("False Positives: {}".format(fp))
+```
+
+Once you’ve added the code, you can run python predict.py to generate predictions. Running everything shows that our false negative rate is .26, which means that of the foreclosed loans, we missed predicting 26% of them. This is a good start, but can use a lot of improvement!
+
+You can find the complete predict.py file [here][41].
+
+Your file tree should now look like this:
+
+```
+loan-prediction
+├── data
+│   ├── Acquisition_2012Q1.txt
+│   ├── Acquisition_2012Q2.txt
+│   ├── Performance_2012Q1.txt
+│   ├── Performance_2012Q2.txt
+│   └── ...
+├── processed
+│   ├── Acquisition.txt
+│   ├── Performance.txt
+│   ├── train.csv
+├── .gitignore
+├── annotate.py
+├── assemble.py
+├── predict.py
+├── README.md
+├── requirements.txt
+├── settings.py
+```
+
+### Writing up a README
+
+Now that we’ve finished our end to end project, we just have to write up a README.md file so that other people know what we did, and how to replicate it. A typical README.md for a project should include these sections:
+
+- A high level overview of the project, and what the goals are.
+- Where to download any needed data or materials.
+- Installation instructions.
+    - How to install the requirements.
+- Usage instructions.
+    - How to run the project.
+    - What you should see after each step.
+- How to contribute to the project.
+    - Good next steps for extending the project.
+
+[Here’s][42] a sample README.md for this project.
+
+### Next steps
+
+Congratulations, you’re done making an end to end machine learning project! You can find a complete example project [here][43]. It’s a good idea to upload your project to [Github][44] once you’ve finished it, so others can see it as part of your portfolio.
+
+There are still quite a few angles left to explore with this data. Broadly, we can split them up into 3 categories – extending this project and making it more accurate, finding other columns to predict, and exploring the data. Here are some ideas:
+
+- Generate more features in annotate.py.
+- Switch algorithms in predict.py.
+- Try using more data from Fannie Mae than we used in this post.
+- Add in a way to make predictions on future data. The code we wrote will still work if we add more data, so we can add more past or future data.
+- Try seeing if you can predict if a bank should have issued the loan originally (vs if Fannie Mae should have acquired the loan).
+    - Remove any columns from train that the bank wouldn’t have known at the time of issuing the loan.
+        - Some columns are known when Fannie Mae bought the loan, but not before.
+    - Make predictions.
+- Explore seeing if you can predict columns other than foreclosure_status.
+    - Can you predict how much the property will be worth at sale time?
+- Explore the nuances between performance updates.
+    - Can you predict how many times the borrower will be late on payments?
+    - Can you map out the typical loan lifecycle?
+- Map out data on a state by state or zip code by zip code level.
+    - Do you see any interesting patterns?
+
+If you build anything interesting, please let us know in the comments!
+
+If you liked this, you might like to read the other posts in our ‘Build a Data Science Porfolio’ series:
+
+- [Storytelling with data][45].
+- [How to setup up a data science blog][46].
+
+
+--------------------------------------------------------------------------------
+
+via: https://www.dataquest.io/blog/data-science-portfolio-machine-learning/
+
+作者:[Vik Paruchuri][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对ID](https://github.com/校对ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.dataquest.io/blog
+[1]: https://www.dataquest.io/blog/data-science-portfolio-machine-learning/#email-signup
+[2]: https://github.com/dataquestio/loan-prediction
+[3]: https://www.dataquest.io/blog/data-science-portfolio-project/
+[4]: https://atom.io/
+[5]: https://www.jetbrains.com/pycharm/
+[6]: https://github.com/
+[7]: http://pandas.pydata.org/
+[8]: http://scikit-learn.org/
+[9]: http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html
+[10]: https://collegescorecard.ed.gov/data/
+[11]: https://reddit.com/r/datasets
+[12]: https://cloud.google.com/bigquery/public-data/#usa-names
+[13]: https://github.com/caesar0301/awesome-public-datasets
+[14]: http://www.fanniemae.com/portal/funding-the-market/data/loan-performance-data.html
+[15]: http://www.fanniemae.com/portal/funding-the-market/data/loan-performance-data.html
+[16]: https://loanperformancedata.fanniemae.com/lppub-docs/lppub_glossary.pdf
+[17]: https://loanperformancedata.fanniemae.com/lppub-docs/lppub_faq.pdf
+[18]: https://loanperformancedata.fanniemae.com/lppub-docs/lppub_file_layout.pdf
+[19]: https://loanperformancedata.fanniemae.com/lppub-docs/acquisition-sample-file.txt
+[20]: https://loanperformancedata.fanniemae.com/lppub-docs/performance-sample-file.txt
+[21]: https://github.com/dataquestio/loan-prediction/blob/master/.gitignore
+[22]: https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet
+[23]: https://github.com/dataquestio/loan-prediction
+[24]: https://github.com/dataquestio/loan-prediction/blob/master/requirements.txt
+[25]: https://www.continuum.io/downloads
+[26]: https://loanperformancedata.fanniemae.com/lppub/index.html
+[27]: https://loanperformancedata.fanniemae.com/lppub-docs/lppub_file_layout.pdf
+[28]: https://github.com/dataquestio/loan-prediction/blob/master/settings.py
+[29]: https://loanperformancedata.fanniemae.com/lppub-docs/lppub_file_layout.pdf
+[30]: http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html
+[31]: http://pandas.pydata.org/pandas-docs/stable/generated/pandas.read_csv.html
+[32]: https://github.com/dataquestio/loan-prediction/blob/master/assemble.py
+[33]: https://docs.python.org/3/library/stdtypes.html#dict.get
+[34]: https://github.com/dataquestio/loan-prediction/blob/master/annotate.py
+[35]: http://scikit-learn.org/
+[36]: http://scikit-learn.org/stable/modules/generated/sklearn.cross_validation.cross_val_predict.html
+[37]: https://en.wikipedia.org/wiki/Binary_classification
+[38]: https://en.wikipedia.org/wiki/Logistic_regression
+[39]: http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html
+[40]: http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html
+[41]: https://github.com/dataquestio/loan-prediction/blob/master/predict.py
+[42]: https://github.com/dataquestio/loan-prediction/blob/master/README.md
+[43]: https://github.com/dataquestio/loan-prediction
+[44]: https://www.github.com/
+[45]: https://www.dataquest.io/blog/data-science-portfolio-project/
+[46]: https://www.dataquest.io/blog/how-to-setup-a-data-science-blog/
diff --git a/sources/tech/20160705 How to Encrypt a Flash Drive Using VeraCrypt.md b/sources/tech/20160705 How to Encrypt a Flash Drive Using VeraCrypt.md
deleted file mode 100644
index 2dd2ae024f..0000000000
--- a/sources/tech/20160705 How to Encrypt a Flash Drive Using VeraCrypt.md	
+++ /dev/null
@@ -1,100 +0,0 @@
-How to Encrypt a Flash Drive Using VeraCrypt
-============================================
-
-Many security experts prefer open source software like VeraCrypt, which can be used to encrypt flash drives, because of its readily available source code.
-
-Encryption is a smart idea for protecting data on a USB flash drive, as we covered in our piece that described ]how to encrypt a flash drive][1] using Microsoft BitLocker.
-
-But what if you do not want to use BitLocker?
-
-You may be concerned that because Microsoft's source code is not available for inspection, it could be susceptible to security "backdoors" used by the government or others. Because source code for open source software is widely shared, many security experts feel open source software is far less likely to have any backdoors.
-
-Fortunately, there are several open source encryption alternatives to BitLocker.
-
-If you need to be able to encrypt and access files on any Windows machine, as well as computers running Apple OS X or Linux, the open source [VeraCrypt][2] offers an excellent alternative.
-
-VeraCrypt is derived from TrueCrypt, a well-regarded open source encryption software product that has now been discontinued. But the code for TrueCrypt was audited and no major security flaws were found. In addition, it has since been improved in VeraCrypt.
-
-Versions exist for Windows, OS X and Linux.
-
-Encrypting a USB flash drive with VeraCrypt is not as straightforward as it is with BitLocker, but it still only takes a few minutes.
-
-### Encrypting Flash Drive with VeraCrypt in 8 Steps
-
-After [downloading VeraCrypt][3] for your operating system:
-
-Start VeraCrypt, and click on Create Volume to start the VeraCrypt Volume Creation Wizard.
-
-![](http://www.esecurityplanet.com/imagesvr_ce/6246/Vera0.jpg)
-
-The VeraCrypt Volume Creation Wizard allows you to create an encrypted file container on the flash drive which sits along with other unencrypted files, or you can choose to encrypt the entire flash drive. For the moment, we will choose to encrypt the entire flash drive.
-
-![](http://www.esecurityplanet.com/imagesvr_ce/6703/Vera1.jpg)
-
-On the next screen, choose Standard VeraCrypt Volume.
-
-![](http://www.esecurityplanet.com/imagesvr_ce/835/Vera2.jpg)
-
-Select the drive letter of the flash drive you want to encrypt (in this case O:).
-
-![](http://www.esecurityplanet.com/imagesvr_ce/9427/Vera3.jpg)
-
-Choose the Volume Creation Mode. If your flash drive is empty or you want to delete everything it contains, choose the first option. If you want to keep any existing files, choose the second option.
-
-![](http://www.esecurityplanet.com/imagesvr_ce/7828/Vera4.jpg)
-
-This screen allows you to choose your encryption options. If you are unsure of which to choose, leave the default settings of AES and SHA-512.
-
-![](http://www.esecurityplanet.com/imagesvr_ce/5918/Vera5.jpg)
-
-After confirming the Volume Size screen, enter and re-enter the password you want to use to encrypt your data.
-
-![](http://www.esecurityplanet.com/imagesvr_ce/3850/Vera6.jpg)
-
-To work effectively, VeraCrypt must draw from a pool of entropy or "randomness." To generate this pool, you'll be asked to move your mouse around in a random fashion for about a minute. Once the bar has turned green, or preferably when it reaches the far right of the screen, click Format to finish creating your encrypted drive.
-
-![](http://www.esecurityplanet.com/imagesvr_ce/7468/Vera8.jpg)
-
-### Using a Flash Drive Encrypted with VeraCrypt
-
-When you want to use an encrypted flash drive, first insert the drive in the computer and start VeraCrypt.
-
-Then select an unused drive letter (such as z:) and click Auto-Mount Devices.
-
-![](http://www.esecurityplanet.com/imagesvr_ce/2016/Vera10.jpg)
-
-Enter your password and click OK.
-
-![](http://www.esecurityplanet.com/imagesvr_ce/8222/Vera11.jpg)
-
-The mounting process may take a few minutes, after which your unencrypted drive will become available with the drive letter you selected previously.
-
-### VeraCrypt Traveler Disk Setup
-
-If you set up a flash drive with an encrypted container rather than encrypting the whole drive, you also have the option to create what VeraCrypt calls a traveler disk. This installs a copy of VeraCrypt on the USB flash drive itself, so when you insert the drive in another Windows computer you can run VeraCrypt automatically from the flash drive; there is no need to install it on the computer.
-
-You can set up a flash drive to be a Traveler Disk by choosing Traveler Disk SetUp from the Tools menu of VeraCrypt.
-
-![](http://www.esecurityplanet.com/imagesvr_ce/5812/Vera12.jpg)
-
-It is worth noting that in order to run VeraCrypt from a Traveler Disk on a computer, you must have administrator privileges on that computer. While that may seem to be a limitation, no confidential files can be opened safely on a computer that you do not control, such as one in a business center.
-
->Paul Rubens has been covering enterprise technology for over 20 years. In that time he has written for leading UK and international publications including The Economist, The Times, Financial Times, the BBC, Computing and ServerWatch.
-
---------------------------------------------------------------------------------
-
-via: http://www.esecurityplanet.com/open-source-security/how-to-encrypt-flash-drive-using-veracrypt.html
-
-作者:[Paul Rubens ][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.esecurityplanet.com/author/3700/Paul-Rubens
-[1]: http://www.esecurityplanet.com/views/article.php/3880616/How-to-Encrypt-a-USB-Flash-Drive.htm
-[2]: http://www.esecurityplanet.com/open-source-security/veracrypt-a-worthy-truecrypt-alternative.html
-[3]: https://veracrypt.codeplex.com/releases/view/619351
-
-
-
diff --git a/sources/tech/20160706 What is Git.md b/sources/tech/20160706 What is Git.md
deleted file mode 100644
index f98de42fd3..0000000000
--- a/sources/tech/20160706 What is Git.md	
+++ /dev/null
@@ -1,123 +0,0 @@
-translating by cvsher
-What is Git
-===========
-
-Welcome to my series on learning how to use the Git version control system! In this introduction to the series, you will learn what Git is for and who should use it.
-
-If you're just starting out in the open source world, you're likely to come across a software project that keeps its code in, and possibly releases it for use, by way of Git. In fact, whether you know it or not, you're certainly using software right now that is developed using Git: the Linux kernel (which drives the website you're on right now, if not the desktop or mobile phone you're accessing it on), Firefox, Chrome, and many more projects share their codebase with the world in a Git repository.
-
-On the other hand, all the excitement and hype over Git tends to make things a little muddy. Can you only use Git to share your code with others, or can you use Git in the privacy of your own home or business? Do you have to have a GitHub account to use Git? Why use Git at all? What are the benefits of Git? Is Git the only option?
-
-So forget what you know or what you think you know about Git, and let's take it from the beginning.
-
-### What is version control?
-
-Git is, first and foremost, a version control system (VCS). There are many version control systems out there: CVS, SVN, Mercurial, Fossil, and, of course, Git.
-
-Git serves as the foundation for many services, like GitHub and GitLab, but you can use Git without using any other service. This means that you can use Git privately or publicly.
-
-If you have ever collaborated on anything digital with anyone, then you know how it goes. It starts out simple: you have your version, and you send it to your partner. They make some changes, so now there are two versions, and send the suggestions back to you. You integrate their changes into your version, and now there is one version again.
-
-Then it gets worse: while you change your version further, your partner makes more changes to their version. Now you have three versions; the merged copy that you both worked on, the version you changed, and the version your partner has changed.
-
-As Jason van Gumster points out in his article, 【Even artists need version control][1], this syndrome tends to happen in individual settings as well. In both art and science, it's not uncommon to develop a trial version of something; a version of your project that might make it a lot better, or that might fail miserably. So you create file names like project_justTesting.kdenlive and project_betterVersion.kdenlive, and then project_best_FINAL.kdenlive, but with the inevitable allowance for project_FINAL-alternateVersion.kdenlive, and so on.
-
-Whether it's a change to a for loop or an editing change, it happens to the best of us. That is where a good version control system makes life easier.
-
-### Git snapshots
-
-Git takes snapshots of a project, and stores those snapshots as unique versions.
-
-If you go off in a direction with your project that you decide was the wrong direction, you can just roll back to the last good version and continue along an alternate path.
-
-If you're collaborating, then when someone sends you changes, you can merge those changes into your working branch, and then your collaborator can grab the merged version of the project and continue working from the new current version.
-
-Git isn't magic, so conflicts do occur ("You changed the last line of the book, but I deleted that line entirely; how do we resolve that?"), but on the whole, Git enables you to manage the many potential variants of a single work, retaining the history of all the changes, and even allows for parallel versions.
-
-### Git distributes
-
-Working on a project on separate machines is complex, because you want to have the latest version of a project while you work, makes your own changes, and share your changes with your collaborators. The default method of doing this tends to be clunky online file sharing services, or old school email attachments, both of which are inefficient and error-prone.
-
-Git is designed for distributed development. If you're involved with a project you can clone the project's Git repository, and then work on it as if it was the only copy in existence. Then, with a few simple commands, you can pull in any changes from other contributors, and you can also push your changes over to someone else. Now there is no confusion about who has what version of a project, or whose changes exist where. It is all locally developed, and pushed and pulled toward a common target (or not, depending on how the project chooses to develop).
-
-### Git interfaces
-
-In its natural state, Git is an application that runs in the Linux terminal. However, as it is well-designed and open source, developers all over the world have designed other ways to access it.
-
-It is free, available to anyone for $0, and comes in packages on Linux, BSD, Illumos, and other Unix-like operating systems. It looks like this:
-
-```
-$ git --version
-git version 2.5.3
-```
-
-Probably the most well-known Git interfaces are web-based: sites like GitHub, the open source GitLab, Savannah, BitBucket, and SourceForge all offer online code hosting to maximise the public and social aspect of open source along with, in varying degrees, browser-based GUIs to minimise the learning curve of using Git. This is what the GitLab interface looks like:
-
-![](https://opensource.com/sites/default/files/0_gitlab.png)
-
-Additionally, it is possible that a Git service or independent developer may even have a custom Git frontend that is not HTML-based, which is particularly handy if you don't live with a browser eternally open. The most transparent integration comes in the form of file manager support. The KDE file manager, Dolphin, can show the Git status of a directory, and even generate commits, pushes, and pulls.
-
-![](https://opensource.com/sites/default/files/0_dolphin.jpg)
-
-[Sparkleshare][2] uses Git as a foundation for its own Dropbox-style file sharing interface.
-
-![](https://opensource.com/sites/default/files/0_sparkleshare_1.jpg)
-
-For more, see the (long) page on the official [Git wiki][3] listing projects with graphical interfaces to Git.
-
-### Who should use Git?
-
-You should! The real question is when? And what for?
-
-### When should I use Git, and what should I use it for?
-
-To get the most out of Git, you need to think a little bit more than usual about file formats.
-
-Git is designed to manage source code, which in most languages consists of lines of text. Of course, Git doesn't know if you're feeding it source code or the next Great American Novel, so as long as it breaks down to text, Git is a great option for managing and tracking versions.
-
-But what is text? If you write something in an office application like Libre Office, then you're probably not generating raw text. There is usually a wrapper around complex applications like that which encapsulate the raw text in XML markup and then in a zip container, as a way to ensure that all of the assets for your office file are available when you send that file to someone else. Strangely, though, something that you might expect to be very complex, like the save files for a [Kdenlive][4] project, or an SVG from [Inkscape][5], are actually raw XML files that can easily be managed by Git.
-
-If you use Unix, you can check to see what a file is made of with the file command:
-
-```
-$ file ~/path/to/my-file.blah
-my-file.blah: ASCII text
-$ file ~/path/to/different-file.kra: Zip data (MIME type "application/x-krita")
-```
-
-If unsure, you can view the contents of a file with the head command:
-
-```
-$ head ~/path/to/my-file.blah
-```
-
-If you see text that is mostly readable by you, then it is probably a file made of text. If you see garbage with some familiar text characters here and there, it is probably not made of text.
-
-Make no mistake: Git can manage other formats of files, but it treats them as blobs. The difference is that in a text file, two Git snapshots (or commits, as we call them) might be, say, three lines different from each other. If you have a photo that has been altered between two different commits, how can Git express that change? It can't, really, because photographs are not made of any kind of sensible text that can just be inserted or removed. I wish photo editing were as easy as just changing some text from "<sky>ugly greenish-blue</sky>" to "<sky>blue-with-fluffy-clouds</sky>" but it truly is not.
-
-People check in blobs, like PNG icons or a speadsheet or a flowchart, to Git all the time, so if you're working in Git then don't be afraid to do that. Know that it's not sensible to do that with huge files, though. If you are working on a project that does generate both text files and large blobs (a common scenario with video games, which have equal parts source code to graphical and audio assets), then you can do one of two things: either invent your own solution, such as pointers to a shared network drive, or use a Git add-on like Joey Hess's excellent [git annex][6], or the [Git-Media][7] project.
-
-So you see, Git really is for everyone. It is a great way to manage versions of your files, it is a powerful tool, and it is not as scary as it first seems.
-
---------------------------------------------------------------------------------
-
-via: https://opensource.com/resources/what-is-git
-
-作者:[Seth Kenlon ][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: https://opensource.com/users/seth
-[1]: https://opensource.com/life/16/2/version-control-isnt-just-programmers
-[2]: http://sparkleshare.org/
-[3]: https://git.wiki.kernel.org/index.php/InterfacesFrontendsAndTools#Graphical_Interfaces
-[4]: https://opensource.com/life/11/11/introduction-kdenlive
-[5]: http://inkscape.org/
-[6]: https://git-annex.branchable.com/
-[7]: https://github.com/alebedev/git-media
-
-
-
-
diff --git a/sources/tech/20160711 Getting started with Git.md b/sources/tech/20160711 Getting started with Git.md
deleted file mode 100644
index 032e5e3510..0000000000
--- a/sources/tech/20160711 Getting started with Git.md	
+++ /dev/null
@@ -1,139 +0,0 @@
-Getting started with Git
-=========================
-
-![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/life/get_started_lead.jpeg?itok=r22AKc6P)
->Image by : opensource.com
-
-
-In the introduction to this series we learned who should use Git, and what it is for. Today we will learn how to clone public Git repositories, and how to extract individual files without cloning the whole works.
-
-Since Git is so popular, it makes life a lot easier if you're at least familiar with it at a basic level. If you can grasp the basics (and you can, I promise!), then you'll be able to download whatever you need, and maybe even contribute stuff back. And that, after all, is what open source is all about: having access to the code that makes up the software you run, the freedom to share it with others, and the right to change it as you please. Git makes this whole process easy, as long as you're comfortable with Git.
-
-So let's get comfortable with Git.
-
-### Read and write
-
-Broadly speaking, there are two ways to interact with a Git repository: you can read from it, or you can write to it. It's just like a file: sometimes you open a document just to read it, and other times you open a document because you need to make changes.
-
-In this article, we'll cover reading from a Git repository. We'll tackle the subject of writing back to a Git repository in a later article.
-
-### Git or GitHub?
-
-A word of clarification: Git is not the same as GitHub (or GitLab, or Bitbucket). Git is a command-line program, so it looks like this:
-
-```
-$ git
-usage: Git [--version] [--help] [-C <path>] 
-  [-p | --paginate | --no-pager] [--bare]
-  [--Git-dir=<path>] <command> [<args>]
-
-```
-
-As Git is open source, lots of smart people have built infrastructures around it which, in themselves, have become very popular.
-
-My articles about Git teach pure Git first, because if you understand what Git is doing then you can maintain an indifference to what front end you are using. However, my articles also include common ways of accomplishing each task through popular Git services, since that's probably what you'll encounter first.
-
-### Installing Git
-
-To install Git on Linux, grab it from your distribution's software repository. BSD users should find Git in the Ports tree, in the devel section.
-
-For non-open source operating systems, go to the [project site][1] and follow the instructions. Once installed, there should be no difference between Linux, BSD, and Mac OS X commands. Windows users will have to adapt Git commands to match the Windows file system, or install Cygwin to run Git natively, without getting tripped up by Windows file system conventions.
-
-### Afternoon tea with Git
-
-Not every one of us needs to adopt Git into our daily lives right away. Sometimes, the most interaction you have with Git is to visit a repository of code, download a file or two, and then leave. On the spectrum of getting to know Git, this is more like afternoon tea than a proper dinner party. You make some polite conversation, you get the information you need, and then you part ways without the intention of speaking again for at least another three months.
-
-And that's OK.
-
-Generally speaking, there are two ways to access Git: via command line, or by any one of the fancy Internet technologies providing quick and easy access through the web browser.
-
-Say you want to install a trash bin for use in your terminal because you've been burned one too many times by the rm command. You've heard about Trashy, which calls itself "a sane intermediary to the rm command", and you want to look over its documentation before you install it. Lucky for you, [Trashy is hosted publicly on GitLab.com][2].
-
-### Landgrab
-
-The first way we'll work with this Git repository is a sort of landgrab method: we'll clone the entire thing, and then sort through the contents later. Since the repository is hosted with a public Git service, there are two ways to do this: on the command line, or through a web interface.
-
-To grab an entire repository with Git, use the git clone command with the URL of the Git repository. If you're not clear on what the right URL is, the repository should tell you. GitLab gives you a copy-and-paste repository URL [for Trashy][3].
-
-![](https://opensource.com/sites/default/files/1_gitlab-url.jpg)
-
-You might notice that on some services, both SSH and HTTPS links are provided. You can use SSH only if you have write permissions to the repository. Otherwise, you must use the HTTPS URL.
-
-Once you have the right URL, cloning the repository is pretty simple. Just git clone the URL, and optionally name the directory to clone it into. The default behaviour is to clone the git directory to your current directory; for example, 'trashy.git' gets put in your current location as 'trashy'. I use the .clone extension as a shorthand for repositories that are read-only, and the .git extension as shorthand for repositories I can read and write, but that's not by any means an official mandate.
-
-```
-$ git clone https://gitlab.com/trashy/trashy.git trashy.clone
-Cloning into 'trashy.clone'...
-remote: Counting objects: 142, done.
-remote: Compressing objects: 100% (91/91), done.
-remote: Total 142 (delta 70), reused 103 (delta 47)
-Receiving objects: 100% (142/142), 25.99 KiB | 0 bytes/s, done.
-Resolving deltas: 100% (70/70), done.
-Checking connectivity... done.
-```
-
-Once the repository has been cloned successfully, you can browse files in it just as you would any other directory on your computer.
-
-The other way to get a copy of the repository is through the web interface. Both GitLab and GitHub provide a snapshot of any repository in a .zip file. GitHub has a big green download button, but on GitLab, look for an inconspicuous download button on the far right of your browser window:
-
-![](https://opensource.com/sites/default/files/1_gitlab-zip.jpg)
-
-### Pick and choose
-
-An alternate method of obtaining a file from a Git repository is to find the file you're after and pluck it right out of the repository. This method is only supported via web interfaces, which is essentially you looking at someone else's clone of a repository; you can think of it as a sort of HTTP shared directory.
-
-The problem with using this method is that you might find that certain files don't actually exist in a raw Git repository, as a file might only exist in its complete form after a make command builds the file, which won't happen until you download the repository, read the README or INSTALL file, and run the command. Assuming, however, that you are sure a file does exist and you just want to go into the repository, grab it, and walk away, you can do that.
-
-In GitLab and GitHub, click the Files link for a file view, view the file in Raw mode, and use your web browser's save function, e.g. in Firefox, File > Save Page As. In a GitWeb repository (a web view of personal git repositories used some who prefer to host git themselves), the Raw view link is in the file listing view.
-
-![](https://opensource.com/sites/default/files/1_webgit-file.jpg)
-
-### Best practices
-
-Generally, cloning an entire Git repository is considered the right way of interacting with Git. There are a few reasons for this. Firstly, a clone is easy to keep updated with the git pull command, so you won't have to keep going back to some web site for a new copy of a file each time an improvement has been made. Secondly, should you happen to make an improvement yourself, then it is easier to submit those changes to the original author if it is all nice and tidy in a Git repository.
-
-For now, it's probably enough to just practice going out and finding interesting Git repositories and cloning them to your drive. As long as you know the basics of using a terminal, then it's not hard to do. Don't know the basics of terminal usage? Give me five more minutes of your time.
-
-### Terminal basics
-
-The first thing to understand is that all files have a path. That makes sense; if I told you to open a file for me on a regular non-terminal day, you'd have to get to where that file is on your drive, and you'd do that by navigating a bunch of computer windows until you reached that file. For example, maybe you'd click your home directory > Pictures > InktoberSketches > monkey.kra.
-
-In that scenario, we could say that the file monkeysketch.kra has the path $HOME/Pictures/InktoberSketches/monkey.kra.
-
-In the terminal, unless you're doing special sysadmin work, your file paths are generally going to start with $HOME (or, if you're lazy, just the ~ character) followed by a list of folders up to the filename itself. This is analogous to whatever icons you click in your GUI to reach the file or folder.
-
-If you want to clone a Git repository into your Documents directory, then you could open a terminal and run this command:
-
-```
-$ git clone https://gitlab.com/foo/bar.git 
-$HOME/Documents/bar.clone
-```
-
-Once that is complete, you can open a file manager window, navigate to your Documents folder, and you'll find the bar.clone directory waiting for you.
-
-If you want to get a little more advanced, you might revisit that repository at some later date, and try a git pull to see if there have been updates to the project:
-
-```
-$ cd $HOME/Documents/bar.clone
-$ pwd
-bar.clone
-$ git pull
-```
-
-For now, that's all the terminal commands you need to get started, so go out and explore. The more you do it, the better you get at it, and that is, at least give or take a vowel, the name of the game.
-
---------------------------------------------------------------------------------
-
-via: https://opensource.com/life/16/7/stumbling-git
-
-作者:[Seth Kenlon][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: https://opensource.com/users/seth
-[1]: https://git-scm.com/download
-[2]: https://gitlab.com/trashy/trashy
-[3]: https://gitlab.com/trashy/trashy.git
- 
diff --git a/sources/tech/20160724  Terminator A Linux Terminal Emulator With Multiple Terminals In One Window.md b/sources/tech/20160724  Terminator A Linux Terminal Emulator With Multiple Terminals In One Window.md
new file mode 100644
index 0000000000..2f1b393c80
--- /dev/null
+++ b/sources/tech/20160724  Terminator A Linux Terminal Emulator With Multiple Terminals In One Window.md	
@@ -0,0 +1,86 @@
+sevenot translating
+Terminator A Linux Terminal Emulator With Multiple Terminals In One Window
+=============================================================================
+
+![](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/lots-of-terminals-in-terminator_1.jpg?659)
+
+Each Linux distribution has a default terminal emulator for interacting with system through commands. But the default terminal app might not be perfect for you. There are so many terminal apps that will provide you more functionalities to perform more tasks simultaneously to sky-rocket speed of your work. Such useful terminal emulators include Terminator, a multi-windows supported free terminal emulator for your Linux system.
+
+### What Is Linux Terminal Emulator?
+
+A Linux terminal emulator is a program that lets you interact with the shell. All Linux distributions come with a default Linux terminal app that let you pass commands to the shell.
+
+### Terminator, A Free Linux Terminal App
+
+Terminator is a Linux terminal emulator that provides several features that your default terminal app does not support. It provides the ability to create multiple terminals in one window and faster your work progress. Other than multiple windows, it allows you to change other properties such as, terminal fonts, fonts colour, background colour and so on. Let's see how we can install and use Terminator in different Linux distributions.
+
+### How To Install Terminator In Linux?
+
+#### Install Terminator In Ubuntu Based Distributions
+
+Terminator is available in the default Ubuntu repository. So you don't require to add any additional PPA. Just use APT or Software App to install it in Ubuntu.
+
+```
+sudo apt-get install terminator
+```
+
+In case Terminator is not available in your default repository, just compile Terminator from source code.
+
+[DOWNLOAD SOURCE CODE][1]
+
+Download Terminator source code and extract it on your desktop. Now open your default terminal & cd into the extracted folder.
+
+Now use the following command to install Terminator -
+
+```
+sudo ./setup.py install
+```
+
+#### Install Terminator In Fedora & Other Derivatives
+
+```
+dnf install terminator
+```
+
+#### Install Terminator In OpenSuse
+
+[INSTALL IN OPENSUSE][2]
+
+### How To Use Multiple Terminals In One Window?
+
+After you have installed Terminator, simply open multiple terminals in one window. Simply right click and divide.
+
+![](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/multiple-terminals-in-terminator_orig.jpg)
+
+![](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/multiple-terminals-in-terminator-emulator.jpg?697)
+
+You can create as many terminals as you want, if you can manage them.
+
+![](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/lots-of-terminals-in-terminator.jpg?706)
+
+### Customise Terminals 
+
+Right click the terminal and click Properties. Now you can customise fonts, fonts colour, title colour & background and terminal fonts colour & background.
+
+![](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/customize-terminator-interface.jpg?702)
+
+![](http://www.linuxandubuntu.com/uploads/2/1/1/5/21152474/free-terminal-emulator_orig.jpg)
+
+### Conclusion & What Is Your Favorite Terminal Emulator?
+
+Terminator is an advanced terminal emulator and it also let you customize the interface. If you have not yet switched from your default terminal emulator then just try this one. I know you'll like it. If you're using any other free terminal emulator, then let us know your favorite terminal emulator. Also don't forget to share this article with your friends. Perhaps your friends are searching for something like this.
+
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/mandatory-access-control-with-selinux-or-apparmor-linux/
+
+作者:[author][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.linuxandubuntu.com/home/terminator-a-linux-terminal-emulator-with-multiple-terminals-in-one-window
+[1]: https://launchpad.net/terminator/+download
+[2]: http://software.opensuse.org/download.html?project=home%3AKorbi123&package=terminator
diff --git a/sources/tech/20160729  Best Password Manager all platform.md b/sources/tech/20160729  Best Password Manager all platform.md
new file mode 100644
index 0000000000..1c90462aae
--- /dev/null
+++ b/sources/tech/20160729  Best Password Manager all platform.md	
@@ -0,0 +1,460 @@
+Translating by Flowsnow!
+Best Password Manager — For Windows, Linux, Mac, Android, iOS and Enterprise
+==============================
+
+![](https://4.bp.blogspot.com/-uMOdpnxBV9w/V5x4YW54SbI/AAAAAAAAo_E/o-gUmO46UB0Ji2IMzd_xdY5pVsCcJnFwQCLcB/s1600/free-best-password-manager-2016.png)
+
+When it comes to safeguarding your Internet security, installing an antivirus software or running a [Secure Linux OS][1] on your system does not mean you are safe enough from all kinds of cyber-threats.
+
+Today majority of Internet users are vulnerable to cyber attacks, not because they aren't using any best antivirus software or other security measures, but because they are using [weak passwords][2] to secure their online accounts.
+
+Passwords are your last lines of defense against online threats. Just look back to some recent data breaches and cyber attacks, including high-profile [data breach at OPM][3] (United States Office of Personnel Management) and the extra-marital affair site [Ashley Madison][4], that led to the exposure of hundreds of millions of records online.
+
+Although you can not control data breaches, it is still important to create strong passwords that can withstand dictionary and [brute-force attacks][5].
+
+You see, the longer and more complex your password is, the much harder it is crack.
+
+### How to Stay Secure Online?
+
+Security researchers have always advised online users to create long, complex and different passwords for their various online accounts. So, if one site is breached, your other accounts on other websites are secure enough from being hacked.
+
+Ideally, your strong password should be at least 16 characters long, should contain a combination of digits, symbols, uppercase letters and lowercase letters and most importantly the most secure password is one you don't even know.
+
+The password should be free of repetition and not contain any dictionary word, pronoun, your username or ID, and any other predefined letter or number sequences.
+
+I know this is a real pain to memorize such complex password strings and unless we are human supercomputers, remembering different passwords for several online accounts is not an easy task.
+
+The issue is that today people subscribe to a lot of online sites and services, and it's usually hard to create and remember different passwords for every single account.
+
+But, Luckily to make this whole process easy, there's a growing market for password managers for PCs and phones that can significantly reduce your password memorizing problem, along with the cure for your bad habit of setting weak passwords.
+
+### What is Password Manager?
+
+![](https://1.bp.blogspot.com/-LY7pI45tMq0/V5r_XV083RI/AAAAAAAAo6M/MivILg0_4Vs7UgLKZJqM5vhvYujQCCcpgCLcB/s1600/best-password-manager-software.png)
+
+Password Manager software has come a very long way in the past few years and is an excellent system that both allows you to create complex passwords for different sites and remember them.
+
+A password manager is just software that creates, stores and organizes all your passwords for your computers, websites, applications and networks.
+
+Password managers that generate passwords and double as a form filler are also available in the market, which has the ability to enter your username and password automatically into login forms on websites.
+
+So, if you want super secure passwords for your multiple online accounts, but you do not want to memorize them all, Password Manager is the way to go.
+
+### How does a Password Manager work?
+
+Typically, Password Manager software works by generating long, complex, and, most importantly, unique password strings for you, and then stores them in encrypted form to protect the confidential data from hackers with physical access to your PC or mobile device.
+
+The encrypted file is accessible only through a master password. So, all you need to do is remember just one master password to open your password manager or vault and unlock all your other passwords.
+
+?
+
+However, you need to make sure your master password is extra-secure of at least 16 characters long.
+
+### Which is the Best Password Manager? How to Choose?
+
+I've long recommended password managers, but most of our readers always ask:
+
+- Which password manager is best?
+- Which password manager is the most secure? Help!
+
+So, today I'm introducing you some of the best Password Manager currently available in the market for Windows, Linux, Mac, Android, iOS and Enterprise.
+
+Before choosing a good password manager for your devices, you should check these following features:
+
+- Cross-Platform Application
+- Works with zero-knowledge model
+- Offers two-factor authentication (multi-factor authentication)
+
+Note: Once adopted, start relying on your password manager because if you are still using weak passwords for your important online accounts, nobody can save you from malicious hackers.
+
+### Best Password Managers for Windows
+
+![](https://2.bp.blogspot.com/-8MUEI5RctdA/V5sDM_oCoaI/AAAAAAAAo60/LX4AktoS_f0JeYDORSqmDZMfmsbOa6QnACLcB/s1600/Best-Password-Manager-for-Windows.png)
+
+Windows users are most vulnerable to cyber attacks because Windows operating system has always been the favorite target of hackers. So, it is important for Windows users to make use of a good password manager.
+
+Some other best password manager for windows: Keeper, Password Safe, LockCrypt, 1Password, and Dashlane.
+
+- 1. Keeper Password Manager (Cross-Platform)
+
+![](https://1.bp.blogspot.com/-9ISKGyTAX9U/V5xec18I21I/AAAAAAAAo8E/i8IqZXXpDwMGe8wnI6Adj3qduR_Qm5o3ACLcB/s1600/keeper-Password-Manager-for-mac-os-x.png)
+
+Keeper is a secure, easy-to-use and robust password manager for your Windows, Mac, iPhone, iPad, and iPod devices.
+
+Using military-grade 256-bit AES encryption, Keeper password manager keeps your data safe from prying eyes.
+
+It has a secure digital vault for protecting and managing your passwords, as well as other secret information. Keeper password manager application supports Two-factor authentication and available for every major operating system.
+
+There is also an important security feature, called Self-destruct, which if enabled, will delete all records from your device if the incorrect master password is entered more than five times incorrectly.
+
+But you don't need worry, as this action will not delete the backup records stored on Keeper's Cloud Security Vault.
+
+Download Keeper Password Manager: [Windows, Linux and Mac][6] | [iOS][7] | [Android][8] | [Kindle][9]
+
+- 2. Dashlane Password Manager (Cross-Platform)
+
+![](https://3.bp.blogspot.com/-2BuFpcAe9K8/V5xjugOWPuI/AAAAAAAAo9A/wpooAjcH74EzxfNJwrFu-Mcn0IkwiRGjACLcB/s1600/Dashlane-Password-Manager-for-Android.png)
+
+DashLane Password Manager software is a little newer, but it offers great features for almost every platform.
+
+DashLane password manager works by encrypting your personal info and accounts' passwords with AES-256 encryption on a local machine, and then syncs your details with its online server, so that you can access your accounts database from anywhere.
+
+The best part of DashLane is that it has an automatic password changer that can change your accounts' passwords for you without having to deal with it yourself.
+
+DashLane Password Manager app for Android gives you the secure password management tools right to your Android phone: your password vault and form auto-filler for online stores and other sites.
+
+DashLane Password Manager app for Android is completely free to use on a single device and for accessing multiple devices, you can buy a premium version of the app.
+
+Download DashLane Password Manager: [Windows][10] and [Mac][11] | [iOS][12] | [Android][13]
+
+- 3. LastPass Password Manager (Cross-Platform)
+
+![](https://3.bp.blogspot.com/--o_hWTgXh2M/V5sAjw7FlYI/AAAAAAAAo6U/Ajmvt0rgRAQE3M_YeYurpbsUoLBN8OTLwCLcB/s1600/LastPass-Password-Manager-for-Windows.png)
+
+LastPass is one of the best Password Manager for Windows users, though it comes with the extension, mobile app, and even desktop app support for all the browsers and operating systems.
+
+LastPass is an incredibly powerful cloud-based password manager software that encrypts your personal info and accounts' passwords with AES-256 bit encryption and even offers a variety of two-factor authentication options in order to ensure no one else can log into your password vault.
+
+LastPass Password Manager comes for free as well as a premium with a fingerprint reader support.
+
+Download LastPass Password Manager: [Windows, Mac, and Linux][14] | [iOS][15] | [Android][16]
+
+### Best Password Manager for Mac OS X
+
+![](https://2.bp.blogspot.com/-lEim3E-0wcg/V5sFhOVYK7I/AAAAAAAAo7A/z6Lp8_ULdJAD8ErZ1a-FevXPO8nR3JKNACLcB/s1600/Best-Password-Manager-for-mac-os-x.png)
+
+People often say that Mac computers are more secure than Windows and that "Macs don't get viruses," but it is not entirely correct.
+
+As proof, you can read our previous articles on cyber attacks against Mac and iOs users, and then decide yourself that you need a password manager or not.
+
+Some other best password manager for Mac OS X:  1Password, Dashlane, LastPass, OneSafe, PwSafe.
+
+- 1. LogMeOnce Password Manager (Cross-Platform)
+
+![](https://4.bp.blogspot.com/-fl64fXK2bdA/V5sHL215j_I/AAAAAAAAo7M/fbn4EsrQMkU3tWWfiAsWlTgKKXb0oEzlwCLcB/s1600/LogMeOnce-Password-Manager-for-Mac-os-x.png)
+
+LogMeOnce Password Management Suite is one of the best password manager for Mac OS X, as well as syncs your passwords across Windows, iOS, and Android devices.
+
+LogMeOnce is one of the best Premium and Enterprise Password Management Software that offers a wide variety of features and options, including Mugshot feature.
+
+If your phone is ever stolen, LogMeOnce Mugshot feature tracks the location of the thief and also secretly takes a photo of the intruder when trying to gain access to your account without permission.
+
+LogmeOnce protects your passwords with military-grade AES-256 encryption technology and offers Two-factor authentication to ensure that even with the master password in hand, a thief hacks your account.
+
+Download LogMeOnce Password Manager: [Windows and Mac][17] | [iOS][18] | [Android][19]
+
+- 2. KeePass Password Manager (Cross-Platform)
+
+![](https://4.bp.blogspot.com/-XWwdG1z9sDw/V5sA7azAy6I/AAAAAAAAo6c/dkkfMRuxDoE_gi5OMRvDOUFq15P5NRO6QCLcB/s1600/Keepass-Password-Manager-for-Windows.png)
+
+Although LastPass is one of the best password manager, some people are not comfortable with a cloud-based password manager.
+
+KeePass is a popular password manager application for Windows, but there are browser extensions and mobile apps for KeePass as well.
+
+KeePass password manager for Windows stores your accounts' passwords on your PC, so you remain in control of them, and also on Dropbox, so you can access it using multiple devices.
+
+KeePass encrypts your passwords and login info using the most secure encryption algorithms currently known: AES 256-bit encryption by default, or optional, Twofish 256-bit encryption.
+
+KeePass is not just free, but it is also open source, which means its code and integrity can be examined by anyone, adding a degree of confidence.
+
+Download KeePass Password Manager: [Windows and Linux][20] | [Mac][21] | [iOS][22] | [Android][23]
+3. Apple iCloud Keychain
+
+![](https://4.bp.blogspot.com/-vwY_dmsKIBg/V5xfhIZGxxI/AAAAAAAAo8M/OjPrBsp9GysF-bK3oqHtW74hKNYO61W9QCLcB/s1600/Apple-iCloud-Keychain-Security.png)
+
+Apple introduced the iCloud Keychain password management system as a convenient way to store and automatically sync all your login credentials, Wi-Fi passwords, and credit card numbers securely across your approved Apple devices, including Mac OS X, iPhone, and iPad.
+
+Your Secret Data in Keychain is encrypted with 256-bit AES (Advanced Encryption Standard) and secured with elliptic curve asymmetric cryptography and key wrapping.
+
+Also, iCloud Keychain generates new, unique and strong passwords for you to use to protect your computer and accounts.
+
+Major limitation: Keychain doesn't work with other browsers other than Apple Safari.
+
+Also Read: [How to Setup iCloud Keychain][24]?
+Best Password Manager for Linux
+
+![](https://1.bp.blogspot.com/-2zDAqYEQTQA/V5xgbo_OcQI/AAAAAAAAo8Y/hWzGLW7R4vse3QnpCM5-qmSHLtzK5M1VACLcB/s1600/best-Password-Manager-for-linux.png)
+
+No doubt, some Linux distributions are the safest operating systems exist on the earth, but as I said above that adopting Linux doesn't completely protect your online accounts from hackers.
+
+There are a number of cross-platform password managers available that sync all your accounts' passwords across all your devices, such as LastPass, KeePass, RoboForm password managers.
+
+Here below I have listed two popular and secure open source password managers for Linux:
+
+- 1. SpiderOak Encryptr Password Manager (Cross-Platform)
+
+![](https://4.bp.blogspot.com/-SZkmP7dpXZM/V5xiKeuT4KI/AAAAAAAAo8s/QhvfBz3OX78IUit_HLym0sdKxlz99qFfgCLcB/s1600/SpiderOak-Encryptr-Password-Manager-for-linux.png)
+
+SpiderOak's Encryptr Password Manager is a zero-knowledge cloud-based password manager that encrypts protect your passwords using Crypton JavaScript framework, developed by SpiderOak and recommended by Edward Snowden.
+
+It is a cross-platform, open-Source and free password manager that uses end-to-end encryption and works perfectly for Ubuntu, Debian Linux Mint, and other Linux distributions.
+
+Encryptr Password Manager application itself is very simple and comes with some basic features.
+
+Encryptr software lets you encrypt three types of files: Passwords, Credit Card numbers and general any text/keys.
+
+Download Encryptr Password Manager: [Windows, Linux and Mac][25] | [iOS][26] | [Android][27]
+
+2. EnPass Password Manager (Cross-Platform)
+
+![](https://4.bp.blogspot.com/-_IF81t9rL7U/V5xhBIPUSHI/AAAAAAAAo8c/6-kbLXTl2G0EESTH4sP9KvZLzTFlCyypACLcB/s1600/EnPass-Password-Manager-for-Linux.png)
+
+Enpass is an excellent security oriented Linux password manager that works perfectly with other platforms too. Enpass offers you to backup and restores stored passwords with third-party cloud services, including Google Drive, Dropbox, OneDrive, or OwnCloud.
+
+It makes sure to provide the high levels of security and protects your data by a master password and encrypted it with 256-bit AES using open-source encryption engine SQLCipher, before uploading backup onto the cloud.
+"We do not host your Enpass data on our servers. So, no signup is required for us. Your data is only stored on your device," EnPass says.
+Additionally, by default, Enpass locks itself every minute when you leave your computer unattended and clears clipboard memory every 30 seconds to prevent your passwords from being stolen by any other malicious software.
+
+Download EnPass Password Manager: [Windows][28], [Linux][29] | [Mac][30] | [iOS][31] | [Android][32]
+
+3. RoboForm Password Manager (Cross-Platform)
+
+![](https://3.bp.blogspot.com/-g8Qf9V1EdqU/V5sBkDk614I/AAAAAAAAo6k/5ZTr9LyosU82F16GxajewvU4sWYyJFq5gCLcB/s1600/Roboform-Password-Manager-for-Windows.png)
+
+You can easily find good password managers for Windows OS, but RoboForm Free Password Manager software goes a step further.
+
+Besides creating complex passwords and remembering them for you, RoboForm also offers a smart form filler feature to save your time while browsing the Web.
+
+RoboForm encrypts your login info and accounts' passwords using military grade AES encryption with the key that is obtained from your RoboForm Master Password.
+
+RoboForm is available for browsers like Internet Explorer, Chrome, and Firefox as well as mobile platforms with apps available for iOS, Android, and Windows Phone.
+
+Download RoboForm Password Manager: [Windows and Mac][33] | [Linux][34] | [iOS][35] | [Android][36]
+
+### Best Password Manager for Android
+
+![](https://1.bp.blogspot.com/-1PXI2KDrDEU/V5xigbW8lgI/AAAAAAAAo8w/Zv5hrdOcbSU7LA0kYrNpvJ1rxjg7EoOewCLcB/s1600/best-Password-Manager-for-android.png)
+
+More than half of the world's population today is using Android devices, so it becomes necessary for Android users to secure their online accounts from hackers who are always seeking access to these devices.
+
+Some of the best Password Manager apps for Android include 1Password, Keeper, DashLane, EnPass, OneSafe, mSecure and SplashID Safe.
+
+- 1. 1Password Password Manager (Cross-Platform)
+
+![](https://4.bp.blogspot.com/--w3s9SoWgYA/V5xjJwVRUTI/AAAAAAAAo84/BSucybvPdtUKYYcRtDbn-_2cOz-mfMA9gCLcB/s1600/1password-Password-Manager-for-android.png)
+
+1Password Password Manager app for Android is one of the best apps for managing all your accounts' passwords.
+
+1Password password manager app creates strong, unique and secure passwords for every account, remembers them all for you, and logs you in with just a single tap.
+
+1Password password manager software secures your logins and passwords with AES-256 bit encryption, and syncs them to all of your devices via your Dropbox account or stores locally for any other application to sync if you choose.
+
+Recently, the Android version of 1Password password manager app has added Fingerprint support for unlocking all of your passwords instead of using your master password.
+
+Download 1Password Password Manager: [Windows and Mac][37] | [iOS][38] | [Android][39]
+
+- 2. mSecure Password Manager (Cross-Platform)
+
+![](https://4.bp.blogspot.com/-nvjjS2dWfPc/V5xkEdAOYvI/AAAAAAAAo9I/EDGfA5hzacIq46gWG-6BD2UPHwQAHD-pgCLcB/s1600/mSecure-password-manager-for-android.png)
+
+Like other popular password manager solutions, mSecure Password Manager for Android automatically generates secure passwords for you and stores them using 256-bit Blowfish encryption.
+
+The catchy and unique feature mSecure Password Manager software provides its ability to self-destruct database after 5, 10, or 20 failed attempts (as per your preference) to input the right password.
+
+You can also sync all of your devices with Dropbox, or via a private Wi-Fi network. In either case, all your data is transmitted safely and securely between devices regardless of the security of your cloud account.
+
+Download mSecure Password Manager software: [Windows and Mac][40] | [iOS][41] | [Android][42]
+
+### Best Password Manager for iOS
+
+![](https://4.bp.blogspot.com/-SOXYw_9mFq0/V5xk6Kl8-DI/AAAAAAAAo9Q/AMbEl_t3HjAJ4ZX7gLVoa33z-myE4bK5wCLcB/s1600/best-Password-Manager-for-ios-iphone.png)
+
+As I said, Apple's iOS is also prone to cyber attacks, so you can use some of the best password manager apps for iOS to secure your online accounts, including Keeper, OneSafe, Enpass, mSecure, LastPass, RoboForm, SplashID Safe and LoginBox Pro.
+
+- 1. OneSafe Password Manager (Cross-Platform)
+
+![](https://2.bp.blogspot.com/-HPEJpqeOs00/V5xlSh7OUxI/AAAAAAAAo9Y/d5qkOy3BieMSxjGrnrnH4fvzUzAzDqhCgCLcB/s1600/onesafe-password-manager-for-ios.png)
+
+OneSafe is one of the best Password Manager apps for iOS devices that lets you store not only your accounts' passwords but also sensitive documents, credit card details, photos, and more.
+
+OneSafe password manager app for iOS encrypts your data behind a master password, with AES-256 encryption — the highest level available on mobile — and Touch ID. There is also an option for additional passwords for given folders.
+
+OneSafe password manager for iOS also offers an in-app browser that supports autofill of logins, so that you don't need to enter your login details every time.
+
+Besides this, OneSafe also provides advanced security for your accounts' passwords with features like auto-lock, intrusion detection, self-destruct mode, decoy safe and double protection.
+
+Download OneSafe Password Manager: [iOS][43] | [Mac][44] | [Android][45] | [Windows][46]
+
+- 2. SplashID Safe Password Manager (Cross-Platform)
+
+![](https://1.bp.blogspot.com/-FcNub2p-QNE/V5xmDW7QXvI/AAAAAAAAo9o/23VuGUAMCYYS64kKlUqBcfx3JIfBr5gTgCLcB/s1600/SplashID-Safe-password-manager-for-ios.png)
+
+SplashID Safe is one of the oldest and best password manager tools for iOS that allows users to securely store their login data and other sensitive information in an encrypted record.
+
+All your information, including website logins, credit card and social security data, photos and file attachments, are protected with 256-bit encryption.
+
+SplashID Safe Password Manager app for iOS also provides web autofill option, meaning you will not have to bother copy-pasting your passwords in login.
+
+The free version of SplashID Safe app comes with basic record storage functionality, though you can opt for premium subscriptions that provide cross-device syncing among other premium features.
+
+Download SplashID Safe Password Manager: [Windows and Mac][47] | [iOS][48] | [Android][49]
+
+3. LoginBox Pro Password Manager
+
+![](https://3.bp.blogspot.com/-4GzhwZFXDHQ/V5xogkDk49I/AAAAAAAAo90/69rmVdKD-VUG0kHJXIqE2x-mVlWZEDrYwCLcB/s1600/LoginBox-Pro-Password-Manager-for-ios.png)
+
+LoginBox Pro is another great password manager app for iOS devices. The app provides a single tap login to any website you visit, making the password manager app as the safest and fastest way to sign in to password-protected internet sites.
+
+LoginBox Password Manager app for iOS combines a password manager as well as a browser.
+
+From the moment you download it, all your login actions, including entering information, tapping buttons, checking boxes, or answering security questions, automatically completes by the LoginBox Password Manager app.
+
+For security, LoginBox Password Manager app uses hardware-accelerated AES encryption and passcode to encrypt your data and save it on your device itself.
+
+Download LoginBox Password Manager: [iOS][50] | [Android][51]
+
+### Best Online Password Managers
+
+Using an online password manager tool is the easiest way to keep your personal and private information safe and secure from hackers and people with malicious intents.
+
+Here I have listed some of the best online password managers that you can rely on to keep yourself safe online:
+
+- 1. Google Online Password Manager
+
+![](https://2.bp.blogspot.com/-HCSzj5tKgwY/V5xqVjjtfgI/AAAAAAAAo-A/OYcgv-S5wmQlAskF1jrEGQAy98ogMnXTgCLcB/s1600/google-online-password-manager.png)
+
+Did you know Google has its homebrew dedicated password manager?
+
+Google Chrome has a built-in password manager tool that offers you an option to save your password whenever you sign in to a website or web service using Chrome.
+
+All of your stored accounts' passwords are synced with your Google Account, making them available across all of your devices using the same Google Account.
+
+Chrome password manager lets you manage all your accounts' passwords from the Web.
+
+So, if you prefer using a different browser, like Microsoft Edge on Windows 10 or Safari on iPhone, just visit [passwords.google.com][52], and you'll see a list of all your passwords you have saved with Chrome. Google's two-factor authentication protects this list.
+
+- 2. Clipperz Online Password Manager
+
+![](https://2.bp.blogspot.com/-gs8b_N_k6CA/V5xrvzbUIKI/AAAAAAAAo-M/vsTXHZNErkQu6g8v9V1R2FxLkdppZq_GACLcB/s1600/Clipperz-Online-Password-Manager.png)
+
+Clipperz is a free, cross-platform best online password manager that does not require you to download any software. Clipperz online password manager uses a bookmarklet or sidebar to create and use direct logins.
+
+Clipperz also offers an offline password manager version of its software that allows you to download your passwords to an [encrypted disk][53] or a USB drive so you can take them with you while traveling and access your accounts' passwords when you are offline.
+
+Some features of Clipperz online password manager also includes password strength indicator, application locking, SSL secure connection, one-time password and a password generator.
+
+Clipperz online password manager can work on any computer that runs a browser with a JavaScript browser.
+
+- 3. Passpack Online Password Manager
+
+![](https://4.bp.blogspot.com/-ng91nPnzbWI/V5xsarl2mqI/AAAAAAAAo-Q/zJlFK-63vugeoyymDL26c5mPiWNsGQjuACLcB/s1600/Passpack-Free-Online-Password-Manager.png)
+
+Passpack is an excellent online password manager with a competitive collection of features that creates, stores and manages passwords for your different online accounts.
+
+PassPack online password manager also allows you to share your passwords safely with your family or coworkers for managing multiple projects, team members, clients, and employees easily.
+
+Your usernames and passwords for different accounts are encrypted with AES-256 Encryption on PassPack's servers that even hackers access to its server can not read your login information.
+
+Download the PassPack online password manager toolbar to your web browser and navigate the web normally. Whenever you log into any password-protected site, PassPack saves your login data so that you do not have to save your username and password manually on its site.
+
+### Best Enterprise Password Manager
+
+Over the course of last 12 months, we've seen some of the biggest data breaches in the history of the Internet and year-over-year the growth is heating up.
+
+According to statistics, a majority of employees even don't know how to protect themselves online, which led company’s business at risk.
+
+To keep password sharing mechanism secure in an organization, there exist some password management tools specially designed for enterprises use, such as Vaultier, CommonKey, Meldium, PassWork, and Zoho Vault.
+
+- 1. Meldium Enterprise Password Manager Software
+
+![](https://3.bp.blogspot.com/-3rKr3KUpuiQ/V5xs8JR7pVI/AAAAAAAAo-c/VF1tmKbwPzoJmNvA3Ym69CizG7_VqM6ywCLcB/s1600/Meldium-Enterprise-Password-Manager.png)
+
+LogMeIn's Meldium password management tool comes with a one-click single sign-on solution that helps businesses access to web apps securely and quickly.
+
+It automatically logs users into apps and websites without typing usernames and passwords and also tracks password usage within your organization.
+
+Meldium password manager is perfect for sharing accounts within your team member without sharing the actual password, which helps organizations to protect themselves from phishing attacks.
+
+- 2. Zoho Vault Password Management Software
+
+![](https://2.bp.blogspot.com/-J-N_1wOYxmI/V5xtrz42QWI/AAAAAAAAo-o/QF4n4QAF7ZMBd7uIRdjM6Hdd1MHwsXWQACLcB/s1600/zoho-vault--Enterprise-Password-Manager.png)
+
+Zoho Vault is one of the best Password Manager for Enterprise users that helps your team share passwords and other sensitive information fast and securely while monitoring each user's usage.
+
+All your team members need to download is the Zoho browser extension. Zoho Vault password manager will automatically fill passwords from your team's shared vault.
+
+Zoho Vault also provides features that let you monitor your team's password usage and security level so that you can know who is using which login.
+
+The Zoho Vault enterprise-level package even alerts you whenever a password is changed or accessed.
+
+### For Extra Security, Use 2-Factor Authentication
+
+![](https://4.bp.blogspot.com/-jDnJBDoibtQ/V5xuHVHukRI/AAAAAAAAo-w/1Erjgk-IvKs__TXwYDz-8Groz9hWEElZgCLcB/s1600/two-factor-authentication-password-security.png)
+
+No matter how strong your password is, there still remains a possibility for hackers to find some or the other way to hack into your account.
+
+Two-factor authentication is designed to fight this issue. Instead of just one password, it requires you to enter the second passcode which is sent either to your mobile number via an SMS or to your email address via an email.
+
+So, I recommend you to enable two-factor authentication now along with using a password manager software to secure your online accounts and sensitive information from hackers.
+
+
+
+
+
+--------------------------------------------------------------------------------
+
+via: https://thehackernews.com/2016/07/best-password-manager.html
+
+作者:[Swati Khandelwal][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://thehackernews.com/2016/07/best-password-manager.html#author-info
+
+[1]: http://thehackernews.com/2016/03/subgraph-secure-operating-system.html
+[2]: http://thehackernews.com/2016/01/password-security-manager.html
+[3]: http://thehackernews.com/2015/09/opm-hack-fingerprint.html
+[4]: http://thehackernews.com/2015/08/ashley-madison-accounts-leaked-online.html
+[5]: http://thehackernews.com/2013/05/cracking-16-character-strong-passwords.html
+[6]: https://keepersecurity.com/download.html
+[7]: https://itunes.apple.com/us/app/keeper-password-manager-digital/id287170072?mt=8
+[8]: https://play.google.com/store/apps/details?id=com.callpod.android_apps.keeper
+[9]: http://www.amazon.com/gp/mas/dl/android?p=com.callpod.android_apps.keeper
+[10]: https://www.dashlane.com/download
+[11]: https://www.dashlane.com/passwordmanager/mac-password-manager
+[12]: https://itunes.apple.com/in/app/dashlane-free-secure-password/id517914548?mt=8
+[13]: https://play.google.com/store/apps/details?id=com.dashlane&hl=en
+[14]: https://lastpass.com/misc_download2.php
+[15]: https://itunes.apple.com/us/app/lastpass-for-premium-customers/id324613447?mt=8&ign-mpt=uo%3D4
+[16]: https://play.google.com/store/apps/details?id=com.lastpass.lpandroid
+[17]: https://www.logmeonce.com/download/
+[18]: https://itunes.apple.com/us/app/logmeonce-free-password-manager/id972000703?ls=1&mt=8
+[19]: https://play.google.com/store/apps/details?id=log.me.once
+
+[20]: http://keepass.info/download.html
+[21]: https://itunes.apple.com/us/app/kypass-companion/id555293879?ls=1&mt=12
+[22]: https://itunes.apple.com/de/app/ikeepass/id299697688?mt=8
+[23]: https://play.google.com/store/apps/details?id=keepass2android.keepass2android
+[24]: https://support.apple.com/en-in/HT204085
+[25]: https://spideroak.com/opendownload
+[26]: https://itunes.apple.com/us/app/spideroak/id360584371?mt=8
+[27]: https://play.google.com/store/apps/details?id=com.spideroak.android
+[28]: https://www.enpass.io/download-enpass-for-windows/
+[29]: https://www.enpass.io/download-enpass-linux/
+[30]: https://itunes.apple.com/app/enpass-password-manager-best/id732710998?mt=12
+[31]: https://itunes.apple.com/us/app/enpass-password-manager/id455566716?mt=8
+[32]: https://play.google.com/store/apps/details?id=io.enpass.app&hl=en
+[33]: http://www.roboform.com/download
+[34]: http://www.roboform.com/for-linux
+[35]: https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=331787573&mt=8
+[36]: https://play.google.com/store/apps/details?id=com.siber.roboform
+[37]: https://1password.com/downloads/
+[38]: https://itunes.apple.com/in/app/1password-password-manager/id568903335?mt=8
+[39]: https://play.google.com/store/apps/details?id=com.agilebits.onepassword&hl=en
+[40]: https://www.msecure.com/desktop-app/
+[41]: https://itunes.apple.com/in/app/msecure-password-manager/id292411902?mt=8
+[42]: https://play.google.com/store/apps/details?id=com.mseven.msecure&hl=en
+[43]: https://itunes.apple.com/us/app/onesafe/id455190486?ls=1&mt=8
+[44]: https://itunes.apple.com/us/app/onesafe-secure-password-manager/id595543758?ls=1&mt=12
+[45]: https://play.google.com/store/apps/details?id=com.lunabee.onesafe
+[46]: https://www.microsoft.com/en-us/store/apps/onesafe/9wzdncrddtx9
+[47]: https://splashid.com/downloads.php
+[48]: https://itunes.apple.com/app/splashid-safe-password-manager/id284334840?mt=8
+[49]: https://play.google.com/store/apps/details?id=com.splashidandroid&hl=en
+[50]: https://itunes.apple.com/app/loginbox-pro/id579954762?mt=8
+[51]: https://play.google.com/store/apps/details?id=com.mygosoftware.android.loginbox
+[52]: https://passwords.google.com/
+[53]: http://thehackernews.com/2014/01/Kali-linux-Self-Destruct-nuke-password.html
+
diff --git a/sources/tech/20160805 Introducing React Native Ubuntu.md b/sources/tech/20160805 Introducing React Native Ubuntu.md
new file mode 100644
index 0000000000..eba89ad8b0
--- /dev/null
+++ b/sources/tech/20160805 Introducing React Native Ubuntu.md	
@@ -0,0 +1,40 @@
+ch_cn translating
+Introducing React Native Ubuntu
+=====================
+
+In the Webapps team at Canonical, we are always looking to make sure that web and near-web technologies are available to developers. We want to make everyone's life easier, enable the use of tools that are familiar to web developers and provide an easy path to using them on the Ubuntu platform.
+
+We have support for web applications and creating and packaging Cordova applications, both of these enable any web framework to be used in creating great application experiences on the Ubuntu platform.
+
+One popular web framework that can be used in these environments is React.js; React.js is a UI framework with a declarative programming model and strong component system, which focuses primarily on the composition of the UI, so you can use what you like elsewhere.
+
+While these environments are great, sometimes you need just that bit more performance, or to be able to work with native UI components directly, but working in a less familiar environment might not be a good use of time. If you are familiar with React.js, it's easy to move into full native development with all your existing knowledge and tools by developing with React Native. React Native is the sister to React.js, you can use the same style and code to create an application that works directly with native components with native levels of performance, but with the ease of and rapid development you would expect.
+
+
+![](http://i.imgur.com/ZsSHWXP.png)
+
+We are happy to announce that along with our HTML5 application support, it is now possible to develop React Native applications on the Ubuntu platform. You can port existing iOS or Android React Native applications, or you can start a new application leveraging your web-dev skills.
+
+You can find the source code for React Native Ubuntu [here][1],
+
+To get started, follow the instructions in [README-ubuntu.md][2] and create your first application.
+
+The Ubuntu support includes the ability to generate packages. Managed by the React Native CLI, building a snap is as easy as 'react-native package-ubuntu --snap'. It's also possible to build a click package for Ubuntu devices; meaning React Native Ubuntu apps are store ready from the start.
+
+Over the next little while there will be blogs posts on everything you need to know about developing a React Native Application for the Ubuntu platform; creating the app, the development process, packaging and releasing to the store. There will also be some information on how to develop new reusable modules, that can add extra functionality to the runtime and be distributed as Node Package Manager (npm) modules.
+
+Go and experiment, and see what you can create.
+
+--------------------------------------------------------------------------------
+
+via: https://developer.ubuntu.com/en/blog/2016/08/05/introducing-react-native-ubuntu/?utm_source=javascriptweekly&utm_medium=email
+
+作者:[Justin McPherson][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://developer.ubuntu.com/en/blog/authors/justinmcp/
+[1]: https://github.com/CanonicalLtd/react-native
+[2]: https://github.com/CanonicalLtd/react-native/blob/ubuntu/README-ubuntu.md
diff --git a/sources/tech/20160807 Going Serverless with AWS Lambda and API Gateway.md b/sources/tech/20160807 Going Serverless with AWS Lambda and API Gateway.md
new file mode 100644
index 0000000000..fa9ff7e5f9
--- /dev/null
+++ b/sources/tech/20160807 Going Serverless with AWS Lambda and API Gateway.md	
@@ -0,0 +1,244 @@
+Going Serverless with AWS Lambda and API Gateway
+============================
+
+Lately, there's been a lot of buzz in the computing world about "serverless". Serverless is a concept wherein you don't manage any servers yourself but instead provide your code or executables to a service that executes them for you. This is execution-as-a-service. It introduces many opportunities and also presents its own unique set of challenges.
+
+A brief digression on computing
+
+In the beginning, there was... well. It's a little complicated. At the very beginning, we had mechanical computers. Then along came ENIAC. Things really don't start to get "mass production", however, until the advent of mainframes.
+
+```
+1950s - Mainframes
+1960s - Minicomputers
+1994 - Rack servers
+2001 - Blade servers
+2000s - Virtual servers
+2006 - Cloud servers
+2013 - Containers
+2014 - Serverless
+```
+
+>These are rough release/popularity dates. Argue amongst yourselves about the timeline.
+
+The progression here seems to be a trend toward executing smaller and smaller units of functionality. Each step down generally represents a decrease in the operational overhead and an increase in the operational flexibility.
+
+### The possibilities
+
+Yay! Serverless! But. What advantages do we gain by going serverless? And what challenges do we face?
+
+No billing when there is no execution. In my mind, this is a huge selling point. When no one is using your site or your API, you aren't paying for. No ongoing infrastructure costs. Pay only for what you need. In some ways, this is the fulfillment of the cloud computing promise "pay only for what you use".
+
+No servers to maintain or secure. Server maintenance and security is handled by your vendor (you could, of course, host serverless yourself, but in some ways this seems like a step in the wrong direction). Since your execution times are also limited, the patching of security issues is also simplified since there is nothing to restart. This should all be handled seamlessly by your vendor.
+
+Unlimited scalability. This is another big one. Let's say you write the next Pokémon Go. Instead of your site being down every couple of days, serverless lets you just keep growing and growing. A double-edged sword, for sure (with great scalability comes great... bills), but if your service's profitability depends on being up, then serverless can help enable that.
+
+Forced microservices architecture. This one really goes both ways. Microservices seem to be a good way to build flexible, scalable, and fault-tolerant architectures. On the other hand, if your business' services aren't designed this way, you're going to have difficulty adding serverless into your existing architecture.
+
+### But now your stuck on their platform
+
+Limited range of environments. You get what the vendor gives. You want to go serverless in Rust? You're probably out of luck.
+
+Limited preinstalled packages. You get what the vendor pre-installs. But you may be able to supply your own.
+
+Limited execution time. Your function can only run for so long. If you have to process a 1TB file you will likely need to 1) use a work around or 2) use something else.
+
+Forced microservices architecture. See above.
+
+Limited insight and ability to instrument. Just what is your code doing? With serverless, it is basically impossible to drop in a debugger and ride along. You still have the ability to log and emit metrics the usual way but these generally can only take you so far. Some of the most difficult problems may be out of reach when the occur in a serverless environment.
+
+### The playing field
+
+Since the introduction of AWS Lambda in 2014, the number of offerings has expanded quite a bit. Here are a few popular ones:
+
+- AWS Lambda - The Original
+- OpenWhisk - Available on IBM's Bluemix cloud
+- Google Cloud Functions
+- Azure Functions
+
+While all of these have their relative strengths and weaknesses (like, C# support on Azure or tight integration on any vendor's platform) the biggest player here is AWS.
+
+### Building your first API with AWS Lambda and API Gateway
+
+Let's give serverless a whirl, shall we? We'll be using AWS Lambda and API Gateway offerings to build an API that returns "Guru Meditations" as spoken by Jimmy.
+
+All of the code is available on [GitHub][1].
+
+API Documentation:
+
+```
+POST /
+{
+    "status": "success",
+    "meditation": "did u mention banana cognac shower"
+}
+```
+
+### How we'll structure things
+
+The layout:
+
+```
+.
+├── LICENSE
+├── README.md
+├── server
+│   ├── __init__.py
+│   ├── meditate.py
+│   └── swagger.json
+├── setup.py
+├── tests
+│   └── test_server
+│       └── test_meditate.py
+└── tools
+    ├── deploy.py
+    ├── serve.py
+    ├── serve.sh
+    ├── setup.sh
+    └── zip.sh
+```
+
+Things in AWS (for a more detailed view as to what is happening here, consult the source of tools/deploy.py):
+
+- API. The thing that were actually building. It is represented as a separate object in AWS.
+- Execution Role. Every function executes as a particular role in AWS. Ours will be meditations.
+- Role Policy. Every function executes as a role, and every role needs permission to do things. Our lambda function doesn't do much, so we'll just add some logging permissions.
+- Lambda Function. The thing that runs our code.
+- Swagger. Swagger is a specification of an API. API Gateway supports consuming a swagger definition to configure most resources for that API.
+- Deployments. API Gateway provides for the notion of deployments. We won't be using more than one of these for our API here (i.e., everything is production, yolo, etc.), but know that they exist and for a real production-ready service you will probably want to use development and staging environments.
+- Monitoring. In case our service crashes (or begins to accumulate a hefty bill from usage) we'll want to add some monitoring in the form of cloudwatch alarms for errors and billing. Note that you should modify tools/deploy.py to set your email correctly.
+
+### the codes
+
+The lambda function itself will be returning guru meditations at random from a hardcoded list and is very simple:
+
+```
+import logging
+import random
+
+
+logger = logging.getLogger()
+logger.setLevel(logging.INFO)
+
+
+def handler(event, context):
+
+    logger.info(u"received request with id '{}'".format(context.aws_request_id))
+
+    meditations = [
+    "off to a regex/",
+    "the count of machines abides",
+    "you wouldn't fax a bat",
+    "HAZARDOUS CHEMICALS + RKELLY",
+    "your solution requires a blood eagle",
+    "testing is broken because I'm lazy",
+    "did u mention banana cognac shower",
+    ]
+
+    meditation = random.choice(meditations)
+
+    return {
+        "status": "success",
+        "meditation": meditation,
+    }
+```
+
+### The deploy.py script
+
+This one is rather long (sadly) and I won't be including it here. It basically goes through each of the items under "Things in AWS" and ensures each item exists.
+
+Let's deploy this shit
+
+Just run `./tools/deploy.py`.
+
+Well. Almost. There seems to be some issue applying privileges that I can't seem to figure out. Your lambda function will fail to execute because API Gateway does not have permissions to execute your function. The specific error should be "Execution failed due to configuration error: Invalid permissions on Lambda function". I am not sure how to add these using botocore. You can work around this issue by going to the AWS console (sadness), locating your API, going to the / POST endpoint, going to integration request, clicking the pencil icon next to "Lambda Function" to edit it, and then saving it. You'll get a popup stating "You are about to give API Gateway permission to invoke your Lambda function". Click "OK".
+
+When you're finished with that, take the URL that ./tools/deploy.py printed and call it like so to see your new API in action:
+
+```
+$ curl -X POST https://a1b2c3d4.execute-api.us-east-1.amazonaws.com/prod/
+{"status": "success", "meditation": "the count of machines abides"}
+```
+
+### Running Locally
+
+One unfortunate thing about AWS Lambda is that there isn't really a good way to run your code locally. In our case, we will use a simple flask server to host the appropriate endpoint locally and handle calling our handler function:
+
+```
+from __future__ import absolute_import
+
+from flask import Flask, jsonify
+
+from server.meditate import handler
+
+
+app = Flask(__name__)
+
+@app.route("/", methods=["POST"])
+def index():
+
+    class FakeContext(object):
+        aws_request_id = "XXX"
+
+    return jsonify(**handler(None, FakeContext()))
+
+app.run(host="0.0.0.0")
+```
+
+You can run this in the repo with ./tools/serve.sh. Invoke like:
+
+```
+$ curl -X POST http://localhost:5000/
+{
+    "meditation": "your solution requires a blood eagle",
+    "status": "success"
+}
+```
+
+### Testing
+
+You should always test your code. The way we'll be doing this is importing and running our handler function. This is really just plain vanilla python testing:
+
+```
+from __future__ import absolute_import
+
+import unittest
+
+from server.meditate import handler
+
+
+class SubmitTestCase(unittest.TestCase):
+
+    def test_submit(self):
+
+        class FakeContext(object):
+
+            aws_request_id = "XXX"
+
+        response = handler(None, FakeContext())
+
+        self.assertEquals(response["status"], "success")
+        self.assertTrue("meditation" in response)
+```
+
+You can run the tests in the repo with nose2.
+
+### Other possibilities
+
+Seamless integration with AWS services. Using boto, you can pretty simply connect to any of AWS other services. You simply allow your execution role access to these services using IAM and you're on your way. You can get/put files from S3, connect to Dynamo DB, invoke other lambda functions, etc. etc. The list goes on.
+
+Accessing a database. You can easily access remote databases as well. Connect to the database at the top of your lambda handler's module. Execute queries on the connection from within the handler function itself. You will (very likely) have to upload the associated package contents from where it is installed locally for this to work. You may also need to statically compile certain libraries.
+
+Calling other webservices. API Gateway is also a way to translate the output from one web service into a different form. You can take advantage of this to proxy calls through to a different webservice or provide backwards compatibility when a service changes.
+
+--------------------------------------------------------------------------------
+
+via: http://blog.ryankelly.us/2016/08/07/going-serverless-with-aws-lambda-and-api-gateway.html?utm_source=webopsweekly&utm_medium=email
+
+作者:[Ryan Kelly][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://github.com/f0rk/blog.ryankelly.us/
+[1]: https://github.com/f0rk/meditations
diff --git a/sources/tech/20160809 How to Monitor Docker Containers using Grafana on Ubuntu.md b/sources/tech/20160809 How to Monitor Docker Containers using Grafana on Ubuntu.md
new file mode 100644
index 0000000000..acdfff0b4f
--- /dev/null
+++ b/sources/tech/20160809 How to Monitor Docker Containers using Grafana on Ubuntu.md	
@@ -0,0 +1,265 @@
+Being translated by ChrisLeeGit
+How to Monitor Docker Containers using Grafana on Ubuntu
+================================================================================
+
+Grafana is an open source feature rich metrics dashboard. It is very useful for visualizing large-scale measurement data. It provides a powerful and elegant way to create, share, and explore data and dashboards from your disparate metric databases.
+
+It supports a wide variety of graphing options for ultimate flexibility. Furthermore, it supports many different storage backends for your Data Source. Each Data Source has a specific Query Editor that is customized for the features and capabilities that the particular Data Source exposes. The following datasources are officially supported by Grafana: Graphite, InfluxDB, OpenTSDB, Prometheus, Elasticsearch and Cloudwatch
+
+The query language and capabilities of each Data Source are obviously very different. You can combine data from multiple Data Sources onto a single Dashboard, but each Panel is tied to a specific Data Source that belongs to a particular Organization. It supports authenticated login and a basic role based access control implementation. It is deployed as a single software installation which is written in Go and Javascript.
+
+In this article, I'll explain on how to install Grafana on a docker container in Ubuntu 16.04 and configure docker monitoring using this software.
+
+### Pre-requisites ###
+
+- Docker installed server
+
+### Installing Grafana ###
+
+We can build our Grafana in a docker container. There is an official docker image available for building Grafana. Please run this command to build a Grafana container.
+
+```
+root@ubuntu:~# docker run -i -p 3000:3000 grafana/grafana
+
+Unable to find image 'grafana/grafana:latest' locally
+latest: Pulling from grafana/grafana
+5c90d4a2d1a8: Pull complete
+b1a9a0b6158e: Pull complete
+acb23b0d58de: Pull complete
+Digest: sha256:34ca2f9c7986cb2d115eea373083f7150a2b9b753210546d14477e2276074ae1
+Status: Downloaded newer image for grafana/grafana:latest
+t=2016-07-27T15:20:19+0000 lvl=info msg="Starting Grafana" logger=main version=3.1.0 commit=v3.1.0 compiled=2016-07-12T06:42:28+0000
+t=2016-07-27T15:20:19+0000 lvl=info msg="Config loaded from" logger=settings file=/usr/share/grafana/conf/defaults.ini
+t=2016-07-27T15:20:19+0000 lvl=info msg="Config loaded from" logger=settings file=/etc/grafana/grafana.ini
+t=2016-07-27T15:20:19+0000 lvl=info msg="Config overriden from command line" logger=settings arg="default.paths.data=/var/lib/grafana"
+t=2016-07-27T15:20:19+0000 lvl=info msg="Config overriden from command line" logger=settings arg="default.paths.logs=/var/log/grafana"
+t=2016-07-27T15:20:19+0000 lvl=info msg="Config overriden from command line" logger=settings arg="default.paths.plugins=/var/lib/grafana/plugins"
+t=2016-07-27T15:20:19+0000 lvl=info msg="Path Home" logger=settings path=/usr/share/grafana
+t=2016-07-27T15:20:19+0000 lvl=info msg="Path Data" logger=settings path=/var/lib/grafana
+t=2016-07-27T15:20:19+0000 lvl=info msg="Path Logs" logger=settings path=/var/log/grafana
+t=2016-07-27T15:20:19+0000 lvl=info msg="Path Plugins" logger=settings path=/var/lib/grafana/plugins
+t=2016-07-27T15:20:19+0000 lvl=info msg="Initializing DB" logger=sqlstore dbtype=sqlite3
+
+t=2016-07-27T15:20:20+0000 lvl=info msg="Executing migration" logger=migrator id="create playlist table v2"
+t=2016-07-27T15:20:20+0000 lvl=info msg="Executing migration" logger=migrator id="create playlist item table v2"
+t=2016-07-27T15:20:20+0000 lvl=info msg="Executing migration" logger=migrator id="drop preferences table v2"
+t=2016-07-27T15:20:20+0000 lvl=info msg="Executing migration" logger=migrator id="drop preferences table v3"
+t=2016-07-27T15:20:20+0000 lvl=info msg="Executing migration" logger=migrator id="create preferences table v3"
+t=2016-07-27T15:20:20+0000 lvl=info msg="Created default admin user: [admin]"
+t=2016-07-27T15:20:20+0000 lvl=info msg="Starting plugin search" logger=plugins
+t=2016-07-27T15:20:20+0000 lvl=info msg="Server Listening" logger=server address=0.0.0.0:3000 protocol=http subUrl=
+```
+
+We can confirm the working of the Grafana container by running this command `docker ps -a` or by accessing it by URL `http://Docker IP:3000`
+
+All Grafana configuration settings are defined using environment variables, this is much useful when using container technology. The Grafana configuration file is located at /etc/grafana/grafana.ini.
+
+### Understanding the Configuration ###
+
+The Grafana has number of configuration options that can be specified in its configuration file as .ini file or  can be specified using environment variables as mentioned before.
+
+#### Config file locations ####
+
+Normal config file locations.
+
+- Default configuration from : $WORKING_DIR/conf/defaults.ini
+- Custom configuration from  : $WORKING_DIR/conf/custom.ini
+
+PS :  When you install Grafana using the deb or rpm packages or docker images, then your configuration file is located at /etc/grafana/grafana.ini
+
+#### Understanding the config variables ####
+
+Let's see some of the variables in the configuration file below:
+
+`instance_name` : It's the name of the grafana server instance. It default value is fetched from ${HOSTNAME}, which will be replaced with environment variable HOSTNAME, if that is empty or does not exist Grafana will try to use system calls to get the machine name.
+
+`[paths]`
+
+`data` : It's the path where Grafana stores the sqlite3 database (when used), file based sessions (when used), and other data.
+
+`logs` : It's where Grafana stores the logs.
+
+Both these paths are usually specified via command line in the init.d scripts or the systemd service file.
+
+`[server]`
+
+`http_addr` : The IP address to bind the application. If it's left empty it will bind to all interfaces.
+
+`http_port` : The port to which the application is bind to, defaults is 3000. You can redirect your 80 port to 3000 using the below command.
+
+```
+$iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3000
+```
+
+`root_url` : This is the URL used to access Grafana from a web browser.
+
+`cert_file` : Path to the certificate file (if protocol is set to https).
+
+`cert_key` : Path to the certificate key file (if protocol is set to https).
+
+`[database]`
+
+Grafana uses a database to store its users and dashboards and other informations. By default it is configured to use sqlite3 which is an embedded database included in the main Grafana binary.
+
+`type`
+You can choose mysql, postgres or sqlite3 as per our requirement.
+
+`path`
+It's applicable only for sqlite3 database. The file path where the database will be stored.
+
+`host`
+It's applicable only to MySQL or Postgres. it includes IP or hostname and port. For example, for MySQL running on the same host as Grafana: host = 127.0.0.1:3306
+
+`name`
+The name of the Grafana database. Leave it set to grafana or some other name.
+
+`user`
+The database user (not applicable for sqlite3).
+
+`password`
+The database user's password (not applicable for sqlite3).
+
+`ssl_mode`
+For Postgres, use either disable, require or verify-full. For MySQL, use either true, false, or skip-verify.
+
+`ca_cert_path`
+(MySQL only) The path to the CA certificate to use. On many linux systems, certs can be found in /etc/ssl/certs.
+
+`client_key_path`
+(MySQL only) The path to the client key. Only if server requires client authentication.
+
+`client_cert_path`
+(MySQL only) The path to the client cert. Only if server requires client authentication.
+
+`server_cert_name`
+(MySQL only) The common name field of the certificate used by the mysql server. Not necessary if ssl_mode is set to skip-verify.
+
+`[security]`
+
+`admin_user` : It is the name of the default Grafana admin user. The default name set is admin.
+
+`admin_password` : It is the password of the default Grafana admin. It is set on first-run. The default password is admin.
+
+`login_remember_days` : The number of days the keep me logged in / remember me cookie lasts.
+
+`secret_key` : It is used for signing keep me logged in / remember me cookies.
+
+### Essentials components for setting up Monitoring ###
+
+We use the below components  to create our Docker Monitoring system.
+
+`cAdvisor` : It is otherwise called Container Advisor. It provides its users an understanding of the resource usage and performance characteristics. It collects, aggregates, processes and exports information about the running containers. You can go through this documentation for more information about this.
+
+`InfluxDB` : It is a time series, metrics, and analytic database. We use this datasource for setting up our monitoring. cAdvisor  displays only real time information and doesn’t store the metrics. Influx Db helps to store the monitoring information which cAdvisor provides in order to display a time range other than real time.
+
+`Grafana Dashboard` : It allows us to combine all the pieces of information together visually. This powerful Dashboard allows us to run queries against the data store InfluxDB and chart them accordingly in beautiful layout.
+
+### Installation of Docker Monitoring ###
+
+We need to install each of these components one by one in our docker system.
+
+#### Installing InfluxDB ####
+
+We can use this command to pull InfluxDB image and setuup a influxDB container.
+
+```
+root@ubuntu:~# docker run -d -p 8083:8083 -p 8086:8086 --expose 8090 --expose 8099 -e PRE_CREATE_DB=cadvisor --name influxsrv tutum/influxdb:0.8.8
+Unable to find image 'tutum/influxdb:0.8.8' locally
+0.8.8: Pulling from tutum/influxdb
+a3ed95caeb02: Already exists
+23efb549476f: Already exists
+aa2f8df21433: Already exists
+ef072d3c9b41: Already exists
+c9f371853f28: Already exists
+a248b0871c3c: Already exists
+749db6d368d0: Already exists
+7d7c7d923e63: Pull complete
+e47cc7808961: Pull complete
+1743b6eeb23f: Pull complete
+Digest: sha256:8494b31289b4dbc1d5b444e344ab1dda3e18b07f80517c3f9aae7d18133c0c42
+Status: Downloaded newer image for tutum/influxdb:0.8.8
+d3b6f7789e0d1d01fa4e0aacdb636c221421107d1df96808ecbe8e241ceb1823
+
+    -p 8083:8083 : user interface, log in with username-admin, pass-admin
+    -p 8086:8086 : interaction with other application
+    --name influxsrv : container have name influxsrv, use to cAdvisor link it.
+```
+
+You can test your InfluxDB installation by calling this URL >>http://45.79.148.234:8083 and login with user/password as "root".
+
+![InfluxDB Administration 2016-08-01 14-10-08](http://blog.linoxide.com/wp-content/uploads/2016/07/InfluxDB-Administration-2016-08-01-14-10-08-1-1024x530.png)
+
+We can create our required databases from this tab.
+
+![createDB influx](http://blog.linoxide.com/wp-content/uploads/2016/07/createDB-influx-1024x504.png)
+
+#### Installing cAdvisor ####
+
+Our next step is to  install cAdvisor container and link it to the InfluxDB container. You can use this command to create it.
+
+```
+root@ubuntu:~# docker run --volume=/:/rootfs:ro --volume=/var/run:/var/run:rw --volume=/sys:/sys:ro --volume=/var/lib/docker/:/var/lib/docker:ro --publish=8080:8080 --detach=true --link influxsrv:influxsrv --name=cadvisor google/cadvisor:latest -storage_driver_db=cadvisor -storage_driver_host=influxsrv:8086
+Unable to find image 'google/cadvisor:latest' locally
+latest: Pulling from google/cadvisor
+09d0220f4043: Pull complete
+151807d34af9: Pull complete
+14cd28dce332: Pull complete
+Digest: sha256:8364c7ab7f56a087b757a304f9376c3527c8c60c848f82b66dd728980222bd2f
+Status: Downloaded newer image for google/cadvisor:latest
+3bfdf7fdc83872485acb06666a686719983a1172ac49895cd2a260deb1cdde29
+root@ubuntu:~#
+
+    --publish=8080:8080 : user interface
+    --link=influxsrv:influxsrv: link to container influxsrv
+    -storage_driver=influxdb: set the storage driver as InfluxDB
+    Specify what InfluxDB instance to push data to:
+    -storage_driver_host=influxsrv:8086: The ip:port of the database. Default is ‘localhost:8086’
+    -storage_driver_db=cadvisor: database name. Uses db ‘cadvisor’ by default
+```
+
+You can test our cAdvisor installation by calling this URL >>http://45.79.148.234:8080. This will provide you the statistics of your Docker host and containers.
+
+![cAdvisor - Docker Containers 2016-08-01 14-24-18](http://blog.linoxide.com/wp-content/uploads/2016/07/cAdvisor-Docker-Containers-2016-08-01-14-24-18-776x1024.png)
+
+#### Installing the Grafana Dashboard ####
+
+Finally, we need to install the Grafana Dashboard and link to the InfluxDB. You can run this command to setup that.
+
+```
+root@ubuntu:~# docker run -d -p 3000:3000 -e INFLUXDB_HOST=localhost -e INFLUXDB_PORT=8086 -e INFLUXDB_NAME=cadvisor -e INFLUXDB_USER=root -e INFLUXDB_PASS=root --link influxsrv:influxsrv --name grafana grafana/grafana
+f3b7598529202b110e4e6b998dca6b6e60e8608d75dcfe0d2b09ae408f43684a
+```
+
+Now we can login to Grafana and configure the Data Sources. Navigate to http://45.79.148.234:3000 or just http://45.79.148.234:
+
+Username - admin
+Password - admin
+
+Once we've installed Grafana, we can connect the InfluxDB. Login on the Dashboard and click on the Grafana icon(Fireball) in the upper left hand corner of the panel. Click on Data Sources to configure.
+
+![addingdatabsource](http://blog.linoxide.com/wp-content/uploads/2016/08/addingdatabsource-1-1024x804.png)
+
+Now you can add our new Graph to our default Datasource InfluxDB.
+
+![panelgraph](http://blog.linoxide.com/wp-content/uploads/2016/08/panelgraph-1024x576.png)
+
+We can edit and modify our query by adjusting our graph at Metric tab.
+
+![Grafana - Grafana Dashboard 2016-08-01 14-53-40](http://blog.linoxide.com/wp-content/uploads/2016/08/Grafana-Grafana-Dashboard-2016-08-01-14-53-40-1024x504.png)
+
+![Grafana - Grafana Dashboard](http://blog.linoxide.com/wp-content/uploads/2016/08/Grafana-Grafana-Dashboard-1024x509.png)
+
+You can get [more information][1] on docker monitoring here. Thank you for reading this. I would suggest your valuable comments and suggestions on this. Hope you'd a wonderful day!
+
+--------------------------------------------------------------------------------
+
+via: http://linoxide.com/linux-how-to/monitor-docker-containers-grafana-ubuntu/
+
+作者:[Saheetha Shameer][a]
+译者:[ChrisLeeGit](https://github.com/chrisleegit)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]:http://linoxide.com/author/saheethas/
+[1]:https://github.com/vegasbrianc/docker-monitoring
diff --git a/sources/tech/20160811 5 best linux init system.md b/sources/tech/20160811 5 best linux init system.md
new file mode 100644
index 0000000000..2296b6d751
--- /dev/null
+++ b/sources/tech/20160811 5 best linux init system.md	
@@ -0,0 +1,104 @@
+hkurj translating  
+I Best Modern Linux ‘init’ Systems (1992-2015)
+============================================
+
+In Linux and other Unix-like operating systems, the init (initialization) process is the first process executed by the kernel at boot time. It has a process ID (PID) of 1, it is executed in the background until the system is shut down.
+
+The init process starts all other processes, that is daemons, services and other background processes, therefore, it is the mother of all other processes on the system. A process can start many other child processes on the system, but in the event that a parent process dies, init becomes the parent of the orphan process.
+
+![](http://www.tecmint.com/wp-content/uploads/2016/08/Linux-init-Systems.png)
+
+Over the years, many init systems have emerged in major Linux distributions and in this guide, we shall take a look at some of the best init systems you can work with on the Linux operating system.
+
+### 1. System V Init
+
+System V (SysV) is a mature and popular init scheme on Unix-like operating systems, it is the parent of all processes on a Unix/Linux system. SysV is the first commercial Unix operating system designed.
+
+Almost all Linux distributions first used SysV init scheme except Gentoo which has a custom init and Slackware using BSD-style init scheme.
+
+As years have passed by, due to some imperfections, several SysV init replacements have been developed in quests to create more efficient and perfect init systems for Linux.
+
+Although these alternatives seek to improve SysV and probably offer new features, they are still compatible with original SysV init scripts.
+
+### 2. SystemD
+
+SystemD is a relatively new init scheme on the Linux platform. Introduced in Fedora 15, its is an assortment of tools for easy system management. The main purpose is to initialize, manage and keep track of all system processes in the boot process and while the system is running.
+
+Systemd init is comprehensively distinct from other traditional Unix init systems, in the way it practically approaches system and services management. It is also compatible with SysV and LBS init scripts.
+
+It has some of the following eminent features:
+
+- Clean, straightforward and efficient design
+- Concurrent and parallel processing at bootup
+- Better APIv
+- Enables removal of optional processes
+- Supports event logging using journald
+- Supports job scheduling using systemd calender timers
+- Storage of logs in binary files
+- Preservation of systemd state for future reference
+- Better integration with GNOME plus many more
+
+Read the Systemd init Overview: <https://fedoraproject.org/wiki/Systemd>
+
+### 3. Upstart
+
+Upstart is an event-based init system developed by makers of Ubuntu as a replacement for SysV init system. It starts different system tasks and processes, inspects them while the system is running and stops them during system shut down.
+
+It is a hybrid init system which uses both SysV startup scripts and also Systemd scripts, some of the notable features of Upstart init system include:
+
+- Originally developed for Ubuntu Linux but can run on all other distributions
+- Event-based starting and stopping of tasks and services
+- Events are generated during starting and stopping of tasks and services
+- Events can be sent by other system processes
+- Communication with init process through D-Bus
+- Users can start and stop their own processes
+- Re-spawning of services that die abruptly and many more
+
+Visit Homepage: <http://upstart.ubuntu.com/index.html>
+
+### 4. OpenRC
+
+OpenRC is a dependency-based init scheme for Unix-like operating systems, it is compatible with SysV init. As much as it brings some improvements to Sys V, you must keep in mind that OpenRC is not an absolute replacement for /sbin/init file.
+
+It offers some illustrious features and these include:
+
+- It can run on other many Linux distributions including Gentoo and also on BSD
+- Supports hardware initiated init scripts
+- Supports a single configuration file
+- No per-service configurations supported
+- Runs as a daemon
+- Parallel services startup and many more
+
+Visit Homepage: <https://wiki.gentoo.org/wiki/OpenRC>
+
+### 5. runit
+
+runit is also a cross-platform init system that can run on GNU/Linux, Solaris, *BSD and Mac OS X and it is an alternative for SysV init, that offers service supervision.
+
+It comes with some benefits and remarkable components not found in SysV init and possibly other init systems in Linux and these include:
+
+Service supervision, where each service is associated with a service directory
+Clean process state, it guarantees each process a clean state
+It has a reliable logging facility
+Fast system boot up and shutdown
+It is also portable
+Packaging friendly
+Small code size and many more
+Visit Homepage: <http://smarden.org/runit/>
+
+As I had earlier on mentioned, the init system starts and manages all other processes on a Linux system. Additionally, SysV is the primary init scheme on Linux operating systems, but due to some performance weaknesses, system programmers have developed several replacements for it.
+
+And here, we looked at a few of those replacements, but there could be other init systems that you think are worth mentioning in this list. You can let us know of them via the comment section below.
+
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/best-linux-init-systems/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+tecmint+%28Tecmint%3A+Linux+Howto%27s+Guide%29
+
+作者:[Aaron Kili ][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
diff --git a/sources/tech/20160811 How to Mount Remote Linux Filesystem or Directory Using SSHFS Over SSH.md b/sources/tech/20160811 How to Mount Remote Linux Filesystem or Directory Using SSHFS Over SSH.md
new file mode 100644
index 0000000000..3ba803ca1d
--- /dev/null
+++ b/sources/tech/20160811 How to Mount Remote Linux Filesystem or Directory Using SSHFS Over SSH.md	
@@ -0,0 +1,146 @@
+Translating by cposture
+How to Mount Remote Linux Filesystem or Directory Using SSHFS Over SSH
+============================
+
+The main purpose of writing this article is to provide a step-by-step guide on how to mount remote Linux file system using SSHFS client over SSH.
+
+This article is useful for those users and system administrators who want to mount remote file system on their local systems for whatever purposes. We have practically tested by installing SSHFS client on one of our Linux system and successfully mounted remote file systems.
+
+Before we go further installation let’s understand about SSHFS and how it works.
+
+![](http://www.tecmint.com/wp-content/uploads/2012/08/Sshfs-Mount-Remote-Linux-Filesystem-Directory.png)
+>Sshfs Mount Remote Linux Filesystem or Directory
+
+### What Is SSHFS?
+
+SSHFS stands for (Secure SHell FileSystem) client that enable us to mount remote filesystem and interact with remote directories and files on a local machine using SSH File Transfer Protocol (SFTP).
+
+SFTP is a secure file transfer protocol that provides file access, file transfer and file management features over Secure Shell protocol. Because SSH uses encryption while transferring files over the network from one computer to another computer and SSHFS comes with built-in FUSE (Filesystem in Userspace) kernel module that allows any non-privileged users to create their file system without modifying kernel code.
+
+In this article, we will show you how to install and use SSHFS client on any Linux distribution to mount remote Linux filesystem or directory on a local Linux machine.
+
+#### Step 1: Install SSHFS Client in Linux Systems
+
+By default sshfs packages does not exists on all major Linux distributions, you need to enable [epel repository][1] under your Linux systems to install sshfs with the help of Yum command with their dependencies.
+
+```
+# yum install sshfs
+# dnf install sshfs              [On Fedora 22+ releases]
+$ sudo apt-get install sshfs     [On Debian/Ubuntu based systems]
+```
+
+#### Step 2: Creating SSHFS Mount Directory
+
+Once the sshfs package installed, you need to create a mount point directory where you will mount your remote file system. For example, we have created mount directory under /mnt/tecmint.
+
+```
+# mkdir /mnt/tecmint
+$ sudo mkdir /mnt/tecmint     [On Debian/Ubuntu based systems]
+```
+
+### Step 3: Mounting Remote Filesystem with SSHFS
+
+Once you have created your mount point directory, now run the following command as a root user to mount remote file system under /mnt/tecmint. In your case the mount directory would be anything.
+
+The following command will mount remote directory called /home/tecmint under /mnt/tecmint in local system. (Don’t forget replace x.x.x.x with your IP Address and mount point).
+
+```
+# sshfs tecmint@x.x.x.x:/home/tecmint/ /mnt/tecmint
+$ sudo sshfs -o allow_other tecmint@x.x.x.x:/home/tecmint/ /mnt/tecmint     [On Debian/Ubuntu based systems]
+```
+
+If your Linux server is configured with SSH key based authorization, then you will need to specify the path to your public keys as shown in the following command.
+
+```
+# sshfs -o IdentityFile=~/.ssh/id_rsa tecmint@x.x.x.x:/home/tecmint/ /mnt/tecmint
+$ sudo sshfs -o allow_other,IdentityFile=~/.ssh/id_rsa tecmint@x.x.x.x:/home/tecmint/ /mnt/tecmint     [On Debian/Ubuntu based systems]
+```
+
+#### Step 4: Verifying Remote Filesystem is Mounted
+
+If you have run the above command successfully without any errors, you will see the list of remote files and directories mounted under /mnt/tecmint.
+
+```
+# cd /mnt/tecmint
+# ls
+[root@ tecmint]# ls
+12345.jpg                       ffmpeg-php-0.6.0.tbz2                Linux                                           news-closeup.xsl     s3.jpg
+cmslogs                         gmd-latest.sql.tar.bz2               Malware                                         newsletter1.html     sshdallow
+epel-release-6-5.noarch.rpm     json-1.2.1                           movies_list.php                                 pollbeta.sql
+ffmpeg-php-0.6.0                json-1.2.1.tgz                       my_next_artical_v2.php                          pollbeta.tar.bz2
+```
+
+#### Step 5: Checking Mount Point with df -hT Command
+
+If you run df -hT command you will see the remote file system mount point.
+
+```
+# df -hT
+```
+
+Sample Output
+
+```
+Filesystem                          Type        Size  Used Avail Use% Mounted on
+udev                                devtmpfs    730M     0  730M   0% /dev
+tmpfs                               tmpfs       150M  4.9M  145M   4% /run
+/dev/sda1                           ext4         31G  5.5G   24G  19% /
+tmpfs                               tmpfs       749M  216K  748M   1% /dev/shm
+tmpfs                               tmpfs       5.0M  4.0K  5.0M   1% /run/lock
+tmpfs                               tmpfs       749M     0  749M   0% /sys/fs/cgroup
+tmpfs                               tmpfs       150M   44K  150M   1% /run/user/1000
+tecmint@192.168.0.102:/home/tecmint fuse.sshfs  324G   55G  253G  18% /mnt/tecmint
+```
+
+#### Step 6: Mounting Remote Filesystem Permanently
+
+To mount remote filesystem permanently, you need to edit the file called /etc/fstab. To do, open the file with your favorite editor.
+
+```
+# vi /etc/fstab
+$ sudo vi /etc/fstab     [On Debian/Ubuntu based systems]         
+```
+
+Go to the bottom of the file and add the following line to it and save the file and exit. The below entry mount remote server file system with default settings.
+
+```
+sshfs#tecmint@x.x.x.x:/home/tecmint/ /mnt/tecmint fuse.sshfs defaults 0 0
+```
+
+Make sure you’ve [SSH Passwordless Login][2] in place between servers to auto mount filesystem during system reboots..
+
+```
+sshfs#tecmint@x.x.x.x:/home/tecmint/ /mnt/tecmint fuse.sshfs IdentityFile=~/.ssh/id_rsa defaults 0 0
+```
+
+Next, you need to update the fstab file to reflect the changes.
+
+```
+# mount -a
+$ sudo mount -a   [On Debian/Ubuntu based systems]
+```
+
+#### Step 7: Unmounting Remote Filesystem
+
+To unmount remote filesystem, jun issue the following command it will unmount the remote file system.
+
+```
+# umount /mnt/tecmint
+```
+
+That’s all for now, if you’re facing any difficulties or need any help in mounting remote file system, please contact us via comments and if you feel this article is much useful then share it with your friends.
+
+
+-------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/sshfs-mount-remote-linux-filesystem-directory-using-ssh/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+tecmint+%28Tecmint%3A+Linux+Howto%27s+Guide%29
+
+作者:[Ravi Saive][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/admin/
+[1]: http://www.tecmint.com/how-to-enable-epel-repository-for-rhel-centos-6-5/
+[2]: http://www.tecmint.com/ssh-passwordless-login-using-ssh-keygen-in-5-easy-steps/
diff --git a/sources/tech/20160813 Journey-to-HTTP2.md b/sources/tech/20160813 Journey-to-HTTP2.md
new file mode 100644
index 0000000000..e75737f57e
--- /dev/null
+++ b/sources/tech/20160813 Journey-to-HTTP2.md	
@@ -0,0 +1,215 @@
+NearTan 认领
+
+Journey to HTTP/2
+===================
+
+It has been quite some time since I last wrote through my blog and the reason is not being able to find enough time to put into it. I finally got some time today and thought to put some of it writing about HTTP.
+
+HTTP is the protocol that every web developer should know as it powers the whole web and knowing it is definitely going to help you develop better applications.
+
+In this article, I am going to be discussing what HTTP is, how it came to be, where it is today and how did we get here.
+
+### What is HTTP?
+
+First things first, what is HTTP? HTTP is the TCP/IP based application layer communication protocol which standardizes how the client and server communicate with each other. It defines how the content is requested and transmitted across the internet. By application layer protocol, I mean it’s just an abstraction layer that standardizes how the hosts (clients and servers) communicate and itself it depends upon TCP/IP to get request and response between the client and server. By default TCP port 80 is used but other ports can be used as well. HTTPS, however, uses port 443.
+
+#### HTTP/0.9 - The One Liner (1991)
+
+The first documented version of HTTP was HTTP/0.9 which was put forward in 1991. It was the simplest protocol ever; having a single method called GET. If a client had to access some webpage on the server, it would have made the simple request like below
+
+```
+GET /index.html
+```
+
+And the response from server would have looked as follows
+
+```
+(response body)
+(connection closed)
+```
+
+That is, the server would get the request, reply with the HTML in response and as soon as the content has been transferred, the connection will be closed. There were
+
+- No headers
+- GET was the only allowed method
+- Response had to be HTML
+
+As you can see, the protocol really had nothing more than being a stepping stone for what was to come.
+
+#### HTTP/1.0 - 1996
+
+In 1996, the next version of HTTP i.e. HTTP/1.0 evolved that vastly improved over the original version.
+
+Unlike HTTP/0.9 which was only designed for HTML response, HTTP/1.0 could now deal with other response formats i.e. images, video files, plain text or any other content type as well. It added more methods (i.e. POST and HEAD), request/response formats got changed, HTTP headers got added to both the request and responses, status codes were added to identify the response, character set support was introduced, multi-part types, authorization, caching, content encoding and more was included.
+
+Here is how a sample HTTP/1.0 request and response might have looked like:
+
+```
+GET / HTTP/1.0
+Host: kamranahmed.info
+User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5)
+Accept: */*
+```
+
+As you can see, alongside the request, client has also sent it’s personal information, required response type etc. While in HTTP/0.9 client could never send such information because there were no headers.
+
+Example response to the request above may have looked like below
+
+```
+HTTP/1.0 200 OK 
+Content-Type: text/plain
+Content-Length: 137582
+Expires: Thu, 05 Dec 1997 16:00:00 GMT
+Last-Modified: Wed, 5 August 1996 15:55:28 GMT
+Server: Apache 0.84
+
+(response body)
+(connection closed)
+```
+
+In the very beginning of the response there is HTTP/1.0 (HTTP followed by the version number), then there is the status code 200 followed by the reason phrase (or description of the status code, if you will).
+
+In this newer version, request and response headers were still kept as ASCII encoded, but the response body could have been of any type i.e. image, video, HTML, plain text or any other content type. So, now that server could send any content type to the client; not so long after the introduction, the term “Hyper Text” in HTTP became misnomer. HMTP or Hypermedia transfer protocol might have made more sense but, I guess, we are stuck with the name for life.
+
+One of the major drawbacks of HTTP/1.0 were you couldn’t have multiple requests per connection. That is, whenever a client will need something from the server, it will have to open a new TCP connection and after that single request has been fulfilled, connection will be closed. And for any next requirement, it will have to be on a new connection. Why is it bad? Well, let’s assume that you visit a webpage having 10 images, 5 stylesheets and 5 javascript files, totalling to 20 items that needs to fetched when request to that webpage is made. Since the server closes the connection as soon as the request has been fulfilled, there will be a series of 20 separate connections where each of the items will be served one by one on their separate connections. This large number of connections results in a serious performance hit as requiring a new TCP connection imposes a significant performance penalty because of three-way handshake followed by slow-start.
+
+### Three-way Handshake
+
+Three-way handshake in it’s simples form is that all the TCP connections begin with a three-way handshake in which the client and the server share a series of packets before starting to share the application data.
+
+- SYN - Client picks up a random number, let’s say x, and sends it to the server.
+- SYN ACK - Server acknowledges the request by sending an ACK packet back to the client which is made up of a random number, let’s say y picked up by server and the number x+1 where x is the number that was sent by the client
+- ACK - Client increments the number y received from the server and sends an ACK packet back with the number x+1
+
+Once the three-way handshake is completed, the data sharing between the client and server may begin. It should be noted that the client may start sending the application data as soon as it dispatches the last ACK packet but the server will still have to wait for the ACK packet to be recieved in order to fulfill the request.
+
+![](http://i.imgur.com/uERG2G2.png)
+
+However, some implementations of HTTP/1.0 tried to overcome this issue by introducing a new header called Connection: keep-alive which was meant to tell the server “Hey server, do not close this connection, I need it again”. But still, it wasn’t that widely supported and the problem still persisted.
+
+Apart from being connectionless, HTTP also is a stateless protocol i.e. server doesn’t maintain the information about the client and so each of the requests has to have the information necessary for the server to fulfill the request on it’s own without any association with any old requests. And so this adds fuel to the fire i.e. apart from the large number of connections that the client has to open, it also has to send some redundant data on the wire causing increased bandwidth usage.
+
+#### HTTP/1.1 - 1999
+
+After merely 3 years of HTTP/1.0, the next version i.e. HTTP/1.1 was released in 1999; which made alot of improvements over it’s predecessor. The major improvements over HTTP/1.0 included
+
+- New HTTP methods were added, which introduced PUT, PATCH, HEAD, OPTIONS, DELETE
+
+- Hostname Identification In HTTP/1.0 Host header wasn’t required but HTTP/1.1 made it required.
+
+- Persistent Connections As discussed above, in HTTP/1.0 there was only one request per connection and the connection was closed as soon as the request was fulfilled which resulted in accute performance hit and latency problems. HTTP/1.1 introduced the persistent connections i.e. connections weren’t closed by default and were kept open which allowed multiple sequential requests. To close the connections, the header Connection: close had to be available on the request. Clients usually send this header in the last request to safely close the connection.
+
+- Pipelining It also introduced the support for pipelining, where the client could send multiple requests to the server without waiting for the response from server on the same connection and server had to send the response in the same sequence in which requests were received. But how does the client know that this is the point where first response download completes and the content for next response starts, you may ask! Well, to solve this, there must be Content-Length header present which clients can use to identify where the response ends and it can start waiting for the next response.
+
+>It should be noted that in order to benefit from persistent connections or pipelining, Content-Length header must be available on the response, because this would let the client know when the transmission completes and it can send the next request (in normal sequential way of sending requests) or start waiting for the the next response (when pipelining is enabled).
+
+>But there was still an issue with this approach. And that is, what if the data is dynamic and server cannot find the content length before hand? Well in that case, you really can’t benefit from persistent connections, could you?! In order to solve this HTTP/1.1 introduced chunked encoding. In such cases server may omit content-Length in favor of chunked encoding (more to it in a moment). However, if none of them are available, then the connection must be closed at the end of request.
+
+- Chunked Transfers In case of dynamic content, when the server cannot really find out the Content-Length when the transmission starts, it may start sending the content in pieces (chunk by chunk) and add the Content-Length for each chunk when it is sent. And when all of the chunks are sent i.e. whole transmission has completed, it sends an empty chunk i.e. the one with Content-Length set to zero in order to identify the client that transmission has completed. In order to notify the client about the chunked transfer, server includes the header Transfer-Encoding: chunked
+
+- Unlike HTTP/1.0 which had Basic authentication only, HTTP/1.1 included digest and proxy authentication
+- Caching
+- Byte Ranges
+- Character sets
+- Language negotiation
+- Client cookies
+- Enhanced compression support
+- New status codes
+- ..and more
+
+I am not going to dwell about all the HTTP/1.1 features in this post as it is a topic in itself and you can already find a lot about it. The one such document that I would recommend you to read is Key differences between HTTP/1.0 and HTTP/1.1 and here is the link to original RFC for the overachievers.
+
+HTTP/1.1 was introduced in 1999 and it had been a standard for many years. Although, it improved alot over it’s predecessor; with the web changing everyday, it started to show it’s age. Loading a web page these days is more resource-intensive than it ever was. A simple webpage these days has to open more than 30 connections. Well HTTP/1.1 has persistent connections, then why so many connections? you say! The reason is, in HTTP/1.1 it can only have one outstanding connection at any moment of time. HTTP/1.1 tried to fix this by introducing pipelining but it didn’t completely address the issue because of the head-of-line blocking where a slow or heavy request may block the requests behind and once a request gets stuck in a pipeline, it will have to wait for the next requests to be fulfilled. To overcome these shortcomings of HTTP/1.1, the developers started implementing the workarounds, for example use of spritesheets, encoded images in CSS, single humungous CSS/Javascript files, domain sharding etc.
+
+#### SPDY - 2009
+
+Google went ahead and started experimenting with alternative protocols to make the web faster and improving web security while reducing the latency of web pages. In 2009, they announced SPDY.
+
+>SPDY is a trademark of Google and isn’t an acronym.
+
+It was seen that if we keep increasing the bandwidth, the network performance increases in the beginning but a point comes when there is not much of a performance gain. But if you do the same with latency i.e. if we keep dropping the latency, there is a constant performance gain. This was the core idea for performance gain behind SPDY, decrease the latency to increase the network performance.
+
+>For those who don’t know the difference, latency is the delay i.e. how long it takes for data to travel between the source and destination (measured in milliseconds) and bandwidth is the amount of data transfered per second (bits per second).
+
+The features of SPDY included, multiplexing, compression, prioritization, security etc. I am not going to get into the details of SPDY, as you will get the idea when we get into the nitty gritty of HTTP/2 in the next section as I said HTTP/2 is mostly inspired from SPDY.
+
+SPDY didn’t really try to replace HTTP; it was a translation layer over HTTP which existed at the application layer and modified the request before sending it over to the wire. It started to become a defacto standards and majority of browsers started implementing it.
+
+In 2015, at Google, they didn’t want to have two competing standards and so they decided to merge it into HTTP while giving birth to HTTP/2 and deprecating SPDY.
+
+#### HTTP/2 - 2015
+
+By now, you must be convinced that why we needed another revision of the HTTP protocol. HTTP/2 was designed for low latency transport of content. The key features or differences from the old version of HTTP/1.1 include
+
+- Binary instead of Textual
+- Multiplexing - Multiple asynchronous HTTP requests over a single connection
+- Header compression using HPACK
+- Server Push - Multiple responses for single request
+- Request Prioritization
+- Security
+
+![](http://i.imgur.com/S85j8gg.png)
+
+##### 1. Binary Protocol
+
+HTTP/2 tends to address the issue of increased latency that existed in HTTP/1.x by making it a binary protocol. Being a binary protocol, it easier to parse but unlike HTTP/1.x it is no longer readable by the human eye. The major building blocks of HTTP/2 are Frames and Streams
+
+**Frames and Streams**
+
+HTTP messages are now composed of one or more frames. There is a HEADERS frame for the meta data and DATA frame for the payload and there exist several other types of frames (HEADERS, DATA, RST_STREAM, SETTINGS, PRIORITY etc) that you can check through the HTTP/2 specs.
+
+Every HTTP/2 request and response is given a unique stream ID and it is divided into frames. Frames are nothing but binary pieces of data. A collection of frames is called a Stream. Each frame has a stream id that identifies the stream to which it belongs and each frame has a common header. Also, apart from stream ID being unique, it is worth mentioning that, any request initiated by client uses odd numbers and the response from server has even numbers stream IDs.
+
+Apart from the HEADERS and DATA, another frame type that I think worth mentioning here is RST_STREAM which is a special frame type that is used to abort some stream i.e. client may send this frame to let the server know that I don’t need this stream anymore. In HTTP/1.1 the only way to make the server stop sending the response to client was closing the connection which resulted in increased latency because a new connection had to be opened for any consecutive requests. While in HTTP/2, client can use RST_STREAM and stop receiving a specific stream while the connection will still be open and the other streams will still be in play.
+
+##### 2. Multiplexing
+
+Since HTTP/2 is now a binary protocol and as I said above that it uses frames and streams for requests and responses, once a TCP connection is opened, all the streams are sent asynchronously through the same connection without opening any additional connections. And in turn, the server responds in the same asynchronous way i.e. the response has no order and the client uses the assigned stream id to identify the stream to which a specific packet belongs. This also solves the head-of-line blocking issue that existed in HTTP/1.x i.e. the client will not have to wait for the request that is taking time and other requests will still be getting processed.
+
+##### 3. HPACK Header Compression
+
+It was part of a separate RFC which was specifically aimed at optimizing the sent headers. The essence of it is that when we are constantly accessing the server from a same client there is alot of redundant data that we are sending in the headers over and over, and sometimes there might be cookies increasing the headers size which results in bandwidth usage and increased latency. To overcome this, HTTP/2 introduced header compression.
+
+![](http://i.imgur.com/3IPWXvR.png)
+
+Unlike request and response, headers are not compressed in gzip or compress etc formats but there is a different mechanism in place for header compression which is literal values are encoded using Huffman code and a headers table is maintained by the client and server and both the client and server omit any repetitive headers (e.g. user agent etc) in the subsequent requests and reference them using the headers table maintained by both.
+
+While we are talking headers, let me add here that the headers are still the same as in HTTP/1.1, except for the addition of some pseudo headers i.e. :method, :scheme, :host and :path
+
+##### 4. Server Push
+
+Server push is another tremendous feature of HTTP/2 where the server, knowing that the client is going to ask for a certain resource, can push it to the client without even client asking for it. For example, let’s say a browser loads a web page, it parses the whole page to find out the remote content that it has to load from the server and then sends consequent requests to the server to get that content.
+
+Server push allows the server to decrease the roundtrips by pushing the data that it knows that client is going to demand. How it is done is, server sends a special frame called PUSH_PROMISE notifying the client that, “Hey, I am about to send this resource to you! Do not ask me for it.” The PUSH_PROMISE frame is associated with the stream that caused the push to happen and it contains the promised stream ID i.e. the stream on which the server will send the resource to be pushed.
+
+##### 5. Request Prioritization
+
+A client can assign a priority to a stream by including the prioritization information in the HEADERS frame by which a stream is opened. At any other time, client can send a PRIORITY frame to change the priority of a stream.
+
+Without any priority information, server processes the requests asynchronously i.e. without any order. If there is priority assigned to a stream, then based on this prioritization information, server decides how much of the resources need to be given to process which request.
+
+##### 6. Security
+
+There was extensive discussion on whether security (through TLS) should be made mandatory for HTTP/2 or not. In the end, it was decided not to make it mandatory. However, most vendors stated that they will only support HTTP/2 when it is used over TLS. So, although HTTP/2 doesn’t require encryption by specs but it has kind of become mandatory by default anyway. With that out of the way, HTTP/2 when implemented over TLS does impose some requirementsi.e. TLS version 1.2 or higher must be used, there must be a certain level of minimum keysizes, ephemeral keys are required etc.
+
+HTTP/2 is here and it has already surpassed SPDY in adaption which is gradually increasing. HTTP/2 has alot to offer in terms of performance gain and it is about time we should start using it.
+
+For anyone interested in further details here is the [link to specs][1] and a [link demonstrating the performance benefits of][2] HTTP/2. For any questions or comments, use the comments section below. Also, while reading, if you find any blatant lies; do point them out.
+
+And that about wraps it up. Until next time! stay tuned.
+
+--------------------------------------------------------------------------------
+
+via: http://kamranahmed.info/blog/2016/08/13/http-in-depth/?utm_source=webopsweekly&utm_medium=email
+
+作者:[Kamran Ahmed][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://github.com/kamranahmedse
+
+[1]: https://http2.github.io/http2-spec
+[2]: http://www.http2demo.io/
+
diff --git a/sources/tech/20160816 Deploying React with Zero Configuration.md b/sources/tech/20160816 Deploying React with Zero Configuration.md
new file mode 100644
index 0000000000..16b53e461c
--- /dev/null
+++ b/sources/tech/20160816 Deploying React with Zero Configuration.md	
@@ -0,0 +1,70 @@
+翻译中--byzky001
+Deploying React with Zero Configuration
+========================
+
+So you want to build an app with [React][1]? "[Getting started][2]" is easy… and then what?
+
+React is a library for building user interfaces, which comprise only one part of an app. Deciding on all the other parts — styles, routers, npm modules, ES6 code, bundling and more — and then figuring out how to use them is a drain on developers. This has become known as [javascript fatigue][3]. Despite this complexity, usage of React continues to grow.
+
+The community answers this challenge by sharing boilerplates. These [boilerplates][4] reveal the profusion of architectural choices developers must make. That official "Getting Started" seems so far away from the reality of an operational app.
+
+### New, Zero-configuration Experience
+
+Inspired by the cohesive developer experience provided by [Ember.js][5] and [Elm][6], the folks at Facebook wanted to provide an easy, opinionated way forward. They created a new way to develop React apps, `create-react-app`. In the three weeks since initial public release, it has received tremendous community awareness (over 8,000 GitHub stargazers) and support (dozens of pull requests).
+
+`create-react-app` is different than many past attempts with boilerplates and starter kits. It targets zero configuration [[convention-over-configuration]][7], focusing the developer on what is interesting and different about their application.
+
+A powerful side-effect of zero configuration is that the tools can now evolve in the background. Zero configuration lays the foundation for the tools ecosystem to create automation and delight developers far beyond React itself.
+
+### Zero-configuration Deploy to Heroku
+
+Thanks to the zero-config foundation of create-react-app, the idea of zero-config deployment seemed within reach. Since these new apps all share a common, implicit architecture, the build process can be automated and then served with intelligent defaults. So, [we created this community buildpack to experiment with no-configuration deployment to Heroku][8].
+
+#### Create and Deploy a React App in Two Minutes
+
+You can get started building React apps for free on Heroku.
+
+```
+npm install -g create-react-app
+create-react-app my-app
+cd my-app
+git init
+heroku create -b https://github.com/mars/create-react-app-buildpack.git
+git add .
+git commit -m "react-create-app on Heroku"
+git push heroku master
+heroku open
+```
+
+Try it yourself [using the buildpack docs][9].
+
+### Growing Up from Zero Config
+
+create-react-app is very new (currently version 0.2) and since its target is a crystal-clear developer experience, more advanced use cases are not supported (or may never be supported). For example, it does not provide server-side rendering or customized bundles.
+
+To support greater control, create-react-app includes the command npm run eject. Eject unpacks all the tooling (config files and package.json dependencies) into the app's directory, so you can customize to your heart's content. Once ejected, changes you make may necessitate switching to a custom deployment with Node.js and/or static buildpacks. Always perform such project changes through a branch / pull request, so they can be easily undone. Heroku's Review Apps are perfect for testing changes to the deployment.
+
+We'll be tracking progress on create-react-app and adapting the buildpack to support more advanced use cases as they become available. Happy deploying!
+
+
+
+--------------------------------------------------------------------------------
+
+via: https://blog.heroku.com/deploying-react-with-zero-configuration?c=7013A000000NnBFQA0&utm_campaign=Display%20-%20Endemic%20-Cooper%20-Node%20-%20Blog%20-%20Zero-Configuration&utm_medium=display&utm_source=cooperpress&utm_content=blog&utm_term=node
+
+作者:[Mars Hall][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://blog.heroku.com/deploying-react-with-zero-configuration?c=7013A000000NnBFQA0&utm_campaign=Display%20-%20Endemic%20-Cooper%20-Node%20-%20Blog%20-%20Zero-Configuration&utm_medium=display&utm_source=cooperpress&utm_content=blog&utm_term=node
+[1]: https://facebook.github.io/react/
+[2]: https://facebook.github.io/react/docs/getting-started.html
+[3]: https://medium.com/@ericclemmons/javascript-fatigue-48d4011b6fc4
+[4]: https://github.com/search?q=react+boilerplate
+[5]: http://emberjs.com/
+[6]: http://elm-lang.org/
+[7]: http://rubyonrails.org/doctrine/#convention-over-configuration
+[8]: https://github.com/mars/create-react-app-buildpack
+[9]: https://github.com/mars/create-react-app-buildpack#usage
diff --git a/sources/tech/20160817 Building a Real-Time Recommendation Engine with Data Science.md b/sources/tech/20160817 Building a Real-Time Recommendation Engine with Data Science.md
new file mode 100644
index 0000000000..235ef44e25
--- /dev/null
+++ b/sources/tech/20160817 Building a Real-Time Recommendation Engine with Data Science.md	
@@ -0,0 +1,201 @@
+JianhuanZhuo
+
+Building a Real-Time Recommendation Engine with Data Science
+======================
+
+Editor’s Note: This presentation was given by Nicole White at GraphConnect Europe in April 2016. Here’s a quick review of what she covered:
+- [Basic graph-powered recommendations][1]
+- [Social recommendations][2]
+- [Similarity recommendations][3]
+- [Cluster recommendations][4]
+
+What we’re going to be talking about today is data science and graph recommendations:
+
+I’ve been with Neo4j for two years now, but have been working with Neo4j and Cypher for three. I discovered this particular graph database when I was a grad student at the University of Texas Austin studying for a masters in statistics with a focus on social networks.
+
+[Real-time recommendation engines][5] are one of the most common use cases for Neo4j, and one of the things that makes it so powerful and easy to use. To explore this, I’ll explain how to incorporate statistical methods into these recommendations by using example datasets.
+
+The first will be simple – entirely in Cypher with a focus on social recommendations. Next we’ll look at the similarity recommendation, which involves similarity metrics that can be calculated, and finally a clustering recommendation.
+
+### Basic Graph-Powered Recommendations
+
+The following dataset includes food and drink places in the Dallas Fort Worth International Airport, one of the major airport hubs in the United States:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816214938/recommendation-engine-data-science-nicole-white-graphconnect-768x432.png)
+
+We have place nodes in yellow and are modeling their location in terms of gate and terminal. And we are also categorizing the place in terms of major categories for food and drink. Some include Mexican food, sandwiches, bars and barbecue.
+
+Let’s do a simple recommendation. We want to find a specific type of food in a certain location in the airport, and the curled brackets represent user inputs which are being entered into our hypothetical app:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816215426/food-drink-real-time-recommendation-engine-parameters-768x208.png)
+
+This English sentence maps really well as a Cypher query:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816215537/cypher-query-dfw-food-drink-real-time-recommendations-768x364.png)
+
+This is going to pull all the places in the category, terminal and gate the user has requested. Then we get the absolute distance of the place to gate where the user is, and return the results in ascending order. Again, a very simple Cypher recommendation to a user based just on their location in the airport.
+
+### Social Recommendations
+
+Let’s look at a social recommendation. In our hypothetical app, we have users who can log in and “like” places in a way similar to Facebook and can also check into places:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816215655/social-media-recommendation-engine-data-model-768x550.png)
+
+Consider this data model on top of the first model that we explored, and now let’s find food and drink places in the following categories closest to some gate in whatever terminal that user’s friends like:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816215751/social-recommendation-engine-parameters-768x258.png)
+
+The MATCH clause is very similar to the MATCH clause of our first Cypher query, except now we are matching on likes and friends:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816215846/social-recommendation-engine-cypher-query-768x468.png)
+
+The first three lines are the same, but for the user in question – the user that’s “logged in” – we want to find their friends through the :FRIENDS_WITH relationship along with the places those friends liked. With just a few added lines of Cypher, we are now taking a social aspect into account for our recommendation engine.
+
+Again, we’re only showing categories that the user explicitly asked for that are in the same terminals the user is in. And, of course, we want to filter this by the user who is logged in and making this request, and it returns the name of the place along with its location and category. We are also accounting for how many friends have liked that place and the absolute value of the distance of the place from the gate, all returned in the RETURN clause.
+
+### Similarity Recommendation
+
+Now let’s take a look at a similarity recommendation engine:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816215944/similarity-recommendation-engine-data-model-768x646.png)
+
+Similarly to our earlier data model, we have users who can like places, but this time they can also rate places with an integer between one and 10. This is easily modeled in Neo4j by adding a property to the relationship.
+
+This allows us to find other similar users, like in the example of Greta and Alice. We’ve queried the places they’ve mutually liked, and for each of those places, we can see the weights they have assigned. Presumably, we can use these numbers to determine how similar they are to each other:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816220107/user-similarity-recommendation-engine-768x481.png)
+
+Now we have two vectors:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816220204/user-length-vectors-300x180.png)
+
+And now let’s apply Euclidean distance to find the distance between those two vectors:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816220239/euclidean-distance-user-vectors.png)
+
+And when we plug in all the numbers, we get the following similarity metric, which is really the distance metric between the two users:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816220323/user-vector-distance-similarity-metric-768x140.png)
+
+You can do this between two specific users easily in Cypher, especially if they’ve only mutually liked a small subset of places. Again, here we’re matching on two users, Alice and Greta, and are trying to find places they’ve mutually liked:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816220517/similarity-recommendation-engine-Cypher-query-users-768x399.png)
+
+They both have to have a :LIKES relationship to the place for it to be found in this result, and then we can easily calculate the Euclidean distance between them with the square root of the sum of their squared differences in Cypher.
+
+While this may work in an example with two specific people, it doesn’t necessarily work in real time when you’re trying to infer similar users from another user on the fly, by comparing them against every other user in the database in real time. Needless to say, this doesn’t work very well.
+
+To find a way around this, we pre-compute this calculation and store it in an actual relationship:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816220642/similarity-recommendation-engine-user-euclidean-distance-768x284.png)
+
+While in large datasets we would do this in batches, in this small example dataset, we can match on a Cartesian product of all the users and places they’ve mutually liked. When we use WHERE id(u1) < id(u2) as part of our Cypher query, this is just a trick to ensure we’re not finding the same pair twice on both the left and the right.
+
+Then with their Euclidean distance and themselves, we’re going to create a relationship between them called :DISTANCE and set a Euclidean property called euclidean. In theory, we could also store other similarity metrics on some relationship between users to capture different similarity metrics, since some might be more useful than others in certain contexts.
+
+And it’s really this ability to model properties on relationships in Neo4j that makes things like this incredibly easy. However, in practice you don’t want to store every single relationship that can possibly exist because you’ll only want to return the top few people of their neighbors.
+
+So you can just store the top in according to some threshold so you don’t have this fully connected graph. This allows you to perform graph database queries like the below in real time, because we’ve pre-computed it and stored it on the relationship, and in Cypher we’ll be able to grab that very quickly:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816220737/similarity-recommendation-engine-parameters-768x256.png)
+
+In this query, we’re matching on places and categories:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816221031/similarity-real-time-recommendation-engine-cypher-query-768x384.png)
+
+Again, the first three lines are the same, except that for the logged-in user, we’re getting users who have a :DISTANCE relationship to them. This is where what we went over earlier comes into play – in practice you should only store the top :DISTANCE relationships to users who are similar to them so you’re not grabbing a huge volume of users in this MATCH clause. Instead, we’re grabbing users who have a :DISTANCE relationship to them where those users like that place.
+
+This has allowed us to express a somewhat complicated pattern in just a few lines. We’re also grabbing the :LIKES relationship and putting it on a variable because we’re going to use those weights later to apply a rating.
+
+What’s important here is that we’re ordering those users by their distance ascending, because it is a distance metric, and we want the lowest distances because that indicates they are the most similar.
+
+With those other users ordered by the Euclidean distance, we’re going to collect the top three users’ ratings and use those as our average score to recommend these places. In other words, we’ve taken an active user, found users who are most similar to them based on the places they’ve liked, and then averaged the scores those similar users have given to rank those places in a result set.
+
+We’re essentially taking an average here by adding it up and dividing by the number of elements in the collection, and we’re ordering by that average ascending. Then secondarily, we’re ordering by the gate distance. Hypothetically, there could be ties I suppose, and then you order by the gate distance and then returning the name, category, gate and terminal.
+
+### Cluster Recommendations
+
+Our final example is going to be a cluster recommendation, which can be thought of as a workflow of offline computing that may be required as a workaround in Cypher. This may now be obsolete based on the new procedures announced at GraphConnect Europe, but sometimes you have to do certain algorithmic approaches that Cypher version 2.3 doesn’t expose.
+
+This is where you can use some form of statistical software, pull data out of Neo4j into a software such as Apache Spark, R or Python. Below is an example of R code for pulling data out of Neo4j, running an algorithm, and then – if appropriate – writing the results of that algorithm back into Neo4j as either a property, node, relationship or a new label. 
+
+By persisting the results of that algorithm into the graph, you can use it in real-time with queries similar to the ones we just went over:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816221336/cluster-recommendation-r-code-algorithm-neo4j.png)
+
+Below is some example code for how you do this in R, but you can easily do the same thing with whatever software you’re most comfortable with, such as Python or Spark. All you have to do is log in and connect to the graph.
+
+In the following example, I’ve clustered users together based on their similarities. Each user is represented as an observation, and I want to get the average rating that they’ve given each category:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816221449/cluster-recommendation-engine-r-code-example-768x403.png)
+
+Presumably, users who rate the bar category in similar ways are similar in general. Here I’m grabbing the names of users who like places in the same category, the category name, and the average weight of the “likes” relationships, as average weight, and that’s going to give me a table like this:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816221557/cluster-user-category-likes-768x295.png)
+
+Because we want each user to be an observation, we will have to manipulate the data where each feature is the average weight rating they’ve given restaurants within that category, per category. We’ll then use this to determine how similar they are, and I’m going to use a clustering algorithm to determine users being in different clusters.
+
+In R this is very straightforward:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816221716/clustering-algorith-recommendation-r-example-768x347.png)
+
+For this demonstration we are using k-means, which allows you to easily grab cluster assignments. In summary, I ran a clustering algorithm and now for each user I have a cluster assignment. 
+
+Bob and David are in the same cluster – they’re in cluster two – and now I’ll be able to see in real time which users have been determined to be in the same cluster.
+
+Next we write it into a CSV, which we then load into the graph:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816221805/load-csv-cypher-example-768x227.png)
+
+We have users and cluster assignments, so the CSV will only have two columns. LOAD CSV is a syntax that’s built into Cypher that allows you to call a CSV from some file path or URL and alias it as something. Then we’ll match on the users that already exist in the graph, grab the user column out of that CSV, and merge on the cluster. 
+
+Here we’re creating a new labeled node in the graph, the Cluster ID, which was given by k-means. Next we create relationships between the user and the cluster, which allows us to easily query when we get to the actual recommendation users who are in the same cluster.
+
+Now we have a new label cluster where users who are in the same cluster have a relationship to that cluster. Below is what our new data model looks like, which is on top of the other data models we explored:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816221913/clustering-recommendation-engine-data-model-768x401.png)
+
+Now let’s consider the following query:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816222009/clustering-recommendation-engine-parameters-768x259.png)
+
+With this Cypher query, we’re going beyond similar users to users in the same cluster. At this point we’ve also deleted those distance relationships:
+
+![](https://s3.amazonaws.com/dev.assets.neo4j.com/wp-content/uploads/20160816222045/cluster-recommendation-engine-cypher-query-example-768x385.png)
+
+In this query, we’ve taken the user who’s logged in, finding their cluster based on the user-cluster relationship, and finding their neighbors who are in that same cluster.
+
+We’ve assigned that to some variable cl, and we’re getting other users – which I’ve aliased as a neighbor variable – who have a user-cluster relationship to that same cluster, and then we’re getting the places that neighbor has liked. Again, we’re putting the “likes” on a variable, r, because we’re going want to grab weights off of the relationship to order our results.
+
+All we’ve changed in the query is that instead of using the similarity distance, we’re grabbing users in the same cluster, asserting categories, asserting the terminal and asserting that we’re only grabbing the user who is logged in. We’re collecting all those weights of the :LIKES relationships from their neighbors liking places, getting the category, the absolute value of the distance, ordering that in descending order, and returning those results.
+
+In these examples we’ve been able to take a pretty involved process and persist it in the graph, and then used the results of that algorithm – the results of the clustering algorithm and the clustering assignments – in real time.
+
+Our preferred workflow is to update these clustering assignments however frequently you see fit — for example, nightly or hourly. And, of course, you can use intuition to figure out how often is acceptable to be updating these cluster assignments.
+
+--------------------------------------------------------------------------------
+
+via: https://neo4j.com/blog/real-time-recommendation-engine-data-science/?utm_source=dbweekly&utm_medium=email
+
+作者:[Nicole White][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://neo4j.com/blog/contributor/nicole-white/
+[1]: https://neo4j.com/blog/real-time-recommendation-engine-data-science/?utm_source=dbweekly&utm_medium=email#basic-graph-recommendations
+[2]: https://neo4j.com/blog/real-time-recommendation-engine-data-science/?utm_source=dbweekly&utm_medium=email#social-recommendations
+[3]: https://neo4j.com/blog/real-time-recommendation-engine-data-science/?utm_source=dbweekly&utm_medium=email#similarity-recommendations
+[4]: https://neo4j.com/blog/real-time-recommendation-engine-data-science/?utm_source=dbweekly&utm_medium=email#cluster-recommendations
+[5]: https://neo4j.com/use-cases/real-time-recommendation-engine/
+
+
+
+
+
+
+
+
+
+
diff --git a/sources/tech/20160820 Building your first Atom plugin.md b/sources/tech/20160820 Building your first Atom plugin.md
new file mode 100644
index 0000000000..8cecc9c09f
--- /dev/null
+++ b/sources/tech/20160820 Building your first Atom plugin.md	
@@ -0,0 +1,758 @@
+Tranlating by lengmi.
+Building your first Atom plugin
+=====
+
+>Authored by [GitHub Campus Expert][1] @NickTikhonov.
+
+This tutorial will teach you how to write your first package for the Atom text editor. We'll be building a clone of [Sourcerer][2], a plugin for finding and using code snippets from StackOverflow. By the end of this tutorial you will have written a plugin that converts programming problems written in English into code snippets pulled from StackOverflow:
+
+![](https://cloud.githubusercontent.com/assets/6755555/17759382/836dd780-64ab-11e6-8f6a-329f66f01fd7.gif)
+
+### What you need to know
+
+Atom is written using web technologies. Our package will be built entirely using the EcmaScript 6 standard for JavaScript. You will need to be familiar with:
+
+- Using the command line
+- JavaScript programming
+- Promises
+- HTTP
+
+### Tutorial repository
+
+You can follow this tutorial step-by-step or check out the [supplementary repository on GitHub][3], which contains the plugin source code. The repository history contains one commit for each step outlined here.
+
+### Getting Started
+
+#### Installing Atom
+
+Download Atom by following the instructions on the [Atom website][4]. We will also need to install apm, the Atom Package Manager command line tool. You can do this by opening Atom and navigating to Atom > Install Shell Commands in the application menu. Check that apm was installed correctly by opening your command line terminal and running apm -v, which should print the version of the tool and related environments:
+
+```
+apm -v
+> apm  1.9.2
+> npm  2.13.3
+> node 0.10.40
+> python 2.7.10
+> git 2.7.4
+```
+
+#### Generating starter code
+
+Let's begin by creating a new package using a utility provided by Atom.
+
+- Launch the editor and press Cmd+Shift+P (on MacOS) or Ctrl+Shift+P (on Windows/Linux) to open the Command Palette.
+
+- Search for "Package Generator: Generate Package" and click the corresponding item on the list. You will see a prompt where you can enter the name of the package - "sourcefetch".
+
+- Press enter to generate the starter package, which should automatically be opened in Atom.
+
+If you don't see package files appear in the sidebar, press Cmd+K Cmd+B (on MacOS) or Ctrl+K Ctrl+B (on Windows/Linux).
+
+![](https://cloud.githubusercontent.com/assets/6755555/17759387/8387a354-64ab-11e6-97db-ea469f008bef.gif)
+
+The Command Palette lets you find and run package commands using fuzzy search. This is a convenient way to run commands without navigating menus or remembering shortcuts. We will be using it throughout this tutorial.
+
+#### Running the starter code
+
+Let's try out the starter package before diving into the code itself. We will first need to reload Atom to make it aware of the new package that was added. Open the Command Palette again and run the "Window: Reload" command.
+
+Reloading the current window ensures that Atom runs the latest version of our source code. We will be running this command every time we want to test the changes we make to our package.
+
+Run the package toggle command by navigating to Packages > sourcefetch > Toggle using the editor menu, or run sourcefetch: Toggle using the Command Palette. You should see a black box appear at the top of the screen. Hide it by running the command again.
+
+![](https://cloud.githubusercontent.com/assets/6755555/17759386/83799fc0-64ab-11e6-9f0c-0df9b1dbff8b.gif)
+
+#### The "toggle" command
+
+Let's open lib/sourcefetch.js, which contains the package logic and defines the toggle command.
+
+```
+toggle() {
+ console.log('Sourcefetch was toggled!');
+ return (
+   this.modalPanel.isVisible() ?
+   this.modalPanel.hide() :
+   this.modalPanel.show()
+ );
+}
+```
+
+toggle is a function exported by the module. It uses a ternary operator to call show and hide on the modal panel based on its visibility. modalPanel is an instance of Panel, a UI element provided by the Atom API. We declare modalPanel inside export default, which lets us access it as an instance variable with this.
+
+```
+this.subscriptions.add(atom.commands.add('atom-workspace', {
+  'sourcefetch:toggle': () => this.toggle()
+}));
+```
+
+The above statement tells Atom to execute toggle every time the user runs sourcefetch:toggle. We subscribe an anonymous function, () => this.toggle(), to be called every time the command is run. This is an example of event-driven programming, a common paradigm in JavaScript.
+
+#### Atom Commands
+
+Commands are nothing more than string identifiers for events triggered by the user, defined within a package namespace. We've already used:
+
+- package-generator:generate-package
+- window:reload
+- sourcefetch:toggle
+
+Packages subscribe to commands in order to execute code in response to these events.
+
+### Making your first code change
+
+Let's make our first code change—we're going to change toggle to reverse text selected by the user.
+
+#### Change "toggle"
+
+- Change the toggle function to match the snippet below.
+
+```
+toggle() {
+  let editor
+  if (editor = atom.workspace.getActiveTextEditor()) {
+    let selection = editor.getSelectedText()
+    let reversed = selection.split('').reverse().join('')
+    editor.insertText(reversed)
+  }
+}
+```
+
+#### Test your changes
+
+- Reload Atom by running Window: Reload in the Command Palette
+
+- Navigate to File > New to create a new file, type anything you like and select it with the cursor.
+
+- Run the sourcefetch:toggle command using the Command Palette, Atom menu, or by right clicking and selecting "Toggle sourcefetch"
+
+The updated command will toggle the order of the selected text:
+
+![](https://cloud.githubusercontent.com/assets/6755555/17759381/836acd60-64ab-11e6-84dc-4ef4471a361f.gif)
+
+See all code changes for this step in the [sourcefetch tutorial repository][4].
+
+### The Atom Editor API
+
+The code we added uses the TextEditor API to access and manipulate the text inside the editor. Let's take a closer look.
+
+```
+let editor
+if (editor = atom.workspace.getActiveTextEditor()) { /* ... */ }
+```
+
+The first two lines obtain a reference to a TextEditor instance. The variable assignment and following code is wrapped in a conditional to handle the case where there is no text editor instance available, for example, if the command was run while the user was in the settings menu.
+
+```
+let selection = editor.getSelectedText()
+```
+
+Calling getSelectedText gives us access to text selected by the user. If no text is currently selected, the function returns an empty string.
+
+```
+let reversed = selection.split('').reverse().join('')
+editor.insertText(reversed)
+```
+
+Our selected text is reversed using [JavaScript String methods][6] . Finally, we call insertText to replace the selected text with the reversed counterpart. You can learn more about the different TextEditor methods available by reading the [Atom API documentation][5].
+
+### Exploring the starter package
+
+Now that we've made our first code change, let's take a closer look at how an Atom package is organized by exploring the starter code.
+
+#### The main file
+
+The main file is the entry-point to an Atom package. Atom knows where to find the main file from an entry in package.json:
+
+```
+"main": "./lib/sourcefetch",
+```
+
+The file exports an object with lifecycle functions which Atom calls on certain events.
+
+- activate is called when the package is initially loaded by Atom. This function is used to initialize objects such as user interface elements needed by the package, and to subscribe handler functions to package commands.
+
+- deactivate is called when the package is deactivated, for example, when the editor is closed or refreshed by the user.
+
+- serialize is called by Atom to allow you to save the state of the package between uses. The returned value is passed as an argument to activate when the package is next loaded by Atom.
+
+We are going to rename our package command to fetch, and remove user interface elements we won't be using. Update the file to match the version below:
+
+```
+'use babel';
+
+import { CompositeDisposable } from 'atom'
+
+export default {
+
+  subscriptions: null,
+
+  activate() {
+    this.subscriptions = new CompositeDisposable()
+
+    this.subscriptions.add(atom.commands.add('atom-workspace', {
+      'sourcefetch:fetch': () => this.fetch()
+    }))
+  },
+
+  deactivate() {
+    this.subscriptions.dispose()
+  },
+
+  fetch() {
+    let editor
+    if (editor = atom.workspace.getActiveTextEditor()) {
+      let selection = editor.getSelectedText()
+      selection = selection.split('').reverse().join('')
+      editor.insertText(selection)
+    }
+  }
+};
+```
+
+### Activation commands
+
+To improve performance, Atom packages can be lazy loading. We can tell Atom to load our package only when certain commands are run by the user. These commands are called activation commands and are defined in package.json:
+
+```
+"activationCommands": {
+  "atom-workspace": "sourcefetch:toggle"
+},
+```
+
+Update this entry to make fetch an activation command.
+
+```
+"activationCommands": {
+  "atom-workspace": "sourcefetch:fetch"
+},
+```
+
+Some packages, such as those which modify Atom's appearance need to be loaded on startup. In those cases, activationCommands can be omitted entirely.
+
+### Triggering commands
+
+#### Menu items
+
+JSON files inside the menus folder specify which menu items are created for our package. Let's take a look at `menus/sourcefetch.json`:
+
+```
+"context-menu": {
+  "atom-text-editor": [
+    {
+      "label": "Toggle sourcefetch",
+      "command": "sourcefetch:toggle"
+    }
+  ]
+},
+```
+
+The context-menu object lets us define new items in the right-click menu. Each item is defined by a label to be displayed in the menu and a command to run when the item is clicked.
+
+```
+"context-menu": {
+  "atom-text-editor": [
+    {
+      "label": "Fetch code",
+      "command": "sourcefetch:fetch"
+    }
+  ]
+},
+```
+
+The menu object in the same file defines custom application menu items created for the package. We're going to rename this entry as well:
+
+```
+"menu": [
+  {
+    "label": "Packages",
+    "submenu": [
+      {
+        "label": "sourcefetch",
+        "submenu": [
+          {
+            "label": "Fetch code",
+            "command": "sourcefetch:fetch"
+          }
+        ]
+      }
+    ]
+  }
+]
+```
+
+#### Keyboard shortcuts
+
+Commands can also be triggered with keyboard shortcuts, defined with JSON files in the keymaps directory:
+
+```
+{
+  "atom-workspace": {
+    "ctrl-alt-o": "sourcefetch:toggle"
+  }
+}
+```
+
+The above lets package users call toggle with Ctrl+Alt+O on Windows/Linux or Cmd+Alt+O on MacOS.
+
+Rename the referenced command to fetch:
+
+```
+"ctrl-alt-o": "sourcefetch:fetch"
+```
+
+Reload Atom by running the Window: Reload command. You should see that the application and right-click menus are updated, and the reverse functionality should work as before.
+
+See all code changes for this step in the [sourcefetch tutorial repository][7].
+
+### Using NodeJS modules
+
+Now that we've made our first code change and learned about Atom package structure, let's introduce our first dependency—a module from Node Package Manager (npm). We will use the request module to make HTTP requests and download the HTML of a website. This functionality will be needed later, to scrape StackOverflow pages.
+
+#### Installing dependencies
+
+Open your command line application, navigate to your package root directory and run:
+
+```
+npm install --save request@2.73.0
+apm install
+```
+
+These commands add the request Node module to our dependencies list and install the module into the node_modules directory. You should see a new entry in package.json. The @ symbol tells npm to install the specific version we will be using for this tutorial. Running apm install lets Atom know to use our newly installed module.
+
+```
+"dependencies": {
+  "request": "^2.73.0"
+}
+```
+
+#### Downloading and logging HTML to the Developer Console
+
+Import request into our main file by adding an import statement to the top of lib/sourcefetch.js:
+
+```
+import { CompositeDisposable } from 'atom'
+import request from 'request'
+```
+
+Now, add a new function, download to the module's exports, below fetch:
+
+```
+export default {  
+
+  /* subscriptions, activate(), deactivate() */
+
+  fetch() {
+    ...
+  },
+
+  download(url) {
+    request(url, (error, response, body) => {
+      if (!error && response.statusCode == 200) {
+        console.log(body)
+      }
+    })
+  }
+}
+```
+
+This function uses request to download the contents of a web page and logs the output to the Developer Console. When the HTTP request completes, our callback function will be called with the response as an argument.
+
+The final step is to update fetch so that it calls download:
+
+```
+fetch() {
+  let editor
+  if (editor = atom.workspace.getActiveTextEditor()) {
+    let selection = editor.getSelectedText()
+    this.download(selection)
+  }
+},
+```
+
+Instead of reversing the selected text, fetch now treats the selection as a URL, passing it to download. Let's see our changes in action:
+
+- Reload Atom by running the Window: Reload command.
+
+- Open the Developer Tools. To do this, navigate to View > Developer > Toggle Developer Tools in the menu.
+
+- Create a new file, navigate to File > New.
+
+- Enter and select a URL, for example, http://www.atom.io.
+
+- Run our package command in any of the three ways previously described:
+
+![](https://cloud.githubusercontent.com/assets/6755555/17759384/836ea91c-64ab-11e6-8fbe-7d15fb482c6d.gif)
+
+>Developer Tools make it easy to debug Atom packages. Any console.log statement will print to the interactive console, and you can use the Elements tab to explore the visual structure of the whole applicatio—which is just an HTML [Document Object Model (DOM)][8].
+
+See all code changes for this step in the [sourcefetch tutorial repository][9].
+
+### Using Promises to insert downloaded HTML into the editor
+
+Ideally, we would like our download function to return the HTML as a string instead of just printing page contents into the console. Returning body won't work, however, since we get access to body inside of the callback rather than download itself.
+
+We will solve this problem by returning a Promise rather than the value itself. Let's change download to return a Promise:
+
+```
+download(url) {
+  return new Promise((resolve, reject) => {
+    request(url, (error, response, body) => {
+      if (!error && response.statusCode == 200) {
+        resolve(body)
+      } else {
+        reject({
+          reason: 'Unable to download page'
+        })
+      }
+    })
+  })
+}
+```
+
+Promises allow us to return values obtained asynchronously by wrapping asynchronous logic in a function that provides two callbacks— resolve for returning a value successfully, and reject for notifying the caller of an error. We call reject if an error is returned by request, and resolve the HTML otherwise.
+
+Let's change fetch to work with the Promise returned by download:
+
+```
+fetch() {
+  let editor
+  if (editor = atom.workspace.getActiveTextEditor()) {
+    let selection = editor.getSelectedText()
+    this.download(selection).then((html) => {
+      editor.insertText(html)
+    }).catch((error) => {
+      atom.notifications.addWarning(error.reason)
+    })
+  }
+},
+```
+
+In our new version of fetch, we get access to the HTML by calling then on the Promise returned by download. This lets us insert the HTML into the editor. We also accept and handle any errors returned by calling catch. We handle errors by displaying a warning notification using the Atom Notification API.
+
+Let's see what changed. Reload Atom and run the package command on a selected URL:
+
+![](https://cloud.githubusercontent.com/assets/6755555/17759379/8357bb08-64ab-11e6-9bd2-6f63b8f50dcc.gif)
+
+If the command is run on an invalid URL, a warning notification will be displayed:
+
+![](https://cloud.githubusercontent.com/assets/6755555/17759378/833ab09e-64ab-11e6-9896-2f874b0fdc8a.gif)
+
+See all code changes for this step in the [sourcefetch tutorial repository][10].
+
+#### Building a scraper to extract code snippets from StackOverflow HTML
+
+The next step involves extracting code snippets from the HTML of a StackOverflow page we obtained in the previous step. In particular, we're interested in code from the accepted answer—an answer chosen to be correct by the question author. We can greatly simplify our package implementation by assuming any such answer to be relevant and correct.
+
+#### Constructing a query using jQuery and Chrome Developer Tools
+
+This section assumes you are using the Chrome web browser. You may be able to follow along using another browser, but instructions may change.
+
+Let's take a look at a typical StackOverflow page that contains an accepted answer with a code snippet. We are going to explore the HTML using Chrome Developer Tools:
+
+- Open Chrome and navigate to any StackOverflow page containing an accepted answer with code, such as this hello world example in Python or this question about reading text from a file in C.
+
+- Scroll down to the accepted answer and highlight a section of the code snippet.
+
+- Right click and select Inspect
+
+- Inspect the location of the code snippet within the HTML code using the Elements browser.
+
+Note that the document has the following structure:
+
+```
+<div class="accepted-answer">
+  ...
+    ...
+      <pre>
+        <code>
+          ...snippet elements...
+        </code>
+      </pre>
+    ...
+  ...
+</div>
+```
+
+- The accepted answer is denoted by a div with class accepted-answer
+
+- Block code snippets are located inside a pre element
+
+- Elements that render the code snippet itself sit inside a code tag
+
+![](https://cloud.githubusercontent.com/assets/6755555/17759380/83689a90-64ab-11e6-89b2-7172c03baae7.gif)
+
+Now let's construct a jQuery statement for extracting code snippets:
+
+- Click the Console tab within Developer Tools to access the JavaScript console.
+
+- Type $('div.accepted-answer pre code').text() into the console and press Enter.
+
+You should see the accepted answer code snippets printed out in the console. The code we just ran uses a special $ function provided by jQuery. $ accepts a query string to select and return certain HTML elements from the website. Let's take a look at how this code works by considering a couple of intermediate example queries:
+
+```
+$('div.accepted-answer')
+> [<div id="answer-1077349" class="answer accepted-answer" ... ></div>]
+```
+
+The above query will match all <div> elements that contain the class accepted-answer, in our case - just one div.
+
+```
+$('div.accepted-answer pre code')
+> [<code>...</code>]
+```
+
+Building upon the previous, this query will match any `<code>` element that is inside a `<pre>` element contained within the previously matched `<div>`.
+
+```
+$('div.accepted-answer pre code').text()
+> "print("Hello World!")"
+```
+
+The text function extracts and concatenates all text from the list of elements that would otherwise be returned by the previous query. This also strips out elements used for syntax highlighting purposes from the code.
+
+### Introducing Cheerio
+
+Our next step involves using the query we created to implement a scraping function using Cheerio, a jQuery implementation for server-side applications.
+
+#### Install Cheerio
+
+Open your command line application, navigate to your package root directory and run:
+
+```
+npm install --save cheerio@0.20.0
+apm install
+```
+
+#### Implement the scraping function
+
+- Add an import statement for cheerio in lib/sourcefetch.js:
+
+```
+import { CompositeDisposable } from 'atom'
+import request from 'request'
+import cheerio from 'cheerio'
+```
+
+- Now create a new function that extracts code snippets given StackOverflow HTML, called scrape:
+
+```
+fetch() {
+  ...
+},
+
+scrape(html) {
+  $ = cheerio.load(html)
+  return $('div.accepted-answer pre code').text()
+},
+
+download(url) {
+  ...
+}
+```
+
+- Finally, let's change fetch to pass downloaded HTML to scrape instead of inserting it into the editor:
+
+```
+fetch() {
+  let editor
+  let self = this
+
+  if (editor = atom.workspace.getActiveTextEditor()) {
+    let selection = editor.getSelectedText()
+    this.download(selection).then((html) => {
+      let answer = self.scrape(html)
+      if (answer === '') {
+        atom.notifications.addWarning('No answer found :(')
+      } else {
+        editor.insertText(answer)
+      }
+    }).catch((error) => {
+      console.log(error)
+      atom.notifications.addWarning(error.reason)
+    })
+  }
+},
+```
+
+Our scraping function is implemented in just two lines because cheerio does all of the work for us! We create a $ function by calling load with our HTML string, and use this function to run our jQuery statement and return the results. You can explore the entire Cheerio API in their developer documentation.
+
+### Testing the updated package
+
+- Reload Atom and run soucefetch:fetch on a selected StackOverflow URL to see the progress so far.
+
+If we run the command on a page with an accepted answer, it will be inserted into the editor:
+
+![](https://cloud.githubusercontent.com/assets/6755555/17759383/836e26b8-64ab-11e6-9f16-321903470ce2.gif)
+
+If we run the command on a page with no accepted answer, a warning notification will be displayed instead:
+
+
+![](https://cloud.githubusercontent.com/assets/6755555/17759388/838d3864-64ab-11e6-8091-b4d15bd56025.gif)
+
+Our new iteration of fetch gives us the code snippet within a StackOverflow page instead of the entire HTML contents. Note that our updated fetch function checks for the absence of an answer and displays a notification to alert the user.
+
+See all code changes for this step in the [sourcefetch tutorial repository][11].
+
+### Implementing Google search to find relevant StackOverflow URLs
+
+Now that we can turn StackOverflow URLs into code snippets, let's implement our final function, search, which will return a relevant URL given the description of a snippet, such as "hello world" or "quicksort". We will be using Google search via the unofficial google npm module, which allows us to search programmatically.
+
+#### Installing the Google npm module
+
+- Install google by opening your command line application at the package root directory, and run:
+
+```
+npm install --save google@2.0.0
+apm install
+```
+
+#### Importing and configuring the module
+
+Add an import statement for google at the top of lib/sourcefetch.js:
+
+```
+import google from "google"
+```
+
+We will configure the library to limit the number of results returned during search. Add the following line below the import statement to limit returned results to just the top one.
+
+```
+google.resultsPerPage = 1
+```
+
+#### Implementing the search function
+
+Next, let's implement our search function itself:
+
+```
+fetch() {
+  ...
+},
+
+search(query, language) {
+  return new Promise((resolve, reject) => {
+    let searchString = `${query} in ${language} site:stackoverflow.com`
+
+    google(searchString, (err, res) => {
+      if (err) {
+        reject({
+          reason: 'A search error has occured :('
+        })
+      } else if (res.links.length === 0) {
+        reject({
+          reason: 'No results found :('
+        })
+      } else {
+        resolve(res.links[0].href)
+      }
+    })
+  })
+},
+
+scrape() {
+  ...
+}
+```
+
+The code above searches Google for a StackOverflow page relevant to the given query and programming language, returning the URL of the top result. Let's take a look at how it works:
+
+```
+let searchString = `${query} in ${language} site:stackoverflow.com`
+```
+
+We construct the search string using the query entered by the user and the current language selected. For example, if the user types "hello world" while editing Python, the query will be hello world in python site:stackoverflow.com. The final part of the string is a filter provided by Google Search that lets us limit results to those linked to StackOverflow.
+
+```
+google(searchString, (err, res) => {
+  if (err) {
+    reject({
+      reason: 'A search error has occured :('
+    })
+  } else if (res.links.length === 0) {
+    reject({
+      reason: 'No results found :('
+    })
+  } else {
+    resolve(res.links[0].href)
+  }
+})
+```
+
+We wrap the call to google inside a Promise so that we can return our URL asynchronously. We propagate any errors returned by the library, also returning an error when there are no results available. We resolve the URL of the top result otherwise.
+
+### Updating fetch to use search
+
+Our final step is to update fetch to use search:
+
+```
+fetch() {
+  let editor
+  let self = this
+
+  if (editor = atom.workspace.getActiveTextEditor()) {
+    let query = editor.getSelectedText()
+    let language = editor.getGrammar().name
+
+    self.search(query, language).then((url) => {
+      atom.notifications.addSuccess('Found google results!')
+      return self.download(url)
+    }).then((html) => {
+      let answer = self.scrape(html)
+      if (answer === '') {
+        atom.notifications.addWarning('No answer found :(')
+      } else {
+        atom.notifications.addSuccess('Found snippet!')
+        editor.insertText(answer)
+      }
+    }).catch((error) => {
+      atom.notifications.addWarning(error.reason)
+    })
+  }
+}
+```
+
+Let's take a look at what changed:
+
+- Our selected text is now treated as the query entered by the user.
+
+- We obtain the language of the current editor tab using the TextEditor API
+
+- We call search to obtain a URL, which we access by calling then on the resulting Promise
+
+Instead of calling then on the Promise returned by download, we instead return the Promise itself and chain another then call onto the original call. This helps us avoid callback hell
+
+See all code changes for this step in the [sourcefetch tutorial repository][12].
+
+### Testing the final plugin
+
+And we're done! See the final plugin in action by reloading Atom and running our package command on a problem description, and don't forget to select a language in the bottom-right corner.
+
+![](https://cloud.githubusercontent.com/assets/6755555/17759382/836dd780-64ab-11e6-8f6a-329f66f01fd7.gif)
+
+### Next steps
+
+Now that you know the basics of hacking Atom, feel free to practice what you've learned [by forking the sourcefetch repository and adding your own features][13].
+
+--------------------------------------------------------------------------------
+
+via: https://github.com/blog/2231-building-your-first-atom-plugin
+
+作者:[NickTikhonov][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://github.com/NickTikhonov
+[1]: https://education.github.com/experts
+[2]: https://github.com/NickTikhonov/sourcerer
+[3]: https://github.com/NickTikhonov/sourcefetch-guide
+[4]: https://github.com/NickTikhonov/sourcefetch-tutorial/commit/89e174ab6ec6e270938338b34905f75bb74dbede
+[5]: https://atom.io/docs/api/latest/TextEditor
+[6]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
+[7]: https://github.com/NickTikhonov/sourcefetch-tutorial/commit/aa3ec5585b0aa049393351a30be14590df09c29a
+[8]: https://www.wikipedia.com/en/Document_Object_Model
+[9]: https://github.com/NickTikhonov/sourcefetch-tutorial/commit/85992043e57c802ca71ff6e8a4f9c477fbfd13db
+[10]: https://github.com/NickTikhonov/sourcefetch-tutorial/commit/896d160dca711f4a53ff5b182018b39cf78d2774
+[11]: https://github.com/NickTikhonov/sourcefetch-tutorial/commit/039a1e1e976d029f7d6b061b4c0dac3eb4a3b5d2
+[12]: https://github.com/NickTikhonov/sourcefetch-tutorial/commit/aa9d0b5fc4811a70292869730e0f60ddf0bcf2aa
+[13]: https://github.com/NickTikhonov/sourcefetch-tutorial
diff --git a/sources/tech/20160823 The infrastructure behind Twitter -  efficiency and optimization.md b/sources/tech/20160823 The infrastructure behind Twitter -  efficiency and optimization.md
new file mode 100644
index 0000000000..d451393499
--- /dev/null
+++ b/sources/tech/20160823 The infrastructure behind Twitter -  efficiency and optimization.md	
@@ -0,0 +1,129 @@
+Eriwoon Start to translate this article
+The infrastructure behind Twitter: efficiency and optimization
+===========
+
+
+In the past, we've published details about Finagle, Manhattan, and the summary of how we re-architected the site to be able to handle events like Castle in the Sky, the Super Bowl, 2014 World Cup, the global New Year's Eve celebration, among others. In this infrastructure series, we're focusing on the core infrastructure and components that run Twitter. We're also going to focus each blog on efforts surrounding scalability, reliability, and efficiency in a way that highlights the history of our infrastructure, challenges we've faced, lessons learned, upgrades made, and where we're heading.
+
+### Data center efficiency
+
+#### History
+
+Twitter hardware and data centers are at the scale few technology companies ever reach. However, this was not accomplished without a few missteps along the way. Our uptime has matured through a combination of physical improvements and software-based changes.
+
+During the period when the fail whale was prevalent, outages occurred due to software limitations, as well as physical failures at the hardware or infrastructure level. Failure domains existed in various definitions which had to be aggregated to determine the risk and required redundancy for services. As the business scaled in customers, services, media content, and global presence, the strategy evolved to efficiently and resiliently support the service.
+
+#### Challenges
+
+Software dependencies on bare metal were further dependant on our data centers' ability to operate and maintain uptime of power, fiber connectivity, and environment. These discrete physical failure domains had to be reviewed against the services distributed on the hardware to provide for fault tolerance.
+
+The initial decision of which data center service provider to scale with was done when specialization in site selection, operation, and design was in its infancy. We began in a hosted provider then migrated to a colocation facility as we scaled. Early service interruptions occurred as result of equipment failures, data center design issues, maintenance issues, and human error. As a result, we continually iterated on the physical layer designs to increase the resiliency of the hardware and the data center operations.
+
+The physical reasons for service interruptions were inclusive of hardware failures at the server component level, top of rack switch, and core switches. For example, during the initial evaluation of our customized servers, the hardware team determined the cost of the second power supply was not warranted given the low rate of failure of server power supplies — so they were removed from the design. The data center power topology provides redundancy through separate physical whips to the racks and requires the second power supply. Removal of the second power supply eliminated the redundant power path, leaving the hardware vulnerable to impact during distribution faults in the power system. To mitigate the impact of the single power supply, ATS units were required to be added at the rack level to allow a secondary path for power.
+
+The layering of systems with diverse fiber paths, power sources, and physical domains continued to separate services from impacts at relatively small scale interruptions, thus improving resiliency.
+
+#### Lessons learned and major technology upgrades, migrations, and adoptions
+
+We learned to model dependencies between the physical failure domains, (i.e. building power and cooling, hardware, fiber) and the services distributed across them to better predict fault tolerance and drive improvements.
+
+We added additional data centers providing regional diversity to mitigate risk from natural disaster and the ability to fail between regions when it was needed during major upgrades, deploys or incidents. The active-active operation of data centers provided for staged code deployment reducing overall impacts of code rollouts.
+
+The efficiency of power use by the data centers has improved with expanding the operating ranges of the environmental envelope and designing the hardware for resiliency at the higher operating temperatures.
+
+#### Future work
+
+Our data centers continue to evolve in strategy and operation, providing for live changes to the operating network and hardware without interruption to the users. Our strategy will continue to focus on scale within the existing power and physical footprints through optimization and maintaining flexibility while driving efficiency in the coming years.
+
+### Hardware efficiency
+
+#### History and challenges
+
+Our hardware engineering team was started to qualify and validate performance of off-the-shelf purchased hardware, and evolved into customization of hardware for cost and performance optimizations.
+
+Procuring and consuming hardware at Twitter's scale comes with a unique set of challenges. In order to meet the demands of our internal customers, we initially started a program to qualify and ensure the quality of purchased hardware. The team was primarily focused on performance and reliability testing ensuring that systems could meet the demands. Running systematic tests to validate the behavior was predictable, and there were very few bugs introduced.
+
+As we scaled our major workloads (Mesos, Hadoop, Manhattan, and MySQL) it became apparent the available market offerings didn't quite meet the needs. Off-the-shelf servers come with enterprise features, like raid controllers and hot swap power supplies. These components improve reliability at small scale, but often decrease performance and increase cost; for example some raid controllers interfered with the performance of SSDs and could be a third of the cost of the system.
+
+At the time, we were a large user of mysql databases. Issues arose from both supply and performance of SAS media. The majority of deployments were 1u servers, and the total number of drives used plus a writeback cache could predict the performance of a system often time limited to a sustained 2000 sequential IOPS. In order to continue scaling this workload, we were stranding CPU cores and disk capacity to meet IOPS requirement. We were unable to find cost-effective solutions at this time.
+
+As our volume of hardware reached a critical mass, it made sense to invest in a hardware engineering team for customized white box solutions with focus on reducing the capital expenses and increased performance metrics.
+
+#### Major technology changes and adoption
+
+We've made many transitions in our hardware technology stack. Below is a timeline for adoptions of new technology and internally developed platforms.
+
+- 2012 - SSDs become the primary storage media for our MySQL and key/value databases.
+- 2013 - Our first custom solution for Hadoop workloads is developed, and becomes our primary bulk storage solution.
+- 2013 - Our custom solution is developed for Mesos, TFE, and cache workloads.
+- 2014 - Our custom SSD key/value server completes development.
+- 2015 - Our custom database solution is developed.
+- 2016 - We developed GPU systems for inference and training of machine learning models.
+
+#### Lessons learned
+
+The objective of our Hardware Engineering team is to significantly reduce the capital expenditure and operating expenditure by making small tradeoffs that improve our TCO. Two generalizations can apply to reduce the cost of a server:
+
+1. Removing the unused components
+2. Improving utilization
+
+Twitter's workload is divided into four main verticals: storage, compute, database, and gpu. Twitter defines requirements on a per vertical basis, allowing Hardware Engineering to produce a focused feature set for each. This approach allows us to optimize component selection where the equipment may go unused or underutilized. For example, our storage configuration has been designed specifically for Hadoop workloads and was delivered at a TCO reduction of 20% over the original OEM solution. At the same time, the design improved both the performance and reliability of the hardware. Similarly, for our compute vertical, the Hardware Engineering Team has improved the efficiency of these systems by removing unnecessary features.
+
+There is a minimum overhead required to operate a server, and we quickly reached a point where it could no longer remove components to reduce cost. In the compute vertical specifically, we decided the best approach was to look at solutions that replaced multiple nodes with a single node, and rely on Aurora/Mesos to manage the capacity. We settled on a design that replaced two of our previous generation compute nodes with a single node.
+
+Our design verification began with a series of rough benchmarks, and then progressed to a series of production load tests confirming a scaling factor of 2. Most of this improvement came from simply increasing the thread count of the CPU, but our testing confirmed a 20-50% improvement in our per thread performance. Additionally we saw a 25% increase in our per thread power efficiency, due to sharing the overhead of the server across more threads.
+
+For the initial deployment, our monitoring showed a 1.5 replacement factor, which was well below the design goal. An examination of the performance data revealed there was a flawed assumption in the workload characteristics, and that it needed to be identified.
+
+Our Hardware Engineering Team's initial action was to develop a model to predict the packing efficiency of the current Aurora job set into various hardware configurations. This model correctly predicted the scaling factor we were observing in the fleet, and suggested we were stranding cores due to unforeseen storage requirements. Additionally, the model predicted we would see a still improved scaling factor by changing the memory configuration as well.
+
+Hardware configuration changes take time to implement, so Hardware Engineering identified a few large jobs and worked with our SRE teams to adjust the scheduling requirements to reduce the storage needs. These changes were quick to deploy, and resulted in an immediate improvement to a 1.85 scaling factor.
+
+In order to address the situation permanently, we needed to adjust to configuration of the server. Simply expanding the installed memory and disk capacity resulted in a 20% improvement in the CPU core utilization, at a minimal cost increase. Hardware Engineering worked with our manufacturing partners to adjust the bill of materials for the initial shipments of these servers. Follow up observations confirmed a 2.4 scaling factor exceeding the target design.
+
+### Migration from bare metal to mesos
+
+Until 2012, running a service inside Twitter required hardware requisitions. Service owners had to find out and request the particular model or class of server, worry about your rack diversity, maintain scripts to deploy code, and manage dead hardware. There was essentially no "service discovery." When a web service needed to talk to the user service, it typically loaded up a YAML file containing all of the host IPs and ports of the user service and the service used that list (port reservations were tracked in a wiki page). As hardware died or was added, managing required editing and committing changes to the YAML file that would go out with the next deploy. Making changes in the caching tier meant many deploys over hours and days, adding a few hosts at a time and deploying in stages. Dealing with cache inconsistencies during the deploy was a common occurrence, since some hosts would be using the new list and some the old. It was possible to have a host running old code (because the box was temporarily down during the deploy) resulting in a flaky behavior with the site.
+
+In 2012/2013, two things started to get adopted at Twitter: service discovery (via a zookeeper cluster and a library in the core module of Finagle) and Mesos (including our own scheduler framework on top of Mesos called Aurora, now an Apache project).
+
+Service discovery no longer required static YAML host lists. A service either self-registered on startup or was automatically registered under mesos into a "serverset" (which is just a path to a list of znodes in zookeeper based on the role, environment, and service name). Any service that needed to talk to that service would just watch that path and get a live view of what servers were out there.
+
+With Mesos/Aurora, instead of having a script (we were heavy users of Capistrano) that took a list of hosts, pushed binaries around and orchestrated a rolling restart, a service owner pushed the package into a service called "packer" (which is a service backed by HDFS), uploaded an aurora configuration that described the service (how many CPUs it needed, how much memory, how many instances needed, the command lines of all the tasks each instance should run) and Aurora would complete the deploy. It schedules instances on an available hosts, downloads the artifact from packer, registers it in service discovery, and launches it. If there are any failures (hardware dies, network fails, etc), Mesos/Aurora automatically reschedules the instance on another host.
+
+#### Twitter's Private PaaS
+
+Mesos/Aurora and Service Discovery in combination were revolutionary. There were many bugs and growing pains over the next few years and many hard lessons learned about distributed systems, but the fundamental design was sound. In the old world, the teams were constantly dealing with and thinking about hardware and its management. In the new world, the engineers only have to think about how best to configure their services and how much capacity to deploy. We were also able to radically improve the CPU utilization of Twitter's fleet over time, since generally each service that got their own bare metal hardware didn't fully utilize its resources and did a poor job of managing capacity. Mesos allows us to pack multiple services into a box without having to think about it, and adding capacity to a service is only requesting quota, changing one line of a config, and doing a deploy.
+
+Within two years, most "stateless" services moved into Mesos. Some of the most important and largest services (including our user service and our ads serving system) were among the first to move. Being the largest, they saw the biggest benefit to their operational burden. This allowed them to reduce their operational burden.
+
+We are continuously looking for ways to improve the efficiency and optimization of the infrastructure. As part of this, we regularly benchmark against public cloud providers and offerings to validate our TCO and performance expectations of the infrastructure. We also have a good presence in public cloud, and will continue to utilize the public cloud when it's the best available option. The next series of this post will mainly focus on the scale of our infrastructure.
+
+Special thanks to Jennifer Fraser, David Barr, Geoff Papilion, Matt Singer, and Lam Dong for all their contributions to this blog post.
+
+
+
+
+
+--------------------------------------------------------------------------------
+
+via: https://blog.twitter.com/2016/the-infrastructure-behind-twitter-efficiency-and-optimization?utm_source=webopsweekly&utm_medium=email
+
+作者:[mazdakh][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://twitter.com/intent/user?screen_name=mazdakh
+[1]: https://twitter.com/jenniferfraser
+[2]: https://twitter.com/davebarr
+[3]: https://twitter.com/gpapilion
+[4]: https://twitter.com/lamdong
+
+
+
+
+
+
+
diff --git a/sources/tech/20160826 Understanding Different Classifications of Shell Commands and Their Usage in Linux.md b/sources/tech/20160826 Understanding Different Classifications of Shell Commands and Their Usage in Linux.md
new file mode 100644
index 0000000000..29b3bf04da
--- /dev/null
+++ b/sources/tech/20160826 Understanding Different Classifications of Shell Commands and Their Usage in Linux.md	
@@ -0,0 +1,219 @@
+Understanding Different Classifications of Shell Commands and Their Usage in Linux
+====
+
+When it comes to gaining absolute control over your Linux system, then nothing comes close to the command line interface (CLI). In order to become a Linux power user, one must understand the [different types of shell commands][1] and the appropriate ways of using them from the terminal.
+
+In Linux, there are several types of commands, and for a new Linux user, knowing the meaning of different commands enables for efficient and precise usage. Therefore, in this article, we shall walk through the various classifications of shell commands in Linux.
+
+One important thing to note is that the command line interface is different from the shell, it only provides a means for you to access the shell. The shell, which is also programmable then makes it possible to communicate with the kernel using commands.
+
+Different classifications of Linux commands fall under the following classifications:
+
+### 1. Program Executables (File System Commands)
+
+When you run a command, Linux searches through the directories stored in the $PATH environmental variable from left to right for the executable of that specific command.
+
+You can view the directories in the $PATH as follows:
+
+```
+$ echo $PATH
+/home/aaronkilik/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
+```
+
+In the above order, the directory /home/aaronkilik/bin will be searched first followed by /usr/local/sbin and so on, the order is significant in the search process.
+
+Examples of file system commands in /usr/bin directory:
+
+```
+$ ll /bin/
+```
+
+Sample Output
+
+```
+total 16284
+drwxr-xr-x  2 root root    4096 Jul 31 16:30 ./
+drwxr-xr-x 23 root root    4096 Jul 31 16:29 ../
+-rwxr-xr-x  1 root root    6456 Apr 14 18:53 archdetect*
+-rwxr-xr-x  1 root root 1037440 May 17 16:15 bash*
+-rwxr-xr-x  1 root root  520992 Jan 20  2016 btrfs*
+-rwxr-xr-x  1 root root  249464 Jan 20  2016 btrfs-calc-size*
+lrwxrwxrwx  1 root root       5 Jul 31 16:19 btrfsck -> btrfs*
+-rwxr-xr-x  1 root root  278376 Jan 20  2016 btrfs-convert*
+-rwxr-xr-x  1 root root  249464 Jan 20  2016 btrfs-debug-tree*
+-rwxr-xr-x  1 root root  245368 Jan 20  2016 btrfs-find-root*
+-rwxr-xr-x  1 root root  270136 Jan 20  2016 btrfs-image*
+-rwxr-xr-x  1 root root  249464 Jan 20  2016 btrfs-map-logical*
+-rwxr-xr-x  1 root root  245368 Jan 20  2016 btrfs-select-super*
+-rwxr-xr-x  1 root root  253816 Jan 20  2016 btrfs-show-super*
+-rwxr-xr-x  1 root root  249464 Jan 20  2016 btrfstune*
+-rwxr-xr-x  1 root root  245368 Jan 20  2016 btrfs-zero-log*
+-rwxr-xr-x  1 root root   31288 May 20  2015 bunzip2*
+-rwxr-xr-x  1 root root 1964536 Aug 19  2015 busybox*
+-rwxr-xr-x  1 root root   31288 May 20  2015 bzcat*
+lrwxrwxrwx  1 root root       6 Jul 31 16:19 bzcmp -> bzdiff*
+-rwxr-xr-x  1 root root    2140 May 20  2015 bzdiff*
+lrwxrwxrwx  1 root root       6 Jul 31 16:19 bzegrep -> bzgrep*
+-rwxr-xr-x  1 root root    4877 May 20  2015 bzexe*
+lrwxrwxrwx  1 root root       6 Jul 31 16:19 bzfgrep -> bzgrep*
+-rwxr-xr-x  1 root root    3642 May 20  2015 bzgrep*
+```
+
+### 2. Linux Aliases
+
+These are user defined commands, they are created using the alias shell built-in command, and contain other shell commands with some options and arguments. The ideas is to basically use new and short names for lengthy commands.
+
+The syntax for creating an alias is as follows:
+
+```
+$ alias newcommand='command -options'
+```
+
+To list all aliases on your system, issue the command below:
+
+```
+$ alias -p
+alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
+alias egrep='egrep --color=auto'
+alias fgrep='fgrep --color=auto'
+alias grep='grep --color=auto'
+alias l='ls -CF'
+alias la='ls -A'
+alias ll='ls -alF'
+alias ls='ls --color=auto'
+```
+
+To create a new alias in Linux, go through some below examples.
+
+```
+$ alias update='sudo apt update'
+$ alias upgrade='sudo apt dist-upgrade'
+$ alias -p | grep 'up'
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/08/Create-Aliase-in-Linux.png)
+
+However, the aliases we have created above only work temporarily, when the system is restarted, they will not work after the next boot. You can set permanent aliases in your `.bashrc` file as shown below.
+
+![](http://www.tecmint.com/wp-content/uploads/2016/08/Set-Linux-Aliases-Permanent.png)
+
+After adding them, run the command below to active.
+
+```
+$ source ~/.bashrc
+```
+
+### 3. Linux Shell Reserved Words
+
+In shell programming, words such as if, then, fi, for, while, case, esac, else, until and many others are shell reserved words. As the description implies, they have specialized meaning to the shell.
+
+You can list out all Linux shell keywords using type command as shown:
+
+```
+$ type if then fi for while case esac else until
+if is a shell keyword
+then is a shell keyword
+fi is a shell keyword
+for is a shell keyword
+while is a shell keyword
+case is a shell keyword
+esac is a shell keyword
+else is a shell keyword
+until is a shell keyword
+```
+
+Suggested Read: 10 Useful Linux Chaining Operators with Practical Examples
+
+### 4. Linux Shell Functions
+
+A shell function is a group of commands that are executed collectively within the current shell. Functions help to carry out a specific task in a shell script. The conventional form of writing shell functions in a script is:
+
+```
+function_name() {
+command1
+command2
+…….
+}
+```
+
+Alternatively,
+
+```
+function function_name {
+command1
+command2
+…….
+}
+```
+
+Let’s take a look at how to write shell functions in a script named shell_functions.sh.
+
+```
+#!/bin/bash 
+#write a shell function to update and upgrade installed packages 
+upgrade_system(){
+sudo apt update;
+sudo apt dist-upgrade;
+}
+#execute function
+upgrade_system
+```
+
+Instead of executing the two commands: sudo apt update and sudo apt dist-upgrade from the command line, we have written a simple shell function to execute the two commands as a single command, upgrade_system within a script.
+
+Save the file and thereafter, make the script executable. Finally run it as below:
+
+```
+$ chmod +x shell_functions.sh
+$ ./shell_functions.sh
+```
+
+![](http://www.tecmint.com/wp-content/uploads/2016/08/Linux-Shell-Functions-Script.png)
+
+### 5. Linux Shell Built-in Commands
+
+These are Linux commands that built into the shell, thus you cannot find them within the file system. They include pwd, cd, bg, alias, history, type, source, read, exit and many others.
+
+You can list or check Linux built-in commands using type command as shown:
+
+```
+$ type pwd
+pwd is a shell builtin
+$ type cd
+cd is a shell builtin
+$ type bg
+bg is a shell builtin
+$ type alias
+alias is a shell builtin
+$ type history
+history is a shell builtin
+```
+
+Learn about some Linux built-in Commands usage:
+
+- [15 ‘pwd’ Command Examples in Linux][2]
+- [15 ‘cd’ Command Examples in Linux][3]
+- [Learn The Power of Linux ‘history’ Command][4]
+
+### Conclusion
+
+As a Linux user, it is always important to know the type of command you are running. I believe, with the precise and simple-to-understand explanation above including a few relevant illustrations, you probably have a good understanding of the [various categories of Linux commands][5].
+
+You can as well get in tough through the comment section below for any questions or supplementary ideas that you would like to offer us.
+
+--------------------------------------------------------------------------------
+
+via: http://linoxide.com/firewall/pfsense-setup-basic-configuration/
+
+作者:[Aaron Kili ][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
+[1]: http://www.tecmint.com/different-types-of-linux-shells/
+[2]: http://www.tecmint.com/pwd-command-examples/
+[3]: http://www.tecmint.com/cd-command-in-linux/
+[4]: http://www.tecmint.com/history-command-examples/
+[5]: http://www.tecmint.com/category/linux-commands/
diff --git a/sources/tech/20160827 4 Best Linux Boot Loaders.md b/sources/tech/20160827 4 Best Linux Boot Loaders.md
new file mode 100644
index 0000000000..7ea63cdb94
--- /dev/null
+++ b/sources/tech/20160827 4 Best Linux Boot Loaders.md	
@@ -0,0 +1,78 @@
+translating by ucasFL
+
+4 Best Linux Boot Loaders
+====
+
+When you turn on your machine, immediately after POST (Power On Self Test) is completed successfully, the BIOS locates the configured bootable media, and reads some instructions from the master boot record (MBR) or GUID partition table which is the first 512 bytes of the bootable media. The MBR contains two important sets of information, one is the boot loader and two, the partition table.
+
+### What is a Boot Loader?
+
+A boot loader is a small program stored in the MBR or GUID partition table that helps to load an operating system into memory. Without a boot loader, your operating system can not be loaded into memory.
+
+There are several boot loaders we can install together with Linux on our systems and in this article, we shall briefly talk about a handful of the best Linux boot loaders to work with.
+
+### 1. GNU GRUB
+
+GNU GRUB is a popular and probably the most used multiboot Linux boot loader available, based on the original GRUB (GRand Unified Bootlader) which was created by Eirch Stefan Broleyn. It comes with several improvements, new features and bug fixes as enhancements of the original GRUB program.
+
+Importantly, GRUB 2 has now replaced the GRUB. And notably, the name GRUB was renamed to GRUB Legacy and is not actively developed, however, it can be used for booting older systems since bug fixes are still on going.
+
+GRUB has the following prominent features:
+
+- Supports multiboot
+- Supports multiple hardware architectures and operating systems such as Linux and Windows
+- Offers a Bash-like interactive command line interface for users to run GRUB commands as well interact with configuration files
+- Enables access to GRUB editor
+- Supports setting of passwords with encryption for security
+- Supports booting from a network combined with several other minor features
+
+Visit Homepage: <https://www.gnu.org/software/grub/>
+
+### 2. LILO (Linux Loader)
+
+LILO is a simple yet powerful and stable Linux boot loader. With the growing popularity and use of GRUB, which has come with numerous improvements and powerful features, LILO has become less popular among Linux users.
+
+While it loads, the word “LILO” is displayed on the screen and each letter appears before or after a particular event has occurred. However, the development of LILO was stopped in December 2015, it has a number of features as listed below:
+
+- Does not offer an interactive command line interface
+- Supports several error codes
+- Offers no support for booting from a network
+- All its files are stored in the first 1024 cylinders of a drive
+- Faces limitation with BTFS, GPT and RAID plus many more.
+
+Visit Homepage: <http://lilo.alioth.debian.org/>
+
+### 3. BURG – New Boot Loader
+
+Based on GRUB, BURG is a relatively new Linux boot loader. Because it is derived from GRUB, it ships in with some of the primary GRUB features, nonetheless, it also offers remarkable features such as a new object format to support multiple platforms including Linux, Windows, Mac OS, FreeBSD and beyond.
+
+Additionally, it supports a highly configurable text and graphical mode boot menu, stream plus planned future improvements for it to work with various input/output devices.
+
+Visit Homepage: <https://launchpad.net/burg>
+
+### 4. Syslinux
+
+Syslinux is an assortment of light weight boot loaders that enable booting from CD-ROMs, from a network and so on. It supports filesystems such as FAT for MS-DOS, and ext2, ext3, ext4 for Linux. It as well supports uncompressed single-device Btrfs.
+
+Note that Syslinux only accesses files in its own partition, therefore, it does not offer multi-filesystem boot capabilities.
+
+Visit Homepage: <http://www.syslinux.org/wiki/index.php?title=The_Syslinux_Project>
+
+### Conclusion
+
+A boot loader allows you to manage multiple operating systems on your machine and select which one to use at a particular time, without it, your machine can not load the kernel and the rest of the operating system files.
+
+Have we missed any tip-top Linux boot loader here? If so, then let us know by using the comment form below by making suggestions of any commendable boot loaders that can support Linux operating system.
+
+
+--------------------------------------------------------------------------------
+
+via: http://linoxide.com/firewall/pfsense-setup-basic-configuration/
+
+作者:[Aaron Kili][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/best-linux-boot-loaders/
diff --git a/sources/tech/20160830 Spark comparison - AWS vs. GCP.md b/sources/tech/20160830 Spark comparison - AWS vs. GCP.md
new file mode 100644
index 0000000000..acb3c23367
--- /dev/null
+++ b/sources/tech/20160830 Spark comparison - AWS vs. GCP.md	
@@ -0,0 +1,82 @@
+Spark comparison: AWS vs. GCP
+===========
+
+
+>Tianhui Michael Li and Ariel M’ndange-Pfupfu will lead a hands-on online course Oct 10, 12, and 14, 2016: Distributed Computing with Spark for Beginners. Instruction includes building functioning applications from end-to-end and mastering critical tooling around Spark.
+
+There’s little doubt that cloud computing will play an important role in data science for the foreseeable future. The flexible, scalable, on-demand computing power available is an important resource, and as a result, there’s a lot of competition between the providers of this service. Two of the biggest players in the space are [Amazon Web Services (AWS)][1] and [Google Cloud Platform (GCP)][2].
+
+This article includes a short comparison of distributed Spark workloads in AWS and GCP—both in terms of setup time and operating cost. We ran this experiment with our students at The Data Incubator, [a big data training organization][3] that helps companies hire top-notch data scientists and train their employees on the latest data science skills. Even with the efficiencies built into Spark, the cost and time of distributed workloads can be substantial, and we are always looking for the most efficient technologies so our students are learning the best and fastest tools.
+
+### Submitting Spark jobs to the cloud
+
+Spark is a popular distributed computation engine that incorporates MapReduce-like aggregations into a more flexible, abstract framework. There are APIs for Python and Java, but writing applications in Spark’s native Scala is preferable. That makes job submission simple, as you can package your application and all its dependencies into one JAR file.
+
+
+It’s common to use Spark in conjunction with HDFS for distributed data storage, and YARN for cluster management; this makes Spark a perfect fit for AWS’s Elastic MapReduce (EMR) clusters and GCP’s Dataproc clusters. Both EMR and Dataproc clusters have HDFS and YARN preconfigured, with no extra work required.
+
+### Configuring cloud services
+
+Managing data, clusters, and jobs from the command line is more scalable than using the web interface. For AWS, this means installing and using the command-line interface (cli). You’ll have to set up your credentials beforehand as well as make a separate keypair for the EC2 instances that are used under the hood. You’ll also need to set up roles—basically permissions—for both users (making sure they have sufficient rights) and EMR itself (usually running aws emr create-default-roles in the cli is good enough to get started).
+
+For GCP the process is more straightforward. If you install the Google Cloud SDK and sign in with your Google account, you should be able to do most things right off the bat. The thing to remember here is to enable the relevant APIs in the API Manager: Compute Engine, Dataproc, and Cloud Storage JSON.
+
+Once you have things set up to your liking, the fun part begins! Using commands like aws s3 cp or gsutil cp you can copy your data into the cloud. Once you have buckets set up for your inputs, outputs, and anything else you might need, running your app is as easy as starting up a cluster and submitting the JAR file. Make sure you know where the logs are kept—it can be tricky to track down problems or bugs in a cloud environment.
+
+### You get what you pay for
+
+When it comes to cost, Google’s service is more affordable in several ways. First, the raw cost of purchasing computing power is cheaper. Running a Google Compute Engine machine with 4 vCPUs and 15 GB of RAM will run you $0.20 every hour, or $0.24 with Dataproc. An identically-specced AWS instance will cost you $0.336 per hour running EMR.
+
+The second factor to consider is the granularity of the billing. AWS charges by the hour, so you pay the full rate even if your job takes 15 minutes. GCP charges by the minute, with a 10-minute minimum charge. This ends up being a huge difference in cost in a lot of use cases.
+
+Both services have various other discounts. You can effectively bid on spare cloud capacity with AWS’s spot instances or GCP’s preemptible instances. These will be cheaper than dedicated, on-demand instances, but they’re not guaranteed to be available. Discounted rates are available on GCP if your instances live for long periods of time (25% to 100% of the month). On AWS, paying some of the costs upfront or buying in bulk can save you some money. The bottom line is, if you’re a power user and you use cloud computing on a regular or even constant basis, you’ll need to delve deeper and perform your own calculations.
+
+Lastly, the costs for new users wanting to try out these services are lower for GCP. They offer a 60-day free trial with $300 in credit to use however you want. AWS only offers a free tier where certain services are free to a certain point or discounted, so you will end up paying to run Spark jobs.This means that if you want to test out Spark for the first time, you’ll have more freedom to do what you want on GCP without worrying about price.
+
+### Performance comparison
+
+We set up a trial to compare the performance and cost of a typical Spark workload. The trial used clusters with one master and five core instances of AWS’s m3.xlarge and GCP’s n1-standard-4. They differ slightly in specification, but the number of virtual cores and amount of memory is the same. In fact, they behaved almost identically when it came to job execution time.
+
+The job itself involved parsing, filtering, joining, and aggregating data from the publicly available Stack Exchange Data Dump. We ran the same JAR on a ~50M subset of the data (Cross Validated) and then on the full ~9.5G data set.
+
+![](https://d3ansictanv2wj.cloudfront.net/1400_img_1_AWS_GCP-25ed6069029112a8439d89999796be18.jpg)
+>Figure 1. Credit: Michael Li and Ariel M'ndange-Pfupfu.
+
+![](https://d3ansictanv2wj.cloudfront.net/1400_img_2_AWS_GCP-448714718896b21e32f8b47d4657fc8c.jpg)
+>Figure 2. Credit: Michael Li and Ariel M'ndange-Pfupfu.
+
+The short job clearly benefited from GCP’s by-the-minute billing, being charged only for 10 minutes of cluster time, whereas AWS charged for a full hour.  But even the longer job was cheaper on GPS both because of fractional-hour billing and a lower per-unit time cost for comparable performance.  It’s also worth noting that storage costs weren’t included in this comparison.
+
+### Conclusion
+
+AWS was the first mover in the space, and this shows in the API. Its ecosystem is vast, but its permissions model is a little dated, and its configuration is a little arcane. By contrast, Google is the shiny new entrant in this space and has polished off some of the rough edges. It is missing some features on our wishlist, like an easy way to auto-terminate clusters and detailed billing information broken down by job. Also, for managing tasks programmatically in Python, the API client library isn’t as full-featured as AWS’s Boto.
+
+If you’re new to cloud computing, GCP is easier to get up and running, and the credits make it a tempting platform. Even if you are already used to AWS, you may still find the cost savings make switching worth it, although the switching costs may not make moving to GCP worth it.
+
+Ultimately, it’s difficult to make sweeping statements about these services because they’re not just one entity; they’re entire ecosystems of integrated parts, and both have pros and cons. The real winners are the users.  As an example, at The Data Incubator, our Ph.D. data science fellows really appreciate the cost reduction as they learn about distributed workloads.  And while our big data corporate training clients may be less price sensitive, they appreciate being able to crunch enterprise data faster, while holding price constant. Data scientists can now enjoy the multitude of options available, and the benefits of having a competitive cloud computing market.
+
+
+
+
+
+
+
+
+
+
+
+--------------------------------------------------------------------------------
+
+via: https://www.oreilly.com/ideas/spark-comparison-aws-vs-gcp?utm_source=dbweekly&utm_medium=email
+
+作者:[Michael Li][a]  [Ariel M'Ndange-Pfupfu][b] 
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.oreilly.com/people/76a5b-michael-li
+[b]: https://www.oreilly.com/people/Ariel-Mndange-Pfupfu
+[1]: https://aws.amazon.com/
+[2]: https://cloud.google.com/
+[3]: https://www.thedataincubator.com/training.html?utm_source=OReilly&utm_medium=blog&utm_campaign=AWSvsGCP
diff --git a/sources/tech/20160831 Apache Spark Scale - A 60 TB  production use case.md b/sources/tech/20160831 Apache Spark Scale - A 60 TB  production use case.md
new file mode 100644
index 0000000000..28fbd202ec
--- /dev/null
+++ b/sources/tech/20160831 Apache Spark Scale - A 60 TB  production use case.md	
@@ -0,0 +1,131 @@
+Apache Spark @Scale: A 60 TB+ production use case
+===========
+
+Facebook often uses analytics for data-driven decision making. Over the past few years, user and product growth has pushed our analytics engines to operate on data sets in the tens of terabytes for a single query. Some of our batch analytics is executed through the venerable [Hive][1] platform (contributed to Apache Hive by Facebook in 2009) and [Corona][2], our custom MapReduce implementation. Facebook has also continued to grow its Presto footprint for ANSI-SQL queries against several internal data stores, including Hive. We support other types of analytics such as graph processing and machine learning ([Apache Giraph][3]) and streaming (e.g., [Puma][4], [Swift][5], and [Stylus][6]).
+
+While the sum of Facebook's offerings covers a broad spectrum of the analytics space, we continually interact with the open source community in order to share our experiences and also learn from others. [Apache Spark][7] was started by Matei Zaharia at UC-Berkeley's AMPLab in 2009 and was later contributed to Apache in 2013. It is currently one of the fastest-growing data processing platforms, due to its ability to support streaming, batch, imperative (RDD), declarative (SQL), graph, and machine learning use cases all within the same API and underlying compute engine. Spark can efficiently leverage larger amounts of memory, optimize code across entire pipelines, and reuse JVMs across tasks for better performance. Recently, we felt Spark had matured to the point where we could compare it with Hive for a number of batch-processing use cases. In the remainder of this article, we describe our experiences and lessons learned while scaling Spark to replace one of our Hive workload
+
+### Use case: Feature preparation for entity ranking
+
+Real-time entity ranking is used in a variety of ways at Facebook. For some of these online serving platforms raw feature values are generated offline with Hive and data loaded into its real-time affinity query system. The old Hive-based infrastructure built years ago was computationally resource intensive and challenging to maintain because the pipeline was sharded into hundreds of smaller Hive jobs. In order to enable fresher feature data and improve manageability, we took one of the existing pipelines and tried to migrate it to Spark.
+
+### Previous Hive implementation
+
+The Hive-based pipeline was composed of three logical stages where each stage corresponded to hundreds of smaller Hive jobs sharded by entity_id, since running large Hive jobs for each stage was less reliable and limited by the maximum number of tasks per job.
+
+
+![](https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-xaf1/t39.2365-6/14050196_257613611304247_245043082_n.jpg)
+
+The three logical steps can be summarized as follows:
+
+1. Filter out non-production features and noise.
+2. Aggregate on each (entity_id, target_id) pair.
+3. Shard the table into N number of shards and pipe each shard through a custom binary to generate a custom index file for online querying.
+
+The Hive-based pipeline building the index took roughly three days to complete. It was also challenging to manage, because the pipeline contained hundreds of sharded jobs that made monitoring difficult. There was no easy way to gauge the overall progress of the pipeline or calculate an ETA. When considering the aforementioned limitations of the existing Hive pipeline, we decided to attempt to build a faster and more manageable pipeline with Spark.
+
+### Spark implementation
+
+Debugging at full scale can be slow, challenging, and resource intensive. We started off by converting the most resource intensive part of the Hive-based pipeline: stage two. We started with a sample of 50 GB of compressed input, then gradually scaled up to 300 GB, 1 TB, and then 20 TB. At each size increment, we resolved performance and stability issues, but experimenting with 20 TB is where we found our largest opportunity for improvement.
+
+While running on 20 TB of input, we discovered that we were generating too many output files (each sized around 100 MB) due to the large number of tasks. Three out of 10 hours of job runtime were spent moving files from the staging directory to the final directory in HDFS. Initially, we considered two options: Either improve batch renaming in HDFS to support our use case, or configure Spark to generate fewer output files (difficult due to the large number of tasks — 70,000 — in this stage). We stepped back from the problem and considered a third alternative. Since the tmp_table2 table we generate in step two of the pipeline is temporary and used only to store the pipeline's intermediate output, we were essentially compressing, serializing, and replicating three copies for a single read workload with terabytes of data. Instead, we went a step further: Remove the two temporary tables and combine all three Hive stages into a single Spark job that reads 60 TB of compressed data and performs a 90 TB shuffle and sort. The final Spark job is as follows:
+
+
+![](https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-xfp1/t39.2365-6/14146896_1073876729364007_1912864323_n.jpg)
+
+### How did we scale Spark for this job?
+
+Of course, running a single Spark job for such a large pipeline didn't work on the first try, or even on the 10th try. As far as we know, this is the largest real-world Spark job attempted in terms of shuffle data size (Databrick's Petabyte sort was on synthetic data). It took numerous improvements and optimizations to the core Spark infrastructure and our application to get this job to run. The upside of this effort is that many of these improvements are applicable to other large-scale workloads for Spark, and we were able to contribute all our work back into the open source Apache Spark project — see the JIRAs for additional details. Below, we highlight the major improvements that enabled one of the entity ranking pipelines to be deployed into production.
+
+### Reliability fixes
+
+#### Dealing with frequent node reboots
+
+In order to reliably execute long-running jobs, we want the system to be fault-tolerant and recover from failures (mainly due to machine reboots that can occur due to normal maintenance or software errors). Although Spark is designed to tolerate machine reboots, we found various bugs/issues that needed to be addressed before it was robust enough to handle common failures.
+
+- Make PipedRDD robust to fetch failure (SPARK-13793): The previous implementation of PipedRDD was not robust enough to fetch failures that occur due to node reboots, and the job would fail whenever there was a fetch failure. We made change in the PipedRDD to handle fetch failure gracefully so the job can recover from these types of fetch failure.
+- Configurable max number of fetch failures (SPARK-13369): With long-running jobs such as this one, probability of fetch failure due to a machine reboot increases significantly. The maximum allowed fetch failures per stage was hard-coded in Spark, and, as a result, the job used to fail when the max number was reached. We made a change to make it configurable and increased it from four to 20 for this use case, which made the job more robust against fetch failure.
+- Less disruptive cluster restart: Long-running jobs should be able to survive a cluster restart so we don't waste all the processing completed so far. Spark's restartable shuffle service feature lets us preserve the shuffle files after node restart. On top of that, we implemented a feature in Spark driver to be able to pause scheduling of tasks so the jobs don't fail due to excessive task failure due to cluster restart.
+
+#### Other reliability fixes
+
+- Unresponsive driver (SPARK-13279): Spark driver was stuck due to O(N^2) operations while adding tasks, resulting in the job being stuck and killed eventually. We fixed the issue by removing the unnecessary O(N^2) operations.
+- Excessive driver speculation: We discovered that the Spark driver was spending a lot of time in speculation when managing a large number of tasks. In the short term, we disabled speculation for this job. We are currently working on a change in the Spark driver to reduce speculation time in the long term.
+- TimSort issue due to integer overflow for large buffer (SPARK-13850): We found that Spark's unsafe memory operation had a bug that leads to memory corruption in TimSort. Thanks to Databricks folks for fixing this issue, which enabled us to operate on large in-memory buffer.
+- Tune the shuffle service to handle large number of connections: During the shuffle phase, we saw many executors timing out while trying to connect to the shuffle service. Increasing the number of Netty server threads (spark.shuffle.io.serverThreads) and backlog (spark.shuffle.io.backLog) resolved the issue.
+- Fix Spark executor OOM (SPARK-13958) (deal maker): It was challenging to pack more than four reduce tasks per host at first. Spark executors were running out of memory because there was bug in the sorter that caused a pointer array to grow indefinitely. We fixed the issue by forcing the data to be spilled to disk when there is no more memory available for the pointer array to grow. As a result, now we can run 24 tasks/host without running out of memory.
+
+### Performance improvements
+
+After implementing the reliability improvements above, we were able to reliably run the Spark job. At this point, we shifted our efforts on performance-related projects to get the most out of Spark. We used Spark's metrics and several profilers to find some of the performance bottlenecks.
+
+#### Tools we used to find performance bottleneck
+
+- Spark UI Metrics: Spark UI provides great insight into where time is being spent in a particular phase. Each task's execution time is split into sub-phases that make it easier to find the bottleneck in the job.
+- Jstack: Spark UI also provides an on-demand jstack function on an executor process that can be used to find hotspots in the code.
+- Spark Linux Perf/Flame Graph support: Although the two tools above are very handy, they do not provide an aggregated view of CPU profiling for the job running across hundreds of machines at the same time. On a per-job basis, we added support for enabling Perf profiling (via libperfagent for Java symbols) and can customize the duration/frequency of sampling. The profiling samples are aggregated and displayed as a Flame Graph across the executors using our internal metrics collection framework.
+
+### Performance optimizations
+
+- Fix memory leak in the sorter (SPARK-14363) (30 percent speed-up): We found an issue when tasks were releasing all memory pages but the pointer array was not being released. As a result, large chunks of memory were unused and caused frequent spilling and executor OOMs. Our change now releases memory properly and enabled large sorts to run efficiently. We noticed a 30 percent CPU improvement after this change.
+- Snappy optimization (SPARK-14277) (10 percent speed-up): A JNI method — (Snappy.ArrayCopy) — was being called for each row being read/written. We raised this issue, and the Snappy behavior was changed to use the non-JNI based System.ArrayCopy instead. This change alone provided around 10 percent CPU improvement.
+- Reduce shuffle write latency (SPARK-5581) (up to 50 percent speed-up): On the map side, when writing shuffle data to disk, the map task was opening and closing the same file for each partition. We made a fix to avoid unnecessary open/close and observed a CPU improvement of up to 50 percent for jobs writing a very high number of shuffle partitions.
+- Fix duplicate task run issue due to fetch failure (SPARK-14649): The Spark driver was resubmitting already running tasks when a fetch failure occurred, which led to poor performance. We fixed the issue by avoiding rerunning the running tasks, and we saw the job was more stable when fetch failures occurred.
+- Configurable buffer size for PipedRDD (SPARK-14542) (10 percent speed-up): While using a PipedRDD, we found out that the default buffer size for transferring the data from the sorter to the piped process was too small and our job was spending more than 10 percent of time in copying the data. We made the buffer size configurable to avoid this bottleneck.
+- Cache index files for shuffle fetch speed-up (SPARK-15074): We observed that the shuffle service often becomes the bottleneck, and the reducers spend 10 percent to 15 percent of time waiting to fetch map data. Digging deeper into the issue, we found out that the shuffle service is opening/closing the shuffle index file for each shuffle fetch. We made a change to cache the index information so that we can avoid file open/close and reuse the index information for subsequent fetches. This change reduced the total shuffle fetch time by 50 percent.
+- Reduce update frequency of shuffle bytes written metrics (SPARK-15569) (up to 20 percent speed-up): Using the Spark Linux Perf integration, we found that around 20 percent of the CPU time was being spent probing and updating the shuffle bytes written metrics.
+- Configurable initial buffer size for Sorter (SPARK-15958) (up to 5 percent speed-up): The default initial buffer size for the Sorter is too small (4 KB), and we found that it is very small for large workloads — and as a result we waste a significant amount of time expending the buffer and copying the contents. We made a change to make the buffer size configurable, and with large buffer size of 64 MB we could avoid significant data copying, making the job around 5 percent faster.
+- Configuring number of tasks: Since our input size is 60 T and each HDFS block size is 256 M, we were spawning more than 250,000 tasks for the job. Although we were able to run the Spark job with such a high number of tasks, we found that there is significant performance degradation when the number of tasks is too high. We introduced a configuration parameter to make the map input size configurable, so we can reduce that number by 8x by setting the input split size to 2 GB.
+
+After all these reliability and performance improvements, we are pleased to report that we built and deployed a faster and more manageable pipeline for one of our entity ranking systems, and we provided the ability for other similar jobs to run in Spark.
+
+### Spark pipeline vs. Hive pipeline performance comparison
+
+We used the following performance metrics to compare the Spark pipeline against the Hive pipeline. Please note that these numbers aren't a direct comparison of Spark to Hive at the query or job level, but rather a comparison of building an optimized pipeline with a flexible compute engine (e.g., Spark) instead of a compute engine that operates only at the query/job level (e.g., Hive).
+
+CPU time: This is the CPU usage from the perspective of the OS. For example, if you have a job that is running only one process on a 32-core machine using 50 percent of all CPU for 10 seconds, then your CPU time would be 32 * 0.5 * 10 = 160 CPU seconds.
+
+![](https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-xpt1/t39.2365-6/14146892_595234533986285_2004398348_n.jpg)
+
+CPU reservation time: This is the CPU reservation from the perspective of the resource management framework. For example, if we reserve a 32-core machine for 10 seconds to run the job, the CPU reservation time is 32 * 10 = 320 CPU seconds. The ratio of CPU time to CPU reservation time reflects how well are we utilizing the reserved CPU resources on the cluster. When accurate, the reservation time provides a better comparison between execution engines when running the same workloads when compared with CPU time. For example, if a process requires 1 CPU second to run but must reserve 100 CPU seconds, it is less efficient by this metric than a process that requires 10 CPU seconds but reserves only 10 CPU seconds to do the same amount of work. We also compute the memory reservation time but do not include it here, since the numbers were similar to the CPU reservation time due to running experiments on the same hardware, and that in both the Spark and Hive cases we do not cache data in memory. Spark has the ability to cache data in memory, but due to our cluster memory limitations we decided to work out-of-core, similar to Hive.
+
+![](https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-xfa1/t39.2365-6/14129680_325754934432503_513809233_n.jpg)
+
+Latency: End-to-end elapsed time of the job.
+
+![](https://fbcdn-dragon-a.akamaihd.net/hphotos-ak-xap1/t39.2365-6/14129681_178723715883876_1030939470_n.jpg)
+
+### Conclusion and future work
+
+Facebook uses performant and scalable analytics to assist in product development. Apache Spark offers the unique ability to unify various analytics use cases into a single API and efficient compute engine. We challenged Spark to replace a pipeline that decomposed to hundreds of Hive jobs into a single Spark job. Through a series of performance and reliability improvements, we were able to scale Spark to handle one of our entity ranking data processing use cases in production. In this particular use case, we showed that Spark could reliably shuffle and sort 90 TB+ intermediate data and run 250,000 tasks in a single job. The Spark-based pipeline produced significant performance improvements (4.5-6x CPU, 3-4x resource reservation, and ~5x latency) compared with the old Hive-based pipeline, and it has been running in production for several months.
+
+While this post details our most challenging use case for Spark, a growing number of customer teams have deployed Spark workloads into production. Performance, maintainability, and flexibility are the strengths that continue to drive more use cases to Spark. Facebook is excited to be a part of the Spark open source community and will work together to develop Spark toward its full potential.
+
+
+
+
+
+
+
+
+--------------------------------------------------------------------------------
+
+via: https://code.facebook.com/posts/1671373793181703/apache-spark-scale-a-60-tb-production-use-case/?utm_source=dbweekly&utm_medium=email
+
+作者:[Sital Kedia][a]  [王硕杰][b]  	[Avery Ching][c]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.facebook.com/sitalkedia
+[b]: https://www.facebook.com/shuojiew
+[c]: https://code.facebook.com/posts/1671373793181703/apache-spark-scale-a-60-tb-production-use-case/?utm_source=dbweekly&utm_medium=email#
+[1]: https://code.facebook.com/posts/370832626374903/even-faster-data-at-the-speed-of-presto-orc/
+[2]: https://www.facebook.com/notes/facebook-engineering/under-the-hood-scheduling-mapreduce-jobs-more-efficiently-with-corona/10151142560538920/
+[3]: https://code.facebook.com/posts/509727595776839/scaling-apache-giraph-to-a-trillion-edges/
+[4]: https://research.facebook.com/publications/realtime-data-processing-at-facebook/
+[5]: https://research.facebook.com/publications/realtime-data-processing-at-facebook/
+[6]: https://research.facebook.com/publications/realtime-data-processing-at-facebook/
+[7]: http://spark.apache.org/
+
diff --git a/sources/tech/20160901 A Raspberry Pi Hadoop Cluster with Apache Spark on YARN - Big Data 101.md b/sources/tech/20160901 A Raspberry Pi Hadoop Cluster with Apache Spark on YARN - Big Data 101.md
new file mode 100644
index 0000000000..f001632f4f
--- /dev/null
+++ b/sources/tech/20160901 A Raspberry Pi Hadoop Cluster with Apache Spark on YARN - Big Data 101.md	
@@ -0,0 +1,247 @@
+chisper is translating.
+
+A Raspberry Pi Hadoop Cluster with Apache Spark on YARN: Big Data 101
+======
+
+Sometimes on DQYDJ we like to reveal a little bit of the process behind the data.  In that past, that has meant articles on how we ingest fixed width data with R and how we model the cost of a Negative Income Tax in America, amongst many other fine examples.
+
+Today I’ll be sharing some explorations with Big Data.  For a change of pace I’ll be doing it with the world’s most famous Little Computer – the Raspberry Pi.  If that’s not your thing, we’ll see you in the next piece (probably with some already finished data).  For the rest of you, read on… today we’re going to build a Raspberry Pi Hadoop Cluster!
+
+### I. Why Even Build a Raspberry Pi Hadoop Cluster?
+
+![](https://dqydj.com/wp-content/uploads/2016/08/IMG_9132-245x300.png)
+>3/4 Angle of my Three Node Raspberry Pi Hadoop Cluster
+
+We do a lot of work with data here on DQYDJ, but nothing we have done qualifies as Big Data.
+
+Like so many controversial topics, the difference between “Big” Data and lower-case data might be best explained with a joke:
+
+>“If it fits in RAM, it’s not Big Data.” – Unknown (If you know, pass it along!)
+
+It seems like there are two solutions to the problem here:
+
+1. We can find a dataset big enough to overwhelm any actual physical or virtual RAM on our home computers.
+2. Or, we can buy some computers where the data we have now would overwhelm the machine without special treatment.
+
+Enter the Raspberry Pi 2 Model B!
+
+These impressive little works of art and engineering have 1 GB of RAM and your choice of MicroSD card for a hard drive.  Furthermore, you can get them for under $50 each, so you can build out a Hadoop cluster for under $250.
+
+There might be no cheaper ticket into the Big Data world!
+
+
+### II. Building The Raspberry Pi Hadoop Cluster
+
+![](https://dqydj.com/wp-content/uploads/2016/08/IMG_9124-218x300.png)
+>My favorite part of the build – the raw materials!
+
+Now I’ll link you to what I used to build the cluster itself. Note that if you do buy on Amazon using these links you’ll be supporting the site.  (Thank you for the support!)
+
+- Raspberry Pi 2 Model B (x3)
+- Pictures of ingredients for a Raspberry Pi Hadoop Cluster
+- 4 Layer Acrylic Case/Stand
+- 6 Post USB Charger (I picked the White RAVPower 50W 10A 6-Port USB Charger)
+- 5 Port Ethernet Switch (Fast is fine, the ethernet ports on the Pi are only 100 Mbit)
+- MicroSD Cards (This 5 pack of 32 GB cards was perfectly fine)
+- Short MicroUSB Cables (to power the Pis)
+- Short Ethernet Patch Cables
+- Two sided foam tape (I had some 3M brand, it worked wonderfully)
+
+#### Now Bake the Pis!
+
+1. First, mount the three Raspberry Pis, one each to an acrylic panel (see the below image).
+2. Next, mount the ethernet switch to a fourth acrylic panel with 2 sided foam tape.
+3. Mount the USB charger on the acrylic panel which will become the ‘top’ (again with 2 sided foam tape)
+4. Follow the instructions for the acrylic case and build the levels – I chose to put the Pis below the switch and charger (see the two completed build shots).
+
+Figure out a way to get the wires where you need them – if you bought the same USB cables and Ethernet cables as me, I was able to wrap them around the mounting posts between levels.
+
+Leave the Pis unplugged for now – you will burn the Raspbian OS on the MicroSDs before continuing.
+
+![](https://dqydj.com/wp-content/uploads/2016/08/IMG_9126.png)
+>Raspberry Pis Mounted on Acrylic
+
+#### Burn Raspbian to the MicroSDs!
+
+Put Raspbian on 3 MicroSDs – follow the instructions on the [Raspberry Pi Raspbian page][1].  I use my Windows 7 PC for this, and [Win32DiskImager][2].
+
+Pop one of the cards into whatever Pi you want to be the master node, then start it up by plugging in USB!
+
+#### Setup the Master
+
+There is no need for me to repeat an excellent guide here – you should [follow the instructions in Because We Can Geek’s guide][3] and install Hadoop 2.7.1.
+
+There were a few caveats with my setup – I’ll walk you through what I ended up using to get a working setup.  Note that I am using the IP addresses 192.168.1.50 – 192.168.1.52, with the two slaves on .51 and .52 and the master on .50.  Your network will vary, you will have to look around or discuss this step in the comments if you need help setting up static addresses on your network.
+
+Once you are done with the guide, you should enable a swapfile.  Spark on YARN will be cutting it very close on RAM, so faking it with a swapfile is what you’ll need as you approach memory limits.
+
+(If you haven’t done this before, [this tutorial will get you there][4].  Keep swappiness at a low level – MicroSD cards don’t handle it well).
+
+Now I’m ready to share some subtle differences between my setup and Because We Can Geek’s.
+
+For starters, make sure you pick uniform names for all of your Pi nodes – in /etc/hostname, I used ‘RaspberryPiHadoopMaster’ for the master, and ‘RaspberryPiHadoopSlave#’ for the slaves.
+
+Here is my /etc/hosts on the Master:
+
+You will also need to edit a few configuration files in order to get Hadoop, YARN, and Spark playing nicely.  (You may as well make these edits now.)
+
+Here is hdfs-site.xml:
+
+```
+yarn-site.xml (Note the changes in memory!):
+
+slaves:
+
+core-site.xml:
+```
+
+#### Setup the 2 Slaves:
+
+Again, [just follow the instructions on Because We Can Geek][5].  You will need to make minor edits to the files above – note that the master doesn’t change in yarn-site.xml, and you do not need to have a slaves file on the slaves themselves.
+
+
+
+
+### III. Test YARN on Our Raspberry Pi Hadoop Cluster!
+
+![](https://dqydj.com/wp-content/uploads/2016/08/IMG_9130-235x300.png)
+>The Raspberry Pi Hadoop Cluster Straight On!
+
+If everything is working, on the master you should be able to do a:
+
+>\> start-dfs.sh
+
+>\> start-yarn.sh
+
+And see everything come up!  Do this from the Hadoop user, which will be ‘hduser’ if you obey the tutorials.
+
+Next, run ‘hdfs dfsadmin -report’ to see if all three datanodes have come up.  Verify you have the bolded line in your report, ‘Live datanodes (3)’:
+
+```
+Configured Capacity: 93855559680 (87.41 GB)
+Raspberry Pi Hadoop Cluster picture Straight On
+Present Capacity: 65321992192 (60.84 GB)
+DFS Remaining: 62206627840 (57.93 GB)
+DFS Used: 3115364352 (2.90 GB)
+DFS Used%: 4.77%
+Under replicated blocks: 0
+Blocks with corrupt replicas: 0
+Missing blocks: 0
+Missing blocks (with replication factor 1): 0
+————————————————-
+Live datanodes (3):
+Name: 192.168.1.51:50010 (RaspberryPiHadoopSlave1)
+Hostname: RaspberryPiHadoopSlave1
+Decommission Status : Normal
+
+…
+
+…
+
+…
+```
+
+You may now run through some ‘Hello, World!’ type exercises, or feel free to move to the next step!
+
+
+
+### IV. Installing Spark on YARN on Pi!
+
+YARN stands for Yet Another Resource Negotiator, and is included in the base Hadoop install as an easy to use resource manager.
+
+[Apache Spark][6] is another package in the Hadoop ecosystem – it’s an execution engine, much like the (in)famous and [bundled MapReduce][7].  At a general level, Spark benefits from in-memory storage over the disk-based storage of MapReduce.  Some workloads will run at 10-100x the speed of a comparable MapReduce workload – after you’ve got your cluster built, read a bit on the difference between Spark and MapReduce.
+
+On a personal level, I was particularly impressed with the Spark offering because of the easy integration of two languages used quite often by Data Engineers and Scientists – Python and R.
+
+Installation of Apache Spark is very easy – in your home directory, ‘wget <path to a Apache Spark built for Hadoop 2.7>’ ([from this page][8]).  Then, ‘tar -xzf <that tgz file>’.  Finally, copy the resulting directory to /opt and clean up any of the files you downloaded – that’s all there is to it!
+
+I also have a two line ‘spark-env.sh’ file, which is inside the ‘conf’ directory of Spark:
+
+>SPARK_MASTER_IP=192.168.1.50
+
+>SPARK_WORKER_MEMORY=512m
+
+(I’m not even sure this is necessary, since we will be running on YARN)
+
+
+### V. Hello, World! Finding an Interesting Data-set for Apache Spark!
+
+
+The canonical ‘Hello, World!’ in the Hadoop world is to do some sort of word counting.
+
+I decided that I wanted my introduction to do something introspective… why not count my most commonly used words on this site?  Perhaps some big data about myself would be useful…maybe?
+
+It’s a simple two step process to dump and sanitize your blog data, if you’re running on WordPress like we are.
+
+1. I used ‘[Export to Text][9]‘ to dump all of my content into a text file.
+2. I wrote some quick Python to strip all the HTML using [the library bleach][10]:
+
+Now you’ll have a smaller file suitable for copying to HDFS on your Pi cluster!
+
+If you didn’t do this work on the master Pi, find a way to transfer it over (scp, rsync, etc.), and copy it to HDFS with a command like this:
+
+>hdfs dfs -copyFromLocal dqydj_stripped.txt /dqydj_stripped.txt
+
+You’re now ready for the final step – writing some code for Apache Spark!
+
+
+
+### VI: Lighting Apache Spark
+
+Cloudera had an excellent program to use as a base for our super-wordcount program, which you can find here. We’re going to modify it for our introspective Word Counting program.
+
+Install the package ‘stop-words’ for Python on your master Pi.  Although interesting (I’ve used the word ‘the’ on DQYDJ 23,295 times!), you probably don’t want to see your stop words dominating the top of your data.  Additionally, replace any references to my dqydj files in the following code with the path to your own special dataset:
+
+Save wordCount.py and make sure all of your paths are correct.
+
+Now you’re ready for the incantation that got Spark running on YARN for me, and (drumroll, please) revealed which words I use the most on DQYDJ:
+
+>/opt/spark-2.0.0-bin-hadoop2.7/bin/spark-submit –master yarn –executor-memory 512m –name wordcount –executor-cores 8 wordCount.py /dqydj_stripped.txt
+
+
+
+
+
+### VII.  The Conclusion: Which Words Do I Use the Most on DQYDJ?
+
+
+he top ten “non-stop words” on DQYDJ?  “can, will, it’s, one, even, like, people, money, don’t, also“.
+
+Hey, not bad – money snuck into the top ten.  That seems like a good thing to talk about on a website dedicated to finance, investing and economics – right?
+
+Here’s the rest of the top 50 most frequently used words by yours truly.  Please use them to draw wild conclusions about the quality of the rest of my posts!
+
+![](https://dqydj.com/wp-content/uploads/2016/08/dqydj_pk_most_used_words.png)
+
+I hope you enjoyed this introduction to Hadoop, YARN, and Apache Spark – you’re now set to go off and write other applications on top of Spark.
+
+Your next step is to start to read through the documentation of Pyspark (and the libraries for other languages too, if you’re so inclined) to learn some of the functionality available to you.  Depending on your interests and how your data is actually stored, you’ll have a number of paths to explore further – there are packages for streaming data, SQL, and even machine learning!
+
+What do you think?  Are you going to build a Raspberry Pi Hadoop Cluster?  Want to rent some time on mine?  What’s the most surprising word you see up there, and why is it ‘S&P’?
+
+
+--------------------------------------------------------------------------------
+
+via: https://dqydj.com/raspberry-pi-hadoop-cluster-apache-spark-yarn/?utm_source=dbweekly&utm_medium=email
+
+作者:[PK][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://dqydj.com/about/#contact_us
+[1]: https://www.raspberrypi.org/downloads/raspbian/
+[2]: https://sourceforge.net/projects/win32diskimager/
+[3]: http://www.becausewecangeek.com/building-a-raspberry-pi-hadoop-cluster-part-1/
+[4]: https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04
+[5]: http://www.becausewecangeek.com/building-a-raspberry-pi-hadoop-cluster-part-2/
+[6]: https://spark.apache.org/
+[7]: https://hadoop.apache.org/docs/r1.2.1/mapred_tutorial.html
+[8]: https://spark.apache.org/downloads.html
+[9]: https://wordpress.org/support/plugin/export-to-text
+[10]: https://pypi.python.org/pypi/bleach
+
+
+
+
diff --git a/sources/tech/20160908 Using webpack with the Amazon Cognito Identity SDK for JavaScript.md b/sources/tech/20160908 Using webpack with the Amazon Cognito Identity SDK for JavaScript.md
new file mode 100644
index 0000000000..62b30cb95d
--- /dev/null
+++ b/sources/tech/20160908 Using webpack with the Amazon Cognito Identity SDK for JavaScript.md	
@@ -0,0 +1,180 @@
+Using webpack with the Amazon Cognito Identity SDK for JavaScript
+=====
+
+This blog post is aimed at developers of all experience levels who develop and deploy JavaScript based applications (whether server-side with Node.js or client side) that incorporate the AWS SDK, the Amazon Cognito Identity SDK for JavaScript and who also use the popular [webpack][1] module bundler.
+
+In July 2016, Amazon Web Services [launched Amazon Cognito User Pools][2], a feature that makes it easy for developers to add sign-up and sign-in functionality to mobile and web applications. To help developers easily realize the power of user pools within their own applications, we also released the [Amazon Cognito Identity SDK for JavaScript][3].
+
+Amazon Cognito User Pools allows you to easily add user sign-up and sign-in to your mobile and web applications. The fully managed user pool can scale to hundreds of millions of users and you can have multiple directories per AWS account. Creating a user pool takes just a few minutes and allows you to decide exactly which attributes (including address, email, phone number as well as custom attributes) are mandatory and even optional when a new user signs up for your application or service. Your application can also specify the desired password strength, whether the use of Multi-Factor Authentication (MFA) is required, and verify new users via phone number or email address to further enhance the security of your application.
+
+If you are new to the Amazon Cognito Identity SDK for JavaScript [this AWS blog post][4] is a great place to start.
+
+### Why Use Asset & Module Bundling with the Amazon Cognito Identity SDK for JavaScript
+
+Today, modern web applications for both mobile and desktop have to provide the user with a secure, fast, responsive, and native-app-like experience. There is no doubt that modern browsers are extremely powerful and cater to a vast array of possible implementation approaches. Many of the more popular implementations rely heavily on the deployment of a JavaScript application through some form of asset packaging and/or module bundling. This allows a developer to take their JavaScript application and create one or more files that can be loaded by the client browser by using script tags.
+
+There are many schools of thought on how you can achieve this packaging , including task runners such as [Grunt][5] and [Gulp][6], and bundlers such as [Browserify][7]. However, there is a general consensus that asset packaging is not only about improving load times—it enables the modularization of your application while ensuring testability and robustness.
+
+### Using webpack with the Amazon Cognito Identity SDK for JavaScript
+
+In the many requests we get to provide more detail on how to integrate the Amazon Cognito Identity SDK for JavaScript within a webpack environment, we’re specifically asked how to ensure that webpack correctly manages the following third-party dependencies:
+
+- [JavaScript BN library for BigInteger computation][8] (jsbn)
+- [an extension to jsbn][9] the with the rest of the jsbn methods including most public BigInteger methods (jsbn2)
+- the [Stanford JavaScript Crypto Library][10] (sjcl)
+
+Throughout these examples, the following bower libraries are used by bower.json
+
+```
+"aws-cognito-sdk": "https://raw.githubusercontent.com/aws/amazon-cognito-identity-js/master/dist/aws-cognito-sdk.js",
+"amazon-cognito-identity": "https://raw.githubusercontent.com/aws/amazon-cognito-identity-js/master/dist/amazon-cognito-identity.min.js",
+"sjcl": "https://raw.githubusercontent.com/bitwiseshiftleft/sjcl/master/sjcl.js",
+"jsbn": "https://raw.githubusercontent.com/andyperlitch/jsbn/master/index.js",
+```
+
+For all the reasons we gave earlier for the importance of asset packaging to development processes, and unless your application is extremely small, the use of an asset packaging tool such as webpack is almost always recommended. Of course, one could simply pull in all of these dependencies using tags. However, this would pollute global namespace, and not provide the most optimal resource management and loading approach. Many developers start with a standard webpack.config.js file that has a standard babel loader, as shown here.
+
+```
+{
+  /** test for file ending in js or jsx 
+   * exclude node_module and bower_components - we dont want to babel these 
+   * use the babel loader 
+   * apply the react and es2015 (es6) transformations **/
+
+  test: /\.jsx?$/,
+  exclude: /(node_modules|bower_components)/,
+  loader: 'babel',
+  query: {
+    presets: ['react', 'es2015']
+  }
+}
+```
+
+It’s important to remember that this configuration doesn’t take into account that some of third-party dependencies used by the Amazon Cognito Identity SDK for JavaScript currently do not use the [Universal Module Definition (UMD) pattern for JavaScript][11].
+
+The UMD pattern attempts to offer Asynchronous Module Definition (AMD) based compatibility with the most popular script loaders of the day such as [RequireJS][12] and [CommonJS][13].
+
+This is a pattern that webpack relies on, and so we must make some changes to how webpack loads these modules. Without these changes, you may encounter errors such as the following.
+
+```
+amazon-cognito-identity.min.js:19 Uncaught ReferenceError: BigInteger is not defined
+```
+
+Such an error may be encountered when making a call to AWSCognito.CognitoIdentityServiceProvider.CognitoUser property authenticateUser This is an example of where we can make use of the webpack imports and exports loader capability to overcome this error.
+
+### Using webpack Loaders
+
+According to the [webpack documentation][14] "loaders allow you to preprocess files as you require() or “load” them. Loaders are kind of like “tasks” are in other build tools, and provide a powerful way to handle front-end build steps. Loaders can transform files from a different language like, CoffeeScript to JavaScript, or inline images as data URLs"
+
+In order to resolve the lack of UMD compatibility, you will rely to two specific loaders, import and export.
+
+#### Using the Export Loader
+
+In the case of the Amazon Cognito Identity SDK for JavaScript, we need to ensure we export theAWSCognito variables into the scope of the module that requires/imports (for ES6) them.
+
+```
+{
+  test: /aws-cognito-sdk\/index\.js/,
+  loader: 'exports?AWSCognito'
+}
+```
+
+Using the exports loader has the effect of exporting a module method within bundle created by webpack. As a result, both AWSCognito and AWS are now accessible when required or import(ed) (for ES6).
+
+```
+var AWSCognito = require('aws-cognito-sdk')
+
+/*** EXPORTS from export-loader ***/ 
+module.exports = AWSCongito
+```
+
+More information about the exports loader can be found here
+
+#### Using the Import Loader
+
+The import loader is mostly used to inject (import) variables into the scope of another module. This is especially useful if third-party modules are relying on global variables like BitInteger or sjcl as is the case with Amazon Cognito Identity SDK for JavaScript.
+
+If you don’t use the webpack loader, the following is generated within the bundle.
+
+```
+__webpack_require__(431);       // refers to jsbin
+__webpack_require__(432);       // refers to sjcl
+```
+
+Beacuse neither jsbin or sjcl export anything, any calls that rely on these modules will result in an error.
+
+To resolve this, we can use the following webpack loader configuration:
+
+```
+{
+  test: /amazon-cognito-identity\/index\.js/,
+  loader: 'imports?jsbn,BigInteger=>jsbn.BigInteger,sjcl'
+},
+{
+  test: /sjcl\/index\.js/,
+  loader: 'imports?sjcl'
+}
+```
+
+This injects the following into the bundle (in this case bundle.js) created by webpack.
+
+````
+/*** IMPORTS FROM imports-loader ***/
+var jsbn = __webpack_require__(431);
+var BigInteger = jsbn.BigInteger;
+var sjcl = __webpack_require__(432);
+```
+
+As a result, jsbn, BigInteger and sjcl are all imported from their respective modules into Amazon Cognito Identity SDK for JavaScript.
+
+More information about the import loader can be found [here][15]
+
+### Next Steps
+
+We encourage you to download the [Amazon Cognito Identity SDK for JavaScript][16] and start building your application. Coupled with webpack, and by following the guidance in this blog, you we hope you have a smooth development experience.
+
+If you have any comments or questions, please free to comment below, reach out via email (teichtah@amazon.com) or raise an issue [here][17].
+
+### References
+
+This blog post makes reference to the following third party resources
+
+- webpack - https://webpack.github.io/
+- webpack documentation - http://webpack.github.io/docs/what-is-webpack.html
+- webpack exports loader - https://github.com/webpack/exports-loader
+- webpack imports loader - https://github.com/webpack/imports-loader
+- JavaScript BN library for BigInteger computation - http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js
+- jsbns - http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn2.js
+- Stanford JavaScript Crypto Library - https://github.com/bitwiseshiftleft/sjcl
+- RequireJS - http://requirejs.org/
+- CommonJS - http://www.commonjs.org/
+
+--------------------------------------------------------------------------------
+
+via: https://mobile.awsblog.com/post/Tx1A84CLMDJ744T/Using-webpack-with-the-Amazon-Cognito-Identity-SDK-for-JavaScript?utm_source=webopsweekly&utm_medium=email
+
+作者:[Marc Teichtahl ][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://mobile.awsblog.com/blog/author/Marc+Teichtahl
+[1]: https://webpack.github.io/
+[2]: https://blogs.aws.amazon.com/security/post/Tx13NVD4AWG9QK9/Amazon-Cognito-Your-User-Pools-is-Now-Generally-Available
+[3]: https://github.com/aws/amazon-cognito-identity-js
+[4]: http://mobile.awsblog.com/post/Tx2O14ZY8A5LFHT/Accessing-Your-User-Pools-using-the-Amazon-Cognito-Identity-SDK-for-JavaScript
+[5]: http://gruntjs.com/
+[6]: http://gulpjs.com/
+[7]: http://browserify.org/
+[8]: http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js
+[9]: http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn2.js
+[10]: https://github.com/bitwiseshiftleft/sjcl
+[11]: https://github.com/umdjs/umd
+[12]: http://requirejs.org/
+[13]: http://www.commonjs.org/
+[14]: http://webpack.github.io/docs/what-is-webpack.html
+[15]: https://github.com/webpack/imports-loader
+[16]: https://github.com/aws/amazon-cognito-identity-js
+[17]: https://github.com/aws/amazon-cognito-identity-js/issues
+
diff --git a/sources/tech/20160910 HOSTING .NET CORE ON LINUX WITH DOCKER - A NOOB'S GUIDE.md b/sources/tech/20160910 HOSTING .NET CORE ON LINUX WITH DOCKER - A NOOB'S GUIDE.md
new file mode 100644
index 0000000000..54abcb99dd
--- /dev/null
+++ b/sources/tech/20160910 HOSTING .NET CORE ON LINUX WITH DOCKER - A NOOB'S GUIDE.md	
@@ -0,0 +1,436 @@
+HOSTING .NET CORE ON LINUX WITH DOCKER - A NOOB'S GUIDE
+=====
+
+This post builds on my previous [introduction to .NET Core][1]. First I upgrade that RESTful API from .NET Core RC1 to .NET Core 1.0, then I add support for Docker and describe how to host it on Linux in a production environment.
+
+
+![](http://blog.scottlogic.com/nsoper/assets/noob.png)
+
+I’m completely new to Docker and I’m far from a Linux expert, so these are very much the thoughts of a noob.
+
+### INSTALLATION
+
+Follow the instructions on https://www.microsoft.com/net/core to install .NET Core on your development machine. This will include the dotnet command line tool and the latest Visual Studio tooling for Windows.
+
+### SOURCE CODE
+
+You can jump straight to the finished source code on GitHub.
+
+### CONVERTING TO .NET CORE 1.0
+
+Naturally, my first port of call when thinking about how to upgrade the API from RC1 to 1.0 was to Google it with Bing. There are two pretty comprehensive guides that I followed:
+
+- [Migrating from DNX to .NET Core CLI][2]
+- [Migrating from ASP.NET 5 RC1 to ASP.NET Core 1.0][3]
+
+I advise reading through both of these very carefully when migrating your code because I tried to skim read the second one without reading the first one and got very confused and frustrated!
+
+I won’t describe the changes in detail because you can look at the commit on GitHub. Here is a summary of what I changed:
+
+- Updated version numbers on global.json and project.json
+- Removed obsolete sections from project.json
+- Using the more lightweight ControllerBase rather than Controller because I don’t need methods related to MVC views (this was an optional change)
+- Removed the Http prefix from helper methods e.g. HttpNotFound -> NotFound
+- LogVerbose -> LogTrace
+- Namespace changes: Microsoft.AspNetCore.*
+- Using SetBasePath in Startup (appsettings.json won’t be found without this)
+- Running via WebHostBuilder rather than WebApplication.Run
+- Removed Serilog (at the time of writing it does not support .NET Core 1.0)
+
+The only real headache here is the need to remove Serilog. I could have implemented my own file logger, but I just deleted file logging because I didn’t want to focus on it for this exercise.
+
+Unfortunately, there will be plenty of third party developers that will be playing catch up with support for .NET Core 1.0 but I have sympathy for them because they are often working in their spare time without anything close to the resources available to Microsoft. I recommend reading Travis Illig’s [.NET Core 1.0 is Released, but Where is Autofac][4]? for a third party developer’s point of view on this!
+
+Having made these changes I was able to dotnet restore, dotnet build and dotnet run from the project.json directory and see the API working as before.
+
+### RUNNING WITH DOCKER
+
+At the time of writing, Docker only really works on Linux. There is beta support for Docker on Windows and OS X but they both rely on virtualisation so I’ve chosen to run Ubuntu 14.04 as a VirtualBox. Follow these instructions if you haven’t already got Docker installed.
+
+I’ve been doing a bit of reading about Docker recently but I’ve never tried to actually do anything with it until now. I’ll assume the reader has no Docker knowledge so I’ll explain all parts of the commands that I’m using.
+
+#### HELLO DOCKER
+
+Having installed Docker on my Ubuntu machine, my next move was to follow the instructions at https://www.microsoft.com/net/core#docker to see how to get started with .NET Core and Docker.
+
+First start a container with .NET Core installed:
+
+```
+docker run -it microsoft/dotnet:latest
+```
+
+The -it option means interactive so having executed this command you will be inside the container and free to run any bash commands you like.
+
+Then we can run five commands to get Microsoft’s Hello World .NET Core console application running inside Docker!
+
+1. mkdir hwapp
+2. cd hwapp
+3. dotnet new
+4. dotnet restore
+5. dotnet run
+
+You can exit to leave the container, then docker ps -a to show that you have created a container which has exited. You should really now tidy up that container using docker rm <container_name>.
+
+#### MOUNTING THE SOURCE
+
+My next move was to use the same microsoft/dotnet image as above but to mount the source for my application as a data volume.
+
+First check out the repository at the relevant commit:
+
+1. git clone https://github.com/niksoper/aspnet5-books.git
+2. cd aspnet5-books/src/MvcLibrary
+3. git checkout dotnet-core-1.0
+
+Now start a container running .NET Core 1.0 with the source located at /books. Note that you’ll need to change the /path/to/repo part to match your machine:
+
+```
+docker run -it \
+-v /path/to/repo/aspnet5-books/src/MvcLibrary:/books \
+microsoft/dotnet:latest
+```
+
+Now you can run the application inside the container!
+
+```
+cd /books
+dotnet restore
+dotnet run
+```
+
+That’s great as a proof of concept but we don’t really want to have to worry about mounting the source code into a container like this whenever we want to start the application.
+
+#### ADDING A DOCKERFILE
+
+The next step I took was to introduce a Dockerfile, which will allow the application to be started easily inside its own container.
+
+My Dockerfile lives in the src/MvcLibrary directory alongside project.json and looks like this:
+
+```
+FROM microsoft/dotnet:latest
+
+# Create directory for the app source code
+RUN mkdir -p /usr/src/books
+WORKDIR /usr/src/books
+
+# Copy the source and restore dependencies
+COPY . /usr/src/books
+RUN dotnet restore
+
+# Expose the port and start the app
+EXPOSE 5000
+CMD [ "dotnet", "run" ]
+```
+
+Strictly, the `RUN mkdir -p /usr/src/books` command is not needed because COPY will create any missing directories automatically.
+
+Docker images are built in layers. We start from the image containing .NET Core and add another layer which builds the application from source then runs the application.
+
+Having added the Dockerfile, I then ran the following commands to build the image and start a container using that image (make sure you are in the same directory as the Dockerfile and you should really use your own username):
+
+1. docker build -t niksoper/netcore-books .
+2. docker run -it niksoper/netcore-books
+
+You should see that the application started listening just as before, except this time we don’t need to bother mounting the source code because it’s already contained in the docker image.
+
+#### EXPOSING AND PUBLISHING A PORT
+
+This API isn’t going to be very useful unless we can communicate with it from outside the container. Docker has the concept of exposing and publishing ports, which are two very different things.
+
+From the official Docker documentation:
+
+>The EXPOSE instruction informs Docker that the container listens on the specified network ports at runtime. EXPOSE does not make the ports of the container accessible to the host. To do that, you must use either the -p flag to publish a range of ports or the -P flag to publish all of the exposed ports.
+
+EXPOSE only adds metadata to the image so you can think of it as documentation for the consumers of the image. Technically, I could have left out the EXPOSE 5000 line completely because I know the port that the API is listening on but leaving it in is helpful and certainly recommended.
+
+At this stage I want to access the API directly from the host so I need to use -p to publish the port - this allows a request to port 5000 on the host be forwarded to port 5000 in the container regardless of whether the port has previously been exposed via the Dockerfile:
+
+```
+docker run -d -p 5000:5000 niksoper/netcore-books
+```
+
+Using -d tells docker to run the container in detached mode so we won’t see its output but it will still be running and listening on port 5000 - prove this to yourself with docker ps.
+
+So then I prepared to celebrate by making a request from the host to the container:
+
+```
+curl http://localhost:5000/api/books
+```
+
+It didn’t work.
+
+Making the same curl request repeatedly, I see one of two errors - either curl: (56) Recv failure: Connection reset by peer or curl: (52) Empty reply from server.
+
+I went back to the docker run documentation and double checked I was using the -p option correctly as well as EXPOSE in the Dockerfile. I couldn’t see the problem and became a bit sad…
+
+After pulling myself together, I decided to consult one of my local DevOps heroes - Dave Wybourn (also mentioned in this post on Docker Swarm). His team had run into this exact problem and the issue was the way that I had (not) configured Kestrel - the new lightweight, cross platform web server used for .NET Core.
+
+By default, Kestrel will listen on http://localhost:5000. The problem here is that localhost is a loopback interface.
+
+From Wikipedia:
+
+>In computer networking, localhost is a hostname that means this computer. It is used to access the network services that are running on the host via its loopback network interface. Using the loopback interface bypasses any local network interface hardware.
+
+This is a problem when running inside a container because localhost can only be reached from within that container. The solution was to update the Main method in Startup.cs to configure the URLs that Kestrel will listen on:
+
+```
+public static void Main(string[] args)
+{
+  var host = new WebHostBuilder()
+    .UseKestrel()
+    .UseContentRoot(Directory.GetCurrentDirectory())
+    .UseUrls("http://*:5000") // listen on port 5000 on all network interfaces
+    .UseIISIntegration()
+    .UseStartup<Startup>()
+    .Build();
+
+  host.Run();
+}
+```
+
+With this extra configuration in place, I could then rebuild image and run the application in a container which will accept requests from the host:
+
+1. docker build -t niksoper/netcore-books .
+2. docker run -d -p 5000:5000 niksoper/netcore-books
+3. curl -i http://localhost:5000/api/books
+
+I now get the following response:
+
+```
+HTTP/1.1 200 OK
+Date: Tue, 30 Aug 2016 15:25:43 GMT
+Transfer-Encoding: chunked
+Content-Type: application/json; charset=utf-8
+Server: Kestrel
+
+[{"id":"1","title":"RESTful API with ASP.NET Core MVC 1.0","author":"Nick Soper"}]
+```
+
+### KESTREL IN PRODUCTION
+
+Microsoft’s words:
+
+>Kestrel is great for serving dynamic content from ASP.NET, however the web serving parts aren’t as feature rich as full-featured servers like IIS, Apache or Nginx. A reverse proxy-server can allow you to offload work like serving static content, caching requests, compressing requests, and SSL termination from the HTTP server.
+
+So I need to set up Nginx on my Linux machine to act as my reverse proxy. Microsoft spell out how to do this in Publish to a Linux Production Environment. I’ll summarise the instructions here:
+
+1. Use dotnet publish to produce a self contained package for the application
+2. Copy the published application to the server
+3. Install and configure Nginx (as a reverse proxy server)
+4. Install and configure supervisor (for keeping the Kestrel server running)
+5. Enable and configure AppArmor (for limiting the resources available to an application)
+6. Configure the server firewall
+7. Secure Nginx (involves building from source and configuring SSL)
+
+It’s beyond the scope of this post to cover all of that, so I’m only going to concentrate on configuring Nginx as a reverse proxy - and naturally, I’m going to use Docker to do it.
+
+### RUN NGINX IN ANOTHER CONTAINER
+
+My aim is to run Nginx in a second Docker container and configure it as a reverse proxy to my application container.
+
+I’ve used the official Nginx image from Docker Hub. First I tried it out like this:
+
+```
+docker run -d -p 8080:80 --name web nginx
+```
+
+This starts a container running Nginx and maps port 8080 on the host to port 80 in the container. Hitting http://localhost:8080 in the browser now shows the default Nginx landing page.
+
+Now we’ve proved how easy it is to get Nginx running, we can kill the container.
+
+```
+docker rm -f web
+```
+
+### CONFIGURING NGINX AS A REVERSE PROXY
+
+Nginx can be configured as a reverse proxy by editing the config file at /etc/nginx/conf.d/default.conf like this:
+
+```
+server {
+  listen 80;
+
+  location / {
+    proxy_pass http://localhost:6666;
+  }
+}
+```
+
+The config above will cause Nginx to proxy all requests from the root to http://localhost:6666. Remember localhost here refers to the container running Nginx. We can use our own config file inside the Nginx container using a volume:
+
+```
+docker run -d -p 8080:80 \
+-v /path/to/my.conf:/etc/nginx/conf.d/default.conf \
+nginx
+```
+
+Note: this maps a single file from the host to the container, rather than an entire directory.
+
+### COMMUNICATING BETWEEN CONTAINERS
+
+Docker allows inter-container communication using shared virtual networks. By default, all containers started by the Docker daemon will have access to a virtual network called bridge. This allows containers to be referenced from other containers on the same network via IP address and port.
+
+You can discover the IP address of a running container by inspecting it. I’ll start a container from the niksoper/netcore-books image that I created earlier, and inspect it:
+
+1. docker run -d -p 5000:5000 --name books niksoper/netcore-books
+2. docker inspect books
+
+![](http://blog.scottlogic.com/nsoper/assets/docker-inspect-ip.PNG)
+
+We can see this container has "IPAddress": "172.17.0.3".
+
+So now if I create the following Nginx config file, then start an Nginx container using that file, then it will proxy requests to my API:
+
+```
+server {
+  listen 80;
+
+  location / {
+    proxy_pass http://172.17.0.3:5000;
+  }
+}
+```
+
+Now I can start an Nginx container using that config (note I’m mapping port 8080 on the host to port 80 on the Nginx container):
+
+```
+docker run -d -p 8080:80 \
+-v ~/dev/nginx/my.nginx.conf:/etc/nginx/conf.d/default.conf \
+nginx
+```
+
+A request to http://localhost:8080 will now be proxied to my application. Note the Server header in the following curl response:
+
+![](http://blog.scottlogic.com/nsoper/assets/nginx-proxy-response.PNG)
+
+### DOCKER COMPOSE
+
+At this point I was fairly pleased with my progress but I thought there must be a better way of configuring Nginx without needing to know the exact IP address of the application container. Another of the local Scott Logic DevOps heroes - Jason Ebbin - stepped up at this point and suggested Docker Compose.
+
+As a high level description - Docker Compose makes it very easy to start up a collection of interconnected containers using a declarative syntax. I won’t go into the details of how Docker Compose works because you can read about it in this previous post.
+
+I’ll start with the docker-compose.yml file that I’m using:
+
+```
+version: '2'
+services:
+    books-service:
+        container_name: books-api
+        build: .
+
+    reverse-proxy:
+        container_name: reverse-proxy
+        image: nginx
+        ports:
+         - "9090:8080"
+        volumes:
+         - ./proxy.conf:/etc/nginx/conf.d/default.conf
+```
+
+This is version 2 syntax, so you’ll need to have at least version 1.6 of Docker Compose in order for this to work.
+
+This file tells Docker to create two services - one for the application and another for the Nginx reverse proxy.
+
+### BOOKS-SERVICE
+
+This builds a container called books-api from the Dockerfile in the same directory as this docker-compose.yml. Note that this container does not need to publish any ports because it only needs to be accessed from the reverse-proxy container rather than the host operating system.
+
+### REVERSE-PROXY
+
+This starts a container called reverse-proxy based on the nginx image with a proxy.conf file mounted as the config from the current directory. It maps port 9090 on the host to port 8080 in the container which allows us to access the container from the host at http://localhost:9090.
+
+The proxy.conf file looks like this:
+
+```
+server {
+    listen 8080;
+
+    location / {
+      proxy_pass http://books-service:5000;
+    }
+}
+```
+
+The key point here is that we can now refer to books-service by name so we don’t need to know the IP address of the books-api container!
+
+Now we can start the two containers with a working reverse proxy (-d means detached so we don’t see the output from the containers):
+
+```
+docker compose up -d
+```
+
+Prove the containers were created:
+
+```
+docker ps
+```
+
+And finally confirm that we can hit the API via the reverse proxy:
+
+```
+curl -i http://localhost:9090/api/books
+```
+
+### WHAT’S GOING ON?
+
+Docker Compose makes this happen by creating a new virtual network called mvclibrary_default which is used by both books-api and reverse-proxy containers (the name is based on the parent directory of the docker-compose.yml file).
+
+Prove the network exists with docker network ls:
+
+![](http://blog.scottlogic.com/nsoper/assets/docker-network-ls.PNG)
+
+You can see the details of the new network using docker network inspect mvclibrary_default:
+
+![](http://blog.scottlogic.com/nsoper/assets/network-inspect.PNG)
+
+Note that Docker has assigned "Subnet": "172.18.0.0/16" to the network. The /16 part is CIDR notation and a full explanation is way beyond the scope of this post but CIDR just refers to a range of IP addresses. Running docker network inspect bridge shows "Subnet": "172.17.0.0/16" so the two networks do not overlap.
+
+Now docker inspect books-api to confirm the application container is using this network:
+
+![](http://blog.scottlogic.com/nsoper/assets/docker-inspect-books-api.PNG)
+
+Notice the two "Aliases" for the container are the container identifier (3c42db680459) and the service name given in docker-compose.yml (books-service). We’re using the books-service alias to reference the application container in the custom Nginx configuration file. This could have been done manually with docker network create but I like Docker Compose because it wraps up container creation and interdependencies cleanly and succinctly.
+
+### CONCLUSION
+
+So now I can get the application running on Linux with Nginx in a few easy steps, without making any lasting changes to the host operating system:
+
+```
+git clone https://github.com/niksoper/aspnet5-books.git
+cd aspnet5-books/src/MvcLibrary
+git checkout blog-docker
+docker-compose up -d
+curl -i http://localhost:9090/api/books
+```
+
+I know what I have described in this post is not a truly production ready setup because I’ve not spoken about any of the following, but most of these topics could take an entire post on their own:
+
+- Security concerns like firewalls or SSL configuration
+- How to ensure the application keeps running
+- How to be selective about what to include in a Docker image (I dumped everything in via the Dockerfile)
+- Databases - how to manage them in containers
+
+This has been a very interesting learning experience for me because for a while now I have been curious to explore the new cross platform support that comes with ASP.NET Core, and the opportunity to explore a little bit of the DevOps world using Docker Compose for a “Configuration as Code” approach has been both enjoyable and educational.
+
+If you’re at all curious about Docker then I encourage you to get stuck in by trying it out - especially if this puts you out of your comfort zone. Who knows, you might enjoy it?
+
+--------------------------------------------------------------------------------
+
+via: http://blog.scottlogic.com/2016/09/05/hosting-netcore-on-linux-with-docker.html?utm_source=webopsweekly&utm_medium=email
+
+作者:[Nick Soper][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://blog.scottlogic.com/nsoper
+[1]: http://blog.scottlogic.com/2016/01/20/restful-api-with-aspnet50.html
+[2]: https://docs.microsoft.com/en-us/dotnet/articles/core/migrating-from-dnx   
+[3]: https://docs.asp.net/en/latest/migration/rc1-to-rtm.html
+[4]: http://www.paraesthesia.com/archive/2016/06/29/netcore-rtm-where-is-autofac/
+
+
+
+
+
+
diff --git a/sources/tech/20160911 17 tar command practical examples in Linux.md b/sources/tech/20160911 17 tar command practical examples in Linux.md
new file mode 100644
index 0000000000..88ddbaed46
--- /dev/null
+++ b/sources/tech/20160911 17 tar command practical examples in Linux.md	
@@ -0,0 +1,310 @@
+GHLandy translating
+
+17 tar command practical examples in Linux
+=====
+
+Tar (tape archive ) is the most widely used command in Unix like operating system for creating archive of multiple files and folders into a single archive file and that archive file can be further compressed using  gzip and bzip2 techniques. In other words we can say that tar command is used to take backup by archiving multiple files and directory into a single tar or archive file and later on files & directories can be extracted from the tar compressed file.
+
+In this article we will discuss 17 practical examples of tar command in Linux.
+
+Syntax of tar command:
+
+```
+# tar <options> <files>
+```
+
+Some of the commonly used options in tar command are listed below :
+
+![](http://www.linuxtechi.com/wp-content/uploads/2016/09/tar-command-options.jpg)
+
+Note : hyphen ( – ) in the tar command options are optional.
+
+### Example: 1 Create a tar archive file
+
+Let’s create a tar file of /etc directory and ‘/root/ anaconda-ks.cfg’ file.
+
+```
+[root@linuxtechi ~]# tar -cvf myarchive.tar /etc /root/anaconda-ks.cfg
+```
+
+Above command will create a tar file with the name “myarchive” in the current folder. Tar file contains all the files and directories of /etc folder and anaconda-ks.cfg file.
+
+In the tar command ‘-c‘ option specify to create a tar file, ‘-v’ is used for verbose output and ‘-f’ option is used to specify the archive file name.
+
+```
+[root@linuxtechi ~]# ls -l myarchive.tar
+-rw-r--r--. 1 root root 22947840 Sep 7 00:24 myarchive.tar
+[root@linuxtechi ~]#
+```
+
+### Example:2 List the contents of tar archive file.
+
+Using ‘–t‘ option in tar command we can view the contents of tar files without extracting it.
+
+```
+[root@linuxtechi ~]# tar -tvf myarchive.tar
+```
+
+Listing a specific file or directory from tar file. In the below example i am trying to view whether ‘anaconda-ks.cfg’ file is there in the tar file or not.
+
+```
+[root@linuxtechi ~]# tar -tvf myarchive.tar root/anaconda-ks.cfg
+-rw------- root/root 953 2016-08-24 01:33 root/anaconda-ks.cfg
+[root@linuxtechi ~]#
+```
+
+### Example:3 Append or add files to end of archive or tar file.
+
+‘-r‘ option in the tar command is used to append or add file to existing tar file. Let’s add /etc/fstab file in ‘data.tar‘
+
+```
+[root@linuxtechi ~]# tar -rvf data.tar /etc/fstab
+```
+
+Note: In the Compressed tar file we can’t append file or directory.
+
+### Example:4 Extracting files and directories from tar file.
+
+‘-x‘ option is used to extract the files and directories from the tar file. Let’s extract the content of above created tar file.
+
+```
+[root@linuxtechi ~]# tar -xvf myarchive.tar
+```
+
+This command will extract all the files and directories of myarchive tar file in the current working directory.
+
+### Example:5 Extracting tar file to a particular folder.
+
+In case you want to extract tar file to a particular folder or directory then use ‘-C‘ option and after that specify the path of a folder.
+
+```
+[root@linuxtechi ~]# tar -xvf myarchive.tar -C /tmp/
+```
+
+### Example:6 Extracting particular file or directory from tar file.
+
+Let’s assume you want to extract only anaconda-ks.cfg file from the tar file under /tmp folder.
+
+Syntax :
+
+```
+# tar –xvf {tar-file } {file-to-be-extracted } -C {path-where-to-extract}
+
+[root@linuxtechi tmp]# tar -xvf /root/myarchive.tar root/anaconda-ks.cfg -C /tmp/
+root/anaconda-ks.cfg
+[root@linuxtechi tmp]# ls -l /tmp/root/anaconda-ks.cfg
+-rw-------. 1 root root 953 Aug 24 01:33 /tmp/root/anaconda-ks.cfg
+[root@linuxtechi tmp]#
+```
+
+### Example:7 Creating and compressing tar file (tar.gz or .tgz )
+
+Let’s assume that we want to create a tar file of /etc and /opt folder and also want to compress it using gzip tool. This can be achieved using ‘-z‘ option in tar command. Extensions of such tar files will be either tar.gz or .tgz
+
+```
+[root@linuxtechi ~]# tar -zcpvf myarchive.tar.gz /etc/ /opt/
+```
+
+Or
+
+```
+[root@linuxtechi ~]# tar -zcpvf myarchive.tgz /etc/ /opt/
+```
+
+### Example:8 Creating and compressing tar file ( tar.bz2 or .tbz2 )
+
+Let’s assume that we want to create compressed (bzip2) tar file of /etc and /opt folder. This can be achieved by using the option ( -j) in the tar command.Extensions of such tar files will be either tar.bz2 or .tbz
+
+```
+[root@linuxtechi ~]# tar -jcpvf myarchive.tar.bz2 /etc/ /opt/
+```
+
+Or
+
+```
+[root@linuxtechi ~]# tar -jcpvf myarchive.tbz2 /etc/ /opt/
+```
+
+### Example:9 Excluding particular files or type while creating tar file.
+
+Using “–exclude” option in tar command we can exclude the particular file or file type while creating tar file. Let’s assume we want to exclude the file type of html while creating the compressed tar file.
+
+```
+[root@linuxtechi ~]# tar -zcpvf myarchive.tgz /etc/ /opt/ --exclude=*.html
+```
+
+### Example:10 Listing the contents of tar.gz or .tgz file
+
+Contents of tar file with the extensions tar.gz or .tgz is viewed by using the option ‘-t’. Example is shown below :
+
+```
+[root@linuxtechi ~]# tar -tvf myarchive.tgz  | more 
+............................................. 
+drwxr-xr-x root/root         0 2016-09-07 08:41 etc/ 
+-rw-r--r-- root/root       541 2016-08-24 01:23 etc/fstab 
+-rw------- root/root         0 2016-08-24 01:23 etc/crypttab 
+lrwxrwxrwx root/root         0 2016-08-24 01:23 etc/mtab -> /proc/self/mounts 
+-rw-r--r-- root/root       149 2016-09-07 08:41 etc/resolv.conf 
+drwxr-xr-x root/root         0 2016-09-06 03:55 etc/pki/ 
+drwxr-xr-x root/root         0 2016-09-06 03:15 etc/pki/rpm-gpg/ 
+-rw-r--r-- root/root      1690 2015-12-09 04:59 etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 
+-rw-r--r-- root/root      1004 2015-12-09 04:59 etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-Debug-7 
+-rw-r--r-- root/root      1690 2015-12-09 04:59 etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-Testing-7 
+-rw-r--r-- root/root      3140 2015-09-15 06:53 etc/pki/rpm-gpg/RPM-GPG-KEY-foreman 
+..........................................................
+```
+
+### Example:11 Listing the contents of tar.bz2 or .tbz2 file.
+
+Contents of tar file with the extensions tar.bz2 or .tbz2 is viewed by using the option ‘-t’. Example is shown below :
+
+```
+[root@linuxtechi ~]# tar -tvf myarchive.tbz2  | more 
+........................................................ 
+rwxr-xr-x root/root         0 2016-08-24 01:25 etc/pki/java/ 
+lrwxrwxrwx root/root         0 2016-08-24 01:25 etc/pki/java/cacerts -> /etc/pki/ca-trust/extracted/java/cacerts 
+drwxr-xr-x root/root         0 2016-09-06 02:54 etc/pki/nssdb/ 
+-rw-r--r-- root/root     65536 2010-01-12 15:09 etc/pki/nssdb/cert8.db 
+-rw-r--r-- root/root      9216 2016-09-06 02:54 etc/pki/nssdb/cert9.db 
+-rw-r--r-- root/root     16384 2010-01-12 16:21 etc/pki/nssdb/key3.db 
+-rw-r--r-- root/root     11264 2016-09-06 02:54 etc/pki/nssdb/key4.db 
+-rw-r--r-- root/root       451 2015-10-21 09:42 etc/pki/nssdb/pkcs11.txt 
+-rw-r--r-- root/root     16384 2010-01-12 15:45 etc/pki/nssdb/secmod.db 
+drwxr-xr-x root/root         0 2016-08-24 01:26 etc/pki/CA/ 
+drwxr-xr-x root/root         0 2015-06-29 08:48 etc/pki/CA/certs/ 
+drwxr-xr-x root/root         0 2015-06-29 08:48 etc/pki/CA/crl/ 
+drwxr-xr-x root/root         0 2015-06-29 08:48 etc/pki/CA/newcerts/ 
+drwx------ root/root         0 2015-06-29 08:48 etc/pki/CA/private/ 
+drwx------ root/root         0 2015-11-20 06:34 etc/pki/rsyslog/ 
+drwxr-xr-x root/root         0 2016-09-06 03:44 etc/pki/pulp/ 
+..............................................................
+```
+
+### Example:12 Extracting or unzip tar.gz or .tgz files.
+
+tar files with extension tar.gz or .tgz is extracted or unzipped with option ‘-x’ and ‘-z’. Example is shown below :
+
+```
+[root@linuxtechi ~]# tar -zxpvf myarchive.tgz -C /tmp/
+```
+
+Above command will extract tar file under /tmp folder.
+
+Note : Now a days tar command will take care compression file types automatically while extracting, it means it is optional for us to specify compression type in tar command. Example is shown below :
+
+```
+[root@linuxtechi ~]# tar -xpvf myarchive.tgz -C /tmp/
+```
+
+### Example:13 Extracting or unzip tar.bz2 or .tbz2 files
+
+tar files with the extension tar.bz2 or .tbz2 is extract with option ‘-j’ and ‘-x’. Example is shown below:
+
+```
+[root@linuxtechi ~]# tar -jxpvf myarchive.tbz2 -C /tmp/
+```
+
+Or
+
+```
+[root@linuxtechi ~]# tar xpvf myarchive.tbz2 -C /tmp/
+```
+
+### Example:14 Scheduling backup with tar command
+
+There are some real time scenarios where we have to create the tar files of particular files and directories for backup purpose on daily basis. Let’s suppose we have to take backup of whole /opt folder on daily basis, this can be achieved by creating a cron job of tar command. Example is shown below :
+
+```
+[root@linuxtechi ~]# tar -zcvf optbackup-$(date +%Y-%m-%d).tgz /opt/
+```
+
+Create a cron job for above command.
+
+### Example:15 Creating compressed archive or tar file with -T and -X option.
+
+There are some real time scenarios where we want tar command to take input from a file and that file will consists of path of files & directory that are to be archived and compressed, there might some files that we would like to exclude in the archive which are mentioned in input file.
+
+In the tar command input file is specified after ‘-T’ option and file which consists of exclude list is specified after ‘-X’ option.
+
+Let’s suppose we want to archive and compress the directories like /etc , /opt and /home and want to exclude the file ‘/etc/sysconfig/kdump’ and ‘/etc/sysconfig/foreman‘, Create a text file ‘/root/tar-include’ and ‘/root/tar-exclude’ and put the following contents in respective file.
+
+```
+[root@linuxtechi ~]# cat /root/tar-include
+/etc
+/opt
+/home
+[root@linuxtechi ~]#
+[root@linuxtechi ~]# cat /root/tar-exclude
+/etc/sysconfig/kdump
+/etc/sysconfig/foreman
+[root@linuxtechi ~]#
+```
+
+Now run the below command to create and compress archive file.
+
+```
+[root@linuxtechi ~]# tar zcpvf mybackup-$(date +%Y-%m-%d).tgz -T /root/tar-include -X /root/tar-exclude
+```
+
+### Example:16 View the size of .tar , .tgz and .tbz2 file.
+
+Use the below commands to view the size of tar and compressed tar files.
+
+```
+[root@linuxtechi ~]# tar -czf - data.tar | wc -c
+427
+[root@linuxtechi ~]# tar -czf - mybackup-2016-09-09.tgz | wc -c
+37956009
+[root@linuxtechi ~]# tar -czf - myarchive.tbz2 | wc -c
+30835317
+[root@linuxtechi ~]#
+```
+
+### Example:17 Splitting big tar file into smaller files.
+
+In Unix like operating big file is divided or split into smaller files using split command. Big tar file can also be divided into the smaller parts using split command.
+
+Let’s assume we want to split ‘mybackup-2016-09-09.tgz‘ file into smaller parts of each 6 MB.
+
+```
+Syntax :  split -b <Size-in-MB> <tar-file-name>.<extension> “prefix-name”
+```
+
+```
+[root@linuxtechi ~]# split -b 6M mybackup-2016-09-09.tgz mybackup-parts
+```
+
+Above command will split the mybackup compressed tar file into the smaller files each of size 6 MB in current working directory and split file names will starts from mybackup-partsaa … mybackup-partsag. In case if you want to append numbers in place of alphabets then use ‘-d’ option in above split command.
+
+```
+[root@linuxtechi ~]# ls -l mybackup-parts*
+-rw-r--r--. 1 root root 6291456 Sep 10 03:05 mybackup-partsaa
+-rw-r--r--. 1 root root 6291456 Sep 10 03:05 mybackup-partsab
+-rw-r--r--. 1 root root 6291456 Sep 10 03:05 mybackup-partsac
+-rw-r--r--. 1 root root 6291456 Sep 10 03:05 mybackup-partsad
+-rw-r--r--. 1 root root 6291456 Sep 10 03:05 mybackup-partsae
+-rw-r--r--. 1 root root 6291456 Sep 10 03:05 mybackup-partsaf
+-rw-r--r--. 1 root root 637219  Sep 10 03:05 mybackup-partsag
+[root@linuxtechi ~]#
+```
+
+Now we can move these files into another server over the network and then we can merge all the files into a single tar compressed file using below mentioned command.
+
+```
+[root@linuxtechi ~]# cat mybackup-partsa* > mybackup-2016-09-09.tgz
+[root@linuxtechi ~]#
+```
+
+That’s all, hope you like different examples of tar command. Please share your feedback and comments.
+
+--------------------------------------------------------------------------------
+
+via: http://www.linuxtechi.com/17-tar-command-examples-in-linux/
+
+作者:[Pradeep Kumar ][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.linuxtechi.com/author/pradeep/
diff --git a/sources/tech/20160912 15 Top Open Source Artificial Intelligence Tools.md b/sources/tech/20160912 15 Top Open Source Artificial Intelligence Tools.md
new file mode 100644
index 0000000000..1700c3f693
--- /dev/null
+++ b/sources/tech/20160912 15 Top Open Source Artificial Intelligence Tools.md	
@@ -0,0 +1,127 @@
+translating by Chao-zhi
+
+15 Top Open Source Artificial Intelligence Tools
+====
+
+Artificial Intelligence (AI) is one of the hottest areas of technology research. Companies like IBM, Google, Microsoft, Facebook and Amazon are investing heavily in their own R&D, as well as buying up startups that have made progress in areas like machine learning, neural networks, natural language and image processing. Given the level of interest, it should come as no surprise that a recent [artificial intelligence report][1] from experts at Stanford University concluded that "increasingly useful applications of AI, with potentially profound positive impacts on our society and economy are likely to emerge between now and 2030."
+
+In a recent [article][2], we provided an overview of 45 AI projects that seem particularly promising or interesting. In this slideshow, we're focusing in on open source artificial intelligence tools, with a closer look at fifteen of the best-known open source AI projects.
+
+![](http://www.datamation.com/imagesvr_ce/5668/00AI.jpg)
+
+Open Source Artificial Intelligence
+
+These open source AI applications are on the cutting edge of artificial intelligence research.
+
+![](http://www.datamation.com/imagesvr_ce/8922/01Caffe.JPG)
+
+### 1. Caffe
+
+The brainchild of a UC Berkeley PhD candidate, Caffe is a deep learning framework based on expressive architecture and extensible code. It's claim to fame is its speed, which makes it popular with both researchers and enterprise users. According to its website, it can process more than 60 million images in a single day using just one NVIDIA K40 GPU. It is managed by the Berkeley Vision and Learning Center (BVLC), and companies like NVIDIA and Amazon have made grants to support its development.
+
+![](http://www.datamation.com/imagesvr_ce/1232/02CNTK.JPG)
+
+### 2. CNTK
+
+Short for Computational Network Toolkit, CNTK is one of Microsoft's open source artificial intelligence tools. It boasts outstanding performance whether it is running on a system with only CPUs, a single GPU, multiple GPUs or multiple machines with multiple GPUs. Microsoft has primarily utilized it for research into speech recognition, but it is also useful for applications like machine translation, image recognition, image captioning, text processing, language understanding and language modeling.
+
+![](http://www.datamation.com/imagesvr_ce/2901/03Deeplearning4j.JPG)
+
+### 3. Deeplearning4j
+
+Deeplearning4j is an open source deep learning library for the Java Virtual Machine (JVM). It runs in distributed environments and integrates with both Hadoop and Apache Spark. It makes it possible to configure deep neural networks, and it's compatible with Java, Scala and other JVM languages.
+
+The project is managed by a commercial company called Skymind, which offers paid support, training and an enterprise distribution of Deeplearning4j.
+
+![](http://www.datamation.com/imagesvr_ce/7269/04DMLT.JPG)
+
+### 4. Distributed Machine Learning Toolkit
+
+Like CNTK, the Distributed Machine Learning Toolkit (DMTK) is one of Microsoft's open source artificial intelligence tools. Designed for use in big data applications, it aims to make it faster to train AI systems. It consists of three key components: the DMTK framework, the LightLDA topic model algorithm, and the Distributed (Multisense) Word Embedding algorithm. As proof of DMTK's speed, Microsoft says that on an eight-cluster machine, it can "train a topic model with 1 million topics and a 10-million-word vocabulary (for a total of 10 trillion parameters), on a document collection with over 100-billion tokens," a feat that is unparalleled by other tools.
+
+![](http://www.datamation.com/imagesvr_ce/2890/05H2O.JPG)
+
+### 5. H20
+
+Focused more on enterprise uses for AI than on research, H2O has large companies like Capital One, Cisco, Nielsen Catalina, PayPal and Transamerica among its users. It claims to make is possible for anyone to use the power of machine learning and predictive analytics to solve business problems. It can be used for predictive modeling, risk and fraud analysis, insurance analytics, advertising technology, healthcare and customer intelligence.
+
+It comes in two open source versions: standard H2O and Sparkling Water, which is integrated with Apache Spark. Paid enterprise support is also available.
+
+![](http://www.datamation.com/imagesvr_ce/1127/06Mahout.JPG)
+
+### 6. Mahout
+
+An Apache Foundation project, Mahout is an open source machine learning framework. According to its website, it offers three major features: a programming environment for building scalable algorithms, premade algorithms for tools like Spark and H2O, and a vector-math experimentation environment called Samsara. Companies using Mahout include Adobe, Accenture, Foursquare, Intel, LinkedIn, Twitter, Yahoo and many others. Professional support is available through third parties listed on the website.
+
+![](http://www.datamation.com/imagesvr_ce/4038/07MLlib.JPG)
+
+### 7. MLlib
+
+Known for its speed, Apache Spark has become one of the most popular tools for big data processing. MLlib is Spark's scalable machine learning library. It integrates with Hadoop and interoperates with both NumPy and R. It includes a host of machine learning algorithms for classification, regression, decision trees, recommendation, clustering, topic modeling, feature transformations, model evaluation, ML pipeline construction, ML persistence, survival analysis, frequent itemset and sequential pattern mining, distributed linear algebra and statistics.
+
+![](http://www.datamation.com/imagesvr_ce/839/08NuPIC.JPG)
+
+### 8. NuPIC
+
+Managed by a company called Numenta, NuPIC is an open source artificial intelligence project based on a theory called Hierarchical Temporal Memory, or HTM. Essentially, HTM is an attempt to create a computer system modeled after the human neocortex. The goal is to create machines that "approach or exceed human level performance for many cognitive tasks."
+
+In addition to the open source license, Numenta also offers NuPic under a commercial license, and it also offers licenses on the patents that underlie the technology.
+
+![](http://www.datamation.com/imagesvr_ce/99/09OpenNN.JPG)
+
+### 9. OpenNN
+
+Designed for researchers and developers with advanced understanding of artificial intelligence, OpenNN is a C++ programming library for implementing neural networks. Its key features include deep architectures and fast performance. Extensive documentation is available on the website, including an introductory tutorial that explains the basics of neural networks. Paid support for OpenNNis available through Artelnics, a Spain-based firm that specializes in predictive analytics.
+
+![](http://www.datamation.com/imagesvr_ce/4168/10OpenCyc.JPG)
+
+### 10. OpenCyc
+
+Developed by a company called Cycorp, OpenCyc provides access to the Cyc knowledge base and commonsense reasoning engine. It includes more than 239,000 terms, about 2,093,000 triples, and about 69,000 owl:sameAs links to external semantic data namespaces. It is useful for rich domain modeling, semantic data integration, text understanding, domain-specific expert systems and game AIs. The company also offers two other versions of Cyc: one for researchers that is free but not open source and one for enterprise use that requires a fee.
+
+![](http://www.datamation.com/imagesvr_ce/9761/11Oryx2.JPG)
+
+### 11. Oryx 2
+
+Built on top of Apache Spark and Kafka, Oryx 2 is a specialized application development framework for large-scale machine learning. It utilizes a unique lambda architecture with three tiers. Developers can use Oryx 2 to create new applications, and it also includes some pre-built applications for common big data tasks like collaborative filtering, classification, regression and clustering. The big data tool vendor Cloudera created the original Oryx 1 project and has been heavily involved in continuing development.
+
+![](http://www.datamation.com/imagesvr_ce/7423/12.%20PredictionIO.JPG)
+
+### 12. PredictionIO
+
+In February this year, Salesforce bought PredictionIO, and then in July, it contributed the platform and its trademark to the Apache Foundation, which accepted it as an incubator project. So while Salesforce is using PredictionIO technology to advance its own machine learning capabilities, work will also continue on the open source version. It helps users create predictive engines with machine learning capabilities that can be used to deploy Web services that respond to dynamic queries in real time.
+
+![](http://www.datamation.com/imagesvr_ce/6886/13SystemML.JPG)
+
+### 13. SystemML
+
+First developed by IBM, SystemML is now an Apache big data project. It offers a highly-scalable platform that can implement high-level math and algorithms written in R or a Python-like syntax. Enterprises are already using it to track customer service on auto repairs, to direct airport traffic and to link social media data with banking customers. It can run on top of Spark or Hadoop.
+
+![](http://www.datamation.com/imagesvr_ce/5742/14TensorFlow.JPG)
+
+### 14. TensorFlow
+
+TensorFlow is one of Google's open source artificial intelligence tools. It offers a library for numerical computation using data flow graphs. It can run on a wide variety of different systems with single- or multi-CPUs and GPUs and even runs on mobile devices. It boasts deep flexibility, true portability, automatic differential capabilities and support for Python and C++. The website includes a very extensive list of tutorials and how-tos for developers or researchers interested in using or extending its capabilities.
+
+![](http://www.datamation.com/imagesvr_ce/9018/15Torch.JPG)
+
+### 15. Torch
+
+Torch describes itself as "a scientific computing framework with wide support for machine learning algorithms that puts GPUs first." The emphasis here is on flexibility and speed. In addition, it's fairly easy to use with packages for machine learning, computer vision, signal processing, parallel processing, image, video, audio and networking. It relies on a scripting language called LuaJIT that is based on Lua.
+
+
+
+
+--------------------------------------------------------------------------------
+
+via: http://www.datamation.com/open-source/slideshows/15-top-open-source-artificial-intelligence-tools.html
+
+作者:[Cynthia Harvey][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.datamation.com/author/Cynthia-Harvey-6460.html
+[1]: https://ai100.stanford.edu/sites/default/files/ai_100_report_0906fnlc_single.pdf
+[2]: http://www.datamation.com/applications/artificial-intelligence-software-45-ai-projects-to-watch-1.html
diff --git a/sources/tech/20160912 8 best practices for building containerized applications.md b/sources/tech/20160912 8 best practices for building containerized applications.md
new file mode 100644
index 0000000000..d67ee5c473
--- /dev/null
+++ b/sources/tech/20160912 8 best practices for building containerized applications.md	
@@ -0,0 +1,74 @@
+8 best practices for building containerized applications
+====
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/containers_2015-2-osdc-lead.png?itok=0yid3gFY)
+
+Containers are a major trend in deploying applications in both public and private clouds. But what exactly are containers, why have they become a popular deployment mechanism, and how will you need to modify your application to optimize it for a containerized environment?
+
+### What are containers?
+
+The technology behind containers has a long history beginning with SELinux in 2000 and Solaris zones in 2005. Today, containers are a combination of several kernel features including SELinux, Linux namespaces, and control groups, providing isolation of end user processes, networking, and filesystem space.
+
+### Why are they so popular?
+
+The recent widespread adoption of containers is largely due to the development of standards aimed at making them easier to use, such as the Docker image format and distribution model. This standard calls for immutable images, which are the launching point for a container runtime. Immutable images guarantee that the same image the development team releases is what gets tested and deployed into the production environment.
+
+The lightweight isolation that containers provide creates a better abstraction for an application component. Components running in containers won't interfere with each other the way they might running directly on a virtual machine. They can be prevented from starving each other of system resources, and unless they are sharing a persistent volume won't block attempting to write to the same files. Containers have helped to standardize practices like logging and metric collection, and they allow for increased multi-tenant density on physical and virtual machines, all of which leads to lower deployment costs.
+
+### How do you build a container-ready application?
+
+Changing your application to run inside of a container isn't necessarily a requirement. The major Linux distributions have base images that can run anything that runs on a virtual machine. But the general trend in containerized applications is following a few best practices:
+
+- 1. Instances are disposable
+
+Any given instance of your application shouldn't need to be carefully kept running. If one system running a bunch of containers goes down, you want to be able to spin up new containers spread out across other available systems.
+
+- 2. Retry instead of crashing
+
+When one service in your application depends on another service, it should not crash when the other service is unreachable. For example, your API service is starting up and detects the database is unreachable. Instead of failing and refusing to start, you design it to retry the connection. While the database connection is down the API can respond with a 503 status code, telling the clients that the service is currently unavailable. This practice should already be followed by applications, but if you are working in a containerized environment where instances are disposable, then the need for it becomes more obvious.
+
+- 3. Persistent data is special
+
+Containers are launched based on shared images using a copy-on-write (COW) filesystem. If the processes the container is running choose to write out to files, then those writes will only exist as long as the container exists. When the container is deleted, that layer in the COW filesystem is deleted. Giving a container a mounted filesystem path that will persist beyond the life of the container requires extra configuration, and extra cost for the physical storage. Clearly defining the abstraction for what storage is persisted promotes the idea that instances are disposable. Having the abstraction layer also allows a container orchestration engine to handle the intricacies of mounting and unmounting persistent volumes to the containers that need them.
+
+- 4. Use stdout not log files
+
+You may now be thinking, if persistent data is special, then what do I do with log files? The approach the container runtime and orchestration projects have taken is that processes should instead [write to stdout/stderr][1], and have infrastructure for archiving and maintaining [container logs][2].
+
+- 5. Secrets (and other configurations) are special too
+
+You should never hard-code secret data like passwords, keys, and certificates into your images. Secrets are typically not the same when your application is talking to a development service, a test service, or a production service. Most developers do not have access to production secrets, so if secrets are baked into the image then a new image layer will have to be created to override the development secrets. At this point, you are no longer using the same image that was created by your development team and tested by quality engineering (QE), and have lost the benefit of immutable images. Instead, these values should be abstracted away into environment variables or files that are injected at container startup.
+
+- 6. Don't assume co-location of services
+
+In an orchestrated container environment you want to allow the orchestrator to send your containers to whatever node is currently the best fit. Best fit could mean a number of things: it could be based on whichever node has the most space right now, the quality of service the container is requesting, whether the container requires persistent volumes, etc. This could easily mean your frontend, API, and database containers all end up on different nodes. While it is possible to force an API container to each node (see [DaemonSets][3] in Kubernetes), this should be reserved for containers that perform tasks like monitoring the nodes themselves.
+
+- 7. Plan for redundancy / high availability
+
+Even if you don't have enough load to require an HA setup, you shouldn't write your service in a way that prevents you from running multiple copies of it. This will allow you to use rolling deployments, which make it easy to move load off one node and onto another, or to upgrade from one version of a service to the next without taking any downtime.
+
+- 8. Implement readiness and liveness checks
+
+It is common for applications to have startup time before they are able to respond to requests, for example, an API server that needs to populate in-memory data caches. Container orchestration engines need a way to check that your container is ready to serve requests. Providing a readiness check for new containers allows a rolling deployment to keep an old container running until it is no longer needed, preventing downtime. Similarly, a liveness check is a way for the orchestration engine to continue to check that the container is in a healthy state. It is up to the application creator to decide what it means for their container to be healthy, or "live". A container that is no longer live will be killed, and a new container created in its place.
+
+### Want to find out more?
+
+I'll be at the Grace Hopper Celebration of Women in Computing in October, come check out my talk: [Containerization of Applications: What, Why, and How][4]. Not headed to GHC this year? Then read on about containers, orchestration, and applications on the [OpenShift][5] and [Kubernetes][6] project sites.
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/life/16/9/8-best-practices-building-containerized-applications
+
+作者:[Jessica Forrester ][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/jwforres
+[1]: https://docs.docker.com/engine/reference/commandline/logs/
+[2]: http://kubernetes.io/docs/getting-started-guides/logging/
+[3]: http://kubernetes.io/docs/admin/daemons/
+[4]: https://www.eiseverywhere.com/ehome/index.php?eventid=153076&tabid=351462&cid=1350690&sessionid=11443135&sessionchoice=1&
+[5]: https://www.openshift.org/
+[6]: http://kubernetes.io/
diff --git a/sources/tech/20160912 Content Security Policy, Your Future Best Friend.md b/sources/tech/20160912 Content Security Policy, Your Future Best Friend.md
new file mode 100644
index 0000000000..80f435bd4e
--- /dev/null
+++ b/sources/tech/20160912 Content Security Policy, Your Future Best Friend.md	
@@ -0,0 +1,257 @@
+Content Security Policy, Your Future Best Friend
+=====
+
+A long time ago, my personal website was attacked. I do not know how it happened, but it happened. Fortunately, the damage from the attack was quite minor: A piece of JavaScript was inserted at the bottom of some pages. I updated the FTP and other credentials, cleaned up some files, and that was that.
+
+One point made me mad: At the time, there was no simple solution that could have informed me there was a problem and — more importantly — that could have protected the website’s visitors from this annoying piece of code.
+
+A solution exists now, and it is a technology that succeeds in both roles. Its name is content security policy (CSP).
+
+### What Is A CSP? Link
+
+The idea is quite simple: By sending a CSP header from a website, you are telling the browser what it is authorized to execute and what it is authorized to block.
+
+Here is an example with PHP:
+
+```
+<?php
+    header("Content-Security-Policy: <your directives>");
+?>
+```
+
+#### SOME DIRECTIVES LINK
+
+You may define global rules or define rules related to a type of asset:
+
+```
+default-src 'self' ;
+     # self = same port, same domain name, same protocol => OK
+```
+
+The base argument is default-src: If no directive is defined for a type of asset, then the browser will use this value.
+
+```
+script-src 'self' www.google-analytics.com ;
+     # JS files on these domains => OK
+```
+
+In this example, we’ve authorized the domain name www.google-analytics.com as a source of JavaScript files to use on our website. We’ve added the keyword 'self'; if we redefined the directive script-src with another rule, it would override default-src rules.
+
+If no scheme or port is specified, then it enforces the same scheme or port from the current page. This prevents mixed content. If the page is https://example.com, then you wouldn’t be able to load http://www.google-analytics.com/file.js because it would be blocked (the scheme wouldn’t match). However, there is an exception to allow a scheme upgrade. If http://example.com tries to load https://www.google-analytics.com/file.js, then the scheme or port would be allowed to change to facilitate the scheme upgrade.
+
+```
+style-src 'self' data: ;
+     # Data-Uri in a CSS => OK
+```
+
+In this example, the keyword data: authorizes embedded content in CSS files.
+
+Under the CSP level 1 specification, you may also define rules for the following:
+
+- `img-src`
+
+valid sources of images
+
+- `connect-src`
+
+applies to XMLHttpRequest (AJAX), WebSocket or EventSource
+
+- `font-src`
+
+valid sources of fonts
+
+- `object-src`
+
+valid sources of plugins (for example, `<object>, <embed>, <applet>`)
+
+- `media-src`
+
+valid sources of `<audio> and <video>`
+
+
+CSP level 2 rules include the following:
+
+- `child-src`
+
+valid sources of web workers and elements such as `<frame>` and `<iframe>` (this replaces the deprecated frame-src from CSP level 1)
+
+- `form-action`
+
+valid sources that can be used as an HTML `<form>` action
+
+- `frame-ancestors`
+
+valid sources for embedding the resource using `<frame>, <iframe>, <object>, <embed> or <applet>`.
+
+- `upgrade-insecure-requests`
+
+instructs user agents to rewrite URL schemes, changing HTTP to HTTPS (for websites with a lot of old URLs that need to be rewritten).
+
+For better backwards-compatibility with deprecated properties, you may simply copy the contents of the actual directive and duplicate them in the deprecated one. For example, you may copy the contents of child-src and duplicate them in frame-src.
+
+CSP 2 allows you to whitelist paths (CSP 1 allows only domains to be whitelisted). So, rather than whitelisting all of www.foo.com, you could whitelist www.foo.com/some/folder to restrict it further. This does require CSP 2 support in the browser, but it is obviously more secure.
+
+#### AN EXAMPLE 
+
+I made a simple example for the Paris Web 2015 conference, where I presented a talk entitled “[CSP in Action][1].”
+Without CSP, the page would look like this:
+
+![](https://www.smashingmagazine.com/wp-content/uploads/2016/09/csp_smashing1b-500.jpg)
+
+Not very nice. What if we enabled the following CSP directives?
+
+```
+<?php
+    header("Content-Security-Policy: 
+      default-src 'self' ;
+      script-src 'self' www.google-analytics.com stats.g.doubleclick.net ; 
+      style-src 'self' data: ;
+      img-src 'self' www.google-analytics.com stats.g.doubleclick.net data: ;
+      frame-src 'self' ;");
+?>
+```
+
+What would the browser do? It would (very strictly) apply these directives under the primary rule of CSP, which is that anything not authorized in a CSP directive will be blocked (“blocked” meaning not executed, not displayed and not used by the website).
+
+By default in CSP, inline scripts and styles are not authorized, which means that every `<script>`, onclick or style attribute will be blocked. You could authorize inline CSS with style-src 'unsafe-inline' ;.
+
+In a modern browser with CSP support, the example would look like this:
+
+![](https://www.smashingmagazine.com/wp-content/uploads/2016/09/csp_smashing5-500.jpg)
+
+What happened? The browser applied the directives and rejected anything that was not authorized. It sent these notifications to the console:
+
+![](https://www.smashingmagazine.com/wp-content/uploads/2016/09/csp_smashing2-500.jpg)
+
+If you’re still not convinced of the value of CSP, have a look at Aaron Gustafson’s article “[More Proof We Don’t Control Our Web Pages][2].”
+
+Of course, you may use stricter directives than the ones in the example provided above:
+
+- set default-src to 'none',
+- specify what you need for each rule,
+- specify the exact paths of required files,
+- etc.
+
+
+### More Information On CSP 
+
+#### SUPPORT 
+
+CSP is not a nightly feature requiring three flags to be activated in order for it to work. CSP levels 1 and 2 are candidate recommendations! [Browser support for CSP level 1][3] is excellent.
+
+![](https://www.smashingmagazine.com/wp-content/uploads/2016/09/csp_smashing3-500.jpg)
+
+The [level 2 specification][4] is more recent, so it is a bit less supported.
+
+![](https://www.smashingmagazine.com/wp-content/uploads/2016/09/csp_smashing4-500.jpg)
+
+CSP level 3 is an early draft now, so it is not yet supported, but you can already do great things with levels 1 and 2.
+
+#### OTHER CONSIDERATIONS
+
+CSP has been designed to reduce cross-site scripting (XSS) risks, which is why enabling inline scripts in script-src directives is not recommended. Firefox illustrates this issue very nicely: In the browser, hit Shift + F2 and type security csp, and it will show you directives and advice. For example, here it is used on Twitter’s website:
+
+![](https://www.smashingmagazine.com/wp-content/uploads/2016/09/csp_smashing6b-500.jpg)
+
+Another possibility for inline scripts or inline styles, if you really have to use them, is to create a hash value. For example, suppose you need to have this inline script:
+
+```
+<script>alert('Hello, world.');</script>
+```
+
+You might add 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng=' as a valid source in your script-src directives. The hash generated is the result of this in PHP:
+
+```
+<?php
+    echo base64_encode(hash('sha256', "alert('Hello, world.');", true));
+?>
+```
+
+I said earlier that CSP is designed to reduce XSS risks — I could have added, “… and reduce the risks of unsolicited content.” With CSP, you have to know where your sources of content are and what they are doing on your front end (inline styles, etc.). CSP can also help you force contributors, developers and others to respect your rules about sources of content!
+
+Now your question is, “OK, this is great, but how do we use it in a production environment?”
+
+### How To Use It In The Real World
+
+The easiest way to get discouraged with using CSP the first time is to test it in a live environment, thinking, “This will be easy. My code is bad ass and perfectly clean.” Don’t do this. I did it. It’s stupid, trust me.
+
+As I explained, CSP directives are activated with a CSP header — there is no middle ground. You are the weak link here. You might forget to authorize something or forget a piece of code on your website. CSP will not forgive your oversight. However, two features of CSP greatly simplify this problem.
+
+#### REPORT-URI
+
+Remember the notifications that CSP sends to the console? The directive report-uri can be used to tell the browser to send them to the specified address. Reports are sent in JSON format.
+
+```
+report-uri /csp-parser.php ;
+```
+
+So, in the csp-parser.php file, we can process the data sent by the browser. Here is the most basic example in PHP:
+
+```
+$data = file_get_contents('php://input');
+
+    if ($data = json_decode($data, true)) {
+     $data = json_encode(
+      $data,
+      JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
+      );
+     mail(EMAIL, SUBJECT, $data);
+    }
+```
+
+This notification will be transformed into an email. During development, you might not need anything more complex than this.
+
+For a production environment (or a more visited development environment), you’d better use a way other than email to collect information, because there is no auth or rate limiting on the endpoint, and CSP can be very noisy. Just imagine a page that generates 100 CSP notifications (for example, a script that display images from an unauthorized source) and that is viewed 100 times a day — you could get 10,000 notifications a day!
+
+A service such as report-uri.io can be used to simplify the management of reporting. You can see other simple examples for report-uri (with a database, with some optimizations, etc.) on GitHub.
+
+### REPORT-ONLY
+
+As we have seen, the biggest issue is that there is no middle ground between CSP being enabled and disabled. However, a feature named report-only sends a slightly different header:
+
+```
+<?php
+    header("Content-Security-Policy-Report-Only: <your directives>");
+?>
+```
+
+Basically, this tells the browser, “Act as if these CSP directives were being applied, but do not block anything. Just send me the notifications.” It is a great way to test directives without the risk of blocking any required assets.
+
+With report-only and report-uri, you can test CSP directives with no risk, and you can monitor in real time everything CSP-related on a website. These two features are really powerful for deploying and maintaining CSP!
+
+### Conclusion
+
+#### WHY CSP IS COOL
+
+CSP is most important for your users: They don’t have to suffer any unsolicited scripts or content or XSS vulnerabilities on your website.
+
+The most important advantage of CSP for website maintainers is awareness. If you’ve set strict rules for image sources, and a script kiddie attempts to insert an image on your website from an unauthorized source, that image will be blocked, and you will be notified instantly.
+
+Developers, meanwhile, need to know exactly what their front-end code does, and CSP helps them master that. They will be prompted to refactor parts of their code (avoiding inline functions and styles, etc.) and to follow best practices.
+
+#### HOW CSP COULD BE EVEN COOLER LINK
+
+Ironically, CSP is too efficient in some browsers — it creates bugs with bookmarklets. So, do not update your CSP directives to allow bookmarklets. We can’t blame any one browser in particular; all of them have issues:
+
+- Firefox
+- Chrome (Blink)
+- WebKit
+
+Most of the time, the bugs are false positives in blocked notifications. All browser vendors are working on these issues, so we can expect fixes soon. Anyway, this should not stop you from using CSP.
+
+--------------------------------------------------------------------------------
+
+via: https://www.smashingmagazine.com/2016/09/content-security-policy-your-future-best-friend/?utm_source=webopsweekly&utm_medium=email
+
+作者:[Nicolas Hoffmann][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.smashingmagazine.com/author/nicolashoffmann/
+[1]: https://rocssti.net/en/example-csp-paris-web2015
+[2]: https://www.aaron-gustafson.com/notebook/more-proof-we-dont-control-our-web-pages/
+[3]: http://caniuse.com/#feat=contentsecuritypolicy
+[4]: http://caniuse.com/#feat=contentsecuritypolicy2
+
diff --git a/sources/tech/20160912 Five Linux Server Distros Worth Checking Out.md b/sources/tech/20160912 Five Linux Server Distros Worth Checking Out.md
new file mode 100644
index 0000000000..68816e2b8e
--- /dev/null
+++ b/sources/tech/20160912 Five Linux Server Distros Worth Checking Out.md	
@@ -0,0 +1,44 @@
+Five Linux Server Distros Worth Checking Out
+====
+
+>Pretty much any of the nearly 300 Linux distributions you'll find listed on Distrowatch can be made to work as servers. Here are those that stand out above the rest.
+
+![](http://windowsitpro.com/site-files/windowsitpro.com/files/imagecache/large_img/uploads/2016/09/cloudservers.jpg)
+
+Pretty much any of the nearly 300 Linux distributions you'll find listed on Distrowatch can be made to work as servers. Since Linux's earliest days, users have been provisioning "all purpose" distributions such as Slackware, Debian and Gentoo to do heavy lifting as servers for home and business. That may be fine for the hobbyist, but its a lot of unnecessary work for the professional.
+
+From the beginning, however, there have been distributions with no other purpose but to serve files and applications, help workstations share common peripherals, serve-up web pages and all the other things we ask servers to do, whether in the cloud, in a data center or on a shelf in a utility closet.
+
+Here's a look at four of the most used Linux server distros, as well as one distro that might fit the bill for smaller businesses.
+
+**Red Hat Enterprise Linux**: Perhaps the best known of Linux server distros, RHEL has a reputation for being a solid distribution ready for the most demanding mission critical tasks -- like running the New York Stock Exchange for instance. It's also backed by Red Hat's best-of-breed support.
+
+The downside? While Red Hat is known for offering customer service and support that's second to none, its support subscriptions aren't cheap. Some might point out, however, that you get what you pay for. Cheaper third party support for RHEL is available, but you might want to do some research before going that route.
+
+**CentOS**: Anyone who likes RHEL but would like to avoid shoveling money to Red Hat for support should take a look at CentOS, which is basically an RHEL fork. Although it's been around since 2004, in 2014 it became officially sponsored by Red Hat, which now employs most of the project's developers. This means that security patches and bug fixes are made available to CentOS soon after they're pushed to Red Hat.
+
+If you're going to deploy CentOS, you'll need people with Linux skills on staff, because as far as technical support goes, you're mainly on your own. The good news is that the CentOS community offers excellent resources, such as mailing lists, web forums, and chat rooms, so help is available to those who search.
+
+**Ubuntu Server**: When Canonical announced many years back that it was coming out with a server edition of Ubuntu, you could hear the snickers. Laughter turned into amazement rather quickly, however, as Ubuntu Server rapidly took hold. This was partly due to the DNA it shares as a derivative of Debian, which has long been a favorite base for Linux servers. Ubuntu filled a gap by adding affordable technical support, superior hardware support, developer tools and lots of polish.
+
+How popular is Ubuntu Server? Recent figures show it being the most deployed operating system both on OpenStack and on the Amazon Elastic Compute Cloud, where it outpaces second place Amazon Linux Amazon Machine Image by a mile and leaves third place Windows in the virtual dust. Another study shows it as the most used Linux web server.
+
+**SUSE Linux Enterprise Server**: This German distro has a large base of users in Europe, and was a top server distro on this side of the Atlantic until PR issues arose after it was bought by Novell in the early part of the century. With those days long behind it, SUSE has been gaining ground in the US, and its use will probably accelerate now that HPE is naming it as its preferred Linux partner.
+
+SUSE Linux Enterprise Server, or SLES, is stable and easy to maintain, which you'd expect for a distro that's been around for nearly as long as Linux itself. Affordable 24/7 "rapid-response" technical support is available, making it suitable for mission critical deployments.
+
+**ClearOS**: Based on RHEL, ClearOS is included here because it's simple enough for anyone, even most non-techies, to configure. Targeted at small to medium sized businesses, it can also be used as an entertainment server by home users. Using a web-based administration interface for ease-of-use, it's built with the premise in mind that "building your IT infrastructure should be as simple as downloading apps on your smart phone."
+
+The latest release, version 7.2, includes capabilities that might not be expected from a "lightweight" offering, such as VM support which includes Microsoft Hyper-V, support for the XFS and BTRFS file systems, as well as support for LVM caching and IPv6. Available in a free version or in an inexpensive "professional" version that comes with a variety of support options.
+
+--------------------------------------------------------------------------------
+
+via: http://windowsitpro.com/industry/five-linux-server-distros-worth-checking-out
+
+作者:[Christine Hall][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://windowsitpro.com/industry/five-linux-server-distros-worth-checking-out
diff --git a/sources/tech/20160913 4 big ways companies benefit from having open source program offices.md b/sources/tech/20160913 4 big ways companies benefit from having open source program offices.md
new file mode 100644
index 0000000000..674ddfdb85
--- /dev/null
+++ b/sources/tech/20160913 4 big ways companies benefit from having open source program offices.md	
@@ -0,0 +1,50 @@
+4 big ways companies benefit from having open source program offices
+====
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/BUSINESS_creativity.png?itok=x2HTRKVW)
+
+In the first article in my series on open source program offices, I took a deep dive into [what an open source program office is and why your company might need one][1]. Next I looked at [how Google created a new kind of open source program office][2]. In this article, I'll explain a few benefits of having an open source program office.
+
+At first glance, one big reason why a company not in the business of software development might more enthusiastically embrace an open source program office is because they have less to lose. After all, they're not gambling with software products that are directly tied to revenue. Facebook, for example, can easily unleash a distributed key-value datastore as an open source project because they don't sell a product called "enterprise key-value datastore." That answers the question of risk, but it still doesn't answer the question of what they gain from contributing to the open source ecosystem. Let's look at a few potential reasons and then tackle each. You'll notice a lot of overlap with vendor open source program offices, but some of the motivations are slightly different.
+
+### Recruiting
+
+Recruiting is perhaps the easiest way to sell an open source program office to upper management. Show them the costs associated with recruiting, as well as the return on investment, and then explain how developing relationships with talented engineers results in a pipeline of talented developers who are actually familiar with your technology and excited to help work on it. We don't really need to go in more depth here—it's self-explanatory, right?
+
+### Technology influence
+
+Once upon a time, companies that didn't specialize in selling software were powerless to influence development cycles of their software vendors directly, especially if they were not a large customer. Open source completely changed that dynamic and brought users onto a more level playing field with vendors. With the rise of open source development, anyone could push technology into a chosen direction, assuming they were willing to invest the time and resources. But these companies learned that simply investing in developer time, although fruitful, could be even more useful if tied to an overarching strategic effort&mdash. Think bug fixes vs. software architects—lots of companies push bug fixes to upstream open source projects, but some of these same companies began to learn that coordinating a sustained effort with a deeper commitment paid off with faster feature development, which could be good for business. With the open source program office model, companies have staffers who can sniff out strategically important open source communities in which they then invest developer resources.
+
+With rapidly growing companies such as Google and Facebook, providing leadership in existing open source projects still proved insufficient for their expanding businesses. Facing the challenges of intense growth and building out hyperscale systems, many of the largest companies had built highly customized stacks of software for internal use only. What if they could convince others to collaborate on some of these infrastructure projects? Thus, while they maintained investments in areas such as the Linux kernel, Apache, and other existing projects, they also began to release their own large projects. Facebook released Cassandra, Twitter created Mesos, and eventually Google created the Kubernetes project. These projects have become major platforms for industry innovation, proving to be spectacular successes for the companies involved. (Note that Facebook stopped using Cassandra internally after it needed to create a new software project to solve the problem at a larger scale. However, by that time Cassandra had already become popular and DataStax had formed to take on development). Each of these projects have spawned entire ecosystems of developers, related projects, and end users that serve to accelerate growth and development.
+
+This would not have been possible without coordination between an open source program office and strategic company initiatives. Without that effort, each of the companies mentioned would still be trying to solve these problems individually—and more slowly. Not only have these projects helped solve business problems internally, they also helped establish the companies that created them as industry heavyweights. Sure, Google has been an industry titan for a few years now, but the growth of Kubernetes ensures both better software, and a direct say in the future direction of container technologies, even more than it already had. These companies are still known for their hyperscale infrastructure and for simply being large Silicon Valley stalwarts. Lesser known, but possibly even more important, is their new relevance as technology producers. Open source program offices guide these efforts and maximize their impact, through technology recommendations and relationships with influential developers, not to mention deep expertise in community governance and people management.
+
+### Marketing power
+
+Going hand-in-hand with technology influence is how each company talks about its open source efforts. By honing the messages around these projects and communities, an open source program office is able to deliver maximum impact through targeted marketing campaigns. Marketing has long been a dirty word in open source circles, because everyone has had a bad experience with corporate marketing. In the case of open source communities, marketing takes on a vastly different form from a traditional approach and involves amplifying what is already happening in the communities of strategic importance. Thus, an open source program office probably won't create whiz-bang slides about a project that hasn't even released any code yet, but they'll talk about the software they've created and other initiatives they've participated in. Basically, no vaporware here.
+
+Think of the first efforts made by Google's open source program office. They didn't simply contribute code to the Linux kernel and other projects—they talked about it a lot, often in keynotes at open source conferences. They didn't just give money to students who write open source code—they created a global program, the Google Summer of Code, that became a cultural touchstone of open source development. This marketing effort cemented Google's status as a major open source developer long before Kubernetes was even developed. As a result, Google wielded major influence during the creation of the GPLv3 license, and company speakers and open source program office representatives became staples at tech events. The open source program office is the entity best situated to coordinate these efforts and deliver real value for the parent company.
+
+### Improve internal processes
+
+Improving internal processes may not sound like a big benefit, but overcoming chaotic internal processes is a challenge for every open source program office, whether software vendor or company-driven. Whereas a software vendor must make sure that their processes don't step on products they release (for example, unintentionally open sourcing proprietary software), a user is more concerned with infringement of intellectual property (IP) law: patents, copyrights, and trademarks. No one wants to get sued simply for releasing software. Without an active open source program office to manage and coordinate licensing and other legal questions, large companies face great difficulty in arriving at a consensus around open source processes and governance. Why is this important? If different groups release software under incompatible licenses, not only will this prove to be an embarrassment, it also will provide a significant obstacle to achieving one of the most basic goals—improved collaboration.
+
+Combined with the fact that many of these companies are still growing incredibly quickly, an inability to establish basic rules around process will prove unwieldy sooner than expected. I've seen large spreadsheets with a matrix of approved and unapproved licenses as well as guidelines for how to (and how not to) create open source communities while complying with legal restrictions. The key is to have something that developers can refer to when they need to make decisions, without incurring the legal overhead of a massive, work-slowing IP review every time a developer wants to contribute to an open source community.
+
+Having an active open source program office that maintains rules over license compliance and source contribution, as well as establishing training programs for engineers, helps to avoid potential legal pitfalls and costly lawsuits. After all, what good is better collaboration on open source projects if the company loses real money because someone didn't read the license? The good news is that companies have less to worry about with respect to proprietary IP when compared to software vendors. The bad news is that their legal questions are no less complex, especially when they run directly into the legal headwinds of a software vendor.
+
+How has your organization benefited from having an open source program office? Let me know about it in the comments.
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/business/16/9/4-big-ways-companies-benefit-having-open-source-program-offices
+
+作者:[John Mark Walker][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/johnmark
+[1]: https://opensource.com/business/16/5/whats-open-source-program-office
+[2]: https://opensource.com/business/16/8/google-open-source-program-office
diff --git a/sources/tech/20160913 How to Use Markdown in WordPress to Improve Workflow.md b/sources/tech/20160913 How to Use Markdown in WordPress to Improve Workflow.md
new file mode 100644
index 0000000000..a199eddabe
--- /dev/null
+++ b/sources/tech/20160913 How to Use Markdown in WordPress to Improve Workflow.md	
@@ -0,0 +1,58 @@
+How to Use Markdown in WordPress to Improve Workflow
+====
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/09/markdown-wordpress-featured-2.jpg)
+
+Markdown is a simple markup language that helps you format your plain text documents with minimal effort. You may be used to formatting your articles using HTML or the Visual Editor in WordPress, but using markdown makes formatting a lot easier, and you can always export it to several formats including (but not limited to) HTML.
+
+WordPress does not come with native markdown support, but there are plugins that can add this functionality to your website if you so desire.
+
+In this tutorial I will demonstrate how to use the popular WP-Markdown plugin to add markdown support to a WordPress website.
+
+### Installation
+
+You can install this plugin directly by navigating to “Plugins -> Add New” and entering “[wp-markdown][1]” in the search box provided. The plugin should appear as the first option on the list. Click “Install Now” to install it.
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/08/markdown-wordpress-install-plugin-1.png)
+
+### Configuration
+
+Once you have installed the plugin and activated it, navigate to “Settings -> Writing” in the menu and scroll down until you get to the markdown section.
+
+You can enable markdown support in posts, pages and comments. You can also enable a help bar for your post editor or comments which could be handy if you’re just learning the markdown syntax.
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/09/markdown-wordpress-configuration.png)
+
+If you include code snippets in your blog posts, enabling the “Prettify syntax highlighter” option will automatically provide syntax highlighting for your code snippets.
+
+Once you are satisfied with your selections, click “Save Changes” to save your settings.
+
+### Write your posts with Markdown
+
+Once you have enabled markdown support on your website, you can start using it right away.
+
+Create a new post by going to “Posts -> Add New.” You will notice that the default Visual and Plain Text editors have been replaced by the markdown editor.
+
+If you did not enable the markdown help bar in the configuration options, you will not see a live preview of your formatted markdown. Nonetheless, as long as your syntax is correct, your markdown will be converted to valid HTML when you save or publish the post.
+
+However, if you’re a beginner to markdown and the live preview feature is important to you, simply go back to the settings to enable the help bar option, and you will get a nice live preview area at the bottom of your posts. In addition, you also get some buttons on top that will help you quickly insert markdown syntax into your posts. This could be a potentially amazing setting if people use it. You can adjust the priority of the notification on individual apps. This will let you choose what you see in the notification bar.
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/08/markdown-wordpress-create-post.png)
+
+### Wrap up
+
+As you can see adding markdown support to a WordPress website is really easy, and it will only take a few minutes of your time. If you are completely new to markdown, you might also check out our [markdown cheatsheet][2] which provides a comprehensive reference to the markdown syntax.
+
+--------------------------------------------------------------------------------
+
+via: https://www.maketecheasier.com/use-markdown-in-wordpress/?utm_medium=feed&utm_source=feedpress.me&utm_campaign=Feed%3A+maketecheasier
+
+作者:[Ayo Isaiah][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.maketecheasier.com/author/ayoisaiah/
+[1]: https://wordpress.org/plugins/wp-markdown/
+[2]: https://www.maketecheasier.com/productive-with-markdown-cheatsheet/
diff --git a/sources/tech/20160913 Monitoring Docker Containers with Elasticsearch and cAdvisor.md b/sources/tech/20160913 Monitoring Docker Containers with Elasticsearch and cAdvisor.md
new file mode 100644
index 0000000000..fe5d8025a7
--- /dev/null
+++ b/sources/tech/20160913 Monitoring Docker Containers with Elasticsearch and cAdvisor.md	
@@ -0,0 +1,207 @@
+Monitoring Docker Containers with Elasticsearch and cAdvisor
+=======
+
+If you’re running a Swarm Mode cluster or even a single Docker engine, you’ll end up asking this question:
+
+>How do I keep track of all that’s happening?
+
+The answer is “not easily.”
+
+You need a few things to have a complete overview of stuff like:
+
+
+1. Number and status of containers
+2. If, where, and when a container has been moved to another node
+3. Number of containers on a given node
+4. Traffic peaks at a given time
+5. Orphan volumes and networks
+6. Free disk space, free inodes
+7. Number of containers against number of veths attached to the docker0 and docker_gwbridge bridges
+8. Up and down Swarm nodes
+9. Centralize logs
+
+The goal of this post is to demonstrate the use of [Elasticsearch][1] + [Kibana][2] + [cAdvisor][3] as tools to analyze and gather metrics and visualize dashboards for Docker containers.
+
+Later on in this post, you can find a dashboard trying to address a few points from the previous list. There are also points that can’t be addressed by simply using cAdvisor, like the status of Swarm Mode nodes.
+
+Also, if you have specific needs that aren’t covered by cAdvisor or another tool, I encourage you to write your own data collector and data shipper (e.g., [Beats][4]). Note that I won’t be showing you how to centralize Docker containers log on Elasticsearch.
+
+>[“How do you keep track of all that’s happening in a Swarm Mode cluster? Not easily.” via @fntlnz][5]
+
+### Why Do We Need to Monitor Containers?
+
+Imagine yourself in the classic situation of managing a virtual machine, either just one or several. You are a tmux hero, so you have your sessions preconfigured to do basically everything, monitoring included. There’s a problem in production? You just do a top, htop, iotop, jnettop, whatevertop on all your machines, and you’re ready for troubleshooting!
+
+Now imagine that you have the same three nodes but split into 50 containers. You need some history displayed nicely in a single place where you can perform queries to know what happened instead of just risking your life in front of those ncurses tools.
+
+### What Is the Elastic Stack?
+
+The Elastic Stack is a set of tools composed of:
+
+- Elasticsearch
+- Kibana
+- Logstash
+- Beats
+
+We’re going to use a few open-source tools from the Elastic Stack, such as Elasticsearch for the JSON-based analytics engine and Kibana to visualize data and create dashboards.
+
+Another important piece of the Elastic Stack is Beats, but in this post, we’re focused on containers. There’s no official Beat for Docker, so we’ll just use cAdvisor that can natively talk with Elasticsearch.
+
+cAdvisor is a tool that collects, aggregates, and exports metrics about running containers. In our case, those metrics are being exported to an Elasticsearch storage.
+
+Two cool facts about cAdvisor are:
+
+- It’s not limited to Docker containers.
+- It has its own webserver with a simple dashboard to visualize gathered metrics for the current node.
+
+### Set Up a Test Cluster or BYOI
+
+As I did in my previous posts, my habit is to provide a small script to allow the reader to set up a test environment on which to try out my project’s steps in no time. So you can use the following not-for-production-use script to set up a little Swarm Mode cluster with Elasticsearch running as a container.
+
+>If you have enough time/experience, you can BYOI (Bring Your Own Infrastructure).
+
+
+To follow this post, you’ll just need:
+
+- One or more nodes running the Docker daemon >= 1.12
+- At least a stand-alone Elasticsearch node 2.4.X
+
+Again, note that this post is not about setting up a production-ready Elasticsearch cluster. A single node cluster is not recommended for production. So if you’re planning a production installation, please refer to [Elastic guidelines][6].
+
+### A friendly note for early adopters
+
+I’m usually an early adopter (and I’m already using the latest alpha version in production, of course). But for this post, I chose not to use the latest Elasticsearch 5.0.0 alpha. Their roadmap is not perfectly clear to me, and I don’t want be the root cause of your problems!
+
+So the Elasticsearch reference version for this post is the latest stable version, 2.4.0 at the moment of writing.
+
+### Test cluster setup script
+
+As said, I wanted to provide this script for everyone who would like to follow the blog without having to figure out how to create a Swarm cluster and install an Elasticsearch. Of course, you can skip this if you choose to use your own Swarm Mode engines and your own Elasticsearch nodes.
+
+To execute the setup script, you’ll need:
+
+- [Docker Machine][7] – latest version: to provision Docker engines on DigitalOcean
+- [DigitalOcean API Token][8]: to allow docker-machine to start nodes on your behalf
+
+![](https://resources.codeship.com/hubfs/CTAs/EVAL/Codeship_Request_Trial_Access.png?t=1473869513342)
+
+### Create Cluster Script
+
+Now that you have everything we need, you can copy the following script in a file named create-cluster.sh:
+
+```
+#!/usr/bin/env bash
+#
+# Create a Swarm Mode cluster with a single master and a configurable number of workers
+
+workers=${WORKERS:-"worker1 worker2"}
+
+#######################################
+# Creates a machine on Digital Ocean
+# Globals:
+#   DO_ACCESS_TOKEN The token needed to access DigitalOcean's API
+# Arguments:
+#   $1 the actual name to give to the machine
+#######################################
+create_machine() {
+  docker-machine create \
+    -d digitalocean \
+    --digitalocean-access-token=$DO_ACCESS_TOKEN \
+    --digitalocean-size 2gb \
+    $1
+}
+
+#######################################
+# Executes a command on the specified machine
+# Arguments:
+#   $1     The machine on which to run the command
+#   $2..$n The command to execute on that machine
+#######################################
+machine_do() {
+  docker-machine ssh $@
+}
+
+main() {
+
+  if [ -z "$DO_ACCESS_TOKEN" ]; then
+    echo "Please export a DigitalOcean Access token: https://cloud.digitalocean.com/settings/api/tokens/new"
+    echo "export DO_ACCESS_TOKEN=<yourtokenhere>"
+    exit 1
+  fi
+
+  if [ -z "$WORKERS" ]; then
+    echo "You haven't provided your workers by setting the \$WORKERS environment variable, using the default ones: $workers"
+  fi
+
+  # Create the first and only master
+  echo "Creating the master"
+
+  create_machine master1
+
+  master_ip=$(docker-machine ip master1)
+
+  # Initialize the swarm mode on it
+  echo "Initializing the swarm mode"
+  machine_do master1 docker swarm init --advertise-addr $master_ip
+
+  # Obtain the token to allow workers to join
+  worker_tkn=$(machine_do master1 docker swarm join-token -q worker)
+  echo "Worker token: ${worker_tkn}"
+
+  # Create and join the workers
+  for worker in $workers; do
+    echo "Creating worker ${worker}"
+    create_machine $worker
+    machine_do $worker docker swarm join --token $worker_tkn $master_ip:2377
+  done
+}
+
+main $@
+```
+
+And make it executable:
+
+```
+chmod +x create-cluster.sh
+```
+
+### Create the cluster
+
+As the name suggests, we’ll use the script to create the cluster. By default, the script will create a cluster with a single master and two workers. If you want to configure the number of workers, you can do that by setting the WORKERS environment variable.
+
+Now, let’s create that cluster!
+
+```
+./create-cluster.sh
+```
+
+Ok, now you can go out for a coffee. This will take a while.
+
+Finally the cluster is ready!
+
+--------------------------------------------------------------------------------
+
+via: https://blog.codeship.com/monitoring-docker-containers-with-elasticsearch-and-cadvisor/
+
+作者:[Lorenzo Fontana][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://blog.codeship.com/author/lorenzofontana/
+
+
+
+
+
+
+
+[1]: https://github.com/elastic/elasticsearch
+[2]: https://github.com/elastic/kibana
+[3]: https://github.com/google/cadvisor
+[4]: https://github.com/elastic/beats
+[5]: https://twitter.com/share?text=%22How+do+you+keep+track+of+all+that%27s+happening+in+a+Swarm+Mode+cluster%3F+Not+easily.%22+via+%40fntlnz&url=https://blog.codeship.com/monitoring-docker-containers-with-elasticsearch-and-cadvisor/
+[6]: https://www.elastic.co/guide/en/elasticsearch/guide/2.x/deploy.html
+[7]: https://docs.docker.com/machine/install-machine/
+[8]: https://cloud.digitalocean.com/settings/api/tokens/new
diff --git a/sources/tech/20160913 Ryver: Why You Should Be Using It instead of Slack.md b/sources/tech/20160913 Ryver: Why You Should Be Using It instead of Slack.md
new file mode 100644
index 0000000000..f4f4c54fcb
--- /dev/null
+++ b/sources/tech/20160913 Ryver: Why You Should Be Using It instead of Slack.md	
@@ -0,0 +1,62 @@
+Ryver: Why You Should Be Using It instead of Slack
+=====
+
+It seems like everyone has heard of Slack, a team communication tool that can be used across multiple platforms to stay in the loop. It has revolutionised the way users discuss and plan projects, and it’s a clear upgrade to emails.
+
+I work in small writing teams, and I’ve never had a problem with communicating with others on my phone or computer while using it. If you want to keep up to date with team of any size, it’s a great way to stay in the loop.
+
+So, why are we here? Ryver is supposed to be the next big thing, offering an upgraded service in comparison to Slack. It’s completely free, and they’re pushing for a larger share of the market.
+
+Is it good enough to be a Slack-Killer? What are the differences between two similar sounding services?
+
+Read on to find out more.
+
+### Why Ryver?
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/04/Ryver.jpg)
+
+Why mess with something that works? The developers at Ryver are well aware of Slack, and they’re hoping their improved service will be enough to make you switch over. They promise a completely free team-communication service with no hidden charges along the way.
+
+Thankfully, they deliver on their main aim with a high quality product.
+
+Extra content is the name of the game, and they promise to remove some of the limits you’ll find on a free account with Slack. Unlimited data storage is a major plus point, and it’s also more open in a number of ways. If storage limits are an issue for you, you have to check out Ryver.
+
+It’s a simple system to use, as it was built so that all functions are always one click away. It’s a mantra used to great success by Apple, and there aren’t many growing pains when you first get started.
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/09/ryver-web-interface.png)
+
+Conversations are split between personal chats and public posts, and it means there’s a clear line between team platforms and personal use. It should help to avoid broadcasting any embarrassing announcements to your colleagues, and I’ve seen a few during my time as a Slack user.
+
+Integration with a number of existing apps is supported, and there are native applications for most platforms.
+
+You can add guests when needed at no additional cost, and it’s useful if you deal with external clients regularly. Guests can add more guests, so there’s an element of fluidity that isn’t seen with the more popular option.
+
+Think of Ryver as a completely different service that will cater to different needs. If you need to deal with numerous clients on the same account, it’s worth trying out.
+
+The question is how is it free? The quick answer is premium users will be paying your way. Like Spotify and other services, there’s a minority paying for the rest of us. Here’s a direct link to their download page if you’re interested in giving it a go.
+
+### Should You Switch to Ryver?
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/04/Slack-homepage.jpg)
+
+Slack is great as long as you stick to smaller teams like I do, but Ryver has a lot to offer. The idea of a completely free team messaging program is noble, and it works perfectly.
+
+There’s nothing wrong with using both, so make sure to try out the competition if you’re not willing to pay for a premium Slack account. You might find that both are better in different situations, depending on what you need.
+
+Above all, Ryver is a great free alternative, and it’s more than just a Slack clone. They have a clear idea of what they’re trying to achieve, and they have a decent product that offers something different in a crowded marketplace.
+
+However, there’s a chance that it will disappear if there’s a sustained lack of funding in the future. It could leave your teams and discussions in disarray. Everything is fine for now, but be careful if you plan to export a larger business over to the new upstart.
+
+If you’re tired of Slack’s limitations on a free account, you’ll be impressed by what Ryver has to offer. To learn more, check out their website for information about the service.
+
+--------------------------------------------------------------------------------
+
+via: https://www.maketecheasier.com/why-use-ryver-instead-of-slack/?utm_medium=feed&utm_source=feedpress.me&utm_campaign=Feed%3A+maketecheasier
+
+作者:[James Milin-Ashmore][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.maketecheasier.com/author/james-ashmore/
diff --git a/sources/tech/20160913 Taskwarrior: A Brilliant Command-Line TODO App For Linux.md b/sources/tech/20160913 Taskwarrior: A Brilliant Command-Line TODO App For Linux.md
new file mode 100644
index 0000000000..6b44101e53
--- /dev/null
+++ b/sources/tech/20160913 Taskwarrior: A Brilliant Command-Line TODO App For Linux.md	
@@ -0,0 +1,52 @@
+Taskwarrior: A Brilliant Command-Line TODO App For Linux
+====
+
+Taskwarrior is a simple, straight-forward command-line based TODO app for Ubuntu/Linux. This open-source app has to be one of the easiest of all [CLI based apps][4] I've ever used. Taskwarrior helps you better organize yourself, and without installing bulky new apps which sometimes defeats the whole purpose of TODO apps.
+
+![](https://2.bp.blogspot.com/-pQnRlOUNIxk/V9cuc3ytsBI/AAAAAAAAKHs/yYxyiAk4PwMIE0HTxlrm6arWOAPcBRRywCLcB/s1600/taskwarrior-todo-app.png)
+
+### Taskwarrior: A Simple CLI Based TODO App That Gets The Job Done!
+
+Taskwarrior is an open-source and cross-platform, command-line based TODO app, which lets you manage your to-do lists right from the Terminal. The app lets you add tasks, shows you the list, and removes tasks from that list with much ease. And what's more, it's available within your default repositories, no need to fiddle with PPAs. In Ubuntu 16.04 LTS and similar, do the following in Terminal to install Taskwarrior. 
+
+```
+sudo apt-get install task
+```
+
+A simple use case can be as follows:
+
+```
+$ task add Read a book
+Created task 1.
+$ task add priority:H Pay the bills
+Created task 2.
+```
+
+This is the same example I used in the screenshot above. Yes, you can set priority levels (H, L or M) as shown. And then you can use 'task' or 'task next' commands to see your newly-created todo list. For example:
+
+```
+$ task next
+
+ID Age P Description                      Urg
+-- --- - -------------------------------- ----
+ 2 10s H Pay the bills                     6
+ 1 20s   Read a book                       0
+```
+
+And once its completed, you can use 'task 1 done' or 'task 2 done' commands to clear the lists. A more comprehensive list of commands, use-cases [can be found here][1]. Also, Taskwarrior is cross-platform, which means you'll find a version that [fits your needs][2] no matter what. There's even an [Android version][3] if you want one. Enjoy!
+
+--------------------------------------------------------------------------------
+
+via: http://www.techdrivein.com/2016/09/taskwarrior-command-line-todo-app-linux.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+techdrivein+%28Tech+Drive-in%29
+
+作者:[Manuel Jose ][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.techdrivein.com/2016/09/taskwarrior-command-line-todo-app-linux.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+techdrivein+%28Tech+Drive-in%29
+[1]: https://taskwarrior.org/docs/
+[2]: https://taskwarrior.org/download/
+[3]: https://taskwarrior.org/news/news.20160225.html
+[4]: http://www.techdrivein.com/search/label/Terminal
diff --git a/sources/tech/20160913 The Five Principles of Monitoring Microservices.md b/sources/tech/20160913 The Five Principles of Monitoring Microservices.md
new file mode 100644
index 0000000000..9d283f8bdf
--- /dev/null
+++ b/sources/tech/20160913 The Five Principles of Monitoring Microservices.md	
@@ -0,0 +1,95 @@
+The Five Principles of Monitoring Microservices
+====
+
+![](http://thenewstack.io/wp-content/uploads/2016/09/toppicsysdig.jpg)
+
+The need for microservices can be summed up in just one word: speed. The need to deliver more functionality and reliability faster has revolutionized the way developers create software. Not surprisingly, this change has caused ripple effects within software management, including monitoring systems. In this post, we’ll focus on the radical changes required to monitor your microservices in production efficiently. We’ll lay out five guiding principles for adapting your monitoring approach for this new software architecture.
+
+Monitoring is a critical piece of the control systems of microservices, as the more complex your software gets, the harder it is to understand its performance and troubleshoot problems. Given the dramatic changes to software delivery, however, monitoring needs an overhaul to perform well in a microservice environment. The rest of this article presents the five principles of monitoring microservices, as follows:
+
+1. Monitor containers and what’s inside them.
+2. Alert on service performance, not container performance.
+3. Monitor services that are elastic and multi-location.
+4. Monitor APIs.
+5. Map your monitoring to your organizational structure.
+
+Leveraging these five principles will allow you to establish more effective monitoring as you make your way towards microservices. These principles will allow you to address both the technological changes associated with microservices, in addition to the organizational changes related to them.
+
+### The Principles of Microservice Monitoring
+
+#### 1. Monitor Containers and What’s Running Inside Them
+
+Containers gained prominence as the building blocks of microservices. The speed, portability, and isolation of containers made it easy for developers to embrace a microservice model. There’s been a lot written on the benefits of containers so we won’t recount it all here.
+
+Containers are black boxes to most systems that live around them. That’s incredibly useful for development, enabling a high level of portability from development through production, from developer laptop to cloud. But when it comes to operating, monitoring and troubleshooting a service, black boxes make common activities harder, leading us to wonder: what’s running in the container? How is the application/code performing? Is it spitting out important custom metrics? From the DevOps perspective, you need deep visibility inside containers rather than just knowing that some containers exist.
+
+![](http://thenewstack.io/wp-content/uploads/2016/09/greatfordev.jpg)
+
+The typical process for instrumentation in a non-containerized environment — an agent that lives in the user space of a host or VM — doesn’t work particularly well for containers. That’s because containers benefit from being small, isolated processes with as few dependencies as possible.
+
+And, at scale, running thousands of monitoring agents for even a modestly-sized deployment is an expensive use of resources and an orchestration nightmare. Two potential solutions arise for containers: 1) ask your developers to instrument their code directly, or 2) leverage a universal kernel-level instrumentation approach to see all application and container activity on your hosts. We won’t go into depth here, but each method has pros and cons.
+
+#### 2. Leverage Orchestration Systems to Alert on Service Performance
+
+Making sense of operational data in a containerized environment is a new challenge. The metrics of a single container have a much lower marginal value than the aggregate information from all the containers that make up a function or a service.
+
+This particularly applies to application-level information, like which queries have the slowest response times or which URLs are seeing the most errors, but also applies to infrastructure-level monitoring, like which services’ containers are using the most resources beyond their allocated CPU shares.
+
+Increasingly, software deployment requires an orchestration system to “translate” a logical application blueprint into physical containers. Common orchestration systems include Kubernetes, Mesosphere DC/OS and Docker Swarm. Teams use an orchestration system to (1) define your microservices and (2) understand the current state of each service in deployment. You could argue that the orchestration system is even more important than the containers. The actual containers are ephemeral — they matter only for the short time that they exist — while your services matter for the life of their usefulness.
+
+DevOps teams should redefine alerts to focus on characteristics that get as close to monitoring the experience of the service as possible. These alerts are the first line of defense in assessing if something is impacting the application. But getting to these alerts is challenging, if not impossible unless your monitoring system is container-native.
+
+Container-native solutions leverage orchestration metadata to dynamically aggregate container and application data and calculate monitoring metrics on a per-service basis. Depending on your orchestration tool, you might have different layers of a hierarchy that you’d like to drill into. For example, in Kubernetes, you typically have a Namespace, ReplicaSets, Pods and some containers. Aggregating at these various layers is essential for logical troubleshooting, regardless of the physical deployment of the containers that make up the service.
+
+![](http://thenewstack.io/wp-content/uploads/2016/09/servicemonitoring.jpg)
+
+#### 3. Be Prepared for Services that are Elastic and Multi-Location
+
+Elastic services are certainly not a new concept, but the velocity of change is much faster in container-native environments than virtualized environments. Rapidly changing environments can wreak havoc on brittle monitoring systems.
+
+Frequently monitoring legacy systems required manual tuning of metrics and checks based on individual deployments of software. This tuning can be as specific as defining the individual metrics to be captured, or configuring collection based on what application is operating in a particular container. While that may be acceptable on a small scale (think tens of containers), it would be unbearable in anything larger. Microservice focused monitoring must be able to comfortably grow and shrink in step with elastic services, without human intervention.
+
+For example, if the DevOps team must manually define what service a container is included in for monitoring purposes, they no doubt drop the ball as Kubernetes or Mesos spins up new containers regularly throughout the day. Similarly, if Ops were required to install a custom stats endpoint when new code is built and pushed into production, challenges may arise as developers pull base images from a Docker registry.
+
+In production, build monitoring toward a sophisticated deployment that spans multiple data centers or multiple clouds. Leveraging, for example, AWS CloudWatch will only get you so far if your services span your private data center as well as AWS. That leads back to implementing a monitoring system that can span these different locations as well as operate in dynamic, container-native environments.
+
+#### 4. Monitor APIs
+
+In microservice environments, APIs are the lingua franca. They are essentially the only elements of a service that are exposed to other teams. In fact, response and consistency of the API may be the “internal SLA” even if there isn’t a formal SLA defined.
+
+As a result, API monitoring is essential. API monitoring can take many forms but clearly, must go beyond binary up/down checks. For instance, it’s valuable to understand the most frequently used endpoints as a function of time. This allows teams to see if anything noticeable has changed in the usage of services, whether it be due to a design change or a user change.
+
+You can also consider the slowest endpoints of your service, as these can reveal significant problems, or, at the very least, point to areas that need the most optimization in your system.
+
+Finally, the ability to trace service calls through your system represents another critical capability. While typically used by developers, this type of profiling will help you understand the overall user experience while breaking information down into infrastructure and application-based views of your environment.
+
+#### 5. Map Monitoring to Your Organizational Structure
+
+While most of this post has been focused on the technological shift in microservices and monitoring, like any technology story, this is as much about people as it is about software bits.
+
+For those of you familiar with Conway’s law, he reminds us that the design of systems is defined by the organizational structure of the teams building them. The allure of creating faster, more agile software has pushed teams to think about restructuring their development organization and the rules that govern it.
+
+![](http://thenewstack.io/wp-content/uploads/2016/09/mapmonitoring.jpg)
+
+So if an organization wants to benefit from this new software architecture approach, their teams must, therefore, mirror microservices themselves. That means smaller teams, loosely coupled; that can choose their direction as long as it still meets the needs of the whole. Within each team, there is more control than ever over languages used, how bugs are handled, or even operational responsibilities.
+
+DevOps teams can enable a monitoring platform that does exactly this: allows each microservice team to isolate their alerts, metrics, and dashboards, while still giving operations a view into the global system.
+
+### Conclusion
+
+There’s one, clear trigger event that precipitated the move to microservices: speed. Organizations wanted to deliver more capabilities to their customers in less time. Once this happened, technology stepped in, the architectural move to micro-services and the underlying shift to containers make speed happen. Anything that gets in the way of this progress train is going to get run over on the tracks.
+
+As a result, the fundamental principles of monitoring need to adapt to the underlying technology and organizational changes that accompany microservices. Operations teams that recognize this shift can adapt to microservices earlier and easier.
+
+--------------------------------------------------------------------------------
+
+via: http://linoxide.com/firewall/pfsense-setup-basic-configuration/
+
+作者:[Apurva Dave][a] [Loris Degioanni][b]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://thenewstack.io/author/apurvadave/
+[b]: http://thenewstack.io/author/lorisdegioanni/
diff --git a/sources/tech/20160914 Down and dirty with Windows Nano Server 2016.md b/sources/tech/20160914 Down and dirty with Windows Nano Server 2016.md
new file mode 100644
index 0000000000..613d3de86e
--- /dev/null
+++ b/sources/tech/20160914 Down and dirty with Windows Nano Server 2016.md	
@@ -0,0 +1,85 @@
+Down and dirty with Windows Nano Server 2016
+====
+
+![](http://images.techhive.com/images/article/2016/04/pokes-fun-at-1164459_1280-100654917-primary.idge.jpg)
+
+>Nano Server is a very fast, powerful tool for remotely administering Windows servers, but you need to know what you're doing
+
+There's been a good deal of talk around the [upcoming Nano version of Windows Server 2016][1], the remote-administered, command-line version designed with private clouds and datacenters in mind. But there's also a big difference between talking about it and getting your hands into it. Let's get into the guts.
+
+Nano has no local login, is 64-bit all the way (applications, tools, and agents), and is fast to set up, update, and restart (for the rare times it needs to restart). It's perfect for compute hosts in or out of a cluster, a storage host, a DNS server, an IIS web server, and any server-hosting applications running in a container or virtual-machine guest operating system.
+
+A Nano Server isn't all that fun to play with: You have to know what you want to accomplish. Otherwise, you'll be looking at a remote PowerShell connection and wondering what you're supposed to do next. But if you know what you want, it's very fast and powerful.
+
+Microsoft has provided a [quick-start guide][2] to setting up Nano Server. Here, I take the boots-on-the-ground approach to show you what it's like in the real world.
+
+First, you have to create a .vhd virtual hard drive file. As you can see in Figure 1, I had a few issues with files not being in the right place. PowerShell errors often indicate a mistyped line, but in this case, I had to keep double-checking where I put the files so that it could use the ISO information (which has to be copied and pasted to the server you want to create the .vhd file on). Once you have everything in place, you should see it go through the process of creating the .vhd file.
+
+![](http://images.techhive.com/images/article/2016/09/nano-server-1-100682371-large.idge.jpg)
+>Figure 1: One of the many file path errors I got when trying to run the New-NanoServerImage script. Once I worked out the file-location issues, it went through and created the .vhd file (as shown here).
+
+Next, when you create the VM in Hyper-V using the VM wizard, you need to point to an existing virtual hard disk and point to the new .vhd file you created (Figure 2).
+
+![](http://images.techhive.com/images/article/2016/09/nano-server-2-100682368-large.idge.jpg)
+>Figure 2: Connecting to a virtual hard disk (the one you created at the start).
+
+When you start up the Nano server, you may get a memory error depending on how much memory you allocated and how much memory the Hyper-V server has left if you have other VMs running. I had to shut off a few VMs and increase the RAM until it finally started up. That was unexpected -- [Microsoft's Nano system][3] requirements say you can run it with 512MB, although it recommends you give it at least 800MB. (I ended up allocating 8GB after 1GB didn't work; I was impatient, so I didn't try increments in between.)
+
+I finally came to the login screen, then signed in to get the Nano Server Recovery Console (Figure 3), which is essentially Nano server's terminal screen.
+
+![](http://images.techhive.com/images/article/2016/09/nano-server-3-100682369-large.idge.jpg)
+>Figure 3: The Nano Server Recovery Console.
+
+Once I was in, I thought I was golden. But in trying to figure out a few details (how to join a domain, how to inject drivers I might not have, how to add roles), I realized that some configuration pieces would have been easier to add when I ran the New-NanoServerImage cmdlet by popping in a few more parameters.
+
+However, once you have the server up and running, there are ways to configure it live. It all starts with a Remote PowerShell connection, as Figure 4 shows.
+
+![](http://images.techhive.com/images/article/2016/09/nano-server-4-100682370-large.idge.jpg)
+>Figure 4: Getting information from the Nano Server Recovery Console that you can use to perform a PowerShell Remote connection.
+
+Microsoft provides direction on how to make the connection happen, but after trying four different sites, I found MSDN has the clearest (working) direction on the subject. Figure 5 shows the result.
+
+![](http://images.techhive.com/images/article/2016/09/nano-server-5-100682372-large.idge.jpg)
+>Figure 5: Making the remote PowerShell connection to your Nano Server.
+
+Note: Once you've done the remote connection the long way, you can connect more quickly using a single line:
+
+```
+Enter-PSSession –ComputerName "192.168.0.100"-Credential ~\Administrator.
+```
+
+If you knew ahead of time that this server was going to be a DNS server or be part of a compute cluster and so on, you would have added those roles or feature packages when you were creating the .vhd image in the first place. If you're looking to do so after the fact, you'll need to make the remote PowerShell connection, then install the NanoServerPackage and import it. Then you can see which packages you want to deploy using Find-NanoServerPackage (shown in Figure 6).
+
+![](http://images.techhive.com/images/article/2016/09/nano-server-6-100682373-large.idge.jpg)
+>Figure 6: Once you have installed and imported the NanoServerPackage, you can find the one you need to get your Nano Server up and running with the roles and features you require.
+
+I tested this out by running the DNS package with the following command: `Install-NanoServerPackage –Name Microsoft-NanoServer-DNS-Package`. Once it was installed, I had to enable it with the following command: `Enable-WindowsOptionalFeature –Online –FeatureName DNS-Server-Full-Role`.
+
+Obviously I didn't know these commands ahead of time. I have never run them before in my life, nor had I ever enabled a DNS role this way, but with a little research I had a DNS (Nano) Server up and running.
+
+The next part of the process involves using PowerShell to configure the DNS server. That's a completely different topic and one best researched online. But it doesn't appear to be mind-blowingly difficult once you've learned the cmdlets to use: Add a zone? Use the Add-DNSServerPrimaryZone cmdlet. Add a record in that zone? Use the Add-DNSServerResourceRecordA. And so on.
+
+After doing all this command-line work, you'll likely want proof that any of this is working. You should be able to do a quick review of PowerShell commands and not the many DNS ones that now present themselves (using Get-Command).
+
+But if you need a GUI-based confirmation, you can open Server Manager on a GUI-based server and add the IP address of the Nano Server. Then right-click that server and choose Manage As to provide your credentials (~\Administrator and password). Once you have connected, right-click the server in Server Manager and choose Add Roles and Features; it should show that you have DNS installed as a role, as Figure 7 shows.
+
+![](http://images.techhive.com/images/article/2016/09/nano-server-7-100682374-large.idge.jpg)
+>Figure 7: Proving through the GUI that DNS was installed.
+
+Don't bother trying to remote-desktop into the server. There is only so much you can do through the Server Manager tool, and that isn't one of them. And just because you can confirm the DNS role doesn't mean you have the ability to add new roles and features through the GUI. It's all locked down. Nano Server is how you'll make any needed adjustments.
+
+--------------------------------------------------------------------------------
+
+via: http://www.infoworld.com/article/3119770/windows-server/down-and-dirty-with-windows-nano-server-2016.html?utm_source=webopsweekly&utm_medium=email
+
+作者:[J. Peter Bruzzese ][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.infoworld.com/author/J.-Peter-Bruzzese/
+[1]: http://www.infoworld.com/article/3049191/windows-server/nano-server-a-slimmer-slicker-windows-server-core.html
+[2]: https://technet.microsoft.com/en-us/windows-server-docs/compute/nano-server/getting-started-with-nano-server
+[3]: https://technet.microsoft.com/en-us/windows-server-docs/get-started/system-requirements--and-installation
+
diff --git a/sources/tech/20160914 How to Speed Up LibreOffice with 4 Simple Steps.md b/sources/tech/20160914 How to Speed Up LibreOffice with 4 Simple Steps.md
new file mode 100644
index 0000000000..a3dc030f3d
--- /dev/null
+++ b/sources/tech/20160914 How to Speed Up LibreOffice with 4 Simple Steps.md	
@@ -0,0 +1,90 @@
+How to Speed Up LibreOffice with 4 Simple Steps
+====
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/08/speed-up-libreoffice-featured-2.jpg)
+
+For many fans and supporters of Open Source software, LibreOffice is the best alternative to Microsoft Office, and it has definitely seen huge improvements over the last few releases. However, the initial startup experience still leaves a lot to be desired. There are ways to improve launch time and overall performance of LibreOffice.
+
+I will go over some practical steps that you can take to improve the load time and responsiveness of LibreOffice in the paragraphs below.
+
+### 1. Increase Memory Per Object and Image Cache
+
+This will help the program load faster by allocating more memory resources to the image cache and objects.
+
+1. Launch LibreOffice Writer (or Calc)
+
+2. Navigate to “Tools -> Options” in the menubar or use the keyboard shortcut “Alt + F12.”
+
+3. Click “Memory” under LibreOffice and increase “Use for LibreOffice” to 128MB.
+
+4. Also increase “Memory per object” to 20Mb.
+
+5. Click “Ok” to save your changes.
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/08/speed-up-libreoffice-step-1.png)
+
+Note: You can set the numbers higher or lower than the suggested values depending on how powerful your machine is. It is best to experiment and see which value gives you the optimum performance.
+
+### 2. Enable LibreOffice QuickStarter
+
+If you have a generous amount of RAM on your machine, say 4GB and above, you can enable the “Systray Quickstarter” option to keep part of LibreOffice in memory for quicker response with opening new documents.
+
+You will definitely see improved performance in opening new documents after enabling this option.
+
+1. Open the options dialog by navigating to “Tools -> Options.”
+
+2. In the sidebar under “LibreOffice”, select “Memory.”
+
+3. Tick the “Enable Systray Quickstarter” checkbox.
+
+4. Click “OK” to save the changes.
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/08/speed-up-libreoffice-2.png)
+
+Once this option is enabled, you will see the LibreOffice icon in your system tray with options to open any type of document.
+
+### 3. Disable Java Runtime
+
+Another easy way to speed up the launch time and responsiveness of LibreOffice is to disable Java.
+
+1. Open the Options dialog using “Alt + F12.”
+
+2. In the sidebar, select “LibreOffice,” then “Advanced.”
+
+3. Uncheck the “Use Java runtime environment” option.
+
+4. Click “OK” to close the dialog.
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/08/speed-up-libreoffice-3.png)
+
+If all you use is Writer and Calc, disabling Java will not stop you from working with your files as normal. But to use LibreOffice Base and some other special features, you may need to re-enable it again. In that case, you will get a popup asking if you wish to turn it back on.
+
+### 4. Reduce Number of Undo Steps
+
+By default, LibreOffice allows you to undo up to 100 changes to a document. Most users do not need anywhere near that, so holding that many steps in memory is largely a waste of resources.
+
+I recommend that you reduce this number to 20 to free up memory for other things, but feel free to customise this part to suit your needs.
+
+1. Open the options dialog by navigating to “Tools -> Options.”
+
+2. In the sidebar under “LibreOffice,” select “Memory.”
+
+3. Under “Undo” and change the number of steps to your preferred value.
+
+4. Click “OK” to save the changes.
+
+![](https://maketecheasier-2d0f.kxcdn.com/assets/uploads/2016/08/speed-up-libreoffice-5.png)
+
+If the tips provided helped you speed up the launch time of your LibreOffice Suite, let us know in the comments. Also, please share any other tips you may know for others to benefit as well.
+
+--------------------------------------------------------------------------------
+
+via: https://www.maketecheasier.com/speed-up-libreoffice/?utm_medium=feed&utm_source=feedpress.me&utm_campaign=Feed%3A+maketecheasier
+
+作者:[Ayo Isaiah][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.maketecheasier.com/author/ayoisaiah/
diff --git a/sources/tech/20160914 It's time to make LibreOffice and OpenOffice one again.md b/sources/tech/20160914 It's time to make LibreOffice and OpenOffice one again.md
new file mode 100644
index 0000000000..d91d08d03a
--- /dev/null
+++ b/sources/tech/20160914 It's time to make LibreOffice and OpenOffice one again.md	
@@ -0,0 +1,66 @@
+Translating by bianjp
+
+It's time to make LibreOffice and OpenOffice one again
+==========
+
+![](http://tr2.cbsistatic.com/hub/i/2016/09/14/2e91089b-7ebd-4579-bf8f-74c34d1a94ce/e7e9c8dd481d8e068f2934c644788928/openofficedeathhero.jpg)
+
+Let's talk about OpenOffice. More than likely you've already read, countless times, that Apache OpenOffice is near the end. The last stable iteration was 4.1.2 (released October, 2015) and a recent major security flaw took a month to patch. A lack of coders has brought development to a creeping crawl. And then, the worst possible news hit the ether; the project suggested users switch to MS Office (or LibreOffice).
+
+For whom the bells tolls? The bell tolls for thee, OpenOffice.
+
+I'm going to say something that might ruffle a few feathers. Are you ready for it?
+
+The end of OpenOffice will be a good thing for open source and for users.
+
+Let me explain.
+
+### One fork to rule them all
+
+When LibreOffice was forked from OpenOffice we saw yet another instance of the fork not only improving on the original, but vastly surpassing it. LibreOffice was an instant success. Every Linux distribution that once shipped with OpenOffice migrated to the new kid on the block. LibreOffice burst out of the starting gate and immediately hit its stride. Updates came at an almost breakneck speed and the improvements were plenty and important.
+
+After a while, OpenOffice became an afterthought for the open source community. This, of course, was exacerbated when Oracle decided to discontinue the project in 2011 and donated the code to the Apache Project. By this point OpenOffice was struggling to move forward and that brings us to now. A burgeoning LibreOffice and a suffering, stuttering OpenOffice.
+
+But I say there is a light at the end of this rather dim tunnel.
+
+### Unfork them
+
+This may sound crazy, but I think it's time LibreOffice and OpenOffice became one again. Yes, I know there are probably political issues and egos at stake, but I believe the two would be better served as one. The benefits of this merger would be many. Off the top of my head:
+
+- Bring the MS Office filters together: OpenOffice has a strong track record of better importing certain files from MS Office (whereas LibreOffice has been known to be improving, but spotty).
+- More developers for LibreOffice: Although OpenOffice wouldn't bring with it a battalion of developers, it would certainly add to the mix.
+- End the confusion: Many users assume OpenOffice and LibreOffice are the same thing. Some don't even know that LibreOffice exists. This would end that confusion.
+- Combine their numbers: Separate, OpenOffice and LibreOffice have impressive usage numbers. Together, they would be a force.
+### A golden opportunity
+
+The possible loss of OpenOffice could actually wind up being a golden opportunity for open source office suites in general. Why? I would like to suggest something that I believe has been necessary for a while now. If OpenOffice and LibreOffice were to gather their forces, diff their code, and merge, they could then do some much-needed retooling of not just the internal works of the whole, but also of the interface.
+
+Let's face it, the LibreOffice and (by extension) OpenOffice UIs are both way out of date. When I install LibreOffice 5.2.1.2 the tool bar is an absolute disaster (Figure A).
+
+### Figure A
+
+![](http://tr2.cbsistatic.com/hub/i/2016/09/14/cc5250df-48cd-40e3-a083-34250511ffab/c5ac8eb1e2cb12224690a6a3525999f0/openofficea.jpg)
+
+#### The LibreOffice default toolbar setup.
+
+As much as I support and respect (and use daily) LibreOffice, it has become all too clear the interface needs a complete overhaul. What we're dealing with now is a throwback to the late 90s/early 2000s and it has to go. When a new user opens up LibreOffice for the first time, they are inundated with buttons, icons, and toolbars. Ubuntu Unity helped this out with the Head up Display (HUD), but that did nothing for other desktops and distributions. Sure, the enlightened user has no problem knowing what to look for and where it is (or to even customize the toolbars to reflect their specific needs), but for a new or average user, that interface is a nightmare. Now would be the perfect time for this change. Bring in the last vestiges of the OpenOffice developers and have them join the fight for an improved interface. With the combination of the additional import filters from OpenOffice and a modern interface, LibreOffice could finally make some serious noise on both the home and business desktops.
+
+### Will this actually happen?
+
+This needs to happen. Will it? I have no idea. But even if the powers that be decide the UI isn't in need of retooling (which would be a mistake), bringing OpenOffice into the fold would still be a big step forward. The merging of the two efforts would bring about a stronger focus on development, easier marketing, and far less confusion by the public at large.
+
+I realize this might seem a bit antithetical to the very heart and spirit of open source, but merging LibreOffice and OpenOffice would combine the strengths of the two constituent pieces and possibly jettison the weaknesses.
+
+From my perspective, that's a win-win.
+
+--------------------------------------------------------------------------------
+
+via: http://www.techrepublic.com/article/its-time-to-make-libreoffice-and-openoffice-one-again/
+
+作者:[Jack Wallen ][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.techrepublic.com/search/?a=jack%2Bwallen
diff --git a/sources/tech/20160914 NitroShare – Easily Share Files Between Multiple Operating Systems on Local Network.md b/sources/tech/20160914 NitroShare – Easily Share Files Between Multiple Operating Systems on Local Network.md
new file mode 100644
index 0000000000..0210a50789
--- /dev/null
+++ b/sources/tech/20160914 NitroShare – Easily Share Files Between Multiple Operating Systems on Local Network.md	
@@ -0,0 +1,98 @@
+NitroShare – Easily Share Files Between Multiple Operating Systems on Local Network
+====
+
+One of the most important uses of a network is for file sharing purposes. There are multiple ways Linux and Windows, Mac OS X users on a network can now share files with each other and in this post, we shall cover Nitroshare, a cross-platform, open-source and easy-to-use application for sharing files across a local network.
+
+Nitroshare tremendously simplifies file sharing on a local network, once installed, it integrates with the operating system seamlessly. On Ubuntu, simply open it from the applications indicator, and on Windows, check it in the system tray.
+
+Additionally, it automatically detects every other device on a network that has Nitroshare installed thereby enabling a user to easily transfer files from one machine to another by selecting which device to transfer to.
+
+The following are the illustrious features of Nitroshare:
+
+- Cross-platform, runs on Linux, Windows and Mac OS X
+- Easy to setup, no configurations required
+- It’s simple to use
+- Supports automatic discovery of devices running Nitroshare on local network
+- Supports optional TSL encryption for security
+- Works at high speeds on fast networks
+- Supports transfer of files and directories (folders on Windows)
+- Supports desktop notifications about sent files, connected devices and more
+
+The latest version of Nitroshare was developed using Qt 5, it comes with some great improvements such as:
+
+- Polished user interfaces
+- Simplified device discovery process
+- Removal of file size limitation from other versions
+- Configuration wizard has also been removed to make it easy to use
+
+### How To Install Nitroshare on Linux Systems
+
+
+NitroShare is developed to run on a wide variety of modern Linux distributions and desktop environments.
+
+#### On Debian Sid and Ubuntu 16.04+
+
+NitroShare is included in the Debian and Ubuntu software repositories and can be easily installed with the following command.
+
+```
+$ sudo apt-get install nitroshare
+```
+
+But the available version might be out of date, however, to install the latest version of Nitroshare, issue the command below to add the PPA for the latest packages:
+
+```
+$ sudo apt-add-repository ppa:george-edison55/nitroshare
+$ sudo apt-get update
+$ sudo apt-get install nitroshare
+```
+
+#### On Fedora 24-23
+
+Recently, NitroShare has been included to Fedora repositories and can be installed with the following command:
+
+```
+$ sudo dnf install nitroshare
+```
+
+#### On Arch Linux
+
+For Arch Linux, NitroShare packages are available from the AUR and can be built/installed with the following commands:
+
+```
+# wget https://aur.archlinux.org/cgit/aur.git/snapshot/nitroshare.tar.gz
+# tar xf nitroshare.tar.gz
+# cd nitroshare
+# makepkg -sri
+```
+
+### How to Use NitroShare on Linux
+
+Note: As I had already mentioned earlier on, all other machines that you wish to share files with on the local network must have Nitroshare installed and running.
+
+After successfully installing it, search for Nitroshare in the system dash or system menu and launch it.
+
+![](http://www.tecmint.com/wp-content/uploads/2016/09/NitroShare-Send-Files.png)
+
+After selecting the files, click on “Open” to proceed to choosing the destination device as in the image below. Select the device and click “Ok” that is if you have any devices running Nitroshare on the local network.
+
+![](http://www.tecmint.com/wp-content/uploads/2016/09/NitroShare-Local-Devices.png)
+
+![](http://www.tecmint.com/wp-content/uploads/2016/09/NitroShare-File-Transfer-Progress.png)
+
+From the NitroShare settings – General tab, you can add the device name, set default downloads location and in Advance settings you can set port, buffer, timeout, etc. only if you needed.
+
+Homepage: <https://nitroshare.net/index.html>
+
+That’s it for now, if you have any issues regarding Nitroshare, you can share with us using our comment section below. You can as well make suggestions and let us know of any wonderful, cross-platform file sharing applications out there that we probably have no idea about and always remember to stay connected to Tecmint.
+
+--------------------------------------------------------------------------------
+
+via: http://linoxide.com/firewall/pfsense-setup-basic-configuration/
+
+作者:[Aaron Kili][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
diff --git a/sources/tech/20160914 Server Monitoring with Shinken on Ubuntu 16.04.md b/sources/tech/20160914 Server Monitoring with Shinken on Ubuntu 16.04.md
new file mode 100644
index 0000000000..9580189e1e
--- /dev/null
+++ b/sources/tech/20160914 Server Monitoring with Shinken on Ubuntu 16.04.md	
@@ -0,0 +1,364 @@
+Server Monitoring with Shinken on Ubuntu 16.04
+=====
+
+
+Shinken is an open source computer and network monitoring framework written in python and compatible with Nagios. Shinken can be used on all operating systems that can run python applications like Linux, Unix, and Windows. Shinken was written by Jean Gabes as proof of concept for a new Nagios architecture, but it was turned down by the Nagios author and became an independent network and system monitoring tool that stays compatible with Nagios.
+
+In this tutorial, I will show you how to install Shinken from source and add a Linux host to the monitoring system. I will use Ubuntu 16.04 Xenial Xerus as the operating system for the Shinken server and monitored host.
+
+### Step 1 - Install Shinken Server
+
+Shinken is a python framework, we can install it with pip or install it from source. In this step, we will install Shinken from source.
+
+There are some tasks that have to be completed before we start installing Shinken.
+
+Install some new python packages and create Linux user with the name "shinken":
+
+```
+sudo apt-get install python-setuptools python-pip python-pycurl
+useradd -m -s /bin/bash shinken
+```
+
+Download the Shinken source from GitHub repository:
+
+```
+git clone https://github.com/naparuba/shinken.git
+cd shinken/
+```
+
+Then install Shinken with the command below:
+
+```
+git checkout 2.4.3
+python setup.py install
+```
+
+Next, for better results, we need to install 'python-cherrypy3' from the ubuntu repository:
+
+```
+sudo apt-get install python-cherrypy3
+```
+
+Now Shinken is installed, next we add Shinken to start at boot time and start it:
+
+```
+update-rc.d shinken defaults
+systemctl start shinken
+```
+
+### Step 2 - Install Shinken Webui2
+
+Webui2 is the Shinken web interface available from shinken.io. The easiest way to install Sshinken webui2 is by using the shinken CLI command (which has to be executed as shinken user).
+
+Login to the shinken user:
+
+```
+su - shinken
+```
+
+Initialize the shinken configuration file - The command will create a new configuration .shinken.ini:
+
+```
+shinken --init
+```
+
+And install webui2 with this shinken CLI command:
+
+```
+shinken install webui2
+```
+
+![](https://www.howtoforge.com/images/server-monitoring-with-shinken-on-ubuntu-16-04/6.png)
+
+Webui2 is installed, but we need to install MongoDB and another python package with pip. Run command below as root:
+
+```
+sudo apt-get install mongodb
+pip install pymongo>=3.0.3 requests arrow bottle==0.12.8
+```
+
+Next, go to the shinken directory and add the new webui2 module by editing the 'broker-master.cfg' file:
+
+```
+cd /etc/shinken/brokers/
+vim broker-master.cfg
+```
+
+Add a new option inside module on line 40:
+
+```
+modules     webui2
+```
+
+Save the file and exit the editor.
+
+Now go to the contacts directory and edit the file 'admin.cfg' for the admin configuration.
+
+```
+cd /etc/shinken/contacts/
+vim admin.cfg
+```
+
+Change the values shown below:
+
+```
+contact_name    admin       # Username 'admin'
+password        yourpass    # Pass 'mypass'
+```
+
+Save and exit.
+
+### Step 3 - Install Nagios-plugins and Shinken Packages
+
+In this step, we will install Nagios-plugins and some Perl module. Then install additional shinken packages from shinken.io to perform the monitoring.
+
+Install Nagios-plugins and cpanminus which is required for building and installing the Perl modules:
+
+```
+sudo apt-get install nagios-plugins* cpanminus
+```
+
+Install these Perl modules with the cpanm command:
+
+```
+cpanm Net::SNMP
+cpanm Time::HiRes
+cpanm DBI
+```
+
+Now create new link for utils.pm file to shinken the directory and create a new directory for Log_File_Health:
+
+```
+chmod u+s /usr/lib/nagios/plugins/check_icmp
+ln -s /usr/lib/nagios/plugins/utils.pm /var/lib/shinken/libexec/
+mkdir -p /var/log/rhosts/
+touch /var/log/rhosts/remote-hosts.log
+```
+
+Next, install the shinken packages ssh and linux-snmp for monitoring SSH and SNMP sources from shinken.io:
+
+```
+su - shinken
+shinken install ssh
+shinken install linux-snmp
+```
+
+### Step 4 - Add a New Linux Host/host-one
+
+We will add a new Linux host that shall be monitored by using an Ubuntu 16.04 server with IP address 192.168.1.121 and hostname 'host-one'.
+
+Connect to the Linux host-one:
+
+```
+ssh host1@192.168.1.121
+```
+
+Install the snmp and snmpd packages from the Ubuntu repository:
+
+```
+sudo apt-get install snmp snmpd
+```
+
+Next, edit the configuration file 'snmpd.conf' with vim:
+
+```
+vim /etc/snmp/snmpd.conf
+```
+
+Comment line 15 and uncomment line 17:
+
+```
+#agentAddress  udp:127.0.0.1:161
+agentAddress udp:161,udp6:[::1]:161
+```
+
+Comment line 51 and 53, then add new line configuration below:
+
+```
+#rocommunity mypass  default    -V systemonly
+#rocommunity6 mypass  default   -V systemonly
+
+rocommunity mypass
+```
+
+Save and exit.
+
+Now start the snmpd service with the systemctl command:
+
+```
+systemctl start snmpd
+```
+
+Go to the shinken server and define the new host by creating a new file in the 'hosts' directory.
+
+```
+cd /etc/shinken/hosts/
+vim host-one.cfg
+```
+
+Paste configuration below:
+
+```
+define host{
+        use                 generic-host,linux-snmp,ssh
+        contact_groups      admins
+        host_name           host-one
+        address             192.168.1.121
+        _SNMPCOMMUNITY      mypass        # SNMP Pass Config on snmpd.conf
+    }
+```
+
+Save and exit.
+
+Edit the SNMP configuration on the Shinken server:
+
+```
+vim /etc/shinken/resource.d/snmp.cfg
+```
+
+Change 'public' to 'mypass' - must be the same password that you used in the snmpd configuration file on the client host-one.
+
+```
+$SNMPCOMMUNITYREAD$=mypass
+```
+
+Save and exit.
+
+Now reboot both servers - Shinken server and the monitored Linux host:
+
+```
+reboot
+```
+
+The new Linux host has been added successfully to the Shinken server.
+
+### Step 5 - Access Shinken Webui2
+
+Visit the Shinken webui2 on port 7677 (replace the IP in the URL with your IP):
+
+```
+http://192.168.1.120:7767
+```
+
+Log in with user admin and your password (the one that you have set in the admin.cfg configuration file).
+
+![](https://www.howtoforge.com/images/server-monitoring-with-shinken-on-ubuntu-16-04/1.png)
+
+Shinken Dashboard in Webui2.
+
+![](https://www.howtoforge.com/images/server-monitoring-with-shinken-on-ubuntu-16-04/2.png)
+
+Our 2 servers are monitored with Shinken.
+
+![](https://www.howtoforge.com/images/server-monitoring-with-shinken-on-ubuntu-16-04/3.png)
+
+List all services that are monitored with linux-snmp.
+
+![](https://www.howtoforge.com/images/server-monitoring-with-shinken-on-ubuntu-16-04/4.png)
+
+Status of all hosts and services.
+
+
+![](https://www.howtoforge.com/images/server-monitoring-with-shinken-on-ubuntu-16-04/5.png)
+
+### Step 6 - Common Problems with Shinken
+
+- Problems with the NTP server
+
+When you get this error with NTP.
+
+```
+TimeSync - CRITICAL ( NTP CRITICAL: No response from the NTP server)
+TimeSync - CRITICAL ( NTP CRITICAL: Offset unknown )
+```
+
+To solve this problem, install ntp on all Linux hosts.
+
+```
+sudo apt-get install ntp ntpdate
+```
+
+Edit the ntp configuration:
+
+```
+vim /etc/ntp.conf
+```
+
+Comment all the pools and replace it with:
+
+```
+#pool 0.ubuntu.pool.ntp.org iburst
+#pool 1.ubuntu.pool.ntp.org iburst
+#pool 2.ubuntu.pool.ntp.org iburst
+#pool 3.ubuntu.pool.ntp.org iburst
+
+pool 0.id.pool.ntp.org
+pool 1.asia.pool.ntp.org
+pool 0.asia.pool.ntp.org
+```
+
+Next, add a new line inside restrict:
+
+```
+# Local users may interrogate the ntp server more closely.
+restrict 127.0.0.1
+restrict 192.168.1.120 #shinken server IP address
+restrict ::1
+NOTE: 192.168.1.120 is the Shinken server IP address.
+```
+
+Save and exit.
+
+Start ntp and check the Shinken dashboard:
+
+```
+ntpd
+```
+
+- Problem check_netint.pl Not Found
+
+Download the source from the github repository to the shinken lib directory:
+
+```
+cd /var/lib/shinken/libexec/
+wget https://raw.githubusercontent.com/Sysnove/shinken-plugins/master/check_netint.pl
+chmod +x check_netint.pl
+chown shinken:shinken check_netint.pl
+```
+
+- Problem with NetworkUsage
+
+There is error message:
+
+```
+ERROR : Unknown interface eth\d+
+```
+
+Check your network interface and edit the linux-snmp template.
+
+On my Ubuntu server, the network interface is 'enp0s8', not eth0, so I got this error.
+
+Edit the linux-snmp template packs with vim:
+
+```
+vim /etc/shinken/packs/linux-snmp/templates.cfg
+```
+
+Add the network interface to line 24:
+
+```
+_NET_IFACES         eth\d+|em\d+|enp0s8
+```
+
+--------------------------------------------------------------------------------
+
+via: https://www.howtoforge.com/tutorial/server-monitoring-with-shinken-on-ubuntu-16-04/
+
+作者:[Muhammad Arul][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.howtoforge.com/tutorial/server-monitoring-with-shinken-on-ubuntu-16-04/
+Save and exit.
diff --git a/sources/tech/20160915 GOOGLER: NOW YOU CAN GOOGLE FROM LINUX TERMINAL.md b/sources/tech/20160915 GOOGLER: NOW YOU CAN GOOGLE FROM LINUX TERMINAL.md
new file mode 100644
index 0000000000..29220291bf
--- /dev/null
+++ b/sources/tech/20160915 GOOGLER: NOW YOU CAN GOOGLE FROM LINUX TERMINAL.md	
@@ -0,0 +1,129 @@
+GOOGLER: NOW YOU CAN GOOGLE FROM LINUX TERMINAL!
+====
+
+![](https://itsfoss.com/wp-content/uploads/2016/09/google-from-linux-terminal.jpg)
+
+A quick question: What do you do every day? Of course, a lot of things. But I can tell one thing, you search on Google almost every day (if not every day). Am I right?
+
+Now, if you are a Linux user (which I’m guessing you are) here’s another question: wouldn’t it be nice if you can Google without even leaving the terminal? Without even firing up a Browser window?
+
+If you are a *nix enthusiast and also one of those people who just love the view of the terminal, I know your answer is – Yes. And I think, the rest of you will also like the nifty little tool I’m going to introduce today. It’s called Googler!
+
+### GOOGLER: GOOGLE IN YOUR LINUX TERMINAL
+
+Googler is a straightforward command-line utility for Google-ing right from your terminal window. Googler mainly supports three types of Google Searches:
+
+- Google Search: Simple Google searching, equivalent to searching on Google homepage.
+- Google News Search: Google searching for News, equivalent to searching on Google News.
+- Google Site Search: Google searching for results from a specific site.
+
+Googler shows the search results with the title, URL and page excerpt. The search results can be opened directly in the browser with only a couple of keystrokes.
+
+![](https://itsfoss.com/wp-content/uploads/2016/09/googler-1.png)
+
+### INSTALLATION ON UBUNTU
+
+Let’s go through the installation process first.
+
+At first make sure you have python version 3.3 or later using this command:
+
+```
+python3 --version
+```
+
+If not, upgrade it. Googler requires python 3.3+ for running.
+
+Though Googler is yet not available through package repository on Ubuntu, we can easily install it from the GitHub repository. All we have to do is run the following commands:
+
+```
+cd /tmp
+git clone https://github.com/jarun/googler.git
+cd googler
+sudo make install
+cd auto-completion/bash/
+sudo cp googler-completion.bash /etc/bash_completion.d/
+```
+
+And that’s it. Googler is installed along with command autocompletion feature.
+
+### FEATURES & BASIC USAGE
+
+If we go through all its features, Googler is actually quite powerful a tool. Some of the main features are:
+
+Interactive Interface: Run the following command in terminal:
+
+```
+googler
+```
+
+The interactive interface will be opened. The developer of Googler, Arun [Prakash Jana][1] calls it the omniprompt. You can enter ? for available commands on omniprompt.
+
+![](https://itsfoss.com/wp-content/uploads/2016/09/googler-2.png)
+
+From the omniprompt, enter any search phrases to initiate the search. You can then enter n or p to navigate next or previous page of search results.
+
+To open any search result in a browser window, just enter the index number of that result. Or you can open the search page itself by entering o .
+
+- News Search: If you want to search News, start googler with the N optional argument:
+
+```
+googler -N
+```
+
+The subsequent omniprompt will fetch results from Google News.
+
+- Site Search: If you want to search pages from a specific site, run googler with w {domain} argument:
+
+```
+googler -w itsfoss.com
+```
+
+The subsequent omniprompt with fetch results only from It’s FOSS blog!
+
+- Manual Page: Run the following command for Googler manual page equipped with various examples:
+
+```
+man googler
+```
+
+- Google country/domain specific search:
+
+```
+googler -c in "hello world"
+```
+
+The above example command will open search results from Google’s Indian domain (in for India).
+
+- Filter search results by duration and language preference.
+- Google search keywords support, such as: site:example.com or filetype:pdf etc.
+- HTTPS proxy support.
+- Shell commands autocomplete.
+- Disable automatic spelling correction.
+
+There are much more. You can twist Googler to suit your needs.
+
+Googler can also be integrated with a text-based browser ( like – [elinks][2], [links][3], [lynx][4], w3m etc.), so that you wouldn’t even need to leave the terminal for browsing web pages. The instructions can be found on the [GitHub project page of Googler][5].
+
+If you want a graphical demonstration of Googler’s various features, feel free to check the terminal recording attached to the GitHub project page : [jarun/googler v2.7 quick demo][6].
+
+### THOUGHTS ON GOOGLER?
+
+Though Googler might not feel necessary or desired to everybody, for someone who doesn’t want to open the browser just for searching on google or simply want to spend as much as time possible on the terminal window, it is a great tool indeed. What do you think?
+
+--------------------------------------------------------------------------------
+
+via: http://linoxide.com/firewall/pfsense-setup-basic-configuration/
+
+作者:[Munif Tanjim][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://itsfoss.com/author/munif/
+[1]: https://github.com/jarun
+[2]: http://elinks.or.cz/
+[3]: http://links.twibright.com/
+[4]: http://lynx.browser.org/
+[5]: https://github.com/jarun/googler#faq
+[6]: https://asciinema.org/a/85019
diff --git a/sources/tech/20160915 Insomnia 3.0 Is a Slick Desktop REST Client for Linux.md b/sources/tech/20160915 Insomnia 3.0 Is a Slick Desktop REST Client for Linux.md
new file mode 100644
index 0000000000..5460c57e8d
--- /dev/null
+++ b/sources/tech/20160915 Insomnia 3.0 Is a Slick Desktop REST Client for Linux.md	
@@ -0,0 +1,53 @@
+translating by Bestony
+
+Insomnia 3.0 Is a Slick Desktop REST Client for Linux
+=====
+
+![](http://www.omgubuntu.co.uk/wp-content/uploads/2016/09/insomnia-app-screenshot.png)
+
+Looking for a free REST client for the Linux desktop? Don’t lose sleep: get [Insomnia][1].
+
+The app is cross-platform and works on Linux, macOS and Windows. Its developer, Gregory Schier, told us that he created the app “to help developers communicate with [REST APIs][2].”
+
+He also told that Insomnia already has around 10,000 active users — 9% of which are on Linux.
+
+“So far, the feedback from Linux users has been very positive because similar applications (not nice ones anyway) aren’t usually available for Linux.”
+
+Insomnia aims to ‘speed up your API testing workflow’, by letting you organise, run and debug HTTP requests through a cleanly designed interface.
+
+The app also includes advanced features like cookie management, global environments, SSL validation, and code snippet generation.
+
+As I am not a developer I can’t evaluate this app first-hand, nor tell you why it rocks or highlight any major feature deficiencies.
+
+But I thought I’d bring the app to your attention and let you decide for yourself. If you’ve been hunting for a slickly designed GUI alternative to command-line tools like HTTPie, it might be well worth giving it a whirl.
+
+### Download Insomnia 3.0 for Linux
+
+Insomnia 3.0 (not to be confused with Insomnia v2.0 which is only available on Chrome) is available to download for Windows, macOS and Linux.
+
+[Download Insomnia 3.0][4]
+
+An installer is available for Ubuntu 14.04 LTS and up, as is a cross-distro AppImage:
+
+[Download Insomnia 3.0 (.AppImage)][5]
+
+If you want to keep pace with development of the app you can follow [Insomnia on Twitter][6].
+
+
+--------------------------------------------------------------------------------
+
+via: http://www.omgubuntu.co.uk/2016/09/insomnia-3-is-free-rest-client-for-linux?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+d0od+%28OMG%21+Ubuntu%21%29
+
+作者:[JOEY-ELIJAH SNEDDON ][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://plus.google.com/117485690627814051450/?rel=author
+[1]: http://insomnia.rest/
+[2]: https://en.wikipedia.org/wiki/Representational_state_transfer
+[3]: https://github.com/jkbrzt/httpie
+[4]: https://insomnia.rest/download/
+[5]: https://builds.insomnia.rest/downloads/linux/latest
+[6]: https://twitter.com/GetInsomnia
diff --git a/sources/tech/20160915 Using Ansible to Provision Vagrant Boxes.md b/sources/tech/20160915 Using Ansible to Provision Vagrant Boxes.md
new file mode 100644
index 0000000000..57003390d8
--- /dev/null
+++ b/sources/tech/20160915 Using Ansible to Provision Vagrant Boxes.md	
@@ -0,0 +1,159 @@
+Using Ansible to Provision Vagrant Boxes
+====
+
+![](https://i1.wp.com/cdn.fedoramagazine.org/wp-content/uploads/2016/08/vagrant-plus-ansible.jpg?w=1352&ssl=1)
+
+Ansible is a great tool for system administrators who want to automate system administration tasks. From configuration management to provisioning and managing containers for application deployments, Ansible [makes it easy][1]. The lightweight module based architecture is ideal for system administration. One advantage is that when the node is not being managed by Ansible, no resources are used.
+
+This article covers how to use Ansible to provision Vagrant boxes. A [Vagrant box][2] in simple terms is a virtual machine prepackaged with tools required to run the development environment. You can use these boxes to distribute the development environment used by other team members for project work. Using Ansible, you can automate provisioning the Vagrant boxes with your development packages.
+
+This tutorial uses Fedora 24 as the host system and [CentOS][3] 7 as the Vagrant box.
+
+### Setting up prerequisites
+
+To configure Vagrant boxes using Ansible, you’ll need a few things setup. This tutorial requires you to install Ansible and Vagrant on the host machine. On your host machine, execute the following command to install the tools:
+
+```
+sudo dnf install ansible vagrant vagrant-libvirt
+```
+
+The above command installs both Ansible and Vagrant on your host system, along with Vagrant’s libvirt provider. Vagrant doesn’t provide functionality to host your virtual machine guests (VMs). Rather, it depends on third party providers such as libvirt, VirtualBox, VMWare, etc. to host the VMs. This provider works directly with libvirt and KVM on your Fedora system.
+
+Next, make sure your user is in the wheel group. This special group allows you to run system administration commands. If you created your user as an administrator, such as during installation, you’ll have this group membership. Run the following command:
+
+```
+id | grep wheel
+```
+
+If you see output, your user is in the group, and you can move on to the next section. If not, run the following command. You’ll need to provide the password for the root account. Substitute your user name for the text <username>:
+
+```
+su -c 'usermod -a -G wheel <username>'
+```
+
+Then you will need to logout, and log back in, to inherit the group membership properly.
+
+Now it’s time to create your first Vagrant box, which you’ll then configure using Ansible.
+
+### Setting up the Vagrant box
+
+Before you use Ansible to provision a box, you must create the box. To start, create a new directory which will store files related to the Vagrant box. To create this directory and make it the current working directory, issue the following command:
+
+```
+mkdir -p ~/lampbox && cd ~/lampbox
+```
+
+Before you create the box, you should understand the goal. This box is a simple example that runs CentOS 7 as its base system, along with the Apache web server, MariaDB (the popular open source database server from the original developers of MySQL) and PHP.
+
+To initialize the Vagrant box, use the vagrant init command:
+
+```
+vagrant init centos/7
+```
+
+This command initializes the Vagrant box and creates a file named Vagrantfile, with some pre-configured variables. Open this file so you can modify it. The following line lists the base box used by this configuration.
+
+```
+config.vm.box = "centos/7"
+```
+
+Now setup port forwarding, so after you finish setup and the Vagrant box is running, you can test the server. To setup port forwarding, add the following line just before the end statement in Vagrantfile:
+
+```
+config.vm.network "forwarded_port", guest: 80, host: 8080
+```
+
+This option maps port 80 of the Vagrant Box to port 8080 of the host machine.
+
+The next step is to set Ansible as our provisioning provider for the Vagrant Box. Add the following lines before the end statement in your Vagrantfile to set Ansible as the provisioning provider:
+
+```
+config.vm.provision :ansible do |ansible|
+  ansible.playbook = "lamp.yml"
+end
+```
+
+(You must add all three lines before the final end statement.) Notice the statement ansible.playbook = “lamp.yml”. This statement defines the name of the playbook used to provision the box.
+
+### Creating the Ansible playbook
+
+In Ansible, playbooks describe a policy to be enforced on your remote nodes. Put another way, playbooks manage configurations and deployments on remote nodes. Technically speaking, a playbook is a YAML file in which you write tasks to perform on remote nodes. In this tutorial, you’ll create a playbook named lamp.yml to provision the box.
+
+To make the playbook, create a file named lamp.yml in the same directory where your Vagrantfile is located and add the following lines to it:
+
+```
+---
+- hosts: all
+  become: yes
+  become_user: root
+  tasks:
+  - name: Install Apache
+    yum: name=httpd state=latest
+  - name: Install MariaDB
+    yum: name=mariadb-server state=latest
+  - name: Install PHP5
+    yum: name=php state=latest
+  - name: Start the Apache server
+    service: name=httpd state=started
+  - name: Install firewalld
+    yum: name=firewalld state=latest
+  - name: Start firewalld
+    service: name=firewalld state=started
+  - name: Open firewall
+    command: firewall-cmd --add-service=http --permanent
+```
+
+An explanation of each line of lamp.yml follows.
+
+- hosts: all specifies the playbook should run over every host defined in the Ansible configuration. Since no hosts are configured hosts yet, the playbook will run on localhost.
+- sudo: true states the tasks should be performed with root privileges.
+- tasks: specifies the tasks to perform when the playbook runs. Under the tasks section:
+- - name: … provides a descriptive name to the task
+- - yum: … specifies the task should be executed by the yum module. The options name and state are key=value pairs for use by the yum module.
+
+When this playbook executes, it installs the latest versions of the Apache (httpd) web server, MariaDB, and PHP. Then it installs and starts firewalld, and opens a port for the Apache server. You’re now done writing the playbook for the box. Now it’s time to provision it.
+
+### Provisioning the box
+
+A few final steps remain before using the Vagrant Box provisioned using Ansible. To run this provisioning, execute the following command:
+
+```
+vagrant up --provider libvirt
+```
+
+The above command starts the Vagrant box, downloads the base box image to the host system if not already present, and then runs the playbook lamp.yml to provision.
+
+If everything works fine, the output looks somewhat similar to this example:
+
+![](https://i1.wp.com/cdn.fedoramagazine.org/wp-content/uploads/2016/08/vagrant-ansible-playbook-run.png?w=574&ssl=1)
+
+This output shows that the box has been provisioned. Now check whether the server is accessible. To confirm, open your web browser on the host machine and point it to the address http://localhost:8080. Remember, port 8080 of the local host is forwarded to port 80 of the Vagrant box. You should be greeted with the Apache welcome page like the one shown below:
+
+![](https://i0.wp.com/cdn.fedoramagazine.org/wp-content/uploads/2016/08/vagrant-ansible-apache-up.png?w=1004&ssl=1)
+
+To make changes to your Vagrant box, first edit the Ansible playbook lamp.yml. You can find plentiful documentation on Ansible at [its official website][4]. Then run the following command to re-provision the box:
+
+```
+vagrant provision
+```
+
+### Conclusion
+
+You’ve now seen how to use Ansible to provision Vagrant boxes. This was a basic example, but you can use these tools for many other use cases. For example, you can deploy complete applications along with up-to-date version of required tools. Be creative as you use Ansible to provision your remote nodes or containers.
+
+
+--------------------------------------------------------------------------------
+
+via: https://fedoramagazine.org/using-ansible-provision-vagrant-boxes/
+
+作者:[Saurabh Badhwar][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://h4xr.id.fedoraproject.org/
+[1]: https://ansible.com/
+[2]: https://www.vagrantup.com/
+[3]: https://centos.org/
+[4]: http://docs.ansible.com/ansible/index.html
diff --git a/sources/tech/20160917 How to Install Latest XFCE Desktop in Ubuntu 16.04 and Fedora 22-24.md b/sources/tech/20160917 How to Install Latest XFCE Desktop in Ubuntu 16.04 and Fedora 22-24.md
new file mode 100644
index 0000000000..516873c9ea
--- /dev/null
+++ b/sources/tech/20160917 How to Install Latest XFCE Desktop in Ubuntu 16.04 and Fedora 22-24.md	
@@ -0,0 +1,82 @@
+translating by ucasFL
+
+How to Install Latest XFCE Desktop in Ubuntu 16.04 and Fedora 22-24
+====
+
+Xfce is a modern, open source and lightweight desktop environment for Linux systems. It also works well on many other Unix-like systems such as Mac OS X, Solaris, *BSD plus several others. It is fast and also user friendly with a simple and elegant user interface.
+
+Installing a desktop environment on servers can sometimes prove helpful, as certain applications may require a desktop interface for efficient and reliable administration and one of the remarkable properties of Xfce is its low system resources utilization such as low RAM consumption, thereby making it a recommended desktop environment for servers if need be.
+
+### XFCE Desktop Features
+
+Additionally, some of its noteworthy components and features are listed below:
+
+- Xfwm windows manager
+- Thunar file manager
+- User session manger to deal with logins, power management and beyond
+- Desktop manager for setting background image, desktop icons and many more
+- An application manager
+- It’s highly pluggable as well plus several other minor features
+
+The latest stable release of this desktop is Xfce 4.12, all its features and changes from previous versions are listed here.
+
+#### Install Xfce Desktop on Ubuntu 16.04
+
+Linux distributions such as Xubuntu, Manjaro, OpenSUSE, Fedora Xfce Spin, Zenwalk and many others provide their own Xfce desktop packages, however, you can install the latest version as follows.
+
+```
+$ sudo apt update
+$ sudo apt install xfce4 
+```
+
+Wait for the installation process to complete, then logout out of your current session or you can possibly restart your system as well. At the login interface, choose Xfce desktop and login as in the screen shot below:
+
+![](http://www.tecmint.com/wp-content/uploads/2016/09/Select-Xfce-Desktop-at-Login.png)
+
+![](http://www.tecmint.com/wp-content/uploads/2016/09/XFCE-Desktop.png)
+
+#### Install Xfce Desktop in Fedora 22-24
+
+If you have an existing Fedora distribution and wanted to install xfce desktop, you can use yum or dnf to install it as shown.
+
+```
+-------------------- On Fedora 22 --------------------
+# yum install @xfce
+-------------------- On Fedora 23-24 --------------------
+# dnf install @xfce-desktop-environment
+```
+
+After installing Xfce, you can choose the xfce login from the Session menu or reboot the system.
+
+![](http://www.tecmint.com/wp-content/uploads/2016/09/Select-Xfce-Desktop-at-Fedora-Login.png)
+
+![](http://www.tecmint.com/wp-content/uploads/2016/09/Install-Xfce-Desktop-in-Fedora.png)
+
+If you don’t want Xfce desktop on your system anymore, use the command below to uninstall it:
+
+```
+-------------------- On Ubuntu 16.04 -------------------- 
+$ sudo apt purge xfce4
+$ sudo apt autoremove
+-------------------- On Fedora 22 -------------------- 
+# yum remove @xfce
+-------------------- On Fedora 23-24 --------------------
+# dnf remove @xfce-desktop-environment
+```
+
+In this simple how-to guide, we walked through the steps for installation of latest version of Xfce desktop, which I believe were easy to follow. If all went well, you can enjoy using xfce, as one of the [best desktop environments for Linux systems][1].
+
+However, to get back to us, you can use the feedback section below and remember to always stay connected to Tecmint.
+
+--------------------------------------------------------------------------------
+
+via: http://linoxide.com/firewall/pfsense-setup-basic-configuration/
+
+作者:[Aaron Kili ][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
+[1]: http://www.tecmint.com/best-linux-desktop-environments/
diff --git a/sources/tech/LFCS/Part 13 - How to Configure and Troubleshoot Grand Unified Bootloader (GRUB).md b/sources/tech/LFCS/Part 13 - How to Configure and Troubleshoot Grand Unified Bootloader (GRUB).md
deleted file mode 100644
index cf24c51b58..0000000000
--- a/sources/tech/LFCS/Part 13 - How to Configure and Troubleshoot Grand Unified Bootloader (GRUB).md	
+++ /dev/null
@@ -1,185 +0,0 @@
-Part 13 - LFCS: How to Configure and Troubleshoot Grand Unified Bootloader (GRUB)
-=====================================================================================
-
-Because of the changes in the LFCS exam requirements effective Feb. 2, 2016, we are adding the necessary topics to the [LFCS series][1] published here. To prepare for this exam, your are highly encouraged to use the [LFCE series][2] as well.
-
-![](http://www.tecmint.com/wp-content/uploads/2016/03/Configure-Troubleshoot-Grub-Boot-Loader.png)
->LFCS: Configure and Troubleshoot Grub Boot Loader – Part 13
-
-In this article we will introduce you to GRUB and explain why a boot loader is necessary, and how it adds versatility to the system.
-
-The [Linux boot process][3] from the time you press the power button of your computer until you get a fully-functional system follows this high-level sequence:
-
-* 1. A process known as **POST** (**Power-On Self Test**) performs an overall check on the hardware components of your computer.
-* 2. When **POST** completes, it passes the control over to the boot loader, which in turn loads the Linux kernel in memory (along with **initramfs**) and executes it. The most used boot loader in Linux is the **GRand Unified Boot loader**, or **GRUB** for short.
-* 3. The kernel checks and accesses the hardware, and then runs the initial process (mostly known by its generic name “**init**”) which in turn completes the system boot by starting services.
-
-In Part 7 of this series (“[SysVinit, Upstart, and Systemd][4]”) we introduced the [service management systems and tools][5] used by modern Linux distributions. You may want to review that article before proceeding further.
-
-### Introducing GRUB Boot Loader
-
-Two major **GRUB** versions (**v1** sometimes called **GRUB Legacy** and **v2**) can be found in modern systems, although most distributions use **v2** by default in their latest versions. Only **Red Hat Enterprise Linux 6** and its derivatives still use **v1** today.
-
-Thus, we will focus primarily on the features of **v2** in this guide.
-
-Regardless of the **GRUB** version, a boot loader allows the user to:
-
-* 1). modify the way the system behaves by specifying different kernels to use,
-* 2). choose between alternate operating systems to boot, and
-* 3). add or edit configuration stanzas to change boot options, among other things.
-
-Today, **GRUB** is maintained by the **GNU** project and is well documented in their website. You are encouraged to use the [GNU official documentation][6] while going through this guide.
-
-When the system boots you are presented with the following **GRUB** screen in the main console. Initially, you are prompted to choose between alternate kernels (by default, the system will boot using the latest kernel) and are allowed to enter a **GRUB** command line (with `c`) or edit the boot options (by pressing the `e` key).
-
-![](http://www.tecmint.com/wp-content/uploads/2016/03/GRUB-Boot-Screen.png)
->GRUB Boot Screen
-
-One of the reasons why you would consider booting with an older kernel is a hardware device that used to work properly and has started “acting up” after an upgrade (refer to [this link][7] in the AskUbuntu forums for an example).
-
-The **GRUB v2** configuration is read on boot from `/boot/grub/grub.cfg` or `/boot/grub2/grub.cfg`, whereas `/boot/grub/grub.conf` or `/boot/grub/menu.lst` are used in **v1**. These files are NOT to be edited by hand, but are modified based on the contents of `/etc/default/grub` and the files found inside `/etc/grub.d`.
-
-In a **CentOS 7**, here’s the configuration file that is created when the system is first installed:
-
-```
-GRUB_TIMEOUT=5
-GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)"
-GRUB_DEFAULT=saved
-GRUB_DISABLE_SUBMENU=true
-GRUB_TERMINAL_OUTPUT="console"
-GRUB_CMDLINE_LINUX="vconsole.keymap=la-latin1 rd.lvm.lv=centos_centos7-2/swap crashkernel=auto  vconsole.font=latarcyrheb-sun16 rd.lvm.lv=centos_centos7-2/root rhgb quiet"
-GRUB_DISABLE_RECOVERY="true"
-```
-
-In addition to the online documentation, you can also find the GNU GRUB manual using info as follows:
-
-```
-# info grub
-```
-
-If you’re interested specifically in the options available for /etc/default/grub, you can invoke the configuration section directly:
-
-```
-# info -f grub -n 'Simple configuration'
-```
-
-Using the command above you will find out that `GRUB_TIMEOUT` sets the time between the moment when the initial screen appears and the system automatic booting begins unless interrupted by the user. When this variable is set to `-1`, boot will not be started until the user makes a selection.
-
-When multiple operating systems or kernels are installed in the same machine, `GRUB_DEFAULT` requires an integer value that indicates which OS or kernel entry in the GRUB initial screen should be selected to boot by default. The list of entries can be viewed not only in the splash screen shown above, but also using the following command:
-
-### In CentOS and openSUSE:
-
-```
-# awk -F\' '$1=="menuentry " {print $2}' /boot/grub2/grub.cfg
-```
-
-### In Ubuntu:
-
-```
-# awk -F\' '$1=="menuentry " {print $2}' /boot/grub/grub.cfg
-```
-
-In the example shown in the below image, if we wish to boot with the kernel version **3.10.0-123.el7.x86_64** (4th entry), we need to set `GRUB_DEFAULT` to `3` (entries are internally numbered beginning with zero) as follows:
-
-```
-GRUB_DEFAULT=3
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/03/Boot-System-with-Old-Kernel-Version.png)
->Boot System with Old Kernel Version
-
-One final GRUB configuration variable that is of special interest is `GRUB_CMDLINE_LINUX`, which is used to pass options to the kernel. The options that can be passed through GRUB to the kernel are well documented in the [Kernel Parameters file][8] and in [man 7 bootparam][9].
-
-Current options in my **CentOS 7** server are:
-
-```
-GRUB_CMDLINE_LINUX="vconsole.keymap=la-latin1 rd.lvm.lv=centos_centos7-2/swap crashkernel=auto  vconsole.font=latarcyrheb-sun16 rd.lvm.lv=centos_centos7-2/root rhgb quiet"
-```
-
-Why would you want to modify the default kernel parameters or pass extra options? In simple terms, there may be times when you need to tell the kernel certain hardware parameters that it may not be able to determine on its own, or to override the values that it would detect.
-
-This happened to me not too long ago when I tried **Vector Linux**, a derivative of **Slackware**, on my 10-year old laptop. After installation it did not detect the right settings for my video card so I had to modify the kernel options passed through GRUB in order to make it work.
-
-Another example is when you need to bring the system to single-user mode to perform maintenance tasks. You can do this by appending the word single to `GRUB_CMDLINE_LINUX` and rebooting:
-
-```
-GRUB_CMDLINE_LINUX="vconsole.keymap=la-latin1 rd.lvm.lv=centos_centos7-2/swap crashkernel=auto  vconsole.font=latarcyrheb-sun16 rd.lvm.lv=centos_centos7-2/root rhgb quiet single"
-```
-
-After editing `/etc/defalt/grub`, you will need to run `update-grub` (Ubuntu) or `grub2-mkconfig -o /boot/grub2/grub.cfg` (**CentOS** and **openSUSE**) afterwards to update `grub.cfg` (otherwise, changes will be lost upon boot).
-
-This command will process the boot configuration files mentioned earlier to update `grub.cfg`. This method ensures changes are permanent, while options passed through GRUB at boot time will only last during the current session.
-
-### Fixing Linux GRUB Issues
-
-If you install a second operating system or if your GRUB configuration file gets corrupted due to human error, there are ways you can get your system back on its feet and be able to boot again.
-
-In the initial screen, press `c` to get a GRUB command line (remember that you can also press `e` to edit the default boot options), and use help to bring the available commands in the GRUB prompt:
-
-![](http://www.tecmint.com/wp-content/uploads/2016/03/Fix-Grub-Issues-in-Linux.png)
->Fix Grub Configuration Issues in Linux
-
-We will focus on **ls**, which will list the installed devices and filesystems, and we will examine what it finds. In the image below we can see that there are 4 hard drives (`hd0` through `hd3`).
-
-Only `hd0` seems to have been partitioned (as evidenced by msdos1 and msdos2, where 1 and 2 are the partition numbers and msdos is the partitioning scheme).
-
-Let’s now examine the first partition on `hd0` (**msdos1**) to see if we can find GRUB there. This approach will allow us to boot Linux and there use other high level tools to repair the configuration file or reinstall GRUB altogether if it is needed:
-
-```
-# ls (hd0,msdos1)/
-```
-
-As we can see in the highlighted area, we found the `grub2` directory in this partition:
-
-![](http://www.tecmint.com/wp-content/uploads/2016/03/Find-Grub-Configuration.png)
->Find Grub Configuration
-
-Once we are sure that GRUB resides in (**hd0,msdos1**), let’s tell GRUB where to find its configuration file and then instruct it to attempt to launch its menu:
-
-```
-set prefix=(hd0,msdos1)/grub2
-set root=(hd0,msdos1)
-insmod normal
-normal
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/03/Find-and-Launch-Grub-Menu.png)
->Find and Launch Grub Menu
-
-Then in the GRUB menu, choose an entry and press **Enter** to boot using it. Once the system has booted you can issue the `grub2-install /dev/sdX` command (change `sdX` with the device you want to install GRUB on). The boot information will then be updated and all related files be restored.
-
-```
-# grub2-install /dev/sdX
-```
-
-Other more complex scenarios are documented, along with their suggested fixes, in the [Ubuntu GRUB2 Troubleshooting guide][10]. The concepts explained there are valid for other distributions as well.
-
-### Summary
-
-In this article we have introduced you to GRUB, indicated where you can find documentation both online and offline, and explained how to approach an scenario where a system has stopped booting properly due to a bootloader-related issue.
-
-Fortunately, GRUB is one of the tools that is best documented and you can easily find help either in the installed docs or online using the resources we have shared in this article.
-
-Do you have questions or comments? Don’t hesitate to let us know using the comment form below. We look forward to hearing from you!
-
---------------------------------------------------------------------------------
-
-via: http://www.tecmint.com/linux-basic-shell-scripting-and-linux-filesystem-troubleshooting/
-
-作者:[Gabriel Cánepa][a]
-译者:[译者ID](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.tecmint.com/author/gacanepa/
-[1]: http://www.tecmint.com/sed-command-to-create-edit-and-manipulate-files-in-linux/
-[2]: http://www.tecmint.com/installing-network-services-and-configuring-services-at-system-boot/
-[3]: http://www.tecmint.com/linux-boot-process/
-[4]: http://www.tecmint.com/linux-boot-process-and-manage-services/
-[5]: http://www.tecmint.com/best-linux-log-monitoring-and-management-tools/
-[6]: http://www.gnu.org/software/grub/manual/
-[7]: http://askubuntu.com/questions/82140/how-can-i-boot-with-an-older-kernel-version
-[8]: https://www.kernel.org/doc/Documentation/kernel-parameters.txt
-[9]: http://man7.org/linux/man-pages/man7/bootparam.7.html
-[10]: https://help.ubuntu.com/community/Grub2/Troubleshooting
diff --git a/sources/tech/LXD/Part 4 - LXD 2.0--Resource control.md b/sources/tech/LXD/Part 4 - LXD 2.0--Resource control.md
index 736c5b84bc..e404cccffa 100644
--- a/sources/tech/LXD/Part 4 - LXD 2.0--Resource control.md	
+++ b/sources/tech/LXD/Part 4 - LXD 2.0--Resource control.md	
@@ -1,6 +1,3 @@
-ezio is translating
-
-
 Part 4 - LXD 2.0: Resource control
 ======================================
 
diff --git a/sources/tech/awk/Part 13 - How to Write Scripts Using Awk Programming Language.md b/sources/tech/awk/Part 13 - How to Write Scripts Using Awk Programming Language.md
new file mode 100644
index 0000000000..05ef0902fe
--- /dev/null
+++ b/sources/tech/awk/Part 13 - How to Write Scripts Using Awk Programming Language.md	
@@ -0,0 +1,160 @@
+Part 13 - How to Write Scripts Using Awk Programming Language
+====
+
+All along from the beginning of the Awk series up to Part 12, we have been writing small Awk commands and programs on the command line and in shell scripts respectively.
+
+However, Awk, just as Shell, is also an interpreted language, therefore, with all that we have walked through from the start of this series, you can now write Awk executable scripts.
+
+Similar to how we write a shell script, Awk scripts start with the line:
+
+```
+#! /path/to/awk/utility -f 
+```
+
+For example on my system, the Awk utility is located in /usr/bin/awk, therefore, I would start an Awk script as follows:
+
+```
+#! /usr/bin/awk -f 
+```
+
+Explaining the line above:
+
+```
+#! – referred to as Shebang, which specifies an interpreter for the instructions in a script
+/usr/bin/awk – is the interpreter
+-f – interpreter option, used to read a program file
+```
+
+That said, let us now dive into looking at some examples of Awk executable scripts, we can start with the simple script below. Use your favorite editor to open a new file as follows:
+
+```
+$ vi script.awk
+```
+
+And paste the code below in the file:
+
+```
+#!/usr/bin/awk -f 
+BEGIN { printf "%s\n","Writing my first Awk executable script!" }
+```
+
+Save the file and exit, then make the script executable by issuing the command below:
+
+```
+$ chmod +x script.awk
+```
+
+Thereafter, run it:
+
+```
+$ ./script.awk
+```
+
+Sample Output
+
+```
+Writing my first Awk executable script!
+```
+
+A critical programmer out there must be asking, “where are the comments?”, yes, you can also include comments in your Awk script. Writing comments in your code is always a good programming practice.
+
+It helps other programmers looking through your code to understand what you are trying to achieve in each section of a script or program file.
+
+Therefore, you can include comments in the script above as follows.
+
+```
+#!/usr/bin/awk -f 
+#This is how to write a comment in Awk
+#using the BEGIN special pattern to print a sentence 
+BEGIN { printf "%s\n","Writing my first Awk executable script!" }
+```
+
+Next, we shall look at an example where we read input from a file. We want to search for a system user named aaronkilik in the account file, /etc/passwd, then print the username, user ID and user GID as follows:
+
+Below is the content of our script called second.awk.
+
+```
+#! /usr/bin/awk -f 
+#use BEGIN sepecial character to set FS built-in variable
+BEGIN { FS=":" }
+#search for username: aaronkilik and print account details 
+/aaronkilik/ { print "Username :",$1,"User ID :",$3,"User GID :",$4 }
+```
+
+Save the file and exit, make the script executable and execute it as below:
+
+```
+$ chmod +x second.awk
+$ ./second.awk /etc/passwd
+```
+
+Sample Output
+
+```
+Username : aaronkilik User ID : 1000 User GID : 1000
+```
+
+In the last example below, we shall use do while statement to print out numbers from 0-10:
+
+Below is the content of our script called do.awk.
+
+```
+#! /usr/bin/awk -f 
+#printing from 0-10 using a do while statement 
+#do while statement 
+BEGIN {
+#initialize a counter
+x=0
+do {
+print x;
+x+=1;
+}
+while(x<=10)
+}
+```
+
+After saving the file, make the script executable as we have done before. Afterwards, run it:
+
+```
+$ chmod +x do.awk
+$ ./do.awk
+```
+
+Sample Output
+
+```
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+```
+
+### Summary
+
+We have come to the end of this interesting Awk series, I hope you have learned a lot from all the 13 parts, as an introduction to Awk programming language.
+
+As I mentioned from the beginning, Awk is a complete text processing language, for that reason, you can learn more other aspects of Awk programming language such as environmental variables, arrays, functions (built-in & user defined) and beyond.
+
+There is yet additional parts of Awk programming to learn and master, so, below, I have provided some links to important online resources that you can use to expand your Awk programming skills, these are not necessarily all that you need, you can also look out for useful Awk programming books.
+
+
+For any thoughts you wish to share or questions, use the comment form below. Remember to always stay connected to Tecmint for more exciting series.
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/write-shell-scripts-in-awk-programming/
+
+作者:[Aaron Kili |][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
diff --git a/sources/tech/awk/part 12 - How to Use Flow Control Statements in Awk.md b/sources/tech/awk/part 12 - How to Use Flow Control Statements in Awk.md
new file mode 100644
index 0000000000..37bd91461f
--- /dev/null
+++ b/sources/tech/awk/part 12 - How to Use Flow Control Statements in Awk.md	
@@ -0,0 +1,246 @@
+How to Use Flow Control Statements in Awk - part12
+====
+
+When you review all the Awk examples we have covered so far, right from the start of the Awk series, you will notice that all the commands in the various examples are executed sequentially, that is one after the other. But in certain situations, we may want to run some text filtering operations based on some conditions, that is where the approach of flow control statements sets in.
+
+![](http://www.tecmint.com/wp-content/uploads/2016/08/Use-Flow-Control-Statements-in-Awk.png)
+
+There are various flow control statements in Awk programming and these include:
+
+- if-else statement
+- for statement
+- while statement
+- do-while statement
+- break statement
+- continue statement
+- next statement
+- nextfile statement
+- exit statement
+
+However, for the scope of this series, we shall expound on: if-else, for, while and do while statements. Remember that we already walked through how to use next statement in Part 6 of this Awk series.
+
+### 1. The if-else Statement
+
+The expected syntax of the if statement is similar to that of the shell if statement:
+
+```
+if  (condition1) {
+actions1
+}
+else {
+actions2
+}
+```
+
+In the above syntax, condition1 and condition2 are Awk expressions, and actions1 and actions2 are Awk commands executed when the respective conditions are satisfied.
+
+When condition1 is satisfied, meaning it’s true, then actions1 is executed and the if statement exits, otherwise actions2 is executed.
+
+The if statement can also be expanded to a if-else_if-else statement as below:
+
+```
+if (condition1){
+actions1
+}
+else if (conditions2){
+actions2
+}
+else{
+actions3
+}
+```
+
+For the form above, if condition1 is true, then actions1 is executed and the if statement exits, otherwise condition2 is evaluated and if it is true, then actions2 is executed and the if statement exits. However, when condition2 is false then, actions3 is executed and the if statement exits.
+
+Here is a case in point of using if statements, we have a list of users and their ages stored in the file, users.txt.
+
+We want to print a statement indicating a user’s name and whether the user’s age is less or more than 25 years old.
+
+```
+aaronkilik@tecMint ~ $ cat users.txt
+Sarah L			35    	F
+Aaron Kili		40    	M
+John  Doo		20    	M
+Kili  Seth		49    	M    
+```
+
+We can write a short shell script to carry out our job above, here is the content of the script:
+
+```
+#!/bin/bash
+awk ' { 
+if ( $3 <= 25 ){
+print "User",$1,$2,"is less than 25 years old." ;
+}
+else {
+print "User",$1,$2,"is more than 25 years old" ; 
+}
+}'    ~/users.txt
+```
+
+Then save the file and exit, make the script executable and run it as follows:
+
+```
+$ chmod +x test.sh
+$ ./test.sh
+```
+
+Sample Output
+
+```
+User Sarah L is more than 25 years old
+User Aaron Kili is more than 25 years old
+User John Doo is less than 25 years old.
+User Kili Seth is more than 25 years old
+```
+
+### 2. The for Statement
+
+In case you want to execute some Awk commands in a loop, then the for statement offers you a suitable way to do that, with the syntax below:
+
+Here, the approach is simply defined by the use of a counter to control the loop execution, first you need to initialize the counter, then run it against a test condition, if it is true, execute the actions and finally increment the counter. The loop terminates when the counter does not satisfy the condition.
+
+```
+for ( counter-initialization; test-condition; counter-increment ){
+actions
+}
+```
+
+The following Awk command shows how the for statement works, where we want to print the numbers 0-10:
+
+```
+$ awk 'BEGIN{ for(counter=0;counter<=10;counter++){ print counter} }'
+```
+
+Sample Output
+
+```
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+```
+
+### 3. The while Statement
+
+The conventional syntax of the while statement is as follows:
+
+```
+while ( condition ) {
+actions
+}
+```
+
+The condition is an Awk expression and actions are lines of Awk commands executed when the condition is true.
+
+Below is a script to illustrate the use of while statement to print the numbers 0-10:
+
+```
+#!/bin/bash
+awk ' BEGIN{ counter=0 ;
+while(counter<=10){
+print counter;
+counter+=1 ;
+}
+}  
+```
+
+Save the file and make the script executable, then run it:
+
+```
+$ chmod +x test.sh
+$ ./test.sh
+```
+
+Sample Output
+
+```
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+```
+
+### 4. The do while Statement
+
+It is a modification of the while statement above, with the following underlying syntax:
+
+```
+do {
+actions
+}
+while (condition) 
+```
+
+The slight difference is that, under do while, the Awk commands are executed before the condition is evaluated. Using the very example under while statement above, we can illustrate the use of do while by altering the Awk command in the test.sh script as follows:
+
+```
+#!/bin/bash
+awk ' BEGIN{ counter=0 ;  
+do{
+print counter;  
+counter+=1 ;    
+}
+while (counter<=10)   
+} 
+'
+```
+
+After modifying the script, save the file and exit. Then make the script executable and execute it as follows:
+
+```
+$ chmod +x test.sh
+$ ./test.sh
+```
+
+Sample Output
+
+```
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+```
+
+### Conclusion
+
+This is not a comprehensive guide regarding Awk flow control statements, as I had mentioned earlier on, there are several other flow control statements in Awk.
+
+Nonetheless, this part of the Awk series should give you a clear fundamental idea of how execution of Awk commands can be controlled based on certain conditions.
+
+You can as well expound more on the rest of the flow control statements to gain more understanding on the subject matter. Finally, in the next section of the Awk series, we shall move into writing Awk scripts.
+
+--------------------------------------------------------------------------------
+
+via: http://www.tecmint.com/use-flow-control-statements-with-awk-command/
+
+作者:[Aaron Kili][a]
+译者:[译者ID](https://github.com/译者ID)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.tecmint.com/author/aaronkili/
+
+
diff --git a/translated/talk/20160505 Confessions of a cross-platform developer.md b/translated/talk/20160505 Confessions of a cross-platform developer.md
deleted file mode 100644
index 611ccf506c..0000000000
--- a/translated/talk/20160505 Confessions of a cross-platform developer.md	
+++ /dev/null
@@ -1,103 +0,0 @@
-
-一位跨平台开发者的自白
-=============================================
-
-![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/business_clouds.png?itok=cucHuJnU)
-
-Andreia Gaita[1]将会在OSCON开源大会上发表一个题为[跨平台开发者的自白][2]的演讲。她长期从事于开源工作,并且为Mono[3]【译者注:一个致力于开创.NET在Linux上使用的开源工程】工程做着贡献。Andreia任职于GitHub,她的工作是专注于为Visual Studio构建github上的可扩展管理器。
-
-
-我在她发表演讲前就迫不及待的想要问她一些关于跨平台开发的事,作为一名跨平台开发者,她已经有了16年的学习经验了。
-
-![](https://opensource.com/sites/default/files/images/life/Interview%20banner%20Q%26A.png)
-
-
-**在你跨平台工作的过程中,你使用过的最简单的和最难的代码语言是什么?**
-
-
-很少讨论某种语言的好坏,大多数是关于对于这种语言的库和工具的易用性。编译器、解释器以及构建器语言决定了用它们做跨平台开发的难易程度(不论它是否可能),能够实现UI和本地系统访问的函数库的可用性都决定了开发时兼容操作系统的程度。如果这样想,我认为C#最适合完成跨平台开发工作。这种语言有它自己的特色,它允许本地快速唤醒和精确的内存地址;如果你希望你的代码能够与系统和本地函数库进行交互,那么你就会需要C#。当我需要特殊的系统集成的时候,我就会切换到C或者C++。
-
-
-**你使用的跨平台开发工具或者抽象有哪些?**
-
-我的大部分跨平台工作都是为其他人开发工具和库,然后把它们组合起来开发跨平台的应用程序,其中大多用到MONO/C#。说真的,我不太懂抽象。大多数时候,我依赖Mono去完成一个跨平台的app开发以及它的UI,或者一个偶然的游戏开发中的Unity3D部分。我经常使用Electron【译者注:Atom编辑器的兄弟项目,可以用Electron开发桌面应用】。
-
-
-
-**你接触过哪些构建系统?它们之间的区别是由于语言还是平台的不同?**
-
-
-我试着选择适合我使用语言的构建系统。那样 ,就会很少遇到让我头疼的问题。它需要支持平台和体系结构间的选择、构建智能工件位置(多个并行构建)以及易配置性等。大多数时候,我的项目会结合C/C++和C#,同时我也想要为一些开源分支构建相应的配置环境(Debug, Release, Windows, OSX, Linux, Android, iOS, etc, etc.)),通常也需要为每个构建的智能工件选择带有不同标志的编译器。所以我不得不做很多工作,而这种以我的方式进行的工作并没有很多收获。我时常尝试着用不同的构建系统,仅仅是想了解到最新的情况,但是最终我还是回到使用makefile的情况,采用shell和批处理脚本之一与Perl脚本相结合的方式,以达到使用他们的目的(因为如果我想用户去做很多我做的事情,我还是最好选择一种命令行脚本语言,这样它在哪里都可以用)。
-
-
-
-**你怎样平衡在这种统一用户接口视觉体验需求上的强烈渴望呢?** 
-
-
-
-用户接口的跨平台实现很困难。在过去几年中我已经使用了一些跨平台GUI,并且我认为一些问题没有最优解。那些问题都基于两种操作,你也可以选择一个跨平台工具去做一个UI,去用你所喜欢的所有的平台,这样感觉不是太对,用小的代码库和比较低的维护费用。或者你可以选择去开发一个特有平台的UI,那样看起来就会是很本地化并且能很好的使其与一个大型的代码库结合,也会有很高的维护费用。这种决定完全取决于APP的类型。它的特色在哪里呢?你有什么资源?你可以把它运行在多少平台上?
-
-
-最后,我认为用户对于这种框架性的“一个UI统治所有”的UI的容忍度更大了,就比如Electron。我曾经有个Chromium+C+C#的框架侧项目,并且希望我在一天内用C#构建Electron型的app,这样的话我就可以做到两全其美了。
-
- 
- **你对构建或者打包有依赖性吗 ?**
-
-
- 我很少谈及依赖性问题。很多次我都被ABI【译者注:应用程序二进制接口】的崩溃、存在冲突的征兆以及包的丢失问题所困扰。我决定我要使用的操作系统的版本,都是选择最低的依赖去使问题最小化。通常这就意味着有五种不同的Xcode的副本和OSX框架库 ,在同样的机器上有五种不同的Visual Studio版本需要相应的被安装,多种clang【译者注:C语言、C++、Object-C、C++语言的轻量级编译器】和gcc版本,一系列的可以运行的其他的VM版本。如果我不能确定我要使用的操作系统的包的规定,我时常会连接静态库与子模块之间的依赖确保它们一直可用。大多时候,我会避免这些很棘手的问题,除非我非常需要使用他们。 
-
-  
-  **你使用能够持续集成的、代码重读的相关工具吗?**
-
-  基本每天都用。这是保持高效的唯一方式。我在一个项目中做的第一件事情是配置跨平台构建脚本,保证每件事尽可能自动化完成。当你想要使用多平台的时候,CI【译者注:持续集成】是至关重要的。在一个机器上,没有人能结合所有的不同的平台。并且一旦你的构建过程没有包含所有的平台,你就不会注意到你搞砸的事情。在一个共享的多平台代码库中 ,不同的人拥有不同的平台和特征,所以仅有的方法是保证跨团队浏览代码时结合CI和其他分析工具的公平性。这不同于其他的软件项目,如果不使用相关的工具就只有失败了。
-
-
-  **你依赖于自动构建测试或者趋向于在每个平台上构建并且进行局部测试吗?**
-
-
-  对于不包括UI的工具和库,我通常能够侥幸完成自动构建测试。如果那是一个UI,两种方法我都会用到——做一个可靠的,可自动编写脚本的UI,因为基本没有GUI工具,所以我不得不在去创建UI自动化工具,这种工具可以工作在所有的我用到的平台上,或者我也可以手动完成。如果一个项目使用一个定制的UI工具(一个像Unity3D那样做的OpenGL UI ),开发自动化的可编写脚本工具和更多的自动化工具就相当容易。不过,没有什么东西会像人类一样通过双击而搞砸事情。
-
-
-
-  **如果你要做跨平台开发,你想要在不同的平台上使用不同的编辑器,比如在Windows上使用Visual Studio,在Linux上使用Qt Creator,在Mac上使用XCode吗?还是你更趋向于使用Eclipse这样的可以在所有平台上使用的编辑器?**
-
-
-
-  我喜欢使用不同的编辑器构建系统。我更喜欢在不同的带有构建脚本的IDE上保存项目文件(可以使增加IDE变得更容易),这些脚本可以为他们支持的平台去驱动IDE开始工作。对于一个开发者来说编辑器是最重要的工具,开发者花费时间和精力去学习使用各种编辑器,但是它们都不能相互替代。我使用我最喜欢的编辑器和工具,每个人也应该能使用他们最喜爱的工具。
-
-
-
-  **在跨平台开发的时候,你更喜欢使用什么开发环境和IDE呢?**
-
-
-  跨平台开发者好像被诅咒一样,他们不得不选择小众化的编辑器去完成大多数跨平台的工作。我爱用Visual Studio,但是我不能依赖它完成除windows平台外的工作(你可能不想让windows成为你的初级交叉编译平台,所以我不会使用它作为我的初级集成开发环境。即使我这么做了,跨平台开发者的潜意识也知道有可能会用到很多平台。这就意味着必须很熟悉他们——使用一种平台上的编辑器就必须知道这种操作系统的设置,运行方式以及它的局限性等。做这些事情就需要头脑清醒(我的捷径是加强记忆),我不得不依赖于跨平台编辑器。所以我使用Emacs 和Sublime。
-
-
-
-  **你最喜欢的过去跨平台项目是什么**?
-
-
-  我一直很喜欢Mono,并且得心应手,在一些开发中大多数的项目都是用一些方法围绕着它进行的。Gluezilla曾经是我在多年前开发的一个Mozilla【译者注:Mozilla基金会,为支持和领导开源Mozilla项目而设立的非营利组织】结合器,可以C#开发的app嵌入到web浏览器试图中,并且看起来很明显。在这一点上,我开发过一个窗体app,它是在linux上开发的,它运行在带有一个嵌入GTK试图的windows系统上,并且这个系统将会运行一个Mozilla浏览器试图。CppSharp项目(以前叫做Cxxi,更早时叫做CppInterop)是一个我开始结合绑定有C#的C++库的项目,这样就可以唤醒和创建实例来把C#结合到C++中。这样做的话,它在运行的时候就能够发现所使用的平台以及用来创建本地运行库的编译器;而且还为它生成正确的C#绑定。这多么有趣啊。
-
-   
-   **你怎样看跨平台开发的未来趋势呢?**
-
-
-   我们构建本地应用程序的方式已经改变了,我觉得在各种桌面操作系统之间存在差异,而且这种差异将会变得更加微妙;所以构建跨平台的应用程序将会更加容易,而且这种应用程序即使没有在本平台也可以完全兼容。不好的是,这可能意味着应用程序很难获得,并且当在操作系统上使用的时候功能得不到最好的发挥。我们知道怎样把库和工具以及运行环境的跨平台开发做的更好,但是跨平台应用程序的开发仍然需要我们的努力。
-
-
-   --------------------------------------------------------------------------------
-
-   via: https://opensource.com/business/16/5/oscon-interview-andreia-gaita
-
-   作者:[Marcus D. Hanwell ][a]
-   译者:[vim-kakali](https://github.com/vim-kakali)
-   校对:[校对者ID](https://github.com/校对者ID)
-
-   本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-   [a]: https://opensource.com/users/mhanwell
-   [1]: https://twitter.com/sh4na
-   [2]: http://conferences.oreilly.com/oscon/open-source-us/public/schedule/detail/48702
-   [3]: http://www.mono-project.com/
-
diff --git a/translated/talk/20160506 Linus Torvalds Talks IoT Smart Devices Security Concerns and More.md b/translated/talk/20160506 Linus Torvalds Talks IoT Smart Devices Security Concerns and More.md
deleted file mode 100644
index 876f84f68e..0000000000
--- a/translated/talk/20160506 Linus Torvalds Talks IoT Smart Devices Security Concerns and More.md	
+++ /dev/null
@@ -1,48 +0,0 @@
-
-Linus Torvalds 谈及物联网,智能设备,安全连接等问题[video]
-===========================================================================
-
-![](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/elc-linus-b.jpg?itok=6WwnCSjL)
->Dirk Hohndel 在嵌入式大会上采访 Linus Torvalds 。
-
-
- [嵌入式大会(Embedded Linux Conference)][0] 从在 San Diego 【译注:圣迭戈,美国加利福尼亚州的一个太平洋沿岸城市。】开始举办到现在已经有 11 年了,在 4 月 4 日到 6 日,Linus Torvalds 加入了会议的主题讨论。他是 Linux 内核的缔造者和最高决策者,也是“我们都在这里的原因”,在采访他的对话中,英特尔的 Linux 和开源技术总监 Dirk Hohndel 谈到了 Linux 在嵌入式和物联网应用程序领域的快速发展前景。Torvalds 很少出席嵌入式 Linux 大会,这些大会经常被 Linux 桌面、服务器和云技术夺去光芒。
-![](https://www.linux.com/sites/lcom/files/styles/floated_images/public/elc-linus_0.jpg?itok=FNPIDe8k)
->Linus Torvalds 在嵌入式 Linux 大会上的演讲。
-
-
-物联网是嵌入式大会的主题,也包括未来开放物联网的最高发展方向(OpenIoT Summit),这是采访 Torvalds 的主要话题。
-
-Torvalds  对 Hohndel 说到,“或许你不会在物联网设备上看到 Linux 的影子,但是在你有一个核心设备的时候,你就会需要它。你需要智能设备尤其在你有 23 [物联网标准]的时候。如果你全部使用低级设备,它们没必要一定运行 Linux ,它们采用的标准稍微有点不同,所以你需要很多智能设备。我们将来也不会有一个完全开放的标准,只是给它一个统一的标准,但是你将会需要 4 分之 3 的主要协议,它们都是这些智能核心的转化形式。”
-
-当 Hohndel 问及在物联网的巨大安全漏洞的时候, Torvalds 神情如常。他说:“我不担心安全问题因为我们能做的不是很多,物联网如果遭受攻击是无法挽回的-这是事实。"
-
-Linux 缔造者看起来更关心的是一次性的嵌入式项目缺少及时的上游贡献,尽管他注意到近年来这些都有一些本质上的提升,特别是在硬件上的发展。
-
-Torvalds 说:”嵌入式领域历来就很难与开源开发者有所联系,但是我认为这些都在发生改变,ARM 团队也已经很优秀了。内核维护者事实上也看到了硬件性能的提升。一切都在变好,但是昨天却不是这样的。”
-
-Torvalds 承认他在家经常使用桌面系统而不是嵌入式系统,并且在使用硬件的时候他有“两只左手”。
-
-“我已经用电烙铁弄坏了很多东西。”他说到。“我真的不适合搞硬件开发。”;另一方面,Torvalds 设想如果他现在是个年轻人,他可能被 Raspberry Pi(树莓派) 和 BeagleBone(猎兔犬板)【译注:Beagle板实际是由TI支持的一个以教育(STEP)为目的的开源项目】欺骗。“最主要是原因是如果你善于焊接,那么你就仅仅是买到了一个新的板子。”
-
-同时,Torvalds 也承诺他要为 Linux 桌面再奋斗一个 25 年。他笑着说:“我要为它工作一生。”
-
-下面,请看完整视频。
-
-获取关于嵌入式 Linux 和物联网的最新信息。进入 2016 年嵌入式 Linux 大会 150+ 分钟的会议全程。[现在观看][1].
-[video](https://youtu.be/tQKUWkR-wtM)
-
---------------------------------------------------------------------------------
-
-via: https://www.linux.com/news/linus-torvalds-talks-iot-smart-devices-security-concerns-and-more-video
-
-作者:[ERIC BROWN][a]
-译者:[vim-kakali](https://github.com/vim-kakali)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: https://www.linux.com/users/ericstephenbrown
-[0]: http://events.linuxfoundation.org/events/embedded-linux-conference
-[1]: http://go.linuxfoundation.org/elc-openiot-summit-2016-videos?utm_source=lf&utm_medium=blog&utm_campaign=linuxcom
-
diff --git a/translated/talk/20160525 What containers and unikernels can learn from Arduino and Raspberry Pi.md b/translated/talk/20160525 What containers and unikernels can learn from Arduino and Raspberry Pi.md
new file mode 100644
index 0000000000..958cf7ee67
--- /dev/null
+++ b/translated/talk/20160525 What containers and unikernels can learn from Arduino and Raspberry Pi.md	
@@ -0,0 +1,51 @@
+容器和 Unikernel 能从 Raspberry Pi(树莓派)和 Arduino 学到什么
+==========================================================================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/bus-containers.png?itok=vM7_7vs0)
+
+
+某一天,我和我的一个机械工程师朋友聊天的时候。 他最近在做一个给半挂卡车的电子辅助刹车系统,他提到他们公司的办公室里都是 [Arduinos][1]。这主要是方便员工可以快速的对新的想法进行实验。他也提到了,Arduinos 其实比自己画电路板更加昂贵。对此,我感到非常震惊。因为我从软件行业得到的印象是 Arduinos 比定制电路板更加便宜。
+
+我常常把 [Arduinos][2] 和 [Raspberry Pi][3] 看做是可以制作非常有趣设备的小型,Cool,特别的组件。我主要是从事软件行业,并且常常想让 Linux 在 x86 和 x86-64 设备上都可以执行一致。真相就是,Arduinos 并不特殊。实际上,他们非常通用。他们相当的小,便宜,但是非常得灵活。这就是为什么他们向野火一样流行起来。他们有所有种类的输入输出设备,和扩展卡。他们能让制作者快速的构建非常 Cool 的设备。他们甚至可以让公司可以快速的开发产品。
+
+一整套 Arduino 的价格比批量生产的电路板高了很多,但是,看不见的时间成本却低了很多。当电路板大规模生产的时候,价格可以控制的很低,但是,之前的研发费用却高了很多。所以,长话短说,答案就是,使用 Arduino 划得来。
+
+### Unikernel, Rump 内核,和容器主机
+
+Unikernel, Rump 内核和迷你 Linux 发行版,这些操作系统是为了特有用途而构建的。这些特有的操作系统,某种程度上就像定制电路板。他们需要前期的研发,还需要为了工具化而设计,但是,当大规模部署的时候,他可以提供强大的性能。
+
+迷你操作系统,例如:红帽企业版或者 CoreOS 是为了运行容器而构建的。他们很小,快速,并且很容易在启动时配置,并且运行容器非常良好。缺点就是他需要额外的工程量来添加第三方插件,比如监控客户端或者虚拟化的工具。一些工具也需要为了超级权限的容器而重新设计。 如果你正在构建一个巨大的容器环境,这些额外的工作量是划算的。但是,如果只是尝试,那就没必要了。
+
+容器提供了运行标准化的工作流程 (比如使用 [glibc][4] 编译) 的能力。一个好处就是你可以在你的电脑上构建和测试这个工作单元 (Docker 镜像) 并且在完全不同的硬件上或者云端非常顺利的部署。而且保持着相同的特性。在生产环境中,容器的宿主机可以依旧被运维配置管理,但是应用被开发团队控制。这就是对两个团队来说最好的合作方式。
+
+Unikernels 和 Rump 内核依旧是为了特定目标构建的,但是却更进一步。整个的操作系统在构建的时候就被开发或者架构师配置了。这带来了好处,同时还有挑战。
+
+一个好处就是,开发人员可以控制这个工作流程的运转。理论上说,一个开发者可以为了不同的特性,尝试 [不同的 TCP 协议栈][5],并且选择最好的一个。在操作系统启动的时候,开发人也可以配置 IP 地址,而不是通过 DHCP。 开发人员也可以裁剪任何对于应用而言不需要的部分。这也是性能提升的保障,通过减少[不必要的上下文切换][6]。
+
+同时,Unikernel 也带来了挑战。目前,有很大的工具缺口。 现在,和画板子的世界类似,开发人员需要花费很多时间和精力在检查是否有完整的库文件存在,不然的话,他们必须改变他们应用的执行方式。在如何让嵌入式操作系统在运行时配置的时候,也存在挑战。最后,每次操作系统的大改动,都需要[反馈到开发人员][7]来进行修改。这并没有一个在开发和运维之间明确的界限,所以我能想象,为了接受了这个开发流程,一些组织或者公司必须要改变。
+
+### 结论
+
+这也有一些有趣的传闻在专门的容器主机,Rump 内核和 Unikernel,因为,他们会带来一个特定工作流程的潜在变革(嵌入式,云,等等)。在这个令人激动又快速发展的领域请保持你的关注,但是也不要放松警惕。
+
+目前,Unikernel 看起来和定制电路板很像。他们都需要前期的研发投资,并且都是独特的,可以为确定的工作流程带来好处。同时,容器甚至在常规的工作流中都非常有趣,而且他不需要那么多的投入。一个简单的例子,运维团队能方便的在容器上部署一个应用,但是在 Unikernel 上部署一个应用则需要重新设计和编码,而且业界并不能完全保证,这个工作流程可以被部署在 Unikernel 上。
+
+容器,Rump 内核 和 Unikernel 有一个光明的未来!
+
+--------------------------------------
+via: https://opensource.com/business/16/5/containers-unikernels-learn-arduino-raspberry-pi 
+
+作者:[Scott McCarty][a]
+译者:[MikeCoder](https://github.com/MikeCoder)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/fatherlinux
+[1]: https://opensource.com/resources/what-arduino
+[2]: https://opensource.com/life/16/4/arduino-day-3-projects
+[3]: https://opensource.com/resources/what-raspberry-pi
+[4]: https://en.wikipedia.org/wiki/GNU_C_Library
+[5]: http://www.eetasia.com/ARTICLES/2001JUN/2001JUN18_NTEK_CT_AN5.PDF
+[6]: https://en.wikipedia.org/wiki/Context_switch
+[7]: http://developers.redhat.com/blog/2016/05/18/3-reasons-i-should-build-my-containerized-applications-on-rhel-and-openshift/
diff --git a/translated/talk/20160531 The Anatomy of a Linux User.md b/translated/talk/20160531 The Anatomy of a Linux User.md
deleted file mode 100644
index a404697347..0000000000
--- a/translated/talk/20160531 The Anatomy of a Linux User.md	
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-一个 Linux 用户的故事
-================================
-
-
-
-**一些新的 GNU/Linux 用户都很清楚的知道 Linux 不是 Windows .其他很多人都不是很清楚的知道.最好的发行版设计者努力保持新的思想**
-
-### Linux 的核心
-
-不管怎么说,Nicky 都不是那种表面上看起来很值得注意的人.她已经三十岁了,却决定回到学校学习.她在海军待了6年时间直到她的老友给她一份新的工作,而且这份工作比她在军队的工作还好.在过去的军事分支服务期间发生了很多事情.我认识她还是她在军队工作的时候.她是8个州的货车运输业协商区域的管理者.那会我在达拉斯跑肉品包装工具的运输.
-![](http://i2.wp.com/fossforce.com/wp-content/uploads/2016/05/anatomy.jpg?w=525)
-
-
-Nicky 和我在 2006 年成为了好朋友.她很外向,并且有很强的好奇心,她几乎走过每个运输商走的路线.一个星期五晚上我们在灯光下有一场很激烈的争论,像这样的 达 30 分钟的争论在我们之间并不少见.或许这并不比油漆一个球场省事,但是气氛还是可以控制的住的,感觉就像是一个很恐怖的游戏.在这次争论的时候她问到我是否可以修复她的电脑.
-
-她知道我为了一些贫穷的孩子能拥有他们自己的电脑做出的努力,当她抱怨她的电脑很慢的时候,我提到了参加 Bill Gate 的 401k 计划[译注:这个计划是为那些为内部收益代码做过贡献的人们提供由税收定义的养老金账户.].Nicky 说这是了解 Linux 的最佳时间.
-
-她的电脑相当好,它是一个带有 Dell 19 显示器的华硕电脑.不好的是,当不需要一些东西的时候,这个 Windows 电脑会强制性的显示所有的工具条和文本菜单.我们把电脑上的文件都做了备份之后就开始安装 Linux 了.我们一起完成了安装,并且我确信她知道了如何分区.不到一个小时,她的电脑上就有了一个漂亮的 PCLinuxOS 桌面.
-
-她会经常谈论她使用新系统的方法,系统看起来多么漂亮.她不曾提及的是,她几乎被她面前的井然有序的漂亮桌面吸引.她说她的桌面带有漂亮的"微光".这是我在安装系统期间特意设置的.我每次在安装 Linux 的时候都会进行这样的配置.我想让每个 Linux 用户的桌面都配置这个漂亮的微光.
-
-大概第一周左右,她给我打电话或者发邮件问了一些常规的问题,但是最主要的问题还是她想知道怎样保存她打开的 Office 文件(OpenOffice)以至于她的同事也可以读这些文件.教一个人使用 Linux 或者 Open/LibreOffice 的时候最重要的就是教她保存文件.大多数用户仅仅看到第一个提示,只需要手指轻轻一点就可以以打开文件模式( Open Document Format )保存.
-
-
-大约一年前或者更久,一个高中生说他没有通过期末考试,因为教授不能打开他写着论文的文件.这引来不能决定谁对谁错的读者的激烈评论.这个高中生和教授都不知道这件事该怪谁.
-
-我知道一些大学教授甚至每个人都能够打开一个 ODF 文件.见鬼,很少有像微软这样优秀的公司,我想 Microsoft Office 现在已经能打开 ODT 或者 ODF 文件了.我也不能确保,毕竟我最近一次用 Microsoft Office 是在 2005 年.
-
-甚至在困难时期,当 Microsoft 很受欢迎并且很热衷于为使用他们系统的厂商的企业桌面上安装他们自己的软件的时候,我和一些使用 Microsoft Office 的用户的产品生意和合作从来没有出现过问题,因为我会提前想到可能出现的问题并且不会有侥幸心理.我会发邮件给他们询问他们正在使用的 Office 版本.这样,我就可以确保以他们能够读写的格式保存文件.
-
-说到 Nicky ,她花了很多时间学习她的 Linux 系统.我很惊奇于她的热情.
-
-当人们意识到所有的使用 Windows 的习惯和工具都要被抛弃的时候,学习 Linux 系统也会很容易.甚至在我们谈论第一次用的系统时,我查看这些系统的桌面或者下载文件夹大多都找不到 some_dodgy_file.exe 这样的文件.
-
-在我们通常讨论这些文件的时候,我们也会提及关于更新的问题.很长时间我都不会在一台电脑上反复设置去完成多种程序的更新和安装.比如 Mint ,它没有带有 Synaptic [译注:一个 Linux 管理图像程序包]的完整更新方法,这让我失去兴趣.但是我们的老成员 dpkg 和 apt 是我们的好朋友,聪明的领导者已经得到肯定并且认识到命令行看起来不是那么舒服,同时欢迎新的用户加入.
-
-我很生气,强烈反对机器对 Synaptic 的削弱,最后我放弃使用它.你记得你第一次使用的 Linux 发行版吗?记得你什么时候在 Synaptic 中详细查看大量的软件列表吗?记得你怎样开始检查并标记每个你发现的很酷的程序吗?你记得有多少这样的程序开始都是使用"lib"这样的文件吗?
-
-是的,我也是。我安装并且查看了一些新的安装程序,直到我发现那些库文件是应用程序的螺母和螺栓,而不是应用程序本身.这就是为什么这些聪明的开发者在 Linux Mint 和 Ubuntu 之后创造了聪明、漂亮和易于使用的应用程序的安装程序.Synaptic 仍然是我们的老大,但是对于一些后来者,安装像 lib 文件这样的方式需要打开大量的文件夹,很多这样的事情都会导致他们放弃使用这个系统.在新的安装程序中,这些文件会被放在一个文件夹中甚至不会展示给用户.总之,这也是该有的解决方法.
-
-除非你要改变应该支持的需求.
-
-现在的 Linux 发行版中有很多有用的软件,我也很感谢这些开发者,因为他们,我的工作变得容易.不是每一个 Linux 新用户都像 Nicky 这样富有热情.她相当不错的完成了安装过程并且达到了忘我的状态.像她这样极具热情的毕竟是少数.大多数新的 Linux 用户也就是在需要的时候才这样用心.
-
-很不错,他们都是要教自己的孩子使用 Linux 的人.
---------------------------------------------------------------------------------
-
-via: http://fossforce.com/2016/05/anatomy-linux-user/
-
-作者:[Ken Starks][a]
-译者:[vim-kakali](https://github.com/vim-kakali)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://linuxlock.blogspot.com/
diff --git a/translated/talk/20160627 Linux Applications That Works On All Distributions – Are They Any Good.md b/translated/talk/20160627 Linux Applications That Works On All Distributions – Are They Any Good.md
new file mode 100644
index 0000000000..45232346bf
--- /dev/null
+++ b/translated/talk/20160627 Linux Applications That Works On All Distributions – Are They Any Good.md	
@@ -0,0 +1,87 @@
+适用于所有发行版的 Linux 应用程序——是否真的有好处呢?
+============================================================================
+
+![](http://www.iwillfolo.com/wordpress/wp-content/uploads/2016/06/Bundled-applications.jpg)
+
+
+让我们回顾一下 Linux 社区最初的愿景——促进应用程序的分发来解决发行版的碎片化。
+
+继上周的文章:[Ubuntu’s Snap, Red Hat’s Flatpak And Is ‘One Fits All’ Linux Packages Useful?][1] 之后,一系列新观点浮出水面,他们可能包含关于这些应用程序有效性的重要信息。
+
+### 缺点
+
+就这个话题在[这里][2]的评论,一个叫 Till 的 [Gentoo][3] 使用者,对于上一次我们未能完全解释的问题给出了一些新的观点。
+
+对于上一次我们选择仅仅称之为膨胀的的东西,Till 在这里进行了深入的剖析,这帮助我们更好的理解它的组成和其影响。
+
+这些被称之为“捆绑应用”的应用程序能够工作在所有发行版的机制是——将它依赖的库都包含在它们的应用软件之中,Till 说:
+
+
+>“捆绑应用装载了大量的并不被应用开发者所维护的软件。如果其中的函数库X被发现了一个安全问题,你得为每一个独立的应用程序安装更新来确保你的系统安全。”
+
+本质上,Till 提出了一个重要的安全问题。但是它并不仅仅与安全有关系,它还关系到许多方面,比如说系统维护,原子更新等等。
+
+此外,如果我们进一步假设:依赖开发者们也许会合作,将他们的软件与使用它的应用程序一起发布(一个乌托邦的情况),这将导致整个平台的开发整体放缓。
+
+另一个将会导致的问题是依赖关系变得模糊,就是说,如果你想知道一个应用程序捆绑了哪些依赖关系,你必须依靠开发者发布这些数据。
+
+或者就像 Till 说的:“比如说像包XY是否包含了函数库Z的更新这样的问题将会是你每天需要面对的”
+
+与之相反,对于 Linux 现行的标准的包管理方法(包括二进制包和源码包),你能够很容易的注意到哪些函数库已经在系统中更新了。
+并且,你也可以很轻松的知道哪些应用使用了这个函数库,这就将你从繁琐的单独检查每一个应用程序的工作中解救了出来。
+
+其他可能由膨胀导致的缺点包括:更大的包体积(每一个应用程序捆绑了依赖),更高的内存占用(没有分享函数库),并且,少了一个包过滤机制来防止恶意软件:发行版的包维护者也充当了一个在开发者和用户之间的过滤者,他保障了用户获得高质量的软件。而捆绑应用中就不再是这种情况了。
+
+最后一点,Till 声称,尽管在某些情况下很有用,但是在大多数情况下,捆绑应用程序将消弱自由软件在发行版中的地位(专有的供应商将被允许发布他们的软件而不在公共储存库中分享它)。
+
+除此之外,它引出了许多其他问题。很多问题仅仅只与开发人员有关。
+
+### 优点
+
+相比之下,另一个名叫 Sven 的人的评论试图反驳目前普遍反对使用捆绑应用程序的观点,从而证明和支持使用它。
+
+“浪费空间?”——斯文声称在当今世界我们有很多其他事情在浪费磁盘空间,比如电影存储在硬盘上、本地安装等等……
+
+最终,这些事情浪费的空间要远远多于仅仅 100 MB 而你每天都要使用的程序。因此浪费空间的说法实在很荒谬。
+
+“浪费运行内存?”——主要的观点有:
+
+- 共享库浪费的内存要远远少于程序运行时产生的数据
+
+- 而今运行内存已经很便宜了
+
+“安全梦魇”——不是每个应用程序的运行真正上要注重安全。
+
+而且,许多应用程序甚至从来没有看到任何安全更新,除非在“滚动更新的发行版”。
+
+除了 Sven 这种从实用出发的观点以外,Till 其实也指出了捆绑应用在一些情况下也有着其优点:
+
+- 专有的供应商想要保持他们的代码游离于公共存储库之外将更加容易。
+- 不被你的发行版打包的小生态的应用程序将变得更加可行。
+- 在没有Beta包的二进制发行版中测试应用将变得简单。
+- 将用户从复杂的依赖关系中解放出来。
+
+### 最后的思考
+
+虽然关于此问题有着不同的想法,但是有一个被大家共同接受的观点是:捆绑应用对于填补 Linux 生态系统有着其独到的作用。
+
+虽然如此,它的定位,不论是主流的还是边缘的,都变得愈发清晰,至少理论上是这样。
+
+想要尽可能优化其系统的用户,在大多数情况下应该要避免使用捆绑应用。
+
+而讲究易用性、尽可能在维护系统上少费劲的用户,可能应该会感觉这种新应用十分舒爽。
+
+--------------------------------------------------------------------------------
+
+via: http://www.iwillfolo.com/linux-applications-that-works-on-all-distributions-are-they-any-good/
+
+作者:[Editorials][a]
+译者:[Chao-zhi](https://github.com/Chao-zhi)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.iwillfolo.com/category/editorials/
+[1]: http://www.iwillfolo.com/ubuntus-snap-red-hats-flatpack-and-is-one-fits-all-linux-packages-useful/
+[2]: http://www.proli.net/2016/06/25/gnulinux-bundled-application-ramblings/
+[3]: http://www.iwillfolo.com/5-reasons-use-gentoo-linux/
diff --git a/translated/talk/20160627 Linux Practicality vs Activism.md b/translated/talk/20160627 Linux Practicality vs Activism.md
new file mode 100644
index 0000000000..81135bf56f
--- /dev/null
+++ b/translated/talk/20160627 Linux Practicality vs Activism.md	
@@ -0,0 +1,71 @@
+Linux 的实用性 VS 行动主义
+==================================
+
+>我们使用 Linux 是因为它比其他操作系统更实用,还是其他更高级的理由呢?
+
+其中一件关于运行 Linux 的最伟大的事情之一就是它所提供的自由。凡出现在 Linux 社区之间的划分在于我们如何珍惜这种自由。
+
+一些人认为,通过使用 Linux 所享有的自由是从供应商锁定或高软件成本的自由。大多数人会称这个是一个实际的考虑。而其他用户会告诉你,他们享受的是自由软件的自由。那就意味着拥抱支持 [开源软件运动][1] 的 Linux 发行版,完全避免专有软件和所有相关的东西。
+
+
+在这篇文章中,我将带你比较这两种自由的区别,以及他们如何影响 Linux 的使用。
+
+### 专有的问题
+
+大多数的用户有一个共同的一点是他们的喜欢避免专有软件。对于像我这样的实际的爱好者来说,这是一个我怎么样花我的钱,来控制我的软件和避免供应商锁定的问题。当然,我不是一个程序员……所以我调整我的安装软件是十分温柔的。但也有一些个别情况,一个应用程序的小调整可以意味着它的工作和不工作的区别。
+
+还有就是选择避开专有软件的Linux爱好者,因为他们觉得这是不道德的使用。通常这里主要的问题是使用专有软件会带走或者干脆阻碍你的个人自由。像这些用户更喜欢使用的Linux发行版和软件来支持 [自由软件理念][2] 。虽然它类似于开源的概念并经常直接与之混淆,[这里有些差异][3] 。
+
+因此,这里有个问题:像我这样的用户往往以其便利掩盖了其纯软件自由的理想化。不要误会我的意思,像我这样的人更喜欢使用符合自由软件背后的理想软件,但我们也更有可能做出让步,以完成特定的任务。
+
+这两种类型的 Linux 爱好者都喜欢使用非专有的解决方案。但是,自由软件倡导者根本不会去使用所有权,在那里作为实际的用户将依靠具有最佳性能的最佳工具。这意味着,在有些情况下的实际用户愿意来运行他们的非专有操作系统上的专有应用或代码实例。
+
+最终,这两种类型的用户都喜欢使用 Linux 所提供的。但是,我们这样做的原因往往会有所不同。有人认为那些不支持自由软件的人是无知的。我不同意,我认为它是实用方便性的问题。那些喜欢实用方便性的用户根本不关心他们软件的政治问题。
+
+### 实用方便性
+
+当你问起绝大多数的人为什么使用他们现在的操作系统,回答通常都集中于实用方便性。这种关于方便性的例子可能包括“它是我一直使用的东西”、“它运行的软件是我需要的”。 其他人可能进一步解释说,并没有那么多软件影响他们对操作系统的偏好和熟悉程度,最后,有“利基任务”或硬件兼容性问题也提供了很好的理由让我们用这个操作系统而不是另一个。
+
+这可能会让你们中许多人很惊讶,但我今天运行的桌面 Linux 最大的一个原因是由于熟悉。即使我为别人提供对 Windows 和 OS X 的支持,但实际上我是相当沮丧地使用这些操作系统,因为它们根本就不是我记忆中的那样习惯用法。我相信这可以让我对那些 Linux 新手表示同情,因为我太懂得踏入陌生的领域是怎样的让人倒胃口了。我的观点是这样的 —— 熟悉具有价值。而且熟悉同样使得实用方便性变得有力量。
+
+现在,如果我们把它和一个自由软件倡导者的需求来比较,你会发现那些人都愿意学习新的东西,甚至更具挑战性,去学习那些若转化成为他们所避免使用的非自由软件。这就是我经常赞美的那种用户,我认为他们愿意采取最少路径来遵循坚持他们的原则是十分值得赞赏的。
+
+### 自由的价值
+
+我不羡慕那些自由软件倡导者的一个地方,就是根据 [自由软件基金会][4] 所规定的标准需要确保他们可以一直使用 Linux 发行版和硬件,以便于尊重他们的数字自由。这意味着 Linux 内核需要摆脱专有的斑点的驱动支持和不需要任何专有代码的硬件。当然不是不可能的,但它很接近。
+
+一个自由软件倡导者可以达到的最好的情况是硬件是“自由兼容”的。有些供应商,可以满足这一需求,但他们大多是提供依赖于 Linux 兼容专有固件的硬件。伟大的实际用户对自由软件倡导者来说是个搅局者。
+
+那么这一切意味着的是,倡导者必须比实际的 Linux 爱好者,更加警惕。这本身并不一定是消极的,但如果是打算用自由软件的方法来计算的话那就值得考虑了。通过对比,实用的用户可以专心地使用与    Linux 兼容的任何软件或硬件。我不知道你是怎么想的,但在我眼中是更轻松一点的。
+
+### 定义自由软件 
+
+这一部分可能会让一部分人失望,因为我不相信自由软件只有一种。从我的立场,我认为真正的自由是能够在一个给定的情况里沉浸在所有可用的数据里,然后用最适合这个人的生活方式的途径来达成协议。
+
+所以对我来说,我更喜欢使用的 Linux 桌面,满足了我所有的需求,这包括使用非专有软件和专有软件。尽管这是公平的建议,专有的软件限制了我的个人自由,但我必须反驳这一点,因为我有选择用不用它,即选择的自由。
+
+或许,这也就是为什么我发现自己更确定开源软件的理想,而不是坚持自由软件运动背后的理念的原因。我更愿意和那些不会花时间告诉我,我是怎么用错了的那些人群在一起。我的经验是,那些开源的人群仅仅是感兴趣去分享自由软件的优点,而不是因为自由软件的理想主义的激情。
+
+我觉的自由软件的概念实在是太棒了。对那些需要活跃在软件政治,并指出使用专有软件的人的缺陷的人来说,那么我认为 Linux ( [GNU/Linux][5] ) 行动是一个不错的选择。在我们的介绍里,像我一样的实际用户更倾向于从自由软件的支持者改变方向。
+
+当我介绍 Linux 的桌面时,我富有激情地分享它的实际优点。而且我成功地让他们享受这一经历,我允许用户自己去发现自由软件的观点。但我发现大多数人使用的 Linux 不是因为他们想拥抱自由软件,而是因为他们只是想要最好的用户体验。也许只有我是这样的,很难说。
+
+嘿!说你呢?你是一个自由软件倡导者吗?也许你是个使用桌面 Linux 发行专有软件/代码的粉丝?那么评论和分享您的 Linux 桌面体验吧!
+
+
+--------------------------------------------------------------------------------
+
+via: http://www.datamation.com/open-source/linux-practicality-vs-activism.html
+
+作者:[Matt Hartley][a]
+译者:[joVoV](https://github.com/joVoV)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.datamation.com/author/Matt-Hartley-3080.html
+[1]: https://en.wikipedia.org/wiki/Free_software_movement
+[2]: https://www.gnu.org/philosophy/free-sw.en.html
+[3]: https://www.gnu.org/philosophy/free-software-for-freedom.en.html
+[4]: https://en.wikipedia.org/wiki/Free_Software_Foundation
+[5]: https://en.wikipedia.org/wiki/GNU/Linux_naming_controversy
\ No newline at end of file
diff --git a/translated/talk/20160718 Tips for managing your project's issue tracker.md b/translated/talk/20160718 Tips for managing your project's issue tracker.md
new file mode 100644
index 0000000000..a56bdca1d8
--- /dev/null
+++ b/translated/talk/20160718 Tips for managing your project's issue tracker.md	
@@ -0,0 +1,101 @@
+几个小窍门帮你管理项目的问题追踪器
+==============================================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/BUSINESS_opennature_3.png?itok=30fRGfpv)
+
+对于大多数开源项目来讲,问题追踪系统是至关重要的。虽然市面上有非常多的开源工具提供了这样的功能,但是大量项目还是选择了GitHub自带的问题追踪器(Issue Tracker)。
+
+它结构简单,因此其他人可以非常轻松地参与进来,但这才刚刚开始。
+
+如果没有适当的处理,你的代码仓库会挤满重复的问题单、模糊不明的特性需求单、混淆不清的bug报告单。项目维护者会被大量的组织内协调工作压得喘不过气来,新的贡献者也搞不清楚项目当前的重点工作是什么。
+
+接下来,我们一起研究下,如何玩转GitHub的问题单。
+
+### 问题单就是用户的故事
+
+我的团队曾经和开源专家[Jono Bacon][1]做过一次对话,他是[The Art of Community][2]的作者、GitHub的战略顾问和前社区总监。他告诉我们,高质量的问题单是项目成功的关键。尽管有些人把问题单仅仅看作是一堆难题的列表,但是他认为这个难题列表是我们必须要时刻关注、完善管理并进行分类的。他还认为,给问题单打上标签的做法,会令人意想不到的提升我们对代码和社区的了解程度,也让我们更清楚问题的关键点在哪里。
+
+“在提交问题单时,用户不太会有耐心或者有兴趣把问题的细节描述清楚。在这种情况下,你应当努力花最短的时间,尽量多的获取有用的信息。”,Jono Bacon说。
+
+统一的问题单模板可以大大减轻项目维护者的负担,尤其是开源项目的维护者。我们发现,让用户讲故事的方法总是可以把问题描述的非常清楚。用户讲故事时需要说明“是谁,做了什么,为什么而做”,也就是:我是【何种用户】,为了【达到何种目的】,我要【做何种操作】。
+
+实际操作起来,大概是这样的:
+
+>我是一名顾客,我想付钱,所以我想创建个账户。
+
+我们建议,问题单的标题始终使用这样的用户故事形式。你可以设置[问题单模板][3]来保证这点。
+
+![](https://opensource.com/sites/default/files/resize/issuetemplate-new-520x293.png)
+> 问题单模板让特性需求单保持统一的形式
+
+这个做法的核心点在于,问题单要被清晰的呈现给它涉及的每一个人:它要尽量简单的指明受众(或者说用户),操作(或者说任务),和收益(或者说目标)。你不需要拘泥于这个具体的模板,只要能把故事里的是什么事情或者是什么原因搞清楚,就达到目的了。
+
+### 高质量的问题单
+
+问题单的质量是参差不齐的,这一点任何一个开源软件的贡献者或维护者都能证实。具有良好格式的问题单所应具备的素质在[The Agile Samurai][4]有过概述。
+
+问题单需要满足如下条件:
+
+- 客户价值所在
+- 避免使用术语或晦涩的文字,就算不是专家也能看懂
+- 可以切分,也就是说我们可以一小步一小步的对最终价值进行交付
+- 尽量跟其他问题单没有瓜葛,这会降低我们在问题范围上的灵活性
+- 可以协商,也就说我们有好几种办法达到目标
+- 问题足够小,可以非常容易的评估出所需时间和资源
+- 可衡量,我们可以对结果进行测试
+
+### 那其他的呢? 要有约束
+
+如果一个问题单很难衡量,或者很难在短时间内完成,你也一样有办法搞定它。有些人把这种办法叫做”约束“。
+
+例如,”这个软件要快“,这种问题单是不符合我们的故事模板的,而且是没办法协商的。多快才是快呢?这种模糊的需求没有达到”好问题单“的标准,但是如果你把一些概念进一步定义一下,例如”每个页面都需要在0.5秒内加载完“,那我们就能更轻松的解决它了。我们可以把”约束“看作是成功的标尺,或者是里程碑。每个团队都应该定期的对”约束“进行测试。
+
+### 问题单里面有什么?
+
+敏捷方法中,用户的故事里通常要包含验收指标或者标准。如果是在GitHub里,建议大家使用markdown的清单来概述完成这个问题单需要完成的任务。优先级越高的问题单应当包含更多的细节。
+
+比如说,你打算提交一个问题单,关于网站的新版主页的。那这个问题单的子任务列表可能就是这样的:
+
+![](https://opensource.com/sites/default/files/resize/markdownchecklist-520x255.png)
+>使用markdown的清单把复杂问题拆分成多个部分
+
+在必要的情况下,你还可以链接到其他问题单,那些问题单每个都是一个要完成的任务。(GitHub里做这个挺方便的)
+
+将特性进行细粒度的拆分,这样更轻松的跟踪整体的进度和测试,要能更高频的发布有价值的代码。
+
+一旦以问题单的形式收到数据,我们还可以用API更深入的了解软件的健康度。
+
+”在统计问题单的类型和趋势时,GitHub的API可以发挥巨大作用“,Bacon告诉我们,”如果再做些数据挖掘工作,你就能发现代码里的问题点,谁是社区的活跃成员,或者其他有用的信息。“
+
+有些问题单管理工具提供了API,通过API可以增加额外的信息,比如预估时间或者历史进度。
+
+### 让大伙都上车
+
+一旦你的团队决定使用某种问题单模板,你要想办法让所有人都按照模板来。代码仓库里的ReadMe.md其实也可以是项目的”How-to“文档。这个文档会描述清除这个项目是做什么的(最好是用可以搜索的语言),并且解释其他贡献者应当如何参与进来(比如提交需求单、bug报告、建议,或者直接贡献代码)
+
+![](https://opensource.com/sites/default/files/resize/readme-520x184.png)
+>为新来的合作者在ReadMe文件里增加清晰的说明
+
+ReadMe文件是提供”问题单指引“的完美场所。如果你希望特性需求单遵循”用户讲故事“的格式,那就把格式写在ReadMe里。如果你使用某种跟踪工具来管理待办事项,那就标记在ReadMe里,这样别人也能看到。
+
+”问题单模板,合理的标签,如何提交问题单的文档,确保问题单被分类,所有的问题单都及时做出回复,这些对于开源项目都至关重要“,Bacon说。
+
+记住一点:这不是为了完成工作而做的工作。这时让其他人更轻松的发现、了解、融入你的社区而设立的规则。
+
+"关注社区的成长,不仅要关注参与开发者的的数量增长,也要关注那些在问题单上帮助我们的人,他们让问题单更加明确、保持更新,这是活跃沟通和高效解决问题的力量源泉",Bacon说。
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/life/16/7/how-take-your-projects-github-issues-good-great
+
+作者:[Matt Butler][a]
+译者:[echoma](https://github.com/echoma)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/mattzenhub
+[1]: http://www.jonobacon.org/
+[2]: http://www.artofcommunityonline.org/
+[3]: https://help.github.com/articles/creating-an-issue-template-for-your-repository/
+[4]: https://www.amazon.ca/Agile-Samurai-Masters-Deliver-Software/dp/1934356581
diff --git a/translated/talk/20160728 I've been Linuxing since before you were born.md b/translated/talk/20160728 I've been Linuxing since before you were born.md
new file mode 100644
index 0000000000..2ff2df8c83
--- /dev/null
+++ b/translated/talk/20160728 I've been Linuxing since before you were born.md	
@@ -0,0 +1,68 @@
+
+我玩 Linux 那会儿你还不知道在哪呢
+=====================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/life/OSDC_Penguin_Image_520x292_12324207_0714_mm_v1a.png?itok=WfAkwbFy)
+
+在很久以前,世界上没有 Linux 系统。真的没有!之前也从未存在过。不像现在, Linux 系统随处可见。有各种风格的 Unix 系统,有苹果的操作系统,有微软的 Windows 操作系统。
+
+随时 Windows 桌面的到来,改的东西很多,但是保留下来的东西的更多。尽管增加至20GB以上的存储,Windows 还是大体保持不变。(除了不能在DOS的提示下完成实际的工作)嘿,谁还记得 Gorilla.bas 那个出现在 DOS 系统里的炸香蕉的游戏吗?多么美好的时光啊!互联网永不会忘记,而且你可以在 Kongregate.com 这个网站玩Flash版本的这个游戏。
+
+苹果系统也改变了,变得更加友好、安全、美观。你别想着打开这个密封的盒子,它强制规定了用户能使用的硬件接口。 1998年:没有软盘。 2012年:没有光驱。 12英寸的 MacBook 只有一个提供电源的USB Type-C 端口、蓝牙、无线网卡、外部存储、视频输出接口和其它的一些配件。如果你想每次想接入一个新的设备而不想使用其它的配件和软件,这真是太抓狂了。其它:头戴式耳机。这是个非独有的硬件接口在 Apple 的领域里注定保留下来。
+
+还有其它的操作系统,比如: Amiga, BeOS, OS/2 以及无数的你能查到的操作系统。我支持的这些系统是因为现在都很容易查寻到,也没有理由不推荐它们。 Amiga、 BeOS 和 OS/2 这些系统都值得关注,因为它们有强大的功能,比如32位的多任务和高级图形处理能力。但是市场的影响击败了强大的系统性能,因此普通的 Apple 和 Windows 操作系统统治了市场的主导地位,而那些曾经的系统也逐渐销声匿迹了。
+
+然后 Linux 系统出现了,世界也因此改变了。
+
+### 第一款电脑
+
+我曾经使用过的第一款电脑是 Apple IIc ,大概在 1994年左右,那个时候 Linux 系统刚出来 3年。这是我从一个朋友那里借来的,用起来各方面都还不错,但是很不方便。所以我自己花了将近 500美元买了一台二手的 Tandy牌子的电脑。这对于卖电脑的人来说是一件很伤心的事,因为新电脑要花费双倍的价钱。那个时候,电脑贬值的速度非常快。这个电脑看起来像是个怪物:一个英特尔 386SX 的 CPU, 4MB的内存,一个 107MB的硬盘, 14英寸的彩色 CRT 显示器,运行 MS-DOS 5和 Windows 3.1系统。
+
+我曾无数次的拆开那个可怜的怪物,并且多次重新安装 Windows 和 DOS 系统。因为 Windows 桌面用的比较少,所以我的大部分工作都是在 DOS 下完成的。我喜欢玩血腥暴力的视频游戏,包括 Doom, Duke Nukem,Quake 和 Heretic。啊!那些美好的,让人心动的 8位图像。
+
+那个时候硬件的发展一直落后于软件,因此我经常升级硬件。现在我们能买到满足我们需求的任何配置的电脑。我已经好多年都没有再更新我的任何电脑硬件了。
+
+### 比特杂志
+
+回到那些曾经辉煌的年代,电脑商店布满大街小巷,找到本地的一家网络服务提供商(ISP)也不用走遍整条街。ISP 那个时候真的非比寻常。他们都是讨人喜欢的一些友好的大公司,像美国电信公司和有线电视公司等。他们都非常友好,并且提供各种各样的像 BBS、文件下载、MUDs (多玩家在线游戏)等的额外服务。
+
+我花了很多的时间在电脑商店购买配件,但是很多时候我一个女人家去那里会让店里的员工感到吃惊,我真的很无语了,这怎么就会让一些人看不惯了。我现成已经是一位58岁的老奶了,但是他们还是一样的看不惯我。我希望我这个让人讨厌的女人在我死之前能被他们所接受。
+
+那些商店的书架上摆满了比特杂志。有关比特杂志的历史信息可以在互联网档案库中查到。比特杂志是当地一家免费的杂志,有很多关于计算机方面的优秀的文章和大量的广告。可惜当时的广告没有网络版,因此大家不能再看到那些很有价值的关于计算机方面的详细信息了。你知道现在的广告商们有多么的抓狂吗?他们埋怨那些排斥广告的人,致使他们不得不在科技新闻中植入一些隐藏的广告。他们应该学习一下过去的广告模式,那时候的广告里有很多有价值的信息,大家都喜欢阅读。我从比特杂志和其它的电脑杂志的广告中学到了所有关于计算机硬件的知识。电脑购买者杂志更是非常好的学习资料,其中有上百页的广告和很多高质量的文章。
+
+
+![](https://opensource.com/sites/default/files/resize/march2002-300x387.jpg)
+
+比特杂志的出版者 Paul Harwood 开启了我的写作生涯。我的第一篇计算机专业性质的文章就是在比特杂志发表的。 Paul,仿佛你一直都在我的身旁,谢谢你。
+
+在互联网档案库中,关于比特杂志的分类广告信息已经几乎查询不到了。分类广告模式曾经给出版商带来巨大的收入。免费的分类广告网站 Craigslist 在这个领域独占鳌头,同时也扼杀了像比特杂志这种以传统的报纸和出版为主的杂志行业。
+
+其中有一些让我最难以忘怀的记忆就是一个12岁左右的吊儿郎当的小屁孩,匆匆忙忙地跑到我钟爱的电脑店里递给我一本初学者好书比特杂志,他脸上挂满了对我这种光鲜亮丽的女人所做工作的不屑和不理解的表情。我拆开杂志,指着其中一篇我写的关于Linux系统的文章给他看,他说“哦,确实是这样的,我明白了”。他尴尬的脸变成了那种正常生理上都不可能呈现的颜色,然后很仓促地夹着尾巴溜走了。(不是的,我亲爱的、诚实的读者们,他不是真的只有12岁,而应该是20来岁。他现在应该比以前更成熟懂事一些了吧!)
+
+###发现 Linux
+
+我第一次了解到 Linux 系统是在比特杂志上,大概在 1977年左右。我一开始用的一些 Linux操作系统版本是 Red Hat 5 和 Mandrake Linux。 Mandrake真是太棒了,它是第一款易安装型的Linux系统,并且还附带图形界面和声卡驱动,因此我可以快速玩转 Tux Racer 游戏。不像那时候大多数的有教育背景的 Linux 用户那样,因为我之前没接触过 Unix系统,所以我学习起来比较难。但是一切都还顺利吧,因为我学到的东西都很有用。相对于学习 Windows 系统来说,我在 Linux 系统里学到的东西都很实用,也没必要像在Windows环境中那样非得把任务拖放到DOS下来执行。
+
+玩转电脑真是充满了太多的乐趣,后来我转行成为计算机自由顾问,去帮助一些小型公司的IT部门把数据迁移到 Linux 服务器上,这迫使 Windows 系统或多或少的失去一些用武之地。通常情况下我们都是在背地里偷偷地做这些工作的,因为那段时期微软把 Linux 称为毒瘤,诬蔑 Linux 系统是一种共产主义用来削弱和吞噬我们身体里的珍贵血液的阴谋。
+
+### Linux 赢了
+
+我持续做了很多年的顾问工作,也做其它一些相关的工作,比如:电脑硬件修理和升级、布线、系统和网络管理,还有其它一些混合的包括 Apple、Windows、Linux 系统在内的网络工作。Apple 和 Windows 系统故意不兼容对方,因此这两个系统真的是最头疼,也最难整合到同一网络中。但是在Linux系统和其它开源软件中最有趣的一件事是总有一些人能够时时处理这些厂商之间的兼容性问题。
+
+现在已经大不同了。在系统间的互操作方面一直存在一些兼容性问题,而且也没有1级桌面的 Linux 系统 OEM 厂商。我觉得这是因为微软和苹果公司在零售行业在大力垄断造成的。也许他们这样做反而帮了我们帮大忙,因为我们可以从 ZaReason 和 System76 这样独立的 Linux 系统开发商得到更好的服务和更高性能的系统。他们对 Linux 系统都很专业,也特别欢迎我们这样的用户。
+
+Linux 除了在零售行业的桌面版系统占有一席之地以外,从嵌入式系统到超级计算机以及分布式计算系统等各个方面都占据着主要地位。开源技术掌握着软件行业的发展方向,所有的软件行业的前沿技术,比如容器、集群以及人工智能的发展都离不开开源技术的支持。从我的第一台老式的 386SX 电脑到现在,Linux 和开源技术都取得了巨大的进步。
+
+如果没有 Linux 系统和开源,这一切都不可能发生。
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/life/16/7/my-linux-story-carla-schroder
+
+作者:[Carla Schroder ][a]
+译者:[译者ID](https://github.com/rusking)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/carlaschroder
diff --git a/translated/talk/Torvalds2.0: Patricia Torvalds on computing, college, feminism and increasing diversity in tech.md b/translated/talk/Torvalds2.0: Patricia Torvalds on computing, college, feminism and increasing diversity in tech.md
new file mode 100644
index 0000000000..e650dfde93
--- /dev/null
+++ b/translated/talk/Torvalds2.0: Patricia Torvalds on computing, college, feminism and increasing diversity in tech.md	
@@ -0,0 +1,50 @@
+Torvalds2.0: Patricia Torvalds 谈“计算”,大学,女权主义和科技界的多元化
+![Image by : Photo by Becky Svartström. Modified by Opensource.com. CC BY-SA 4.0](http://opensource.com/sites/default/files/styles/image-full-size/public/images/life/osdc-lead-patriciatorvalds.png)
+Image by : Photo by Becky S. Modified by Opensource.com. [CC BY-SA 4.0][1]
+图片来源:照片来自Becky Svartström, 修改自Opensource.com.
+Patricia Torvalds 并不是那个在 Linux 和开源领域非常有名同样叫做 Torvalds 的人。
+![](http://opensource.com/sites/default/files/images/life-uploads/ptorvalds.png)
+18岁的时候,Patricia 已经是一个有多项科技成就并且拥有开源产业经验的女权主义者。紧接着,她把目光投入到在杜克大学地球科学学院工程学第一学年的学习中,以实习生的身份在位于美国奥勒冈州伯特兰市的 Puppet 实验室工作。但不久后,她又直接去了北卡罗纳州的达拉莫,开始秋季学期的大学学习。
+在这次独家采访中,Patricia 表示,使她对计算机科学和工程学感兴趣的(剧透警告:不是她的父亲)原因,包括高中时候对科技的偏爱,女权主义在她的生活中扮演了重要角色以及对科技多元化缺乏的思考。
+![](http://opensource.com/sites/default/files/images/life/Interview%20banner%20Q%26A.png)
+###什么东西使你钟情于学习计算机科学和工程学?###
+我在科技方面的兴趣的确产生于高中时代。我曾一度想投身于生物学,直到大约大学二年级的时候。大二结束以后,我在波特兰 VA 当网页设计实习生。与此同时,我参加了一个叫做“风险勘探”的工程学课程,在我大二学年的后期,我们把一个水下机器人送到了太平洋。但是,转折点大概是在青年时代的中期当我因被授予“NCWIT Aspiration in Computing"奖而被称作地区和民族英雄的时候出现的。
+这个奖项的获得我感觉确立了自己的兴趣。当然,我认为最重要的部分是我加入到一个所有获奖者在里面的 Facebook 群,里面那些已经获奖的女孩们简直难以置信的相互支持。由于在 XV 和 VA
+的工作,我在获奖前就已经确定了致力于计算机科学,但是和这些女孩的交谈更加坚定了这份兴趣,使之更加强壮。再后来,初高年级后期的时候教授 XV 也使我体会到工程学和计算机科学对我来说的确很有趣。
+###你打算学习什么?毕业以后你已经知道自己想干什么了吗?###
+我希望主修力学或电气科学以及计算机工程学和计算机科学还有女性学。毕业以后,我希望在一个支持或者创造科技为社会造福的公司工作,或者自己开公司。
+###我的女儿在高中有一门 Visual Basic的编程课。她是整个班上唯一的一个女生,并且以疲倦和痛苦的经历结束这门课程。你的经历是什么样的呢?###
+我的高中在高年级的时候开设计算机科学的课程,我也学习了 Visual Basic!这门课不是很糟糕,但我的确是20多个人的班级里唯一的三四个女生之一。其他的计算机课程似乎也有相似的性别比例差异。然而,我所在的高中极其小并且老师对科技非常支持和包容,所以我并没有感到厌倦。希望在未来的一些年里计算机方面的课程会变得更加多样化。
+###你的学校做了哪些促进科技的智举?它们如何能够变得更好?###
+我的高中学校给了我们长时间的机会接触到计算机,老师们会突然在不相关的课程上安排科技基础任务,比如有好多次任务,我们必须建一个供社会学习课程使用的网站,我认为这很棒因为它使我们每一个人都能接触到科技。机器人俱乐部也很活跃并且资金充足,但是非常小,我不是其中的成员。学校的科技/工程学项目中一个非常强大的组成部分是一门叫做”风险勘测“的学生教学工程学课程,这是一门需要亲自动手的课程,并且每年处理一个工程学或者计算机科学难题。我和我的一个同学在这儿教授了两年,在课程结束以后,有学生上来告诉我他们对从事工程学或者计算机科学感兴趣。
+然而,我的高中没有特别的关注于让年轻女性加入到这些项目中来,并且在人种上也没有呈现多样化。计算机课程和俱乐部大量的主要成员都是男性白人。这的确应该能够有所改善。
+###在成长过程中,你如何在家使用科技?###
+老实说,小的时候,我使用电脑(我的父亲设了一个跟踪装置,当我们上网一个小时就会断线)玩尼奥宠物和或者相似的游戏。我想我本可以毁坏跟踪装置或者在不连接网络的情况下玩游戏,但我没有这样做。我有时候也会和我的父亲做一些小的科学项目,我还记得我和他在电脑终端上打印出”Hello world"无数次。但是大多数时候,我都是和我的妹妹一起玩网络游戏,直到高中的时候才开始学习“计算”。
+###你在高中学校的女权俱乐部很积极,从这份经历中你学到了什么?现在对你来说什么女权问题是最重要的?###
+在高中二年级的后期,我和我的朋友一起建立了女权俱乐部。刚开始,我们受到了很多反对和抵抗,并且这从来就没有完全消失过。到我们毕业的时候,女权主义理想已经彻底成为了学校文化的一个部分。我们在学校做的女权主义工作通常是以一些比较直接的方式并集中于像着装要求这样一些问题。
+就我个人来说,我更集中于交叉地带的女权主义,把女权主义运用到缓解其他方面的压迫比如种族歧视和阶级歧视。Facebook 网页《Gurrilla Feminism》是交叉地带女权主义一个非常好的例子并且我从中学到了很多。我目前管理波特兰分支。
+在科技多样性方面女权主义对我也非常重要,尽管作为一名和科技世界有很强联系的上流社会女性,女权主义问题对我产生的影响相比其他人来说非常少,我所涉及的交叉地带女权主义也是同样的。出版集团比如《Model View Culture》非常鼓舞我,并且我很感激 Shanley Kane 所做的一切。
+###你会给想教他们的孩子学习编程的父母什么样的建议?###
+老实说,从没有人把我推向计算机科学或者工程学。正如我前面说的,在很长一段时间里,我想成为一名遗传学家。大二结束的那个夏天,我在 VA 当了一个夏季的网页设计实习生,这彻底改变了我之前的想法。所以我不知道我是否能够完整的回答这个问题。
+我的确认为真实的兴趣很重要。如果在我12岁的时候,我的父亲让我坐在一台电脑前,教我安装网站服务器,我认为我不会对计算机科学感兴趣。相反,我的父母给了我很多可以支配的自由让我去做自己想做的事情,绝大多数时候是为我的尼奥宠物游戏编糟糕的HTML网站。比我小的妹妹们没有一个对工程学或计算机科学感兴趣,我的父母也不在乎。我感到很幸运的是我的父母给了我和我的妹妹们鼓励和资源去探索自己的兴趣。
+仍然,在我成长过程中我也常说未来要和我的父亲做同样的职业,尽管我还不知道我父亲是干什么的,只知道他有一个很酷的工作。另外,中学的时候有一次,我告诉我的父亲这件事,然后他没有发表什么看法只是告诉我高中的时候不要想这事。所以我猜想这从一定程度上刺激了我。
+###对于开源社区的领导者们,你有什么建议给他们来吸引和维持更加多元化的贡献者?###
+我实际上在开源社区不是特别积极和活跃。和女性讨论“计算”我感觉更舒服。我是“NCWIT Aspirarion in Computing"网站的一名成员,这是我对科技有持久兴趣的一个重要方面,同样也包括Facebook群”Ladies Storm Hackathons".
+我认为对于吸引和维持多种多样有天赋的贡献者,安全空间很重要。我过去看到在一些开源社区有人发表关于女性歧视和种族主义的评论,人们指出这一问题随后该人被解职。我认为要维持一个专业的社区必须就骚扰事件和不正当行为有一个很高的标准。当然,人们已经有或者将有很多的选择关于在开源社区或其他任何社区能够表达什么。然而,如果社区领导人真的想吸引和维持多元化有天赋的人员,他们必须创造一个安全的空间并且把社区成员维持在很高的标准上。我也认为一些一些社区领导者不明白多元化的价值。很容易说明科技象征着精英社会,并且很多人被科技忽视的原因是他们不感兴趣,这一问题在很早的准备过程中就提出了。他们争论如果一个人在自己的工作上做得很好,那么他的性别或者民族还有性取向这些情况都变得不重要了。这很容易反驳,但我不想为错误找理由。我认为多元化缺失是一个错误,我们应该为之负责并尽力去改善这件事。
+--------------------------------------------------------------------------------
+
+via: http://opensource.com/life/15/8/patricia-torvalds-interview
+
+作者:[Rikki Endsley][a]
+译者:[ucasFL](https://github.com/ucasFL)
+校对:[校对者ID](https://github.com/校对者ID)
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]:http://opensource.com/users/rikki-endsley
+[1]:https://creativecommons.org/licenses/by-sa/4.0/
+[2]:https://puppetlabs.com/
+[3]:https://www.aspirations.org/
+[4]:https://www.facebook.com/guerrillafeminism
+[5]:https://modelviewculture.com/
+[6]:https://www.aspirations.org/
+[7]:https://www.facebook.com/groups/LadiesStormHackathons/
diff --git a/translated/talk/my-open-source-story/20160316 Growing a career alongside Linux.md b/translated/talk/my-open-source-story/20160316 Growing a career alongside Linux.md
deleted file mode 100644
index f7ddefb92c..0000000000
--- a/translated/talk/my-open-source-story/20160316 Growing a career alongside Linux.md	
+++ /dev/null
@@ -1,49 +0,0 @@
-培养一个 Linux 职业生涯
-==================================
-
-![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/OPENHERE_blue.png?itok=3eqp-7gT)
-
-我与 Linux 的故事开始于 1998 年,一直延续到今天。 当时我在 Gap 公司工作,管理着成千台运行着 [OS/2][1] 系统的台式机 ( 并且在随后的几年里是 [Warp 3.0][2])。 作为一个 OS/2 的使用者,那时我非常高兴。 随着这些台式机的嗡鸣,我们使用 Gap 开发的工具轻而易举地就能支撑起对成千的用户的服务支持。 然而,一切都将改变了。
-
-在 1998 年的 11 月, 我收到邀请加入一个新成立的公司,这家公司将专注于 Linux 上。 这就是后来非常出名的 [Linuxcare][2].
-
-### 我在 Linuxcare 的时光
-
-我曾经有接触过一些 Linux , 但我从未想过要把它提供给企业客户。仅仅几个月后 ( 从这里开始成为了空间和时间上的转折点 ), 我就在管理一整条线的业务,让企业获得他们的软件,硬件,甚至是证书认证等不同风格且在当时非常盛行的的 Linux 服务。
-
-我向我的客户提供像 IBM ,Dell ,HP 等产品以确保他们的硬件能够成功的运行 Linux 。 今天你们应该都听过许多关于在硬件上预载 Linux 的事, 但当时我被邀请到 Dell 去讨论有关于为即将到来的贸易展在笔记本电脑上运行 Linux 的相关事宜。  这是多么激动人心的时刻 !同时我们也在有效期的几年内支持 IBM 和 HP 等多项认证工作。
-
-Linux 变化得非常快,并且它总是这样。 它也获得了更多的关键设备的支持,比如声音,网络和图形。在这段时间, 我把个人使用的系统从基于 RPM 的系统换成了 [Debian][3] 。
-
-### 使用 Linux 的这些年
-
-几年前我在一些以 Linux 为硬设备,客户软件以及数据中心的公司工作。而在二十世纪中期的时候,那时我正在忙着做咨询,而那些在 Redmond 的大型软件公司正围绕着 Linux 做一些分析和验证以和自己的解决方案做对比。 我个人使用的系统一直改变,我仍会在任何能做到的时候运行 Debian 测试系统。
-
-我真的非常欣赏其发行版本的灵活性和永久更新状态。 Debian 是我所使用过的最有趣且拥有良好支持的发行版本且拥有最好的社区的版本之一。
-
-当我回首我使用 Linux 的这几年,我仍记得大约在二十世纪前期和中期的时候在圣何塞,旧金山,波士顿和纽约的那些 Linux 的展览会。在 Linuxcare 时我们总是会做一些有趣而且时髦的展览位,在那边逛的时候总会碰到一些老朋友。这一切工作都是需要付出代价的,所有的这一切都是在努力地强调使用 Linux 的乐趣。
-
-同时,虚拟化和云的崛起也让 Linux 变得更加有趣。 当我在 Linuxcare 的时候, 我们常和 Palo Alto 的一个约 30 人左右的小公司在一块。我们会开车到他们的办公处然后准备一个展览并且他们也会参与进来。 谁会想得到这个小小的开端会成就后来的 VMware ?
-
-我还有许多的故事,能认识这些人并和他们一起工作我感到很幸运。 Linux 在各方面都不断发展且变得尤为重要。 并且甚至随着它重要性的提升,它使用起来仍然非常有趣。 我认为它的开放性和可定制能力给它带来了大量的新用户,这也是让我感到非常震惊的一点。
-
-### 现在
-
-在过去的五年里我逐渐离开 Linux 的主流事物。 我所管理的大规模基础设施项目中包含着许多不同的操作系统 ( 包括非开源的和开源的 ), 但我的心一直以来都是和 Linux 在一起的。
-
-在使用 Linux 过程中的乐趣和不断进步是在过去的 18 年里一直驱动我的动力。我从 Linux 2.0 内核开始看着它变成现在的这样。 Linux 是一个卓越,有机且非常酷的东西。
-
---------------------------------------------------------------------------------
-
-via: https://opensource.com/life/16/3/my-linux-story-michael-perry
-
-作者:[Michael Perry][a]
-译者:[译者ID](https://github.com/chenxinlong)
-校对:[校对者ID](https://github.com/校对者ID)
-
-[a]: https://opensource.com/users/mpmilestogo
-[1]: https://en.wikipedia.org/wiki/OS/2
-[2]: https://archive.org/details/IBMOS2Warp3Collection
-[3]: https://en.wikipedia.org/wiki/Linuxcare
-[4]: https://www.debian.org/
-[5]: 
diff --git a/translated/talk/yearbook2015/20151208 6 creative ways to use ownCloud.md b/translated/talk/yearbook2015/20151208 6 creative ways to use ownCloud.md
deleted file mode 100644
index 5d81ed9cdf..0000000000
--- a/translated/talk/yearbook2015/20151208 6 creative ways to use ownCloud.md	
+++ /dev/null
@@ -1,95 +0,0 @@
-GHLandy Translated 
-
-使用 ownCloud 的六个创意方法
-================================================================================
-![Yearbook cover 2015](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/osdc-open-source-yearbook-lead1-inc0335020sw-201511-01.png)
-
-图片来源:Opensource.com
-
-[ownCloud][1] 是一个自我托管且开源的文件同步和共享服务上。就像 "big boys" Dropbox、Google Drive、Box 和其他的同类服务一样,ownCloud 可以让你访问自己的文件、日历、联系人和其他数据。你可以在自己设备之间进行任意数据(包括它自身的一部分)同步以及给其他人分享文件。然而,ownCloud 并非只能运行在它自己的开发商之中,试试[将 ownCloud 运行在其他服务器上][2]
-
-现在,一起来看看在 ownCloud 上的六件创意事件。其中一些是由于 ownCloud 的开源才得以完成,而另外的则是 ownCloud 自身特有的功能。
-
-### 1. 可扩展的 ownCloud 派集群 ###
-
-由于 ownCloud 是开源的,你可以选择将它运行在自己的服务器中,或者从你信任的服务器提供商那里获取空间——没必要将你的文件存储在大公司的服务器中,谁知他们将你的文件存储到哪里去。[点击此处查看部分 ownCloud 服务商][3],或者下载该服务软件到你的虚拟主机中[搭建自己的服务器][4].
-
-![](https://opensource.com/sites/default/files/images/life-uploads/banana-pi-owncloud-cluster.jpg)
-
-拍摄: Jörn Friedrich Dreyer. [CC BY-SA 4.0.][5]
-
-我们见过最具创意的事情就是组建 [香蕉派集群][6] 和 [树莓派集群][7]。ownCloud 的扩展性通常是成千上万的用户来完成的,这些人则将它往不同方向发展,通过大量的小型系统集群在一起,就可以创建出运行速度非常快的 ownCloud。酷毙了!
-
-### 2. 密码同步 ###
-
-为了让 ownCloud 更容易扩展,我们需要将它模块化,并拥有 [ownCloud app store][8]。然后你就可以在里边搜索音乐、视频播放器、日历、联系人、生产应用、游戏、应用框架等。
-
-仅从 200 多个可用应用中挑选一个是一件非常困难的事,但密码管理则是一个很好的特性。ownCloud app store 里边至少有三款这种应用:[Passwords][9]、[Secure Container][10] 和 [Passman][11]。
-
-![](https://opensource.com/sites/default/files/images/life-uploads/password.png)
-
-### 3. 随心所欲地存储文件 ###
-
-外部存储允许你通过接口将现有数据联系到 ownCloud,让你轻松访问存储在FTP、WebDAV、Amazon S3,甚至 Dropbox 和Google Drive。
-
-注:youtube 视频
-<iframe width="520" height="315" frameborder="0" allowfullscreen="" src="https://www.youtube.com/embed/uezzFDRnoPY"></iframe>
-
-DropBox 喜欢创建自己的 “围墙式花园”,只有注册用户之间才可以进行协作;假如你通过Google Drive 来分享文件,你的同伴也必须要有一个 Google 账号才可以访问的分享。通过 ownCloud 的外部存储功能,你可以轻松打破这些规则障碍。
-
-最有创意的就是把 Google Drive 和 Dropbox 添加为外部存储。这样你就可以无缝的使用它们,并使用不需要账户地链接把文件分享给和你协作的人。
-
-### 4. 下载以上传的文件 ###
-
-由于 ownCloud 的开源,人们可以不受公司需求限制地向它共享代码,增加新特性。共献者关注的往往是安全和隐私,所以 ownCloud 引入的特性常常比别人的要早,比如通过密码保护的公共链接和[设置失效期限][12]。
-
-现在,ownCloud 可以配置分享链接的读写权限了,这就是说链接的访问者可以无缝的编辑你分享给他们的文件(不管是否有密码保护),或者在不提供他们的私人数据来登录其他服务的情况下将文件上传到服务器。
-
-注:youtube 视频
-<iframe width="520" height="315" frameborder="0" allowfullscreen="" src="https://www.youtube.com/embed/3GSppxEhmZY"></iframe>
-
-对于有人想给你分享大体积的文件时,这个特性就非常有用了。相比于上传到第三方站点、然后给你发送一个连接、你再去下载文件(通常需要登录),ownCloud 仅需要上传文件到你提供的分享文件夹、你就可以买上获取到文件了。
-
-### 5. 免费却又安全的存储空间 ###
-
-之前就强调过,我们的代码贡献者最关注的就是安全和隐私,这就是 ownCloud 中有用于加密和解密存储数据的应用的原因。
-
-通过使用 ownCloud 将你的文件存储到 Dropbox 或者 Google Drive,则会违背控制数据以及保持数据隐私的原则。但是加密应用则刚好可以满足安全及隐私问题。在发送数据给这些提供商前进行数据加密,并在取回数据的时候进行解密,你的数据就会变得很安全。
-
-### 6. 在你的可控范围内分享文件 ###
-
-作为开源项目,ownCloud 没有必要自建 “围墙式花园”。进入联邦云共享:[developed and published by ownCloud][13] 协议使不同的文件同步和共享服务器可以彼此之间进行通信,并能够安全地传输文件。联邦云共享本身有一个有趣的故事:[22 所德国大学][14] 想要为自身的 500,000 学生建立一个庞大的云服务,但是每个大学都想控制自己学生数据。于是乎,我们需要一个可行性解决方案:也就是联邦云服务。该解决方案让让学生保持连接,使得他们可以无缝的协同工作。同时,每个大学的系统管理员保持着对自己学生创建的文件的控制权,如限制存储或者限制什么人、什么文件以及如何共享。
-
-注:youtube 视频
-<iframe width="520" height="315" frameborder="0" allowfullscreen="" src="https://www.youtube.com/embed/9-JEmlH2DEg"></iframe>
-
-并且,这项令人崇敬的技术并没有限制于德国的大学之间,而是每个 ownCloud 用户都能在自己的用户设置中找到自己的 [联邦云 ID][15],并将之分享给同伴。
-
-现在你明白了吧。仅六个方法,ownCloud 就能让人们完成特殊和特别的事。而是这一切成为可能的,就是 ownCloud 的开源 —— 设计用来释放你数据。
-
---------------------------------------------------------------------------------
-
-via: https://opensource.com/life/15/12/6-creative-ways-use-owncloud
-
-作者:[Jos Poortvliet][a]
-译者:[GHLandy](https://github.com/GHLandy)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]:https://opensource.com/users/jospoortvliet
-[1]:https://owncloud.com/
-[2]:https://blogs.fsfe.org/mk/new-stickers-and-leaflets-no-cloud-and-e-mail-self-defense/
-[3]:https://owncloud.org/providers
-[4]:https://owncloud.org/install/#instructions-server
-[5]:https://creativecommons.org/licenses/by-sa/4.0/
-[6]:http://www.owncluster.de/
-[7]:https://christopherjcoleman.wordpress.com/2013/01/05/host-your-owncloud-on-a-raspberry-pi-cluster/
-[8]:https://apps.owncloud.com/
-[9]:https://apps.owncloud.com/content/show.php/Passwords?content=170480
-[10]:https://apps.owncloud.com/content/show.php/Secure+Container?content=167268
-[11]:https://apps.owncloud.com/content/show.php/Passman?content=166285
-[12]:https://owncloud.com/owncloud45-community/
-[13]:http://karlitschek.de/2015/08/announcing-the-draft-federated-cloud-sharing-api/
-[14]:https://owncloud.com/customer/sciebo/
-[15]:https://owncloud.org/federation/
diff --git a/translated/talk/yearbook2015/20160306 5 Favorite Open Source Django Packages.md b/translated/talk/yearbook2015/20160306 5 Favorite Open Source Django Packages.md
deleted file mode 100644
index 81e999fd80..0000000000
--- a/translated/talk/yearbook2015/20160306 5 Favorite Open Source Django Packages.md	
+++ /dev/null
@@ -1,164 +0,0 @@
-5个最受喜爱的开源Django包
-================================================================================
-![Yearbook cover 2015](https://opensource.com/sites/default/files/styles/image-full-size/public/u23316/osdc-open-source-yearbook-lead8.png?itok=0_5-hdFE)
-
-图片来源:Opensource.com
-
-_Jacob Kaplan-Moss和Frank Wiles也参与了本文的写作。_
-
-Django围绕“[可重用应用][1]”观点建立:自我包含了提供可重复使用特性的包。你可以将这些可重用应用组装起来,在加上适用于你的网站的特定代码,来搭建你自己的网站。Django具有一个丰富多样的、由可供你使用的可重用应用组建起来的生态系统——PyPI列出了[超过8000个 Django 应用][2]——可你该如何知道哪些是最好的呢?
-
-为了节省你的时间,我们总结了五个最受喜爱的 Django 应用。它们是:
-- [Cookiecutter][3]: 建立 Django 网站的最佳方式。
-- [Whitenoise][4]: 最棒的静态资源服务器。
-- [Django Rest Framework][5]: 使用 Django 开发 REST API 的最佳方式。
-- [Wagtail][6]: 基于 Django 的最佳内容管理系统。
-- [django-allauth][7]: 提供社交账户登录的最佳应用(如 Twitter, Facebook, GitHub 等)。
-
-我们同样推荐你查看 [Django Packages][8],一个可重用 Django 应用的目录。Django Packages 将 Django 应用组织成“表格”,你可以在功能相似的不同应用之间进行比较并做出选择。你可以查看每个包中提供的特性,和使用统计情况。(比如:这是[ REST 工具的表格][9],也许可以帮助你理解我们为何推荐 Django REST Framework.
-
-## 为什么你应该相信我们?
-
-我们使用 Django 的时间比几乎其他人都长。在 Django 发布之前,我们当中的两个人(Frank 和 Jacob)在 [Lawrence Journal-World][10] (Django 的发源地)工作(事实上他们两人推动了 Django 开源发布的进程)。我们在过去的八年当中运行着一个咨询公司,来建议公司使用 Django 去将事情做到最好。
-
-所以,我们见证了Django项目和社群的完整历史,我们见证了流行软件包的兴起和没落。在我们三个中,我们可能私下试用了8000个应用中的一半以上,或者我们知道谁试用过这些。我们对如何使应用变得坚实可靠有着深刻的理解,并且我们对给予这些应用持久力量的来源也有不错的理解。
-
-## 建立Django网站的最佳方式:[Cookiecutter][3]
-
-建立一个新项目或应用总是有些痛苦。你可以用Django内建的 `startproject`。不过,如果你像我们一样,你可能会对你的办事方式很挑剔。Cookiecutter 为你提供了一个快捷简单的方式来构建易于使用的项目或应用模板,从而解决了这个问题。一个简单的例子:键入 `pip install cookiecutter`,然后在命令行中运行以下命令:
-
-```bash
-$ cookiecutter https://github.com/marcofucci/cookiecutter-simple-django
-```
-
-接下来你需要回答几个简单的问题,比如你的项目名称、目录、作者名字、E-Mail和其他几个关于配置的小问题。这些能够帮你补充项目相关的细节。我们使用最最原始的 "_foo_" 作为我们的目录名称。所以 cokkiecutter 在子目录 "_foo_" 下建立了一个简单的 Django 项目。
-
-如果你在"_foo_"项目中闲逛,你会看见你刚刚选择的其它设置已通过模板,连同子目录一同嵌入到文件当中。这个“模板”在我们刚刚在执行 `cookiecutter` 命令时输入的 Github 仓库 URL 中定义。这个样例工程使用了一个 Github 远程仓库作为模板;不过你也可以使用本地的模板,这在建立非重用项目时非常有用。
-
-我们认为 cookiecutter 是一个极棒的 Django 包,但是,事实上其实它在面对纯 Python 甚至非 Python 相关需求时也极为有用。你能够将所有文件依你所愿精确摆放在任何位置上,使得 cookiecutter 成为了一个简化工作流程的极佳工具。
-
-## 最棒的静态资源服务器:[Whitenoise][4]
-
-多年来,托管网站的静态资源——图片、Javascript、CSS——都是一件很痛苦的事情。Django 内建的 [django.views.static.serve][11] 视图,就像Django文章所述的那样,“在生产环境中不可靠,所以只应为开发环境的提供辅助功能。”但使用一个“真正的” Web 服务器,如 NGINX 或者借助 CDN 来托管媒体资源,配置起来会相当困难。
-
-Whitenoice 很简洁地解决了这个问题。它可以像在开发环境那样轻易地在生产环境中设置静态服务器,并且针对生产环境进行了加固和优化。它的设置方法极为简单:
-
-1. 确保你在使用 Django 的 [contrib.staticfiles][12] 应用,并确认你在配置文件中正确设置了 `STATIC_ROOT` 变量。
-
-2. 在 `wsgi.py` 文件中启用 Whitenoise:
-
-   ```python
-   from django.core.wsgi import get_wsgi_application
-   from whitenoise.django import DjangoWhiteNoise
-
-   application = get_wsgi_application()
-   application = DjangoWhiteNoise(application)
-   ```
-
-配置它真的就这么简单!对于大型应用,你可能想要使用一个专用的媒体服务器和/或一个 CDN,但对于大多数小型或中型 Django 网站,Whitenoise 已经足够强大。
-
-如需查看更多关于 Whitenoise 的信息,[请查看文档][13]。
-
-## 开发REST API的最佳工具:[Django REST Framework][5]
-
-REST API 正在迅速成为现代 Web 应用的标准功能。与一个 API 进行简短的会话,你只需使用 JSON 而不是 HTML,当然你可以只用 Django 做到这些。你可以制作自己的视图,设置合适的 `Content-Type`, 然后返回 JSON 而不是渲染后的 HTML 响应。这是在像 [Django Rest Framework][14](下称DRF)这样的API框架发布之前,大多数人所做的。
-
-如果你对 Django 的视图类很熟悉,你会觉得使用DRF构建REST API与使用它们很相似,不过 DRF 只针对特定 API 使用场景而设计。在一般 API 设计中,你会用到它的不少代码,所以我们强调了一些 DRF 的特性来使你更快地接受它,而不是去看一份让你兴奋的示例代码:
-
-* 可自动预览的 API 可以使你的开发和人工测试轻而易举。你可以查看 DRF 的[示例代码][15]。你可以查看 API 响应,并且它支持 POST/PUT/DELETE 类型的操作,不需要你做任何事。
-
-
-* 认证方式易于迁移,如OAuth, Basic Auth, 或API Tokens.
-* 内建请求速度限制。
-* 当与 [django-rest-swagger][16] 结合时,API文档几乎可以自动生成。
-* 第三方库拥有广泛的生态。
-
-当然你可以不依赖 DRF 来构建 API,但我们无法推测你不开始使用 DRF 的原因。就算你不使用 DRF 的全部特性,使用一个成熟的视图库来构建你自己的 API 也会使你的 API 更加一致、完全,更能提高你的开发速度。如果你还没有开始使用 DRF, 你应该找点时间去体验一下。
-
-## 以 Django 为基础的最佳 CMS:[Wagtail][6]
-
-Wagtail是当下Django CMS(内容管理系统)世界中最受人青睐的应用,并且它的热门有足够的理由。就想大多数的 CMS 一样,它具有极佳的灵活性,可以通过简单的 Django 模型来定义不同类型的页面及其内容。使用它,你可以从零开始,在几个小时而不是几天之内来和建造一个基本可以运行的内容管理系统。举一个小例子,为你公司的员工定义一个页面类型可以像下面一样简单:
-
-```python
-from wagtail.wagtailcore.models import Page
-from wagtail.wagtailcore.fields import RichTextField
-from wagtail.wagtailadmin.edit_handlers import FieldPanel, MultiFieldPanel
-from wagtail.wagtailimages.edit_handlers import ImageChooserPanel 
-
-class StaffPage(Page):
-    name = models.CharField(max_length=100)
-    hire_date = models.DateField()
-    bio = models.RichTextField()
-    email = models.EmailField()
-    headshot = models.ForeignKey('wagtailimages.Image', null=True, blank=True) 
-    content_panels = Page.content_panels + [
-                                FieldPanel('name'),
-                                FieldPanel('hire_date'),
-                                FieldPanel('email'),
-                                FieldPanel('bio',classname="full"),
-                                ImageChoosePanel('headshot'),
-                                ] 
-```
-
-然而,Wagtail 真正出彩的地方在于它的灵活性及其易于使用的现代化管理页面。你可以控制不同类型的页面在哪网站的哪些区域可以访问,为页面添加复杂的附加逻辑,还可以极为方便地取得标准的适应/审批工作流。在大多数 CMS 系统中,你会在开发时在某些点上遇到困难。而使用 Wagtail 时,我们经过不懈努力找到了一个突破口,使得让我们轻易地开发出一套简洁稳定的系统,使得程序完全依照我们的想法运行。如果你对此感兴趣,我们写了一篇[深入理解 Wagtail][17].
-
-## 提供社交账户登录的最佳工具:[django-allauth][7]
-
-django-allauth 是一个能够解决你的注册和认证需求的、可重用的Django应用。无论你需要构建本地注册系统还是社交账户注册系统,django-allauth 都能够帮你做到。
-
-这个应用支持多种认证体系,比如用户名或电子邮件。一旦用户注册成功,它可以提供从零到电子邮件认证的多种账户验证的策略。同时,它也支持多种社交账户和电子邮件账户关联。它还支持可插拔的注册表单,可让用户在注册时回答一些附加问题。
-
-django-allauth 支持多于 20 种认证提供者,包括 Facebook, Github, Google 和 Twitter。如果你发现了一个它不支持的社交网站,那很有可能有一款第三方插件提供该网站的接入支持。这个项目还支持自定义后台开发,可以支持自定义的认证方式。
-
-django-allauth 易于配置,且有[完善的文档][18]。该项目通过了很多测试,所以你可以相信它的所有部件都会正常运作。
-
-你有最喜爱的 Django 包吗?请在评论中告诉我们。
-
-## 关于作者
-
-![Photo](https://opensource.com/sites/default/files/styles/profile_pictures/public/pictures/main-one-i-use-everywhere.png?itok=66GC-D1q)
-
-Jeff Triplett
-
-劳伦斯,堪萨斯州  
-<http://www.jefftriplett.com/>
-
-我在 2007 年搬到了堪萨斯州的劳伦斯,在 Django 的发源地—— Lawrence Journal-World 工作。我现在在劳伦斯市的 [Revolution Systems (Revsys)][19] 工作,做一位开发者兼顾问。
-
-我是[北美 Django 运动基金会(DEFNA)][20]的联合创始人,2015 和 2016 年 [DjangoCon US][21] 的会议主席,而且我在 Django 的发源地劳伦斯参与组织了 [Django Birthday][22] 来庆祝 Django 的 10 岁生日。
-
-我是当地越野跑小组的成员,我喜欢篮球,我还喜欢梦见自己随着一道气流游遍美国。
-
---------------------------------------------------------------------------------
-
-via: https://opensource.com/business/15/12/5-favorite-open-source-django-packages
-
-作者:[Jeff Triplett][a]
-译者:[StdioA](https://github.com/StdioA)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]:https://opensource.com/users/jefftriplett
-[1]:https://docs.djangoproject.com/en/1.8/intro/reusable-apps/
-[2]:https://pypi.python.org/pypi?:action=browse&c=523
-[3]:https://github.com/audreyr/cookiecutter
-[4]:http://whitenoise.evans.io/en/latest/base.html
-[5]:http://www.django-rest-framework.org/
-[6]:https://wagtail.io/
-[7]:http://www.intenct.nl/projects/django-allauth/
-[8]:https://www.djangopackages.com/
-[9]:https://www.djangopackages.com/grids/g/rest/
-[10]:http://www2.ljworld.com/news/2015/jul/09/happy-birthday-django/
-[11]:https://docs.djangoproject.com/en/1.8/ref/views/#django.views.static.serve
-[12]:https://docs.djangoproject.com/en/1.8/ref/contrib/staticfiles/
-[13]:http://whitenoise.evans.io/en/latest/index.html
-[14]:http://www.django-rest-framework.org/
-[15]:http://restframework.herokuapp.com/
-[16]:http://django-rest-swagger.readthedocs.org/en/latest/index.html
-[17]:https://opensource.com/business/15/5/wagtail-cms
-[18]:http://django-allauth.readthedocs.org/en/latest/
-[19]:http://www.revsys.com/
-[20]:http://defna.org/
-[21]:https://2015.djangocon.us/
-[22]:https://djangobirthday.com/
diff --git a/translated/tech/20151220 GCC-Inline-Assembly-HOWTO.md b/translated/tech/20151220 GCC-Inline-Assembly-HOWTO.md
deleted file mode 100644
index e0e1fc6d50..0000000000
--- a/translated/tech/20151220 GCC-Inline-Assembly-HOWTO.md	
+++ /dev/null
@@ -1,632 +0,0 @@
-* * *
-
-#  GCC 内联汇编 HOWTO
-
-v0.1, 01 March 2003.
-* * *
-
-_本 HOWTO 文档将讲解 GCC 提供的内联汇编特性的用途和用法。对于阅读这篇文章,这里只有两个前提要求,很明显,就是 x86 汇编语言和 C 语言的基本认识。_
-
-* * *
-
-## 1. 简介
-
-## 1.1 版权许可
-
-Copyright (C)2003 Sandeep S.
-
-本文档自由共享;你可以重新发布它,并且/或者在遵循自由软件基金会发布的 GNU 通用公共许可证下修改它;或者该许可证的版本 2 ,或者(按照你的需求)更晚的版本。
-
-发布这篇文档是希望它能够帮助别人,但是没有任何保证;甚至不包括可售性和适用于任何特定目的的保证。关于更详细的信息,可以查看 GNU 通用许可证。
-
-## 1.2 反馈校正
-
-请将反馈和批评一起提交给 [Sandeep.S](mailto:busybox@sancharnet.in) 。我将感谢任何一个指出本文档中错误和不准确之处的人;一被告知,我会马上改正它们。
-
-## 1.3 致谢
-
-我对提供如此棒的特性的 GNU 人们表示真诚的感谢。感谢 Mr.Pramode C E 所做的所有帮助。感谢在 Govt Engineering College 和 Trichur 的朋友们的精神支持和合作,尤其是 Nisha Kurur 和 Sakeeb S 。 感谢在 Gvot Engineering College 和 Trichur 的老师们的合作。
-
-另外,感谢 Phillip ,  Brennan Underwood 和 colin@nyx.net ;这里的许多东西都厚颜地直接取自他们的工作成果。
-
-* * *
-
-## 2. 概览
-
-在这里,我们将学习 GCC 内联汇编。这内联表示的是什么呢?
-
-我们可以要求编译器将一个函数的代码插入到调用者代码中函数被实际调用的地方。这样的函数就是内联函数。这听起来和宏差不多?这两者确实有相似之处。
-
-内联函数的优点是什么呢?
-
-这种内联方法可以减少函数调用开销。同时如果所有实参的值为常量,它们的已知值可以在编译期允许简化,因此并非所有的内联函数代码都需要被包含。代码大小的影响是不可预测的,这取决于特定的情况。为了声明一个内联函数,我们必须在函数声明中使用 `inline` 关键字。
-
-现在我们正处于一个猜测内联汇编到底是什么的点上。它只不过是一些写为内联函数的汇编程序。在系统编程上,它们方便、快速并且极其有用。我们主要集中学习(GCC)内联汇编函数的基本格式和用法。为了声明内联汇编函数,我们使用 `asm` 关键词。
-
-内联汇编之所以重要,主要是因为它可以操作并且使其输出通过 C 变量显示出来。正是因为此能力, "asm" 可以用作汇编指令和包含它的 C 程序之间的接口。 
-
-* * *
-
-## 3. GCC 汇编语法
-
-GCC , Linux上的 GNU C 编译器,使用 **AT&T** / **UNIX** 汇编语法。在这里,我们将使用 AT&T 语法 进行汇编编码。如果你对 AT&T 语法不熟悉的话,请不要紧张,我会教你的。AT&T 语法和 Intel 语法的差别很大。我会给出主要的区别。
-
-1.  源操作数和目的操作数顺序 
-
-	AT&T 语法的操作数方向和 Intel 语法的刚好相反。在Intel 语法中,第一操作数为目的操作数,第二操作数为源操作数,然而在 AT&T 语法中,第一操作数为源操作数,第二操作数为目的操作数。也就是说,
-
-	Intel 语法中的 "Op-code dst src" 变为 
-	
-	AT&T 语法中的 "Op-code src dst"。
-
-2.  寄存器命名 
-
-	寄存器名称有 % 前缀,即如果必须使用 eax,它应该用作 %eax。
-
-3.  立即数
-
-    AT&T 立即数以 ’$’ 为前缀。静态 "C" 变量 也使用 ’$’ 前缀。在 Intel 语法中,十六进制常量以 ’h’ 为后缀,然而AT&T不使用这种语法,这里我们给常量添加前缀 ’0x’。所以,对于十六进制,我们首先看到一个 ’$’,然后是 ’0x’,最后才是常量。
-
-4.  操作数大小
-
-	在 AT&T 语法中,存储器操作数的大小取决于操作码名字的最后一个字符。操作码后缀 ’b’ 、’w’、’l’分别指明了字节(byte)(8位)、字(word)(16位)、长型(long)(32位)存储器引用。Intel 语法通过给存储器操作数添加’byte ptr’、 ’word ptr’ 和 ’dword ptr’前缀来实现这一功能。
-
-	因此,Intel的 "mov al, byte ptr foo" 在 AT&T 语法中为 "movb foo, %al"。
-
-5.	存储器操作数
-	
-	在 Intel 语法中,基址寄存器包含在 ’[’ 和 ’]’ 中,然而在 AT&T 中,它们变为 ’(’ 和 ’)’。另外,在 Intel 语法中, 间接内存引用为
-
-    section:[base + index*scale + disp], 在 AT&T中变为 
-
-    section:disp(base, index, scale)。
-
-	需要牢记的一点是,当一个常量用于 disp 或 scale,不能添加’$’前缀。
-
-现在我们看到了 Intel 语法和 AT&T 语法之间的一些主要差别。我仅仅写了它们差别的一部分而已。关于更完整的信息,请参考 GNU 汇编文档。现在为了更好地理解,我们可以看一些示例。
-
-> `
-> 
-> <pre>+------------------------------+------------------------------------+
-> |       Intel Code             |      AT&T Code                     |
-> +------------------------------+------------------------------------+
-> | mov     eax,1                |  movl    $1,%eax                   |   
-> | mov     ebx,0ffh             |  movl    $0xff,%ebx                |   
-> | int     80h                  |  int     $0x80                     |   
-> | mov     ebx, eax             |  movl    %eax, %ebx                |
-> | mov     eax,[ecx]            |  movl    (%ecx),%eax               |
-> | mov     eax,[ebx+3]          |  movl    3(%ebx),%eax              | 
-> | mov     eax,[ebx+20h]        |  movl    0x20(%ebx),%eax           |
-> | add     eax,[ebx+ecx*2h]     |  addl    (%ebx,%ecx,0x2),%eax      |
-> | lea     eax,[ebx+ecx]        |  leal    (%ebx,%ecx),%eax          |
-> | sub     eax,[ebx+ecx*4h-20h] |  subl    -0x20(%ebx,%ecx,0x4),%eax |
-> +------------------------------+------------------------------------+
-> </pre>
-> 
-> `
-
-* * *
-
-## 4. 基本内联
-
-基本内联汇编的格式非常直接了当。它的基本格式为
-
-`asm("汇编代码");`
-
-示例
-
-> `
-> 
-> * * *
-> 
-> <pre>asm("movl %ecx %eax"); /* 将 ecx 寄存器的内容移至 eax  */
-> __asm__("movb %bh (%eax)"); /* 将 bh 的一个字节数据 移至 eax 寄存器指向的内存 */
-> </pre>
-> 
-> * * *
-> 
-> `
-
-你可能注意到了这里我使用了 `asm ` 和 `__asm__`。这两者都是有效的。如果关键词 `asm` 和我们程序的一些标识符冲突了,我们可以使用 `__asm__`。如果我们的指令多余一条,我们可以写成一行,并用括号括起,也可以为每条指令添加 ’\n’ 和 ’\t’ 后缀。这是因为gcc将每一条当作字符串发送给 **as**(GAS)( GAS 即 GNU 汇编器 ——译者注),并且通过使用换行符/制表符发送正确地格式化行给汇编器。
-
-示例
-
-> `
-> 
-> * * *
-> 
-> <pre> __asm__ ("movl %eax, %ebx\n\t"
->           "movl $56, %esi\n\t"
->           "movl %ecx, $label(%edx,%ebx,$4)\n\t"
->           "movb %ah, (%ebx)");
-> </pre>
-> 
-> * * *
-> 
-> `
-
-如果在代码中,我们涉及到一些寄存器(即改变其内容),但在没有固定这些变化的情况下从汇编中返回,这将会导致一些不好的事情。这是因为 GCC 并不知道寄存器内容的变化,这会导致问题,特别是当编译器做了某些优化。在没有告知 GCC 的情况下,它将会假设一些寄存器存储了我们可能已经改变的变量的值,它会像什么事都没发生一样继续运行(什么事都没发生一样是指GCC不会假设寄存器装入的值是有效的,当退出改变了寄存器值的内联汇编后,寄存器的值不会保存到相应的变量或内存空间 ——译者注)。我们所可以做的是使用这些没有副作用的指令,或者当我们退出时固定这些寄存器,或者等待程序崩溃。这是为什么我们需要一些扩展功能。扩展汇编正好给我们提供了那样的功能。
-
-* * *
-
-## 5. 扩展汇编
-
-在基本内联汇编中,我们只有指令。然而在扩展汇编中,我们可以同时指定操作数。它允许我们指定输入寄存器、输出寄存器以及修饰寄存器列表。GCC 不强制用户必须指定使用的寄存器。我们可以把头疼的事留给 GCC ,这可能可以更好地适应 GCC 的优化。不管怎樣,基本格式为:
-
-> `
-> 
-> * * *
-> 
-> <pre>       asm ( 汇编程序模板 
->            : 输出操作数					/* 可选的 */
->            : 输入操作数                   /* 可选的 */
->            : 修饰寄存器列表			    /* 可选的 */
->            );
-> </pre>
-> 
-> * * *
-> 
-> `
-
-汇编程序模板由汇编指令组成.每一个操作数由一个操作数约束字符串所描述,其后紧接一个括弧括起的 C 表达式。冒号用于将汇编程序模板和第一个输出操作数分开,另一个(冒号)用于将最后一个输出操作数和第一个输入操作数分开,如果存在的话。逗号用于分离每一个组内的操作数。总操作数的数目限制在10个,或者机器描述中的任何指令格式中的最大操作数数目,以较大者为准。
-
-如果没有输出操作数但存在输入操作数,你必须将两个连续的冒号放置于输出操作数原本会放置的地方周围。
-
-示例:
-
-> `
-> 
-> * * *
-> 
-> <pre>        asm ("cld\n\t"
->              "rep\n\t"
->              "stosl"
->              : /* 无输出寄存器 */
->              : "c" (count), "a" (fill_value), "D" (dest)
->              : "%ecx", "%edi" 
->              );
-> </pre>
-> 
-> * * *
-> 
-> `
-
-现在,这段代码是干什么的?以上的内联汇编是将 `fill_value` 值 连续 `count` 次 拷贝到 寄存器 `edi` 所指位置(每执行stosl一次,寄存器 edi 的值会递增或递减,这取决于是否设置了 direction 标志,因此以上代码实则初始化一个内存块 ——译者注)。 它也告诉 gcc 寄存器 `ecx` 和 `edi` 一直无效(原文为 eax ,但代码修饰寄存器列表中为 ecx,因此这可能为作者的纰漏 ——译者注)。为了使扩展汇编更加清晰,让我们再看一个示例。
-
-> `
-> 
-> * * *
-> 
-> <pre>        
->         int a=10, b;
->         asm ("movl %1, %%eax; 
->               movl %%eax, %0;"
->              :"=r"(b)        /* 输出 */
->              :"r"(a)         /* 输入 */
->              :"%eax"         /* 修饰寄存器 */
->              );       
-> </pre>
-> 
-> * * *
-> 
-> `
-
-这里我们所做的是使用汇编指令使 ’b’ 变量的值等于 ’a’ 变量的值。一些有意思的地方是:
-
-*	"b" 为输出操作数,用 %0 引用,并且 "a" 为输入操作数,用 %1 引用。
-*	"r" 为操作数约束。之后我们会更详细地了解约束(字符串)。目前,"r" 告诉 GCC 可以使用任一寄存器存储操作数。输出操作数约束应该有一个约束修饰符 "=" 。这修饰符表明它是一个只读的输出操作数。
-*	寄存器名字以两个%为前缀。这有利于 GCC 区分操作数和寄存器。操作数以一个 % 为前缀。
-*	第三个冒号之后的修饰寄存器 %eax 告诉 GCC %eax的值将会在 "asm" 内部被修改,所以 GCC 将不会使用此寄存器存储任何其他值。
-
-当 "asm" 执行完毕, "b" 变量会映射到更新的值,因为它被指定为输出操作数。换句话说, "asm" 内 "b" 变量的修改 应该会被映射到 "asm" 外部。
-
-现在,我们可以更详细地看看每一个域。
-
-## 5.1 汇编程序模板
-
-汇编程序模板包含了被插入到 C 程序的汇编指令集。其格式为:每条指令用双引号圈起,或者整个指令组用双引号圈起。同时每条指令应以分界符结尾。有效的分界符有换行符(\n)和逗号(;)。’\n’ 可以紧随一个制表符(\t)。我们应该都明白使用换行符或制表符的原因了吧?和 C 表达式对应的操作数使用 %0、%1 ... 等等表示。
-
-## 5.2 操作数
-
-C 表达式用作 "asm" 内的汇编指令操作数。作为第一双引号内的操作数约束,写下每一操作数。对于输出操作数,在引号内还有一个约束修饰符,其后紧随一个用于表示操作数的 C 表达式。即,
-
-"约束字符串"(C 表达式),它是一个通用格式。对于输出操作数,还有一个额外的修饰符。约束字符串主要用于决定操作数的寻找方式,同时也用于指定使用的寄存器。
-
-如果我们使用的操作数多于一个,那么每一个操作数用逗号隔开。
-
-在汇编程序模板,每个操作数用数字引用。编号方式如下。如果总共有 n 个操作数(包括输入和输出操作数),那么第一个输出操作数编号为 0 ,逐项递增,并且最后一个输入操作数编号为 n - 1 。操作数的最大数目为前一节我们所看到的那样。
-
-输出操作数表达式必须为左值。输入操作数的要求不像这样严格。它们可以为表达式。扩展汇编特性常常用于编译器自己不知道其存在的机器指令 ;-)。如果输出表达式无法直接寻址(例如,它是一个位域),我们的约束字符串必须给定一个寄存器。在这种情况下,GCC 将会使用该寄存器作为汇编的输出,然后存储该寄存器的内容到输出。
-
-正如前面所陈述的一样,普通的输出操作数必须为只写的; GCC 将会假设指令前的操作数值是死的,并且不需要被(提前)生成。扩展汇编也支持输入-输出或者读-写操作数。
-
-所以现在我们来关注一些示例。我们想要求一个数的5次方结果。为了计算该值,我们使用 `lea` 指令。
-
-> `
-> 
-> * * *
-> 
-> <pre>        asm ("leal (%1,%1,4), %0"
->              : "=r" (five_times_x)
->              : "r" (x) 
->              );
-> </pre>
-> 
-> * * *
-> 
-> `
-
-这里我们的输入为x。我们不指定使用的寄存器。 GCC 将会选择一些输入寄存器,一个输出寄存器,并且做我们期望的事。如果我们想要输入和输出存在于同一个寄存器里,我们可以要求 GCC 这样做。这里我们使用那些读-写操作数类型。这里我们通过指定合适的约束来实现它。
-
-> `
-> 
-> * * *
-> 
-> <pre>        asm ("leal (%0,%0,4), %0"
->              : "=r" (five_times_x)
->              : "0" (x) 
->              );
-> </pre>
-> 
-> * * *
-> 
-> `
-
-现在输出和输出操作数位于同一个寄存器。但是我们无法得知是哪一个寄存器。现在假如我们也想要指定操作数所在的寄存器,这里有一种方法。
-
-> `
-> 
-> * * *
-> 
-> <pre>        asm ("leal (%%ecx,%%ecx,4), %%ecx"
->              : "=c" (x)
->              : "c" (x) 
->              );
-> </pre>
-> 
-> * * *
-> 
-> `
-
-在以上三个示例中,我们并没有添加任何寄存器到修饰寄存器里,为什么?在头两个示例, GCC 决定了寄存器并且它知道发生了什么改变。在最后一个示例,我们不必将 'ecx' 添加到修饰寄存器列表(原文修饰寄存器列表拼写有错,这里已修正 ——译者注), gcc 知道它表示x。因此,因为它可以知道 `ecx` 的值,它就不被当作修饰的(寄存器)了。
-
-## 5.3 修饰寄存器列表
-
-一些指令会破坏一些硬件寄存器。我们不得不在修饰寄存器中列出这些寄存器,即汇编函数内第三个 ’**:**’ 之后的域。这可以通知 gcc 我们将会自己使用和修改这些寄存器。所以 gcc 将不会假设存入这些寄存器的值是有效的。我们不用在这个列表里列出输入输出寄存器。因为 gcc 知道 "asm" 使用了它们(因为它们被显式地指定为约束了)。如果指令隐式或显式地使用了任何其他寄存器,(并且寄存器不能出现在输出或者输出约束列表里),那么不得不在修饰寄存器列表中指定这些寄存器。
-
-如果我们的指令可以修改状态寄存器,我们必须将 "cc" 添加进修饰寄存器列表。
-
-如果我们的指令以不可预测的方式修改了内存,那么需要将 "memory" 添加进修饰寄存器列表。这可以使 GCC 不会在汇编指令间保持缓存于寄存器的内存值。如果被影响的内存不在汇编的输入或输出列表中,我们也必须添加 **volatile** 关键词。
-
-我们可以按我们的需求多次读写修饰寄存器。考虑一个模板内的多指令示例;它假设子例程 _foo 接受寄存器 `eax` 和 `ecx` 里的参数。
-
-> `
-> 
-> * * *
-> 
-> <pre>        asm ("movl %0,%%eax;
->               movl %1,%%ecx;
->               call _foo"
->              : /* no outputs */
->              : "g" (from), "g" (to)
->              : "eax", "ecx"
->              );
-> </pre>
-> 
-> * * *
-> 
-> `
-
-## 5.4 Volatile ...?
-
-如果你熟悉内核源码或者其他像内核源码一样漂亮的代码,你一定见过许多声明为 `volatile` 或者 `__volatile__`的函数,其跟着一个 `asm` 或者 `__asm__`。我之前提过关键词 `asm` 和 `__asm__`。那么什么是 `volatile`呢?
-
-如果我们的汇编语句必须在我们放置它的地方执行(即,不能作为一种优化被移出循环语句),将关键词 `volatile` 放置在 asm 后面,()的前面。因为为了防止它被移动、删除或者其他操作,我们将其声明为
-
-`asm volatile ( ... : ... : ... : ...);`
-
-当我们必须非常谨慎时,请使用 `__volatile__`。
-
-如果我们的汇编只是用于一些计算并且没有任何副作用,不使用 `volatile` 关键词会更好。不使用 `volatile` 可以帮助 gcc 优化代码并使代码更漂亮。
-
-
-在 `Some Useful Recipes` 一节中,我提供了多个内联汇编函数的例子。这儿我们详细查看修饰寄存器列表。
-
-* * *
-
-## 6. 更多关于约束
-
-到这个时候,你可能已经了解到约束和内联汇编有很大的关联。但我们很少说到约束。约束用于表明一个操作数是否可以位于寄存器和位于哪个寄存器;是否操作数可以为一个内存引用和哪种地址;是否操作数可以为一个立即数和为哪一个可能的值(即值的范围)。它可以有...等等。
-
-## 6.1 常用约束
-
-在许多约束中,只有小部分是常用的。我们将看看这些约束。
-
-1.  **寄存器操作数约束(r)**
-
-	当使用这种约束指定操作数时,它们存储在通用寄存器(GPR)中。请看下面示例:
-
-    `asm ("movl %%eax, %0\n" :"=r"(myval));`
-
-	这里,变量 myval 保存在寄存器中,寄存器 eax 的值被复制到该寄存器中,并且myval的值从寄存器更新到了内存。当指定 "r" 约束时, gcc 可以将变量保存在任何可用的 GPR 中。为了指定寄存器,你必须使用特定寄存器约束直接地指定寄存器的名字。它们为:
-
-    > `
-    > 
-    > <pre>+---+--------------------+
-    > | r |    Register(s)     |
-    > +---+--------------------+
-    > | a |   %eax, %ax, %al   |
-    > | b |   %ebx, %bx, %bl   |
-    > | c |   %ecx, %cx, %cl   |
-    > | d |   %edx, %dx, %dl   |
-    > | S |   %esi, %si        |
-    > | D |   %edi, %di        |
-    > +---+--------------------+
-    > </pre>
-    > 
-    > `
-
-2.	**内存操作数约束(m)**
-
-	当操作数位于内存时,任何对它们的操作将直接发生在内存位置,这与寄存器约束相反,后者首先将值存储在要修改的寄存器中,然后将它写回到内存位置。但寄存器约束通常用于一个指令必须使用它们或者它们可以大大提高进程速度的地方。当需要在 "asm" 内更新一个 C 变量,而又不想使用寄存器去保存它的只,使用内存最为有效。例如, idtr 的值存储于内存位置:
-
-    `asm("sidt %0\n" : :"m"(loc));`
-
-3.	**匹配(数字)约束**
-
-	在某些情况下,一个变量可能既充当输入操作数,也充当输出操作数。可以通过使用匹配约束在 "asm" 中指定这种情况。
-
-    `asm ("incl %0" :"=a"(var):"0"(var));`
-
-	在操作数子节中,我们也看到了一些类似的示例。在这个匹配约束的示例中,寄存器 "%eax" 既用作输入变量,也用作输出变量。 var 输入被读进 %eax ,并且更新的 %eax 再次被存储进 var。这里的 "0" 用于指定与第0个输出变量相同的约束。也就是,它指定 var 输出实例应只被存储在 "%eax" 中。该约束可用于:
-
-	*	在输入从变量读取或变量修改后,修改被写回同一变量的情况
-	*	在不需要将输入操作数实例和输出操作数实例分开的情况
-
-	使用匹配约束最重要的意义在于它们可以导致有效地使用可用寄存器。
-
-其他一些约束:
-
-1.	"m" : 允许一个内存操作数使用机器普遍支持的任一种地址。
-2.	"o" : 允许一个内存操作数,但只有当地址是可偏移的。即,该地址加上一个小的偏移量可以得到一个地址。
-3.  "V" : A memory operand that is not offsettable. In other words, anything that would fit the `m’ constraint but not the `o’constraint.
-4.	"i" : 允许一个(带有常量)的立即整形操作数。这包括其值仅在汇编时期知道的符号常量。
-5.	"n" : 允许一个带有已知数字的立即整形操作数。许多系统不支持汇编时期的常量,因为操作数少于一个字宽。对于此种操作数,约束应该使用 'n' 而不是'i'。 
-6.	"g" : 允许任一寄存器、内存或者立即整形操作数,不包括通用寄存器之外的寄存器。
-
-
-以下约束为x86特有。
-
-1.	"r" : 寄存器操作数约束,查看上面给定的表格。
-2.	"q" : 寄存器 a、b、c 或者 d。
-3.	"I" : 范围从 0 到 31 的常量(对于 32 位移位)。
-4.  "J" : 范围从 0 到 63 的常量(对于 64 位移位)。
-5.	"K" : 0xff。
-6.  "L" : 0xffff。
-7.  "M" : 0, 1, 2, or 3 (lea 指令的移位)。
-8.  "N" : 范围从 0 到 255 的常量(对于 out 指令)。
-9.  "f" : 浮点寄存器
-10.  "t" : 第一个(栈顶)浮点寄存器
-11.  "u" : 第二个浮点寄存器
-12.  "A" : 指定 `a` 或 `d` 寄存器。这主要用于想要返回 64 位整形数,使用 `d` 寄存器保存最高有效位和 `a` 寄存器保存最低有效位。
-
-## 6.2 约束修饰符
-
-当使用约束时,对于更精确的控制超越了约束作用的需求,GCC 给我们提供了约束修饰符。最常用的约束修饰符为:
-
-1.  "=" : 意味着对于这条指令,操作数为只写的;旧值会被忽略并被输出数据所替换。
-2.  "&" : 意味着这个操作数为一个早期的改动操作数,其在该指令完成前通过使用输入操作数被修改了。因此,这个操作数不可以位于一个被用作输出操作数或任何内存地址部分的寄存器。如果在旧值被写入之前它仅用作输入而已,一个输入操作数可以为一个早期改动操作数。
-
-	约束的列表和解释是决不完整的。示例可以给我们一个关于内联汇编的用途和用法的更好的理解。在下一节,我们会看到一些示例,在那里我们会发现更多关于修饰寄存器列表的东西。
-
-* * *
-
-## 7. 一些实用的诀窍
-
-现在我们已经介绍了关于 GCC 内联汇编的基础理论,现在我们将专注于一些简单的例子。将内联汇编函数写成宏的形式总是非常方便的。我们可以在内核代码里看到许多汇编函数。(usr/src/linux/include/asm/*.h)。
-
-1. 首先我们从一个简单的例子入手。我们将写一个两个数相加的程序。
-
-    > `
-    > 
-    > * * *
-    > 
-    > <pre>int main(void)
-    > {
-    >         int foo = 10, bar = 15;
-    >         __asm__ __volatile__("addl  %%ebx,%%eax"
-    >                              :"=a"(foo)
-    >                              :"a"(foo), "b"(bar)
-    >                              );
-    >         printf("foo+bar=%d\n", foo);
-    >         return 0;
-    > }
-    > </pre>
-    > 
-    > * * *
-    > 
-    > `
-
-    这里我们要求 GCC 将 foo 存放于 %eax,将 bar 存放于 %ebx,同时我们也想要在 %eax 中存放结果。'=' 符号表示它是一个输出寄存器。现在我们可以以其他方式将一个整数加到一个变量。
-
-    > `
-    > 
-    > * * *
-    > 
-    > <pre> __asm__ __volatile__(
-    >                       "   lock       ;\n"
-    >                       "   addl %1,%0 ;\n"
-    >                       : "=m"  (my_var)
-    >                       : "ir"  (my_int), "m" (my_var)
-    >                       :                                 /* 无修饰寄存器列表 */
-    >                       );
-    > </pre>
-    > 
-    > * * *
-    > 
-    > `
-
-    这是一个原子加法。为了移除原子性,我们可以移除指令 'lock'。在输出域中,"=m" 表明 my_var 是一个输出且位于内存。类似地,"ir" 表明 my_int 是一个整型,并应该存在于其他寄存器(回想我们上面看到的表格)。没有寄存器位于修饰寄存器列表中。
-
-2.  现在我们将在一些寄存器/变量上展示一些操作,并比较值。
-
-    > `
-    > 
-    > * * *
-    > 
-    > <pre> __asm__ __volatile__(  "decl %0; sete %1"
-    >                       : "=m" (my_var), "=q" (cond)
-    >                       : "m" (my_var) 
-    >                       : "memory"
-    >                       );
-    > </pre>
-    > 
-    > * * *
-    > 
-    > `
-
-    这里,my_var 的值减 1 ,并且如果结果的值为 0,则变量 cond 置 1。我们可以通过添加指令 "lock;\n\t" 作为汇编模板的第一条指令来添加原子性。
-
-    以类似的方式,为了增加 my_var,我们可以使用 "incl %0" 而不是 "decl %0"。
-
-    这里需要注意的点为(i)my_var 是一个存储于内存的变量。(ii)cond 位于任何一个寄存器 eax、ebx、ecx、edx。约束 "=q" 保证这一点。(iii)同时我们可以看到 memory 位于修饰寄存器列表中。也就是说,代码将改变内存中的内容。
-
-3.  如何置1或清0寄存器中的一个比特位。作为下一个诀窍,我们将会看到它。
-
-    > `
-    > 
-    > * * *
-    > 
-    > <pre>__asm__ __volatile__(   "btsl %1,%0"
-    >                       : "=m" (ADDR)
-    >                       : "Ir" (pos)
-    >                       : "cc"
-    >                       );
-    > </pre>
-    > 
-    > * * *
-    > 
-    > `
-
-    这里,ADDR 变量(一个内存变量)的 'pos' 位置上的比特被设置为 1。我们可以使用 'btrl' 来清楚由 'btsl' 设置的比特位。pos 的约束 "Ir" 表明 pos 位于寄存器并且它的值为 0-31(x86 相关约束)。也就是说,我们可以设置/清除 ADDR 变量上第 0 到 31 位的任一比特位。因为条件码会被改变,所以我们将 "cc" 添加进修饰寄存器列表。
-
-4.  现在我们看看一些更为复杂而有用的函数。字符串拷贝。
-
-    > `
-    > 
-    > * * *
-    > 
-    > <pre>static inline char * strcpy(char * dest,const char *src)
-    > {
-    > int d0, d1, d2;
-    > __asm__ __volatile__(  "1:\tlodsb\n\t"
-    >                        "stosb\n\t"
-    >                        "testb %%al,%%al\n\t"
-    >                        "jne 1b"
-    >                      : "=&S" (d0), "=&D" (d1), "=&a" (d2)
-    >                      : "0" (src),"1" (dest) 
-    >                      : "memory");
-    > return dest;
-    > }
-    > </pre>
-    > 
-    > * * *
-    > 
-    > `
-
-    源地址存放于 esi,目标地址存放于 edi,同时开始拷贝,当我们到达 **0** 时,拷贝完成。约束 "&S"、"&D"、"&a" 表明寄存器 esi、edi和 eax 早期的修饰寄存器,也就是说,它们的内容在函数完成前会被改变。这里很明显可以知道为什么 "memory" 会放在修饰寄存器列表。
-
-    我们可以看到一个类似的函数,它能移动双字块数据。注意函数被声明为一个宏。
-
-    > `
-    > 
-    > * * *
-    > 
-    > <pre>#define mov_blk(src, dest, numwords) \
-    > __asm__ __volatile__ (                                          \
-    >                        "cld\n\t"                                \
-    >                        "rep\n\t"                                \
-    >                        "movsl"                                  \
-    >                        :                                        \
-    >                        : "S" (src), "D" (dest), "c" (numwords)  \
-    >                        : "%ecx", "%esi", "%edi"                 \
-    >                        )
-    > </pre>
-    > 
-    > * * *
-    > 
-    > `
-
-    这里我们没有输出,所以寄存器 ecx、esi和 edi 的内容发生改变,这是块移动的副作用。因此我们必须将它们添加进修饰寄存器列表。
-
-5.  在 Linux 中,系统调用使用 GCC 内联汇编实现。让我们看看如何实现一个系统调用。所有的系统调用被写成宏(linux/unistd.h)。例如,带有三个参数的系统调用被定义为如下所示的宏。
-
-    > `
-    > 
-    > * * *
-    > 
-    > type name(type1 arg1,type2 arg2,type3 arg3) \
-    > { \
-    > long __res; \
-    > __asm__ volatile (  "int $0x80" \
-    >                   : "=a" (__res) \
-    >                   : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
-    >                     "d" ((long)(arg3))); \
-    > __syscall_return(type,__res); \
-    > }
-    > </pre>
-    > 
-    > * * *
-    > 
-    > `
-
-    无论何时调用带有三个参数的系统调用,以上展示的宏用于执行调用。系统调用号位于 eax 中,每个参数位于 ebx、ecx、edx 中。最后 "int 0x80" 是一条用于执行系统调用的指令。返回值被存储于 eax 中。
-
-    每个系统调用都以类似的方式实现。Exit 是一个单一参数的系统调用,让我们看看它的代码看起来会是怎样。它如下所示。
-    
-    > `
-    > 
-    > * * *
-    > 
-    > <pre>{
-    >         asm("movl $1,%%eax;         /* SYS_exit is 1 */
-    >              xorl %%ebx,%%ebx;      /* Argument is in ebx, it is 0 */
-    >              int  $0x80"            /* Enter kernel mode */
-    >              );
-    > }
-    > </pre>
-    > 
-    > * * *
-    > 
-    > `
-
-    Exit 的系统调用号是 1 同时它的参数是 0。因此我们分配 eax 包含 1,ebx 包含 0,同时通过 `int $0x80` 执行 `exit(0)`。这就是 exit 的工作原理。
-
-* * *
-
-## 8. 结束语
-
-这篇文档已经将 GCC 内联汇编过了一遍。一旦你理解了基本概念,你便不难采取自己的行动。我们看了许多例子,它们有助于理解 GCC 内联汇编的常用特性。
-
-GCC 内联是一个极大的主题,这篇文章是不完整的。更多关于我们讨论过的语法细节可以在 GNU 汇编器的官方文档上获取。类似地,对于一个完整的约束列表,可以参考 GCC 的官方文档。
-
-当然,Linux 内核 大规模地使用 GCC 内联。因此我们可以在内核源码中发现许多各种各样的例子。它们可以帮助我们很多。
-
-如果你发现任何的错别字,或者本文中的信息已经过时,请告诉我们。
-
-* * *
-
-## 9. 参考
-
-1.  [Brennan’s Guide to Inline Assembly](http://www.delorie.com/djgpp/doc/brennan/brennan_att_inline_djgpp.html)
-2.  [Using Assembly Language in Linux](http://linuxassembly.org/articles/linasm.html)
-3.  [Using as, The GNU Assembler](http://www.gnu.org/manual/gas-2.9.1/html_mono/as.html)
-4.  [Using and Porting the GNU Compiler Collection (GCC)](http://gcc.gnu.org/onlinedocs/gcc_toc.html)
-5.  [Linux Kernel Source](http://ftp.kernel.org/)
-
-* * *
-via: http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
-
-  作者:[Sandeep.S](mailto:busybox@sancharnet.in) 译者:[cposture](https://github.com/cposture) 校对:[]()
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](http://linux.cn/) 荣誉推出
diff --git a/translated/tech/20160104 What is good stock portfolio management software on Linux.md b/translated/tech/20160104 What is good stock portfolio management software on Linux.md
deleted file mode 100644
index 32e1c8fb2a..0000000000
--- a/translated/tech/20160104 What is good stock portfolio management software on Linux.md	
+++ /dev/null
@@ -1,109 +0,0 @@
-Translating by ivo-wang
-What is good stock portfolio management software on Linux
-linux上那些不错的管理股票组合投资软件
-================================================================================
-如果你在股票市场做投资,那么你可能非常清楚管理组合投资的计划有多重要。管理组合投资的目标是依据你能承受的风险,时间层面的长短和资金盈利的目标去为你量身打造的一种投资计划。鉴于这类软件的重要性,难怪从不缺乏商业性质的app和股票行情检测软件,每一个都可以兜售复杂的组合投资以及跟踪报告功能。
-
-对于这些linux爱好者们,我们找到了一些 **好用的开源组合投资管理工具** 用来在linux上管理和跟踪股票的组合投资,这里高度推荐一个基于java编写的管理软件[JStock][1]。如果你不是一个java粉,你不得不面对这样一个事实JStock需要运行在重型的JVM环境上。同时我相信许多人非常欣赏JStock,安装JRE以后它可以非常迅速的安装在各个linux平台上。没有障碍能阻止你将它安装在你的linux环境中。
-
-开源就意味着免费或标准低下的时代已经过去了。鉴于JStock只是一个个人完成的产物,作为一个组合投资管理软件它最令人印象深刻的是包含了非常多实用的功能,以上所有的荣誉属于它的作者Yan Cheng Cheok!例如,JStock 支持通过监视列表去监控价格,多种组合投资,按习惯/按固定 做股票指示与相关扫描,支持27个不同的股票市场和交易平台云端备份/还原。JStock支持多平台部署(Linux, OS X, Android 和 Windows),你可以通过云端保存你的JStock记录,它可以无缝的备份还原到其他的不同平台上面。
-
-现在我将向你展示如何安装以及使用过程的一些具体细节。
-
-### 在Linux上安装JStock ###
-
-因为JStock使用Java编写,所以必须[安装 JRE][2]才能让它运行起来.小提示JStock 需要JRE1.7或更高版本。如你的JRE版本不能满足这个需求,JStock将会安装失败然后出现下面的报错。
-
-    Exception in thread "main" java.lang.UnsupportedClassVersionError: org/yccheok/jstock/gui/JStock : Unsupported major.minor version 51.0
-
-
-一旦你安装了JRE在你的linux上,从官网下载最新的发布的JStock,然后加载启动它。
-
-    $ wget https://github.com/yccheok/jstock/releases/download/release_1-0-7-13/jstock-1.0.7.13-bin.zip
-    $ unzip jstock-1.0.7.13-bin.zip
-    $ cd jstock
-    $ chmod +x jstock.sh
-    $ ./jstock.sh
-
-教程的其他部分,让我来给大家展示一些JStock的实用功能
-
-### 监视监控列表股票价格的波动 ###
-
-使用JStock你可以创建一个或多个监视列表,它可以自动的监视股票价格的波动并给你提供相应的通知。在每一个监视列表里面你可以添加多个感兴趣的股票进去。之后添加你的警戒值在"Fall Below"和"Rise Above"的表格里,分别是在设定最低价格和最高价格。
-
-![](https://c2.staticflickr.com/2/1588/23795349969_37f4b0f23c_c.jpg)
-
-例如你设置了AAPL股票的最低/最高价格分别是$102 和 $115.50,你将在价格低于$102或高于$115.50的任意时间在桌面得到通知。
-
-你也可以设置邮件通知,之后你将收到一些价格信息的邮件通知。设置邮件通知在栏的"Options"选项。在"Alert"标签,打开"Send message to email(s)",填入你的Gmail账户。一旦完成Gmail认证步骤,JStock将开始发送邮件通知到你的Gmail账户(也可以设置其他的第三方邮件地址)
-![](https://c2.staticflickr.com/2/1644/24080560491_3aef056e8d_b.jpg)
-
-### 管理多个组合投资 ###
-
-JStock能够允许你管理多个组合投资。这个功能对于股票经纪人是非常实用的。你可以为经纪人创建一个投资项去管理你的 买入/卖出/红利 用来了解每一个经纪人的业务情况。你也可以切换不同的组合项目通过选择一个特殊项目在"Portfolio"菜单里面。下面是一张截图用来展示一个意向投资
-![](https://c2.staticflickr.com/2/1646/23536385433_df6c036c9a_c.jpg)
-
-因为能够设置付给经纪人小费的选项,所以你能付给经纪人任意的小费,印花税以及清空每一比交易的小费。如果你非常懒,你也可以在菜单里面设置自动计算小费和给每一个经纪人固定的小费。在完成交易之后JStock将自动的计算并发送小费。
-
-![](https://c2.staticflickr.com/2/1653/24055085262_0e315c3691_b.jpg)
-
-### 显示固定/自选股票提示 ###
-
-如果你要做一些股票的技术分析,你可能需要不同股票的指数(这里叫做“平均股指”),对于股票的跟踪,JStock提供多个[预设技术指示器][3] 去获得股票上涨/下跌/逆转指数的趋势。下面的列表里面是一些可用的指示。
-- 异同平均线(MACD)
-- 相对强弱指数 (RSI)
-- 货币流通指数 (MFI)
-- 顺势指标 (CCI)
-- 十字线
-- 黄金交叉线, 死亡交叉线
-- 涨幅/跌幅 
-
-开启预设指示器能需要在JStock中点击"Stock Indicator Editor"标签。之后点击右侧面板中的安装按钮。选择"Install from JStock server"选项,之后安装你想要的指示器。
-
-![](https://c2.staticflickr.com/2/1476/23867534660_b6a9c95a06_c.jpg)
-
-一旦安装了一个或多个指示器,你可以用他们来扫描股票。选择"Stock Indicator Scanner"标签,点击底部的"Scan"按钮,选择需要的指示器。
-
-![](https://c2.staticflickr.com/2/1653/24137054996_e8fcd10393_c.jpg)
-
-当你选择完需要扫描的股票(例如e.g., NYSE, NASDAQ)以后,JStock将执行扫描,并将捕获的结果通过列表的形式展现在指示器上面。
-
-![](https://c2.staticflickr.com/2/1446/23795349889_0f1aeef608_c.jpg)
-
-除了预设指示器以外,你也可以使用一个图形化的工具来定义自己的指示器。下面这张图例中展示的是当前价格小于或等于60天平均价格
-
-![](https://c2.staticflickr.com/2/1605/24080560431_3d26eac6b5_c.jpg)
-
-### 云备份还原Linux 和 Android JStock ###
-
-另一个非常棒的功能是JStock可以支持云备份还原。Jstock也可以把你的组合投资/监视列表备份还原在 Google Drive,这个功能可以实现在不同平台(例如Linux和Android)上无缝穿梭。举个例子,如果你把Android Jstock组合投资的信息保存在Google Drive上,你可以在Linux班级本上还原他们。
-
-![](https://c2.staticflickr.com/2/1537/24163165565_bb47e04d6c_c.jpg)
-
-![](https://c2.staticflickr.com/2/1556/23536385333_9ed1a75d72_c.jpg)
-
-如果你在从Google Drive还原之后不能看到你的投资信息以及监视列表,请确认你的国家信息与“Country”菜单里面设置的保持一致。
-
-JStock的安卓免费版可以从[Google Play Store][4]获取到。如果你需要完整的功能(比如云备份,通知,图表等),你需要一次性支付费用升级到高级版。我想高级版肯定有它的价值所在。
-
-![](https://c2.staticflickr.com/2/1687/23867534720_18b917028c_c.jpg)
-
-写在最后,我应该说一下它的作者,Yan Cheng Cheok,他是一个十分活跃的开发者,有bug及时反馈给他。最后多有的荣耀都属于他一个人!!!
-
-关于JStock这个组合投资跟踪软件你有什么想法呢?
-
---------------------------------------------------------------------------------
-
-via: http://xmodulo.com/stock-portfolio-management-software-linux.html
-
-作者:[Dan Nanni][a]
-译者:[译者ID](https://github.com/ivo-wang)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]:http://xmodulo.com/author/nanni
-[1]:http://jstock.org/
-[2]:http://ask.xmodulo.com/install-java-runtime-linux.html
-[3]:http://jstock.org/ma_indicator.html
-[4]:https://play.google.com/store/apps/details?id=org.yccheok.jstock.gui
diff --git a/translated/tech/20160416 A newcomer's guide to navigating OpenStack Infrastructure.md b/translated/tech/20160416 A newcomer's guide to navigating OpenStack Infrastructure.md
deleted file mode 100644
index 25353ef462..0000000000
--- a/translated/tech/20160416 A newcomer's guide to navigating OpenStack Infrastructure.md	
+++ /dev/null
@@ -1,64 +0,0 @@
-给学习OpenStack基础设施的新手的入门指南
-===========================================================
-
-任何一个为OpenStack贡献源码的人会受到社区的欢迎,但是,对于一个发展趋近成熟并且快速迭代的开源社区而言,能够拥有一个新手指南并不是
-件坏事。在奥斯汀举办的OpenStack峰会上,[Paul Belanger][1] (红帽公司), [Elizabeth K. Joseph][2] (HPE公司),和[Christopher Aedo][3] (IBM公司)将会就针对新人的OpenStack基础设施作一场专门的会谈。在这次采访中,他们将会提供一些建议和资源来帮助新人成为OpenStack贡献者中的一员。
-![](https://opensource.com/sites/default/files/images/life/Interview%20banner%20Q%26A.png)
-
-**你在谈话中表示你将“投入全部身心于基础设施并解释你需要知道的有关于维持OpenStack正常工作的系统的每一件事情”。这是一个持续了40分钟的的艰巨任务。那么,对于学习OpenStack基础设施的新手来说最需要知道那些事情呢?**
-**Elizabeth K. Joseph (EKJ)**: 我们没有为OpenStack使用GitHub这种提交补丁的方式,这是因为这样做会对新手造成巨大的困扰,尽管由于历史原因我们还是保留了所有原先就在GitHub上的所有库的镜像。相反,我们使用了一种完全开源的复查形式,并通过OpenStack基础设施团队来持续的维持系统集成(CI)。相关的,自从我们使用了CI系统,每一个提交给OpenStack的改变都会在被合并之前进行测试。
-**Paul Belanger (PB)**: 这个项目中的大多数都是富有激情的人,因此当你提交的补丁被某个人否定时不要感到沮丧。
-**Christopher Aedo (CA)**:社区很想帮助你取得成功,因此不要害怕提问或者寻求更多的那些能够促进你理解某些事物的引导者。
-
-### 在你的讲话中,对于一些你无法涉及到的方面,你会向新手推荐哪些在线资源来让他们更加容易入门?
-**PB**:当然是我们的[OpenStack项目基础设施文档][5]。我们已经花了足够大的努力来尽可能让这些文档能够随时保持最新状态。我们对每个运行OpenStack项目并投入使用的系统都有制作专门的页面来进行说明。甚至于连OpenStack云基础设施团队也即将上线。
-
-**EKJ**:我对于将基础设施文档作为新手入门教程这件事上的观点和Paul是一致的,另外,我们十分乐意看到来自那些folk了我们项目的学习者提交上来的补丁。我们通常不会意识到我们忽略了文档中的某些内容,除非它们恰好被人问起。因此,阅读,学习,然后帮助我们修补这些知识上的漏洞。你可以在[OpenStack基础设施邮件清单]提出你的问题,或者在我们位于FreeNode上的#OpenStack-infra的IRC专栏发起你的提问。
-**CA**:我喜欢[这个详细的发布][7],它是由Ian Wienand写的一篇关于构建图片的文章。
-### "gotchas" 会是OpenStack新的代码贡献者苦苦寻找的吗?
-**EKJ**:向项目作出贡献并不仅仅是提交新的代码和新的特性;OpenStack社区高度重视代码复查。如果你想要别人查看你的补丁,那你最好先看看其他人是如何做的,然后参考他们的风格,最后一步布做到你也能够向其他人一样提交清晰且结构分明的代码补丁。你越是能让你的同伴了解你的工作并知道你正在做的复查,那他们也就更有可能形成及时复查你的代码的风格。
-**CA**:我看到过大量的新手在面对Gerrit时受挫,阅读开发者引导中的[开发者工作步骤][9]时,可能只是将他通读了一遍。如果你没有经常使用Gerrit,那你最初对它的感觉可能是困惑和无力的。但是,如果你随后做了一些代码复查的工作,那么你马上就能轻松应对它。同样,我是IRC的忠实粉丝。它可能是一个获得帮助的好地方,但是,你最好保持一个长期保留的状态,这样,尽管你在某个时候没有出现,人们也可以回答你的问题。(阅读[IRC,开源界的成功秘诀][10]。)你不必总是在线,但是你最好能够轻松的在一个通道中进行回滚操作,以此来跟上最新的动态,这种能力非常重要。
-**PB**:我同意Elizabeth和Chris—Gerrit关于寻求何种方式来学习的观点。每个开发人员所作出的努力都将让社区变得更加美好。你不仅仅要提交代码给别人去复查,同时,你也要能够复查其他人的代码。留意Gerrit的用户接口,你可能一时会变的很疑惑。我推荐新手去尝试[Gertty][11],它是一个基于控制台的终端接口,用于Gerrit代码复查系统,而它恰好也是OpenStack基础设施所驱动的一个项目。
-### 你对于OpenStack新手如何通过网络与其他贡献者交流方面有什么好的建议?
-**PB**:对我来说,是通过IRC以及在Freenode上参加#OpenStack-infra专栏([IRC logs][12]).这专栏上面有很多对新手来说很有价值的资源。你可以看到OpenStack项目日复一日的运作情况,同时,一旦你知道了OpenStack项目的工作原理,你将更好的知道如何为OpenStack的未来发展作出贡献。
-**CA**:我想要为IRC再次说明一点,在IRC上保持一整天的在线记录对我来说有非常重大的意义,因为我会感觉到被重视并且时刻保持连接。这也是一种非常好的获得帮助的方式,特别是当你卡在了项目中出现的某一个难题的时候,而在专栏中,总会有一些人很乐意为你解决问题。
-**EKJ**:[OpenStack开发邮件列表][13]对于能够时刻查看到你所致力于的OpenStack项目的最新情况是非常重要的。因此,我推荐一定要订阅它。邮件列表使用课题标签来区分项目,因此你可以设置你的邮件客户端来使用它,并且集中精力于你所关心的项目。除了在线资源之外,全世界范围内也成立了一些OpenStack小组,他们被用来为OpenStack的用户和贡献者提供服务。这些小组可能会定期举行座谈和针对OpenStack主要贡献者的一些活动。你可以在MeetUp.com上搜素你所在地域的贡献者活动聚会,或者在[groups.openstack.org]上查看你所在的地域是否存在OpenStack小组。最后,还有一个每六个月举办一次的OpenStack峰会,这个峰会上会作一些关于基础设施的演说。当前状态下,这个峰会包含了用户会议和开发者会议,会议内容都是和OpenStack相关的东西,包括它的过去,现在和未来。
-### OpenStack需要在那些方面得到提升来让新手更加容易学会并掌握?
-**PB**: 我认为我们的[account-setup][16]过程对于新的贡献者已经做的比较容易了,特别是教他们如何提交他们的第一个补丁。真正参与到OpenStack开发者模式的过程是需要花费很大的努力的,可能对于开发者来说已经显得非常多了;然而,一旦融入进去了,这个模式将会运转的十分高效和令人满意。
-**CA**: 我们拥有一个由专业开发者组成的社区,而且我们的关注点都是发展OpenStack本身,同时,我们致力于让用户付出更小的代价去使用OpenStack云基础设施平台。我们需要发掘更多的应用开发者,并且鼓励更多的人去开发能在OpenStack云上完美运行的云应用程序,我们还鼓励他们在[社区App目录]上去贡献那些由他们开发的app。我们可以通过提升我们的API标准和保证我们不同的库(比如libcloud,phpopencloud已经其他一些库),并让他们持续的为开发者提供可信赖的支持来实现这一目标。还有一点就是通过倡导更多的OpenStack黑客加入进来。所有的这些事情都可以降低新人的学习门槛,这样也能引导他们与这个社区之间的关系更加紧密。y.
-**EKJ**: 我已经致力于开源软件很多年了。但是,对于大量的OpenStack开发者而言,这是一个他们每个人都在从事的第一个开源项目。我发现他们之前使用私有软件的背景并没有为他们塑造开源的观念和方法论,还有在开源项目中需要具备的合作技巧。我乐于看到我们能够让那些曾经一直在使用私有软件工作的人能够真正的明白他们在开源如软件社区所从事的事情的巨大价值。
-### 我认为2016年对于开源Haiku的进一步增长是具有重大意义的一年。通过Haiku来向新手解释OpenStack。
-**PB**: 如果你喜欢自由软件,你可以向OpenStack提交你的第一个补丁。
-**CA**:	在不久的未来,OpenStack将以统治世界的姿态让这个世界变得更好。
-**EKJ**: OpenStack是一个可以免费部署在你的服务器上面并且运行你自己的云的一个软件。
-*Paul,Elizabeth和Christopher将会在4月25号星期一上午11:15于奥斯汀举办的OpenStack峰会上进行演说。
-
-------------------------------------------------------------------------------
-
-via: https://opensource.com/business/16/4/interview-openstack-infrastructure-beginners
-
-作者:[linux.com][a]
-译者:[kylepeng93](https://github.com/kylepeng93)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://rikkiendsley.com/
-[1]: https://twitter.com/pabelanger
-[2]: https://twitter.com/pleia2
-[3]: https://twitter.com/docaedo
-[4]: https://www.openstack.org/summit/austin-2016/summit-schedule/events/7337
-[5]: http://docs.openstack.org/infra/system-config/
-[6]: http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-infra
-[7]: https://www.technovelty.org/openstack/image-building-in-openstack-ci.html
-[8]: https://code.google.com/p/gerrit/
-[9]: http://docs.openstack.org/infra/manual/developers.html#development-workflow
-[10]: https://developer.ibm.com/opentech/2015/12/20/irc-the-secret-to-success-in-open-source/
-[11]: https://pypi.python.org/pypi/gertty
-[12]: http://eavesdrop.openstack.org/irclogs/%23openstack-infra/
-[13]: http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-dev
-[14]: https://groups.openstack.org/
-[15]: https://www.openstack.org/summit/
-[16]: http://docs.openstack.org/infra/manual/developers.html#account-setup
-[17]: https://apps.openstack.org/
-[18]: https://www.openstack.org/summit/austin-2016/summit-schedule/events/7337
diff --git a/translated/tech/20160502 The intersection of Drupal, IoT, and open hardware.md b/translated/tech/20160502 The intersection of Drupal, IoT, and open hardware.md
deleted file mode 100644
index 7cfffd953f..0000000000
--- a/translated/tech/20160502 The intersection of Drupal, IoT, and open hardware.md	
+++ /dev/null
@@ -1,62 +0,0 @@
-Drupal, IoT 和开源硬件的交叉点
-=======================================================
-
-![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/drupal_blue_gray_lead.jpeg?itok=t7W_KD-D)
-
-
-认识一下 [Amber Matz][1],来自由 Lullabot Education 提供的 [Drupalize.Me][3] 的生产经理以及培训师。当她没有倒腾 Arduino,Raspberry Pi 以及电子穿戴设备时,通常会在波特兰 Drupal 用户组里和主持人争论。
-
-在即将举行的 [DrupalCon NOLA][3] 大会上,Amber 将主持一个关于 Drupal 和 IoT 的主题。如果你会去参加,也想了解下开源硬件,IoT 和 Drupal 之间的交叉点,那这个将很合适。如果你去不了新奥尔良的现场也没关系,Amber 还分享了许多很酷的事情。在这次采访中,她讲述了自己参与 Drupal 的原因,一些她自己喜欢的开源硬件项目,以及 IoT 和 Drupal 的未来。
-
-![](https://opensource.com/sites/default/files/images/life/Interview%20banner%20Q%26A.png)
-
-**你是怎么加入 Drupal 社区的?**
-
-在这之前,我在一家大型非盈利性机构市场部的“站长办公室”工作,大量产出没人喜欢的定制 PHP/MySQL 表格。终于我觉得这样很烦并开始在网上寻找更好的方式。然后我找到了 Drupal 6 并开始自己沉迷进去。多年以后,在准备职业转换的时候,发现了波特兰 Drupal 用户组,然后在里面找了一份全职的 Drupal 开发者工作。我一直经常参加在波特兰的聚会,我觉得它是一种很好的社区,交友,以及专业开发的资源。一个偶然的机会,我在 Lullabot 找了一份培训师的工作为 Drupalize.Me 提供内容。现在,我管理着 Drupalize.Me 的内容管道,创建 Drupal 8 的内容,还很大程度地参与到波特兰 Drupal 社区中。我是今年的协调员,寻找并规划演讲者。
-
-**我们得明白:什么是 Arduino 原型,你是怎么找到它的,以及你用 Arduino 做过的最酷的事是什么?**
-
-Arduino,Raspberry Pi,以及可穿戴电子设备,这些年到处都能听到这些术语。我在几年前通过 Becky Stern YouTube 秀(最近由 Becky 继续主持,每周三播出)发现了 [Adafruit 的可穿戴电子设备][4]。我被那些可穿戴设备迷住了,还订了一套 LED 缝制工具,不过没做出任何东西。我就是没搞懂。我没有任何电子相关的背景,而且在我被那些项目吸引的时候,我根本不知道怎么做出那样的东西。看上去太遥远了。
-
-后来,我找到一个 Coursera 的“物联网”专题。(很时髦,对吧?)但我很快就喜欢上了。我最终找到了 Arduino 是什么的解释,以及所有这些其他的重要术语和概念。我订了一套推荐的 Arduino 初学者套件,还附带了一本如何上手的小册子。当我第一次让 LED 闪烁的时候,开心极了。我在圣诞节以及之后有两个星期的假期,然后我什么都没干,就一直根据初学者小册子给 Arduino 电路编程。很奇怪我觉得很放松!我太喜欢了。
-
-一月份的时候,我开始构思我自己的原型设备。在知道我要主持公司培训的开场白时,我用五个 LED 灯和 Arduino 搭建了一个开场白视觉计时器。
-
-![](https://opensource.com/sites/default/files/resize/amber-arduino-lightning-talk-timer-400x400.jpg)
-
-这是一次巨大的成功。我还做了我的第一个可穿戴项目,一件会发光的连帽衫,使用了和 Arduino IDE 兼容的 Gemma 微控制器,一个小的圆形可缝制部件,然后用可导电的线缝起来,将一个滑动可变电阻和衣服帽口的收缩绳连在一起,用来控制缝到帽子里的五个 NeoPixel 灯的颜色。这就是我对原型设计的看法:开展一些很好玩也可能会有点实际用途的疯狂项目。
-
-**Drupal 和 IoT 带来的最大机遇是什么??**
-
-IoT 和网站服务以及 Drupal 分层趋势实际并没有太大差别。就是将数据从一个物体传送到另一个物体,然后将数据转换成一些有用的东西。但数据是如何送达?能用来做点什么?你觉得现在就有一大堆现成的解决方案,应用,中间层,以及 API?采用 IoT,这只会继续成指数增长。我觉得,给我任何一个设备或“物体”,需要只用一种方式来将它连接到因特网的无线可能上。然后有现成的各种代码库来帮助制作者将他们的数据从一个物体送到另一个物体。
-
-那么 Drupal 在这里处于什么位置?首先,网站服务将是第一个明显的地方。但作为一个制作者,我不希望将时间花在编写 Drupal 的订制模块上。我想要的是即插即用!所以我很高兴出现这样的模块能连接 IoT 云端 API 和服务,比如 ThingSpeak,Adafruit.io,IFTTT,以及其他的。我觉得也有一个很好的商业机会,在 Drupal 里构建一套 IoT 云服务,允许用户发送和存储他们的传感器数据,并可以制成表格和图像,还可以写一些插件可以响应特定数据或阙值。每一个 IoT 云 API 服务都是一个细分的机会,所以能留下很大空间给其他人。
-
-**这次 DrupalCon 你有哪些期待?**
-
-我喜欢重新联系 Drupal 上的朋友,认识一些新的人,还能见到 Lullabot 和 Drupalize.Me 的同事(我们是分布式的公司)!Drupal 8 有太多东西可以去探索了,不可抗拒地要帮我们的客户收集培训资料。所以,我很期待参与一些 Drupal 8 相关的主题,以及跟上最新的开发活动。最后,我对新奥尔良也很感兴趣!我曾经在 2004 年去过,很期待将这次将看到哪些改变。
-
-**谈一谈你这次 DrupalCon 上的演讲,超越闪烁:将 Drupal 加到你的 IoT 游乐场中。别人为什么要参与?他们最重要的收获会是什么?**
-
-我的主题的标题是,超越闪烁:将 Drupal 加到你的 IoT 游乐场中,本身有很多假设,我将让所有人都放在同一速度和层次。你不需要了解任何关于 Arduino,物联网,甚至是 Drupal,都能跟上。我将从用 Arduino 让 LED 灯闪烁开始,然后我会谈一下我自己在这里面的最大收获:玩,学,教,和做。我会列出一些曾经激发过我的例子,它们也很有希望能激发和鼓励其他听众去尝试一下。然后,就是展示时间!
-
-首先,第一个东西。它是一个构建提醒信号灯。在这个展示里,我会说明如何将信号灯连到互联网上,以及如何响应从云 API 服务收到的数据。然后,第二个东西。它是一个蒸汽朋克风格 iPhone 外壳形式的“天气手表”。有一个小型 LED 矩阵用来显示我的天气的图标,一个气压和温度传感器,一个 GPS 模块,以及一个 Bluetooth LE 模块,都连接到一个 Adafruit Flora 微控制器上。第二个东西能通过蓝牙连接到我的 iPhone 上的一个应用,并将天气和位置数据通过 MQTT 协议发到 Adafruit.io 的服务器!然后,在 Drupal 这边,我会从云端下载这些数据,根据天气更新一个功能块,然后更新地图。所以大家也能体验一下通过网站服务,地图和 Drupal 8 的功能块所能做的事情。
-
-学习和制作这些展示原型是一次烧脑的探险,我也希望有人能参与这个主题并感染一点我对这个技术交叉的传染性热情!我很兴奋能分享一些我的发现。
-
-
-------------------------------------------------------------------------------
-
-via: https://opensource.com/business/16/5/drupalcon-interview-amber-matz
-
-作者:[Jason Hibbets][a]
-译者:[zpl1025](https://github.com/zpl1025)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: https://opensource.com/users/jhibbets
-[1]: https://www.drupal.org/u/amber-himes-matz
-[2]: https://drupalize.me/
-[3]: https://events.drupal.org/neworleans2016/
-[4]: https://www.adafruit.com/beckystern
diff --git a/translated/tech/20160516 Scaling Collaboration in DevOps.md b/translated/tech/20160516 Scaling Collaboration in DevOps.md
deleted file mode 100644
index 8ef18c4592..0000000000
--- a/translated/tech/20160516 Scaling Collaboration in DevOps.md	
+++ /dev/null
@@ -1,70 +0,0 @@
-Devops的弹性合作
-=================================
-
-![](http://devops.com/wp-content/uploads/2016/05/ScalingCollaboration.jpg)
-
-那些熟悉Devops的人普遍认同这样的文化关乎科技。当然,工具和参与是Devops有效实施的必要。但是Devops成功的基础是[团队合作][1]在企业做事更迅速,有效
-
-
-大多数的DevOps平台和工具的设计具有可扩展性的理念。DevOps环境通常运行在云端,并且容易发生变化。为了支持DevOps实时规模解决需求激增,软件变得很重要。同样的事情是真实的人为因素,但缩放合作是一个完整的不同的故事。
-
-
-跨企业协同是DevOps成功的关键。好的代码和发展是需要的。面临的挑战是如何做到无缝和尽可能多的速度和自动化,而不牺牲质量或性能。企业如何才能简化代码的开发和部署,同时保持知名度,治理和合规?
-
-### 新兴趋势
-
-首先,我先提供一些背景,分享一些通过451研究公司 关于devops的研究而获取的数据。云、敏捷和Devops 在今天是非常重要的,不管是理念还是现实。451研究公司看到企业通过这些东西,还包括容器技术、增长大量的用在生产环境中。
-
-拥抱这些技术和方法有许多优点,比如提高灵活性和速度,降低成本,提高弹性和可靠性,适应新的或新兴的应用。根据451公司的研究,组织也面临着一些障碍,包括缺乏熟悉和所需的技能,这些新兴技术的不成熟,成本和安全问题。
-
-在 “[Voice of the Enterprise: SDI Q4 2015 survey][2],” 451公司发行超过一半的受访者者(最后为57.1%)考虑到他们是最终调查者或新兴科技。另一方面,近半受访者(48.3 %)认为自己是第一个或早起的采用者。
-
-这些普遍性的情绪表现在对其他问题的调查中。当问起容器的执行情况时,50.3%的人表示这不在他们的计划中。49.7%的人是在进行计划、试点或积极使用容器技术。近2/3(65.1%)的人表示,他们用敏捷开发的应用开发,但是只有39.6%的回应,他们正在积极拥抱DevOps。然而,敏捷软件开发已经在行业内存在了多年,451公司注意到通过容器和Devops的提升给他们了很现实的趋势。
-
-当被问及首要的三个痛点是什么,被提及最多的是成本或预算,工作人员和遗留软件问题。随着企业向云,DevOps,和容器等转型,这些都需要加以解决,以及如何规模技术和协作的有效。
-
-### 当前状况
-
-由Devops革命在很大程度上带动产生巨大变化的行业,使得软件开发变得更加高度集成的整个业务。软件的创造是不分种族的,而且更多是协作和社会化的功能。
-
-在推动价值的今天,几年前的概念和方法已经成熟,很快就成为今天的主流技术和框架。企业依靠如敏捷、精益、虚拟化、云计算、自动化等概念来简化开发,同时使工作更加有效。
-
-为适应和发展,企业需要完成一系列的关键任务。当今面临的挑战是如何加快发展的同时降低成本。组织需要消除它和其他业务之间存在的障碍,并在一个由技术驱动的竞争环境中提供更多有效的战略合作。
-
-敏捷、云计算、Devops和容器在这个过程中起着重要的作用,但是有一件事情,他们都是有效的合作。每一种技术和方法都提供了独特的优势,但真正的价值来自于组织的整体能够进行规模协同和组织所使用的工具和平台。成功的DevOps的实现也需要其他利益相关者的参与,发展IT运营团队,包括安全、数据库、存储和业务队伍。
-
-### 合作即平台
-
-有一些在线的服务和平台,比如Github,促进了流式合作。它的功能是一个在线代码库,但是所产生的价值远超出了存储代码。
-
-
-这样一个[协作平台][4] 有助于开发人员和团队合作,因为它提供了一个代码和程序的社区可以共享和讨论。管理者可以监视进度和跟踪下一个代码是什么开发人员可以在一个安全的环境中进行实验,然后把这些实验的生活环境,新的想法和实验可以有效地传达给适当的团队。
-
-更敏捷开发和DevOps的关键之一是允许开发人员测试并收集相关的快速反馈。目标是生产高质量的代码和功能,而不是浪费时间建立和管理基础设施或者安排更多的会议来讨论这个问题。比如GitHub平台,能够更有效的和可扩展的协作是因为代码审查可以由参与者最方便的进行。没有必要尝试协调和安排代码审查会议,使开发人员可以继续不间断地工作,从而产生更大的生产力和工作满意度。
-
-Sendachi的Steven Anderson 指出,Github是一个协作平台,但它也是一个和你一起工作的工具。这样意味着他不仅可以帮助协作和持续集成,还影响了代码质量
-
-合作平台的好处之一是,大型团队的开发人员可以分解成更小的团队,可以更有效地专注于特定的组件。它还允许诸如文件共享、模糊代码开发技术和非技术的贡献,增加了协作和可见性
-
-### 合作是关键
-
-合作的重要性不言而喻。合作是Devops文化的关键,也是当今世界能够进行敏捷开发和保持竞争优势的重要的一点。执行或管理的支持以及内部的传道是很重要的。组织还需要拥抱文化的转变---朝着目标混合技能跨越职能领域
-
-当这样的文化建立起来,有效的合作是至关重要的。一个合作平台是规模合作的必要组件,因为简化了生产活动,并且减少了冗余和尝试,同时还产生了更高质量的结果。
-
-
---------------------------------------------------------------------------------
-
-via: http://devops.com/2016/05/16/scaling-collaboration-devops/
-
-作者:[TONY BRADLEY][a]
-译者:[Bestony](https://github.com/Bestony)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://devops.com/author/tonybsg/
-[1]: http://devops.com/2014/12/15/four-strategies-supporting-devops-collaboration/
-[2]: https://451research.com/
-[3]: https://451research.com/customer-insight-voice-of-the-enterprise-overview
-[4]: http://devops.com/events/analytics-of-collaboration-on-github/
diff --git a/translated/tech/20160519 The future of sharing integrating Pydio and ownCloud.md b/translated/tech/20160519 The future of sharing integrating Pydio and ownCloud.md
deleted file mode 100644
index bf5a25c77e..0000000000
--- a/translated/tech/20160519 The future of sharing integrating Pydio and ownCloud.md	
+++ /dev/null
@@ -1,65 +0,0 @@
-分享的未来:整合 Pydio 与 ownCloud
-=========================================================
-
-![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/BIZ_darwincloud_520x292_0311LL.png?itok=5yWIaEDe)
->图片来源 : 
-opensource.com
-
-开源共享生态圈内容纳了许多各异的项目,他们每一个都能提供出自己的解决方案,且每一个都不按套路来。有很多原因导致你选择开源的解决方案,而非 Dropbox、Google Drive、iCloud 或 OneDrive 这些商业的解决方案。这些商业的解决方案虽然能让你不必为如何管理数据担心,但也理所应当的带着种种限制,其中就包含对于原有基础结构的控制和整合不足。
-
-对于用户而言仍有相当一部分文件分享和同步的替代品可供选择,其中就包括了 Pydio 和 ownCloud。
-
-### Pydio
-
-Pydio (Put your data in orbit 把你的数据放上轨道) 项目由一位作曲家 Charles du Jeu 发起,起初他也只是需要一种与乐队成员分享大型音频文件的方法。[Pydio][1] 是一种文件分享与同步的解决方案,综合了多存储后端,设计时还同时考虑了开发者和系统管理员两方面。在世界各地有逾百万的下载量,已被翻译成 27 种语言。
-
-项目在很开始的时候便开源了,先是在 [SourceForge][2] 上茁壮的成长,现在已在 [GitHub][3] 上安了家.。
-
-用户界面基于 Google 的 [Material 设计][4]。用户可以使用现有的传统的文件基础结构或是本地部署 Pydio,并通过 web、桌面和移动端应用随时随地地管理自己的东西。对于管理员来说,细粒度的访问权限绝对是配置访问时的利器。
-
-在 [Pydio 社区][5],你可以找到许多让你增速的资源。Pydio 网站 [对于如何为 Pydio GitHub 仓库贡献][6] 给出了明确的指导方案。[论坛][7]中也包含了开发者板块和社区。
-
-### ownCloud
-
-[ownCloud][8] 在世界各地拥有逾 8 百万的用户,并且开源,支持自托管文件同步,且共享技术。同步客户端支持所有主流平台并支持 WebDAV 通过 web 界面实现。ownCloud 拥有简单的使用界面,强大的管理工具,和大规模的共享及协作功能——以满足用户管理数据时的需求。
-
-ownCloud 的开放式架构是通过 API 和为应用提供平台来实现可扩展性的。迄今已有逾 300 款应用,功能包括处理像日历、联系人、邮件、音乐、密码、笔记等诸多数据类型。ownCloud 由一个数百位贡献者的国际化的社区开发,安全,并且能做到为小到一个树莓派大到好几百万用户的 PB 级存储集群量身定制。
-
-### 联合共享 (Federated sharing)
-
-文件共享开始转向团队合作时代,而标准化为合作提供了坚实的土壤。
-
-联合共享——一个由 [OpenCloudMesh][9] 项目提供的新开放标准,就是在这个方向迈出的一步。先不说别的,在支持该标准的服务端上,可以像 Pydio 和 ownCloud 那样分享文件和文件夹。
-
-ownCloud 7 率先引入,这种服务端到服务端的分享方式可以让你挂载远程服务端上共享的文件,实际上就是创建你所有云的云。你可以直接创建共享链接,让用户在其他支持联合云共享的服务端上使用。
-
-实现这个新的 API 允许存储解决方案之间更深层次的集成,同时保留了原有平台的安全,控制和属性。
-
-“交换和共享文件是当下和未来不可或缺的东西。”ownCloud 的创始人 Frank Karlitschek 说道:“正因如此,采用联合和分布的方式而非集中的数据孤岛就显得至关重要。[联合共享]的设计初衷便是在保证安全和用户隐私的同时追求分享的无缝、至简之道。”
-
-### 下一步是什么呢?
-
-正如 OpenCloudMesh 做的那样,将会通过像 Pydio 和 ownCloud 这样的机构和公司,合作推广这一文件共享的新开放标准。ownCloud 9 已经引入联合服务端间交换用户列表的功能,让你的用户在你的服务器上享有和你同样的无缝体验。将来,一个中央地址簿服务(联合!)集合,用以检索其他联合云 ID 的想法可能会把云间合作推向一个新的高度。
-
-这一举措无疑有助于日益开放的技术社区中的那些成员方便地讨论,开发,并推动“OCM 分享 API”作为一个厂商中立协议。所有领导 OCM 项目的合作伙伴都全心致力于开放 API 的设计原理,并欢迎其他开源的文件分享和同步社区参与并加入其中。
-
---------------------------------------------------------------------------------
-
-via: https://opensource.com/business/16/5/sharing-files-pydio-owncloud
-
-作者:[ben van 't ende][a]
-译者:[martin2011qi](https://github.com/martin2011qi)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: https://opensource.com/users/benvantende
-[1]: https://pydio.com/
-[2]: https://sourceforge.net/projects/ajaxplorer/
-[3]: https://github.com/pydio/
-[4]: https://www.google.com/design/spec/material-design/introduction.html
-[5]: https://pydio.com/en/community
-[6]: https://pydio.com/en/community/contribute
-[7]: https://pydio.com/forum/f
-[8]: https://owncloud.org/
-[9]: https://wiki.geant.org/display/OCM/Open+Cloud+Mesh
diff --git a/translated/tech/20160524 Writing online multiplayer game with python and asyncio - part 1.md b/translated/tech/20160524 Writing online multiplayer game with python and asyncio - part 1.md
deleted file mode 100644
index 26ddea6b1e..0000000000
--- a/translated/tech/20160524 Writing online multiplayer game with python and asyncio - part 1.md	
+++ /dev/null
@@ -1,71 +0,0 @@
-使用python 和asyncio编写在线多人游戏 - 第1部分
-===================================================================
-
-你曾经把async和python关联起来过吗?在这里我将告诉你怎样做,而且在[working example][1]这个例子里面展示-一个流行的贪吃蛇游戏,这是为多人游戏而设计的。
-[Play game][2]
-
-###1.简介
-
-在技术和文化领域,大量的多人在线游戏毋庸置疑是我们这个世界的主流之一。同时,为一个MMO游戏写一个服务器一般和大量的预算与低水平的编程技术相关,在最近这几年,事情发生了很大的变化。基于动态语言的现代框架允许在稳健的硬件上面处理大量并发的用户连接。同时,HTML5 和 WebSockets 标准允许基于实时的图形游戏直接在web浏览器上创建客户端,而不需要任何的扩展。
-
-对于创建可扩展非堵塞的服务器,Python可能不是最受欢迎的工具,尤其是和在这个领域最受欢迎的node.js相比。但是最近版本的python打算改变这种现状。[asyncio][3]的介绍和一个特别的[async/await][4] 语法使得异步代码看起来像常规的阻塞代码,这使得python成为一个值得信赖的异步编程语言。所以我将尝试利用这些新特点来创建一个多人在线游戏。
-
-###2.异步
-一个游戏服务器应该处理最大数量的用户的并发连接和实时处理这些连接。一个典型的解决方案----创建线程,然而在这种情况下并不能解决这个问题。运行上千的线程需要CPU在它们之间不停的切换(这叫做上下文切换),这将开销非常大,效率很低下。更糟糕的是,因为,此外,它们会占用大量的内存。在python中,还有一个问题,python的解释器(CPython)并不是针对多线程设计的,它主要针对于单线程实现最大数量的行为。这就是为什么它使用GIL(global interpreter lock),一个不允许同时运行多线程python代码的架构,来防止共享物体的不可控用法。正常情况下当当前线程正在等待的时候,解释器转换到另一个线程,通常是一个I/O的响应(像一个服务器的响应一样)。这允许在你的应用中有非阻塞I/O,因为每一个操作仅仅堵塞一个线程而不是堵塞整个服务器。然而,这也使得通常的多线程变得无用,因为它不允许你并发执行python代码,即使是在多核心的cpu上。同时在单线程中拥有非阻塞IO是完全有可能的,因而消除了经常切换上下文的需要。
-
-实际上,你可以用纯python代码来实现一个单线程的非阻塞IO。你所需要的只是标准的[select][5]模块,这个模块可以让你写一个事件循环来等待未阻塞的socket的io。然而,这个方法需要你在一个地方定义所有app的逻辑,不久之后,你的app就会变成非常复杂的状态机。有一些框架可以简化这个任务,比较流行的是[tornade][6] 和 [twisted][7]。他们被用来使用回调方法实现复杂的协议(这和node.js比较相似)。这个框架运行在他自己的事件循环中,这个事件在定义的事件上调用你的回调。并且,这或许是一些情况的解决方案,但是它仍然需要使用回调的方式编程,这使你的代码碎片化。和写同步代码并且并发执行多个副本相比,就像我们会在普通的线程上做一样。这为什么在单个线程上是不可能的呢?
-
-这就是为什么microthread出现的原因。这个想法是为了在一个线程上并发执行任务。当你在一个任务中调用阻塞的方法时,有一个叫做"manager" (或者“scheduler”)的东西在执行事件循环。当有一些事件准备处理的时候,一个manager会让等这个事件的“任务”单元去执行,直到自己停了下来。然后执行完之后就返回那个管理器(manager)。
-
->Microthreads are also called lightweight threads or green threads (a term which came from Java world). Tasks which are running concurrently in pseudo-threads are called tasklets, greenlets or coroutines.(Microthreads 也会被称为lightweight threads 或者 green threads(java中的一个术语)。在伪线程中并发执行的任务叫做tasklets,greenlets或者coroutines).
-
-microthreads的其中一种实现在python中叫做[Stackless Python][8]。这个被用在了一个叫[EVE online][9]的非常有名的在线游戏中,所以它变得非常有名。这个MMO游戏自称说在一个持久的宇宙中,有上千个玩家在做不同的活动,这些都是实时发生的。Stackless 是一个单独的python解释器,它代替了标准的栈调用并且直接控制流来减少上下文切换的开销。尽管这非常有效,这个解决方案不如使用标准解释器的“soft”库有名。像[eventlet][10]和[gevent][11] 的方式配备了标准的I / O库的补丁的I / O功能在内部事件循环执行。这使得将正常的阻塞代码转变成非阻塞的代码变得简单。这种方法的一个缺点是从代码看这并不明显,这被称为非阻塞。Python的新的版本介绍了本地协同程序作为生成器的高级形式。在Python 的3.4版本中,引入了asyncio库,这个库依赖于本地协同程序来提供单线程并发。但是在Python  3.5 协同程序变成了Python语言的一部分,使用新的关键字 async 和 await 来描述。这是一个简单的例子,这表明了使用asyncio来运行 并发任务。
-
-```
-import asyncio
-
-async def my_task(seconds):
-    print("start sleeping for {} seconds".format(seconds))
-    await asyncio.sleep(seconds)
-    print("end sleeping for {} seconds".format(seconds))
-
-all_tasks = asyncio.gather(my_task(1), my_task(2))
-loop = asyncio.get_event_loop()
-loop.run_until_complete(all_tasks)
-loop.close()    
-```
-
-我们启动了两个任务,一个睡眠1秒钟,另一个睡眠2秒钟,输出如下:
-
-```
-start sleeping for 1 seconds
-start sleeping for 2 seconds
-end sleeping for 1 seconds
-end sleeping for 2 seconds
-```
-
-正如你所看到的,协同程序不会阻塞彼此-----第二个任务在第一个结束之前启动。这发生的原因是asyncio.sleep是协同程序,它会返回一个调度器的执行直到时间过去。在下一节中,
-我们将会使用coroutine-based的任务来创建一个游戏循环。
-
---------------------------------------------------------------------------------
-
-via: https://7webpages.com/blog/writing-online-multiplayer-game-with-python-asyncio-getting-asynchronous/
-
-作者:[Kyrylo Subbotin][a]
-译者:[xinglianfly](https://github.com/xinglianfly)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: https://7webpages.com/blog/writing-online-multiplayer-game-with-python-asyncio-getting-asynchronous/
-[1]: http://snakepit-game.com/
-[2]: http://snakepit-game.com/
-[3]: https://docs.python.org/3/library/asyncio.html
-[4]: https://docs.python.org/3/whatsnew/3.5.html#whatsnew-pep-492
-[5]: https://docs.python.org/2/library/select.html
-[6]: http://www.tornadoweb.org/
-[7]: http://twistedmatrix.com/
-[8]: http://www.stackless.com/
-[9]: http://www.eveonline.com/
-[10]: http://eventlet.net/
-[11]: http://www.gevent.org/
diff --git a/translated/tech/20160531 Writing online multiplayer game with python and asyncio - part 2.md b/translated/tech/20160531 Writing online multiplayer game with python and asyncio - part 2.md
deleted file mode 100644
index 73dd55d516..0000000000
--- a/translated/tech/20160531 Writing online multiplayer game with python and asyncio - part 2.md	
+++ /dev/null
@@ -1,234 +0,0 @@
-使用 Python 和 asyncio 编写在线多用人游戏 - 第2部分
-==================================================================
-
-![](https://7webpages.com/media/cache/fd/d1/fdd1f8f8bbbf4166de5f715e6ed0ac00.gif)
-
-你曾经写过异步的 Python 程序吗?这里我将告诉你如果如何做,而且在接下来的部分用一个[实例][1] - 专为多玩家设计的、受欢迎的贪吃蛇游戏来演示。
-
-介绍和理论部分参见第一部分[异步化[第1部分]][2]。
-
-试玩游戏[3]。
-
-### 3. 编写游戏循环主体
-
-游戏循环是每一个游戏的核心。它持续地读取玩家的输入,更新游戏的状态,并且在屏幕上渲染游戏结果。在在线游戏中,游戏循环分为客户端和服务端两部分,所以一般有两个循环通过网络通信。通常客户端的角色是获取玩家输入,比如按键或者鼠标移动,将数据传输给服务端,然后接收需要渲染的数据。服务端处理来自玩家的所有数据,更新游戏的状态,执行渲染下一帧的必要计算,然后将结果传回客户端,例如游戏中对象的位置。如果没有可靠的理由,不混淆客户端和服务端的角色很重要。如果你在客户端执行游戏逻辑的计算,很容易就会和其它客户端失去同步,其实你的游戏也可以通过简单地传递客户端的数据来创建。
-
-游戏循环的一次迭代称为一个嘀嗒。嘀嗒表示当前游戏循环的迭代已经结束,下一帧(或者多帧)的数据已经就绪。在后面的例子中,我们使用相同的客户端,使用 WebSocket 连接服务端。它执行一个简单的循环,将按键码发送给服务端,显示来自服务端的所有信息。[客户端代码戳这里][4]。
-
-#### 例子3.1:基本游戏循环
-
-[例子3.1源码][5]。
-
-我们使用 [aiohttp][6] 库来创建游戏服务器。它可以通过 asyncio 创建网页服务器和客户端。这个库的一个优势是它同时支持普通 http 请求和 websocket。所以我们不用其他网页服务器来渲染游戏的 html 页面。
-
-下面是启动服务器的方法:
-
-```
-app = web.Application()
-app["sockets"] = []
-
-asyncio.ensure_future(game_loop(app))
-
-app.router.add_route('GET', '/connect', wshandler)
-app.router.add_route('GET', '/', handle)
-
-web.run_app(app)
-```
-
-`web.run_app` 是创建服务主任务的快捷方法,通过他的 `run_forever()` 方法来执行 asyncio 事件循环。建议你查看这个方法的源码,弄清楚服务器到底是如何创建和结束的。
-
-`app` 变量就是一个类似于字典的对象,它可以在所连接的客户端之间共享数据。我们使用它来存储连接套接字的列表。随后会用这个列表来给所有连接的客户端发送消息。`asyncio.ensure_future()` 调用会启动主游戏循环的任务,每隔2s向客户端发送嘀嗒消息。这个任务会在同样的 asyncio 事件循环中和网页服务器并行执行。
-
-有两个网页请求处理器:提供 html 页面的处理器 (`handle`);`wshandler` 是主要的 websocket 服务器任务,处理和客户端之间的交互。在事件循环中,每一个连接的客户端都会创建一个新的 `wshandler`。
-
-在启动的任务中,我们在 asyncio 的主事件循环中启动 worker 循环。任务之间的切换发生在他们任何一个使用 `await`语句来等待某个协程结束。例如 `asyncio.sleep` 仅仅是将程序执行权交给调度器指定的时间;`ws.receive` 等待 websocket 的消息,此时调度器可能切换到其它任务。
-
-在浏览器中打开主页,连接上服务器后,试试随便按下键。他们的键值会从服务端返回,每隔2秒这个数字会被游戏循环发给所有客户端的嘀嗒消息覆盖。
-
-我们刚刚创建了一个处理客户端按键的服务器,主游戏循环在后台做一些处理,周期性地同时更新所有的客户端。
-
-#### 例子 3.2: 根据请求启动游戏
-
-[例子 3.2的源码][7]
-
-在前一个例子中,在服务器的生命周期内,游戏循环一直运行着。但是现实中,如果没有一个人连接服务器,空运行游戏循环通常是不合理的。而且,同一个服务器上可能有不同的’游戏房间‘。在这种假设下,每一个玩家创建一个游戏会话(多人游戏中的一个比赛或者大型多人游戏中的副本),这样其他用户可以加入其中。当游戏会话开始时,游戏循环才开始执行。
-
-在这个例子中,我们使用一个全局标记来检测游戏循环是否在执行。当第一个用户发起连接时,启动它。最开始,游戏循环不在执行,标记设置为 `False`。游戏循环是通过客户端的处理方法启动的。
-
-```
-  if app["game_is_running"] == False:
-        asyncio.ensure_future(game_loop(app))
-```
-
-当游戏的循环(`loop()`)运行时,这个标记设置为 `True`;当所有客户端都断开连接时,其又被设置为 `False`。
-
-#### 例子 3.3:管理任务
-
-[例子3.3源码][8]
-
-这个例子用来解释如何和任务对象协同工作。我们把游戏循环的任务直接存储在游戏循环的全局字典中,代替标记的使用。在这个简单例子中并不一定是最优的,但是有时候你可能需要控制所有已经启动的任务。
-
-```
-    if app["game_loop"] is None or \
-       app["game_loop"].cancelled():
-        app["game_loop"] = asyncio.ensure_future(game_loop(app))
-```
-
-这里 `ensure_future()` 返回我们存放在全局字典中的任务对象,当所有用户都断开连接时,我们使用下面方式取消任务:
-
-```
-    app["game_loop"].cancel()
-```
-
-这个 `cancel()` 调用将通知所有的调度器不要向这个协程提交任何执行任务,而且将它的状态设置为已取消,之后可以通过 `cancelled()` 方法来检查是否已取消。这里有一个值得一提的小注意点:当你持有一个任务对象的外部引用时,而这个任务执行中抛出了异常,这个异常不会抛出。取而代之的是为这个任务设置一个异常状态,可以通过 `exception()` 方法来检查是否出现了异常。这种悄无声息地失败在调试时不是很有用。所以,你可能想用抛出所有异常来取代这种做法。你可以对所有未完成的任务显示地调用 `result()` 来实现。可以通过如下的回调来实现:
-
-```
-    app["game_loop"].add_done_callback(lambda t: t.result())
-```
-
-如果我们打算在我们代码中取消任务,但是又不想产生 `CancelError` 异常,有一个检查 `cancelled` 状态的点:
-
-```
-    app["game_loop"].add_done_callback(lambda t: t.result()
-                                       if not t.cancelled() else None)
-```
-
-注意仅当你持有任务对象的引用时必须要这么做。在前一个例子,所有的异常都是没有额外的回调,直接抛出所有异常。
-
-#### 例子 3.4:等待多个事件
-
-[例子 3.4 源码][9]
-
-在许多场景下,在客户端的处理方法中你需要等待多个事件的发生。除了客户端的消息,你可能需要等待不同类型事件的发生。比如,如果你的游戏时间有限制,那么你可能需要等一个来自定时器的信号。或者你需要使用管道来等待来自其它进程的消息。亦或者是使用分布式消息系统网络中其它服务器的信息。
-
-为了简单起见,这个例子是基于例子 3.1。但是这个例子中我们使用 `Condition` 对象来保证已连接客户端游戏循环的同步。我们不保存套接字的全局列表,因为只在方法中使用套接字。当游戏循环停止迭代时,我们使用 `Condition.notify_all()` 方法来通知所有的客户端。这个方法允许在 `asyncio` 的事件循环中使用发布/订阅的模式。
-
-为了等待两个事件,首先我们使用 `ensure_future()` 来封装任务中可以等待的对象。
-
-```
-    if not recv_task:
-        recv_task = asyncio.ensure_future(ws.receive())
-    if not tick_task:
-        await tick.acquire()
-        tick_task = asyncio.ensure_future(tick.wait())
-```
-
-在我们调用 `Condition.wait()` 之前,我们需要在背后获取一把锁。这就是我们为什么先调用 `tick.acquire()` 的原因。在调用 `tick.wait()` 之后,锁会被释放,这样其他的协程也可以使用它。但是当我们收到通知时,会重新获取锁,所以在收到通知后需要调用 `tick.release()` 来释放它。
-
-我们使用 `asyncio.wait()` 协程来等待两个任务。
-
-```
-    done, pending = await asyncio.wait(
-        [recv_task,
-         tick_task],
-        return_when=asyncio.FIRST_COMPLETED)
-```
-
-程序会阻塞,直到列表中的任意一个任务完成。然后它返回两个列表:执行完成的任务列表和仍然在执行的任务列表。如果任务执行完成了,其对应变量赋值为 `None`,所以在下一个迭代时,它可能会被再次创建。
-
-#### 例子 3.5: 结合多个线程
-
-[例子 3.5 源码][10]
-
-在这个例子中,我们结合 asyncio 循环和线程,在一个单独的线程中执行主游戏循环。我之前提到过,由于 `GIL` 的存在,Python 代码的真正并行执行是不可能的。所以使用其它线程来执行复杂计算并不是一个好主意。然而,在使用 `asyncio` 时结合线程有原因的:当我们使用的其它库不支持 `asyncio` 时。在主线程中调用这些库会阻塞循环的执行,所以异步使用他们的唯一方法是在不同的线程中使用他们。
-
-在 asyncio 的循环和 `ThreadPoolExecutor` 中,我们通过 `run_in_executor()` 方法来执行游戏循环。注意 `game_loop()` 已经不再是一个协程了。它是一个由其它线程执行的函数。然而我们需要和主线程交互,在游戏事件到来时通知客户端。asyncio 本身不是线程安全的,它提供了可以在其它线程中执行你的代码的方法。普通函数有 `call_soon_threadsafe()`, 协程有 `run_coroutine_threadsafe()`。我们在 `notify()` 协程中增加代码通知客户端游戏的嘀嗒,然后通过另外一个线程执行主事件循环。
-
-```
-def game_loop(asyncio_loop):
-    print("Game loop thread id {}".format(threading.get_ident()))
-    async def notify():
-        print("Notify thread id {}".format(threading.get_ident()))
-        await tick.acquire()
-        tick.notify_all()
-        tick.release()
-
-    while 1:
-        task = asyncio.run_coroutine_threadsafe(notify(), asyncio_loop)
-        # blocking the thread
-        sleep(1)
-        # make sure the task has finished
-        task.result()
-```
-
-当你执行这个例子时,你会看到 "Notify thread id" 和 "Main thread id" 相等,因为 `notify()` 协程在主线程中执行。与此同时 `sleep(1)` 在另外一个线程中执行,因此它不会阻塞主事件循环。
-
-#### 例子 3.6:多进程和扩展
-
-[例子 3.6 源码][11]
-
-单线程的服务器可能运行得很好,但是它只能使用一个CPU核。为了将服务扩展到多核,我们需要执行多个进程,每个进程执行各自的事件循环。这样我们需要在进程间交互信息或者共享游戏的数据。而且在一个游戏中经常需要进行复杂的计算,例如路径查找。这些任务有时候在一个游戏嘀嗒中没法快速完成。在协程中不推荐进行费时的计算,因为它会阻塞事件的处理。在这种情况下,将这个复杂任务交给并行执行地其它进程可能更合理。
-
-最简单的使用多个核的方法是启动多个使用单核的服务器,就像之前的例子中一样,每个服务器占用不同的端口。你可以使用 `supervisord` 或者其它进程控制的系统。这个时候你需要一个负载均衡器,像 `HAProxy`,使得连接的客户端在多个进程间均匀分布。有一些适配 asyncio 消息系统和存储系统。例如:
-
-- [aiomcache][12] for memcached client
-- [aiozmq][13] for zeroMQ
-- [aioredis][14] for Redis storage and pub/sub
-
-你可以在  github 或者 pypi 上找到其它的安装包,大部分以 `aio` 开头。
-
-使用网络服务在存储持久状态和交互信息时可能比较有效。但是如果你需要进行进程通信的实时处理,它的性能可能不足。此时,使用标准的 unix 管道可能更合适。asyncio 支持管道,这个仓库有个 [使用pipe且比较底层的例子][15]
-
-在当前的例子中,我们使用 Python 的高层库 [multiprocessing][16] 来在不同的核上启动复杂的计算,使用 `multiprocessing.Queue` 来进行进程间的消息交互。不幸的是,当前的 multiprocessing 实现与 asyncio 不兼容。所以每一个阻塞方法的调用都会阻塞事件循环。但是此时线程正好可以起到帮助作用,因为如果在不同线程里面执行 multiprocessing 的代码,它就不会阻塞主线程。所有我们需要做的就是把所有进程间的通信放到另外一个线程中去。这个例子会解释如何使用这个方法。和上面的多线程例子非常类似,但是我们从线程中创建的是一个新的进程。
-
-```
-def game_loop(asyncio_loop):
-    # coroutine to run in main thread
-    async def notify():
-        await tick.acquire()
-        tick.notify_all()
-        tick.release()
-
-    queue = Queue()
-
-    # function to run in a different process
-    def worker():
-        while 1:
-            print("doing heavy calculation in process {}".format(os.getpid()))
-            sleep(1)
-            queue.put("calculation result")
-
-    Process(target=worker).start()
-
-    while 1:
-        # blocks this thread but not main thread with event loop
-        result = queue.get()
-        print("getting {} in process {}".format(result, os.getpid()))
-        task = asyncio.run_coroutine_threadsafe(notify(), asyncio_loop)
-        task.result()
-```
-
-这里我们在另外一个进程中运行 `worker()` 函数。它包括一个执行复杂计算的循环,然后把计算结果放到 `queue` 中,这个 `queue` 是 `multiprocessing.Queue` 的实例。然后我们就可以在另外一个线程的主事件循环中获取结果并通知客户端,就是例子 3.5 一样。这个例子已经非常简化了,它没有合理的结束进程。而且在真实的游戏中,我们可能需要另外一个队列来将数据传递给 `worker`。
-
-有一个项目叫 [aioprocessing][17],它封装了 multiprocessing,使得它可以和 asyncio 兼容。但是实际上它只是和上面例子使用了完全一样的方法:从线程中创建进程。它并没有给你带来任何方便,除了它使用了简单的接口隐藏了后面的这些技巧。希望在 Python 的下一个版本中,我们能有一个基于协程且支持 asyncio 的 multiprocessing 库。
-
-> 注意!如果你从主线程或者主进程中创建了一个不同的线程或者子进程来运行另外一个 asyncio 事件循环,你需要显示地使用 `asyncio.new_event_loop()` 来创建循环,不然的话可能程序不会正常工作。
-
---------------------------------------------------------------------------------
-
-via: https://7webpages.com/blog/writing-online-multiplayer-game-with-python-and-asyncio-writing-game-loop/
-
-作者:[Kyrylo Subbotin][a]
-译者:[chunyang-wen](https://github.com/chunyang-wen)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: https://7webpages.com/blog/writing-online-multiplayer-game-with-python-and-asyncio-writing-game-loop/
-[1]: http://snakepit-game.com/
-[2]: https://7webpages.com/blog/writing-online-multiplayer-game-with-python-asyncio-getting-asynchronous/
-[3]: http://snakepit-game.com/
-[4]: https://github.com/7WebPages/snakepit-game/blob/master/simple/index.html
-[5]: https://github.com/7WebPages/snakepit-game/blob/master/simple/game_loop_basic.py
-[6]: http://aiohttp.readthedocs.org/
-[7]: https://github.com/7WebPages/snakepit-game/blob/master/simple/game_loop_handler.py
-[8]: https://github.com/7WebPages/snakepit-game/blob/master/simple/game_loop_global.py
-[9]: https://github.com/7WebPages/snakepit-game/blob/master/simple/game_loop_wait.py
-[10]: https://github.com/7WebPages/snakepit-game/blob/master/simple/game_loop_thread.py
-[11]: https://github.com/7WebPages/snakepit-game/blob/master/simple/game_loop_process.py
-[12]: https://github.com/aio-libs/aiomcache
-[13]: https://github.com/aio-libs/aiozmq
-[14]: https://github.com/aio-libs/aioredis
-[15]: https://github.com/KeepSafe/aiohttp/blob/master/examples/mpsrv.py
-[16]: https://docs.python.org/3.5/library/multiprocessing.html
-[17]: https://github.com/dano/aioprocessing
diff --git a/translated/tech/20160601 Learn Python Control Flow and Loops to Write and Tune Shell Scripts – Part 2.md b/translated/tech/20160601 Learn Python Control Flow and Loops to Write and Tune Shell Scripts – Part 2.md
deleted file mode 100644
index d7b6bf6c2e..0000000000
--- a/translated/tech/20160601 Learn Python Control Flow and Loops to Write and Tune Shell Scripts – Part 2.md	
+++ /dev/null
@@ -1,285 +0,0 @@
-学习使用 python 控制流和循环来编写和执行 Shell 脚本 —— Part 2
-======================================================================================
-
-在[Python series][1]之前的文章里,我们分享了 Python的一个简介,它的命令行 shell 和 IDLE(译者注:python 自带的一个IDE)。我们也演示了如何进行数值运算,如何用变量存储值,还有如何打印那些值到屏幕上。最后,我们通过一个练习示例讲解了面向对象编程中方法和属性概念。
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/Write-Shell-Scripts-in-Python-Programming.png)
->在 Python 编程中写 Linux Shell 脚本
-
-本篇中,我嫩会讨论控制流(根据用户输入的信息,计算的结果,或者一个变量的当前值选择不同的动作行为)和循环(自动重复执行任务),接着应用到我们目前所学东西中,编写一个简单的 shell 脚本,这个脚本会显示操作系统类型,主机名,内核发行版,版本号和机器硬件名字。
-
-这个例子尽管很基础,但是会帮助我们证明,比起使用一些 bash 工具写 shell 脚本,我们可以使得用 Python OOP 的兼容特性来编写 shell 脚本会更简单些。
-
-换句话说,我们想从这里出发
-
-```
-# uname -snrvm
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/05/Check-Hostname-of-Linux.png)
-> 检查 Linux 的主机号
-
-到
-
-![](http://www.tecmint.com/wp-content/uploads/2016/05/Check-Linux-Hostname-Using-Python-Script.png)
-> 用 Python 脚本来检查 Linux 的主机号
-
-或者
-
-![](http://www.tecmint.com/wp-content/uploads/2016/05/Script-to-Check-Linux-System-Information.png)
-> 用脚本检查 Linux 系统信息
-
-看着不错,不是吗?那我们就挽起袖子,开干吧。
-
-### Python 中的控制流
-
-如我们刚说那样,控制流允许我们根据一个给定的条件,选择不同的输出结果。在 Python 中最简单的实现就是一个 if/else 语句。
-
-基本语法是这样的:
-
-```
-if condition:
-    # action 1
-else:
-    # action 2
-```
-
-当 condition 求值为真(true),下面的代码块就会被执行(`# action 1`代表的部分)。否则,else 下面的代码就会运行。
-condition 可以是任何表达式,只要可以求得值为真或者假。
-
-举个例子:
-
-1. 1 < 3 # 真
-
-2. firstName == "Gabriel" # 对 firstName 为 Gabriel 的人是真,对其他不叫 Gabriel 的人为假
-
- - 在第一个例子中,我们比较了两个值,判断 1 是否小于 3。
- - 在第二个例子中,我们比较了 firstName(一个变量)与字符串 “Gabriel”,看在当前执行的位置,firstName 的值是否等于该字符串。
- - 条件和 else 表达式都必须带着一个冒号(:)。
- - 缩进在 Python 非常重要。同样缩进下的行被认为是相同的代码块。
-
-请注意,if/else 表达式只是 Python 中许多控制流工具的一个而已。我们先在这里了解以下,后面会用在我们的脚本中。你可以在[官方文档][2]中学到更多工具。
-
-### Python 中的循环
-
-简单来说,一个循环就是一组指令或者表达式序列,可以按顺序一直执行,只要一个条件为真,或者在一个列表里一次执行一个条目。
-
-Python 中最简单的循环,就是 for 循环迭代一个给定列表的元素,或者一个字符串从第一个字符开始到最后一个字符结束。
-
-基本语句:
-
-```
-for x in example:
-	# do this
-```
-
-这里的 example 可以是一个列表或者一个字符串。如果是列表,变量 x 就代表列表中每个元素;如果是字符串,x 就代表字符串中每个字符。
-
-```
->>> rockBands = []
->>> rockBands.append("Roxette")
->>> rockBands.append("Guns N' Roses")
->>> rockBands.append("U2")
->>> for x in rockBands:
-    	print(x)
-or
->>> firstName = "Gabriel"
->>> for x in firstName:
-    	print(x)
-```
-
-上面例子的输出如下图所示:
-
-![](http://www.tecmint.com/wp-content/uploads/2016/05/Learn-Loops-in-Python.png)
->学习 Python 中的循环
-
-### Python 模块
-
-很明显,必须有个途径可以保存一系列的 Python 指令和表达式到文件里,然后需要的时候再取出来。
-
-准确来说模块就是这样的。特别地,os 模块提供了一个接口到操作系统的底层,允许我们做许多通常在命令行下的操作。
-
-没错,os 模块包含了许多方法和属性,可以用来调用,就如我们之前文章里讲解的那样。尽管如此,我们需要使用 import 关键词导入(或者叫包含)模块到开发环境里来:
-
-```
->>> import os
-```
-
-我们来打印出当前的工作目录:
-
-```
->>> os.getcwd()
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/05/Learn-Python-Modules.png)
->学习 Python 模块
-
-现在,让我们把所有结合在一起(包括之前文章里讨论的概念),编写需要的脚本。
-
-### Python 脚本
-
-以一个声明开始一个脚本是个不错的想法,表明脚本的目的,发行所依据的证书,和一个修订历史列出所做的修改。尽管这主要是个人喜好,但这会让我们的工作看起来比较专业。
-
-这里有个脚本,可以输出这篇文章最前面展示的那样。脚本做了大量的注释,为了让大家可以理解发生了什么。
-
-在进行下一步之前,花点时间来理解它。注意,我们是如何使用一个 if/else 结构,判断每个字段标题的长度是否比字段本身的值还大。
-
-基于这个结果,我们用空字符去填充一个字段标题和下一个之间的空格。同时,我们使用一定数量的短线作为字段标题与其值之间的分割符。
-
-```
-#!/usr/bin/python3
-# Change the above line to #!/usr/bin/python if you don't have Python 3 installed
-
-# Script name: uname.py
-# Purpose: Illustrate Python's OOP capabilities to write shell scripts more easily
-# License: GPL v3 (http://www.gnu.org/licenses/gpl.html)
-
-# Copyright (C) 2016 Gabriel Alejandro Cánepa
-# ​Facebook / Skype / G+ / Twitter / Github: gacanepa
-# Email: gacanepa (at) gmail (dot) com
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see .
-
-# REVISION HISTORY
-# DATE   	  VERSION AUTHOR    	 CHANGE DESCRIPTION
-# ---------- ------- --------------
-# 2016-05-28 1.0     Gabriel Cánepa    Initial version
-
-# Import the os module
-import os
-
-# Assign the output of os.uname() to the the systemInfo variable
-# os.uname() returns a 5-string tuple (sysname, nodename, release, version, machine)
-# Documentation: https://docs.python.org/3.2/library/os.html#module-os
-systemInfo = os.uname()
-
-# This is a fixed array with the desired captions in the script output
-headers = ["Operating system","Hostname","Release","Version","Machine"]
-
-# Initial value of the index variable. It is used to define the
-# index of both systemInfo and headers in each step of the iteration.
-index = 0
-
-# Initial value of the caption variable.
-caption = ""
-
-# Initial value of the values variable
-values = ""
-
-# Initial value of the separators variable
-separators = ""
-
-# Start of the loop
-for item in systemInfo:
-    if len(item) < len(headers[index]):
-   	 # A string containing dashes to the length of item[index] or headers[index]
-   	 # To repeat a character(s), enclose it within quotes followed
-   	 # by the star sign (*) and the desired number of times.
-   	 separators = separators + "-" * len(headers[index]) + " "
-   	 caption = caption + headers[index] + " "
-   	 values = values + systemInfo[index] + " " * (len(headers[index]) - len(item)) + " "
-    else:
-   	 separators = separators + "-" * len(item) + " "
-   	 caption =  caption + headers[index] + " " * (len(item) - len(headers[index]) + 1)
-   	 values = values + item + " "
-    # Increment the value of index by 1
-    index = index + 1
-# End of the loop
-
-# Print the variable named caption converted to uppercase
-print(caption.upper())
-
-# Print separators
-print(separators)
-
-# Print values (items in systemInfo)
-print(values)
-
-# INSTRUCTIONS:
-# 1) Save the script as uname.py (or another name of your choosing) and give it execute permissions:
-# chmod +x uname.py
-# 2) Execute it:
-# ./uname.py
-```
-
-如果你已经保存上面的脚本到一个文件里,给文件执行权限,并且运行它,像代码底部描述的那样:
-
-```
-# chmod +x uname.py
-# ./uname.py
-```
-
-如果试图运行脚本时,你得到了如下的错误:
-
-```
--bash: ./uname.py: /usr/bin/python3: bad interpreter: No such file or directory
-```
-
-这意味着你没有安装 Python3。如果那样的话,你要么安装 Python3 的包,要么替换解释器那行(如果你跟着下面的步骤去更新 Python 执行文件的软连接,如之前文章里概述的那样,要特别注意并且非常小心):
-
-```
-#!/usr/bin/python3
-```
-
-为
-
-```
-#!/usr/bin/python
-```
-
-这样会导致使用安装好的 Python 2 版本去执行该脚本。
-
-**注意**: 该脚本在 Python 2.x 与 Pyton 3.x 上都测试成功过了。
-
-尽管比较粗糙,你可以认为该脚本就是一个 Python 模块。这意味着你可以在 IDLE 中打开它(File → Open… → Select file):
-
-![](http://www.tecmint.com/wp-content/uploads/2016/05/Open-Python-in-IDLE.png)
->在 IDLE 中打开 Python
-
-一个包含有文件内容的新窗口就会打开。然后执行 Run → Run module(或者按 F5)。脚本的输出就会在原 Shell 里显示出来: 
-
-![](http://www.tecmint.com/wp-content/uploads/2016/05/Run-Python-Script.png)
->执行 Python 脚本
-
-如果你想纯粹用 bash 写一个脚本,也获得同样的结果,你可能需要结合使用 [awk][3],[sed][4],并且借助复杂的方法来存储与获得列表中的元素(忘了提醒使用 tr 命令将小写字母转为大写)
-
-另外,Python 在所有的 Linux 系统版本中集成了至少一个 Python 版本(2.x 或者 3.x,或者两者都有)。你还需要依赖 shell 去完成同样的目标吗,那样你可能会为不同的 shell 编写不同的版本。
-
-这里演示了面向对象编程的特性,会成为一个系统管理员得力的助手。
-
-**注意**:你可以在我的 Github 仓库里获得 [这个 python 脚本][5](或者其他的)。
-
-### 总结
-
-这篇文章里,我们讲解了 Python 中控制流,循环/迭代,和模块的概念。我们也演示了如何利用 Python 中 OOP 的方法和属性,来简化复杂的 shell 脚本。
-
-你有任何其他希望去验证的想法吗?开始吧,写出自己的 Python 脚本,如果有任何问题可以咨询我们。不必犹豫,在分割线下面留下评论,我们会尽快回复你。
-
---------------------------------------------------------------------------------
-
-via: http://www.tecmint.com/learn-python-programming-to-write-linux-shell-scripts/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+tecmint+%28Tecmint%3A+Linux+Howto%27s+Guide%29
-
-作者:[Gabriel Cánepa][a]
-译者:[wi-cuckoo](https://github.com/wi-cuckoo)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.tecmint.com/author/gacanepa/
-[1]: http://www.tecmint.com/learn-python-programming-and-scripting-in-linux/
-[2]: http://please%20note%20that%20the%20if%20/%20else%20statement%20is%20only%20one%20of%20the%20many%20control%20flow%20tools%20available%20in%20Python.%20We%20reviewed%20it%20here%20since%20we%20will%20use%20it%20in%20our%20script%20later.%20You%20can%20learn%20more%20about%20the%20rest%20of%20the%20tools%20in%20the%20official%20docs.
-[3]: http://www.tecmint.com/use-linux-awk-command-to-filter-text-string-in-files/
-[4]: http://www.tecmint.com/sed-command-to-create-edit-and-manipulate-files-in-linux/
-[5]: https://github.com/gacanepa/scripts/blob/master/python/uname.py
-
diff --git a/translated/tech/20160602 How to mount your Google Drive on Linux with google-drive-ocamlfuse.md b/translated/tech/20160602 How to mount your Google Drive on Linux with google-drive-ocamlfuse.md
deleted file mode 100644
index b64fa51ab0..0000000000
--- a/translated/tech/20160602 How to mount your Google Drive on Linux with google-drive-ocamlfuse.md	
+++ /dev/null
@@ -1,68 +0,0 @@
-教你用 google-drive-ocamlfuse 在 Linux 上挂载 Google Drive
-=====================
-
->如果你在找一个方便的方式在 Linux 机器上挂载你的 Google Drive 文件夹, Jack Wallen 将教你怎么使用 google-drive-ocamlfuse 来挂载 Google Drive。
-
-![](http://tr4.cbsistatic.com/hub/i/2016/05/18/ee5d7b81-e5be-4b24-843d-d3ca99230a63/651be96ac8714698f8100afa6883e64d/linuxcloudhero.jpg)
->图片来源: Jack Wallen
-
-Google 还没有发行 Linux 版本的 Google Drive 应用,尽管现在有很多方法从 Linux 中访问你的 Drive 文件。
-(注:不清楚 app 需不需要翻译成应用,这里翻译了)
-
-如果你喜欢界面化的工具,你可以选择 Insync。如果你喜欢用命令行,这有很多工具,像 Grive2 和用 Ocaml 语言编写的、非常容易使用的、基于 FUSE 的系统(注:there are tools such as Grive2 and the incredibly easy to use FUSE-based system written in Ocaml. 这一句感觉翻译不出来)。我将会用后面这种方式演示如何在 Linux 桌面上挂载你的 Google Drive。尽管这是通过命令行完成的,但是它的用法会简单到让你吃惊。它太简单了以至于谁都能做到。
-
-系统特点:
-
-- 对普通文件/文件夹有完全的读写权限
-- 对于 Google Docs,sheets,slides 这三个应用只读
-- 能够访问 Drive 回收站(.trash)
-- 处理重复文件功能
-- 支持多个帐号
-
-接下来完成 google-drive-ocamlfuse 在 Ubuntu 16.04 桌面的安装,然后你就能够访问云盘上的文件了。
-
-### 安装
-
-1. 打开终端。
-2. 用`sudo add-apt-repository ppa:alessandro-strada/ppa`命令添加必要的 PPA
-3. 出现提示的时候,输入密码并按下回车。
-4. 用`sudo apt-get update`命令更新应用。
-5. 输入`sudo apt-get install google-drive-ocamlfuse`命令安装软件。
-(注:这里,我把所有的命令加上着重标记了)
-
-### 授权
-
-接下来就是授权 google-drive-ocamlfuse,让它有权限访问你的 Google 账户。先回到终端窗口敲下命令 google-drive-ocamlfuse,这个命令将会打开一个浏览器窗口,它会提示你登陆你的 Google 帐号或者如果你已经登陆了 Google 帐号,它会询问是否允许 google-drive-ocamlfuse 访问 Google 账户。如果你还没有登陆,先登陆然后点击允许。接下来的窗口(在 Ubuntu 16.04 桌面上会出现,但不会出现在基本系统 Freya 桌面上)将会询问你是否授给 gdfuse 和 OAuth2 Endpoint访问你的 Google 账户的权限,再次点击允许。然后出现的窗口就会告诉你等待授权令牌下载完成,这个时候就能最小化浏览器了。当你的终端提示像图 A 一样的内容,你就能知道令牌下载完了,并且你已经可以挂载 Google Drive 了。
-
-**图 A**
-
-![](http://tr4.cbsistatic.com/hub/i/r/2016/05/18/a493122b-445f-4aca-8974-5ec41192eede/resize/620x/6ae5907ad2c08dc7620b7afaaa9e389c/googledriveocamlfuse3.png)
->图片来源: Jack Wallen
-
-**应用已经得到授权,你可以进行后面的工作。**
-
-### 挂载 Google Drive
-
-在挂载 Google Drive 之前,你得先创建一个文件夹,作为挂载点。在终端里,敲下`mkdir ~/google-drive`命令在你的家目录下创建一个新的文件夹。最后敲下命令`google-drive-ocamlfuse ~/google-drive`将你的 Google Drive 挂载到 google-drive 文件夹中。
-
-这时你可以查看本地 google-drive 文件夹中包含的 Google Drive 文件/文件夹。你能够把 Google Drive 当作本地文件系统来进行工作。
-
-当你想 卸载 google-drive 文件夹,输入命令 `fusermount -u ~/google-drive`。
-
-### 没有 GUI,但它特别好用
-
-我发现这个特别的系统非常容易使用,在同步 Google Drive 时它出奇的快,并且这可以作为一种巧妙的方式备份你的 Google Drive 账户。
-
-试试 google-drive-ocamlfuse,看看你能用它做出什么有趣的事。
-
---------------------------------------------------------------------------------
-
-via: http://www.techrepublic.com/article/how-to-mount-your-google-drive-on-linux-with-google-drive-ocamlfuse/
-
-作者:[Jack Wallen ][a]
-译者:[GitFuture](https://github.com/GitFuture)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.techrepublic.com/search/?a=jack+wallen
diff --git a/translated/tech/20160602 Web Service Efficiency at Instagram with Python.md b/translated/tech/20160602 Web Service Efficiency at Instagram with Python.md
new file mode 100644
index 0000000000..b35fad81eb
--- /dev/null
+++ b/translated/tech/20160602 Web Service Efficiency at Instagram with Python.md	
@@ -0,0 +1,78 @@
+Instagram Web 服务效率与 Python
+===============================================
+
+Instagram 目前是世界上最大规模部署 Django web 框架(该框架完全使用 Python 编写)的主角。我们最初选用 Python 是因为它久负盛名的简洁性与实用性,这非常符合我们的哲学思想——“先做简单的事情”。但简洁性也会带来效率方面的折衷。Instagram 的规模在过去两年中已经翻番,并且最近已突破 5 亿用户,所以急需最大程度地提升 web 服务效率以便我们的平台能够继续顺利地扩大。在过去的一年,我们已经将效率计划(efficiency program)提上日程,并在过去的六个月,我们已经能够做到无需向我们的 Django 层(Django tiers)添加新的容量来维护我们的用户增长。我们将在本文分享一些由我们构建的工具以及如何使用它们来优化我们的日常部署流程。
+
+### 为何需要提升效率?
+
+Instagram,正如所有的软件,受限于像服务器和数据中心能源这样的样物理限制。鉴于这些限制,在我们的效率计划中有两个我们希望实现的主要目标:
+
+1. Instagram 应当能够利用持续代码发布提供正常地通信服务,防止因为自然灾害、区域性网络问题等造成某一个数据中心区丢失。
+2. Instagram 应当能够自由地滚动发布新产品和新功能,不必因容量而受阻。
+
+想要实现这些目标,我们意识到我们需要持续不断地监控我们的系统并在战斗中回归(battle regression)。
+
+
+### 定义效率
+
+Web 服务器的瓶颈通常在于每台服务器上可用的 CPU 时间。在这种环境下,效率就意味着利用相同的 CPU 资源完成更多的任务,也就是说,每秒处理更多的用户请求(requests per second, RPS)。当我们寻找优化方法时,我们面临的第一个最大的挑战就是尝试量化我们当前的效率。到目前为止,我们一直在使用“每次请求的平均 CPU 时间”来评估效率,但使用这种指标也有其固有限制:
+
+1. 设备多样性。使用 CPU 时间来测量 CPU 资源并非理想方案,因为它同时受到 CPU 模型与 CPU 负载影响。
+1. 请求影响数据。测量每次请求的 CPU 资源并非理想方案,因为在使用每次请求测量(per-request measurement)方案时,添加或移除轻量级或重量级的请求也会影响到效率指标。
+
+相对于 CPU 时间来说,CPU 指令是一种更好的指标,因为对于相同的请求,它会报告相同的数字,不管 CPU 模型和 CPU 负载情况如何。我们选择使用了一种叫做”每个活动用户(per active user)“的指标,而不是将我们所有的数据链接到每个用户请求上。我们最终采用”每个活动用户在高峰期间的 CPU 指令(CPU instruction per active user during peak minute)“来测量效率。我们建立好新的度量标准后,下一步就是通过对 Django 的分析来学习更多关于我们的回归(our regressions)。
+
+### Django 服务分析
+
+通过分析我们的 Django web 服务,我们希望回答两个主要问题:
+
+1. 一次 CPU 回归会发生吗?
+2. 是什么导致了 CPU 回归问题发生以及我们该怎样修复它?
+
+想要回答第一个问题,我们需要追踪”每个活动用户的 CPU 指令(CPU-instruction-per-active-user)“指标。如果该指标增加,我们就知道一次 CPU 回归已经发生了。
+
+我们为此构建的工具叫做 Dynostats。Dynostats 利用 Django 中间件以一定的速率采样用户请求,记录键效率以及性能指标,例如 CPU 总指令数、端到端请求时延、花费在访问内存缓存(memcache)和数据库服务的时间等。另一方面,每个请求都有很多可用于聚合的元数据(metadata),例如端点名称、HTTP 请求返回码、服务该请求的服务器名称以及请求中最新提交的哈希值(hash)。对于单个请求记录来说,有两个方面非常强大,因为我们可以在不同的维度上进行切割,那将帮助我们减少任何导致 CPU 回归的原因。例如,我们可以根据他们的端点名称聚合所有请求,正如下面的时间序列图所示,从图中可以清晰地看出在特定端点上是否发生了回归。
+
+![](https://d262ilb51hltx0.cloudfront.net/max/800/1*3iouYiAchYBwzF-v0bALMw.png)
+
+CPU 指令对测量效率很重要——当然,它们也很难获得。Python 并没有支持直接访问 CPU 硬件计数器(CPU 硬件计数器是指可编程 CPU 寄存器,用于测量性能指标,例如 CPU 指令)的公共库。另一方面,Linux 内核提供了 `perf_event_open` 系统调用。通过 Python ctypes 桥接技术能够让我们调用标准 C 库编写的系统调用函数,它也为我们提供了兼容 C 的数据类型,从而可以编程硬件计数器并从它们读取数据。
+ 
+使用 Dynostats,我们已经可以找出 CPU 回归,并探究 CPU 回归发生的原因,例如哪个端点受到的影响最多,谁提交了真正会导致 CPU 回归的变更等。然而,当开发者收到他们的变更已经导致一次 CPU 回归发生的通知时,他们通常难以找出问题所在。如果问题很明显,那么回归可能就不会一开始就被提交!
+ 
+这就是为何我们需要一个 Python 分析器,从而使开发者能够使用它找出回归(一旦 Dynostats 发现了它)发生的根本原因。不同于白手起家,我们决定对一个现成的 Python 分析器 cProfile 做适当的修改。cProfile 模块通常会提供一个统计集合来描述程序不同的部分执行时间和执行频率。我们将 cProfile 的定时器(timer)替换成了一个从硬件计数器读取的 CPU 指令计数器,以此取代对时间的测量。我们在采样请求后产生数据并把数据发送到数据流水线。我们也会发送一些我们在 Dynostats 所拥有的类似元数据,例如服务器名称、集群、区域、端点名称等。
+在数据流水线的另一边,我们创建了一个消费数据的尾随者(tailer)。尾随者的主要功能是解析 cProfile 的统计数据并创建能够表示 Python 函数级别的 CPU 指令的实体。如此,我们能够通过 Python 函数来聚集 CPU 指令,从而更加方便地找出是什么函数导致了 CPU 回归。
+
+### 监控与警报机制
+
+在 Instagram,我们 [每天部署 30-50 次后端服务][1]。这些部署中的任何一个都能发生 CPU 回归的问题。因为每次发生通常都包含至少一个区别(diff),所以找出任何回归是很容易的。我们的效率监控机制包含在每次发布前后都会在 Dynostats 中哦了过扫描 CPU 指令,并且当变更超出某个阈值时发出警告。对于长期会发生 CPU 回归的情况,我们也有一个探测器为负载最繁重的端点提供日常和每周的变更扫描。
+
+部署新的变更并非触发一次 CPU 回归的唯一情况。在许多情况下,新的功能和新的代码路径都由全局环境变量(global environment variables, GEV)控制。 在一个计划好的时间表上,给一个用户子集发布新功能有一些非常一般的实践。我们在 Dynostats 和 cProfile 统计数据中为每个请求添加了这个信息作为额外的元数据字段。来自这些字段的组请求通过转变全局环境变量(GEV),从而暴露出可能的 CPU 回归问题。这让我们能够在它们对性能造成影响前就捕获到 CPU 回归。
+
+
+### 接下来是什么?
+
+Dynostats 和我们定制的 cProfile,以及我们建立去支持它们的监控和警报机制能够有效地找出大多数导致 CPU 回归的元凶。这些进展已经帮助我们恢复了超过 50% 的不必要的 CPU 回归,否则我们就根本不会知道。
+
+我们仍然还有一些可以提升的方面并可以更加便捷将它们地加入到 Instagram 的日常部署流程中: 
+
+1. CPU 指令指标应该要比其它指标如 CPU 时间更加稳定,但我们仍然观察了让我们头疼的差异。保持信号“信噪比(noise ratio)”合理地低是非常重要的,这样开发者们就可以集中于真实的回归上。这可以通过引入置信区间(confidence intervals)的概念来提升,并在信噪比过高时发出警报。针对不同的端点,变化的阈值也可以设置为不同值。
+2. 通过更改 GEV 来探测 CPU 回归的一个限制就是我们要在 Dynostats 中手动启用这些比较的日志输出。当 GEV 逐渐增加,越来越多的功能被开发出来,这就不便于扩展了。
+作为替代,我们能够利用一个自动化框架来调度这些比较的日志输出,并对所有的 GEV 进行遍历,然后当检查到回归时就发出警告。
+3. cProfile 需要一些增强以便更好地处理装饰器函数以及它们的子函数。
+
+鉴于我们在为 Instagram 的 web 服务构建效率框架中所投入的工作,所以我们对于将来使用 Python 继续扩展我们的服务很有信心。
+我们也开始向 Python 语言自身投入更多,并且开始探索从  Python 2 转移 Python 3 之道。我们将会继续探索并做更多的实验以继续提升基础设施与开发者效率,我们期待着很快能够分享更多的经验。
+
+
+--------------------------------------------------------------------------------
+
+via: https://engineering.instagram.com/web-service-efficiency-at-instagram-with-python-4976d078e366#.tiakuoi4p
+
+作者:[Min Ni][a]
+译者:[ChrisLeeGit](https://github.com/chrisleegit)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://engineering.instagram.com/@InstagramEng?source=post_header_lockup
+[1]: https://engineering.instagram.com/continuous-deployment-at-instagram-1e18548f01d1#.p5adp7kcz
diff --git a/translated/tech/20160615 Excel Filter and Edit - Demonstrated in Pandas.md b/translated/tech/20160615 Excel Filter and Edit - Demonstrated in Pandas.md
new file mode 100644
index 0000000000..1742ab1f98
--- /dev/null
+++ b/translated/tech/20160615 Excel Filter and Edit - Demonstrated in Pandas.md	
@@ -0,0 +1,342 @@
+Excel “过滤和编辑” - Pandas中的示范操作
+==================================================
+
+![](http://pbpython.com/images/Boolean-Indexing-Example.png)
+
+### 介绍
+
+我听许多人说我[之前的关于pandas处理一般的Excel任务][1]的文章对于帮助新的pandas用户将Excel任务转化成对应的pandas代码是有用的。这边文章将会保持那个传统通过介绍不同的pandas使用Excel的过滤功能作为一个模型进行索引的例子来理解这样的处理。
+
+大多数pandas的新手首先学到的东西就是基本的数据过滤。即使在过去的时间中使用pandas工作持续数月,我最近意识到我没有在我每天的工作中使用的pandas过滤的方法还有另外一个好处。也就是说,你可以在一个给定的列上过滤,但更新另一组列通过使用一个简化的pandas的语法。这和我所说的Excel中的“过滤和编辑”很像。
+
+这篇文章将会介绍一些过滤一个pandas数据帧同时更新各种标准的例子。同时,我将解释关于panda的索引和如何使用索引功能例如 .loc , .ix和 .iloc来简单快速的更新一个基于简单或者是复杂标准的数据子集。
+
+### Excel: “过滤与编辑”
+
+在数据透视表以外,在EXCEL工具的顶部是一过滤器。这个简单的工具允许用户通过不同的数据,文本与格式标准去快速过滤与排序数据。这里是由几个不同的条件过滤数据产生的样本数据的基本截图:
+
+![](http://pbpython.com/images/filter-example.png)
+该过滤过程是直观的,即使是最新手的用户,也很容易掌握。我也注意到,人们将使用此功能来选择行的数据,然后根据行的标准更新额外的列。下面的例子显示了我所描述的情形:
+
+![](http://pbpython.com/images/commission-example.png)
+
+在这个例子中,我过滤了Account Number, SKU和Unit Price的数据。接着我手工增加了Commission_Rate这列并且在每个单元格中输入0.01。这种方法的好处是可以很容易理解和管理有联系的复杂数据而不用写长Excel公式或者进入VBA。这种方法的缺点是,它是不可重复的,同时,对于从外面入门的人了解哪些标准被用于任何过滤器可能是困难的。
+
+例如,假设你看相关的截图,如果不看每一列数据,没有明显的方法去知道什么被过滤了。幸运的是,我们可以在pandas中完成相似操作。不奇怪的是,在pandas中使用简单干净的代码来执行“过滤和编辑”模式是简单的。
+
+
+### 布尔检索
+
+现在,我想通过pandas中一些布尔索引的细节来使你对这个问题有点感觉。
+如果你想要去理解pandas的在大部分广泛的[索引和数据选择][3]这是一个重要的概念去理解。这种想法可能看起来有点复杂,对新的pandas用户(可能对经验丰富的用户来说太基础),但我认为重要的是要花一些时间,了解它。如果你掌握了这一概念,用pandas进行数据工作的基本过程将更简单。
+
+pandas支持索引(或者选择数据)通过使用标签,基于数字的位置或者一个布尔值的队列(True/False)。使用一个布尔值的列表来选择一个行被称为布尔索引,将是本文其余部分的重点。
+
+我发现,我的pandas的工作流程往往侧重于使用布尔值的列表选择我的数据。换句话说,当我创建了pandas数据帧,我倾向于保持默认的索引在数据框架。因此,索引本身并不具有真正意义,同时也不是简单的选择数据。
+
+
+
+>关键点
+> pandas中布尔索引是一个(几个)选择行的数据的强大的和有用的方法。
+
+让我们看一些例子,数据框架帮助澄清布尔指标在pandas中做的是什么。
+
+首先,我们将创建一个非常小的数据帧仅仅从一个Python的列表获得并使用它来展示布尔检索是如何工作的。
+
+
+```
+import pandas as pd
+sales = [('account', ['Jones LLC', 'Alpha Co', 'Blue Inc', 'Mega Corp']),
+         ('Total Sales', [150, 200, 75, 300]),
+         ('Country', ['US', 'UK', 'US', 'US'])]
+df = pd.DataFrame.from_items(sales)
+```
+
+ |account	|Total Sales	|Country
+:--|:--     |:--          |:
+0	|Jones LLC	|150	|US
+1	|Alpha Co	|200	|UK
+2	|Blue Inc	|75	|US
+3	|Mega Corp	|300	|US
+
+注意值0-3,是怎么样会自动分配给行?这些都是指数,在这个数据集它们不是特别有意义的,但对pandas是有用的,重要的是要了解如下其他没有描述的使用情况下。
+
+当我们引用布尔索引时,我们只是说,我们可以通过一个真实或错误的值的列表表示我们要查看的每一行。
+
+
+在这种情况下,如果我们想查看Jones有限责任公司,Blue公司和Mega公司的数据,我们可以看到,True False名单会看起来像这样:
+
+```
+indices = [True, False, True, True]
+```
+它应该是毫不奇怪,你可以通过把这个列表,传入你的数据,你就看到它只会显示帧中我们的值是True的行:
+
+```
+df[indices]
+```
+
+ |account	|Total Sales	|Country
+:--|:--|:--|:--
+0	|Jones LLC	|150	|US
+2	|Blue Inc	|75	|US
+3	|Mega Corp	|300	|US
+
+这里是一个刚刚发生的操作图像:
+
+![](http://pbpython.com/images/Boolean-Indexing-Example.png)
+
+手工的列表创建索引是工作的。但是显然对任何一个超过微不足道的数据集不是可扩展或是很有用的。幸运的是,pandas就可以用一个简单的查询语言,有熟悉用Python创建这些布尔指标应该很容易(或任何语言)。
+
+例如,让我们看看来自美国的所有销售线。如果我们执行的Python表达式基于Country列:
+
+
+```
+df.Country == 'US'
+```
+
+```
+0     True
+1    False
+2     True
+3     True
+Name: Country, dtype: bool
+```
+
+这个例子显示了pandas如何将你的Python的传统逻辑,把它应用到一个数据帧并返回一个布尔值列表。那么这个布尔值的列表可以通过帧的数据获取相应的行。
+
+在真正的代码中,你不需要做这两个步骤。简洁的做这事的方法典型的看上去如下:
+
+```
+df[df["Country"] == 'US']
+```
+
+ |account	|Total Sales	|Country
+:--|:--|:--|:--
+0	|Jones LLC	|150|	US
+2	|Blue Inc	|75	|US
+3	|Mega Corp|	300|	US
+
+虽然这个概念很简单,你可以编写非常复杂的逻辑,使用Python的威力过滤数据。
+
+
+>关键点
+>在这个例子中, `df[df.Country == 'US']` 等价于 `df[df["Country"] == 'US']`  ‘.’ 标记法是简洁的但是在你列名有空格时不会工作。
+
+
+### 选择需要的列
+
+现在,我们已经找到了如何选择行的数据,我们如何控制哪些列显示?在上面的例子中,没有明显的方法去做。Pandas能使用三种基于位置的索引支持这个用法:.loc , iloc , 和 .ix . 。这些功能也允许我们选择除了我们所看到的行挑选列。
+
+
+
+在这里有很大的困惑关于什么时候选择使用 .loc , iloc , 或者是 .ix。 快速总结的区别是:
+
+- .loc 用于标签索引
+- .iloc 用于基于整数的位置
+- .ix 是一个将尝试使用标签的快捷方式 (like .loc ) 但失败后将回落到基于位置的整数 (like .iloc )
+
+因此问题是我该使用哪个?我必须坦诚我有的时候也会被这些搞混.我发现我最常使用 .loc 。主要是因为我的数据不适合于有意义的基于位置的索引(换句话说,我很少发现自己需要使用 .iloc ) 所以我坚持使用 .loc。
+
+公平讲,每一个方法都有自己的位置,在许多情况下都是有用的。特别的一个场景是当处理多指标数据帧时。我不会在这篇文章中讨论那个话题-也许在未来的博文会说。
+
+现在我们已经涵盖了这样一个话题,让我们来展示如何筛选一行的值的数据帧,具体选择要显示的列。
+
+继续我们的例子,如果我们只是想显示对应于我们的指数的帐户名称,该怎么做?使用.loc很简单:
+
+
+```
+df.loc[[True, True, False, True], "account"]
+```
+
+```
+1     Alpha Co
+2     Blue Inc
+3    Mega Corp
+Name: account, dtype: object
+```
+
+如果你想看到多个列,只需通过一个列表:
+
+```
+df.loc[[True, True, False, True], ["account", "Country"]]
+```
+
+ |	account	|Country
+:--|:--|:--
+0	|Jones LLC|	US
+1	|Alpha Co	|UK
+3	|Mega Corp	|US
+
+真正的威力是当你在你的数据上创建更复杂的查询。在这种情况下,让我们展示所有的帐户名称和销售> 200的国家:
+
+```
+df.loc[df["Total Sales"] > 200, ["account", "Country"]]
+```
+
+|account|	Country
+	:--|:--|:--
+3	|Mega Corp|	US
+
+这个过程可以被认为有点相当于我们上面讨论过的Excel的过滤器。你已经加入了额外的好处,你也可以限制你检索的列数,而不仅仅是行。
+
+
+### 编辑列
+
+所有这些都是好的背景,但当你使用一个类似的方法来更新一个或多个列的基础上的行选择这个过程真的是闪光的。
+
+
+一个简单的例子,让我们增加一个佣金率列到我们的数据:
+
+```
+df["rate"] = 0.02
+```
+
+ |	account	|Total Sales|	Country	|rate
+:--|:--|:--|:--|:--
+0	|Jones LLC	|150	|US	|0.02
+1	|Alpha Co	|200	|UK	|0.02
+2	|Blue Inc	|75	|US	|0.02
+3	|Mega Corp	|300	|US	|0.02
+
+让我们说,如果你卖了超过100,你的利率是5%。基本的过程是设置一个布尔索引来选择列,然后将该值赋给利率列:
+
+
+```
+df.loc[df["Total Sales"] > 100, ["rate"]] = .05
+```
+
+ |	account	|Total Sales|	Country|	rate
+:--|:--|:--|:--|:--
+0	|Jones LLC	|150|	US|	0.05
+1	|Alpha Co	|200	|UK	|0.05
+2|	Blue Inc	|75|	US	|0.02
+3	|Mega Corp	|300|	US|	0.05
+
+希望如果你看过这篇文章,这将是有意义的,它会帮助你理解这个语法是如何工作的。现在你有了“过滤和编辑”方法的基本原理。最后一节将更详细的在Excel和pandas显示这个过程。
+
+
+### 将这些合并在一起
+
+对于最后的例子,我们将创建一个简单的佣金计算器,使用以下规则:
+
+- 所有的佣金计算在交易水平
+- 所有销售的基础佣金为2%
+- 所有衬衫都将获得2.5%的佣金
+- 一个特殊的程序正在进行中,在一个交易中销售的数额>10带得到4%的佣金
+- 所有在一个单一的交易鞋类销售> 1000美元有一个特别的奖金为250美元加上一个4.5%的的佣金
+
+为了在Excel做到这一点,使用的过滤器和编辑方法:
+
+- 添加一个2%的佣金列
+- 添加一个0美元的奖金列
+- 衬衫上的过滤器,并将vale改为2.5%
+- 清楚过滤器
+- 带和数量的过滤器>10和将值更改为4%
+- 清除过滤器
+- 鞋类过滤器> 1000美元,并增加佣金和奖金价值分别为4.5%和250美元
+
+
+我不会显示每一步的屏幕快照,但这里是最后一个过滤器的屏幕快照:
+
+![](http://pbpython.com/images/filter-2.png)
+
+这种方法很简单,可以在很好的操作,但它不具备很好的可重复性,也不能审核。当然还有其他的方法,如在Excel公式或VBA实现这些。然而,这种过滤器和编辑的方法是常见的,是说明性的pandas的逻辑。
+
+现在, 让我们在pandas中运行这个例子.
+
+首先,读入[Excel 文件][4]同时加入默认值2%的利息一列:
+```
+import pandas as pd
+df = pd.read_excel("https://github.com/chris1610/pbpython/blob/master/data/sample-sales-reps.xlsx?raw=true")
+df["commission"] = .02
+df.head()
+```
+
+ |	account number|	customer name|	sales rep|	sku	|category	|quantity	|unit price|	ext price|	date|	commission
+:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--
+0	|680916	|Mueller and Sons	|Loring Predovic	|GP-14407	| Belt	|19	|88.49	|1681.31	|2015-11-17 05:58:34|	0.02
+1	|680916	|Mueller and Sons	|Loring Predovic	|FI-01804|	Shirt	|3|	78.07|	234.21	|2016-02-13 04:04:11	|0.02
+2	|530925	|Purdy and Sons|	Teagan O’Keefe	|EO-54210	|Shirt	|19	|30.21	|573.99	|2015-08-11 12:44:38	|0.02
+3	|14406|	Harber, Lubowitz and Fahey|	Esequiel Schinner|	NZ-99565|	Shirt|	12|	90.29	|1083.48	|2016-01-23 02:15:50	|0.02
+4	|398620|	Brekke Ltd	|Esequiel Schinner	|NZ-99565	|Shirt	|5|	72.64	|363.20	|2015-08-10 07:16:03	|0.02
+
+下一个规则是所有衬衫获得2.5%和带销售>10得到一个4%的利息:
+
+```
+df.loc[df["category"] == "Shirt", ["commission"]] = .025
+df.loc[(df["category"] == "Belt") & (df["quantity"] >= 10), ["commission"]] = .04
+df.head()
+```
+
+|	account number	|customer name	|sales rep|	sku	|category	|quantity|	unit price|	ext price	|date	|commission
+	:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--
+0	|680916|	Mueller and Sons|	Loring Predovic|	GP-14407|	Belt|	19	|88.49|	1681.31	|2015-11-17 05:58:34|	0.040
+1	|680916|	Mueller and Sons|	Loring Predovic|	FI-01804|	Shirt	|3	|78.07	|234.21|	2016-02-13 04:04:11	|0.025
+2	|530925	|Purdy and Sons	|Teagan O’Keefe|	EO-54210	|Shirt|	19	|30.21	|573.99	|2015-08-11 12:44:38|	0.025
+3	|14406|	Harber, Lubowitz and Fahey|	Esequiel Schinner|	NZ-99565|	Shirt|	12|	90.29|	1083.48|	2016-01-23 02:15:50	|0.025
+4	|398620|	Brekke Ltd|	Esequiel Schinner|	NZ-99565|	Shirt|	5	|72.64	|363.20|	2015-08-10 07:16:03	|0.025
+
+最后的佣金规则是加上特别的奖金:
+
+```
+df["bonus"] = 0
+df.loc[(df["category"] == "Shoes") & (df["ext price"] >= 1000 ), ["bonus", "commission"]] = 250, 0.045
+
+# Display a sample of rows that show this bonus
+df.ix[3:7]
+```
+
+|	account number|	customer name	|sales rep|	sku	|category|	quantity|	unit price|	ext price|	date|	commission|	bonus
+	:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--|:--
+3|	14406|	Harber, Lubowitz and Fahey|	Esequiel Schinner|	NZ-99565|	Shirt	|12	|90.29|	1083.48	|2016-01-23 02:15:50	|0.025	|0
+4|	398620|	Brekke Ltd|	Esequiel Schinner	|NZ-99565|	Shirt|	5|	72.64|	363.20|	2015-08-10 07:16:03|	0.025|	0
+5|	282122|	Connelly, Abshire and Von	Beth| Skiles|	GJ-90272|	Shoes|	20|	96.62|	1932.40	|2016-03-17 10:19:05	|0.045	|250
+6	|398620	|Brekke Ltd|	Esequiel Schinner	|DU-87462	|Shirt|	10|	67.64	|676.40|	2015-11-25 22:05:36|	0.025|	0
+7|	218667|	Jaskolski-O’Hara|	Trish Deckow|	DU-87462|	Shirt	|11|	91.86|	1010.46	|2016-04-24 15:05:58|	0.025|	0
+
+为了做好佣金的计算:
+
+```
+#  Calculate the compensation for each row
+df["comp"] = df["commission"] * df["ext price"] + df["bonus"]
+
+# Summarize and round the results by sales rep
+df.groupby(["sales rep"])["comp"].sum().round(2)
+```
+
+```
+sales rep
+Ansley Cummings       2169.76
+Beth Skiles           3028.60
+Esequiel Schinner    10451.21
+Loring Predovic      10108.60
+Shannen Hudson        5275.66
+Teagan O'Keefe        7989.52
+Trish Deckow          5807.74
+Name: comp, dtype: float64
+```
+
+
+如果你有兴趣,一个例子手册被托管在[github][5].
+
+### 总结
+
+
+感谢阅读文章。 我发现,在学习如何使用pandas的新用户的最大的挑战之一是如何找出如何使用他们的基于Excel的知识,以建立一个等效的pandas为基础的解决方案。在许多情况下,andas的解决方案将是更健壮,更快,更容易审计和更强大的。然而,学习曲线可以花一些时间。我希望这个例子显示了如何解决一个问题,使用的是电子表格的过滤工具将是一个有用的指南,为那些刚刚在这个pandas开始的旅程的。祝你好运!
+
+--------------------------------------------------------------------------------
+
+via: http://pbpython.com/excel-filter-edit.html 
+
+作者:[Chris Moffitt ][a]
+译者:[zky001](https://github.com/zky001)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://pbpython.com/author/chris-moffitt.html
+[1]: http://pbpython.com/excel-pandas-comp.html
+[2]: http://pbpython.com/excel-pandas-comp-2.html
+[3]: http://pandas.pydata.org/pandas-docs/stable/indexing.html
+[4]: https://github.com/chris1610/pbpython/blob/master/data/sample-sales-reps.xlsx?raw=true
+[5]: https://github.com/chris1610/pbpython/blob/master/notebooks/Commissions-Example.ipynb
diff --git a/translated/tech/20160620 Detecting cats in images with OpenCV.md b/translated/tech/20160620 Detecting cats in images with OpenCV.md
deleted file mode 100644
index 9b1e19fef7..0000000000
--- a/translated/tech/20160620 Detecting cats in images with OpenCV.md	
+++ /dev/null
@@ -1,228 +0,0 @@
-使用 OpenCV 识别图片中的猫
-=======================================
-
-![](http://www.pyimagesearch.com/wp-content/uploads/2016/05/cat_face_detector_result_04.jpg)
-
-你知道 OpenCV 可以识别在图片中识别猫脸吗?还是在开箱即用的情况下,无需多余的附件。
-
-我也不知道。
-
-但是在看完'[Kendrick Tan broke the story][1]'这个故事之后, 我需要亲自体验一下...去看看到OpenCV 是如何在我没有察觉到的情况下,将这一个功能添加进了他的软件库。
-
-作为这个博客的大纲,我将会展示如何使用 OpenCV 的猫检测器在图片中识别猫脸。同样的,你也可以在视频流中使用该技术。
-
-> 想找这篇博客的源码?[请点这][2]。
-
-
-### 使用 OpenCV 在图片中检测猫
-
-如果你看一眼[OpenCV 的代码库][3],尤其是在[haarcascades 目录][4](OpenCV 用来保存处理他对多种目标检测的Cascade预先训练的级联图像分类), 你将会注意到这两个文件:
-
-- haarcascade_frontalcatface.xml
-- haarcascade_frontalcatface_extended.xml
-
-这两个 Haar Cascade 文件都将被用来在图片中检测猫脸。实际上,我使用了相同的方式来生成这篇博客顶端的图片。
-
-在做了一些调查工作之后,我发现训练这些记过并且将其提供给 OpenCV 仓库的是鼎鼎大名的 [Joseph Howse][5],他在计算机视觉领域有着很高的声望。
-
-在博客的剩余部分,我将会展示给你如何使用 Howse 的 Haar 级联模型来检测猫。
-
-让我们开工。新建一个叫 cat_detector.py 的文件,并且输入如下的代码:
-
-### 使用 OpenCVPython 来检测猫
-
-```
-# import the necessary packages
-import argparse
-import cv2
- 
-# construct the argument parse and parse the arguments
-ap = argparse.ArgumentParser()
-ap.add_argument("-i", "--image", required=True,
-	help="path to the input image")
-ap.add_argument("-c", "--cascade",
-	default="haarcascade_frontalcatface.xml",
-	help="path to cat detector haar cascade")
-args = vars(ap.parse_args())
-```
-
-第2和第3行主要是导入了必要的 python 包。6-12行主要是我们的命令行参数。我们在这只需要使用单独的参数'--image'。
-
-我们可以指定一个 Haar cascade 的路径通过 `--cascade` 参数。默认使用 `haarcascades_frontalcatface.xml`,同时需要保证这个文件和你的 `cat_detector.py` 在同一目录下。
-
-注意:我已经打包了猫的检测代码,还有在这个教程里的样本图片。你可以在博客的'Downloads' 部分下载到。如果你是刚刚接触 Python+OpenCV(或者 Haar 级联模型), 我会建议你下载 zip 压缩包,这个会方便你进行操作。
-
-接下来,就是检测猫的时刻了:
-
-```
-# load the input image and convert it to grayscale
-image = cv2.imread(args["image"])
-gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
- 
-# load the cat detector Haar cascade, then detect cat faces
-# in the input image
-detector = cv2.CascadeClassifier(args["cascade"])
-rects = detector.detectMultiScale(gray, scaleFactor=1.3,
-	minNeighbors=10, minSize=(75, 75))
-```
-
-在15,16行,我们从硬盘上读取了图片,并且进行灰度化(一个常用的图片预处理,方便 Haar cascade 进行分类,尽管不是必须)
-
-20行,我们加载了Haar casacade,即猫检测器,并且初始化了 cv2.CascadeClassifier 对象。
-
-使用 OpenCV 检测猫脸的步骤是21,22行,通过调用 detectMultiScale 方法。我们使用四个参数来调用。包括:
-
-1. 灰度化的图片,即样本图片。
-2. scaleFactor 参数,[图片金字塔][6]使用的检测猫脸时的检测粒度。一个更大的粒度将会加快检测的速度,但是会对准确性产生影响。相反的,一个更小的粒度将会影响检测的时间,但是会增加正确性。但是,细粒度也会增加错误的检测数量。你可以看博客的 'Haar 级联模型笔记' 部分来获得更多的信息。
-3. minNeighbors 参数控制了检测的最小数量,即在给定区域最小的检测猫脸的次数。这个参数很好的可以排除错误的检测结果。
-4. 最后,minSize 参数很好的自我说明了用途。即最后图片的最小大小,这个例子中就是 75\*75
-
-detectMultiScale 函数 return rects,这是一个4维数组链表。这些item 中包含了猫脸的(x,y)坐标值,还有宽度,高度。
-
-最后,让我们在图片上画下这些矩形来标识猫脸:
-
-```
-# loop over the cat faces and draw a rectangle surrounding each
-for (i, (x, y, w, h)) in enumerate(rects):
-	cv2.rectangle(image, (x, y), (x + w, y + h), (0, 0, 255), 2)
-	cv2.putText(image, "Cat #{}".format(i + 1), (x, y - 10),
-		cv2.FONT_HERSHEY_SIMPLEX, 0.55, (0, 0, 255), 2)
- 
-# show the detected cat faces
-cv2.imshow("Cat Faces", image)
-cv2.waitKey(0)
-```
-
-给相关的区域(举个例子,rects),我们在25行依次历遍它。
-
-在26行,我们在每张猫脸的周围画上一个矩形。27,28行展示了一个整数,即图片中猫的数量。
-
-最后,31,32行在屏幕上展示了输出的图片。
-
-### 猫检测结果
-
-为了测试我们的 OpenCV 毛检测器,可以在文章的最后,下载教程的源码。
-
-然后,在你解压缩之后,你将会得到如下的三个文件/目录:
-
-1. cat_detector.py:我们的主程序
-2. haarcascade_frontalcatface.xml: Haar cascade 猫检测资源
-3. images:我们将会使用的检测图片目录。
-
-到这一步,执行以下的命令:
-
-使用 OpenCVShell 检测猫。
-
-```
-$ python cat_detector.py --image images/cat_01.jpg
-```
-
-![](http://www.pyimagesearch.com/wp-content/uploads/2016/05/cat_face_detector_result_01.jpg)
->1. 在图片中检测猫脸,甚至是猫的一部分。
-
-注意,我们已经可以检测猫脸了,即使他的其余部分是被隐藏的。
-
-试下另外的一张图片:
-
-```
-python cat_detector.py --image images/cat_02.jpg
-```
-
-![](http://www.pyimagesearch.com/wp-content/uploads/2016/05/cat_face_detector_result_02.jpg)
->2. 第二个例子就是在略微不同的猫脸中检测。
-
-这次的猫脸和第一次的明显不同,因为它在'Meow'的中央。这种情况下,我们依旧能检测到正确的猫脸。
-
-这张图片的结果也是正确的:
-
-```
-$ python cat_detector.py --image images/cat_03.jpg
-```
-
-![](http://www.pyimagesearch.com/wp-content/uploads/2016/05/cat_face_detector_result_03.jpg)
->3. 使用 OpenCV 和 python 检测猫脸
-
-我们最后的一个样例就是在一张图中检测多张猫脸:
-
-```
-$ python cat_detector.py --image images/cat_04.jpg
-```
-
-![](http://www.pyimagesearch.com/wp-content/uploads/2016/05/cat_face_detector_result_04.jpg)
->Figure 4: Detecting multiple cats in the same image with OpenCV
->4. 在同一张图片中使用 OpenCV 检测多只猫
-
-注意,Haar cascade 的返回值并不是有序的。这种情况下,中间的那只猫会被标记成第三只。你可以通过判断他们的(x, y)坐标来自己排序。
-
-#### 精度的 Tips
-
-xml 文件中的注释,非常重要,Joseph Hower 提到了猫 脸检测器有可能会将人脸识别成猫脸。
-
-这种情况下,他推荐使用两种检测器(人脸&猫脸),然后将出现在人脸识别结果中的结果剔除掉。
-
-#### Haar 级联模型注意事项
-
-这个方法首先出现在 Paul Viola 和 Michael Jones 2001 年发布的 [Rapid Object Detection using a Boosted Cascade of Simple Features] 论文中。现在它已经成为了计算机识别领域引用最多的成果之一。
-
-这个算法能够识别图片中的对象,无论地点,规模。并且,他也能在现有的硬件条件下实现实时计算。
-
-在他们的论文中,Viola 和 Jones 关注在训练人脸检测器;但是,这个框架也能用来检测各类事物,如汽车,香蕉,路标等等。
-
-#### 有问题?
-
-Haar 级联模型最大的问题就是如何确定 detectMultiScale 方法的参数正确。特别是 scaleFactor 和 minNeighbors 参数。你很容易陷入,一张一张图片调参数的坑,这个就是该模型很难被实用化的原因。
-
-这个 scaleFactor 变量控制了用来检测图片各种对象的[图像棱锥图][8]。如何参数过大,你就会得到更少的特征值,这会导致你无法在图层中识别一些目标。
-
-换句话说,如果参数过低,你会检测出过多的图层。这虽然可以能帮助你检测更多的对象。但是他会造成计算速度的降低还会提高错误率。
-
-为了避免这个,我们通常使用[Histogram of Oriented Gradients + Linear SVM detection][9]。
-
-HOG + 线性 SVM 框架,它的参数更加容易的进行调优。而且也有更低的错误识别率,但是最大的缺点及时无法实时运算。
-
-### 对对象识别感兴趣?并且希望了解更多?
-
-![](http://www.pyimagesearch.com/wp-content/uploads/2016/05/custom_object_detector_example.jpg)
->5. 在 PyImageSearch Gurus 课程中学习如何构建自定义的对象识别器。
-
-如果你对学习如何训练自己的自定义对象识别器,请务必要去学习 PyImageSearch Gurus 的课程。
-
-在这个课程中,我提供了15节课还有超过168页的教程,来教你如何从0开始构建自定义的对象识别器。你会掌握如何应用 HOG+线性 SVM 计算框架来构建自己的对象识别器。
-
-### 总结
-
-在这篇博客里,我们学习了如何使用默认的 Haar 级联模型来识别图片中的猫脸。这些 Haar casacades 是通过[Joseph Howse][9] 贡献给 OpenCV 项目的。我是在[这篇文章][10]中开始注意到这个。
-
-尽管 Haar 级联模型相当有用,但是我们也经常用 HOG 和 线性 SVM 替代。因为后者相对而言更容易使用,并且可以有效地降低错误的识别概率。
-
-我也会在[在 PyImageSearch Gurus 的课程中][11]详细的讲述如何使用 HOG 和线性 SVM 对象识别器,来识别包括汽车,路标在内的各种事物。
-
-不管怎样,我希望你享受这篇博客。
-
-在你离开之前,确保你会使用这下面的表单注册 PyImageSearch Newsletter。这样你能收到最新的消息。
-
---------------------------------------------------------------------------------
-
-via: http://www.pyimagesearch.com/2016/06/20/detecting-cats-in-images-with-opencv/
-
-作者:[Adrian Rosebrock][a]
-译者:[译者ID](https://github.com/MikeCoder)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.pyimagesearch.com/author/adrian/
-[1]: http://kendricktan.github.io/find-cats-in-photos-using-computer-vision.html
-[2]: http://www.pyimagesearch.com/2016/06/20/detecting-cats-in-images-with-opencv/#
-[3]: https://github.com/Itseez/opencv
-[4]: https://github.com/Itseez/opencv/tree/master/data/haarcascades
-[5]: http://nummist.com/
-[6]: http://www.pyimagesearch.com/2015/03/16/image-pyramids-with-python-and-opencv/
-[7]: https://www.cs.cmu.edu/~efros/courses/LBMV07/Papers/viola-cvpr-01.pdf
-[8]: http://www.pyimagesearch.com/2015/03/16/image-pyramids-with-python-and-opencv/
-[9]: http://www.pyimagesearch.com/2014/11/10/histogram-oriented-gradients-object-detection/
-[10]: http://kendricktan.github.io/find-cats-in-photos-using-computer-vision.html
-[11]: https://www.pyimagesearch.com/pyimagesearch-gurus/
-
-
-
diff --git a/translated/tech/20160623 Advanced Image Processing with Python.md b/translated/tech/20160623 Advanced Image Processing with Python.md
deleted file mode 100644
index 73f8751225..0000000000
--- a/translated/tech/20160623 Advanced Image Processing with Python.md	
+++ /dev/null
@@ -1,124 +0,0 @@
-Python高级图像处理
-======================================
-
-![](http://www.cuelogic.com/blog/wp-content/uploads/2016/06/Image-Search-Engine.png)
-
-构建图像搜索引擎并不是一件容易的任务。这里有几个概念、工具、想法和技术需要实现。主要的图像处理概念之一是逆图像查询(RIQ)或者说逆图像搜索。Google、Cloudera、Sumo Logic 和 Birst等公司在使用逆图像搜索中名列前茅。通过分析图像和使用数据挖掘RIQ提供了很好的洞察分析能力。
-
-### 顶级公司与逆图像搜索
-
-有很多顶级的技术公司使用RIQ来产生最好的收益。例如:在2014年Pinterest第一次带来了视觉搜索。随后在2015年发布了一份白皮书,揭示了其架构。逆图像搜索让Pinterest获得对时尚对象的视觉特征和显示类似的产品建议的能力。
-
-众所周知,谷歌图片使用逆图像搜索允许用户上传一张图片然后搜索相关联的图片。通过使用先进的算法对提交的图片进行分析和数学建模。然后和谷歌数据库中无数的其他图片进行比较得到相似的结果。
-
-**这是opencv 2.4.9特征比较报告一个图表:**
-
-![](http://www.cuelogic.com/blog/wp-content/uploads/2016/06/search-engine-graph.jpg)
-
-### 算法 & Python库
-
-在我们使用它工作之前,让我们过一遍构建图像搜索引擎的Python库的主要元素:
-
-### 专利算法
-
-#### 尺度不变特征变换算法(SIFT - Scale-Invariant Feature Transform)
-
-1. 非自由功能的一个专利技术,利用图像识别符,以识别相似图像,甚至那些从不同的角度,大小,深度和规模点击,它们被包括在搜索结果中。[点击这里][4]查看SIFT详细视频。
-2. SIFT能与从许多图片中提取的特征的大型数据库正确的匹配搜索条件。
-3. 从不同的方面来匹配相同的图像和匹配不变特征来获得搜索结果是SIFT的另一个特征。了解更多关于尺度不变[关键点][5]
-
-#### 加速鲁棒特征(SURF - Speeded Up Robust Features)算法
-
-1. [SURF][1] 也是一种非自由功能的专利技术而且还是一种“加速”的SIFT版本。不像SIFT,
-
-2. SURF依赖于Hessian矩阵行列式的位置和尺度。
-
-3. 在许多应用中,旋转不变性是不是一个必要条件。所以找不到这个方向的速度加快了这个过程。
-
-4. SURF包括几种特性,在每一步的速度提高。比SIFT快三倍的速度,SIFT擅长旋转和模糊化。然而它不擅长处理照明和变换视角。
-
-5. Open CV,一个程序功能库提供SURF相似的功能,SURF.compute()和SURF.Detect()可以用来找到描述符和要点。阅读更多关于SURF[点击这里][2]
-
-### 开源算法
-
-#### KAZE 算法
-
-1. KAZE是一个开源的非线性尺度空间的二维多尺度和新的特征检测和描述算法。在加性算子分裂的有效技术(AOS)和可变电导扩散是用来建立非线性尺度空间。
-
-2. 多尺度图像处理基础是简单的创建一个图像的尺度空间,同时用正确的函数过滤原始图像,提高时间或规模。
-
-#### AKAZE (Accelerated-KAZE) 算法
-
-1. 顾名思义,这是一个更快的图像搜索方式,找到匹配的关键点在两幅图像之间。AKAZE 使用二进制描述符和非线性尺度空间来平衡精度和速度。
-
-#### BRISK (Binary Robust Invariant Scalable Keypoints) 算法
-
-1. BRISK 非常适合描述关键点的检测与匹配。
-
-2. 是一种高度自适应的算法,基于尺度空间的快速检测器和一个位字符串描述符,有助于加快搜索显着。
-
-3. 尺度空间关键点检测与关键点描述帮助优化手头相关任务的性能
-
-#### FREAK (Fast Retina Keypoint)
-
-1. 这个新的关键点描述的灵感来自人的眼睛。通过图像强度比能有效地计算一个二进制串级联。FREAK算法相比BRISK,SURF和SIFT算法有更快的计算与较低的内存负载。
-
-#### ORB (Oriented FAST and Rotated BRIEF)
-
-1. 快速的二进制描述符,ORB具有抗噪声和旋转不变性。ORB建立在FAST关键点检测器和BRIEF描述符之上,有成本低、性能好的元素属性。
-
-2. 除了快速和精确的定位元件,有效地计算定向的BRIEF,分析变动和面向BRIEF特点相关,是另一个ORB的特征。
-
-### Python库
-
-#### Open CV
-
-1. Open CV提供学术和商业用途。一个开源的机器学习和计算机视觉库,OpenCV便于组织利用和修改代码。
-
-2. 超过2500个优化算法,包括国家最先进的机器学习和计算机视觉算法服务与各种图像搜索--人脸检测、目标识别、摄像机目标跟踪,从图像数据库中寻找类似图像、眼球运动跟随、风景识别等。
-
-3. 像谷歌,IBM,雅虎,IBM,索尼,本田,微软和英特尔这样的大公司广泛的使用OpenCV。
-
-4. OpenCV拥有python,java,C,C++和MATLAB接口,同时支持Windows,Linux,Mac OS和Android。
-
-#### Python图像库 (PIL)
-
-1. Python图像库(PIL)支持多种文件格式,同时提供图像处理和图形解决方案。开源的PIL为你的Python解释器添加图像处理能力。
-2. 标准的图像处理能力包括图像增强、透明和屏蔽作用、图像过滤、像素操作等。
-
-详细的数据和图表,请看OpenCV 2.4.9 特征比较报告。[这里][3]
-
-### 构建图像搜索引擎
-
-图像搜索引擎可以从预置集图像库选择相似的图像。其中最受欢迎的是谷歌的著名的图像搜索引擎。对于初学者来说,有不同的方法来建立这样的系统。提几个如下:
-
-1. 采用图像提取、图像描述提取、元数据提取和搜索结果提取,建立图像搜索引擎。
-2. 定义你的图像描述符,数据集索引,定义你的相似性度量,然后搜索和排名。
-3. 选择要搜索的图像,选择用于进行搜索的目录,所有图片的搜索目录,创建图片特征索引,评估相同的搜索图片的功能,在搜索中匹配图片,并获得匹配的图片。
-
-我们的方法基本上就比较grayscaled版本的图像,逐渐移动到复杂的特征匹配算法如SIFT和SURF,最后沉淀下来的是开源的解决方案称为BRISK。所有这些算法提供了有效的结果,在性能和延迟的细微变化。建立在这些算法上的引擎有许多应用,如分析流行统计的图形数据,在图形内容中识别对象,以及更多。
-
-**例如**:一个图像搜索引擎需要由一个IT公司作为客户机来建立。因此,如果一个品牌的标志图像被提交在搜索中,所有相关的品牌形象搜索显示结果。所得到的结果也可以通过客户端分析,使他们能够根据地理位置估计品牌知名度。但它还比较年轻,RIQ或反向图像搜索尚未被完全挖掘利用。
-
-这就结束了我们的文章,使用Python构建图像搜索引擎。浏览我们的博客部分来查看最新的编程技术。
-
-数据来源:OpenCV 2.4.9 特征比较报告(computer-vision-talks.com)
-
-(Ananthu Nair 的指导与补充)
-
---------------------------------------------------------------------------------
-
-via: http://www.cuelogic.com/blog/advanced-image-processing-with-python/
-
-作者:[Snehith Kumbla][a]
-译者:[Johnny-Liao](https://github.com/Johnny-Liao)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.cuelogic.com/blog/author/snehith-kumbla/
-[1]: http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_surf_intro/py_surf_intro.html
-[2]: http://www.vision.ee.ethz.ch/~surf/eccv06.pdf
-[3]: https://docs.google.com/spreadsheets/d/1gYJsy2ROtqvIVvOKretfxQG_0OsaiFvb7uFRDu5P8hw/edit#gid=10
-[4]: https://www.youtube.com/watch?v=NPcMS49V5hg
-[5]: https://www.cs.ubc.ca/~lowe/papers/ijcv04.pdf
diff --git a/translated/tech/20160626 Backup Photos While Traveling With an Ipad Pro and a Raspberry Pi.md b/translated/tech/20160626 Backup Photos While Traveling With an Ipad Pro and a Raspberry Pi.md
new file mode 100755
index 0000000000..50e67f5e88
--- /dev/null
+++ b/translated/tech/20160626 Backup Photos While Traveling With an Ipad Pro and a Raspberry Pi.md	
@@ -0,0 +1,405 @@
+旅行时通过树莓派和iPad Pro备份图片
+===================================================================
+
+![](http://www.movingelectrons.net/images/bkup_photos_main.jpg)
+>旅行中备份图片 - Gear.
+
+### 介绍
+
+我在很长的时间内一直在寻找一个旅行中备份图片的理想方法,把SD卡放进你的相机包是比较危险和暴露的,SD卡可能丢失或者被盗,数据可能损坏或者在传输过程中失败。比较好的一个选择是复制到另外一个设备即使它也是个SD卡,并且将它放到一个比较安全的地方去,备份到远端也是一个可行的办法,但是如果去了一个没有网络的地方就不太可行了。
+
+我理想的备份步骤需要下面的工具:
+
+1. 用一台iPad pro而不是一台笔记本。我喜欢简便的旅行,我的旅行大部分是商务的而不是拍摄休闲的,这很显然我为什么选择了iPad Pro
+2. 用尽可能少的设备
+3. 设备之间的连接需要很安全。我需要在旅馆和机场使用,所以设备之间的连接需要时封闭的加密的。
+4. 整个过程应该是稳定的安全的,我还用过其他的移动设备,但是效果不太理想[1].
+
+### 设置
+
+我制定了一个满足上面条件并且在未来可以扩充的设定,它包含下面这些部件的使用:
+
+1. [2]9.7寸写作时最棒的又小又轻便的IOS系统的iPad Pro,苹果笔不是不许的,但是当我在路上进行一些编辑的时候依然需要,所有的重活由树莓派做 ,其他设备通过ssh连接设备
+2. [3] 树莓派3包含Raspbian系统
+3. [4]Mini SD卡 [box/case][5].
+5. [6]128G的优盘,对于我是够用了,你可以买个更大的,你也可以买个移动硬盘,但是树莓派没办法给移动硬盘供电,你需要额外准备一个供电的hub,当然优质的线缆能提供可靠便捷的安装和连接。
+6. [9]SD读卡器
+7. [10]另外的sd卡,SD卡我在用满之前就会立即换一个,这样就会让我的照片分布在不同的sd卡上
+
+下图展示了这些设备之间如何相互连接.
+
+![](http://www.movingelectrons.net/images/bkup_photos_diag.jpg)
+>旅行时照片的备份-过程表格.
+
+树莓派会作为一个热点. 它会创建一个WIFI网络,当然也可以建立一个Ad Hoc网络,更简单一些,但是它不会加密设备之间的连接,因此我选择创建WIFI网络。
+
+SD卡放进SD读卡器插到树莓派USB端口上,128G的大容量优盘一直插在树莓派的USB端口上,我选择了一款闪迪的体积比较小。主要的思路就是通过脚本把SD卡的图片备份到优盘上,脚本是增量备份,而且脚本会自动运行,使备份特别快,如果你有很多的照片或者拍摄了很多没压缩的照片,这个任务量就比较大,用ipad来运行Python脚本,而且用来浏览SD卡和优盘的文件。
+
+如果给树莓派连上一根能上网的网线,那样连接树莓派wifi的设备就可以上网啦!
+
+### 1. 树莓派的设置
+
+这部分要用到命令行模式,我会尽可能详细的介绍,方便大家进行下去。
+
+#### 安装和配置Raspbian
+
+给树莓派连接鼠标键盘和显示器,将SD卡插到树莓派上,在官网按步骤安装Raspbian [12].
+
+安装完后执行下面的命令:
+
+```
+sudo apt-get update
+sudo apt-get upgrade
+```
+
+升级机器上所有的软件到最新,我将树莓派连接到本地网络,而且为了安全更改了默认的密码。
+
+Raspbian默认开启了SSH,这样所有的设置可以在一个远程的设备上完成。我也设置了RSA验证,那是个可选的功能,查看能多信息 [这里][13].
+
+这是一个在MAC上建立SSH连接到树莓派上的截图[14]:
+
+##### 建立WPA2验证的WIFI
+
+这个安装过程是基于这篇文章,只适用于我自己做的例子[15].
+
+##### 1. 安装软件包
+
+我们需要安装下面的软件包:
+
+```
+sudo apt-get install hostapd
+sudo apt-get install dnsmasq
+```
+
+hostapd用来创建wifi,dnsmasp用来做dhcp和dns服务,很容易设置.
+
+##### 2. 编辑dhcpcd.conf
+
+通过网络连接树莓派,网络设置树莓派需要dhcpd,首先我们将wlan0设置为一个静态的IP。
+
+用sudo nano `/etc/dhcpcd.conf`命令打开配置文件,在最后一行添加上如下信息:
+
+```
+denyinterfaces wlan0
+```
+
+注意: 必须先配置这个接口才能配置其他接口.
+
+##### 3. 编辑端口
+
+现在设置静态IP,sudo nano `/etc/network/interfaces`打开端口配置文件按照如下信息编辑wlan0选项:
+
+```
+allow-hotplug wlan0
+iface wlan0 inet static
+    address 192.168.1.1
+    netmask 255.255.255.0
+    network 192.168.1.0
+    broadcast 192.168.1.255
+#    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
+```
+
+同样, 然后添加wlan1信息:
+
+```
+#allow-hotplug wlan1
+#iface wlan1 inet manual
+#    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
+```
+
+重要: sudo service dhcpcd restart命令重启dhcpd服务`sudo ifdown eth0; sudo ifup wlan0`命令用来关闭eth0端口再开启用来生效配置文件.
+
+##### 4. 配置Hostapd
+
+接下来我们配置hostapd,`sudo nano /etc/hostapd/hostapd.conf` 用这个命令创建并填写配置信息到文件中:
+
+```
+interface=wlan0
+
+# Use the nl80211 driver with the brcmfmac driver
+driver=nl80211
+
+# This is the name of the network
+ssid=YOUR_NETWORK_NAME_HERE
+
+# Use the 2.4GHz band
+hw_mode=g
+
+# Use channel 6
+channel=6
+
+# Enable 802.11n
+ieee80211n=1
+
+# Enable QoS Support
+wmm_enabled=1
+
+# Enable 40MHz channels with 20ns guard interval
+ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40]
+
+# Accept all MAC addresses
+macaddr_acl=0
+
+# Use WPA authentication
+auth_algs=1
+
+# Require clients to know the network name
+ignore_broadcast_ssid=0
+
+# Use WPA2
+wpa=2
+
+# Use a pre-shared key
+wpa_key_mgmt=WPA-PSK
+
+# The network passphrase
+wpa_passphrase=YOUR_NEW_WIFI_PASSWORD_HERE
+
+# Use AES, instead of TKIP
+rsn_pairwise=CCMP
+```
+
+配置完成后,我们需要运行 `sudo nano /etc/default/hostapd` 命令打开这个配置文件然后找到`#DAEMON_CONF=""` 替换成`DAEMON_CONF="/etc/hostapd/hostapd.conf"`以便hostapd服务能够找到对应的配置文件.
+
+##### 5. 配置Dnsmasq
+
+dnsmasp配置文件包含很多信息方便你使用它,但是我们不需要那么多选项,我建议用下面两条命令把它放到别的地方,不要删除它,然后自己创建一个文件
+
+```
+sudo mv /etc/dnsmasq.conf /etc/dnsmasq.conf.orig  
+sudo nano /etc/dnsmasq.conf  
+```
+
+粘贴下面的信息到新文件中:
+
+```
+interface=wlan0      # Use interface wlan0
+listen-address=192.168.1.1 # Explicitly specify the address to listen on
+bind-interfaces      # Bind to the interface to make sure we aren't sending things elsewhere
+server=8.8.8.8       # Forward DNS requests to Google DNS
+domain-needed        # Don't forward short names
+bogus-priv           # Never forward addresses in the non-routed address spaces.
+dhcp-range=192.168.1.50,192.168.1.100,12h # Assign IP addresses in that range  with a 12 hour lease time
+```
+
+##### 6. 设置IPv4转发
+
+最后我们需要做的事就是配置包转发,用`sudo nano /etc/sysctl.conf`命令打开sysctl.conf文件,将containing `net.ipv4.ip_forward=1`之前的#号删除,然后重启生效
+
+我们还需要给连接树莓派的设备通过WIFI分享一个网络连接,做一个wlan0和eth0的NAT,我们可以参照下面的脚本来实现。
+
+```
+sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE  
+sudo iptables -A FORWARD -i eth0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT  
+sudo iptables -A FORWARD -i wlan0 -o eth0 -j ACCEPT  
+```
+
+我命名了一个hotspot-boot.sh的脚本然后让它可以运行:
+
+```
+sudo chmod 755 hotspot-boot.sh
+```
+
+脚本会在树莓派启动的时候运行,有很多方法实现,下面是我实现的方式:
+
+1. 把文件放到`/home/pi/scripts`目录下.
+2. 编辑rc.local文件,输入`sudo nano /etc/rc.local`命令将运行脚本命令放到exit0之前[16]).
+
+下面是实例.
+
+```
+#!/bin/sh -e
+#
+# rc.local
+#
+# This script is executed at the end of each multiuser runlevel.
+# Make sure that the script will "exit 0" on success or any other
+# value on error.
+#
+# In order to enable or disable this script just change the execution
+# bits.
+#
+# By default this script does nothing.
+
+# Print the IP address
+_IP=$(hostname -I) || true
+if [ "$_IP" ]; then
+  printf "My IP address is %s\n" "$_IP"
+fi
+
+sudo /home/pi/scripts/hotspot-boot.sh &
+
+exit 0
+
+```
+
+#### 安装Samba服务和NTFS兼容驱动.
+
+我们要安装下面几个软件使我们能够访问树莓派分享的文件夹,ntfs-3g可以使我们能够方位ntfs文件系统的文件.
+
+```
+sudo apt-get install ntfs-3g
+sudo apt-get install samba samba-common-bin
+```
+
+你可以参照这些文档来配置Samba[17] .
+
+重要提示,推荐的文档要先挂在外置硬盘,我们不这样做,因为在这篇文章写作的时候树莓派在启动时的auto-mounts功能同时将sd卡和优盘挂载到`/media/pi/`上,这篇文章有一些多余的功能我们也不会采用。
+
+### 2. Python脚本
+
+树莓派配置好后,我们需要让脚本拷贝和备份照片的时候真正的起作用,脚本只提供了特定的自动化备份进程,如果你有基本的cli操作的技能,你可以ssh进树莓派,然后拷贝你自己的照片从一个设备到另外一个设备用cp或者rsync命令。在脚本里我们用rsync命令,这个命令比较可靠而且支持增量备份。
+
+这个过程依赖两个文件,脚本文件自身和`backup_photos.conf`这个配置文件,后者只有几行包含已挂载的目的驱动器和应该挂载到哪个目录,它看起来是这样的:
+
+```
+mount folder=/media/pi/
+destination folder=PDRIVE128GB
+```
+
+重要提示: 在这个符号`=`前后不要添加多余的空格,否则脚本会失效.
+
+下面是这个Python脚本,我把它命名为`backup_photos.py`,把它放到了`/home/pi/scripts/`目录下,我在每行都做了注释可以方便的查看各行的功能.
+
+```
+#!/usr/bin/python3
+
+import os
+import sys
+from sh import rsync
+
+'''
+脚本将挂载到/media/pi的sd卡上的内容复制到一个目的磁盘的同名目录下,目的驱动器的名字在.conf文件里定义好了.
+
+
+Argument:  label/name of the mounted SD Card.
+'''
+
+CONFIG_FILE = '/home/pi/scripts/backup_photos.conf'
+ORIGIN_DEV = sys.argv[1]
+
+def create_folder(path):
+
+    print ('attempting to create destination folder: ',path)
+    if not os.path.exists(path):
+        try: 
+            os.mkdir(path)
+            print ('Folder created.')
+        except:
+            print ('Folder could not be created. Stopping.')
+            return
+    else:
+        print ('Folder already in path. Using that instead.')
+
+
+
+confFile = open(CONFIG_FILE,'rU') 
+#IMPORTANT: rU Opens the file with Universal Newline Support, 
+#so \n and/or \r is recognized as a new line.
+
+confList = confFile.readlines()
+confFile.close()
+
+
+for line in confList:
+    line = line.strip('\n')
+
+    try:
+        name , value = line.split('=')
+
+        if name == 'mount folder':
+            mountFolder = value
+        elif name == 'destination folder':
+            destDevice = value
+
+
+    except ValueError:
+        print ('Incorrect line format. Passing.')
+        pass
+
+
+destFolder = mountFolder+destDevice+'/'+ORIGIN_DEV
+create_folder(destFolder)
+
+print ('Copying files...')
+
+# Comment out to delete files that are not in the origin:
+# rsync("-av", "--delete", mountFolder+ORIGIN_DEV, destFolder)
+rsync("-av", mountFolder+ORIGIN_DEV+'/', destFolder)
+
+print ('Done.')
+```
+
+### 3.iPad Pro的配置
+
+树莓派做了最重的活,而且iPad Pro根本没参与传输文件,我们在iPad上只需要安装上Prompt2来通过ssh连接树莓派就行了,这样你既可以运行Python脚本也可以复制文件了。[18]; [19].
+
+![](http://www.movingelectrons.net/images/bkup_photos_ipad&rpi_prompt.jpg)
+>iPad用prompt通过SSH连接树莓派.
+
+我们安装了Samba,我们可以通过图形方式通过树莓派连接到USB设备,你可以看视频,在不同的设备之间复制和移动文件,文件浏览器是必须的[20] .
+
+### 4. 将它们都放到一起
+
+我们假设`SD32GB-03`是连接到树莓派的SD卡名字,`PDRIVE128GB`是那个优盘通过事先的配置文件挂载好,如果我们想要备份SD卡上的图片,我们需要这么做:
+
+1. 让树莓派先正常运行,将设备挂载好.
+2. 连接树莓派配置好的WIFI网络.
+3. 用prompt这个app通过ssh连接树莓派[21].
+4. 连接好后输入下面的命令:
+
+```
+python3 backup_photos.py SD32GB-03
+```
+
+首次备份需要一些时间基于SD卡的容量,你需要保持好设备之间的连接,在脚本运行之前你可以通过下面这个命令绕过.
+
+```
+nohup python3 backup_photos.py SD32GB-03 &
+```
+
+![](http://www.movingelectrons.net/images/bkup_photos_ipad&rpi_finished.png)
+>运行完成的脚本如图所示.
+
+### 未来的定制
+
+我在树莓派上安装了vnc服务,这样我可以通过ipad连接树莓派的图形界面,我安装了bittorrent用来远端备份我的图片,当然需要先设置好,我会放出这些当我完成这些工作后[23[24]。
+
+你可以在下面发表你的评论和问题,我会在此页下面回复。.
+
+
+--------------------------------------------------------------------------------
+
+via: http://www.movingelectrons.net/blog/2016/06/26/backup-photos-while-traveling-with-a-raspberry-pi.html
+
+作者:[Editor][a]
+译者:[jiajia9linuxer](https://github.com/jiajia9linuxer)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.movingelectrons.net/blog/2016/06/26/backup-photos-while-traveling-with-a-raspberry-pi.html
+[1]: http://bit.ly/1MVVtZi
+[2]: http://www.amazon.com/dp/B01D3NZIMA/?tag=movinelect0e-20
+[3]: http://www.amazon.com/dp/B01CD5VC92/?tag=movinelect0e-20
+[4]: http://www.amazon.com/dp/B010Q57T02/?tag=movinelect0e-20
+[5]: http://www.amazon.com/dp/B01F1PSFY6/?tag=movinelect0e-20
+[6]: http://amzn.to/293kPqX
+[7]: http://amzn.to/290syFY
+[8]: http://amzn.to/290syFY
+[9]: http://amzn.to/290syFY
+[10]: http://amzn.to/290syFY
+[11]: http://amzn.to/293kPqX
+[12]: https://www.raspberrypi.org/downloads/noobs/
+[13]: https://www.raspberrypi.org/documentation/remote-access/ssh/passwordless.md
+[14]: https://www.iterm2.com/
+[15]: https://frillip.com/using-your-raspberry-pi-3-as-a-wifi-access-point-with-hostapd/
+[16]: https://www.raspberrypi.org/documentation/linux/usage/rc-local.md
+[17]: http://www.howtogeek.com/139433/how-to-turn-a-raspberry-pi-into-a-low-power-network-storage-device/
+[18]: http://bit.ly/1MVVtZi
+[19]: https://itunes.apple.com/us/app/prompt-2/id917437289?mt=8&uo=4&at=11lqkH
+[20]: https://itunes.apple.com/us/app/filebrowser-access-files-on/id364738545?mt=8&uo=4&at=11lqkH
+[21]: https://itunes.apple.com/us/app/prompt-2/id917437289?mt=8&uo=4&at=11lqkH
+[22]: https://en.m.wikipedia.org/wiki/Nohup
+[23]: https://itunes.apple.com/us/app/remoter-pro-vnc-ssh-rdp/id519768191?mt=8&uo=4&at=11lqkH
+[24]: https://getsync.com/
diff --git a/translated/tech/20160706 Getting started with Docker Swarm and deploying a replicated Python 3 Application.md b/translated/tech/20160706 Getting started with Docker Swarm and deploying a replicated Python 3 Application.md
deleted file mode 100644
index b06c56b46e..0000000000
--- a/translated/tech/20160706 Getting started with Docker Swarm and deploying a replicated Python 3 Application.md	
+++ /dev/null
@@ -1,282 +0,0 @@
-教程:开始学习如何使用 Docker Swarm 部署可扩展的 Python3 应用
-==============
-
-[Ben Firshman][2]最近在[Dockercon][1]做了一个关于使用 Docker 构建无服务应用的演讲,你可以在[这查看详情][3](可以和视频一起)。之后,我写了[一篇文章][4]关于如何使用[AWS Lambda][5]构建微服务系统。
-
-今天,我想展示给你的就是如何使用[Docker Swarm][6]然后部署一个简单的 Python Falcon REST 应用。尽管,我不会使用[dockerrun][7]或者是其他无服务特性。你可能会惊讶,使用 Docker Swarm 部署(替换)一个 Python(Java, Go 都一样) 应用是如此的简单。
-
-注意:这展示的部分步骤是截取自[Swarm Tutorial][8]。我已经修改了部分章节,并且[在 Vagrant 的帮助文档][9]中添加了构建本地测试环境的文档。请确保,你使用的是1.12或以上版本的 Docker 引擎。我写这篇文章的时候,使用的是1.12RC2版本的 Docker。注意的是,这只是一个测试版本,只会可能还会有修改。
-
-你要做的第一件事,就是你要保证你正确的安装了[Vagrant][10],如果你想本地运行的话。你也可以按如下步骤使用你最喜欢的云服务提供商部署 Docker Swarm 虚拟机系统。
-
-我们将会使用这三台 VM:一个简单的 Docker Swarm 管理平台和两台 worker。
-
-安全注意事项:Vagrantfile 代码中包含了部分位于 Docker 测试服务器上的 shell 脚本。这是一个隐藏的安全问题。如果你没有权限的话。请确保你会在运行代码之前[审查这部分的脚本][11]。
-
-```
-$ git clone https://github.com/chadlung/vagrant-docker-swarm
-$ cd vagrant-docker-swarm
-$ vagrant plugin install vagrant-vbguest
-$ vagrant up
-```
-
-Vagrant up 命令可能会花很长的时间来执行。
-
-SSH 登陆进入 manager1 虚拟机:
-
-```
-$ vagrant ssh manager1
-```
-
-在 manager1 的终端中执行如下命令:
-
-```
-$ sudo docker swarm init --listen-addr 192.168.99.100:2377
-```
-
-现在还没有 worker 注册上来:
-
-```
-$ sudo docker node ls
-```
-
-Let’s register the two workers. Use two new terminal sessions (leave the manager1 session running):
-通过两个新的终端会话(退出 manager1 的登陆后),我们注册两个 worker。
-
-```
-$ vagrant ssh worker1
-```
-
-在 worker1 上执行如下命令:
-
-```
-$ sudo docker swarm join 192.168.99.100:2377
-```
-
-在 worker2 上重复这些命令。
-
-在 manager1 上执行这个命令:
-
-```
-$ docker node ls
-```
-
-你将会看到:
-
-![](http://www.giantflyingsaucer.com/blog/wp-content/uploads/2016/06/Screen-Shot-2016-06-28-at-3.15.25-PM.png)
-
-开始在 manager1 的终端里,部署一个简单的服务。
-
-```
-sudo docker service create --replicas 1 --name pinger alpine ping google.com
-```
-
-这个命令将会部署一个服务,他会从 worker 机器中的一台 ping google.com。(manager 也可以运行服务,不过[这也可以被禁止][12])如果你只是想 worker 运行容器的话)。可以使用如下命令,查看哪些节点正在执行服务:
-
-```
-$ sudo docker service tasks pinger
-```
-
-结果回合这个比较类似:
-
-![](http://www.giantflyingsaucer.com/blog/wp-content/uploads/2016/06/Screen-Shot-2016-06-28-at-5.23.05-PM.png)
-
-所以,我们知道了服务正跑在 worker1 上。我们可以回到 worker1 的会话里,然后进入正在运行的容器:
-
-```
-$ sudo docker ps
-```
-
-![](http://www.giantflyingsaucer.com/blog/wp-content/uploads/2016/06/Screen-Shot-2016-06-28-at-5.25.02-PM.png)
-
-你可以看到容器的 id 是: ae56769b9d4d
-
-在我的例子中,我运行的是如下的代码:
-
-```
-$ sudo docker attach ae56769b9d4d
-```
-
-![](http://www.giantflyingsaucer.com/blog/wp-content/uploads/2016/06/Screen-Shot-2016-06-28-at-5.26.49-PM.png)
-
-你可以仅仅只用 CTRL-C 来停止服务。
-
-回到 manager1,并且移除 pinger 服务。
-
-```
-$ sudo docker service rm pinger
-```
-
-现在,我们将会部署可复制的 Python 应用。请记住,为了保持文章的简洁,而且容易复制,所以部署的是一个简单的应用。
-
-你需要做的第一件事就是将镜像放到[Docker Hub][13]上,或者使用我[已经上传的一个][14]。这是一个简单的 Python 3 Falcon REST 应用。他有一个简单的入口: /hello 带一个 value 参数。
-
-[chadlung/hello-app][15]的 Python 代码看起来像这样:
-
-```
-import json
-from wsgiref import simple_server
- 
-import falcon
- 
- 
-class HelloResource(object):
-    def on_get(self, req, resp):
-        try:
-            value = req.get_param('value')
- 
-            resp.content_type = 'application/json'
-            resp.status = falcon.HTTP_200
-            resp.body = json.dumps({'message': str(value)})
-        except Exception as ex:
-            resp.status = falcon.HTTP_500
-            resp.body = str(ex)
- 
- 
-if __name__ == '__main__':
-    app = falcon.API()
-    hello_resource = HelloResource()
-    app.add_route('/hello', hello_resource)
-    httpd = simple_server.make_server('0.0.0.0', 8080, app)
-    httpd.serve_forever()
-```
-
-Dockerfile 很简单:
-
-```
-FROM python:3.4.4
- 
-RUN pip install -U pip
-RUN pip install -U falcon
- 
-EXPOSE 8080
- 
-COPY . /hello-app
-WORKDIR /hello-app
- 
-CMD ["python", "app.py"]
-```
-
-再一次说明,这是非常详细的奖惩,如果你想,你也可以在本地访问这个入口: <http://127.0.0.1:8080/hello?value=Fred>
-
-这将返回如下结果:
-
-```
-{"message": "Fred"}
-```
-
-在 Docker Hub 上构建和部署这个 hello-app(修改成你自己的 Docker Hub 仓库或者[这个][15]):
-
-```
-$ sudo docker build . -t chadlung/hello-app:2
-$ sudo docker push chadlung/hello-app:2
-```
-
-现在,我们可以将应用部署到之前的 Docker Swarm 了。登陆 manager1 终端,并且执行:
-
-```
-$ sudo docker service create -p 8080:8080 --replicas 2 --name hello-app chadlung/hello-app:2
-$ sudo docker service inspect --pretty hello-app
-$ sudo docker service tasks hello-app
-```
-
-现在,我们已经可以测试了。使用任何一个节点 Swarm 的 IP,来访问/hello 的入口,在本例中,我在 Manager1 的终端里使用 curl 命令:
-
-注意,在 Swarm 中的所有 IP 都可以工作,即使这个服务只运行在一台或者更多的节点上。
-
-```
-$ curl -v -X GET "http://192.168.99.100:8080/hello?value=Chad"
-$ curl -v -X GET "http://192.168.99.101:8080/hello?value=Test"
-$ curl -v -X GET "http://192.168.99.102:8080/hello?value=Docker"
-```
-
-结果就是:
-
-```
-* Hostname was NOT found in DNS cache
-*   Trying 192.168.99.101...
-* Connected to 192.168.99.101 (192.168.99.101) port 8080 (#0)
-> GET /hello?value=Chad HTTP/1.1
-> User-Agent: curl/7.35.0
-> Host: 192.168.99.101:8080
-> Accept: */*
-> 
-* HTTP 1.0, assume close after body
-< HTTP/1.0 200 OK
-< Date: Tue, 28 Jun 2016 23:52:55 GMT
-< Server: WSGIServer/0.2 CPython/3.4.4
-< content-type: application/json
-< content-length: 19
-< 
-{"message": "Chad"}
-```
-
-从浏览器中访问其他节点:
-
-![](http://www.giantflyingsaucer.com/blog/wp-content/uploads/2016/06/Screen-Shot-2016-06-28-at-6.54.31-PM.png)
-
-如果你想看运行的所有服务,你可以在 manager1 节点上运行如下代码:
-
-```
-$ sudo docker service ls
-```
-
-如果你想添加可视化控制平台,你可以安装[Docker Swarm Visualizer][16](这非常简单上手)。在 manager1 的终端中执行如下代码:
-
-![]($ sudo docker run -it -d -p 5000:5000 -e HOST=192.168.99.100 -e PORT=5000 -v /var/run/docker.sock:/var/run/docker.sock manomarks/visualizer)
-
-打开你的浏览器,并且访问: <http://192.168.99.100:5000/>
-
-结果(假设已经运行了两个 Docker Swarm 服务):
-
-![](http://www.giantflyingsaucer.com/blog/wp-content/uploads/2016/06/Screen-Shot-2016-06-30-at-2.37.28-PM.png)
-
-停止运行 hello-app(已经在两个节点上运行了),可以在 manager1 上执行这个代码:
-
-```
-$ sudo docker service rm hello-app
-```
-
-如果想停止, 那么在 manager1 的终端中执行:
-
-```
-$ sudo docker ps
-```
-
-获得容器的 ID,这里是: f71fec0d3ce1
-
-从 manager1 的终端会话中执行这个代码:
-
-```
-$ sudo docker stop f71fec0d3ce1
-```
-
-祝你使用 Docker Swarm。这篇文章主要是以1.12版本来进行描述的。
-
---------------------------------------------------------------------------------
-
-via: http://www.giantflyingsaucer.com/blog/?p=5923
-
-作者:[Chad Lung][a]
-译者:[译者ID](https://github.com/MikeCoder)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.giantflyingsaucer.com/blog/?author=2
-[1]: http://dockercon.com/
-[2]: https://blog.docker.com/author/bfirshman/
-[3]: https://blog.docker.com/author/bfirshman/
-[4]: http://www.giantflyingsaucer.com/blog/?p=5730
-[5]: https://aws.amazon.com/lambda/
-[6]: https://docs.docker.com/swarm/
-[7]: https://github.com/bfirsh/dockerrun
-[8]: https://docs.docker.com/engine/swarm/swarm-tutorial/
-[9]: https://github.com/chadlung/vagrant-docker-swarm
-[10]: https://www.vagrantup.com/
-[11]: https://test.docker.com/
-[12]: https://docs.docker.com/engine/reference/commandline/swarm_init/
-[13]: https://hub.docker.com/
-[14]: https://hub.docker.com/r/chadlung/hello-app/
-[15]: https://hub.docker.com/r/chadlung/hello-app/
-[16]: https://github.com/ManoMarks/docker-swarm-visualizer
diff --git a/translated/tech/20160809 How to build your own Git server.md b/translated/tech/20160809 How to build your own Git server.md
new file mode 100644
index 0000000000..03d77a9607
--- /dev/null
+++ b/translated/tech/20160809 How to build your own Git server.md	
@@ -0,0 +1,222 @@
+如何搭建你自己的 Git 服务器
+====================
+
+![](https://opensource.com/sites/default/files/styles/image-full-size/public/images/business/bus-big-data.png?itok=sOQHDuID)
+
+现在我们将要学习如何搭建 git 服务器,如何编写自定义的 Git 钩子来在特定的事件触发相应的动作(例如通知),或者是发布你的代码到一个站点。
+
+直到现在,作为一个使用者注意力还是被 Git 影响。这篇文章中我将讨论 Git 的管理,并且设计一个灵活的 Git 框架。你可能会觉得这听起来是 “高阶 Git 技术” 或者 “只有狂热粉才能阅读”的一句委婉的说法,但是事实是这里面的每个任务都不需要很深的知识或者其他特殊的训练,就能立刻理解 Git 的工作原理,有可能需要一丁点关于 Linux 的知识
+
+### 共享 Git 服务器
+
+创建你自己的共享 Git 服务器意外地简单,而且遇到各方面的问题时都很值得。不仅仅是因为它保证你有权限查看自己的代码,它还对于利用扩展来维持 Git 的通信敞开了一扇大门,例如个人 Git 钩子,无限制的数据存储,和持续的整合与发布
+
+如果你知道如何使用 Git 和 SSH,那么你已经知道怎么创建一个 Git 服务器了。Git 的设计方式,就是让你在创建或者 clone 一个仓库的时候,就完成了一半服务器的搭建。然后允许用 SSH 访问仓库,而且任何有权限访问的人都可以使用你的仓库,作为 clone 的新仓库的基础。
+
+但是,这是一个小的自组织。按照一些计划你可以以同样的效果创建一些设计优良的 Git 服务器,同时有更好的拓展性。
+
+首要之事:确认你的用户们,现在的用户以及之后的用户都要考虑。如果你是唯一的用户那么没有任何改动的必要。但是如果你试图邀请其他的代码贡献者,那么你应该允许一个专门的分享系统用户给你的开发者们。
+
+假定你有一个可用的服务器(如果没有,这不成问题,Git 会帮忙解决,CentOS 的 Raspberry Pi 3 是个不错的开始),然后第一步就是只允许使用 SSH 密钥认证的 SSH 登录。
+
+一旦你启用了 SSH 密钥认证,创建 gituser 用户。这是给你的所有确认的开发者们的公共用户。
+
+```
+$ su -c 'adduser gituser'
+```
+
+然后切换到刚创建的 gituser 用户,创建一个 `~/.ssh` 的框架,并设置好合适的权限。这很重要,如果权限设置得太开放会使自己受保护的 SSH 默认失败。
+
+```
+$ su - gituser
+$ mkdir .ssh && chmod 700 .ssh
+$ touch .ssh/authorized_keys
+$ chmod 600 .ssh/authorized_keys
+```
+
+`authorized_keys` 文件里包含所有你的开发者们的 SSH 公钥,你开放权限允许他们可以在你的 Git 项目上工作。他们必须创建他们自己的 SSH 密钥对然后把他们的公钥给你。复制公钥到 gituser 用户下的 `authorized_keys` 文件中。例如,为一个叫 Bob 的开发者,执行以下命令:
+
+```
+$ cat ~/path/to/id_rsa.bob.pub >> \ 
+/home/gituser/.ssh/authorized_keys
+```
+
+只要开发者 Bob 有私钥并且把相对应的公钥给你,Bob 就可以用 gituser 用户访问服务器。
+
+但是,你并不是想让你的开发者们能使用服务器,即使只是以 gituser 的身份访问。你只是想给他们访问 Git 仓库的权限。因为这个特殊的原因,Git 提供了一个限制的 shell,准确的说是 git-shell。以 root 身份执行以下命令,把 git-shell 添加到你的系统中,然后设置成 gituser 用户默认的shell。
+
+```
+# grep git-shell /etc/shells || su -c \
+"echo `which git-shell` >> /etc/shells"
+# su -c 'usermod -s git-shell gituser'
+```
+
+现在 gituser 用户只能使用 SSH 来 push 或者 pull Git 仓库,并且无法使用任何一个可以登录的 shell。你应该把你自己添加到和 gituser 一样的组中,在我们的样例服务器中仍是这个组的名字仍是 gituser。
+
+举个例子:
+
+```
+# usermod -a -G gituser seth
+```
+
+仅剩下的一步就是创建一个 Git 仓库。因为没有人能在服务器上与 Git 交互(也就是说,你之后不能 SSH 到服务器然后直接操作这个仓库),创一个最原始的仓库 。如果你想使用位于服务器上的仓库来完成工作,你可以从它的所在处 clone 下来然后直接在你的 home 目录下工作。
+
+说白了,你不需要让它变成一个空的仓库,你需要像工作在一个正常的仓库一样。但是,一个空的仓库没有 *working tree* (也就是说,使用 `checkout` 并没有任何分支显示)。这很重要,因为远程使用者们并不被允许 push 一个有效的分支(如果你正在 `dev` 分支工作然后突然有人把一些变更 push 到你的工作分支,你会有怎么样的感受?)。因为一个空的仓库可以没有有效的分支,这不会成为一个问题。
+
+你可以把这个仓库放到任何你想放的地方,只要你想要放开权限给用户和用户组,让他们可以在仓库下工作。千万不要保存目录到例如一个用户的 home 目录下,因为有严格的权限控制。保存到一个常规的共享地址,例如 `/opt` 或者 `/usr/local/share`。
+
+以 root 身份创建一个空的仓库:
+
+```
+# git init --bare /opt/jupiter.git
+# chown -R gituser:gituser /opt/jupiter.git
+# chmod -R 770 /opt/jupiter.git
+```
+
+现在任何一个用户,只要他被认证为 gituser 或者在 gituser 组中,就可以从 jupiter.git 库中读取或者写入。在本地机器尝试以下操作。
+
+```
+$ git clone gituser@example.com:/opt/jupiter.git jupiter.clone
+Cloning into 'jupiter.clone'...
+Warning: you appear to have cloned an empty repository.
+```
+
+谨记:开发者们一定要把他们的 SSH 公钥加入到 gituser 用户下的 `authorized_keys` 文件里,或者说,如果他们有服务器上的用户(如果你给了他们用户),那么他们的用户必须属于 gituser 用户组。
+
+### Git 钩子
+
+将自己的 Git 服务器跑起来,很赞的一件事就是可以使用 Git 钩子。支持 Git 的设备有时提供一个类钩子接口,但是并没有给你真正的 Git 钩子来访问文件系统。Git 钩子是一个脚本,它将在一个 Git 过程的某些点运行;当一个仓库即将接收一个 commit,或者接受一个 commit 之后,或者即将接收一次 push,或者一次 push 之后等等
+
+这是一个简单的系统:任何放在 `.git/hooks` 目录下的脚本,使用标准的命名体系,就可按设计好的时间运行。一个脚本是否应该被运行取决于它的名字; pre-push 脚本在 push 之前运行,post-received 脚本在 push 之后运行,post-receive 脚本在接受 commit 之后运行等等。
+
+脚本可以用任何语言写;如果在你的系统上有可以执行的脚本语言,例如输出 ‘hello world’ ,那么你就用这个语言来写 Git 钩子脚本。Git 默认会给出一些例子,但是并不能用。
+
+想要动手试一个?这很简单。如果你没有现成的 Git 仓库,首先创建一个 Git 仓库:
+
+```
+$ mkdir jupiter
+$ cd jupiter
+$ git init .
+```
+
+然后写一个功能为输出 “hello world” 的 Git 钩子。因为我使用 tsch 作为传统系统的长期支持 shell,所以我仍然用它作为我的脚本语言,你可以自由的使用自己喜欢的语言(Bash,Python,Ruby,Perl,Rust,Swift,Go):
+
+```
+$ echo "#\!/bin/tcsh" > .git/hooks/post-commit
+$ echo "echo 'POST-COMMIT SCRIPT TRIGGERED'" >> \
+~/jupiter/.git/hooks/post-commit
+$ chmod +x ~/jupiter/.git/hooks/post-commit
+```
+
+现在测试它的输出:
+
+```
+$ echo "hello world" > foo.txt
+$ git add foo.txt
+$ git commit -m 'first commit'
+! POST-COMMIT SCRIPT TRIGGERED
+[master (root-commit) c8678e0] first commit
+1 file changed, 1 insertion(+)
+create mode 100644 foo.txt
+```
+
+然后你已经实现了:你的第一个有功能的 Git 钩子
+
+### 有名的 push-to-web 钩子
+
+Git 钩子最流行的用法就是自动 push 更改的代码到一个正在使用中的网络服务器目录下。这是摆脱 FTP 的很好的方式,对于正在使用的产品保留完整的版本控制,自动整合发布的内容
+
+如果操作正确,在一直以来的网站发布维护中 Git 会完成的很好,而且在某种程度上,很精准。Git 真的好棒。我不知道谁最初想到这个主意,但是我第一次听到它是从 Emacs 和 Git 方面的专家,IBM 的 Bill von Hagen。他的文章包含一系列明确的介绍:[Git 改变了分布式网页开发的游戏规则][1]。
+
+### Git 变量
+
+每一个 Git 钩子都有一系列不同的变量对应触发钩子的不同 Git 行为。你需不需要这些变量,主要取决于你写的程序。如果你需要一个当某人 push 代码时候的通用邮件通知,那么你就不需要特殊化,并且甚至也不需要编写额外的脚本,因为已经有现成的适合你的脚本。如果你想在邮件里查看 commit 信息和 commit 的作者,那么你的脚本就会变得相对棘手些。
+
+Git 钩子并不是被用户直接执行,所以要弄清楚如何收集可能会误解的重要信息。事实上,Git 钩子脚本就像其他的脚本一样,像 BASH, Python, C++ 等等,从标准输入读取参数。不同的是,我们不会给它提供这个输入,所以,你在使用的时候,需要知道可能的输入参数。
+
+在写 Git 钩子之前,看一下 Git 在你的项目目录下 `.git/hooks` 目录中提供的一些例子。举个例子,在这个 `pre-push.sample` 文件里,注释部分说明了如下内容:
+
+```
+# $1 -- 即将 push 的远方仓库的名字
+# $2 -- 即将 push 的远方仓库的 URL
+# 如果 push 的时候,并没有一个命名的远方仓库,那么这两个参数将会一样。
+#
+# 对于这个表单的标准输入, 
+# 参数通过行的形式输入
+# <local ref> <local sha1> <remote ref> <remote sha1>
+```
+
+并不是所有的例子都是这么清晰,而且关于钩子获取变量的文档依旧缺乏(除非你去读 Git 的源码)。但是,如果你有疑问,你可以从[其他用户的尝试中][2]学习,或者你只是写一些基本的脚本,比如 `echo $1, $2, $3` 等等。
+
+### 分支检测示例
+
+我发现,对于生产环境来说有一个共同的需求,就是需要一个只有在特定分支被修改之后,才会触发事件的钩子。以下就是如何跟踪分支的示例。
+
+首先,Git 钩子本身是不受版本控制的。 Git 并不会跟踪他自己的钩子,因为对于钩子来说,他是 Git 的一部分,而不是你仓库的一部分。所以,Git 钩子可以监控你的 Git 服务器上的 Git 仓库的 commit 记录和 push 记录,而不是你本地仓库的一部分。
+
+我们来写一个 post-receive(也就是说,在 commit 被接受之后触发)钩子。第一步就是需要确定分支名:
+
+```
+#!/bin/tcsh
+
+foreach arg ( $< )
+  set argv = ( $arg )
+  set refname = $1
+end
+```
+
+这个 for 循环用来读入第一个参数 `arg($1)` 然后循环用第二个参数 `($2)` 去重写,然后用第三个参数 `($3)` 。在 Bash 中有一个更好的方法,使用 read 命令,并且把值放入数组里。但是,这里是 tcsh,并且变量的顺序可以预测的,所以,这个方法也是可行的。
+
+当我们有了 commit 记录的 refname,我们就能使用 Git 去找到这个分支的人类能读的名字:
+
+```
+set branch = `git rev-parse --symbolic --abbrev-ref $refname`
+echo $branch #DEBUG
+```
+
+然后把这个分支名和我们想要触发的事件的分支名关键字进行比较。
+
+```
+if ( "$branch" == "master" ) then
+  echo "Branch detected: master"
+  git \
+    --work-tree=/path/to/where/you/want/to/copy/stuff/to \
+    checkout -f $branch || echo "master fail"
+else if ( "$branch" == "dev" ) then
+  echo "Branch detected: dev"
+  Git \
+    --work-tree=/path/to/where/you/want/to/copy/stuff/to \
+    checkout -f $branch || echo "dev fail"
+  else
+    echo "Your push was successful."
+    echo "Private branch detected. No action triggered."
+endif
+```
+
+给这个脚本分配可执行权限:
+
+```
+$ chmod +x ~/jupiter/.git/hooks/post-receive
+```
+
+现在,当一个用户在服务器的 master 分支 commit 代码,这个代码就会被复制到一个生产环境的目录,dev 分支的一个 commit 记录也会被复制到其他地方,其他分支将不会触发这些操作。
+
+同时,创造一个 pre-commit 脚本也很简单。比如,判断一个用户是否在他们不该 push 的分支上 push 代码,或者对 commit 信息进行解析等等。
+
+Git 钩子也可以变得复杂,而且他们因为 Git 的工作流的抽象层次不同而变得难以理解,但是他们确实是一个强大的系统,让你能够在你的 Git 基础设施上针对所有的行为进行对应的操作。如果你是一个 Git 重度用户,或者一个全职 Git 管理员,那么 Git 钩子是值得学习的。
+
+在我们这个系列之后和最后的文章,我们将会学习如何使用 Git 来管理非文本的二进制数据,比如音频和图片。
+
+--------------------------------------------------------------------------------
+
+via: https://opensource.com/life/16/8/how-construct-your-own-git-server-part-6
+
+作者:[Seth Kenlon][a]
+译者:[maywanting](https://github.com/maywanting)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://opensource.com/users/seth
+[1]: http://www.ibm.com/developerworks/library/wa-git/
+[2]: https://www.analysisandsolutions.com/code/git-hooks-summary-cheat-sheet.htm
diff --git a/translated/tech/20160809 Part 3 - Let’s Build A Web Server.md b/translated/tech/20160809 Part 3 - Let’s Build A Web Server.md
new file mode 100644
index 0000000000..41a128a8a7
--- /dev/null
+++ b/translated/tech/20160809 Part 3 - Let’s Build A Web Server.md	
@@ -0,0 +1,1024 @@
+translating by StdioA
+
+搭个 Web 服务器(三)
+=====================================
+
+>“当我们必须创造时,才能够学到更多。” ——伯爵
+
+在本系列的第二部分中,你创造了一个可以处理基本 HTTP GET 请求的、朴素的 WSGI 服务器。当时我问了一个问题:“你该如何让你的服务器在同一时间处理多个请求呢?”在这篇文章中,你会找到答案。系好安全带,我们要认真起来,全速前进了!你将会体验到一段非常快速的旅程。准备好你的 Linux,Mac OS X(或者其他 *nix 系统),还有你的 Python. 本文中所有源代码均可在 [GitHub][1] 上找到。
+
+首先,我们来回顾一下 Web 服务器的基本结构,以及服务器处理来自客户端的请求时,所需的必要步骤。你在第一及第二部分中创建的轮询服务器只能够在同一时间内处理一个请求。在处理完当前请求之前,它不能够打开一个新的客户端连接。所有请求为了等待服务都需要排队,在服务繁忙时,这个队伍可能会排的很长,一些客户端可能会感到不开心。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it1.png)
+
+这是轮询服务器 [webserver3a.py][2] 的代码:
+
+```
+#####################################################################
+# 轮询服务器 - webserver3a.py                                       #
+#                                                                   #
+# 使用 Python 2.7.9 或 3.4                                          #
+# 在 Ubuntu 14.04 及 Mac OS X 环境下测试通过                        #
+#####################################################################
+import socket
+
+SERVER_ADDRESS = (HOST, PORT) = '', 8888
+REQUEST_QUEUE_SIZE = 5
+
+
+def handle_request(client_connection):
+    request = client_connection.recv(1024)
+    print(request.decode())
+    http_response = b"""\
+HTTP/1.1 200 OK
+
+Hello, World!
+"""
+    client_connection.sendall(http_response)
+
+
+def serve_forever():
+    listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    listen_socket.bind(SERVER_ADDRESS)
+    listen_socket.listen(REQUEST_QUEUE_SIZE)
+    print('Serving HTTP on port {port} ...'.format(port=PORT))
+
+    while True:
+        client_connection, client_address = listen_socket.accept()
+        handle_request(client_connection)
+        client_connection.close()
+
+if __name__ == '__main__':
+    serve_forever()
+```
+
+为了观察到你的服务器在同一时间只能处理一个请求,我们对服务器的代码做一点点修改:在将响应发送至客户端之后,将程序阻塞 60 秒。这个修改只需要一行代码,来告诉服务器进程暂停 60 秒钟。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it2.png)
+
+这是我们更改后的代码,包含暂停语句的服务器 [webserver3b.py][3]:
+
+```
+#########################################################################
+# 轮询服务器 - webserver3b.py                                           #
+#                                                                       #
+# 使用 Python 2.7.9 或 3.4                                              #
+# 在 Ubuntu 14.04 及 Mac OS X 环境下测试通过                            #
+#                                                                       #
+# - 服务器向客户端发送响应之后,会阻塞 60 秒                            #
+#########################################################################
+import socket
+import time
+
+SERVER_ADDRESS = (HOST, PORT) = '', 8888
+REQUEST_QUEUE_SIZE = 5
+
+
+def handle_request(client_connection):
+    request = client_connection.recv(1024)
+    print(request.decode())
+    http_response = b"""\
+HTTP/1.1 200 OK
+
+Hello, World!
+"""
+    client_connection.sendall(http_response)
+    time.sleep(60)  # 睡眠语句,阻塞该进程 60 秒
+
+
+def serve_forever():
+    listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    listen_socket.bind(SERVER_ADDRESS)
+    listen_socket.listen(REQUEST_QUEUE_SIZE)
+    print('Serving HTTP on port {port} ...'.format(port=PORT))
+
+    while True:
+        client_connection, client_address = listen_socket.accept()
+        handle_request(client_connection)
+        client_connection.close()
+
+if __name__ == '__main__':
+    serve_forever()
+```
+
+用以下命令启动服务器:
+
+```
+$ python webserver3b.py
+```
+
+现在,打开一个新的命令行窗口,然后运行 `curl` 语句。你应该可以立刻看到屏幕上显示的字符串“Hello, World!”:
+
+```
+$ curl http://localhost:8888/hello
+Hello, World!
+```
+
+然后,立刻打开第二个命令行窗口,运行相同的 `curl` 命令:
+
+```
+$ curl http://localhost:8888/hello
+```
+
+如果你在 60 秒之内完成了以上步骤,你会看到第二条 `curl` 指令不会立刻产生任何输出,而只是挂在了哪里。同样,服务器也不会在标准输出流中输出新的请求内容。这是这个过程在我的 Mac 电脑上的运行结果(在右下角用黄色框标注出来的窗口中,我们能看到第二个 `curl` 指令被挂起,正在等待连接被服务器接受):
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it3.png)
+
+当你等待足够长的时间(60 秒以上)后,你会看到第一个 `curl` 程序完成,而第二个 `curl` 在屏幕上输出了“Hello, World!”,然后休眠 60 秒,进而停止运行。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it4.png)
+
+这两个程序这样运行,是因为在服务器在处理完第一个来自 `curl` 的请求之后,只有等待 60 秒才能开始处理第二个请求。这个处理请求的过程按顺序进行(也可以说,迭代进行),一步一步进行,在我们刚刚给出的例子中,在同一时间内只能处理一个请求。
+
+现在,我们来简单讨论一下客户端与服务器的交流过程。为了让两个程序在网络中互相交流,它们必须使用套接字。你应当在本系列的前两部分中见过它几次了。但是,套接字是什么?
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it_socket.png)
+
+套接字是一个交互通道的端点的抽象形式,它可以让你的程序通过文件描述符来与其它程序进行交流。在这篇文章中,我只会单独讨论 Linux 或 Mac OS X 中的 TCP/IP 套接字。这里有一个重点概念需要你去理解:TCP 套接字对。
+
+> TCP 连接使用的套接字对是一个由 4 个元素组成的元组,它确定了 TCP 连接的两端:本地 IP 地址、本地端口、远端 IP 地址及远端端口。一个套接字对独一无二地确定了网络中的每一个 TCP 连接。在连接一端的两个值:一个 IP 地址和一个端口,通常被称作一个套接字。[1][4]
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it_socketpair.png)
+
+所以,元组 {10.10.10.2:49152, 12.12.12.3:8888} 就是一个能够在客户端确定 TCP 连接两端的套接字对,而元组 {12.12.12.3:8888, 10.10.10.2:49152} 则是在服务端确定 TCP 连接两端的套接字对。在这个例子中,确定 TCP 服务端的两个值(IP 地址 `12.12.12.3` 及端口 `8888`),代表一个套接字;另外两个值则代表客户端的套接字。
+
+一个服务器创建一个套接字并开始建立连接的基本工作流程如下:
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it_server_socket_sequence.png)
+
+1. 服务器创建一个 TCP/IP 套接字。我们可以用下面那条 Python 语句来创建:
+
+```
+listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+```
+
+2. 服务器可能会设定一些套接字选项(这个步骤是可选的,但是你可以看到上面的服务器代码做了设定,这样才能够在重启服务器时多次复用同一地址)。
+
+```
+listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+```
+
+3. 然后,服务器绑定一个地址。绑定函数可以将一个本地协议地址赋给套接字。若使用 TCP 协议,调用绑定函数时,需要指定一个端口号,一个 IP 地址,或两者兼有,或两者兼无。[1][4]
+
+```
+listen_socket.bind(SERVER_ADDRESS)
+```
+
+4. 然后,服务器开启套接字的监听模式。
+
+```
+listen_socket.listen(REQUEST_QUEUE_SIZE)
+```
+
+监听函数只应在服务端调用。它会通知操作系统内核,标明它会接受所有向该套接字发送请求的链接。
+
+以上四步完成后,服务器将循环接收来自客户端的连接,一次循环处理一条。当有连接可用时,`accept` 函数将会返回一个已连接的客户端套接字。然后,服务器从客户端套接字中读取请求数据,将它在标准输出流中打印出来,并向客户端回送一条消息。然后,服务器会关闭这个客户端连接,并准备接收一个新的客户端连接。
+
+这是客户端使用 TCP/IP 协议与服务器通信的必要步骤:
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it_client_socket_sequence.png)
+
+下面是一段示例代码,使用这段代码,客户端可以连接你的服务器,发送一个请求,并打印响应内容:
+
+```
+import socket
+
+# 创建一个套接字,并连接值服务器
+sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sock.connect(('localhost', 8888))
+
+# 发送一段数据,并接收响应数据
+sock.sendall(b'test')
+data = sock.recv(1024)
+print(data.decode())
+```
+
+在创建套接字后,客户端需要连接至服务器。我们可以调用 `connect` 函数来完成这个操作:
+
+```
+sock.connect(('localhost', 8888))
+```
+
+客户端只需提供待连接服务器的 IP 地址(或主机名),及端口号,即可连接至远端服务器。
+
+你可能已经注意到了,客户端不需要调用 `bind` 及 `accept` 函数,就可以与服务器建立连接。客户端不需要调用 `bind` 函数是因为客户端不需要关注本地 IP 地址及端口号。操作系统内核中的 TCP/IP 协议栈会在客户端调用 `connect` 函数时,自动为套接字分配本地 IP 地址及本地端口号。这个本地端口被称为临时端口,也就是一个短暂开放的端口。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it_ephemeral_port.png)
+
+服务器中有一些端口被用于承载一些众所周知的服务,它们被称作通用端口:如 80 端口用于 HTTP 服务,22 端口用于 SSH 服务。打开你的 Python shell,与你在本地运行的服务器建立一个连接,来看看内核给你的客户端套接字分配了哪个临时端口(在尝试这个例子之前,你需要运行服务器程序 `webserver3a.py` 或 `webserver3b.py`):
+
+```
+>>> import socket
+>>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+>>> sock.connect(('localhost', 8888))
+>>> host, port = sock.getsockname()[:2]
+>>> host, port
+('127.0.0.1', 60589)
+```
+
+在上面的例子中,内核将临时端口 60589 分配给了你的套接字。
+
+在我开始回答我在第二部分中提出的问题之前,我还需要快速讲解一些概念。你很快就会明白这些概念为什么非常重要。这两个概念,一个是进程,另外一个是文件描述符。
+
+什么是进程?进程就是一个程序执行的实体。举个例子:当你的服务器代码被执行时,它会被载入内存,而内存中表现此次程序运行的实体就叫做进程。内核记录了进程的一系列有关信息——比如进程 ID——来追踪它的运行情况。当你在执行轮询服务器 `webserver3a.py` 或 `webserver3b.py` 时,你只启动了一个进程。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it_server_process.png)
+
+我们在终端窗口中运行 `webserver3b.py`:
+Start the server webserver3b.py in a terminal window:
+
+```
+$ python webserver3b.py
+```
+
+在另一个终端窗口中,我们可以使用 `ps` 命令获取该进程的相关信息:
+
+```
+$ ps | grep webserver3b | grep -v grep
+7182 ttys003    0:00.04 python webserver3b.py
+```
+
+`ps` 命令显示,我们刚刚只运行了一个 Python 进程 `webserver3b`。当一个进程被创建时,内核会为其分配一个进程 ID,也就是 PID。在 UNIX 中,所有用户进程都有一个父进程;当然,这个父进程也有进程 ID,叫做父进程 ID,缩写为 PPID。假设你默认使用 BASH shell,那当你启动服务器时,一个新的进程会被启动,同时被赋予一个 PID,而它的父进程 PID 会被设为 BASH shell 的 PID。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it_ppid_pid.png)
+
+自己尝试一下,看看这一切都是如何工作的。重新开启你的 Python shell,它会创建一个新进程,然后在其中使用系统调用 `os.getpid()` 及 `os.getppid()` 来获取 Python shell 进程的 PID 及其父进程 PID(也就是你的 BASH shell 的 PID)。然后,在另一个终端窗口中运行 `ps` 命令,然后用 `grep` 来查找 PPID(父进程 ID,在我的例子中是 3148)。在下面的屏幕截图中,你可以看到一个我的 Mac OS X 系统中关于进程父子关系的例子,在这个例子中,子进程是我的 Python shell 进程,而父进程是 BASH shell 进程:
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it_pid_ppid_screenshot.png)
+
+另外一个需要了解的概念,就是文件描述符。什么是文件描述符?文件描述符是一个非负整数,当进程打开一个现有文件、创建新文件或创建一个新的套接字时,内核会将这个数返回给进程。你以前可能听说过,在 UNIX 中,一切皆是文件。内核会根据一个文件描述符来为一个进程打开一个文件。当你需要读取文件或向文件写入时,我们同样通过文件描述符来定位这个文件。Python 提供了高层次的文件(或套接字)对象,所以你不需要直接通过文件描述符来定位文件。但是,在高层对象之下,我们就是用它来在 UNIX 中定位文件及套接字:整形的文件描述符。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it_process_descriptors.png)
+
+一般情况下,UNIX shell 会将一个进程的标准输入流的文件描述符设为 0,标准输出流设为 1,而标准错误打印的文件描述符会被设为 2。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it_default_descriptors.png)
+
+我之前提到过,即使 Python 提供了高层次的文件对象或类文件对象来供你操作,你仍然可以在对象上使用 `fileno()` 方法,来获取与该文件相关联的文件描述符。回到 Python shell 中,我们来看看你该怎么做到这一点:
+
+
+```
+>>> import sys
+>>> sys.stdin
+<open file '<stdin>', mode 'r' at 0x102beb0c0>
+>>> sys.stdin.fileno()
+0
+>>> sys.stdout.fileno()
+1
+>>> sys.stderr.fileno()
+2
+```
+
+当你在 Python 中操作文件及套接字时,你可能会使用高层次的文件/套接字对象,但是你仍然有可能会直接使用文件描述符。下面有一个例子,来演示如何用文件描述符做参数来进行一次写入的系统调用:
+
+```
+>>> import sys
+>>> import os
+>>> res = os.write(sys.stdout.fileno(), 'hello\n')
+hello
+```
+
+下面是比较有趣的部分——不过你可能不会为此感到惊讶,因为你已经知道在 Unix 中,一切皆为文件——你的套接字对象同样有一个相关联的文件描述符。和刚才操纵文件时一样,当你在 Python 中创建一个套接字时,你会得到一个对象而不是一个非负整数,但你永远可以用我之前提到过的 `fileno()` 方法获取套接字对象的文件描述符,并可以通过这个文件描述符来直接操纵套接字。
+
+```
+>>> import socket
+>>> sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+>>> sock.fileno()
+3
+```
+
+我还想再提一件事:不知道你有没有注意到,在我们的第二个轮询服务器 `webserver3b.py` 中,当你的服务器休眠 60 秒的过程中,你仍然可以通过第二个 `curl` 命令连接至服务器。当然 `curl` 命令并没有立刻输出任何内容而是挂在哪里,但是既然服务器没有接受连接,那它为什么不立即拒绝掉连接,而让它还能够继续与服务器建立连接呢?这个问题的答案是:当我在调用套接字对象的 `listen` 方法时,我为该方法提供了一个 `BACKLOG` 参数,在代码中用 `REQUEST_QUEUE_SIZE` 变量来表示。`BACKLOG` 参数决定了在内核中为存放即将到来的连接请求所创建的队列的大小。当服务器 `webserver3b.py` 被挂起的时候,你运行的第二个 `curl` 命令依然能够连接至服务器,因为内核中用来存放即将接收的连接请求的队列依然拥有足够大的可用空间。
+
+尽管增大 `BACKLOG` 参数并不能神奇地使你的服务器同时处理多个请求,但当你的服务器很繁忙时,将它设置为一个较大的值还是相当重要的。这样,在你的服务器调用 `accept` 方法时,不需要再等待一个新的连接建立,而可以立刻直接抓取队列中的第一个客户端连接,并不加停顿地立刻处理它。
+
+欧耶!现在你已经了解了一大块内容。我们来快速回顾一下我们刚刚讲解的知识(当然,如果这些对你来说都是基础知识的话,那我们就当复习好啦)。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_checkpoint.png)
+
+- 迭代服务器
+- 服务端套接字创建流程(创建套接字,绑定,监听及接受)
+- 客户端连接创建流程(创建套接字,连接)
+- 套接字对
+- 套接字
+- 临时端口及通用端口
+- 进程
+- 进程 ID(PID),父进程 ID(PPID),以及进程父子关系
+- 文件描述符
+- 套接字的 `listen` 方法中,`BACKLOG` 参数的含义
+
+现在,我可以开始回答第二部分中的那个问题了:“你该如何让你的服务器在同一时间处理多个请求呢?”或者换一种说法:“如何编写一个并发服务器?”
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc2_service_clients.png)
+
+在 UNIX 系统中编写一个并发服务器最简单的方法,就是使用系统调用 `fork()`。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_fork.png)
+
+下面是全新出炉的并发服务器 `webserver3c.py` 的代码,它可以同时处理多个请求(和我们之前的例子 `webserver3b.py` 一样,每个子进程都会休眠 60 秒):
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_it2.png)
+
+```
+#######################################################
+# 并发服务器 - webserver3c.py                         #
+#                                                     #
+# 使用 Python 2.7.9 或 3.4                            #
+# 在 Ubuntu 14.04 及 Mac OS X 环境下测试通过          #
+#                                                     #
+# - 完成客户端请求处理之后,子进程会休眠 60 秒        #
+# - 父子进程会关闭重复的描述符                        #
+#                                                     #
+#######################################################
+import os
+import socket
+import time
+
+SERVER_ADDRESS = (HOST, PORT) = '', 8888
+REQUEST_QUEUE_SIZE = 5
+
+
+def handle_request(client_connection):
+    request = client_connection.recv(1024)
+    print(
+        'Child PID: {pid}. Parent PID {ppid}'.format(
+            pid=os.getpid(),
+            ppid=os.getppid(),
+        )
+    )
+    print(request.decode())
+    http_response = b"""\
+HTTP/1.1 200 OK
+
+Hello, World!
+"""
+    client_connection.sendall(http_response)
+    time.sleep(60)
+
+
+def serve_forever():
+    listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    listen_socket.bind(SERVER_ADDRESS)
+    listen_socket.listen(REQUEST_QUEUE_SIZE)
+    print('Serving HTTP on port {port} ...'.format(port=PORT))
+    print('Parent PID (PPID): {pid}\n'.format(pid=os.getpid()))
+
+    while True:
+        client_connection, client_address = listen_socket.accept()
+        pid = os.fork()
+        if pid == 0:  # 子进程
+            listen_socket.close()  # 关闭子进程中复制的套接字对象
+            handle_request(client_connection)
+            client_connection.close()
+            os._exit(0)  # 子进程在这里退出
+        else:  # 父进程
+            client_connection.close()  # 关闭父进程中的客户端连接对象,并循环执行
+
+if __name__ == '__main__':
+    serve_forever()
+```
+
+在深入研究代码、讨论 `fork` 如何工作之前,先尝试运行它,自己看一看这个服务器是否真的可以同时处理多个客户端请求,而不是像轮询服务器 `webserver3a.py` 和 `webserver3b.py` 一样。在命令行中使用如下命令启动服务器:
+
+```
+$ python webserver3c.py
+```
+
+然后,像我们之前测试轮询服务器那样,运行两个 `curl` 命令,来看看这次的效果。现在你可以看到,即使子进程在处理客户端请求后会休眠 60 秒,但它并不会影响其它客户端连接,因为他们都是由完全独立的进程来处理的。你应该看到你的 `curl` 命令立即输出了“Hello, World!”然后挂起 60 秒。你可以按照你的想法运行尽可能多的 `curl` 命令(好吧,并不能运行特别特别多 ^_^),所有的命令都会立刻输出来自服务器的响应“Hello, World!”,并不会出现任何可被察觉到的延迟行为。试试看吧。
+
+如果你要理解 `fork()`,那最重要的一点是:你调用了它一次,但是它会返回两次:一次在父进程中,另一次是在子进程中。当你创建了一个新进程,那么 `fork()` 在子进程中的返回值是 0。如果是在父进程中,那 `fork()` 函数会返回子进程的 PID。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc2_how_fork_works.png)
+
+我依然记得在第一次看到它并尝试使用 `fork()` 的时候,我是多么的入迷。它在我眼里就像是魔法一样。这就好像我在读一段顺序执行的代码,然后“砰”地一声,代码变成了两份,然后出现了两个实体,同时并行地运行相同的代码。讲真,那个时候我觉得它真的跟魔法一样神奇。
+
+当父进程创建出一个新的子进程时,子进程会复制从父进程中复制一份文件描述符:
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc2_shared_descriptors.png)
+
+你可能注意到,在上面的代码中,父进程关闭了客户端连接:
+
+```
+else:  # parent
+    client_connection.close()  # close parent copy and loop over
+```
+
+不过,既然父进程关闭了这个套接字,那为什么子进程仍然能够从来自客户端的套接字中读取数据呢?答案就在上面的图片中。内核会使用描述符引用计数器来决定是否要关闭一个套接字。当你的服务器创建一个子进程时,子进程会复制父进程的所有文件描述符,内核中改描述符的引用计数也会增加。如果只有一个父进程及一个子进程,那客户端套接字的文件描述符引用数应为 2;当父进程关闭客户端连接的套接字时,内核只会减少它的引用计数,将其变为 1,但这仍然不会使内核关闭该套接字。子进程也关闭了父进程中 `listen_socket` 的复制实体,因为子进程不需要关注新的客户端连接,而只需要处理已建立的客户端连接中的请求。
+
+```
+listen_socket.close()  # 关闭子进程中的复制实体
+```
+
+我们将会在后文中讨论,如果你不关闭那些重复的描述符,会发生什么。
+
+你可以从你的并发服务器源码看到,父进程的主要职责为:接受一个新的客户端连接,复制出一个子进程来处理这个连接,然后继续循环来接受另外的客户端连接,仅此而已。服务器父进程并不会处理客户端连接——子进程才会做这件事。
+
+打个岔:当我们说两个事件并发执行时,我们在说什么?
+A little aside. What does it mean when we say that two events are concurrent?
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc2_concurrent_events.png)
+
+当我们说“两个事件并发执行”时,它通常意味着这两个事件同时发生。简单来讲,这个定义没问题,但你应该记住它的严格定义:
+
+> 如果你阅读代码时,无法判断两个事件的发生顺序,那这两个事件就是并发执行的。[2][5]
+
+好的,现在你又该回顾一下你刚刚学过的知识点了。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_checkpoint.png)
+
+- 在 Unix 中,编写一个并发服务器的最简单的方式——使用 `fork()` 系统调用;
+- 当一个进程复制出另一个进程时,它会变成刚刚复制出的进程的父进程;
+- 在进行 `fork` 调用后,父进程和子进程共享相同的文件描述符;
+- 系统内核通过描述符引用计数来决定是否要关闭该描述符对应的文件或套接字;
+- 服务器父进程的主要职责:现在它做的只是从客户端接受一个新的连接,复制出子进程来处理这个客户端连接,然后开始下一轮循环,去接收新的客户端连接。
+
+我们来看看,如果我们不在父进程与子进程中关闭重复的套接字描述符会发生什么。下面是刚才的并发服务器代码的修改版本,这段代码(`webserver3d.py` 中,服务器不会关闭重复的描述符):
+
+```
+#######################################################
+# 并发服务器 - webserver3d.py                         #
+#                                                     #
+# 使用 Python 2.7.9 或 3.4                            #
+# 在 Ubuntu 14.04 及 Mac OS X 环境下测试通过          #
+#######################################################
+import os
+import socket
+
+SERVER_ADDRESS = (HOST, PORT) = '', 8888
+REQUEST_QUEUE_SIZE = 5
+
+
+def handle_request(client_connection):
+    request = client_connection.recv(1024)
+    http_response = b"""\
+HTTP/1.1 200 OK
+
+Hello, World!
+"""
+    client_connection.sendall(http_response)
+
+
+def serve_forever():
+    listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    listen_socket.bind(SERVER_ADDRESS)
+    listen_socket.listen(REQUEST_QUEUE_SIZE)
+    print('Serving HTTP on port {port} ...'.format(port=PORT))
+
+    clients = []
+    while True:
+        client_connection, client_address = listen_socket.accept()
+        # 将引用存储起来,否则在下一轮循环时,他们会被垃圾回收机制销毁
+        clients.append(client_connection)
+        pid = os.fork()
+        if pid == 0:  # 子进程
+            listen_socket.close()  # 关闭子进程中多余的套接字
+            handle_request(client_connection)
+            client_connection.close()
+            os._exit(0)  # 子进程在这里结束
+        else:  # 父进程
+            # client_connection.close()
+            print(len(clients))
+
+if __name__ == '__main__':
+    serve_forever()
+```
+
+用以下命令来启动服务器:
+
+```
+$ python webserver3d.py
+```
+
+用 `curl` 命令连接服务器:
+
+```
+$ curl http://localhost:8888/hello
+Hello, World!
+```
+
+好,`curl` 命令输出了来自并发服务器的响应内容,但程序并没有退出,而是仍然挂起。到底发生了什么?这个服务器并不会挂起 60 秒:子进程只处理客户端连接,关闭连接然后退出,但客户端的 `curl` 命令并没有终止。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc3_child_is_active.png)
+
+所以,为什么 `curl` 不终止呢?原因就在于多余的文件描述符。当子进程关闭客户端连接时,系统内核会减少客户端套接字的引用计数,将其变为 1。服务器子进程退出了,但客户端套接字并没有被内核关闭,因为该套接字的描述符引用计数并没有变为 0,所以,这就导致了连接终止包(在 TCP/IP 协议中称作 `FIN`)不会被发送到客户端,所以客户端会一直保持连接。这里就会出现另一个问题:如果你的服务器在长时间运行,并且不关闭重复的文件描述符,那么可用的文件描述符会被消耗殆尽:
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc3_out_of_descriptors.png)
+
+使用 `Control-C` 关闭服务器 `webserver3d.py`,然后在 shell 中使用内置命令 `ulimit` 来查看系统默认为你的服务器进程分配的可用资源数:
+
+```
+$ ulimit -a
+core file size          (blocks, -c) 0
+data seg size           (kbytes, -d) unlimited
+scheduling priority             (-e) 0
+file size               (blocks, -f) unlimited
+pending signals                 (-i) 3842
+max locked memory       (kbytes, -l) 64
+max memory size         (kbytes, -m) unlimited
+open files                      (-n) 1024
+pipe size            (512 bytes, -p) 8
+POSIX message queues     (bytes, -q) 819200
+real-time priority              (-r) 0
+stack size              (kbytes, -s) 8192
+cpu time               (seconds, -t) unlimited
+max user processes              (-u) 3842
+virtual memory          (kbytes, -v) unlimited
+file locks                      (-x) unlimited
+```
+
+你可以从上面的结果看到,在我的 Ubuntu box 中,系统为我的服务器进程分配的最大可用文件描述符(文件打开)数为 1024。
+
+现在我们来看一看,如果你的服务器不关闭重复的描述符,它会如何消耗可用的文件描述符。在一个已有的或新建的终端窗口中,将你的服务器进程的最大可用文件描述符设为 256:
+
+```
+$ ulimit -n 256
+```
+
+在你刚刚运行 `ulimit -n 256` 的终端窗口中运行服务器 `webserver3d.py`:
+
+```
+$ python webserver3d.py
+```
+
+然后使用下面的客户端 `client3.py` 来测试你的服务器。
+
+```
+#######################################################
+# 测试客户端 - client3.py                             #
+#                                                     #
+# 使用 Python 2.7.9 或 3.4                            #
+# 在 Ubuntu 14.04 及 Mac OS X 环境下测试通过          #
+#######################################################
+import argparse
+import errno
+import os
+import socket
+
+
+SERVER_ADDRESS = 'localhost', 8888
+REQUEST = b"""\
+GET /hello HTTP/1.1
+Host: localhost:8888
+
+"""
+
+
+def main(max_clients, max_conns):
+    socks = []
+    for client_num in range(max_clients):
+        pid = os.fork()
+        if pid == 0:
+            for connection_num in range(max_conns):
+                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+                sock.connect(SERVER_ADDRESS)
+                sock.sendall(REQUEST)
+                socks.append(sock)
+                print(connection_num)
+                os._exit(0)
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(
+        description='Test client for LSBAWS.',
+        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
+    )
+    parser.add_argument(
+        '--max-conns',
+        type=int,
+        default=1024,
+        help='Maximum number of connections per client.'
+    )
+    parser.add_argument(
+        '--max-clients',
+        type=int,
+        default=1,
+        help='Maximum number of clients.'
+    )
+    args = parser.parse_args()
+    main(args.max_clients, args.max_conns)
+```
+
+在一个新建的终端窗口中,运行 `client3.py` 然后让它与服务器同步创建 300 个连接:
+
+```
+$ python client3.py --max-clients=300
+```
+
+过一会,你的服务器就该爆炸了。这是我的环境中出现的异常截图:
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc3_too_many_fds_exc.png)
+
+这个例子很明显——你的服务器应该关闭重复的描述符。但是,即使你关闭了多余的描述符,你依然没有摆脱险境,因为你的服务器还有一个问题,这个问题在于“僵尸”!
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc3_zombies.png)
+
+
+没错,这个服务器代码确实在制造僵尸进程。我们来看看怎么回事。重新运行你的服务器:
+
+```
+$ python webserver3d.py
+```
+
+在另一个终端窗口中运行以下 `curl` 命令:
+
+```
+$ curl http://localhost:8888/hello
+```
+
+现在,运行 `ps` 环境,来查看正在运行的 Python 进程。下面是我的环境中 `ps` 的运行结果:
+
+```
+$ ps auxw | grep -i python | grep -v grep
+vagrant   9099  0.0  1.2  31804  6256 pts/0    S+   16:33   0:00 python webserver3d.py
+vagrant   9102  0.0  0.0      0     0 pts/0    Z+   16:33   0:00 [python] <defunct>
+```
+
+你看到第二行中,pid 为 9102,状态为 Z+,名字里面有个 `<defunct>` 的进程了吗?那就是我们的僵尸进程。这个僵尸进程的问题在于:你无法将它杀掉。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc3_kill_zombie.png)
+
+就算你尝试使用 `kill -9` 来杀死僵尸进程,它们仍旧会存活。自己试试看,看看结果。
+
+这个僵尸到底是什么,为什么我们的服务器会造出它们呢?一个僵尸进程是一个已经结束的进程,但它的父进程并没有等待它结束,并且也没有收到它的终结状态。如果一个进程在父进程退出之前退出,系统内核会把它变为一个僵尸进程,存储它的部分信息,以便父进程读取。内核保存的进程信息通常包括进程 ID,进程终止状态,以及进程的资源占用情况。OK,所以僵尸进程确实有存在的意义,但如果服务器不管这些僵尸进程,你的系统调用将会被阻塞。我们来看看这个要如何发生。首先,关闭你的服务器;然后,在一个新的终端窗口中,使用 `ulimit` 命令将最大用户进程数设为 400(同时,要确保你的最大可用描述符数大于这个数字,我们在这里设为 500):
+
+```
+$ ulimit -u 400
+$ ulimit -n 500
+```
+
+在你刚刚运行 `ulimit -u 400` 命令的终端中,运行服务器 `webserver3d.py`:
+
+```
+$ python webserver3d.py
+```
+
+在一个新的终端窗口中,运行 `client3.py`,并且让它与服务器同时创建 500 个连接:
+
+```
+$ python client3.py --max-clients=500
+```
+
+然后,过一会,你的服务器应该会再次爆炸,它会在创建新进程时抛出一个 `OSError: 资源暂时不可用` 异常。但它并没有达到系统允许的最大进程数。这是我的环境中输出的异常信息截图:
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc3_resource_unavailable.png)
+
+你可以看到,如果服务器不管僵尸进程,它们会引发问题。我会简单探讨一下僵尸进程问题的解决方案。
+
+我们来回顾一下你刚刚掌握的知识点:
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_checkpoint.png)
+
+- 如果你不关闭重复的描述符,客户端就不会在请求处理完成后终止,因为客户端连接没有被关闭;
+- 如果你不关闭重复的描述符,长久运行的服务器最终会把可用的文件描述符(最大文件打开数)消耗殆尽;
+- 当你创建一个新进程,而父进程不等待子进程,也不在子进程结束后收集它的终止状态,它会变为一个僵尸进程;
+- 僵尸通常都会吃东西,在我们的例子中,僵尸进程会占用资源。如果你的服务器不管僵尸进程,它最终会消耗掉所有的可用进程(最大用户进程数);
+- 你不能杀死僵尸进程,你需要等待它。
+
+所以,你需要做什么来处理僵尸进程呢?你需要修改你的服务器代码,来等待僵尸进程,并收集它们的终止信息。你可以在代码中使用系统调用 `wait` 来完成这个任务。不幸的是,这个方法里理想目标还很远,因为在没有终止的子进程存在的情况下调用 `wait` 会导致程序阻塞,这会阻碍你的服务器处理新的客户端连接请求。那么,我们有其他选择吗?嗯,有的,其中一个解决方案需要结合信号处理以及 `wait` 系统调用。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc4_signaling.png)
+
+这是它的工作流程。当一个子进程退出时,内核会发送 `SIGCHLD` 信号。父进程可以设置一个信号处理器,它可以异步响应 `SIGCHLD` 信号,并在信号响应函数中等待子进程收集终止信息,从而阻止了僵尸进程的存在。 
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part_conc4_sigchld_async.png)
+
+顺便,异步事件意味着父进程无法提前知道事件的发生时间。
+
+修改你的服务器代码,设置一个 `SIGCHLD` 信号处理器,在信号处理器中等待终止的子进程。修改后的代码如下(webserver3e.py):
+
+```
+#######################################################
+# 并发服务器 - webserver3e.py                         #
+#                                                     #
+# 使用 Python 2.7.9 或 3.4                            #
+# 在 Ubuntu 14.04 及 Mac OS X 环境下测试通过          #
+#######################################################
+import os
+import signal
+import socket
+import time
+
+SERVER_ADDRESS = (HOST, PORT) = '', 8888
+REQUEST_QUEUE_SIZE = 5
+
+
+def grim_reaper(signum, frame):
+    pid, status = os.wait()
+    print(
+        'Child {pid} terminated with status {status}'
+        '\n'.format(pid=pid, status=status)
+    )
+
+
+def handle_request(client_connection):
+    request = client_connection.recv(1024)
+    print(request.decode())
+    http_response = b"""\
+HTTP/1.1 200 OK
+
+Hello, World!
+"""
+    client_connection.sendall(http_response)
+    # 挂起进程,来允许父进程完成循环,并在 "accept" 处阻塞
+    time.sleep(3)
+
+
+def serve_forever():
+    listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    listen_socket.bind(SERVER_ADDRESS)
+    listen_socket.listen(REQUEST_QUEUE_SIZE)
+    print('Serving HTTP on port {port} ...'.format(port=PORT))
+
+    signal.signal(signal.SIGCHLD, grim_reaper)
+
+    while True:
+        client_connection, client_address = listen_socket.accept()
+        pid = os.fork()
+        if pid == 0:  # 子进程
+            listen_socket.close()  # 关闭子进程中多余的套接字
+            handle_request(client_connection)
+            client_connection.close()
+            os._exit(0)
+        else:  # 父进程
+            client_connection.close()
+
+if __name__ == '__main__':
+    serve_forever()
+```
+
+运行服务器:
+
+```
+$ python webserver3e.py
+```
+
+使用你的老朋友——`curl` 命令来向修改后的并发服务器发送一个请求:
+
+```
+$ curl http://localhost:8888/hello
+```
+
+再来看看服务器:   
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc4_eintr.png)
+
+刚刚发生了什么?`accept` 调用失败了,错误信息为 `EINTR`
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc4_eintr_error.png)
+
+当子进程退出并触发 `SIGCHLD` 事件时,父进程的 `accept` 调用被阻塞了,系统转去运行信号处理器,当信号处理函数完成时,`accept` 系统调用被打断:
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc4_eintr_accept.png)
+
+别担心,这个问题很好解决。你只需要重新运行 `accept` 系统调用即可。这是修改后的服务器代码 `webserver3f.py`,它可以解决这个问题:
+
+
+```
+#######################################################
+# 并发服务器 - webserver3f.py                         #
+#                                                     #
+# 使用 Python 2.7.9 或 3.4                            #
+# 在 Ubuntu 14.04 及 Mac OS X 环境下测试通过          #
+#######################################################
+import errno
+import os
+import signal
+import socket
+
+SERVER_ADDRESS = (HOST, PORT) = '', 8888
+REQUEST_QUEUE_SIZE = 1024
+
+
+def grim_reaper(signum, frame):
+    pid, status = os.wait()
+
+
+def handle_request(client_connection):
+    request = client_connection.recv(1024)
+    print(request.decode())
+    http_response = b"""\
+HTTP/1.1 200 OK
+
+Hello, World!
+"""
+    client_connection.sendall(http_response)
+
+
+def serve_forever():
+    listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    listen_socket.bind(SERVER_ADDRESS)
+    listen_socket.listen(REQUEST_QUEUE_SIZE)
+    print('Serving HTTP on port {port} ...'.format(port=PORT))
+
+    signal.signal(signal.SIGCHLD, grim_reaper)
+
+    while True:
+        try:
+            client_connection, client_address = listen_socket.accept()
+        except IOError as e:
+            code, msg = e.args
+            # 若 'accept' 被打断,那么重启它
+            if code == errno.EINTR:
+                continue
+            else:
+                raise
+
+        pid = os.fork()
+        if pid == 0:  # 子进程
+            listen_socket.close()  # 关闭子进程中多余的描述符
+            handle_request(client_connection)
+            client_connection.close()
+            os._exit(0)
+        else:  # 父进程
+            client_connection.close()  # 关闭父进程中多余的描述符,继续下一轮循环
+
+
+if __name__ == '__main__':
+    serve_forever()
+```
+
+运行更新后的服务器 `webserver3f.py`:
+
+```
+$ python webserver3f.py
+```
+
+用 `curl` 来向更新后的并发服务器发送一个请求:
+
+```
+$ curl http://localhost:8888/hello
+```
+
+看到了吗?没有 EINTR 异常出现了。现在检查一下,确保没有僵尸进程存活,调用 `wait` 函数的 `SIGCHLD` 信号处理器能够正常处理被终止的子进程。我们只需使用 `ps` 命令,然后看看现在没有处于 Z+ 状态(或名字包含 `<defunct>` )的 Python 进程就好了。很棒!僵尸进程没有了,我们很安心。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_checkpoint.png)
+
+- 如果你创建了一个子进程,但是不等待它,它就会变成一个僵尸进程;
+- 使用 `SIGCHLD` 信号处理器可以异步地等待子进程终止,并收集其终止状态;
+- 当使用事件处理器时,你需要牢记,系统调用可能会被打断,所以你需要处理这种情况发生时带来的异常。
+
+好的,一切顺利。是不是没问题了?额,几乎是。重新尝试运行 `webserver3f.py` 但我们这次不会只发送一个请求,而是同步创建 128 个连接:
+
+```
+$ python client3.py --max-clients 128
+```
+
+现在再次运行 `ps` 命令:
+
+```
+$ ps auxw | grep -i python | grep -v grep
+```
+
+看到了吗?天啊,僵尸进程又出来了!
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc5_zombies_again.png)
+
+这回怎么回事?当你同时运行 128 个客户端,建立 128 个连接时,服务器的子进程几乎会在同一时间处理好你的请求,然后退出。这会导致非常多的 `SIGCHLD` 信号被发送到父进程。问题在于,这些信号不会存储在队列中,所以你的服务器进程会错过很多信号,这也就导致了几个僵尸进程处于无主状态:
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc5_signals_not_queued.png)
+
+这个问题的解决方案依然是设置 `SIGCHLD` 事件处理器。但我们这次将会用 `WNOHANG` 参数循环调用 `waitpid`,来保证所有处于终止状态的子进程都会被处理。下面是修改后的代码,`webserver3g.py`:
+
+```
+#######################################################
+# 并发服务器 - webserver3g.py                         #
+#                                                     #
+# 使用 Python 2.7.9 或 3.4                            #
+# 在 Ubuntu 14.04 及 Mac OS X 环境下测试通过          #
+#######################################################
+import errno
+import os
+import signal
+import socket
+
+SERVER_ADDRESS = (HOST, PORT) = '', 8888
+REQUEST_QUEUE_SIZE = 1024
+
+
+def grim_reaper(signum, frame):
+    while True:
+        try:
+            pid, status = os.waitpid(
+                -1,          # 等待所有子进程
+                 os.WNOHANG  # 无终止进程时,不阻塞进程,并抛出 EWOULDBLOCK 错误
+            )
+        except OSError:
+            return
+
+        if pid == 0:  # 没有僵尸进程存在了
+            return
+
+
+def handle_request(client_connection):
+    request = client_connection.recv(1024)
+    print(request.decode())
+    http_response = b"""\
+HTTP/1.1 200 OK
+
+Hello, World!
+"""
+    client_connection.sendall(http_response)
+
+
+def serve_forever():
+    listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    listen_socket.bind(SERVER_ADDRESS)
+    listen_socket.listen(REQUEST_QUEUE_SIZE)
+    print('Serving HTTP on port {port} ...'.format(port=PORT))
+
+    signal.signal(signal.SIGCHLD, grim_reaper)
+
+    while True:
+        try:
+            client_connection, client_address = listen_socket.accept()
+        except IOError as e:
+            code, msg = e.args
+            # 若 'accept' 被打断,那么重启它
+            if code == errno.EINTR:
+                continue
+            else:
+                raise
+
+        pid = os.fork()
+        if pid == 0:  # 子进程
+            listen_socket.close()  # 关闭子进程中多余的描述符
+            handle_request(client_connection)
+            client_connection.close()
+            os._exit(0)
+        else:  # 父进程
+            client_connection.close()  # 关闭父进程中多余的描述符,继续下一轮循环
+
+if __name__ == '__main__':
+    serve_forever()
+```
+
+运行服务器:
+
+```
+$ python webserver3g.py
+```
+
+使用测试客户端 `client3.py`:
+
+```
+$ python client3.py --max-clients 128
+```
+
+现在来查看一下,确保没有僵尸进程存在。耶!没有僵尸的生活真美好 ^_^
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_conc5_no_zombies.png)
+
+恭喜!你刚刚经历了一段很长的旅程,我希望你能够喜欢它。现在你拥有了自己的建议并发服务器,并且这段代码能够为你在继续研究生产级 Web 服务器的路上奠定基础。
+
+我将会留一个作业:你需要将第二部分中的 WSGI 服务器升级,将它改造为一个并发服务器。你可以在[这里][12]找到更改后的代码。但是,当你实现了自己的版本之后,你才应该来看我的代码。你已经拥有了实现这个服务器所需的所有信息。所以,快去实现它吧 ^_^
+
+然后要做什么呢?乔希·比林斯说过:
+
+> “我们应该做一枚邮票——专注于一件事,不达目的不罢休。”
+
+开始学习基本知识。回顾你已经学过的知识。然后一步一步深入。
+
+![](https://ruslanspivak.com/lsbaws-part3/lsbaws_part3_dig_deeper.png)
+
+> “如果你只学会了方法,你将会被这些方法所困。但如果你学会了原理,那你就能发明出新的方法。”——拉尔夫·沃尔多·爱默生
+
+下面是一份书单,我从这些书中提炼出了这篇文章所需的素材。他们能助你在我刚刚所述的几个方面中发掘出兼具深度和广度的知识。我极力推荐你们去搞到这几本书看看:从你的朋友那里借,在当地的图书馆中阅读,或者直接在亚马逊上把它买回来。下面是我的典藏秘籍:
+
+1. [UNIX网络编程 (卷1):套接字联网API (第3版)][6]
+2. [UNIX环境高级编程 (第3版)][7]
+3. [Linux/UNIX系统编程手册][8]
+4. [TCP/IP详解 (卷1):协议 (第2版) (爱迪生-韦斯莱专业编程系列)][9]
+5. [信号系统简明手册 (第二版): 并发控制深入浅出及常见错误][10]. 这本书也可以从[作者的个人网站][11]中买到。
+
+顺便,我在撰写一本名为《搭个 Web 服务器:从头开始》的书。这本书讲解了如何从头开始编写一个基本的 Web 服务器,里面包含本文中没有的更多细节。订阅邮件列表,你就可以获取到这本书的最新进展,以及发布日期。
+
+--------------------------------------------------------------------------------
+
+via: https://ruslanspivak.com/lsbaws-part3/
+
+作者:[Ruslan][a]
+译者:[StdioA](https://github.com/StdioA)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://github.com/rspivak/
+
+[1]: https://github.com/rspivak/lsbaws/blob/master/part3/
+[2]: https://github.com/rspivak/lsbaws/blob/master/part3/webserver3a.py
+[3]: https://github.com/rspivak/lsbaws/blob/master/part3/webserver3b.py
+[4]: https://ruslanspivak.com/lsbaws-part3/#fn:1
+[5]: https://ruslanspivak.com/lsbaws-part3/#fn:2
+[6]: http://www.amazon.com/gp/product/0131411551/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0131411551&linkCode=as2&tag=russblo0b-20&linkId=2F4NYRBND566JJQL
+[7]: http://www.amazon.com/gp/product/0321637739/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321637739&linkCode=as2&tag=russblo0b-20&linkId=3ZYAKB537G6TM22J
+[8]: http://www.amazon.com/gp/product/1593272200/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1593272200&linkCode=as2&tag=russblo0b-20&linkId=CHFOMNYXN35I2MON
+[9]: http://www.amazon.com/gp/product/0321336313/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321336313&linkCode=as2&tag=russblo0b-20&linkId=K467DRFYMXJ5RWAY
+[10]: http://www.amazon.com/gp/product/1441418687/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1441418687&linkCode=as2&tag=russblo0b-20&linkId=QFOAWARN62OWTWUG
+[11]: http://greenteapress.com/semaphores/
+[12]: https://github.com/rspivak/lsbaws/blob/master/part3/webserver3h.py
diff --git a/translated/tech/20160812 Writing a JavaScript framework - Execution timing.md b/translated/tech/20160812 Writing a JavaScript framework - Execution timing.md
new file mode 100644
index 0000000000..883a99d027
--- /dev/null
+++ b/translated/tech/20160812 Writing a JavaScript framework - Execution timing.md	
@@ -0,0 +1,190 @@
+写一个比setTimeout更棒的javascript框架
+===================
+
+这是JavaScript框架系列的第二章. 在这一章里, 我打算讲一下浏览器的异步代码不同执行方式. 你将了解定时器和事件循环直接的不同差异, 比如 setTimeout 和 Promises.
+
+这个系列是一个开源的客户端框架, 叫做 NX. 在这个系列里, 我主要解释一下写该框架不得不客服的主要困难. 如果你对NX感兴趣可以参观我们的 [主页][1].
+
+这个系列包含以下几个章节:
+
+1. [项目结构][2]
+2. 定时执行 (当前章节)
+3. [沙箱代码评估][3]
+4. 数据绑定 (part 1)
+5. 数据绑定 (part 2)
+6. 自定义元素
+7. 客户端侧路由
+
+### 异步代码执行
+
+你可能比较熟悉 Promise, process.nextTick(), setTimeout() and maybe requestAnimationFrame() 这些作为异步执行的代码. 它们都是内部事件循环, 但是他们确实有一些不同.
+
+在这一章里, 我将解释它们之间的不同, 然后给大家演示一个先进的定时系统框架, 像NX 这样的框架. 不用我们重新做一个,我们将使用原生的内部循环来达到我们的目的.
+
+### 事件循环
+
+事件循环甚至没有在ES6里提到. JavaScript只有jobs 和 job queues. 更加复杂的事件循环是在NodeJS 和HTML5里分别指定的. 我会在后面详细说明.
+
+事件循环叫做一个理由的循环. 它不停的寻找新的任务来运行. 这个循环中的一次事件叫做tick. 运行tick期间的代码叫做task.
+
+```
+while (eventLoop.waitForTask()) {  
+  eventLoop.processNextTask()
+}
+```
+
+Tasks 是同步代码他可以在其它的循环中调用. 一个简单的调用新任务的方式是setTimeout(taskFn). 不管怎样, tasks可能有很多比如 用户事件, networking 或者 DOM 操作.
+
+![](https://risingstack-blog.s3.amazonaws.com/2016/Aug/Execution_timing_event_lopp_with_tasks-1470127590983.svg)
+
+### 任务队列
+
+简单来说, 事件循环可以有多个任务队列. 这里有两个约束条件,相同数据源的事件必须在相同的队列以及任务必须做插入顺序处理. 除此之外, 浏览器可以做任何他想做的事情. 例如, 它可以决定接下来处理哪个任务队列.
+
+```
+while (eventLoop.waitForTask()) {  
+  const taskQueue = eventLoop.selectTaskQueue()
+  if (taskQueue.hasNextTask()) {
+    taskQueue.processNextTask()
+  }
+}
+```
+
+用这个模型, 我们不能精确的控制定时. 如果用setTimeout()浏览器可能决定先运行完其它几个队列才运行我们的队列.
+
+![](https://risingstack-blog.s3.amazonaws.com/2016/Aug/Execution_timing_event_loop_with_task_queues-1470127624172.svg)
+
+### The microtask queue
+
+幸运的是, 事件循环还提供了单个队列叫做microtask队列. 当前任务结束的时候microtask队列会清空每个tick里的任务.
+
+
+```
+while (eventLoop.waitForTask()) {  
+  const taskQueue = eventLoop.selectTaskQueue()
+  if (taskQueue.hasNextTask()) {
+    taskQueue.processNextTask()
+  }
+
+  const microtaskQueue = eventLoop.microTaskQueue
+  while (microtaskQueue.hasNextMicrotask()) {
+    microtaskQueue.processNextMicrotask()
+  }
+}
+```
+
+最简单的调用microtask的方法是Promise.resolve().then(microtaskFn). Microtasks做插入顺序的处理, 并且由于仅存在一个microtask队列, 现在浏览器对于我们来说并不是杂乱无章的了.
+
+此外, microtasks 可以安排新的 microtasks插入到相同的队列.
+
+![](https://risingstack-blog.s3.amazonaws.com/2016/Aug/Execution_timing_event_loop_with_microtask_queue-1470127679393.svg)
+
+### 绘制
+
+最后是绘制调度. 不同于事件处理和分解, 绘制并不是在后台任务完成的. 它是在每个循环tick结束时运行的算法.
+
+在这里浏览器又有了许多自由: 他可能在每个任务以后绘制, 但是他也肯能在好几百个任务都执行了以后也不绘制.
+
+幸运的是, 我们有 requestAnimationFrame(), 它表示在下一个绘制之前执行. 我们最终的模型像这样.
+
+```
+while (eventLoop.waitForTask()) {  
+  const taskQueue = eventLoop.selectTaskQueue()
+  if (taskQueue.hasNextTask()) {
+    taskQueue.processNextTask()
+  }
+
+  const microtaskQueue = eventLoop.microTaskQueue
+  while (microtaskQueue.hasNextMicrotask()) {
+    microtaskQueue.processNextMicrotask()
+  }
+
+  if (shouldRender()) {
+    applyScrollResizeAndCSS()
+    runAnimationFrames()
+    render()
+  }
+}
+```
+
+现在用我们所知道知识来创建定时系统!
+
+### 利用事件循环
+
+和大多数现代框架一样, NX 也是基于 DOM 操作和 后台数据绑定的.在批操作和异步执行方面有更好的性能表现. 基于以上理由我们用 Promises, MutationObservers and requestAnimationFrame().
+
+我们所期望的定时器是这样的:
+
+1. 代码来自于开发者
+2. 数据绑定和DOM操作由NX来执行
+3. developer定义HOOKS
+4. 浏览器进行绘制
+
+#### 步骤 1
+
+NX 寄存器对象基于ES6转变 以及 DOM转换基于MutationObserver(变动观测器)同步运行(下一节详细介绍). 它作为一个microtasks延迟直到步骤 2 执行以后才有反应. 这个延迟已经在 Promise.resolve().then(reaction) 进行了对象转换, 并且它将通过变动观测器自动运行.
+
+#### 步骤 2
+
+开发人员完成了代码(任务). NX的microtask反应注册器开始运行. 当它们进入队列运行. 注意我们仍然在同一个tick循环中.
+
+#### 步骤 3
+
+开发者通过requestAnimationFrame(hook)通知NX运行hooks. 这可能在tick循环后发生. 重要的是hooks运行在下一次绘制之前和所以数据操作之后, 并且DOM和CSS改变都已经完成 .
+
+#### 步骤 4
+
+浏览器绘制下一个视图. 这也有可能发生在tick循环之后, 但是绝对不会发生在步骤3的tick之前.
+
+### 牢记在心里的事情
+
+我们实现了一简单而有效的定时系统. 理论上讲它运行的很好, 但是还是很脆弱, 一个轻微的错误可能会导致很严重的BUG.
+
+在一个复杂的系统当中, 最重要的就是建立一定的规则并在以后保持它们. 在NX中有以下规则.
+
+1. 永远不用setTimeout(fn, 0)来进行内部操作
+2. 用相同的方法来注册microtasks
+3. microtasks仅供内部操作
+4. 不要干预开发者hook运行时间
+
+#### 规则 1 和 2
+
+序列化数据和DOM操作. 这样只要不混合就可以很好的延迟它们. 混合执行会出现莫名其妙的问题. 
+
+setTimeout(fn, 0) 完全不可预测. 使用不同的方法注册microtasks也会发生混乱. 例如 下面的例子中microtask2不会在 microtask1之前正确运行.
+
+```
+Promise.resolve().then().then(microtask1)  
+Promise.resolve().then(microtask2) 
+```
+
+![](https://risingstack-blog.s3.amazonaws.com/2016/Aug/Execution_timing_microtask_registration_method-1470127727609.svg)
+
+#### 规则 3 和 4
+
+分离开发者的代码执行和内部操作是非常重要的. 混合这两种行为会导致不可预测的事情发生, 并且它会迫使开发者了解框架内部. 我想很多前台开发者已经有过类似经历.
+
+### 结论
+
+如果你对 NX 框架感兴趣, 可以参观我们的主页. 还可以再GIT上找到我们的源代码 [NX source code][5] .
+
+在下一节我们再见 [sandboxed code evaluation][4]!
+
+你也可以给我们留言.
+
+--------------------------------------------------------------------------------
+
+via: https://blog.risingstack.com/writing-a-javascript-framework-execution-timing-beyond-settimeout/?utm_source=javascriptweekly&utm_medium=email
+
+作者:[Bertalan Miklos][a]
+译者:[kokialoves](https://github.com/kokialoves)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://blog.risingstack.com/author/bertalan/
+[1]: http://nx-framework.com/
+[2]: https://blog.risingstack.com/writing-a-javascript-framework-project-structuring/
+[3]: https://blog.risingstack.com/writing-a-javascript-framework-sandboxed-code-evaluation/
+[4]: https://blog.risingstack.com/writing-a-javascript-framework-sandboxed-code-evaluation/
+[5]: https://github.com/RisingStack/nx-framework
diff --git a/translated/tech/20160817 Dependency Injection for the Android platform 101 - Part 1.md b/translated/tech/20160817 Dependency Injection for the Android platform 101 - Part 1.md
new file mode 100644
index 0000000000..6ae6ddccfb
--- /dev/null
+++ b/translated/tech/20160817 Dependency Injection for the Android platform 101 - Part 1.md	
@@ -0,0 +1,188 @@
+安卓平台上的依赖注入 - 第一部分
+===========================
+
+![](https://d262ilb51hltx0.cloudfront.net/max/2000/1*YWlAzAY20KLLGIyyD_mzZw.png)
+
+刚开始学习软件工程的时候,我们经常会碰到像这样的事情:
+
+>软件应该符合 SOLID 原则。
+
+但这句话实际是什么意思?让我们看看 SOLID 中每个字母在架构里所代表的重要含义,例如:
+
+- [S  单职责原则][1]
+- [O  开闭原则][2]
+- [L  Liskov 替换原则][3]
+- [I  接口分离原则][4]
+- [D  依赖反转原则][5] 这也是依赖注入的核心概念。
+
+简单来说,我们需要提供一个类,这个类有它所需要的所有对象,以便实现其功能。
+
+### 概述
+
+依赖注入听起来像是描述非常复杂的东西的一个术语,但实际上它很简单,看下面这个例子你就明白了:
+
+```
+class NoDependencyInjection {
+  private Dependency d;
+  
+  public NoDependencyInjection() {
+    d = new Dependency();
+  }
+}
+
+class DependencyInjection {
+  private Dependency d;
+  
+  public DependencyInjection(Dependency d) {
+    this.d = d;
+  }
+}
+```
+
+正如我们所见,第一种情况是我们在构造器里创建了依赖对象,但在第二种情况下,它作为参数被传递给构造器,这就是我们所说的依赖注入。这样做是为了让我们所写的类不依靠特定依赖关系的实现,却能直接使用它。
+
+参数传递的目标是构造器,我们就称之为构造器依赖注入;或者是某个方法,就称之为方法依赖注入:
+
+```
+class Example {
+  private ConstructorDependency cd;
+  private MethodDependency md;
+  Example(ConstructorDependency cd) {
+    this.cd = cd; //Constructor Dependency Injection
+  }
+  
+  public setMethodDependency(MethodDependency md) {
+    this.md = md; //Method Dependency Injection
+  }
+}
+```
+
+
+要是你想总体深入地了解依赖注入,可以看看由 [Dan Lew][t2] 发表的[精彩的演讲][t1],事实上是这个演讲启迪了这个概述。
+
+在 Android 平台,当需要框架来处理依赖注入这个特殊的问题时,我们有不同的选择,其中最有名的框架就是 [Dagger 2][t3]。它最开始是由 Square 公司(译者注:Square 是美国一家移动支付公司)里一些很棒的开发者开发出来的,然后慢慢发展成由 Google 自己开发。特别地,Dagger 1 先被开发出来,然后 Big G 接手这个项目,做了很多改动,比如以注释为基础,在编译的时候就完成 Dagger 的任务,也就是第二个版本。
+
+### 导入框架
+
+安装 Dagger 并不难,但需要导入 `android-apt` 插件,通过向项目的根目录下的 build.gradle 文件中添加它的依赖关系:
+
+```
+buildscript{
+  ...
+  dependencies{
+    ...
+    classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.8’
+  }
+}
+```
+
+然后,我们需要将 `android-apt` 插件应用到项目 build.gradle 文件,放在文件顶部 Android 应用那一句的下一行:
+
+```
+apply plugin: ‘com.neenbedankt.android-apt’
+```
+
+这个时候,我们只用添加依赖关系,然后就能使用库和注释了:
+
+```
+dependencies{
+    ...
+    compile ‘com.google.dagger:dagger:2.6’ 
+    apt ‘com.google.dagger:dagger-compiler:2.6’
+    provided ‘javax.annotation:jsr250-api:1.0’
+}
+```
+
+>需要加上最后一个依赖关系是因为 @Generated 注解在 Android 里还不可用,但它是[原生的 Java 注解][t4]。
+
+### Dagger 模块
+
+要注入依赖,首先需要告诉框架我们能提供什么(比如说上下文)以及特定的对象应该怎样创建。为了完成注入,我们用 `@Module` 注释对一个特殊的类进行了注解(这样 Dagger 就能识别它了),寻找 `@Provide` 标记的方法,生成图表,能够返回我们所请求的对象。
+
+看下面的例子,这里我们创建了一个模块,它会返回给我们 `ConnectivityManager`,所以我们要把 `Context` 对象传给这个模块的构造器。
+
+```
+@Module
+public class ApplicationModule {
+  private final Context context;
+
+  public ApplicationModule(Context context) {
+    this.context = context;
+  }
+
+  @Provides @Singleton
+  public Context providesContext() {
+    return context;
+  }
+
+  @Provides @Singleton
+  public ConnectivityManager providesConnectivityManager(Context context) {
+    return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+  }
+}
+```
+
+>Dagger 中十分有意思的一点是只用在一个方法前面添加一个 Singleton 注解,就能处理所有从 Java 中继承过来的问题。
+
+### 容器
+
+当我们有一个模块的时候,我们需要告诉 Dagger 想把依赖注入到哪里:我们在一个容器里,一个特殊的注解过的接口里完成依赖注入。我们在这个接口里创造不同的方法,而接口的参数是我们想注入依赖关系的类。
+
+下面给出一个例子并告诉 Dagger 我们想要 `MainActivity` 类能够接受 `ConnectivityManager`(或者在图表里的其它依赖对象)。我们只要做类似以下的事:
+
+```
+@Singleton
+@Component(modules = {ApplicationModule.class})
+public interface ApplicationComponent {
+
+  void inject(MainActivity activity);
+}
+```
+
+>正如我们所见,@Component 注解有几个参数,一个是所支持的模块的数组,意味着它能提供的依赖。这里既可以是 Context 也可以是 ConnectivityManager,因为他们在 ApplicationModule 类中有声明。
+
+### 使用
+
+这时,我们要做的是尽快创建容器(比如在应用的 onCreate 方法里面)并且返回这个容器,那么类就能用它来注入依赖了:
+
+>为了让框架自动生成 DaggerApplicationComponent,我们需要构建项目以便 Dagger 能够扫描我们的代码库,并且生成我们需要的部分。
+
+在 `MainActivity` 里,我们要做的两件事是用 `@Inject` 注解符对想要注入的属性进行注释,调用我们在 `ApplicationComponent` 接口中声明的方法(请注意后面一部分会因我们使用的注入类型的不同而变化,但这里简单起见我们不去管它),然后依赖就被注入了,我们就能自由使用他们:
+
+```
+public class MainActivity extends AppCompatActivity {
+  @Inject
+  ConnectivityManager manager;
+
+  @Override
+  protected void onCreate(Bundle savedInstanceState) {
+    ...
+    ((App) getApplication()).getComponent().inject(this);
+  }
+}
+```
+
+### 总结
+
+当然了,我们可以手动注入依赖,管理所有不同的对象,但 Dagger 打消了很多有关模板的“噪声”,Dagger 给我们有用的附加品(比如 `Singleton`),而仅用 Java 处理将会很糟糕。
+
+--------------------------------------------------------------------------------
+
+via: https://medium.com/di-101/di-101-part-1-81896c2858a0#.3hg0jj14o
+
+作者:[Roberto Orgiu][a]
+译者:[GitFuture](https://github.com/GitFuture)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://medium.com/@_tiwiz
+[1]: https://en.wikipedia.org/wiki/Single_responsibility_principle
+[2]: https://en.wikipedia.org/wiki/Open/closed_principle
+[3]: http://liskov_substitution_principle/
+[4]: https://en.wikipedia.org/wiki/Interface_segregation_principle
+[5]: https://en.wikipedia.org/wiki/Dependency_inversion_principle
+[t1]: https://realm.io/news/daniel-lew-dependency-injection-dagger/
+[t2]: https://twitter.com/danlew42
+[t3]: http://google.github.io/dagger/
+[t4]: https://docs.oracle.com/javase/7/docs/api/javax/annotation/Generated.html
diff --git a/translated/tech/20160820 Protocol Buffer Basics C++.md b/translated/tech/20160820 Protocol Buffer Basics C++.md
new file mode 100644
index 0000000000..ed86014010
--- /dev/null
+++ b/translated/tech/20160820 Protocol Buffer Basics C++.md	
@@ -0,0 +1,411 @@
+Protocol Buffer Basics: C++
+============================
+
+这篇教程提供了一个面向 C++ 程序员、关于 `protocol buffers` 的基础介绍。通过创建一个简单的示例应用程序,它将向我们展示:
+
+* 在 `.proto` 文件中定义消息格式
+* 使用 `protocol buffer`  编译器
+* 使用 `C++ protocol buffer API` 读写消息
+
+这不是一个关于使用 C++ protocol buffers 的全面指南。要获取更详细的信息,请参考 [Protocol Buffer Language Guide][1] 和 [Encoding Reference][2]。
+
+### 为什么使用 Protocol Buffers
+
+我们接下来要使用的例子是一个非常简单的"地址簿"应用程序,它能从文件中读取联系人详细信息。地址簿中的每一个人都有一个名字,ID,邮件地址和联系电话。
+
+如何序列化和获取结构化的数据?这里有几种解决方案:
+
+* 以二进制形式发送/接收原生的内存数据结构。通常,这是一种脆弱的方法,因为接收/读取代码的编译必须基于完全相同的内存布局、大小端等等。同时,当文件增加时,原始格式数据会随着与该格式相连的软件拷贝而迅速扩散,这将很难扩展文件格式。
+
+* 你可以创造一种 `ad-hoc` 方法,将数据项编码为一个字符串——比如将 4 个整数编码为 "12:3:-23:67"。虽然它需要编写一次性的编码和解码代码且解码需要耗费小的运行时成本,但这是一种简单灵活的方法。这最适合编码非常简单的数据。
+
+* 序列化数据为 `XML`。这种方法是非常吸引人的,因为 `XML` 是一种适合人阅读的格式,并且有为许多语言开发的库。如果你想与其他程序和项目共享数据,这可能是一种不错的选择。然而,众所周知,`XML` 是空间密集型的,且在编码和解码时,它对程序会造成巨大的性能损失。同时,使用 XML DOM 树被认为比操作一个类的简单字段更加复杂。
+
+`Protocol buffers` 是针对这个问题的一种灵活、高效、自动化的解决方案。使用 `Protocol buffers`,你需要写一个 `.proto` 说明,用于描述你所希望存储的数据结构。利用 `.proto` 文件,protocol buffer 编译器可以创建一个类,用于实现自动化编码和解码高效的二进制格式的 protocol buffer 数据。产生的类提供了构造 `protocol buffer` 的字段的 getters 和 setters,并且作为一个单元,关注读写 `protocol buffer` 的细节。重要的是,`protocol buffer` 格式支持扩展格式,代码仍然可以读取以旧格式编码的数据。
+
+### 在哪可以找到示例代码
+
+示例代码被包含于源代码包,位于 "examples" 文件夹。在[这][4]下载代码。
+
+### 定义你的协议格式
+
+为了创建自己的地址簿应用程序,你需要从 `.proto` 开始。`.proto` 文件中的定义很简单:为你所需要序列化的数据结构添加一个消息(message),然后为消息中的每一个字段指定一个名字和类型。这里是定义你消息的 `.proto` 文件,`addressbook.proto`。
+
+```
+package tutorial;
+
+message Person {
+  required string name = 1;
+  required int32 id = 2;
+  optional string email = 3;
+
+  enum PhoneType {
+    MOBILE = 0;
+    HOME = 1;
+    WORK = 2;
+  }
+
+  message PhoneNumber {
+    required string number = 1;
+    optional PhoneType type = 2 [default = HOME];
+  }
+
+  repeated PhoneNumber phone = 4;
+}
+
+message AddressBook {
+  repeated Person person = 1;
+}
+```
+
+如你所见,其语法类似于 C++ 或 Java。我们开始看看文件的每一部分内容做了什么。
+
+`.proto` 文件以一个 package 声明开始,这可以避免不同项目的命名冲突。在 C++,你生成的类会被置于与 package 名字一样的命名空间。
+
+下一步,你需要定义消息(message)。消息只是一个包含一系列类型字段的集合。大多标准简单数据类型是可以作为字段类型的,包括 `bool`、`int32`、`float`、`double` 和 `string`。你也可以通过使用其他消息类型作为字段类型,将更多的数据结构添加到你的消息中——在以上的示例,`Person` 消息包含了 `PhoneNumber` 消息,同时 `AddressBook` 消息包含 `Person` 消息。你甚至可以定义嵌套在其他消息内的消息类型——如你所见,`PhoneNumber` 类型定义于 `Person` 内部。如果你想要其中某一个字段拥有预定义值列表中的某个值,你也可以定义 `enum` 类型——这儿你想指定一个电话号码可以是 `MOBILE`、`HOME` 或 `WORK` 中的某一个。
+
+每一个元素上的 “=1”、"=2" 标记确定了用于二进制编码的唯一"标签"(tag)。标签数字 1-15 的编码比更大的数字少需要一个字节,因此作为一种优化,你可以将这些标签用于经常使用或 repeated 元素,剩下 16 以及更高的标签用于非经常使用或 optional 元素。每一个 repeated 字段的元素需要重新编码标签数字,因此 repeated 字段对于这优化是一个特别好的候选者。
+
+每一个字段必须使用下面的修饰符加以标注:
+
+* required:必须提供字段的值,否则消息会被认为是 "未初始化的"(uninitialized)。如果 `libprotobuf` 以 debug 模式编译,序列化未初始化的消息将引起一个断言失败。以优化形式构建,将会跳过检查,并且无论如何都会写入消息。然而,解析未初始化的消息总是会失败(通过 parse 方法返回 `false`)。除此之外,一个 required 字段的表现与 optional 字段完全一样。
+
+* optional:字段可能会被设置,也可能不会。如果一个 optional 字段没被设置,它将使用默认值。对于简单类型,你可以指定你自己的默认值,正如例子中我们对电话号码的 `type` 一样,否则使用系统默认值:数字类型为 0、字符串为空字符串、布尔值为 false。对于嵌套消息,默认值总为消息的"默认实例"或"原型",它的所有字段都没被设置。调用 accessor 来获取一个没有显式设置的 optional(或 required) 字段的值总是返回字段的默认值。
+
+* repeated:字段可以重复任意次数(包括 0)。repeated 值的顺序会被保存于 protocol buffer。可以将 repeated 字段想象为动态大小的数组。
+
+你可以查找关于编写 `.proto` 文件的完整指导——包括所有可能的字段类型——在 [Protocol Buffer Language Guide][6]。不要在这里面查找与类继承相似的特性,因为 protocol buffers 不会做这些。
+
+> required 是永久性的,在把一个字段标识为 required 的时候,你应该特别小心。如果在某些情况下你不想写入或者发送一个 required 的字段,那么将该字段更改为 optional 可能会遇到问题——旧版本的读者(译者注:即读取、解析旧版本 Protocol Buffer 消息的一方)会认为不含该字段的消息是不完整的,从而有可能会拒绝解析。在这种情况下,你应该考虑编写特别针对于应用程序的、自定义的消息校验函数。Google 的一些工程师得出了一个结论:使用 required 弊多于利;他们更愿意使用 optional 和 repeated 而不是 required。当然,这个观点并不具有普遍性。
+
+### 编译你的 Protocol Buffers
+
+既然你有了一个 `.proto`,那你需要做的下一件事就是生成一个将用于读写 `AddressBook` 消息的类(从而包括 `Person` 和 `PhoneNumber`)。为了做到这样,你需要在你的 `.proto` 上运行 protocol buffer 编译器 `protoc`:
+
+1. 如果你没有安装编译器,请[下载这个包][4],并按照 README 中的指令进行安装。
+2. 现在运行编译器,知道源目录(你的应用程序源代码位于哪里——如果你没有提供任何值,将使用当前目录),目标目录(你想要生成的代码放在哪里;常与 `$SRC_DIR` 相同),并且你的 `.proto` 路径。在此示例,你...:
+
+```
+protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto
+```
+
+因为你想要 C++ 的类,所以你使用了 `--cpp_out` 选项——也为其他支持的语言提供了类似选项。
+
+在你指定的目标文件夹,将生成以下的文件:
+
+* `addressbook.pb.h`,声明你生成类的头文件。
+* `addressbook.pb.cc`,包含你的类的实现。
+
+### Protocol Buffer API
+
+让我们看看生成的一些代码,了解一下编译器为你创建了什么类和函数。如果你查看 `tutorial.pb.h`,你可以看到有一个在 `tutorial.proto` 中指定所有消息的类。关注 `Person` 类,可以看到编译器为每个字段生成了读写函数(accessors)。例如,对于 `name`、`id`、`email` 和 `phone` 字段,有下面这些方法:
+
+```c++
+// name
+inline bool has_name() const;
+inline void clear_name();
+inline const ::std::string& name() const;
+inline void set_name(const ::std::string& value);
+inline void set_name(const char* value);
+inline ::std::string* mutable_name();
+
+// id
+inline bool has_id() const;
+inline void clear_id();
+inline int32_t id() const;
+inline void set_id(int32_t value);
+
+// email
+inline bool has_email() const;
+inline void clear_email();
+inline const ::std::string& email() const;
+inline void set_email(const ::std::string& value);
+inline void set_email(const char* value);
+inline ::std::string* mutable_email();
+
+// phone
+inline int phone_size() const;
+inline void clear_phone();
+inline const ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >& phone() const;
+inline ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >* mutable_phone();
+inline const ::tutorial::Person_PhoneNumber& phone(int index) const;
+inline ::tutorial::Person_PhoneNumber* mutable_phone(int index);
+inline ::tutorial::Person_PhoneNumber* add_phone();
+```
+
+正如你所见到,getters 的名字与字段的小写名字完全一样,并且 setter 方法以 set_ 开头。同时每个单一(singular)(required 或 optional)字段都有 `has_` 方法,该方法在字段被设置了值的情况下返回 true。最后,所有字段都有一个 `clear_` 方法,用以清除字段到空(empty)状态。
+
+数字 `id` 字段仅有上述的基本读写函数集合(accessors),而 `name` 和 `email` 字段有两个额外的方法,因为它们是字符串——一个是可以获得字符串直接指针的`mutable_` getter ,另一个为额外的 setter。注意,尽管 `email` 还没被设置(set),你也可以调用 `mutable_email`;因为 `email` 会被自动地初始化为空字符串。在本例中,如果你有一个单一的(required 或 optional)消息字段,它会有一个 `mutable_` 方法,而没有 `set_` 方法。
+
+repeated 字段也有一些特殊的方法——如果你看看 repeated `phone` 字段的方法,你可以看到:
+
+* 检查 repeated 字段的 `_size`(也就是说,与 `Person` 相关的电话号码的个数)
+* 使用下标取得特定的电话号码
+* 更新特定下标的电话号码
+* 添加新的电话号码到消息中,之后你便可以编辑。(repeated 标量类型有一个 `add_` 方法,用于传入新的值)
+
+为了获取 protocol 编译器为所有字段定义生成的方法的信息,可以查看 [C++ generated code reference][5]。
+
+#### 枚举和嵌套类(Enums and Nested Classes)
+
+与 `.proto` 的枚举相对应,生成的代码包含了一个 `PhoneType` 枚举。你可以通过 `Person::PhoneType` 引用这个类型,通过 `Person::MOBILE`、`Person::HOME` 和 `Person::WORK` 引用它的值。(实现细节有点复杂,但是你无须了解它们而可以直接使用)
+
+编译器也生成了一个 `Person::PhoneNumber` 的嵌套类。如果你查看代码,你可以发现真正的类型为 `Person_PhoneNumber`,但它通过在 `Person` 内部使用 typedef 定义,使你可以把 `Person_PhoneNumber` 当成嵌套类。唯一产生影响的一个例子是,如果你想要在其他文件前置声明该类——在 C++ 中你不能前置声明嵌套类,但是你可以前置声明 `Person_PhoneNumber`。
+
+#### 标准的消息方法
+
+所有的消息方法都包含了许多别的方法,用于检查和操作整个消息,包括:
+
+* `bool IsInitialized() const;` :检查是否所有 `required` 字段已经被设置。
+* `string DebugString() const;`:返回人类可读的消息表示,对 debug 特别有用。
+* `void CopyFrom(const Person& from);`:使用给定的值重写消息。
+* `void Clear();`:清除所有元素为空(empty)的状态。
+
+上面这些方法以及下一节要讲的 I/O 方法实现了被所有 C++ protocol buffer 类共享的消息(Message)接口。为了获取更多信息,请查看 [complete API documentation for Message][7]。
+
+#### 解析和序列化(Parsing and Serialization)
+
+最后,所有 protocol buffer 类都有读写你选定类型消息的方法,这些方法使用了特定的 protocol buffer [二进制格式][8]。这些方法包括:
+
+* `bool SerializeToString(string* output) const;`:序列化消息以及将消息字节数据存储在给定的字符串。注意,字节数据是二进制格式的,而不是文本格式;我们只使用 `string` 类作为合适的容器。
+* `bool ParseFromString(const string& data);`:从给定的字符创解析消息。
+* `bool SerializeToOstream(ostream* output) const;`:将消息写到给定的 C++ `ostream`。
+* `bool ParseFromIstream(istream* input);`:从给定的 C++ `istream` 解析消息。
+
+这些只是两个用于解析和序列化的选择。再次说明,可以查看 `Message API reference` 完整的列表。
+
+> Protocol Buffers 和 面向对象设计的 Protocol buffer 类通常只是纯粹的数据存储器(像 C++ 中的结构体);它们在对象模型中并不是一等公民。如果你想向生成的 protocol buffer 类中添加更丰富的行为,最好的方法就是在应用程序中对它进行封装。如果你无权控制 .proto 文件的设计的话,封装 protocol buffers 也是一个好主意(例如,你从另一个项目中重用一个 .proto 文件)。在那种情况下,你可以用封装类来设计接口,以更好地适应你的应用程序的特定环境:隐藏一些数据和方法,暴露一些便于使用的函数,等等。但是你绝对不要通过继承生成的类来添加行为。这样做的话,会破坏其内部机制,并且不是一个好的面向对象的实践。
+
+### 写消息(Writing A Message)
+
+现在我们尝试使用 protocol buffer 类。你的地址簿程序想要做的第一件事是将个人详细信息写入到地址簿文件。为了做到这一点,你需要创建、填充 protocol buffer 类实例,并且将它们写入到一个输出流(output stream)。
+
+这里的程序可以从文件读取 `AddressBook`,根据用户输入,将新 `Person` 添加到 `AddressBook`,并且再次将新的 `AddressBook` 写回文件。这部分直接调用或引用 protocol buffer 类的代码会高亮显示。
+
+```c++
+#include <iostream>
+#include <fstream>
+#include <string>
+#include "addressbook.pb.h"
+using namespace std;
+
+// This function fills in a Person message based on user input.
+void PromptForAddress(tutorial::Person* person) {
+  cout << "Enter person ID number: ";
+  int id;
+  cin >> id;
+  person->set_id(id);
+  cin.ignore(256, '\n');
+
+  cout << "Enter name: ";
+  getline(cin, *person->mutable_name());
+
+  cout << "Enter email address (blank for none): ";
+  string email;
+  getline(cin, email);
+  if (!email.empty()) {
+    person->set_email(email);
+  }
+
+  while (true) {
+    cout << "Enter a phone number (or leave blank to finish): ";
+    string number;
+    getline(cin, number);
+    if (number.empty()) {
+      break;
+    }
+
+    tutorial::Person::PhoneNumber* phone_number = person->add_phone();
+    phone_number->set_number(number);
+
+    cout << "Is this a mobile, home, or work phone? ";
+    string type;
+    getline(cin, type);
+    if (type == "mobile") {
+      phone_number->set_type(tutorial::Person::MOBILE);
+    } else if (type == "home") {
+      phone_number->set_type(tutorial::Person::HOME);
+    } else if (type == "work") {
+      phone_number->set_type(tutorial::Person::WORK);
+    } else {
+      cout << "Unknown phone type.  Using default." << endl;
+    }
+  }
+}
+
+// Main function:  Reads the entire address book from a file,
+//   adds one person based on user input, then writes it back out to the same
+//   file.
+int main(int argc, char* argv[]) {
+  // Verify that the version of the library that we linked against is
+  // compatible with the version of the headers we compiled against.
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  if (argc != 2) {
+    cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
+    return -1;
+  }
+
+  tutorial::AddressBook address_book;
+
+  {
+    // Read the existing address book.
+    fstream input(argv[1], ios::in | ios::binary);
+    if (!input) {
+      cout << argv[1] << ": File not found.  Creating a new file." << endl;
+    } else if (!address_book.ParseFromIstream(&input)) {
+      cerr << "Failed to parse address book." << endl;
+      return -1;
+    }
+  }
+
+  // Add an address.
+  PromptForAddress(address_book.add_person());
+
+  {
+    // Write the new address book back to disk.
+    fstream output(argv[1], ios::out | ios::trunc | ios::binary);
+    if (!address_book.SerializeToOstream(&output)) {
+      cerr << "Failed to write address book." << endl;
+      return -1;
+    }
+  }
+
+  // Optional:  Delete all global objects allocated by libprotobuf.
+  google::protobuf::ShutdownProtobufLibrary();
+
+  return 0;
+}
+```
+
+注意 `GOOGLE_PROTOBUF_VERIFY_VERSION` 宏。它是一种好的实践——虽然不是严格必须的——在使用 C++ Protocol Buffer 库之前执行该宏。它可以保证避免不小心链接到一个与编译的头文件版本不兼容的库版本。如果被检查出来版本不匹配,程序将会终止。注意,每个 `.pb.cc` 文件在初始化时会自动调用这个宏。
+
+同时注意在程序最后调用 `ShutdownProtobufLibrary()`。它用于释放 Protocol Buffer 库申请的所有全局对象。对大部分程序,这不是必须的,因为虽然程序只是简单退出,但是 OS 会处理释放程序的所有内存。然而,如果你使用了内存泄漏检测工具,工具要求全部对象都要释放,或者你正在写一个库,该库可能会被一个进程多次加载和卸载,那么你可能需要强制 Protocol Buffer 清除所有东西。
+
+### 读取消息
+
+当然,如果你无法从它获取任何信息,那么这个地址簿没多大用处!这个示例读取上面例子创建的文件,并打印文件里的所有内容。
+
+```c++
+#include <iostream>
+#include <fstream>
+#include <string>
+#include "addressbook.pb.h"
+using namespace std;
+
+// Iterates though all people in the AddressBook and prints info about them.
+void ListPeople(const tutorial::AddressBook& address_book) {
+  for (int i = 0; i < address_book.person_size(); i++) {
+    const tutorial::Person& person = address_book.person(i);
+
+    cout << "Person ID: " << person.id() << endl;
+    cout << "  Name: " << person.name() << endl;
+    if (person.has_email()) {
+      cout << "  E-mail address: " << person.email() << endl;
+    }
+
+    for (int j = 0; j < person.phone_size(); j++) {
+      const tutorial::Person::PhoneNumber& phone_number = person.phone(j);
+
+      switch (phone_number.type()) {
+        case tutorial::Person::MOBILE:
+          cout << "  Mobile phone #: ";
+          break;
+        case tutorial::Person::HOME:
+          cout << "  Home phone #: ";
+          break;
+        case tutorial::Person::WORK:
+          cout << "  Work phone #: ";
+          break;
+      }
+      cout << phone_number.number() << endl;
+    }
+  }
+}
+
+// Main function:  Reads the entire address book from a file and prints all
+//   the information inside.
+int main(int argc, char* argv[]) {
+  // Verify that the version of the library that we linked against is
+  // compatible with the version of the headers we compiled against.
+  GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+  if (argc != 2) {
+    cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
+    return -1;
+  }
+
+  tutorial::AddressBook address_book;
+
+  {
+    // Read the existing address book.
+    fstream input(argv[1], ios::in | ios::binary);
+    if (!address_book.ParseFromIstream(&input)) {
+      cerr << "Failed to parse address book." << endl;
+      return -1;
+    }
+  }
+
+  ListPeople(address_book);
+
+  // Optional:  Delete all global objects allocated by libprotobuf.
+  google::protobuf::ShutdownProtobufLibrary();
+
+  return 0;
+}
+```
+
+### 扩展 Protocol Buffer
+
+早晚在你发布了使用 protocol buffer 的代码之后,毫无疑问,你会想要 "改善"
+ protocol buffer 的定义。如果你想要新的 buffers 向后兼容,并且老的 buffers 向前兼容——几乎可以肯定你很渴望这个——这里有一些规则,你需要遵守。在新的 protocol buffer 版本:
+
+ * 你绝不可以修改任何已存在字段的标签数字
+ * 你绝不可以添加或删除任何 required 字段
+ * 你可以删除 optional 或 repeated 字段
+ * 你可以添加新的 optional 或 repeated 字段,但是你必须使用新的标签数字(也就是说,标签数字在 protocol buffer 中从未使用过,甚至不能是已删除字段的标签数字)。
+
+ (这是对于上面规则的一些[异常情况][9],但它们很少用到。)
+
+ 如果你能遵守这些规则,旧代码则可以欢快地读取新的消息,并且简单地忽略所有新的字段。对于旧代码来说,被删除的 optional 字段将会简单地赋予默认值,被删除的 `repeated` 字段会为空。新代码显然可以读取旧消息。然而,请记住新的 optional 字段不会呈现在旧消息中,因此你需要显式地使用 `has_` 检查它们是否被设置或者在 `.proto` 文件在标签数字后使用 `[default = value]` 提供一个合理的默认值。如果一个 optional 元素没有指定默认值,它将会使用类型特定的默认值:对于字符串,默认值为空字符串;对于布尔值,默认值为 false;对于数字类型,默认类型为 0。注意,如果你添加一个新的 repeated 字段,新代码将无法辨别它被留空(left empty)(被新代码)或者从没被设置(被旧代码),因为 repeated 字段没有 `has_` 标志。
+
+### 优化技巧
+
+C++ Protocol Buffer 库已极度优化过了。但是,恰当的用法能够更多地提高性能。这里是一些技巧,可以帮你从库中挤压出最后一点速度:
+
+* 尽可能复用消息对象。即使它们被清除掉,消息也会尽量保存所有被分配来重用的内存。因此,如果我们正在处理许多相同类型或一系列相似结构的消息,一个好的办法是重用相同的消息对象,从而减少内存分配的负担。但是,随着时间的流逝,对象可能会膨胀变大,尤其是当你的消息尺寸(译者注:各消息内容不同,有些消息内容多一些,有些消息内容少一些)不同的时候,或者你偶尔创建了一个比平常大很多的消息的时候。你应该自己通过调用 [SpaceUsed][10] 方法监测消息对象的大小,并在它太大的时候删除它。
+
+* 对于在多线程中分配大量小对象的情况,你的操作系统内存分配器可能优化得不够好。你可以尝试使用 google 的 [tcmalloc][11]。
+
+### 高级用法
+
+Protocol Buffers 绝不仅用于简单的数据存取以及序列化。请阅读 [C++ API reference][12] 来看看你还能用它来做什么。
+
+protocol 消息类所提供的一个关键特性就是反射。你不需要编写针对一个特殊的消息类型的代码,就可以遍历一个消息的字段并操作它们的值。一个使用反射的有用方法是 protocol 消息与其他编码互相转换,比如 XML 或 JSON。反射的一个更高级的用法可能就是可以找出两个相同类型的消息之间的区别,或者开发某种 "协议消息的正则表达式",利用正则表达式,你可以对某种消息内容进行匹配。只要你发挥你的想像力,就有可能将 Protocol Buffers 应用到一个更广泛的、你可能一开始就期望解决的问题范围上。
+
+反射是由 [Message::Reflection interface][13] 提供的。
+
+--------------------------------------------------------------------------------
+
+via: https://developers.google.com/protocol-buffers/docs/cpptutorial
+
+作者:[Google][a]
+译者:[cposture](https://github.com/cposture)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://developers.google.com/protocol-buffers/docs/cpptutorial
+[1]: https://developers.google.com/protocol-buffers/docs/proto
+[2]: https://developers.google.com/protocol-buffers/docs/encoding
+[3]: https://developers.google.com/protocol-buffers/docs/downloads
+[4]: https://developers.google.com/protocol-buffers/docs/downloads.html
+[5]: https://developers.google.com/protocol-buffers/docs/reference/cpp-generated
+[6]: https://developers.google.com/protocol-buffers/docs/proto
+[7]: https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.message.html#Message
+[8]: https://developers.google.com/protocol-buffers/docs/encoding
+[9]: https://developers.google.com/protocol-buffers/docs/proto#updating
+[10]: https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.message.html#Message.SpaceUsed.details
+[11]: http://code.google.com/p/google-perftools/
+[12]: https://developers.google.com/protocol-buffers/docs/reference/cpp/index.html
+[13]: https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.message.html#Message.Reflection
diff --git a/translated/tech/20160830 Ohm JavaScript Parser that Create a Language in 200 Lines of Code.md b/translated/tech/20160830 Ohm JavaScript Parser that Create a Language in 200 Lines of Code.md
new file mode 100644
index 0000000000..70dd8091a4
--- /dev/null
+++ b/translated/tech/20160830 Ohm JavaScript Parser that Create a Language in 200 Lines of Code.md	
@@ -0,0 +1,192 @@
+Ohm: 一种可以用两百行代码创造一种语言的 JavaScript 解释器
+解释器是一种非常有用的软件库。从概念上简单的说,它们的实现很有挑战并且在计算机科学中经常被认为是暗黑艺术。在这个系列的博文中,我会向你们展示为什么你不需要成为哈利波特就能够很好的控制解释器。但是为了以防万一带上你的魔杖。
+我们将探索一种叫做 Ohm 的新的开源库,它使得搭建解释器很简单并且更加容易再利用。在这个系列里,我们使用 Ohm 去识别数字,构建计算器等等。在这个系列的最后你将已经用少于 200 行的代码发明了一种完整的编程语言。这个强大的工具将让你能够做一些你可能过去认为不可能的事情。
+###为什么解释器很困难?
+解释器非常有用。在很多时候你可能需要一个解释器。一种新的文件格式可能出现,你需要去处理但还没有人为它写了一个库;或者你发现了一种老格式的文件但是现存的解释器不能构建你需要的平台。我已经看到这样的事发生无数次。代码会来来去去但数据却是永恒的。
+基础的解释器很简单:只是把一个数据结构转化成另一个。所以为什么感觉你需要成为 邓布利多【魔法师】才能够把它们做出来。
+解释器的一些历史性的挑战是很难写,绝大多数工具很老并且假设了大量晦涩难懂的计算机科学知识。如果你在大学里上过编译器课程那么课本里可能也有从 1970 年以来的技术。幸运的是,解释器技术从那时候起已经提高了很多。
+代表性地,解释器是通过使用一种叫作形式语法的特殊语法来定义你想要解析的东西这样发明的,然后你需要把它放入像 Bison 和 Yacc 的工具中,这些工具能够产生一堆你需要修改的 C 代码或者链接到你实际写入额的编程语言。另外的选择是用你更喜欢的语言亲自动手写一个解释器,这很慢且很容易出错,在你能够真正使用它之前还有许多额外的工作。
+想像一下,是否你关于你想要解析的东西的语法描述也是解释器?如果你能够仅仅直接运行语法,然后在你需要的地方增加挂钩,那是什么?那就是 Ohm 所做的事。
+###解释器简介
+[Ohm][1]是一种新的解析系统。它类似你可能已经在课本里面看到的语法并且它更强大,使用起来更简单。通过 Ohm, 你能够使用一种灵活的语法以 .ohm 文件格式来写格式定义,然后使用你的宿主语言把语义加入到里面。在这篇博文里,我们将用 JavaScript 作为宿主语言。
+Ohm 建立在一个为制造更简单、更灵活的解释器的一个多年调查基础之上。VPRI 的 [STEPS program](pdf) 使用 Ohm 的前驱为许多特殊的工作创造了专门的语言(比如一个有 400 行代码的平行制图描绘器)[Ometa][3].
+Ohm 有许多有趣的特点和符号,但是不是要全部解释它们,我认为我们应该只需投入其中并构建一些东西。
+###解析整数
+让我们来解析一些数字。这看起来会很简单,只需在一个文本串中寻找毗邻的数字,但是让我们尝试去处理所有形式的数字:整数和浮点数,十六进制数和八进制数,科学计数,负数。解析数字很简单,正确解析却很难。
+亲自构建这个代码将会很困难,会有很多故障,会伴随有许多特殊的情况,比如有时会相互矛盾。
+用 Ohm 构建的解释器涉及三个部分:语法、语义和测试。我通常挑选一个问题的一部分为它写测试,然后构建足够的语法和语义来使测试通过。然后我再挑选问题的另一部分,增加更多的测试,更新语法和语义,从而确保所有的测试能够持续通过。即使我们有了新的强大的工具,写解释器从概念上来说依旧很困难。测试是用一种合理的方式来构建解释器的唯一方法。现在,让我们开始工作。
+我们将从整数开始。一个整数由一系列相互毗邻的数字组成。让我们把下面的内容放入一个叫做 grammar.ohm 的文件中:
+```
+CoolNums {
+   // just a basic integer
+   Number = digit+
+}
+```
+这创造了一条撮合一个或多个数字叫作 Number 的单一规则。+ 意味着一个或更多,就像一个常规的表达。当有一个或更多的数字时,这个规则将会撮合它们,如果没有数字或者有一些不是数字的东西将不会撮合。一个数字定义成从 0 到 9 其中的一个字符。数字也是像 Number 一样的规则,但是它是 Ohm 的其中一条构建规则因此我们不需要去定义它。我们可以推翻它如果我们想的话但在这时候这没有任何意义,毕竟我们不打算去发明一种新的数。
+现在,我们可以读入这个语法并用 Ohm 库来运行它。
+把它放入 test1.js
+```
+var ohm = require('ohm-js');
+var fs = require('fs');
+var assert = require('assert');
+var grammar = ohm.grammar(fs.readFileSync('src/blog_numbers/syntax1.ohm').toString());
+```
+Ohm 的语法调用将把文件读入并解释成一个语法对象。现在我们可以增加一些语义。把下面内容增加到你的 JavaScript 文件中:
+```
+var sem = grammar.createSemantics().addOperation('toJS', {
+    Number: function(a) {
+        return parseInt(this.sourceString,10);
+    }
+});
+```
+这创造了一系列叫作 sem with the operation to JS[伴有 JavaScript 操作的语义] 的语义。这些语义至关重要,一群函数和语法中的每一条规则相匹配。一个函数将会被调用当与它相匹配的语法规则被解析时。上面的 Number 函数将会被调用当语法中的 Number 规则被解析时。语法定义在语言中 chunks[大块] 是什么,语义定义当 chunks[大块] 被解析时应该做什么。
+语义函数能够做我们想做的任何事,比如打印初故障信息,创造对象,或者递归调用 toJS 作用于任何子节点。此时我们仅仅想把文本转换成真正的 JavaScript 整数。
+所有的语义函数有一个包含一些有用性质的暗含对象。源属性代表输入文本和这个节点相匹配。这个 sourceString[源串] 是一个匹配输入串,调用构建在 JavaScript 中的parseInt 函数会把这个串转换成一个数。parseInt 中 10 这个参数告诉 JavaScript 我们输入的是一个以 10 为基底的数。如果少了这个参数, JavaScript 也会假定以 10 为基底,但是我们把它包含在里面因为后面我们将支持以 16 为基底的数,所以使之明确比较好。
+既然我们有一些语法,让我们来实际解析一些东西看一看我们的解释器是否能够工作。如果知道我们的解释器工作?通过测试它,许多许多的测试,每一个边缘情况都需要一个测试。
+伴随标准断言 API,有一个测试函数能够匹配一些输入并运用我们的语义把它转换成一个数,然后比较转换生成的数和我们期望的输入。
+```
+   function test(input, answer) {
+     var match = grammar.match(input);
+     if(match.failed()) return console.log("input failed to match " + input + match.message);     
+     var result = sem(match).toJS();
+     assert.deepEqual(result,answer);
+     console.log('success = ', result, answer);
+    }
+```
+这个函数就是上面这个。现在我们能够为不同的数写一堆测试。如果匹配失败我们的脚本将会丢弃一个例外。如果不能打印成功,让我们尝试一下,把下面这些内容加入到脚本中:
+```
+    test("123",123);
+    test("999",999);
+    test("abc",999);
+```
+然后用节点 test.js 运行脚本
+你的输出应该是这样:
+```
+success =  123 123
+success =  999 999
+input failed to match abcLine 1, col 1:
+> 1 | abc
+      ^
+Expected a digit
+```
+真酷。正如理所当然的那样,前两个成功了,第三个失败了。更好的是,Ohm 自动给了我们一个很棒的错误信息指出匹配失败。
+###浮点数
+我们的解释器工作了,但是它不能做任何非常有趣的事。让我们把它扩展成既能解析整数又能解析浮点数。改变 grammar.ohm 文件使它看起来像下面这样:
+```
+CoolNums {
+  // just a basic integer
+  Number = float | int
+  int    = digit+
+  float  = digit+ "." digit+
+}
+```
+这把 Number 规则改变成指向一个浮点数或者一个整数。我的意思是,我们把这读成"一个 Number 由一个浮点数或者一个整数构成。”然后整数定义成 digit+, 浮点数定义成 digit+ 后面跟着一个句号然后再跟着另一个 digit+. 这意味着在句号前和句号后都至少要有一个数字。如果一个数中没有一个句号那么它就不是一个浮点数,因此就是一个整数。
+现在,让我们再次看一下我们的语义作用。由于我们现在有了新的规则所以我们需要新的作用函数:一个作为整数的,一个作为浮点数的。
+```
+var sem = grammar.createSemantics().addOperation('toJS', {
+    Number: function(a) {
+        return a.toJS();
+    },
+    int: function(a) {
+        console.log("doing int", this.sourceString);
+        return parseInt(this.sourceString,10);
+    },
+    float: function(a,b,c) {
+        console.log("doing float", this.sourceString);
+        return parseFloat(this.sourceString);
+    }
+});
+```
+这里有两件事情需要注意。首先,整数,浮点数和数都有相匹配的语法规则和函数。然而,针对数的作用不再有任何意义。它接收子节点 'a' 然后通过子节点返回 toJS 的结果。换句话说,Number 规则简单的返回相匹配的子规则。由于这是在 Ohm 中任何规则的默认行为,因此实际上我们不用去考虑 Number 的作用,Ohm 会替我们来做这件事。
+第二,整数有一个参数然而浮点数有三个:a, b, 和 c. 这是由于规则的参数数量。参数数量意味着一个规则里面有多少参数。如果我们回过头去看语法,浮点数的规则是:
+```
+  float  = digit+ "." digit+
+```
+浮点数规则通过三个部分来定义:第一个 digit+, '.', 还有第二个 digit+. 这三个部分都会作为参数传递给浮点数的作用函数。因此浮点数必须有三个参数否则 Ohm 库给出一个错误。在这种情况下我们不用在意参数因为我们仅仅直接攫取了输入串,但是我们仍然需要列表的参数来回避编译器错误。后面我们将实际使用其中一些参数。
+现在我们可以为新的浮点数支持添加更多的测试。
+```
+test("123",123);
+test("999",999);
+//test("abc",999);
+test('123.456',123.456);
+test('0.123',0.123);
+test('.123',0.123);
+```
+注意最后一个测试将会失败。一个浮点数必须以一个数开始,即使它仅仅是 0, .123 不是有效的,实际上真正的 JavaScript 语言有相同的规则。
+###十六进制数
+现在我们已经有了整数和浮点数,但是有一些其他的数的语法可能能够很好的支持:十六进制数和科学计数。十六进制数是是以 16 为基底2的数。十六进制数的数字能从 0 到 9 和从 A 到 F. 十六进制数经常用在计算机科学中当用二进制数据工作时,因为你可以仅仅使用两个数字表示 0 到 255 的数。
+在绝大多数源自 C 的编程语言(包括 JavaScript), 十六进制数通过在前面加上 ‘0x' 来向编译器表明后面跟的是一个十六进制数。为了让我们的解释器支持十六进制数,我们只需要添加另一条规则。
+```
+  Number = hex | float | int
+  int    = digit+
+  float  = digit+ "." digit+
+  hex    = "0x" hexDigit+
+  hexDigit := "0".."9" | "a".."f" | "A".."F"
+```
+我实际上已经增加了两条规则。'hex' 表明十六进制数是一个 'ox' 后面一个或多个 ’hexDigits'[十六进制数子] 的串。一个 'hexDigit' 是从 0 到 9, 或从 a 到 f, 或 A 到 F(包扩大写和小写的情况)的一个字符。我也修改了 Number 规则来识别十六进制数作为其他可能的选择。现在我们只需要另一条针对十六进制数的作用规则。
+```
+    hex: function(a,b) {
+        return parseInt(this.sourceString,16);
+    }
+```
+注意到,在这种情况下,我们把 '16' 作为基数传递给 'parseInt', 因为我们希望 JavaScript 知道这是一个十六进制数。
+
+我略过了一些很重要需要注意的事。针对 'hexDigit' 的规则像下面这样:
+```
+  hexDigit := "0".."9" | "a".."f" | "A".."F"
+```
+注意我使用的是 ':=' 而不是 '='. 在 Ohm 中,'=' 是当你需要推翻一条规则的时候使用。证明是 Ohm 已经有了针对 'hexDigit' 的默认规则,就像针对 'digit', 'space' 等一堆其他的东西。如果我使用了 '=', Ohm 将会报告一个错误。这是一个检查从而我不能无意识的推翻一个规则。由于新的 hexDigit 规则和 Ohm 的构建规则一样,所以我们可以仅仅对它添加注释然后让 Ohm 来实现它。我留下这个规则仅仅是因为这样我们可以看到它实际上是如何进行的。
+Now we can add some more tests and see that our hex digits really work:
+现在,我们可以添加更多的测试然后看到十六进制数真的工作。
+```
+test('0x456',0x456);
+test('0xFF',255);
+```
+###科学计数
+最后,让我们来支持科学计数。科学计数是针对非常大或非常小的数比如 1.8×10^3, 在大多数编程语言中,科学计数法表示的数会写成这样:1.8e3 表示 18000, 或者 1.8e-3 表示 .018. 让我们增加另外一对规则来支持这个指数表示:
+```
+    float  = digit+ "." digit+ exp?
+    exp    = "e" "-"? digit+
+```
+上面增加了一个指数规则通过在浮点数规则末尾加上一个 '?'. '?' 表示 0 或 1,所以指数是可选择的但是不能超过一个。增加指数规则也改变了浮点数规则的参数数量,所以我们需要为浮点数作用增加又一个参数,即使我们不使用它。
+```
+    float: function(a,b,c,d) {
+        console.log("doing float", this.sourceString);
+        return parseFloat(this.sourceString);
+    },
+```
+现在我们的测试可以通过了:
+```
+test('4.8e10',4.8e10);
+test('4.8e-10',4.8e-10);
+```
+###结论
+Ohm 是构建解释器的一个很棒的工具,因为它很容易开始并且你可以递增的增加规则。Ohm 也还有其他我今天没有写到的很棒的特点,比如故障观察仪和子类化。
+s.
+到目前为止,我们已经使用 Ohm 来把字符串翻译成 JavaScript 数,并且 Ohm 经常由于需要把一个表示转化成另一个这一目的而使用。然而,Ohm 还有更多的用途。通过放入以系列不同的语义作用你可以使用 Ohm 来真正处理和计算东西。一个单独的语法可以被许多不同的语义使用,这是 Ohm 其中一个不可思议的特点。
+在这个系列的下一篇文章中,我将向你们展示如何计算像(4.85 + 5 * (238 - 68)/2) 这样的数学表达式,不仅仅是解析数。
+额外的挑战:你能够扩展语法来支持八进制数吗?这些以 8 为基底的数能够只用 0 到 7 这几个数字来表示,前面加上一个数字 0 或者字母 o. 看看针对下面这些测试情况是够正确。下次我将给出答案。
+```
+test('0o77',7*8+7);
+test('0o23',0o23);
+```
+
+
+
+
+--------------------------------------------------------------------------------
+
+via: https://www.pubnub.com/blog/2016-08-30-javascript-parser-ohm-makes-creating-a-programming-language-easy/?utm_source=javascriptweekly&utm_medium=email
+
+作者:[Josh Marinacci][a]
+译者:[ucasFL](https://github.com/ucasFL)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: https://www.pubnub.com/blog/author/josh/
+[1]: https://github.com/cdglabs/ohm
+[2]: http://www.vpri.org/pdf/tr2012001_steps.pdf
+[3]: http://tinlizzie.org/ometa/
+
+
diff --git a/translated/tech/20160917 A Web Crawler With asyncio Coroutines.md b/translated/tech/20160917 A Web Crawler With asyncio Coroutines.md
new file mode 100644
index 0000000000..1bce0fd22a
--- /dev/null
+++ b/translated/tech/20160917 A Web Crawler With asyncio Coroutines.md	
@@ -0,0 +1,1088 @@
+一个使用asyncio协程的网络爬虫
+===
+
+
+## 介绍
+
+经典的计算机科学强调高效的算法,尽可能快地完成计算。但是很多网络程序的时间并不是消耗在计算上,而是在等待许多慢速的连接或者低频事件的发生。这些程序暴露出一个新的挑战:如何高效的等待大量网络事件。一个现代的解决方案是异步I/O。
+
+这一章我们将实现一个简单的网络爬虫。这个爬虫只是一个原型式的异步应用,因为它等待许多响应而只做少量的计算。一次爬的网页越多,它就能越快的完成任务。如果它为每个动态的请求启动一个线程的话,随着并发请求数量的增加,它会在耗尽套接字之前,耗尽内存或者线程相关的资源。使用异步I/O可以避免这个的问题。
+
+我们将分三个阶段展示这个例子。首先,我们会实现一个事件循环并用这个事件循环和回调来勾画出一个网络爬虫。它很有效,但是当把它扩展成更复杂的问题时,就会导致无法管理的混乱代码。然后,由于Python的协程不仅有效而且可扩展,我们将用Python的生成器函数实现一个简单的协程。在最后一个阶段,我们将使用Python标准库"asyncio"中功能完整的协程和异步队列完成这个网络爬虫。
+
+## 任务
+
+网络爬虫寻找并下载一个网站上的所有网页,也许还会把它们存档,为它们建立索引。从根URL开始,它获取每个网页,解析出没有遇到过的链接加到队列中。当网页没有未见到过的链接并且队列为空时,它便停止运行。
+
+我们可以通过同时下载大量的网页来加快这一过程。当爬虫发现新的链接,它使用一个新的套接字并行的处理这个新链接,解析响应,添加新链接到队列。当并发很大时,可能会导致性能下降,所以我们会限制并发的数量,在队列保留那些未处理的链接,直到一些正在执行的任务完成。
+
+## 传统方式
+
+怎么使一个爬虫并发?传统的做法是创建一个线程池,每个线程使用一个套接字在一段时间内负责一个网页的下载。比如,下载xkcd.com网站的一个网页:
+
+```python
+def fetch(url):
+    sock = socket.socket()
+    sock.connect(('xkcd.com', 80))
+    request = 'GET {} HTTP/1.0\r\nHost: xkcd.com\r\n\r\n'.format(url)
+    sock.send(request.encode('ascii'))
+    response = b''
+    chunk = sock.recv(4096)
+    while chunk:
+        response += chunk
+        chunk = sock.recv(4096)
+    
+    # Page is now downloaded.
+    links = parse_links(response)
+    q.add(links)
+```
+
+套接字操作默认是阻塞的:当一个线程调用一个类似`connect`和`recv`方法时,它会阻塞,直到操作完成.[^15]因此,为了同一时间内下载多个网页,我们需要很多线程。一个复杂的应用会通过线程池保持空闲的线程来分摊创建线程的开销。同样的做法也适用于套接字,使用连接池。
+
+到目前为止,线程是昂贵的,操作系统对一个进程,一个用户,一台机器能使用线程做了不同的硬性限制。在Jesse系统中,一个Python线程需要50K的内存,开启上万个线程会失败。每个线程的开销和系统的限制就是这种方式的瓶颈所在。
+
+在Dan Kegel那一篇很有影响力的文章"The C10K problem"[^8]中,它提出多线程方式在I/O并发上的局限性。他在开始写道,
+
+>是时候网络服务器要同时处理成千上万的客户啦,你不这样认为么?毕竟,现在网络是个很大的地方。
+
+Kegel在1999年创造出"C10K"术语。一万个连接在今天看来还是可接受的,但是问题依然存在,只不过大小不同。回到那时候,对于C10K问题,每个连接启一个线程是不切实际的。现在这个限制已经成指数级增长。确实,我们的玩具网络爬虫使用线程也可以工作的很好。但是,对于有着千万级连接的大规模应用来说,限制依然存在:会消耗掉所有线程,即使套接字还够用。那么我们该如何解决这个问题?
+
+## 异步
+
+异步I/O框架在一个线程中完成并发操作。让我们看看这是怎么做到的。
+
+异步框架使用*非阻塞*套接字。异步爬虫中,我们在发起到服务器的连接前把套接字设为非阻塞:
+
+```python
+sock = socket.socket()
+sock.setblocking(False)
+try:
+    sock.connect(('xkcd.com', 80))
+except BlockingIOError:
+    pass
+```
+
+对一个非阻塞套接字调用`connect`方法会立即抛出异常,即使它正常工作。这个异常模拟了底层C语言函数的行为,它把`errno`设置为`EINPROGRESS`,告诉你操作已经开始。
+
+现在我们的爬虫需要一种知道连接何时建立的方法,这样它才能发送HTTP请求。我们可以简单地使用循环来重试:
+
+```python
+request = 'GET {} HTTP/1.0\r\nHost: xkcd.com\r\n\r\n'.format(url)
+encoded = request.encode('ascii')
+
+while True:
+    try:
+        sock.send(encoded)
+        break  # Done.
+    except OSError as e:
+        pass
+
+print('sent')
+```
+
+这种方法不仅消耗CPU,也不能有效的等待*多个*套接字。在远古时代,BSD Unix的解决方法是`select`,一个C函数,它在一个或一组非阻塞套接字上等待事件发生。现在,互联网应用大量连接的需求,导致`select`被`poll`代替,以及BSD的`kqueue`和Linux的`epoll`。它们的API和`select`相似,但在大数量的连接中也能有较好的性能。
+
+Python 3.4的`DefaultSelector`使用你系统上最好的类`select`函数。去注册一个网络I/O事件,我们创建一个非阻塞套接字,并使用默认的selector注册。
+
+```python
+from selectors import DefaultSelector, EVENT_WRITE
+
+selector = DefaultSelector()
+
+sock = socket.socket()
+sock.setblocking(False)
+try:
+    sock.connect(('xkcd.com', 80))
+except BlockingIOError:
+    pass
+
+def connected():
+    selector.unregister(sock.fileno())
+    print('connected!')
+
+selector.register(sock.fileno(), EVENT_WRITE, connected)
+```
+
+我们不理会这个伪造的错误,调用`selector.register`,传递套接字文件描述符,一个表示我们想要监听什么事件的常量。为了当连接建立时收到提醒,我们使用`EVENT_WRITE`:它表示什么时候这个套接字可写。我们还传递了一个Python函数,`connected`,当对应事件发生时被调用。这样的函数被称为*回调*。
+
+我们在一个循环中处理I/O提醒,随着selector接收到它们。
+
+```python
+def loop():
+    while True:
+        events = selector.select()
+        for event_key, event_mask in events:
+            callback = event_key.data
+            callback()
+```
+
+`connected`回调函数被保存在`event_key.data`中,一旦这个非阻塞套接字建立连接,它就会被取出来执行。
+
+不像我们前面那个快速重试的循环,这里的`select`调用会阻塞,等待下一个I/O事件,接着执行等待这个事件的回调函数。
+
+到目前为止我们展现了什么?我们展示了如何开始一个I/O操作和当操作准备好时调用回调函数。异步*框架*,它在单线程中执行并发操作,建立在两个功能之上,非阻塞套接字和事件循环。
+
+## 回调
+
+用我们刚刚建立的异步框架,怎么才能完成一个网络爬虫?即使是一个简单的网页下载程序也是很难写的。
+
+首先,我们有一个未获取的URL集合,和一个已经解析过的URL集合。
+
+```python
+urls_todo = set(['/'])
+seen_urls = set(['/'])
+```
+
+这两个集合加在一起就是所有的URL。用"/"初始化它们。
+
+获取一个网页需要一系列的回调。在套接字连接建立时`connected`回调触发,它向服务器发送一个GET请求。但是它要等待响应,所以我们需要注册另一个回调函数,当回调被调用,它也不能一次读取完整的请求,所以,需要再一次注册,如此反复。
+
+让我们把这些回调放在一个`Fetcher`对象中,它需要一个URL,一个套接字,还需要一个地方保存返回的字节:
+
+```python
+class Fetcher:
+    def __init__(self, url):
+        self.response = b''  # Empty array of bytes.
+        self.url = url
+        self.sock = None
+```
+
+我们的入口点在`Fetcher.fetch`:
+
+```python
+    # Method on Fetcher class.
+    def fetch(self):
+        self.sock = socket.socket()
+        self.sock.setblocking(False)
+        try:
+            self.sock.connect(('xkcd.com', 80))
+        except BlockingIOError:
+            pass
+            
+        # Register next callback.
+        selector.register(self.sock.fileno(),
+                          EVENT_WRITE,
+                          self.connected)
+```
+
+`fetch`方法从连接一个套接字开始。但是要注意这个方法在连接建立前就返回了。它必须返回到事件循环中等待连接建立。为了理解为什么要要这样,假设我们程序的整体结构如下:
+
+```python
+# Begin fetching http://xkcd.com/353/
+fetcher = Fetcher('/353/')
+fetcher.fetch()
+
+while True:
+    events = selector.select()
+    for event_key, event_mask in events:
+        callback = event_key.data
+        callback(event_key, event_mask)
+```
+
+所有的事件提醒都在事件循环中的`select`函数后处理。所以`fetch`必须把控制权交给事件循环。这样我们的程序才能知道什么时候连接已建立,接着循环调用`connected`回调,它已经在`fetch`方法中注册过。
+
+这里是我们`connected`方法的实现:
+
+```python
+    # Method on Fetcher class.
+    def connected(self, key, mask):
+        print('connected!')
+        selector.unregister(key.fd)
+        request = 'GET {} HTTP/1.0\r\nHost: xkcd.com\r\n\r\n'.format(self.url)
+        self.sock.send(request.encode('ascii'))
+        
+        # Register the next callback.
+        selector.register(key.fd,
+                          EVENT_READ,
+                          self.read_response)
+```
+
+这个方法发送一个GET请求。一个真正的应用会检查`send`的返回值,以防所有的信息没能一次发送出去。但是我们的请求很小,应用也不复杂。它只是简单的调用`send`,然后等待响应。当然,它必须注册另一个回调并把控制权交给事件循环。接下来也是最后一个回调函数`read_response`,它处理服务器的响应:
+
+```python
+    # Method on Fetcher class.
+    def read_response(self, key, mask):
+        global stopped
+
+        chunk = self.sock.recv(4096)  # 4k chunk size.
+        if chunk:
+            self.response += chunk
+        else:
+            selector.unregister(key.fd)  # Done reading.
+            links = self.parse_links()
+            
+            # Python set-logic:
+            for link in links.difference(seen_urls):
+                urls_todo.add(link)
+                Fetcher(link).fetch()  # <- New Fetcher.
+
+            seen_urls.update(links)
+            urls_todo.remove(self.url)
+            if not urls_todo:
+                stopped = True
+```
+
+这个回调在每次selector发现套接字*可读*时被调用,可读有两种情况:套接字接受到数据或它被关闭。
+
+这个回调函数从套接字读取4K数据。如果没有4k,那么有多少读多少。如果比4K多,`chunk`只包4K数据并且这个套接字保持可读,这样在事件循环的下一个周期,会在次回到这个回调函数。当响应完成时,服务器关闭这个套接字,`chunk`为空。
+
+没有展示的`parse_links`方法,它返回一个URL集合。我们为每个新的URL启动一个fetcher。注意一个使用异步回调方式编程的好处:我们不需要为共享数据加锁,比如我们往`seen_urls`增加新链接时。这是一种非抢占式的多任务,它不会在我们代码中的任意一个地方中断。
+
+我们增加了一个全局变量`stopped`,用它来控制这个循环:
+
+```python
+stopped = False
+
+def loop():
+    while not stopped:
+        events = selector.select()
+        for event_key, event_mask in events:
+            callback = event_key.data
+            callback()
+```
+
+一旦所有的网页被下载下来,fetcher停止这个事件循环,程序退出。
+
+这个例子让异步编程的一个问题明显的暴露出来:意大利面代码。
+
+我们需要某种方式来表达一串计算和I/O操作,并且能够调度多个这样的操作让他们并发的执行。但是,没有线程你不能把这一串操作写在一个函数中:当函数开始一个I/O操作,它明确的把未来所需的状态保存下来,然后返回。你需要考虑如何写这个状态保存的代码。
+
+让我们来解释下这到底是什么意思。考虑在线程中使用通常的阻塞套接字来获取一个网页时是多么简单。
+
+```python
+# Blocking version.
+def fetch(url):
+    sock = socket.socket()
+    sock.connect(('xkcd.com', 80))
+    request = 'GET {} HTTP/1.0\r\nHost: xkcd.com\r\n\r\n'.format(url)
+    sock.send(request.encode('ascii'))
+    response = b''
+    chunk = sock.recv(4096)
+    while chunk:
+        response += chunk
+        chunk = sock.recv(4096)
+    
+    # Page is now downloaded.
+    links = parse_links(response)
+    q.add(links)
+```
+
+在一个套接字操作和下一个操作之间这个函数到底记住了什么?它有一个套接字,一个URL和一个可增长的`response`。运行在线程中的函数使用编程语言的基本功能,栈中的局部变量来保存临时的状态。这样的函数有一个"continuation"----在I/O结束后它要执行的代码。运行时通过线程的指令指针来记住这个continuation。你不必考虑怎么在I/O操作后恢复局部变量和这个continuation。语言本身的特性帮你解决。
+
+但是用一个基于回调的异步框架,这些语言特性不能提供一点帮助。当等待I/O操作时,一个函数必须明确的保存它的状态,因为它会在I/O操作完成之前返回并清除栈帧。为了在我们基于回调的例子中代替局部变量,我们把`sock`和`response`作为Fetcher实例`self`属性。为了代替指令指针,它通过注册`connnected`和`read_response`回调来保存continuation。随着应用功能的增长,我们手动保存回调的复杂性也会增加。如此繁复的记账式工作会让编码者感到头痛。
+
+更糟糕的是,当我们的回调函数抛出异常会发生什么?假设我们没有写好`parse_links`方法,它在解析HTML时抛出异常:
+
+```
+Traceback (most recent call last):
+  File "loop-with-callbacks.py", line 111, in <module>
+    loop()
+  File "loop-with-callbacks.py", line 106, in loop
+    callback(event_key, event_mask)
+  File "loop-with-callbacks.py", line 51, in read_response
+    links = self.parse_links()
+  File "loop-with-callbacks.py", line 67, in parse_links
+    raise Exception('parse error')
+Exception: parse error
+```
+
+这个堆栈回溯只能显示出事件循环调用了一个回调。我们不知道是什么导致了这个错误。这条链的两边都被破坏:不知道从哪来也不知到哪去。这种丢失上下文的现象被称为"stack ripping",它还会阻止我们为回调链设置异常处理。
+
+所以,除了关于多线程和异步那个更高效的争议,还有一个关于这两者之间的争论,谁更容易出错。如果在同步上出现失误,线程更容易出现数据竞争的问题,而回调因为"stack ripping"问题而非常难于调试。
+
+## 协程
+
+还记得我们对你许下的承诺么?我们可以写出这样的异步代码,它既有回调方式的高效,也有多线程代码的简洁。这个结合是同过一种称为协程的模式来实现的。使用Python3.4标准库asyncio和一个叫"aiohttp"的包,在协程中获取一个网页是非常直接的[^10]:
+
+```python
+    @asyncio.coroutine
+    def fetch(self, url):
+        response = yield from self.session.get(url)
+        body = yield from response.read()
+```
+
+它也是可扩展的。在Jesse系统上,与每个线程50k内存相比,一个Python协程只需要3k内存。Python很容易就可以启动上千个协程。
+
+协程的概念可以追溯到计算机科学的远古时代,它很简单,一个可以暂停和恢复的子过程。线程是被操作系统控制的抢占式多任务,而协程是可合作的,它们自己选择什么时候暂停去执行下一个协程。
+
+有很多协程的实现。甚至在Python中也有几种。Python3.4标准库asyncio中的协程,它是建立在生成器,一个Future类和"yield from"语句之上。从Python3.5开始,协程变成了语言本身的特性。然而,理解Python3.4中这个通过语言原有功能实现的协程,是我们处理Python3.5中原生协程的基础。
+
+## 生成器如何工作
+
+在你理解生成器之前,你需要知道普通的Python函数是怎么工作的。当一个函数调用一个子过程,这个被调用函数获得控制权。直到它返回或者有异常发生,才把控制权交给调用者:
+
+```python
+>>> def foo():
+...     bar()
+...
+>>> def bar():
+...     pass
+```
+
+标准的Python解释器是C语言写的。一个Python函数被调用对应的C函数是`PyEval_EvalFrameEx`。它获得一个Python栈帧结构并在这个栈帧的上下文中执行Python字节码。这里是`foo`的字节码:
+
+```python
+>>> import dis
+>>> dis.dis(foo)
+  2           0 LOAD_GLOBAL              0 (bar)
+              3 CALL_FUNCTION            0 (0 positional, 0 keyword pair)
+              6 POP_TOP
+              7 LOAD_CONST               0 (None)
+             10 RETURN_VALUE
+```
+
+`foo`函数在它栈中加载`bar`并调用它,然后把`bar`的返回值从栈中弹出,加载`None`值并返回。
+
+当`PyEval_EvalFrameEx`遇到`CALL_FUNCTION`字节码时,它会创建一个新的栈帧,并用这个栈帧递归的调用`PyEval_EvalFrameEx`来执行`bar`函数。
+
+图
+
+非常重要的一点是,Python的栈帧在堆中分配!Python解释器是一个标准的C程序,所以他的栈帧是正常的栈帧。但是Python的栈帧是在堆中处理。这意味着Python栈帧在函数调用结束后依然可以存在。我们在`bar`函数中保存当前的栈帧,交互式的看看这种现象:
+
+```python
+>>> import inspect
+>>> frame = None
+>>> def foo():
+...     bar()
+...
+>>> def bar():
+...     global frame
+...     frame = inspect.currentframe()
+...
+>>> foo()
+>>> # The frame was executing the code for 'bar'.
+>>> frame.f_code.co_name
+'bar'
+>>> # Its back pointer refers to the frame for 'foo'.
+>>> caller_frame = frame.f_back
+>>> caller_frame.f_code.co_name
+'foo'
+```
+
+现在该说Python生成器了,它使用同样构件--code object和栈帧--去完成一个不可思议的任务。
+
+这是一个生成器函数:
+
+```python
+>>> def gen_fn():
+...     result = yield 1
+...     print('result of yield: {}'.format(result))
+...     result2 = yield 2
+...     print('result of 2nd yield: {}'.format(result2))
+...     return 'done'
+...     
+```
+
+在Python把`gen_fn`编译成字节码的过程中,一旦它看到`yield`语句就知道这是一个生成器函数而不是普通的函数。它就会设置一个标志来记住这个事实:
+
+```python
+>>> # The generator flag is bit position 5.
+>>> generator_bit = 1 << 5
+>>> bool(gen_fn.__code__.co_flags & generator_bit)
+True
+```
+
+当你调用一个生成器函数,Python看到这个标志,就不会运行它而是创建一个生成器:
+
+```python
+>>> gen = gen_fn()
+>>> type(gen)
+<class 'generator'>
+```
+
+Python生成器封装了一个栈帧和函数体代码:
+
+```python
+>>> gen.gi_code.co_name
+'gen_fn'
+```
+
+所有通过调用`gen_fn`的生成器指向同一段代码,但都有各自的栈帧。这些栈帧不再任何一个C函数栈中,而是在堆空间中等待被使用:
+
+图
+
+栈帧中有一个指向最后执行指令的指针。初始化为-1,意味着它没开始运行:
+
+```python
+>>> gen.gi_frame.f_lasti
+-1
+```
+
+当我们调用`send`时,生成器一直运行到第一个`yield`语句处停止。并且`send`返回1,yield语句后的表达式的值。
+
+```python
+>>> gen.send(None)
+1
+```
+
+现在生成器的指令指针是3,字节码一共有56个字节:
+
+```python
+>>> gen.gi_frame.f_lasti
+3
+>>> len(gen.gi_code.co_code)
+56
+```
+
+这个生成器可以在任何时候,任何函数中恢复运行,因为它的栈帧并不在真正的栈中,而是堆中。在调用链中它的位置也是不确定的,它不必遵循普通函数先进后出的顺序。它像云一样自由。
+
+我们可以传递一个`hello`给生成器,它会成为yield语句的结果,并且生成器运行到第二个yield语句处。
+
+```python
+>>> gen.send('hello')
+result of yield: hello
+2
+```
+
+现在栈帧中包含局部变量`result`:
+
+```python
+>>> gen.gi_frame.f_locals
+{'result': 'hello'}
+```
+
+其它从`gen_fn`创建的生成器有着它自己的栈帧和局部变量。
+
+当我们在一次调用`send`,生成器从第二个yield开始运行,以抛出一个特殊的`StopIteration`异常为结束。
+
+```python
+>>> gen.send('goodbye')
+result of 2nd yield: goodbye
+Traceback (most recent call last):
+  File "<input>", line 1, in <module>
+StopIteration: done
+```
+
+这个异常有一个值"done",它就是生成器的返回值。
+
+## 使用生成器构建协程
+
+所以生成器可以暂停,可以给它一个值让它恢复,并且它还有一个返回值。这些特性看起来很适合去建立一个不使用回调的异步编程模型。我们想创造一个协程:一个在程序中可以和其他过程合作调度的过程。我们的协程将会是标准库`asyncio`中协程的一个简化版本,我们将使用生成器,futures和`yield from`语句。
+
+首先,我们需要一种方法去代表协程需要等待的未来事件。一个简化的版本是:
+
+```python
+class Future:
+    def __init__(self):
+        self.result = None
+        self._callbacks = []
+
+    def add_done_callback(self, fn):
+        self._callbacks.append(fn)
+
+    def set_result(self, result):
+        self.result = result
+        for fn in self._callbacks:
+            fn(self)
+```
+
+一个future初始化为未解决的,它同过调用`set_result`来解决。[^12]
+
+让我们用futures和协程来改写我们的fetcher。我们之前用回调写的fetcher如下:
+
+```python
+class Fetcher:
+    def fetch(self):
+        self.sock = socket.socket()
+        self.sock.setblocking(False)
+        try:
+            self.sock.connect(('xkcd.com', 80))
+        except BlockingIOError:
+            pass
+        selector.register(self.sock.fileno(),
+                          EVENT_WRITE,
+                          self.connected)
+
+    def connected(self, key, mask):
+        print('connected!')
+        # And so on....
+```
+
+`fetch`方法开始连接一个套接字,然后注册`connected`回调函数,它会在套接字建立连接后调用。现在我们使用协程把这两步合并:
+
+```python
+    def fetch(self):
+        sock = socket.socket()
+        sock.setblocking(False)
+        try:
+            sock.connect(('xkcd.com', 80))
+        except BlockingIOError:
+            pass
+
+        f = Future()
+
+        def on_connected():
+            f.set_result(None)
+
+        selector.register(sock.fileno(),
+                          EVENT_WRITE,
+                          on_connected)
+        yield f
+        selector.unregister(sock.fileno())
+        print('connected!')
+```
+
+现在,`fetch`是一个生成器,因为他有一个`yield`语句。我们创建一个未决的future,然后yield它,暂停执行直到套接字连接建立。内函数`on_connected`解决这个future。
+
+但是当future被解决,谁来恢复这个生成器?我们需要一个协程驱动器。让我们叫它`task`:
+
+```python
+class Task:
+    def __init__(self, coro):
+        self.coro = coro
+        f = Future()
+        f.set_result(None)
+        self.step(f)
+
+    def step(self, future):
+        try:
+            next_future = self.coro.send(future.result)
+        except StopIteration:
+            return
+
+        next_future.add_done_callback(self.step)
+
+# Begin fetching http://xkcd.com/353/
+fetcher = Fetcher('/353/')
+Task(fetcher.fetch())
+
+loop()
+```
+
+task通过传递一个None值给`fetch`来启动它。`fetch`运行到它yeild一个future,这个future被task捕获作为`next_future`。当套接字连接建立,事件循环运行回调函数`on_connected`,这里future被解决,`step`被调用,生成器恢复运行。
+
+## 用`yield from`重构协程
+
+一旦套接字连接建立,我们就可以发送HTTP请求,然后读取服务器响应。不再需要哪些分散在各处的回调函数,我们把它们放在同一个生成器函数中:
+
+```python
+    def fetch(self):
+        # ... connection logic from above, then:
+        sock.send(request.encode('ascii'))
+
+        while True:
+            f = Future()
+
+            def on_readable():
+                f.set_result(sock.recv(4096))
+
+            selector.register(sock.fileno(),
+                              EVENT_READ,
+                              on_readable)
+            chunk = yield f
+            selector.unregister(sock.fileno())
+            if chunk:
+                self.response += chunk
+            else:
+                # Done reading.
+                break
+```
+
+从套接字中读取所有信息的代码看起来很通用。我们能不把它提取成一个子过程?现在该Python3的`yield from`登场了。它能让一个生成器委托另一个生成器。
+
+让我们先回到原来那个简单的生成器:
+
+```python
+>>> def gen_fn():
+...     result = yield 1
+...     print('result of yield: {}'.format(result))
+...     result2 = yield 2
+...     print('result of 2nd yield: {}'.format(result2))
+...     return 'done'
+...     
+```
+
+为了从其他生成器调用这个生成器,我们使用`yield from`:
+
+
+```python
+>>> # Generator function:
+>>> def caller_fn():
+...     gen = gen_fn()
+...     rv = yield from gen
+...     print('return value of yield-from: {}'
+...           .format(rv))
+...
+>>> # Make a generator from the
+>>> # generator function.
+>>> caller = caller_fn()
+```
+
+这个`caller`的行为的和它委派的生成器表现的完全一致:
+
+```python
+>>> caller.send(None)
+1
+>>> caller.gi_frame.f_lasti
+15
+>>> caller.send('hello')
+result of yield: hello
+2
+>>> caller.gi_frame.f_lasti  # Hasn't advanced.
+15
+>>> caller.send('goodbye')
+result of 2nd yield: goodbye
+return value of yield-from: done
+Traceback (most recent call last):
+  File "<input>", line 1, in <module>
+StopIteration
+```
+
+注意到`caller`的指令指针保持15不变,就是`yield from`的地方,即使内部的生成器从一个yield语句运行到下一个yield,它始终不变。[^13]从`caller`外部来看,我们无法分辨yield出的值是来自`caller`还是它委派的生成器。而从`gen`内部来看,我们也不能分辨传给它的值是来自`caller`还是`caller`的外面。`yield from`是一个光滑的管道,值通过它进出`gen`,一直到`gen`结束。
+
+协程可以用`yield from`把工作委派给子协程,还可以接受子协程的返回值。注意到上面的`caller`打印出"return value of yield-from: done"。当`gen`完成后,它的返回值成为`caller`中`yield from`语句的值。
+
+```python
+    rv = yield from gen
+```
+
+我们批评过基于回调的异步编程模式,其中最大的不满是关于`stack ripping`:当一个回调抛出异常,它的堆栈回溯通常是毫无用处的。它只显示出事件循环运行了它,而没有说为什么。那么协程怎么样?
+
+```python
+>>> def gen_fn():
+...     raise Exception('my error')
+>>> caller = caller_fn()
+>>> caller.send(None)
+Traceback (most recent call last):
+  File "<input>", line 1, in <module>
+  File "<input>", line 3, in caller_fn
+  File "<input>", line 2, in gen_fn
+Exception: my error
+```
+
+这还是比较有用的,当异常抛出时,堆栈回溯显示出`caller_fn`委派了`gen_fn`。令人更欣慰的是,你可以像正常函数一样使用异常处理:
+
+```python
+>>> def gen_fn():
+...     yield 1
+...     raise Exception('uh oh')
+...
+>>> def caller_fn():
+...     try:
+...         yield from gen_fn()
+...     except Exception as exc:
+...         print('caught {}'.format(exc))
+...
+>>> caller = caller_fn()
+>>> caller.send(None)
+1
+>>> caller.send('hello')
+caught uh oh
+```
+
+所以我们可以像提取子过程一样提取子协程。让我们从fetcher中提取一些有用的子协程。我们先写一个可以读一块数据的协程`read`:
+
+```python
+def read(sock):
+    f = Future()
+
+    def on_readable():
+        f.set_result(sock.recv(4096))
+
+    selector.register(sock.fileno(), EVENT_READ, on_readable)
+    chunk = yield f  # Read one chunk.
+    selector.unregister(sock.fileno())
+    return chunk
+```
+
+在`read`的基础上,`read_all`协程读取整个信息:
+
+```python
+def read_all(sock):
+    response = []
+    # Read whole response.
+    chunk = yield from read(sock)
+    while chunk:
+        response.append(chunk)
+        chunk = yield from read(sock)
+
+    return b''.join(response)
+```
+
+如果你换个角度看,它们看起来就像在做阻塞I/O的普通函数一样。但是事实上,`read`和`read_all`都是协程。yield from`read`暂停`read_all`直到I/O操作完成。当`read_all`暂停时,事件循环正在做其它的工作等待其他的I/O操作。`read`在下次循环中完成I/O操作时,`read_all`恢复运行。
+
+现在,`fetch`可以直接调用`read_all`:
+
+```python
+class Fetcher:
+    def fetch(self):
+		 # ... connection logic from above, then:
+        sock.send(request.encode('ascii'))
+        self.response = yield from read_all(sock)
+```
+
+神奇的是,Task类不需要做任何改变,它像以前一样驱动`fetch`协程:
+
+```python
+Task(fetcher.fetch())
+loop()
+```
+
+当`read`yield一个future时,task从`yield from`管道中接受它,就像直接从`fetch`接受一样。当循环解决一个future时,task把它的结果送给`fetch`,通过管道,`read`接受到这个值,这完全就像task直接驱动`read`一样:
+
+图
+
+亲爱的读者,我们已经完成了对asyncio协程探索。我们深入观察了生成器的机制,实现了简单的future和task。我们指出协程是如何利用两个世界的优点:比线程高效,比回调清晰。当然真正的asyncio比我们这个简化版本要复杂的多。真正的框架需要处理zero-copyI/0,公平调度,异常处理和其他大量特性。
+
+使用asyncio编写协程代码比你现在看到的要简单的多。在前面的代码中,我们从基本原理去实现协程,所以你看到了回调,task和future,甚至非阻塞套接字和``select``调用。但是当用asyncio编写应用,这些都不会出现在你的代码中。我们承诺过,你可以像这样下载一个网页:
+
+```python
+    @asyncio.coroutine
+    def fetch(self, url):
+        response = yield from self.session.get(url)
+        body = yield from response.read()
+```
+
+对我们的探索还满意么?回到我们原始的任务:使用asyncio写一个网络爬虫。
+
+## 使用协程
+
+我们从描述爬虫如何工作开始。现在是时候用asynio去实现它了。
+
+我们爬虫从获取第一个网页开始,解析出链接并把它们加到队列中。此后它开始傲游整个网站,并发的获取网页。倒是由于客户端和服务端的限制,我们希望有一个最大数目的worker。任何时候一个worker完成一个网页的获取,它应该立即从队列中取出下一个链接。我们会遇到没有事干的时候,所以worker必须能够暂停。一旦又有worker获取一个有很多链接的网页,队列会突增,暂停的worker立马被唤醒。最后,当任务完成后我们的程序必须能退出。
+
+假如你的worker是线程,怎样去描述你的算法?我们可以使用Python标准库中的同步队列。每次有新的一项加入,队列增加它的tasks计数器。线程worker完成一个任务后调用`task_done`。主线程阻塞在`Queue.join`,直到tasks计数器与`task_done`调用次数相匹配,然后退出。
+
+通过个一asynio队列,协程使用和线程一样的模式来实现。首先我们导入它[^6]:
+
+```python
+try:
+    from asyncio import JoinableQueue as Queue
+except ImportError:
+    # In Python 3.5, asyncio.JoinableQueue is
+    # merged into Queue.
+    from asyncio import Queue
+```
+
+我们把worker的共享状态收集在一个crawler类中,主要的逻辑写在`crawl`方法中。我们在一个协程中启动`crawl`,运行asyncio的事件循环直到`crawl`完成:
+
+```python
+loop = asyncio.get_event_loop()
+
+crawler = crawling.Crawler('http://xkcd.com',
+                           max_redirect=10)
+
+loop.run_until_complete(crawler.crawl())
+```
+
+crawler用一个跟URL和最大重定向数来初始化,它把`(URL, max_redirect`)序对放入队列中。(为什么要这样做,敬请期待)
+
+```python
+class Crawler:
+    def __init__(self, root_url, max_redirect):
+        self.max_tasks = 10
+        self.max_redirect = max_redirect
+        self.q = Queue()
+        self.seen_urls = set()
+        
+        # aiohttp's ClientSession does connection pooling and
+        # HTTP keep-alives for us.
+        self.session = aiohttp.ClientSession(loop=loop)
+        
+        # Put (URL, max_redirect) in the queue.
+        self.q.put((root_url, self.max_redirect))
+```
+
+现在队列中未完成的任务数是1。回到我们的主程序,启动事件循环和`crawl`方法:
+
+```python
+loop.run_until_complete(crawler.crawl())
+```
+`crawl`协程唤起workers。它像一个主线程:阻塞在`join`上直到所有任务完成,同时workers在后台运行。
+
+```python
+    @asyncio.coroutine
+    def crawl(self):
+        """Run the crawler until all work is done."""
+        workers = [asyncio.Task(self.work())
+                   for _ in range(self.max_tasks)]
+
+        # When all work is done, exit.
+        yield from self.q.join()
+        for w in workers:
+            w.cancel()
+```
+
+如果worker是线程,可能我们不会一次把它们全部创建出来。为了避免创建线程的昂贵代价,通常一个线程池会按需增长。但是协程很便宜,我们简单的把他们全部创建出来。
+
+怎么关闭这个`crawler`很有趣。当`join`完成,worker存活但是被暂停:他们等待更多的URL。所以主协程在退出之前清除它们。否则Python解释器关闭调用所有对象的析构函数,活着的worker叫喊到:
+
+```
+ERROR:asyncio:Task was destroyed but it is pending!
+```
+
+`cancel`又是如何工作的呢?生成器还有一个我们还没介绍的特点。你可以从外部抛一个异常给它:
+
+
+```python
+>>> gen = gen_fn()
+>>> gen.send(None)  # Start the generator as usual.
+1
+>>> gen.throw(Exception('error'))
+Traceback (most recent call last):
+  File "<input>", line 3, in <module>
+  File "<input>", line 2, in gen_fn
+Exception: error
+```
+
+生成器被`throw`恢复,但是他现在抛出一个异常。如何生成器没有捕获异常的代码,这个异常被传递到顶层。所以注销一个协程:
+
+```python
+    # Method of Task class.
+    def cancel(self):
+        self.coro.throw(CancelledError)
+```
+
+任何时候生成器在`yield from`语句暂停,被恢复并且抛出一个异常。我们在task的`step`方法中处理撤销。
+
+现在worker直到他被注销了,所以当它被销毁时,它不再抱怨。
+
+一旦`crawl`注销了worker,它就退出。同时事件循环看见这个协程结束了,也就退出l。
+
+```python
+loop.run_until_complete(crawler.crawl())
+```
+
+`crawl`方法包含了所有主协程需要做的事。而worker则完成了从队列中获取URL,获取网页,解析它们得到新的链接。每个worker独立的运行`worker`协程:
+
+```python
+    @asyncio.coroutine
+    def work(self):
+        while True:
+            url, max_redirect = yield from self.q.get()
+
+            # Download page and add new links to self.q.
+            yield from self.fetch(url, max_redirect)
+            self.q.task_done()
+```
+
+Python看见这段代码包含`yield from`语句,就把它编译成生成器函数。所以在`crawl`方法中,我们调用了10次`self.work`,但并没有真正执行,它仅仅创建了10个生成器对象并把它们包装成Task对象。task接收生成器yield的future,通过调用send方法,future的结果做为send的参数,来驱动它。由于生成器有自己的栈帧,它们可以独立运行,独立的局部变量和指令指针。
+
+worker使用队列来协调, 等待新的URL:
+
+```python
+    url, max_redirect = yield from self.q.get()
+```
+
+队列的`get `方法也是一个协程,它一直暂停到有新的URL进入队列。
+
+碰巧,这也是最后crawl停止时,协程暂停的地方。当主协程注销worker时,从协程的角度,`yield from`抛出`CancelledError`结束了它在循环中的最后旅程。
+
+worker获取一个网页,解析链接,把新的链接放入队列中,接着调用`task_done`减小计数器。最终一个worker遇到一个没有新链接的网页,并且队列里也没有任务,这次`task_done`的调用使计数器减为0,而`crawl`正阻塞在`join`方法上,现在它就可以结束了。
+
+我们承诺过要解释为什么队列中要使用序对,像这样:
+
+```python
+# URL to fetch, and the number of redirects left.
+('http://xkcd.com/353', 10)
+```
+
+新的URL的重定向次数是10。获取一个特别的URL会重定向一个新的位置。我们减小重定向次数,并把新的URL放入队列中。
+
+```python
+# URL with a trailing slash. Nine redirects left.
+('http://xkcd.com/353/', 9)
+```
+
+我们使用的`aiohttp`默认的会重定向返回最终的结果。但是,我们告诉它不要这样做,爬虫自己处理重定向。所以它可以合并那些目的相同的重定向路径:如果我们已经看到一个URL,说明它已经从其他的地方走过这条路了。
+
+
+```python
+    @asyncio.coroutine
+    def fetch(self, url, max_redirect):
+        # Handle redirects ourselves.
+        response = yield from self.session.get(
+            url, allow_redirects=False)
+
+        try:
+            if is_redirect(response):
+                if max_redirect > 0:
+                    next_url = response.headers['location']
+                    if next_url in self.seen_urls:
+                        # We have been down this path before.
+                        return
+    
+                    # Remember we have seen this URL.
+                    self.seen_urls.add(next_url)
+                    
+                    # Follow the redirect. One less redirect remains.
+                    self.q.put_nowait((next_url, max_redirect - 1))
+    	     else:
+    	         links = yield from self.parse_links(response)
+    	         # Python set-logic:
+    	         for link in links.difference(self.seen_urls):
+                    self.q.put_nowait((link, self.max_redirect))
+                self.seen_urls.update(links)
+        finally:
+            # Return connection to pool.
+            yield from response.release()
+```
+
+如果这是多进程代码,就有可能遇到讨厌的竞争条件。比如,一个work检查一个链接是否在`seen_urls`中,如果没有它就把这个链接加到队列中并把它放到`seen_urls`中。如果它在这两步操作之间被中断,而另一个work解析到相同的链接,发现它并没有出现在`seen_urls`中就把它加入队列中。这导致同样的链接在队列中出现两次,做了重复的工作和错误的统计。
+
+然而,一个协程只在`yield from`是才会被中断。这是协程比多线程少遇到竞争条件的关键。多线程必须获得锁来明确的进入一个临界区,否则它就是可中断的。而Python的协程默认是不会被中断的,只有它yield主动放弃控制权。
+
+我们不再需要在用回调方式时的fetcher类了。这个类只是不高效回调的一个变通方法:在等待I/O时,它需要一个存储状态的地方,因为局部变量并不能在函数调用间保留。倒是`fetch`协程可以像普通函数一样用局部变量保存它的状态,所以我们不再需要一个类。
+
+当`fetch`完成对服务器回应的处理,它返回到调用它的work。work调用`task_done`,接着从队列中取出一个URL。
+
+当`fetch`把新的链接放入队列中,它增加未完成的任务计数器。主协程在等待`q.join`。而当没有新的链接并且这是队列中最后一个URL,work调用`task_done`,任务计数器变为0,主协程从`join`中退出。
+
+与work和主协程一起工作的队列代码像这样:
+
+```python
+class Queue:
+    def __init__(self):
+        self._join_future = Future()
+        self._unfinished_tasks = 0
+        # ... other initialization ...
+    
+    def put_nowait(self, item):
+        self._unfinished_tasks += 1
+        # ... store the item ...
+
+    def task_done(self):
+        self._unfinished_tasks -= 1
+        if self._unfinished_tasks == 0:
+            self._join_future.set_result(None)
+
+    @asyncio.coroutine
+    def join(self):
+        if self._unfinished_tasks > 0:
+            yield from self._join_future
+```
+
+主协程`crawl`yield from`join`。所以当最后一个workd把计数器减为0,它告诉`crawl`恢复运行。
+
+旅程快要结束了。我们的程序从`crawl`调用开始:
+
+```python
+loop.run_until_complete(self.crawler.crawl())
+```
+
+程序如何结束?因为`crawl`是一个生成器函数。调用它返回一个生成器。为了驱动它,asyncio把它包装成一个task:
+
+
+class EventLoop:
+    def run_until_complete(self, coro):
+        """Run until the coroutine is done."""
+        task = Task(coro)
+        task.add_done_callback(stop_callback)
+        try:
+            self.run_forever()
+        except StopError:
+            pass
+
+class StopError(BaseException):
+    """Raised to stop the event loop."""
+
+def stop_callback(future):
+    raise StopError
+```
+
+当这个任务完成,它抛出`StopError`, 事件循环把这个异常当作正常退出的信号。
+
+但是,task的`add_done_callbock`和`result`方法又是什么呢?你可能认为task就像一个future,不错,你的直觉是对的。我们必须承认一个向你隐藏的细节,task是future。
+
+```python
+class Task(Future):
+    """A coroutine wrapped in a Future."""
+```
+
+通常,一个future被别人调用`set_result`。但是task,当协程结束时,它自己解决自己。记得我们解释过当Python生成器返回时,它抛出一个特殊的`StopIteration`异常:
+
+```python
+    # Method of class Task.
+    def step(self, future):
+        try:
+            next_future = self.coro.send(future.result)
+        except CancelledError:
+            self.cancelled = True
+            return
+        except StopIteration as exc:
+
+            # Task resolves itself with coro's return
+            # value.
+            self.set_result(exc.value)
+            return
+
+        next_future.add_done_callback(self.step)
+```
+
+所以当事件循环调用`task.add_done_callback(stop_callback)`,它就准备被这个task结束。在看一次`run_until_complete`;
+
+```python
+    # Method of event loop.
+    def run_until_complete(self, coro):
+        task = Task(coro)
+        task.add_done_callback(stop_callback)
+        try:
+            self.run_forever()
+        except StopError:
+            pass
+```
+
+当task捕获`StopIteration`并解决自己,这个回调重循环中抛出`StopError`。循环结束调用栈回到`run_until_complete`。我们的程序结束。
+
+## 总结
+
+现代的程序越来越多是I/O密集型而不是CPU密集型。对于这样的程序,Python的线程和不合适:全局锁阻止真正的并行计算,并且抢占切换也导致他们更容易出现竞争。异步通常是正确的选择。但是随着基于回调的异步代码增加,它会变得非常混乱。协程是一个更整洁的替代者。它们自然的重构成子过程,有健全的异常处理和栈追溯。
+
+如果我们换个角度看`yield from`语句,一个协程看起来像一个传统的线程。甚至我们采用金典的多线程模式编程,不需要重新发明。因此,与回调相比,协程更适合有经验的多线程的编码者。
+
+但是当我们打开眼睛关注`yield from`语句,我们能看到协程放弃控制权的标志点。不像多线程,协程展示出我们的代码哪里可以被中断哪里不能。在Glyph Lefkowitz富有启发性的文章"Unyielding"[^4]:"线程让局部推理变得困难,然而局部推理可能是软件开发中最重要的事"。然而,明确的yield,让"通过子过程本身而不是整个系统理解它的行为(应此,正确性)"成为可能。
+
+这章写于Python和异步的复兴时期。你刚学到的基于生成器的的协程,在2014年发布在Python 3.4的"asyncio"模块。2015年9月,Python 3.5发布,协程成为语言的一部分。这个原生的协程通过"async def"来声明, 使用"await"而不是"yield from"委托一个协程或者等待Future。
+
+
+除了这些优点,核心的思想不变。Python新的原生协程与生成器只是在语法上不同,工作原理非常相似。事实上,在Python解释器中它们公用同一个实现方法。Task,Future和事件循环扮演这在asynico中同样的角色。
+
+你已经知道asyncio协程是如何工作的了,现在你可以忘记大部分的细节。这些机制隐藏在一个整洁的接口下。但是你对这基本原理的理解能让你在现代异步环境下正确而高效的编写代码。
+
+[^4]: [https://glyph.twistedmatrix.com/2014/02/unyielding.html](https://glyph.twistedmatrix.com/2014/02/unyielding.html)
+
+[^5]: [https://docs.python.org/3/library/queue.html](https://docs.python.org/3/library/queue.html)
+
+[^6]: [https://docs.python.org/3/library/asyncio-sync.html](https://docs.python.org/3/library/asyncio-sync.html)
+
+[^7]: For a complex solution to this problem, see [http://www.tornadoweb.org/en/stable/stack_context.html](http://www.tornadoweb.org/en/stable/stack_context.html)
+
+[^8]: [http://www.kegel.com/c10k.html](http://www.kegel.com/c10k.html)
+
+[^9]: The actual `asyncio.Queue` implementation uses an `asyncio.Event` in place of the Future shown here. The difference is an Event can be reset, whereas a Future cannot transition from resolved back to pending.
+
+[^10]: The `@asyncio.coroutine` decorator is not magical. In fact, if it decorates a generator function and the `PYTHONASYNCIODEBUG` environment variable is not set, the decorator does practically nothing. It just sets an attribute, `_is_coroutine`, for the convenience of other parts of the framework. It is possible to use asyncio with bare generators not decorated with `@asyncio.coroutine` at all.
+
+<latex>
+[^11]: Jesse listed indications and contraindications for using async in "What Is Async, How Does It Work, And When Should I Use It?", available at pyvideo.org. 
+[^bayer]: Mike Bayer compared the throughput of asyncio and multithreading for different workloads in his "Asynchronous Python and Databases": http://techspot.zzzeek.org/2015/02/15/asynchronous-python-and-databases/
+</latex>
+<markdown>
+[^11]: Jesse listed indications and contraindications for using async in ["What Is Async, How Does It Work, And When Should I Use It?":](http://pyvideo.org/video/2565/what-is-async-how-does-it-work-and-when-should). Mike Bayer compared the throughput of asyncio and multithreading for different workloads in ["Asynchronous Python and Databases":](http://techspot.zzzeek.org/2015/02/15/asynchronous-python-and-databases/)
+</markdown>
+
+[^12]: This future has many deficiencies. For example, once this future is resolved, a coroutine that yields it should resume immediately instead of pausing, but with our code it does not. See asyncio's Future class for a complete implementation.
+
+[^13]: In fact, this is exactly how "yield from" works in CPython. A function increments its instruction pointer before executing each statement. But after the outer generator executes "yield from", it subtracts 1 from its instruction pointer to keep itself pinned at the "yield from" statement. Then it yields to *its* caller. The cycle repeats until the inner generator throws `StopIteration`, at which point the outer generator finally allows itself to advance to the next instruction.
+
+[^14]: Python's global interpreter lock prohibits running Python code in parallel in one process anyway. Parallelizing CPU-bound algorithms in Python requires multiple processes, or writing the parallel portions of the code in C. But that is a topic for another day.
+
+[^15]: Even calls to `send` can block, if the recipient is slow to acknowledge outstanding messages and the system's buffer of outgoing data is full.
+
+<markdown>
+[^16]: Guido introduced the standard asyncio library, called "Tulip" then, at [PyCon 2013](http://pyvideo.org/video/1667/keynote).
+</markdown>
+<latex>
+[^16]: Guido introduced the standard asyncio library, called "Tulip" then, at PyCon 2013.
+</latex>
+
+[^17]: Python 3.5's built-in coroutines are described in [PEP 492](https://www.python.org/dev/peps/pep-0492/), "Coroutines with async and await syntax."
+
+--------------------------------------
+via: http://aosabook.org/en/500L/pages/a-web-crawler-with-asyncio-coroutines.html
+
+作者: A. Jesse Jiryu Davis and Guido van Rossum
+译者:[qingyunha](https://github.com/qingyunha)
+校对:
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](http://linux.cn/) 荣誉推出 
diff --git a/translated/tech/LFCS/Part 12 - How to Explore Linux with Installed Help Documentations and Tools.md b/translated/tech/LFCS/Part 12 - How to Explore Linux with Installed Help Documentations and Tools.md
deleted file mode 100644
index 2d0238becf..0000000000
--- a/translated/tech/LFCS/Part 12 - How to Explore Linux with Installed Help Documentations and Tools.md	
+++ /dev/null
@@ -1,178 +0,0 @@
-LFCS第十二讲: 如何使用Linux的帮助文档和工具
-==================================================================================
-
-由于 2016 年 2 月 2 号开始启用了新的 LFCS 考试要求,  我们在[LFCS series][1]系列添加了一些必要的内容 . 为了考试的需要,  我们强烈建议你看一下[LFCE series][2] .
-
-![](http://www.tecmint.com/wp-content/uploads/2016/03/Explore-Linux-with-Documentation-and-Tools.png)
->LFCS: 了解Linux的帮助文档和工具
-
-当你习惯了在命令行下进行工作, 你会发现Linux有许多文档需要你去使用和配置Linux系统.
-
-另一个你必须熟悉命令行帮助工具的理由是,在[LFCS][3] 和 [LFCE][4] 考试中, 你只能靠你自己和命令行工具,没有互联网也没有百度。
-
-基于上面的理由, 在这一章里我们将给你一些建议来帮助你通过**Linux Foundation Certification** 考试.
-
-### Linux 帮助手册
-
-man命令, 大体上来说就是一个工具手册. 它包含选项列表(和解释) , 甚至还提供一些例子.
-
-我们用**man command** 加工具名称来打开一个帮助手册以便获取更多内容. 例如:
-
-```
-# man diff
-```
-
-我们将打开`diff`的手册页, 这个工具将一行一行的对比文本文档 (如你想退出只需要轻轻的点一下Q键).
-
-下面我来比较两个文本文件 `file1` 和 `file2` . 这两个文本文件包含着相同版本Linux的安装包信息.
-
-输入`diff` 命令它将告诉我们 `file1` 和`file2` 有什么不同:
-
-```
-# diff file1 file2
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/03/Compare-Two-Text-Files-in-Linux.png)
->在Linux中比较两个文本文件
-
-`<` 这个符号是说`file2`少一行. 如果是 `file1`少一行, 我们将用 `>` 符号来替代.
-
-接下来说, **7d6** 意思是说 文件1的**#7**行在 `file2`中被删除了 ( **24d22** 和**41d38**是同样的意思), 65,67d61告诉我们移动 **65** 到 **67** . 我们把以上步骤都做了两个文件将完全匹配.
-
-你还可以通过 `-y` 选项来对比两个文件:
-
-```
-# diff -y file1 file2
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/03/Compare-and-List-Difference-of-Two-Files.png)
->通过列表来列出两个文件的不同
-
- 当然你也可以用`diff`来比较两个二进制文件 . 如果它们完全一样, `diff` 将什么也不会输出. 否则, 他将会返回如下信息: “**Binary files X and Y differ**”.
-
-### –help 选项
-
-`--help`选项 , 大多数命令都可以用它(并不是所有) , 他可以理解为一个命令的简单介绍. 尽管它不提供工具的详细介绍, 但是确实是一个能够快速列出程序使用信息的不错的方法.
-
-例如,
-
-```
-# sed --help
-```
-
-显示 sed 的每个选项的用法(sed文本流编辑器).
-
-一个经典的`sed`例子,替换文件字符. 用 `-i` 选项 (描述为 “**编辑文件在指定位置**”), 你可以编辑一个文件而且并不需要打开他. 如果你想要备份一个原始文件, 用 `-i` 选项 加后缀来创建一个原始文件的副本.
-
-例如, 替换 `lorem.txt`中的`Lorem` 为 `Tecmint` (忽略大小写)  并且创建一个新的原始文件副本, 命令如下:
-
-```
-# less lorem.txt | grep -i lorem
-# sed -i.orig 's/Lorem/Tecmint/gI' lorem.txt
-# less lorem.txt | grep -i lorem
-# less lorem.txt.orig | grep -i lorem
-```
-
- 请注意`lorem.txt`文件中`Lorem` 都已经替换为 `Tecmint`  , 并且原始的 `lorem.txt` 保存为`lorem.txt.orig`.
-
-![](http://www.tecmint.com/wp-content/uploads/2016/03/Replace-A-String-in-File.png)
->替换文件文本
-
-### /usr/share/doc内的文档
-这可能是我最喜欢的方法. 如果你进入 `/usr/share/doc` 目录, 你可以看到好多Linux已经安装的工具的名称的文件夹.
-
-根据[Filesystem Hierarchy Standard][5](文件目录标准),这些文件夹包含了许多帮助手册没有的信息, 还有一些模板和配置文件.
-
-例如, 让我们来看一下 `squid-3.3.8` (版本可能会不同) 一个非常受欢迎的HTTP代理[squid cache server][6].
-
-让我们用`cd`命令进入目录 :
-
-```
-# cd /usr/share/doc/squid-3.3.8
-```
-
-列出当前文件夹列表:
-
-```
-# ls
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/03/List-Files-in-Linux.png)
->ls linux列表命令
-
-你应该特别注意 `QUICKSTART` 和 `squid.conf.documented`. 这两个文件包含了Squid许多信息, . 对于别的安装包来说, 他们的名字可能不同 (有可能是 **QuickRef** 或者**00QUICKSTART**), 但原理是一样的.
-
-对于另外一些安装包, 比如 the Apache web server, 在`/usr/share/doc`目录提供了配置模板, 当你配置独立服务器或者虚拟主机的时候会非常有用.
-
-### GNU 信息文档
-
-你可以把它想象为帮助手册的超级链接形式. 正如上面说的, 他不仅仅提供工具的帮助信息, 而且还是超级链接的形式(是的!在命令行中的超级链接) 你可以通过箭头按钮和回车按钮来浏览你需要的内容.
-
-一个典型的例子是:
-
-```
-# info coreutils
-```
-
-通过coreutils 列出当前系统的 基本文件,shell脚本和文本处理工具[basic file, shell and text manipulation utilities][7] , 你可以得到他们的详细介绍.
-
-![](http://www.tecmint.com/wp-content/uploads/2016/03/Info-Coreutils.png)
->Info Coreutils
-
-和帮助手册一样你可以按Q键退出.
-
-此外, GNU info 还可以像帮助手册一样使用. 例如:
-
-```
-# info tune2fs
-```
-
-它将显示 **tune2fs**的帮助手册,  ext2/3/4 文件系统管理工具.
-
-让我们来看看怎么用**tune2fs**:
-
-显示 **/dev/mapper/vg00-vol_backups**文件系统信息:
-
-```
-# tune2fs -l /dev/mapper/vg00-vol_backups
-```
-
-修改文件系统标签 (修改为Backups):
-
-```
-# tune2fs -L Backups /dev/mapper/vg00-vol_backups
-```
-
-设置 `/` 自检的挂载次数 (用`-c` 选项设置 `/`的自检的挂载次数 或者用  `-i` 选项设置  自检时间 **d=days, w=weeks, and m=months**).
-
-```
-# tune2fs -c 150 /dev/mapper/vg00-vol_backups # Check every 150 mounts
-# tune2fs -i 6w /dev/mapper/vg00-vol_backups # Check every 6 weeks
-```
-
-以上这些内容也可以通过 `--help` 选项找到, 或者查看帮助手册.
-
-### 摘要
-
-不管你选择哪种方法,知道并且会使用它们在考试中对你是非常有用的. 你知道其它的一些方法吗? 欢迎给我们留言.
-
-
---------------------------------------------------------------------------------
-
-via: http://www.tecmint.com/linux-basic-shell-scripting-and-linux-filesystem-troubleshooting/
-
-作者:[Gabriel Cánepa][a]
-译者:[kokialoves](https://github.com/kokialoves)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]:http://www.tecmint.com/author/gacanepa/
-[1]: http://www.tecmint.com/sed-command-to-create-edit-and-manipulate-files-in-linux/
-[2]: http://www.tecmint.com/installing-network-services-and-configuring-services-at-system-boot/
-[3]: http://www.tecmint.com/sed-command-to-create-edit-and-manipulate-files-in-linux/
-[4]: http://www.tecmint.com/installing-network-services-and-configuring-services-at-system-boot/
-[5]: http://www.tecmint.com/linux-directory-structure-and-important-files-paths-explained/
-[6]: http://www.tecmint.com/configure-squid-server-in-linux/
-[7]: http://www.tecmint.com/sed-command-to-create-edit-and-manipulate-files-in-linux/
-[8]: 
diff --git a/translated/tech/LXD/Part 3 - LXD 2.0--Your first LXD container.md b/translated/tech/LXD/Part 3 - LXD 2.0--Your first LXD container.md
deleted file mode 100644
index 4b6dcfd8aa..0000000000
--- a/translated/tech/LXD/Part 3 - LXD 2.0--Your first LXD container.md	
+++ /dev/null
@@ -1,438 +0,0 @@
-kylepeng93 is translating
-你的地一个LXD容器
-==========================================
-
-这是第三篇发布的博客[LXD2.0系列]
-由于在管理LXD容器时涉及到大量的命令,所以这篇文章的篇幅是比较长的,如果你更喜欢使用同样的命令来快速的一步步实现整个过程,你可以[尝试我们的在线示例]!
-![](https://linuxcontainers.org/static/img/containers.png)
-
-### 创建并启动一个新的容器
-正如我在先前的文章中提到的一样,LXD命令行客户端使用了少量的图片来做了一个预配置。Ubuntu的所有发行版和架构平台都拥有最好的官方图片,但是对于其他的发行版仍然有大量的非官方图片,那些图片都是由社区制作并且被LXC上层贡献者所维护。
-### Ubuntu
-如果你想要支持最为完善的ubuntu版本,你可以按照下面的去做:
-```
-lxc launch ubuntu:
-```
-注意,这里所做的解释会随着ubuntu LTS的发布而变化。因此对于你使用的脚本应该取决于下面提到的具体你想要安装的版本:
-###Ubuntu14.04 LTS
-得到最新的,已经测试过的,稳定的ubuntu14.04 LTS镜像,你可以简单的执行:
-```
-lxc launch ubuntu:14.04
-```
-这该模式下,一个任意的容器名将会被指定给它。
-如果你更喜欢指定一个你自己的命令,你可以这样做:
-```
-lxc launch ubuntu:14.04 c1
-```
-
-如果你想要指定一个特定的体系架构(非主要的),比如32位Intel镜像,你可以这样做:
-```
-lxc launch ubuntu:14.04/i386 c2
-```
-
-### 当前的Ubuntu开发版本
-上面使用的“ubuntu:”远程方式只会给你提供官方的并经过测试的ubuntu镜像。但是如果你想要未经测试过的日常构建版本,开发版可能对你来说是合适的,你将要使用“ubuntu-daily”来远程获取。
-```
-lxc launch ubuntu-daily:devel c3
-```
-
-在这个例子中,最新的ubuntu开发版本将会被选自动选中。
-你也可以更加精确,比如你可以使用代号名:
-```
-lxc launch ubuntu-daily:xenial c4
-```
-
-### 最新的Alpine Linux
-Alpine镜像在“Images:”远程中可用,可以通过如下命令执行:
-```
-lxc launch images:alpine/3.3/amd64 c5
-```
-
-### And many more
-### 其他
-所有ubuntu镜像列表可以这样获得:
-```
-lxc image list ubuntu:
-lxc image list ubuntu-daily:
-```
-
-所有的非官方镜像:
-```
-lxc image list images:
-```
-
-所有给定的可用远程别名清单可以这样获得(针对“ubuntu:”远程):
-```
-lxc image alias list ubuntu:
-```
-
-
-### 创建但不启动一个容器
-如果你想创建一个容器或者一批容器,但是你不想马上启动他们,你可以使用“lxc init”替换掉“lxc launch”。所有的选项都是相同的,唯一的不同就是它并不会在你创建完成之后启动容器。
-```
-lxc init ubuntu:
-```
-
-### 关于你的容器的信息
-### 列出所有的容器
-为了列出你的所有容器,你可以这样这做:
-```
-lxc list
-```
- 
-有大量的选项供你选择来改变被显示出来的列。在一个拥有大量容器的系统上,默认显示的列可能会有点慢(因为必须获取容器中的网络信息),你可以这样做来避免这种情况:
-```
-lxc list --fast
-```
-
-上面的命令显示了一个不同的列的集合,这个集合在服务器端需要处理的信息更少。
-你也可以基于名字或者属性来过滤掉一些东西:
-```
-stgraber@dakara:~$ lxc list security.privileged=true
-+------+---------+---------------------+-----------------------------------------------+------------+-----------+
-| NAME |  STATE  |        IPV4         |                       IPV6                    |    TYPE    | SNAPSHOTS |
-+------+---------+---------------------+-----------------------------------------------+------------+-----------+
-| suse | RUNNING | 172.17.0.105 (eth0) | 2607:f2c0:f00f:2700:216:3eff:fef2:aff4 (eth0) | PERSISTENT | 0         |
-+------+---------+---------------------+-----------------------------------------------+------------+-----------+
-```
-
-在这个例子中,只有那些有特权(用户命名空间不可用)的容器才会被列出来。
-```
-stgraber@dakara:~$ lxc list --fast alpine
-+-------------+---------+--------------+----------------------+----------+------------+
-|    NAME     |  STATE  | ARCHITECTURE |      CREATED AT      | PROFILES |    TYPE    |
-+-------------+---------+--------------+----------------------+----------+------------+
-| alpine      | RUNNING | x86_64       | 2016/03/20 02:11 UTC | default  | PERSISTENT |
-+-------------+---------+--------------+----------------------+----------+------------+
-| alpine-edge | RUNNING | x86_64       | 2016/03/20 02:19 UTC | default  | PERSISTENT |
-+-------------+---------+--------------+----------------------+----------+------------+
-```
-
-在这个例子中,只有在名字中带有“alpine”的容器才会被列出来(支持复杂的正则表达式)。
-###  获取容器的详细信息
-由于list命令显然不能以一种友好的可读方式显示容器的所有信息,因此你可以使用如下方式来查询单个容器的信息:
-```
-lxc info <container>
-```
-
-例如:
-```
-stgraber@dakara:~$ lxc info zerotier
-Name: zerotier
-Architecture: x86_64
-Created: 2016/02/20 20:01 UTC
-Status: Running
-Type: persistent
-Profiles: default
-Pid: 31715
-Processes: 32
-Ips:
- eth0: inet 172.17.0.101
- eth0: inet6 2607:f2c0:f00f:2700:216:3eff:feec:65a8
- eth0: inet6 fe80::216:3eff:feec:65a8
- lo: inet 127.0.0.1
- lo: inet6 ::1
- lxcbr0: inet 10.0.3.1
- lxcbr0: inet6 fe80::c0a4:ceff:fe52:4d51
- zt0: inet 29.17.181.59
- zt0: inet6 fd80:56c2:e21c:0:199:9379:e711:b3e1
- zt0: inet6 fe80::79:e7ff:fe0d:5123
-Snapshots:
- zerotier/blah (taken at 2016/03/08 23:55 UTC) (stateless)
- ```
- 
-### 生命周期管理命令
-这些命令对于任何容器或者虚拟机管理器或许都是最普通的命令,但是它们仍然需要被涉及到。
-所有的这些命令在批量操作时都能接受多个容器名。
-###  启动
-启动一个容器就向下面一样简单:
-```
-lxc start <container>
-```
-
-###  停止
-停止一个容器可以这样来完成:
-```
-lxc stop <container>
-```
-
-如果容器不合作(即没有对发出的信号产生回应),这时候,你可以使用下面的方式强制执行:
-```
-lxc stop <container> --force
-```
-
-###  重启
-通过下面的命令来重启一个容器:
-```
-lxc restart <container>
-```
-
-如果容器不合作(即没有对发出的信号产生回应),你可以使用下面的方式强制执行:
-```
-lxc restart <container> --force
-```
-
-###  暂停
-你也可以“暂停”一个容器,在这种模式下,所有的容器任务将会被发送相同的信号,这也意味着他们将仍然是可见的,并且仍然会占用内存,但是他们不会从调度程序中得到任何的CPU时间片。
-如果你有一个CPU的饥饿容器,而这个容器需要一点时间来启动,但是你却并 不会经常用到它。这时候,你可以先启动它,然后将它暂停,并在你需要它的时候再启动它。
-```
-lxc pause <container>
-```
-
-###  删除
-最后,如果你不需要这个容器了,你可以用下面的命令删除它:
-```
-lxc delete <container>
-```
-
-注意,如果容器还处于运行状态时你将必须使用“-forece”。
-### 容器的配置
-LXD拥有大量的容器配置设定,包括资源限制,容器启动控制以及对各种设备是否允许访问的配置选项。完整的清单因为太长所以并没有在本文中列出,但是,你可以从[here]获取它。
-就设备而言,LXD当前支持下面列出的这些设备:
-- 磁盘
-既可以是一块物理磁盘,也可以只是一个被挂挂载到容器上的分区,还可以是一个来自主机的绑定挂载路径。
-- 网络接口卡
-一块网卡。它可以是一块桥接的虚拟网卡,或者是一块点对点设备,还可以是一块以太局域网设备或者一块已经被连接到容器的真实物理接口。
-- unix块
-一个UNIX块设备,比如/dev/sda
-- unix字符
-一块UNIX字符设备,比如/dev/kvm
-- none
-这种特殊类型被用来隐藏那种可以通过profiles文件被继承的设备。
-
-### 配置profiles文件
-所有可用的profiles的文件列表可以这样获取:
-```
-lxc profile list
-```
-
-为了看到给定profile文件的内容,最简单的方式是这样做:
-```
-lxc profile show <profile>
-```
-
-你可能想要改变文件里面的内容,可以这样做:
-```
-lxc profile edit <profile>
-```
-
-你可以使用如下命令来改变profiles的列表并将这种变化应用到给定的容器中:
-```
-lxc profile apply <container> <profile1>,<profile2>,<profile3>,...
-```
-
-###  本地配置
-For things that are unique to a container and so don’t make sense to put into a profile, you can just set them directly against the container:
-
-```
-lxc config edit <container>
-```
-
-上面的命令将会完成和“profile edit”命令一样的功能。
-
-即使不在文本编辑器中打开整个文件的内容,你也可以像这样修改单独的键:
-```
-lxc config set <container> <key> <value>
-```
-或者添加设备,例如
-```
-lxc config device add my-container kvm unix-char path=/dev/kvm
-```
-
-上面的命令将会为名为“my-container”的容器打开一个/dev/kvm入口。
-对一个profile文件使用“lxc profile set”和“lxc profile device add”命令也能实现上面的功能。
-#### 读取配置
-你可以使用如下命令来阅读容器的本地配置:
-```
-lxc config show <container>
-```
-
-或者得到已经被展开了的配置(包含了所有的键值):
-```
-lxc config show --expanded <container>
-```
-
-例如:
-```
-stgraber@dakara:~$ lxc config show --expanded zerotier
-name: zerotier
-profiles:
-- default
-config:
- security.nesting: "true"
- user.a: b
- volatile.base_image: a49d26ce5808075f5175bf31f5cb90561f5023dcd408da8ac5e834096d46b2d8
- volatile.eth0.hwaddr: 00:16:3e:ec:65:a8
- volatile.last_state.idmap: '[{"Isuid":true,"Isgid":false,"Hostid":100000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":100000,"Nsid":0,"Maprange":65536}]'
-devices:
- eth0:
-  name: eth0
-  nictype: macvlan
-  parent: eth0
-  type: nic
-  limits.ingress: 10Mbit
-  limits.egress: 10Mbit
- root:
-  path: /
-  size: 30GB
-  type: disk
- tun:
-  path: /dev/net/tun
-  type: unix-char
-ephemeral: false
-```
-
-这样做可以很方便的检查有哪些配置属性被应用到了给定的容器。
-###  实时配置更新
-注意,除非在文档中已经被明确指出,否则所有的键值和设备入口都会被应用到受影响的实时容器。这意味着你可以添加和移除某些设备或者在不重启容器的情况下修改正在运行的容器的安全profile配置文件。
-### 获得一个shell
-LXD允许你对容器中的任务进行直接的操作。最常用的做法是在容器中得到一个shell或者执行一些管理员任务。
-和SSH相比,这样做的好处是你可以接触到独立与容器之外的网络或者任何一个软件又或者任何一个能够在容器内可见的文件配置。
-执行环境
-对比LXD在容器内执行命令的方式,有一点是不同的,那就是它自身并不是在容器中运行。这也意味着它不知道该使用什么样的shell,以及设置什么样的环境变量和哪里是它的家目录。
-通过LXD来执行命令必须总是在最小路径环境变量集并且HONE环境变量必须为/root的情况下以容器的超级用户身份来执行(即uid为0,gid为0)。
-其他的环境变量可以通过命令行来设置,或者在“environment.<key>”配置文件中设置成永久环境变量。
-###  执行命令
-在容器中获得一个shell可以简单的执行下列命令得到:
-```
-lxc exec <container> bash
-```
-
-当然,这样做的前提是容器内已经安装了bash。
-
-更多复杂的命令要求有对参数分隔符的合理使用。
-```
-lxc exec <container> -- ls -lh /
-```
-
-如果想要设置或者重写变量,你可以使用“-env”参数,例如:
-```
-stgraber@dakara:~$ lxc exec zerotier --env mykey=myvalue env | grep mykey
-mykey=myvalue
-```
-
-### 管理文件
-因为LXD可以直接访问容器的文件系统,因此,它可以直接往容器中读取和写入任意文件。当我们需要提取日志文件或者与容器发生交互时,这个特性是很有用的。
-#### 从容器中取回一个文件
-想要从容器中获得一个文件,简单的执行下列命令:
-```
-lxc file pull <container>/<path> <dest>
-```
-
-例如:
-```
-stgraber@dakara:~$ lxc file pull zerotier/etc/hosts hosts
-```
-
-或者将它读取到标准输出:
-```
-stgraber@dakara:~$ lxc file pull zerotier/etc/hosts -
-127.0.0.1 localhost
-
-# 下面的所有行对于支持IPv6的主机是有用的
-::1 ip6-localhost ip6-loopback
-fe00::0 ip6-localnet
-ff00::0 ip6-mcastprefix
-ff02::1 ip6-allnodes
-ff02::2 ip6-allrouters
-ff02::3 ip6-allhosts
-```
-
-#### 向容器发送一个文件  
-
-发送会简单的以另一种方式完成:
-```
-lxc file push <source> <container>/<path>
-```
-
-#### 直接编辑一个文件
-当只是简单的提取一个给定的路径时,编辑是一个很方便的功能,在你的默认文本编辑器中打开它,这样在你关闭编辑器时会自动将编辑的内容保存到容器。
-```
-lxc file edit <container>/<path>
-```
-
-### 快照管理
-LXD允许你对容器执行快照功能并恢复它。快照包括了容器在某一时刻的完整状态(如果-stateful被使用的话将会包括运行状态),这也意味着所有的容器配置,容器设备和容器文件系统也会被包保存。
-#### 创建一个快照
-你可以使用下面的命令来执行快照功能:
-```
-lxc snapshot <container>
-```
-
-命令执行完成之后将会生成名为snapX(X为一个自动增长的数)的记录。
-除此之外,你还可以使用如下命令命名你的快照:
-```
-lxc snapshot <container> <snapshot name>
-```
-
-#### 列出所有的快照
-一个容器的所有快照的数量可以使用“lxc list”来得到,但是真正的快照列表只能执行“lxc info”命令才能看到。
-```
-lxc info <container>
-```
-
-#### 恢复快照
-为了恢复快照,你可以简单的执行下面的命令:
-```
-lxc restore <container> <snapshot name>
-```
-
-#### 给快照重命名
-可以使用如下命令来给快照重命名:
-```
-lxc move <container>/<snapshot name> <container>/<new snapshot name>
-```
-
-#### 从快照中创建一个新的容器
-你可以使用快照来创建一个新的容器,而这个新的容器除了一些可变的信息将会被重置之外(例如MAC地址)其余所有信息都将和快照完全相同。
-```
-lxc copy <source container>/<snapshot name> <destination container>
-```
-
-#### 删除一个快照
-
-最后,你可以执行下面的命令来删除一个快照:
-```
-lxc delete <container>/<snapshot name>
-```
-
-### 克隆并重命名
-得到一个纯净的发行版镜像总是让人感到愉悦,但是,有时候你想要安装一系列的软件到你的容器中,这时,你需要配置它然后将它分割并分配到多个其他的容器中。
-#### 复制一个容器
-为了复制一个容器并有效的将它克隆到一个新的容器中,你可以执行下面的命令:
-```
-lxc copy <source container> <destination container>
-```
-目标容器在所有方面将会完全和源容器等同。除非它没有
-目标容器在所有方面将会完全和源容器等同。除了新的容器没有任何源容器的快照以及一些可变值将会被重置之外(例如MAC地址)。
-#### 移除一个快照
-LXD允许你复制容器并在主机之间移动它。但是,关于这一点将在后面的文章中介绍。
-现在,“move”命令将会被用作给容器重命名。
-```
-lxc move <old name> <new name>
-```
-
-唯一的要求就是当容器被停止时,容器内的任何事情都会被保存成它本来的样子,包括可变化的信息(类似MAC地址等)。
-### 结论
-这篇如此长的文章介绍了大多数你可能会在日常操作中使用到的命令。
-很显然,那些如此之多的命令都会有额外的可以让你的命令更加有效率或者可以让你指定你的LXD容器的某个具体方面的参数。最好的学习这些命令的方式就是深入学习它们的帮助文档,当然。只是对于那些你真正需要用到的命令参数。
-### 额外的信息
-LXD的主要网站是:<https://linuxcontainers.org/lxd>
-Github上的开发说明: <https://github.com/lxc/lxd>
-邮件列表支持:<https://lists.linuxcontainers.org>
-IRC支持: #lxcontainers on irc.freenode.net
-如果你不想或者不能在你的机器上安装LXD,你可以[try it online instead][1]!
-
---------------------------------------------------------------------------------
-
-来自于:https://www.stgraber.org/2016/03/19/lxd-2-0-your-first-lxd-container-312/
-作者:[Stéphane Graber][a]
-译者:[kylepeng93](https://github.com/译者ID)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创翻译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: https://www.stgraber.org/author/stgraber/
-[0]: https://www.stgraber.org/2016/03/11/lxd-2-0-blog-post-series-012/
-[1]: https://linuxcontainers.org/lxd/try-it
-[2]: https://github.com/lxc/lxd/blob/master/doc/configuration.md
diff --git a/translated/tech/awk/Part 7 - How to Read Awk Input from STDIN in Linux.md b/translated/tech/awk/Part 7 - How to Read Awk Input from STDIN in Linux.md
deleted file mode 100644
index 0d0d499d00..0000000000
--- a/translated/tech/awk/Part 7 - How to Read Awk Input from STDIN in Linux.md	
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-在 Linux 上怎么读取标准输入(STDIN)作为 Awk 的输入
-============================================
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/Read-Awk-Input-from-STDIN.png)
-
-
-在 Awk 工具系列的前几节,我们看到大多数操作都是从一个文件或多个文件读取输入,或者你想要把标准输入作为 Awk 的输入.
-在 Awk 系列的第7节中,我们将会看到几个例子,这些例子都是关于你可以筛选其他命令的输出代替从一个文件读取输入作为 awk 的输入.
-
-
-我们开始使用 [dir utility][1] , dir 命令和 [ls 命令][2] 相似,在第一个例子下面,我们使用 'dir -l' 命令的输出作为 Awk 命令的输入,这样就可以打印出文件拥有者的用户名,所属组组名以及在当前路径下他/她拥有的文件.
-```
-# dir -l | awk '{print $3, $4, $9;}'
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/List-Files-Owned-By-User-in-Directory.png)
->列出当前路径下的用户文件
-
-
-看另一个例子,我们 [使用 awk 表达式][3] ,在这里,我们想要在 awk 命令里使用一个表达式筛选出字符串,通过这样来打印出 root 用户的文件.命令如下:
-```
-# dir -l | awk '$3=="root" {print $1,$3,$4, $9;} '
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/List-Files-Owned-by-Root-User.png)
->列出 root 用户的文件
-
-
-上面的命令包含了 '(==)' 来进行比较操作,这帮助我们在当前路径下筛选出 root 用户的文件.这种方法的实现是通过使用 '$3=="root"' 表达式.
-
-让我们再看另一个例子,我们使用一个 [awk 比较运算符][4] 来匹配一个确定的字符串.
-
-
-现在,我们已经用了 [cat utility][5] 来浏览文件名为 tecmint_deals.txt 的文件内容,并且我们想要仅仅查看有字符串 Tech 的部分,所以我们会运行下列命令: 
-```
-# cat tecmint_deals.txt
-# cat tecmint_deals.txt | awk '$4 ~ /tech/{print}'
-# cat tecmint_deals.txt | awk '$4 ~ /Tech/{print}'
-```
-
-![](http://www.tecmint.com/wp-content/uploads/2016/06/Use-Comparison-Operator-to-Match-String.png)
->用 Awk 比较运算符匹配字符串
-
-
-在上面的例子中,我们已经用了参数为 `~ /匹配字符/` 的比较操作,但是上面的两个命令给我们展示了一些很重要的问题.
-
-当你运行带有 tech 字符串的命令时终端没有输出,因为在文件中没有 tech 这种字符串,但是运行带有 Tech 字符串的命令,你却会得到包含 Tech 的输出. 
-
-所以你应该在进行这种比较操作的时候时刻注意这种问题,正如我们在上面看到的那样, awk 对大小写很敏感.
-
-
-你可以一直使用另一个命令的输出作为 awk 命令的输入来代替从一个文件中读取输入,这就像我们在上面看到的那样简单.
-
-
-希望这些例子足够简单可以使你理解 awk 的用法,如果你有任何问题,你可以在下面的评论区提问,记得查看 awk 系列接下来的章节内容,我们将关注 awk 的一些功能,比如变量,数字表达式以及赋值运算符.
---------------------------------------------------------------------------------
-
-via: http://www.tecmint.com/read-awk-input-from-stdin-in-linux/
-
-作者:[Aaron Kili][a]
-译者:[vim-kakali](https://github.com/vim-kakali)
-校对:[校对者ID](https://github.com/校对者ID)
-
-本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
-
-[a]: http://www.tecmint.com/author/aaronkili/
-[1]: http://www.tecmint.com/linux-dir-command-usage-with-examples/
-[2]: http://www.tecmint.com/15-basic-ls-command-examples-in-linux/
-[3]: http://www.tecmint.com/combine-multiple-expressions-in-awk
-[4]: http://www.tecmint.com/comparison-operators-in-awk
-[5]: http://www.tecmint.com/13-basic-cat-command-examples-in-linux/
-
-
-
diff --git a/translated/tech/ubuntu vs ubuntu on windows.md b/translated/tech/ubuntu vs ubuntu on windows.md
new file mode 100644
index 0000000000..b8f81c8b8a
--- /dev/null
+++ b/translated/tech/ubuntu vs ubuntu on windows.md	
@@ -0,0 +1,35 @@
+Ubuntu 14.04/16.04 与Windows 10周年版ubuntu Bash性能对比
+===========================
+
+今年初,当Microsoft和Canonical发布[Windows 10 Bash 和Ubuntu用户空间][1],我尝试做了一些初步性能测试 [Ubuntu on Windows 10 对比 原生Ubuntu][2],这次我发布更多的,关于原生纯净Ubuntu和Base on Windows 10的基准对比。
+
+![](http://www.phoronix.net/image.php?id=windows10-anv-wsl&image=windows_wsl_1_med)
+
+Windows的Linux子系统测试在上周刚刚完成所有测试,并放出升级。 默认的Ubuntu用户空间还是Ubuntu 14.04,但是已经可以升级到16.04。所以测试首先在14.04测试,完成后将系统升级升级到16.04版本并重复所有测试。完成所有基于Windows的测试后,我删除了Ubuntu14.04.5和Ubuntu 16.04 LTS来对比查看性能
+
+
+![](http://www.phoronix.net/image.php?id=windows10-anv-wsl&image=windows_wsl_2_med)
+
+配置为Intel i5 6600K Skylake框架, 16G内存和256东芝ssd, 所有测试都采用原生默认配置。
+
+
+![](https://openbenchmarking.org/embed.php?i=1608096-LO-BASHWINDO87&sha=09989b3&p=2)
+>点击放大查看
+
+
+这次Ubuntu/Bash on Windows和原生Ubuntu对比测试,采用开源软件Phoronix测试套件,完全自动化并可重复测试。
+
+--------------------------------------------------------------------------------
+
+via: https://www.phoronix.com/scan.php?page=article&item=windows10-anv-wsl&num=1
+
+作者:[Michael Larabel][a]
+译者:[VicYu/Vic020](http://vicyu.net)
+校对:[校对者ID](https://github.com/校对者ID)
+
+本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
+
+[a]: http://www.michaellarabel.com/
+[1]: http://www.phoronix.com/scan.php?page=news_item&px=Ubuntu-User-Space-On-Win10
+[2]: http://www.phoronix.com/scan.php?page=article&item=windows-10-lxcore&num=1
+